TASK 21 Increment A: media-source model + persistence (schema migration + round-trip test)

SourceType.MediaSource added; MediaSourceType (Video/Audio) and MediaPlaybackState
(Stopped/Playing/Paused) enums. Source gains MediaPath, MediaIsLooping, MediaVolume
(0-1 clamped), MediaPlaybackState. LayoutStore: MigrateSourceTable adds 4 guarded
ALTERs (MediaPath, MediaIsLooping, MediaVolume, MediaPlaybackState); SELECT appends
indices 26-29, INSERT adds 4 columns + params. Round-trip test
MediaSource_Config_Survives_Save_And_Reload passes. Build 0 warnings, 245/247
tests (2 known pre-existing). Gated on creator run for later integration (ffmpeg).
This commit is contained in:
2026-08-29 09:43:29 -07:00
parent 46f4696db6
commit eba6af5b9b
5 changed files with 188 additions and 65 deletions
@@ -388,4 +388,47 @@ public class LayoutStorePersistenceTests
try { File.Delete(path); } catch { /* best-effort cleanup */ }
}
}
// TASK 21: media-source config (Path, Looping, Volume, PlaybackState) must
// survive a save + reload through the full DELETE-all/re-insert cycle.
[Fact]
public void MediaSource_Config_Survives_Save_And_Reload()
{
var path = Path.Combine(Path.GetTempPath(), $"ytLlive-layout-{Guid.NewGuid():N}.db");
try
{
using var store = new LayoutStore(path);
var scene = new Scene { Name = "Starting" };
var source = new Source
{
Name = "Starting Loop",
Type = SourceType.MediaSource,
Width = 1920,
Height = 1080,
MediaPath = @"C:\videos\starting.mp4",
MediaIsLooping = true,
MediaVolume = 0.75,
MediaPlaybackState = MediaPlaybackState.Playing,
};
scene.Elements.Add(source);
store.Save(new[] { scene }, null, null);
var reloaded = store.Load();
var loaded = Assert.Single(reloaded[0].Elements);
var loadedSource = Assert.IsType<Source>(loaded);
Assert.Equal(SourceType.MediaSource, loadedSource.Type);
Assert.True(loadedSource.IsMediaSource);
Assert.Equal(@"C:\videos\starting.mp4", loadedSource.MediaPath);
Assert.True(loadedSource.MediaIsLooping);
Assert.Equal(0.75, loadedSource.MediaVolume);
Assert.Equal(MediaPlaybackState.Playing, loadedSource.MediaPlaybackState);
}
finally
{
SqliteConnection.ClearAllPools();
try { File.Delete(path); } catch { /* best-effort cleanup */ }
}
}
}