refactor: split LayoutStore by concern — 500-limit Phase 3, Commit E
LayoutStore.cs 1402 -> 47-line shell + 5 partials, grouped by functionality (line count is a consequence, not the goal): - LayoutStore.Migrations.cs: all DB schema migration (EnsureSchema, GetUserVersion, MigrateSourceTable/WebcamConfigTable/ToV3/SceneTable/SceneSocialBarColumn/ SocialsTable/SocialEntryTable) - LayoutStore.Load.cs: Load() scene/source read-back - LayoutStore.Save.cs: Save() full scene tree write - LayoutStore.Settings.cs: key/value settings + mic/record-folder/reusable-stream + license/transition/broadcast/hotkey/window-state GetSetting/UpsertSetting - LayoutStore.Assets.cs: GetAssetBytes/UpsertAsset blob IO - LayoutStore.cs: shell — Instance, fields, ctor, Dispose Converted to public partial class LayoutStore : IDisposable. Pruned per-file dead usings. Phase 3 DOWN: no production .cs exceeds 500 lines. Zero behavior change; build 0 warnings; 246 pass, only the 2 known failures. Docs: ViewModels/index.md Phase 3 tracker updated in same commit.
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using ytLive.Models;
|
||||
|
||||
namespace ytLive.Services;
|
||||
|
||||
public partial class LayoutStore : IDisposable
|
||||
{
|
||||
public void Save(IEnumerable<Scene> scenes, Webcam? webcam, SocialsConfig? socials, Music? music = null)
|
||||
{
|
||||
using var tx = _connection.BeginTransaction();
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM WebcamSceneConfig;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM Source;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM Webcam;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM SocialEntry;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM Socials;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM Music;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM Scene;";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = """
|
||||
INSERT INTO Scene (Id, Name, IsHidden, IsChatScene, HasBackground, HasSocialBar, SortOrder)
|
||||
VALUES ($id, $name, $isHidden, $isChat, $hasBackground, $hasSocialBar, $sort)
|
||||
""";
|
||||
cmd.Transaction = tx;
|
||||
var idP = cmd.Parameters.Add("$id", SqliteType.Text);
|
||||
var nameP = cmd.Parameters.Add("$name", SqliteType.Text);
|
||||
var hiddenP = cmd.Parameters.Add("$isHidden", SqliteType.Integer);
|
||||
var chatP = cmd.Parameters.Add("$isChat", SqliteType.Integer);
|
||||
var backgroundP = cmd.Parameters.Add("$hasBackground", SqliteType.Integer);
|
||||
var socialBarP = cmd.Parameters.Add("$hasSocialBar", SqliteType.Integer);
|
||||
var sortP = cmd.Parameters.Add("$sort", SqliteType.Integer);
|
||||
|
||||
var sort = 0;
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
idP.Value = scene.Id;
|
||||
nameP.Value = scene.Name;
|
||||
hiddenP.Value = scene.IsHidden ? 1 : 0;
|
||||
chatP.Value = scene.IsChatScene ? 1 : 0;
|
||||
backgroundP.Value = scene.HasBackground ? 1 : 0;
|
||||
socialBarP.Value = scene.HasSocialBar ? 1 : 0;
|
||||
sortP.Value = sort++;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = """
|
||||
INSERT INTO Source (Id, SceneId, AssetId, Type, Name, IsEnabled,
|
||||
X, Y, Width, Height, Opacity, MonitorIndex,
|
||||
ClipShape, IsMirrored, IsBackground, CaptureKey, ShowDesktop, SortOrder,
|
||||
ChatFontSize, ChatFontColor, ChatBackgroundColor, ChatMaxMessages,
|
||||
ChatShowTimestamps, ChatShowBadges, ChatMemberColor, ChatSuperChatColor,
|
||||
WebUri, MediaPath, MediaIsLooping, MediaVolume, MediaPlaybackState)
|
||||
VALUES ($id, $sceneId, $assetId, $type, $name, $isEnabled,
|
||||
$x, $y, $w, $h, $opacity, $monitor,
|
||||
$clip, $mirrored, $isBackground, $captureKey, $showDesktop, $sort,
|
||||
$chatFontSize, $chatFontColor, $chatBgColor, $chatMaxMsgs,
|
||||
$chatTimestamps, $chatBadges, $chatMemberColor, $chatSuperChatColor,
|
||||
$webUri, $mediaPath, $mediaIsLooping, $mediaVolume, $mediaPlaybackState)
|
||||
""";
|
||||
cmd.Transaction = tx;
|
||||
var idP = cmd.Parameters.Add("$id", SqliteType.Text);
|
||||
var sceneIdP = cmd.Parameters.Add("$sceneId", SqliteType.Text);
|
||||
var assetIdP = cmd.Parameters.Add("$assetId", SqliteType.Text);
|
||||
var typeP = cmd.Parameters.Add("$type", SqliteType.Text);
|
||||
var nameP = cmd.Parameters.Add("$name", SqliteType.Text);
|
||||
var enabledP = cmd.Parameters.Add("$isEnabled", SqliteType.Integer);
|
||||
var xP = cmd.Parameters.Add("$x", SqliteType.Real);
|
||||
var yP = cmd.Parameters.Add("$y", SqliteType.Real);
|
||||
var wP = cmd.Parameters.Add("$w", SqliteType.Real);
|
||||
var hP = cmd.Parameters.Add("$h", SqliteType.Real);
|
||||
var opacityP = cmd.Parameters.Add("$opacity", SqliteType.Real);
|
||||
var monitorP = cmd.Parameters.Add("$monitor", SqliteType.Integer);
|
||||
var clipP = cmd.Parameters.Add("$clip", SqliteType.Text);
|
||||
var mirroredP = cmd.Parameters.Add("$mirrored", SqliteType.Integer);
|
||||
var isBackgroundP = cmd.Parameters.Add("$isBackground", SqliteType.Integer);
|
||||
var captureKeyP = cmd.Parameters.Add("$captureKey", SqliteType.Text);
|
||||
var showDesktopP = cmd.Parameters.Add("$showDesktop", SqliteType.Integer);
|
||||
var sortP = cmd.Parameters.Add("$sort", SqliteType.Integer);
|
||||
var chatFontSizeP = cmd.Parameters.Add("$chatFontSize", SqliteType.Real);
|
||||
var chatFontColorP = cmd.Parameters.Add("$chatFontColor", SqliteType.Text);
|
||||
var chatBgColorP = cmd.Parameters.Add("$chatBgColor", SqliteType.Text);
|
||||
var chatMaxMsgsP = cmd.Parameters.Add("$chatMaxMsgs", SqliteType.Integer);
|
||||
var chatTimestampsP = cmd.Parameters.Add("$chatTimestamps", SqliteType.Integer);
|
||||
var chatBadgesP = cmd.Parameters.Add("$chatBadges", SqliteType.Integer);
|
||||
var chatMemberColorP = cmd.Parameters.Add("$chatMemberColor", SqliteType.Text);
|
||||
var chatSuperChatColorP = cmd.Parameters.Add("$chatSuperChatColor", SqliteType.Text);
|
||||
var webUriP = cmd.Parameters.Add("$webUri", SqliteType.Text);
|
||||
var mediaPathP = cmd.Parameters.Add("$mediaPath", SqliteType.Text);
|
||||
var mediaIsLoopingP = cmd.Parameters.Add("$mediaIsLooping", SqliteType.Integer);
|
||||
var mediaVolumeP = cmd.Parameters.Add("$mediaVolume", SqliteType.Real);
|
||||
var mediaPlaybackStateP = cmd.Parameters.Add("$mediaPlaybackState", SqliteType.Text);
|
||||
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
var sort = 0;
|
||||
foreach (var element in scene.Elements)
|
||||
{
|
||||
if (element is not Source source) continue;
|
||||
idP.Value = source.Id;
|
||||
sceneIdP.Value = scene.Id;
|
||||
assetIdP.Value = (object?)source.AssetId ?? DBNull.Value;
|
||||
typeP.Value = source.Type.ToString();
|
||||
nameP.Value = source.Name;
|
||||
enabledP.Value = source.IsEnabled ? 1 : 0;
|
||||
xP.Value = source.X;
|
||||
yP.Value = source.Y;
|
||||
wP.Value = source.Width;
|
||||
hP.Value = source.Height;
|
||||
opacityP.Value = source.Opacity;
|
||||
monitorP.Value = (object?)source.MonitorIndex ?? DBNull.Value;
|
||||
clipP.Value = source.ClipShape.ToString();
|
||||
mirroredP.Value = source.IsMirrored ? 1 : 0;
|
||||
isBackgroundP.Value = source.IsBackground ? 1 : 0;
|
||||
captureKeyP.Value = (object?)source.CaptureKey ?? DBNull.Value;
|
||||
showDesktopP.Value = source.ShowDesktop ? 1 : 0;
|
||||
sortP.Value = sort++;
|
||||
chatFontSizeP.Value = source.ChatFontSize;
|
||||
chatFontColorP.Value = source.ChatFontColor;
|
||||
chatBgColorP.Value = source.ChatBackgroundColor;
|
||||
chatMaxMsgsP.Value = source.ChatMaxMessages;
|
||||
chatTimestampsP.Value = source.ChatShowTimestamps ? 1 : 0;
|
||||
chatBadgesP.Value = source.ChatShowBadges ? 1 : 0;
|
||||
chatMemberColorP.Value = source.ChatMemberColor;
|
||||
chatSuperChatColorP.Value = source.ChatSuperChatColor;
|
||||
webUriP.Value = (object?)source.WebUri ?? DBNull.Value;
|
||||
mediaPathP.Value = (object?)source.MediaPath ?? DBNull.Value;
|
||||
mediaIsLoopingP.Value = source.MediaIsLooping ? 1 : 0;
|
||||
mediaVolumeP.Value = source.MediaVolume;
|
||||
mediaPlaybackStateP.Value = source.MediaPlaybackState.ToString();
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (webcam != null)
|
||||
{
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "INSERT INTO Webcam (Id, DeviceId, Name) VALUES ($id, $device, $name);";
|
||||
cmd.Transaction = tx;
|
||||
cmd.Parameters.AddWithValue("$id", webcam.Id);
|
||||
cmd.Parameters.AddWithValue("$device", webcam.DeviceId);
|
||||
cmd.Parameters.AddWithValue("$name", webcam.Name);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = """
|
||||
INSERT INTO WebcamSceneConfig
|
||||
(SceneId, WebcamId, IsVisible, X, Y, Width, Height, Opacity,
|
||||
ClipShape, IsMirrored, BorderColor, BorderOpacity, BorderWidth, BorderAnimation,
|
||||
RectWidth, RectHeight, SortOrder)
|
||||
VALUES ($sceneId, $webcamId, $isVisible, $x, $y, $w, $h, $opacity,
|
||||
$clip, $mirrored, $borderColor, $borderOpacity, $borderWidth, $borderAnimation,
|
||||
$rectWidth, $rectHeight, $sort)
|
||||
""";
|
||||
cmd.Transaction = tx;
|
||||
var sceneIdP = cmd.Parameters.Add("$sceneId", SqliteType.Text);
|
||||
var webcamIdP = cmd.Parameters.Add("$webcamId", SqliteType.Text);
|
||||
var visibleP = cmd.Parameters.Add("$isVisible", SqliteType.Integer);
|
||||
var xP = cmd.Parameters.Add("$x", SqliteType.Real);
|
||||
var yP = cmd.Parameters.Add("$y", SqliteType.Real);
|
||||
var wP = cmd.Parameters.Add("$w", SqliteType.Real);
|
||||
var hP = cmd.Parameters.Add("$h", SqliteType.Real);
|
||||
var opacityP = cmd.Parameters.Add("$opacity", SqliteType.Real);
|
||||
var clipP = cmd.Parameters.Add("$clip", SqliteType.Text);
|
||||
var mirroredP = cmd.Parameters.Add("$mirrored", SqliteType.Integer);
|
||||
var colorP = cmd.Parameters.Add("$borderColor", SqliteType.Text);
|
||||
var borderOpacityP = cmd.Parameters.Add("$borderOpacity", SqliteType.Real);
|
||||
var borderWidthP = cmd.Parameters.Add("$borderWidth", SqliteType.Integer);
|
||||
var animationP = cmd.Parameters.Add("$borderAnimation", SqliteType.Text);
|
||||
var rectWidthP = cmd.Parameters.Add("$rectWidth", SqliteType.Real);
|
||||
var rectHeightP = cmd.Parameters.Add("$rectHeight", SqliteType.Real);
|
||||
var sortP = cmd.Parameters.Add("$sort", SqliteType.Integer);
|
||||
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
var sort = 0;
|
||||
foreach (var config in scene.Elements.OfType<WebcamSceneConfig>())
|
||||
{
|
||||
sceneIdP.Value = scene.Id;
|
||||
webcamIdP.Value = config.WebcamId;
|
||||
visibleP.Value = config.IsVisible ? 1 : 0;
|
||||
xP.Value = config.X;
|
||||
yP.Value = config.Y;
|
||||
wP.Value = config.Width;
|
||||
hP.Value = config.Height;
|
||||
opacityP.Value = config.Opacity;
|
||||
clipP.Value = config.ClipShape.ToString();
|
||||
mirroredP.Value = config.IsMirrored ? 1 : 0;
|
||||
colorP.Value = (object?)(string.IsNullOrEmpty(config.BorderColor) ? null : config.BorderColor) ?? DBNull.Value;
|
||||
borderOpacityP.Value = config.BorderOpacity;
|
||||
borderWidthP.Value = config.BorderWidth;
|
||||
animationP.Value = config.BorderAnimation.ToString();
|
||||
rectWidthP.Value = (object?)config.RectWidth ?? DBNull.Value;
|
||||
rectHeightP.Value = (object?)config.RectHeight ?? DBNull.Value;
|
||||
sortP.Value = sort++;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (socials != null && socials.Entries.Count > 0)
|
||||
{
|
||||
var socialsId = Guid.NewGuid().ToString();
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "INSERT INTO Socials (Id, BarPosition, BarEnabled) VALUES ($id, $pos, $enabled);";
|
||||
cmd.Transaction = tx;
|
||||
cmd.Parameters.AddWithValue("$id", socialsId);
|
||||
cmd.Parameters.AddWithValue("$pos", socials.BarPosition.ToString());
|
||||
cmd.Parameters.AddWithValue("$enabled", socials.BarEnabled ? 1 : 0);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using var entryCmd = _connection.CreateCommand();
|
||||
entryCmd.CommandText = """
|
||||
INSERT INTO SocialEntry (Id, SocialsId, Service, Handle, ProfileUrl, Software, SortOrder)
|
||||
VALUES ($id, $socialsId, $service, $handle, $url, $software, $sort)
|
||||
""";
|
||||
entryCmd.Transaction = tx;
|
||||
var eId = entryCmd.Parameters.Add("$id", SqliteType.Text);
|
||||
var eSocialsId = entryCmd.Parameters.Add("$socialsId", SqliteType.Text);
|
||||
var eService = entryCmd.Parameters.Add("$service", SqliteType.Text);
|
||||
var eHandle = entryCmd.Parameters.Add("$handle", SqliteType.Text);
|
||||
var eUrl = entryCmd.Parameters.Add("$url", SqliteType.Text);
|
||||
var eSoftware = entryCmd.Parameters.Add("$software", SqliteType.Text);
|
||||
var eSort = entryCmd.Parameters.Add("$sort", SqliteType.Integer);
|
||||
|
||||
var entrySort = 0;
|
||||
foreach (var entry in socials.Entries)
|
||||
{
|
||||
eId.Value = Guid.NewGuid().ToString();
|
||||
eSocialsId.Value = socialsId;
|
||||
eService.Value = entry.Service.ToString();
|
||||
eHandle.Value = entry.Handle;
|
||||
eUrl.Value = entry.ProfileUrl;
|
||||
eSoftware.Value = (object?)entry.FediverseSoftware ?? DBNull.Value;
|
||||
eSort.Value = entrySort++;
|
||||
entryCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
if (music != null && !string.IsNullOrWhiteSpace(music.TrackPath))
|
||||
{
|
||||
using var cmd = _connection.CreateCommand();
|
||||
cmd.CommandText = "INSERT INTO Music (Id, TrackPath, IsEnabled) VALUES ($id, $path, $enabled);";
|
||||
cmd.Transaction = tx;
|
||||
cmd.Parameters.AddWithValue("$id", Guid.NewGuid().ToString());
|
||||
cmd.Parameters.AddWithValue("$path", music.TrackPath);
|
||||
cmd.Parameters.AddWithValue("$enabled", music.IsEnabled ? 1 : 0);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "DELETE FROM Asset WHERE Id NOT IN (SELECT AssetId FROM Source WHERE AssetId IS NOT NULL);";
|
||||
cmd.Transaction = tx;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// Save custom background paths and use-default flags per background source
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
var bg = scene.Elements.OfType<Source>().FirstOrDefault(s => s.IsBackground);
|
||||
if (bg == null) continue;
|
||||
|
||||
var pathKey = $"BackgroundPath_{bg.Id}";
|
||||
if (!string.IsNullOrWhiteSpace(bg.CustomBackgroundPath))
|
||||
UpsertSetting(pathKey, bg.CustomBackgroundPath);
|
||||
else
|
||||
{
|
||||
using var del = _connection.CreateCommand();
|
||||
del.CommandText = "DELETE FROM Settings WHERE Key = $key;";
|
||||
del.Parameters.AddWithValue("$key", pathKey);
|
||||
del.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
var defaultKey = $"BackgroundUseDefault_{bg.Id}";
|
||||
UpsertSetting(defaultKey, bg.UseDefaultBackground ? "1" : "0");
|
||||
}
|
||||
|
||||
// Purge background settings left behind by deleted/duplicated rows
|
||||
// (TASK 25 heal drops extras; their keys must not accumulate).
|
||||
using (var purge = _connection.CreateCommand())
|
||||
{
|
||||
purge.Transaction = tx;
|
||||
purge.CommandText =
|
||||
@"DELETE FROM Settings
|
||||
WHERE (Key LIKE 'BackgroundUseDefault\_%' ESCAPE '\'
|
||||
OR Key LIKE 'BackgroundPath\_%' ESCAPE '\')
|
||||
AND SUBSTR(Key, INSTR(Key, '_') + 1) NOT IN (SELECT Id FROM Source);";
|
||||
purge.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
tx.Commit();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user