diff --git a/Services/LayoutStore.Snapshots.cs b/Services/LayoutStore.Snapshots.cs index 403b5bf..d1d82ec 100644 --- a/Services/LayoutStore.Snapshots.cs +++ b/Services/LayoutStore.Snapshots.cs @@ -246,10 +246,12 @@ public partial class LayoutStore : IDisposable private void RestoreDefaults(string buildId) { var columnsByTable = SnapshotTables.ToDictionary(table => table, ReadColumnNames); + var currentWebcamId = GetCurrentWebcamId(); using var tx = _connection.BeginTransaction(); foreach (var table in SnapshotTables.Reverse()) { + if (table == "Webcam") continue; using var delete = _connection.CreateCommand(); delete.Transaction = tx; delete.CommandText = $"DELETE FROM {table};"; @@ -258,10 +260,16 @@ public partial class LayoutStore : IDisposable foreach (var table in SnapshotTables) { + if (table == "Webcam" || (table == "WebcamSceneConfig" && currentWebcamId == null)) continue; var columns = string.Join(", ", columnsByTable[table].Select(QuoteIdentifier)); + var values = string.Join(", ", columnsByTable[table].Select(column => + column == "WebcamId" && table == "WebcamSceneConfig" + ? "$webcamId" + : QuoteIdentifier(column))); using var copy = _connection.CreateCommand(); copy.Transaction = tx; - copy.CommandText = $"INSERT INTO {table} ({columns}) SELECT {columns} FROM Default{table};"; + copy.CommandText = $"INSERT INTO {table} ({columns}) SELECT {values} FROM Default{table};"; + if (table == "WebcamSceneConfig") copy.Parameters.AddWithValue("$webcamId", currentWebcamId!); copy.ExecuteNonQuery(); } diff --git a/ytLive.Tests/LayoutStoreSnapshotTests.cs b/ytLive.Tests/LayoutStoreSnapshotTests.cs index 0969f5d..344f4e7 100644 --- a/ytLive.Tests/LayoutStoreSnapshotTests.cs +++ b/ytLive.Tests/LayoutStoreSnapshotTests.cs @@ -116,6 +116,39 @@ public sealed class LayoutStoreSnapshotTests } } + [Fact] + public void DifferentBuild_RestoresWebcamPlacement_WithoutReplacingChosenCamera() + { + var path = Path.Combine(Path.GetTempPath(), $"ytLlive-layout-snapshot-{Guid.NewGuid():N}.db"); + var sceneId = Guid.NewGuid().ToString(); + var defaultWebcam = new Webcam { Id = "factory-webcam", DeviceId = "factory-device", Name = "Factory camera" }; + var chosenWebcam = new Webcam { Id = "chosen-webcam", DeviceId = "chosen-device", Name = "Chosen camera" }; + try + { + using (var store = new LayoutStore(path)) + { + store.Save(new[] { MakeWebcamScene(sceneId, defaultWebcam.Id, 20) }, defaultWebcam, null); + store.PrepareLayoutSnapshots("build-a"); + store.Save(new[] { MakeWebcamScene(sceneId, chosenWebcam.Id, 200) }, chosenWebcam, null); + } + + using (var reopened = new LayoutStore(path)) + { + reopened.PrepareLayoutSnapshots("build-b"); + var restored = Assert.Single(reopened.Load()); + var config = Assert.Single(restored.Elements.OfType()); + Assert.Equal(chosenWebcam.DeviceId, reopened.Webcam?.DeviceId); + Assert.Equal(chosenWebcam.Id, config.WebcamId); + Assert.Equal(20, config.X); + } + } + finally + { + SqliteConnection.ClearAllPools(); + try { File.Delete(path); } catch { /* best-effort cleanup */ } + } + } + private static Scene MakeScene(string sourceName) => MakeScene(SceneCatalog.Starting, sourceName); @@ -134,6 +167,14 @@ public sealed class LayoutStoreSnapshotTests Elements = { new Source { Name = sourceName, Type = SourceType.TextOverlay } }, }; + private static Scene MakeWebcamScene(string id, string webcamId, double x) + => new() + { + Id = id, + Name = SceneCatalog.Live, + Elements = { new WebcamSceneConfig { WebcamId = webcamId, Name = "Webcam", X = x, Y = 20, Width = 320, Height = 180 } }, + }; + private static SqliteConnection GetConnection(string path) { var connection = new SqliteConnection($"Data Source={path}");