namespace AmiReel.Models; /// /// Persisted user settings (%LOCALAPPDATA%\AmiReel\settings.json). Uses named init /// properties rather than a positional constructor: several members share the same /// type (e.g. TrimStart/FadeSeconds/EndCardHoldSeconds), so a /// positional record would let two arguments be silently transposed at a call site /// without the compiler catching it. /// public sealed record AppSettings { public required string OutputFolder { get; init; } public required string EndCardPath { get; init; } public required string FfmpegPath { get; init; } public required string FfprobePath { get; init; } public required string PreviewPlayerPath { get; init; } public required string Theme { get; init; } public required string Encoder { get; init; } public required string TrimStart { get; init; } public required string FadeSeconds { get; init; } public required string EndCardHoldSeconds { get; init; } public required string ThumbnailInterval { get; init; } public bool? MoveSourcesToOriginals { get; init; } public string SourceSearchFolder { get; init; } = ""; public string VideoSourceCropMode { get; init; } = "Auto"; public string VideoCropLeft { get; init; } = "0"; public string VideoCropRight { get; init; } = "0"; public string VideoCropTop { get; init; } = "0"; public string VideoCropBottom { get; init; } = "0"; // Shorts tab defaults (sticky across launches, same as the fields above). public string ShortsStyle { get; init; } = "Brand"; public string ShortsHook { get; init; } = "THIS RAN ON AN AMIGA"; public string ShortsWebsite { get; init; } = "AMIGADB.NET"; public string ShortsFontFile { get; init; } = ""; public string ShortsBackgroundImage { get; init; } = ""; public string ShortsBackgroundVideo { get; init; } = ""; public string ShortsVideoBlur { get; init; } = "0"; public string ShortsVideoOpacity { get; init; } = "1.0"; public string ShortsSourceCropMode { get; init; } = "Auto"; public string ShortsCropLeft { get; init; } = "0"; public string ShortsCropRight { get; init; } = "0"; public string ShortsCropTop { get; init; } = "0"; public string ShortsCropBottom { get; init; } = "0"; public string ShortsSourceMarginX { get; init; } = "40"; public string ShortsSourceFadeIn { get; init; } = "1.5"; public string ShortsCrf { get; init; } = "16"; public string ShortsPreset { get; init; } = "slow"; public bool ShouldMoveSourcesToOriginals => MoveSourcesToOriginals != false; public static AppSettings Default(string outputFolder) => new() { OutputFolder = outputFolder, EndCardPath = "", FfmpegPath = "", FfprobePath = "", PreviewPlayerPath = "", Theme = "Dark", Encoder = "Auto", TrimStart = "4.414", FadeSeconds = "3", EndCardHoldSeconds = "4", ThumbnailInterval = "10", MoveSourcesToOriginals = true, SourceSearchFolder = "", VideoSourceCropMode = "Auto", VideoCropLeft = "0", VideoCropRight = "0", VideoCropTop = "0", VideoCropBottom = "0", }; public AppSettings Normalize(string fallbackOutputFolder) => this with { OutputFolder = string.IsNullOrWhiteSpace(OutputFolder) ? fallbackOutputFolder : OutputFolder, EndCardPath = EndCardPath ?? "", FfmpegPath = FfmpegPath ?? "", FfprobePath = FfprobePath ?? "", PreviewPlayerPath = PreviewPlayerPath ?? "", Theme = string.IsNullOrWhiteSpace(Theme) ? "Dark" : Theme, Encoder = string.IsNullOrWhiteSpace(Encoder) ? "Auto" : Encoder, TrimStart = string.IsNullOrWhiteSpace(TrimStart) ? "4.414" : TrimStart, FadeSeconds = string.IsNullOrWhiteSpace(FadeSeconds) ? "3" : FadeSeconds, EndCardHoldSeconds = string.IsNullOrWhiteSpace(EndCardHoldSeconds) ? "4" : EndCardHoldSeconds, ThumbnailInterval = string.IsNullOrWhiteSpace(ThumbnailInterval) ? "10" : ThumbnailInterval, MoveSourcesToOriginals = MoveSourcesToOriginals ?? true, SourceSearchFolder = SourceSearchFolder ?? "", VideoSourceCropMode = string.IsNullOrWhiteSpace(VideoSourceCropMode) ? "Auto" : VideoSourceCropMode, VideoCropLeft = string.IsNullOrWhiteSpace(VideoCropLeft) ? "0" : VideoCropLeft, VideoCropRight = string.IsNullOrWhiteSpace(VideoCropRight) ? "0" : VideoCropRight, VideoCropTop = string.IsNullOrWhiteSpace(VideoCropTop) ? "0" : VideoCropTop, VideoCropBottom = string.IsNullOrWhiteSpace(VideoCropBottom) ? "0" : VideoCropBottom, ShortsStyle = string.IsNullOrWhiteSpace(ShortsStyle) ? "Brand" : ShortsStyle, ShortsHook = ShortsHook ?? "THIS RAN ON AN AMIGA", ShortsWebsite = ShortsWebsite ?? "AMIGADB.NET", ShortsFontFile = ShortsFontFile ?? "", ShortsBackgroundImage = ShortsBackgroundImage ?? "", ShortsBackgroundVideo = ShortsBackgroundVideo ?? "", ShortsVideoBlur = string.IsNullOrWhiteSpace(ShortsVideoBlur) ? "0" : ShortsVideoBlur, ShortsVideoOpacity = string.IsNullOrWhiteSpace(ShortsVideoOpacity) ? "1.0" : ShortsVideoOpacity, ShortsSourceCropMode = string.IsNullOrWhiteSpace(ShortsSourceCropMode) ? "Auto" : ShortsSourceCropMode, ShortsCropLeft = string.IsNullOrWhiteSpace(ShortsCropLeft) ? "0" : ShortsCropLeft, ShortsCropRight = string.IsNullOrWhiteSpace(ShortsCropRight) ? "0" : ShortsCropRight, ShortsCropTop = string.IsNullOrWhiteSpace(ShortsCropTop) ? "0" : ShortsCropTop, ShortsCropBottom = string.IsNullOrWhiteSpace(ShortsCropBottom) ? "0" : ShortsCropBottom, ShortsSourceMarginX = string.IsNullOrWhiteSpace(ShortsSourceMarginX) ? "40" : ShortsSourceMarginX, ShortsSourceFadeIn = string.IsNullOrWhiteSpace(ShortsSourceFadeIn) ? "1.5" : ShortsSourceFadeIn, ShortsCrf = string.IsNullOrWhiteSpace(ShortsCrf) ? "16" : ShortsCrf, ShortsPreset = string.IsNullOrWhiteSpace(ShortsPreset) ? "slow" : ShortsPreset, }; }