diff --git a/TwitchDownloaderCLI/Models/LogLevel.cs b/TwitchDownloaderCLI/Models/LogLevel.cs new file mode 100644 index 00000000..e472e17a --- /dev/null +++ b/TwitchDownloaderCLI/Models/LogLevel.cs @@ -0,0 +1,16 @@ +using System; + +namespace TwitchDownloaderCLI.Models +{ + [Flags] + internal enum LogLevel + { + None = 1 << 0, + Status = 1 << 1, + Verbose = 1 << 2, + Info = 1 << 3, + Warning = 1 << 4, + Error = 1 << 5, + Ffmpeg = 1 << 6, + } +} \ No newline at end of file diff --git a/TwitchDownloaderCLI/Modes/Arguments/CacheArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/CacheArgs.cs index 95d1ea76..1f9c89bc 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/CacheArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/CacheArgs.cs @@ -3,15 +3,12 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("cache", HelpText = "Manage the working cache")] - public class CacheArgs : ITwitchDownloaderArgs + internal sealed class CacheArgs : TwitchDownloaderArgs { [Option('c', "clear", Default = false, Required = false, HelpText = "Clears the default cache folder.")] public bool ClearCache { get; set; } [Option("force-clear", Default = false, Required = false, HelpText = "Clears the default cache folder, bypassing the confirmation prompt")] public bool ForceClearCache { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } diff --git a/TwitchDownloaderCLI/Modes/Arguments/ChatDownloadArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/ChatDownloadArgs.cs index b83b7ae7..6ac13d2c 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/ChatDownloadArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/ChatDownloadArgs.cs @@ -4,7 +4,7 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("chatdownload", HelpText = "Downloads the chat from a VOD or clip")] - public class ChatDownloadArgs : ITwitchDownloaderArgs + internal sealed class ChatDownloadArgs : TwitchDownloaderArgs { [Option('u', "id", Required = true, HelpText = "The ID or URL of the VOD or clip to download that chat of.")] public string Id { get; set; } @@ -44,8 +44,5 @@ public class ChatDownloadArgs : ITwitchDownloaderArgs [Option("temp-path", Default = "", HelpText = "Path to temporary folder to use for cache.")] public string TempFolder { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } \ No newline at end of file diff --git a/TwitchDownloaderCLI/Modes/Arguments/ChatRenderArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/ChatRenderArgs.cs index b49a1e7d..55f1e37c 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/ChatRenderArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/ChatRenderArgs.cs @@ -3,7 +3,7 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("chatrender", HelpText = "Renders a chat JSON as a video")] - public class ChatRenderArgs : ITwitchDownloaderArgs + internal sealed class ChatRenderArgs : TwitchDownloaderArgs { [Option('i', "input", Required = true, HelpText = "Path to JSON chat file input.")] public string InputFile { get; set; } @@ -151,8 +151,5 @@ public class ChatRenderArgs : ITwitchDownloaderArgs [Option("scale-highlight-indent", Default = 1.0, HelpText = "Number to scale highlight indent size (sub messages).")] public double ScaleAccentIndent { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } \ No newline at end of file diff --git a/TwitchDownloaderCLI/Modes/Arguments/ChatUpdateArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/ChatUpdateArgs.cs index 507291cb..f26ea60b 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/ChatUpdateArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/ChatUpdateArgs.cs @@ -4,7 +4,7 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("chatupdate", HelpText = "Updates the embedded emotes, badges, bits, and crops of a chat download and/or converts a JSON chat to another format.")] - public class ChatUpdateArgs : ITwitchDownloaderArgs + internal sealed class ChatUpdateArgs : TwitchDownloaderArgs { [Option('i', "input", Required = true, HelpText = "Path to input file. Valid extensions are: .json, .json.gz.")] public string InputFile { get; set; } @@ -41,8 +41,5 @@ public class ChatUpdateArgs : ITwitchDownloaderArgs [Option("temp-path", Default = "", HelpText = "Path to temporary folder to use for cache.")] public string TempFolder { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } diff --git a/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs index bb88c7df..ebbdf978 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs @@ -3,7 +3,7 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("clipdownload", HelpText = "Downloads a clip from Twitch")] - public class ClipDownloadArgs : ITwitchDownloaderArgs + internal sealed class ClipDownloadArgs : TwitchDownloaderArgs { [Option('u', "id", Required = true, HelpText = "The ID or URL of the clip to download.")] public string Id { get; set; } @@ -25,8 +25,5 @@ public class ClipDownloadArgs : ITwitchDownloaderArgs [Option("temp-path", Default = "", HelpText = "Path to temporary caching folder.")] public string TempFolder { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } \ No newline at end of file diff --git a/TwitchDownloaderCLI/Modes/Arguments/FfmpegArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/FfmpegArgs.cs index 8abe5d64..c3988428 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/FfmpegArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/FfmpegArgs.cs @@ -3,12 +3,9 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("ffmpeg", HelpText = "Manage standalone ffmpeg")] - public class FfmpegArgs : ITwitchDownloaderArgs + internal sealed class FfmpegArgs : TwitchDownloaderArgs { [Option('d', "download", Default = false, Required = false, HelpText = "Downloads FFmpeg as a standalone file.")] public bool DownloadFfmpeg { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } diff --git a/TwitchDownloaderCLI/Modes/Arguments/ITwitchDownloaderArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/ITwitchDownloaderArgs.cs deleted file mode 100644 index 6c272186..00000000 --- a/TwitchDownloaderCLI/Modes/Arguments/ITwitchDownloaderArgs.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace TwitchDownloaderCLI.Modes.Arguments -{ - public interface ITwitchDownloaderArgs - { - // TODO: Mode --silent here - public bool? ShowBanner { get; set; } - } -} \ No newline at end of file diff --git a/TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs index 1b7fffbe..a19bdf0b 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs @@ -3,15 +3,12 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("tsmerge", HelpText = "Concatenates multiple .ts/.tsv/.tsa/.m2t/.m2ts (MPEG Transport Stream) files into a single file")] - public class TsMergeArgs : ITwitchDownloaderArgs + internal sealed class TsMergeArgs : TwitchDownloaderArgs { [Option('i', "input", Required = true, HelpText = "Path a text file containing the absolute paths of the files to concatenate, separated by newlines. M3U/M3U8 is also supported.")] public string InputList { get; set; } [Option('o', "output", Required = true, HelpText = "Path to output file.")] public string OutputFile { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } diff --git a/TwitchDownloaderCLI/Modes/Arguments/TwitchDownloaderArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/TwitchDownloaderArgs.cs new file mode 100644 index 00000000..fee8dfcc --- /dev/null +++ b/TwitchDownloaderCLI/Modes/Arguments/TwitchDownloaderArgs.cs @@ -0,0 +1,14 @@ +using CommandLine; +using TwitchDownloaderCLI.Models; + +namespace TwitchDownloaderCLI.Modes.Arguments +{ + internal abstract class TwitchDownloaderArgs + { + [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] + public bool? ShowBanner { get; set; } + + [Option("log-level", Default = Models.LogLevel.Status | LogLevel.Info | LogLevel.Warning | LogLevel.Error, HelpText = "Sets the log level flags. Applicable values are: None, Status, Verbose, Info, Warning, Error, Ffmpeg.")] + public LogLevel LogLevel { get; set; } + } +} \ No newline at end of file diff --git a/TwitchDownloaderCLI/Modes/Arguments/VideoDownloadArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/VideoDownloadArgs.cs index 9acd96d1..b30d6c99 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/VideoDownloadArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/VideoDownloadArgs.cs @@ -3,7 +3,7 @@ namespace TwitchDownloaderCLI.Modes.Arguments { [Verb("videodownload", HelpText = "Downloads a stream VOD from Twitch")] - public class VideoDownloadArgs : ITwitchDownloaderArgs + internal sealed class VideoDownloadArgs : TwitchDownloaderArgs { [Option('u', "id", Required = true, HelpText = "The ID or URL of the VOD to download.")] public string Id { get; set; } @@ -34,8 +34,5 @@ public class VideoDownloadArgs : ITwitchDownloaderArgs [Option("temp-path", Default = "", HelpText = "Path to temporary caching folder.")] public string TempFolder { get; set; } - - [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] - public bool? ShowBanner { get; set; } } } diff --git a/TwitchDownloaderCLI/Modes/CacheHandler.cs b/TwitchDownloaderCLI/Modes/CacheHandler.cs index 127d4b54..28953a68 100644 --- a/TwitchDownloaderCLI/Modes/CacheHandler.cs +++ b/TwitchDownloaderCLI/Modes/CacheHandler.cs @@ -4,7 +4,7 @@ namespace TwitchDownloaderCLI.Modes { - public static class CacheHandler + internal static class CacheHandler { public static void ParseArgs(CacheArgs args) { diff --git a/TwitchDownloaderCLI/Modes/DownloadChat.cs b/TwitchDownloaderCLI/Modes/DownloadChat.cs index 0f995ccb..95c312ef 100644 --- a/TwitchDownloaderCLI/Modes/DownloadChat.cs +++ b/TwitchDownloaderCLI/Modes/DownloadChat.cs @@ -4,6 +4,7 @@ using TwitchDownloaderCLI.Modes.Arguments; using TwitchDownloaderCLI.Tools; using TwitchDownloaderCore; +using TwitchDownloaderCore.Interfaces; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; @@ -13,26 +14,26 @@ internal static class DownloadChat { internal static void Download(ChatDownloadArgs inputOptions) { - var downloadOptions = GetDownloadOptions(inputOptions); + var progress = new CliTaskProgress(inputOptions.LogLevel); - var progress = new CliTaskProgress(); + var downloadOptions = GetDownloadOptions(inputOptions, progress); var chatDownloader = new ChatDownloader(downloadOptions, progress); chatDownloader.DownloadAsync(CancellationToken.None).Wait(); } - private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOptions) + private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOptions, ITaskLogger logger) { if (inputOptions.Id is null) { - Console.WriteLine("[ERROR] - Vod/Clip ID/URL cannot be null!"); + logger.LogError("Vod/Clip ID/URL cannot be null!"); Environment.Exit(1); } var vodClipIdMatch = TwitchRegex.MatchVideoOrClipId(inputOptions.Id); if (vodClipIdMatch is not { Success: true }) { - Console.WriteLine("[ERROR] - Unable to parse Vod/Clip ID/URL."); + logger.LogError("Unable to parse Vod/Clip ID/URL."); Environment.Exit(1); } diff --git a/TwitchDownloaderCLI/Modes/DownloadClip.cs b/TwitchDownloaderCLI/Modes/DownloadClip.cs index 7e2dac5f..8f2f3a28 100644 --- a/TwitchDownloaderCLI/Modes/DownloadClip.cs +++ b/TwitchDownloaderCLI/Modes/DownloadClip.cs @@ -4,6 +4,7 @@ using TwitchDownloaderCLI.Modes.Arguments; using TwitchDownloaderCLI.Tools; using TwitchDownloaderCore; +using TwitchDownloaderCore.Interfaces; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; @@ -13,31 +14,31 @@ internal static class DownloadClip { internal static void Download(ClipDownloadArgs inputOptions) { - var progress = new CliTaskProgress(); + var progress = new CliTaskProgress(inputOptions.LogLevel); if (inputOptions.EncodeMetadata == true) { FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress); } - var downloadOptions = GetDownloadOptions(inputOptions); + var downloadOptions = GetDownloadOptions(inputOptions, progress); var clipDownloader = new ClipDownloader(downloadOptions, progress); clipDownloader.DownloadAsync(new CancellationToken()).Wait(); } - private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOptions) + private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOptions, ITaskLogger logger) { if (inputOptions.Id is null) { - Console.WriteLine("[ERROR] - Clip ID/URL cannot be null!"); + logger.LogError("Clip ID/URL cannot be null!"); Environment.Exit(1); } var clipIdMatch = TwitchRegex.MatchClipId(inputOptions.Id); if (clipIdMatch is not { Success: true }) { - Console.WriteLine("[ERROR] - Unable to parse Clip ID/URL."); + logger.LogError("Unable to parse Clip ID/URL."); Environment.Exit(1); } diff --git a/TwitchDownloaderCLI/Modes/DownloadVideo.cs b/TwitchDownloaderCLI/Modes/DownloadVideo.cs index 855ac6d2..32dd77cb 100644 --- a/TwitchDownloaderCLI/Modes/DownloadVideo.cs +++ b/TwitchDownloaderCLI/Modes/DownloadVideo.cs @@ -4,6 +4,7 @@ using TwitchDownloaderCLI.Modes.Arguments; using TwitchDownloaderCLI.Tools; using TwitchDownloaderCore; +using TwitchDownloaderCore.Interfaces; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; @@ -13,36 +14,28 @@ internal static class DownloadVideo { internal static void Download(VideoDownloadArgs inputOptions) { - var progress = new CliTaskProgress(); + var progress = new CliTaskProgress(inputOptions.LogLevel); FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress); - var downloadOptions = GetDownloadOptions(inputOptions); - downloadOptions.CacheCleanerCallback = directoryInfos => - { - progress.LogInfo( - $"{directoryInfos.Length} unmanaged video caches were found at '{downloadOptions.TempFolder}' and can be safely deleted. " + - "Run 'TwitchDownloaderCLI cache help' for more information."); - - return Array.Empty(); - }; + var downloadOptions = GetDownloadOptions(inputOptions, progress); var videoDownloader = new VideoDownloader(downloadOptions, progress); videoDownloader.DownloadAsync(new CancellationToken()).Wait(); } - private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOptions) + private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOptions, ITaskLogger logger) { if (inputOptions.Id is null) { - Console.WriteLine("[ERROR] - Vod ID/URL cannot be null!"); + logger.LogError("Vod ID/URL cannot be null!"); Environment.Exit(1); } var vodIdMatch = TwitchRegex.MatchVideoId(inputOptions.Id); if (vodIdMatch is not { Success: true }) { - Console.WriteLine("[ERROR] - Unable to parse Vod ID/URL."); + logger.LogError("Unable to parse Vod ID/URL."); Environment.Exit(1); } @@ -74,7 +67,15 @@ private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOp CropEnding = inputOptions.CropEndingTime > 0.0, CropEndingTime = TimeSpan.FromSeconds(inputOptions.CropEndingTime), FfmpegPath = string.IsNullOrWhiteSpace(inputOptions.FfmpegPath) ? FfmpegHandler.FfmpegExecutableName : Path.GetFullPath(inputOptions.FfmpegPath), - TempFolder = inputOptions.TempFolder + TempFolder = inputOptions.TempFolder, + CacheCleanerCallback = directoryInfos => + { + logger.LogInfo( + $"{directoryInfos.Length} unmanaged video caches were found at '{inputOptions.TempFolder}' and can be safely deleted. " + + "Run 'TwitchDownloaderCLI cache help' for more information."); + + return Array.Empty(); + } }; return downloadOptions; diff --git a/TwitchDownloaderCLI/Modes/FfmpegHandler.cs b/TwitchDownloaderCLI/Modes/FfmpegHandler.cs index d012f006..07319990 100644 --- a/TwitchDownloaderCLI/Modes/FfmpegHandler.cs +++ b/TwitchDownloaderCLI/Modes/FfmpegHandler.cs @@ -13,23 +13,29 @@ namespace TwitchDownloaderCLI.Modes { - public static class FfmpegHandler + internal static class FfmpegHandler { public static readonly string FfmpegExecutableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "ffmpeg.exe" : "ffmpeg"; public static void ParseArgs(FfmpegArgs args) { + var progress = new CliTaskProgress(args.LogLevel); + if (args.DownloadFfmpeg) { - DownloadFfmpeg(); + DownloadFfmpeg(progress); } } - private static void DownloadFfmpeg() + private static void DownloadFfmpeg(ITaskProgress progress) { - Console.Write("[INFO] - Downloading FFmpeg"); + if (File.Exists(FfmpegExecutableName)) + { + progress.LogInfo("FFmpeg was already found in the current working directory."); + return; + } - using var progressHandler = new XabeProgressHandler(); + using var progressHandler = new XabeProgressHandler(progress); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -52,8 +58,8 @@ private static void DownloadFfmpeg() } catch { - Console.WriteLine("[ERROR] - Unable to update FFmpeg file permissions. Run '{0}' if further FFmpeg errors occur.", - RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "chmod +x ffmpeg" : "sudo chmod +x ffmpeg"); + var chmodCommand = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "chmod +x ffmpeg" : "sudo chmod +x ffmpeg"; + progress.LogError($"Unable to update FFmpeg file permissions. Run '{chmodCommand}' if further FFmpeg errors occur."); } } @@ -74,17 +80,14 @@ private sealed class XabeProgressHandler : IProgress, IDisposable private readonly ConcurrentQueue _percentQueue = new(); private readonly Timer _timer; - public XabeProgressHandler() + // This may seem overly complicated, but it removes the expensive console writes from the thread that is downloading FFmpeg + public XabeProgressHandler(ITaskProgress progress) { - _timer = new Timer(Callback, _percentQueue, 0, 100); - - static void Callback(object state) + progress.SetTemplateStatus("Downloading FFmpeg {0}%", 0); + _timer = new Timer(_ => { - if (state is not ConcurrentQueue { IsEmpty: false } queue) return; - - var currentPercent = queue.Max(); - Console.Write($"\r[INFO] - Downloading FFmpeg {currentPercent}%"); - } + progress.ReportProgress(_percentQueue.Max()); + }, null, 0, 100); } public void Report(ProgressInfo value) diff --git a/TwitchDownloaderCLI/Modes/MergeTs.cs b/TwitchDownloaderCLI/Modes/MergeTs.cs index baacb6f5..2f717304 100644 --- a/TwitchDownloaderCLI/Modes/MergeTs.cs +++ b/TwitchDownloaderCLI/Modes/MergeTs.cs @@ -10,7 +10,7 @@ internal static class MergeTs { internal static void Merge(TsMergeArgs inputOptions) { - var progress = new CliTaskProgress(); + var progress = new CliTaskProgress(inputOptions.LogLevel); progress.LogInfo("The TS merger is experimental and is subject to change without notice in future releases."); diff --git a/TwitchDownloaderCLI/Modes/RenderChat.cs b/TwitchDownloaderCLI/Modes/RenderChat.cs index c5a65a79..7f495d4e 100644 --- a/TwitchDownloaderCLI/Modes/RenderChat.cs +++ b/TwitchDownloaderCLI/Modes/RenderChat.cs @@ -6,6 +6,7 @@ using TwitchDownloaderCLI.Tools; using TwitchDownloaderCore; using TwitchDownloaderCore.Chat; +using TwitchDownloaderCore.Interfaces; using TwitchDownloaderCore.Options; namespace TwitchDownloaderCLI.Modes @@ -14,17 +15,17 @@ internal static class RenderChat { internal static void Render(ChatRenderArgs inputOptions) { - var progress = new CliTaskProgress(); + var progress = new CliTaskProgress(inputOptions.LogLevel); FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress); - var renderOptions = GetRenderOptions(inputOptions); + var renderOptions = GetRenderOptions(inputOptions, progress); using var chatRenderer = new ChatRenderer(renderOptions, progress); chatRenderer.ParseJsonAsync().Wait(); chatRenderer.RenderVideoAsync(new CancellationToken()).Wait(); } - private static ChatRenderOptions GetRenderOptions(ChatRenderArgs inputOptions) + private static ChatRenderOptions GetRenderOptions(ChatRenderArgs inputOptions, ITaskLogger logger) { ChatRenderOptions renderOptions = new() { @@ -96,12 +97,12 @@ private static ChatRenderOptions GetRenderOptions(ChatRenderArgs inputOptions) if (renderOptions.GenerateMask && renderOptions.BackgroundColor.Alpha == 255 && !(renderOptions.AlternateMessageBackgrounds! && renderOptions.AlternateBackgroundColor.Alpha != 255)) { - Console.WriteLine("[WARNING] - Generate mask option has been selected with an opaque background. You most likely want to set a transparent background with --background-color \"#00000000\""); + logger.LogWarning("Generate mask option has been selected with an opaque background. You most likely want to set a transparent background with --background-color \"#00000000\""); } if (renderOptions.ChatHeight % 2 != 0 || renderOptions.ChatWidth % 2 != 0) { - Console.WriteLine("[WARNING] - Width and Height MUST be even, rounding up to the nearest even number to prevent errors"); + logger.LogWarning("Width and Height MUST be even, rounding up to the nearest even number to prevent errors"); if (renderOptions.ChatHeight % 2 != 0) { renderOptions.ChatHeight++; diff --git a/TwitchDownloaderCLI/Modes/UpdateChat.cs b/TwitchDownloaderCLI/Modes/UpdateChat.cs index f448c906..a06cca71 100644 --- a/TwitchDownloaderCLI/Modes/UpdateChat.cs +++ b/TwitchDownloaderCLI/Modes/UpdateChat.cs @@ -4,6 +4,7 @@ using TwitchDownloaderCLI.Modes.Arguments; using TwitchDownloaderCLI.Tools; using TwitchDownloaderCore; +using TwitchDownloaderCore.Interfaces; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; @@ -13,20 +14,20 @@ internal static class UpdateChat { internal static void Update(ChatUpdateArgs inputOptions) { - var updateOptions = GetUpdateOptions(inputOptions); + var progress = new CliTaskProgress(inputOptions.LogLevel); - var progress = new CliTaskProgress(); + var updateOptions = GetUpdateOptions(inputOptions, progress); var chatUpdater = new ChatUpdater(updateOptions, progress); chatUpdater.ParseJsonAsync().Wait(); chatUpdater.UpdateAsync(new CancellationToken()).Wait(); } - private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions) + private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions, ITaskLogger logger) { if (!File.Exists(inputOptions.InputFile)) { - Console.WriteLine("[ERROR] - Input file does not exist!"); + logger.LogError("Input file does not exist!"); Environment.Exit(1); } @@ -48,13 +49,13 @@ private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions) }; if (inFormat != ChatFormat.Json) { - Console.WriteLine("[ERROR] - Input file must be .json or .json.gz!"); + logger.LogError("Input file must be .json or .json.gz!"); Environment.Exit(1); } if (Path.GetFullPath(inputOptions.InputFile!) == Path.GetFullPath(inputOptions.OutputFile!)) { - Console.WriteLine("[WARNING] - Output file path is identical to input file. This is not recommended in case something goes wrong. All data will be permanently overwritten!"); + logger.LogWarning("Output file path is identical to input file. This is not recommended in case something goes wrong. All data will be permanently overwritten!"); } ChatUpdateOptions updateOptions = new() diff --git a/TwitchDownloaderCLI/Program.cs b/TwitchDownloaderCLI/Program.cs index 4e40a757..3149b62d 100644 --- a/TwitchDownloaderCLI/Program.cs +++ b/TwitchDownloaderCLI/Program.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Runtime.InteropServices; using CommandLine.Text; +using TwitchDownloaderCLI.Models; using TwitchDownloaderCLI.Modes; using TwitchDownloaderCLI.Modes.Arguments; using TwitchDownloaderCLI.Tools; @@ -28,7 +29,7 @@ private static void Main(string[] args) parserResult.WithNotParsed(errors => WriteHelpText(errors, parserResult, parser.Settings)); CoreLicensor.EnsureFilesExist(AppContext.BaseDirectory); - WriteApplicationBanner((ITwitchDownloaderArgs)parserResult.Value, args); + WriteApplicationBanner((TwitchDownloaderArgs)parserResult.Value); parserResult .WithParsed(DownloadVideo.Download) @@ -73,9 +74,9 @@ private static void WriteHelpText(IEnumerable errors, ParserResult' or double quotes ". i.e. `--output 'my output file.mp4'` or `--output "my output file.mp4"` +String arguments that contain spaces should be wrapped in either single quotes ' or double quotes " depending on your shell. i.e. `--output 'my output file.mp4'` or `--output "my output file.mp4"` Default true boolean flags must be assigned: `--default-true-flag=false`. Default false boolean flags should still be raised normally: `--default-false-flag`. +Enum flag arguments may be assigned as `--flag Value1,Value2,Value3` or `--flag "Value1, Value2, Value3"`. + For Linux users, ensure both `fontconfig` and `libfontconfig1` are installed. `apt-get install fontconfig libfontconfig1` on Ubuntu. Some distros, like Linux Alpine, lack fonts for some languages (Arabic, Persian, Thai, etc.) If this is the case for you, install additional fonts families such as [Noto](https://fonts.google.com/noto/specimen/Noto+Sans) or check your distro's wiki page on fonts as it may have an install command for this specific scenario, such as the [Linux Alpine](https://wiki.alpinelinux.org/wiki/Fonts) font page. diff --git a/TwitchDownloaderCLI/Tools/CliTaskProgress.cs b/TwitchDownloaderCLI/Tools/CliTaskProgress.cs index 98dadb04..c0fa4a33 100644 --- a/TwitchDownloaderCLI/Tools/CliTaskProgress.cs +++ b/TwitchDownloaderCLI/Tools/CliTaskProgress.cs @@ -1,9 +1,11 @@ using System; +using System.Runtime.CompilerServices; +using TwitchDownloaderCLI.Models; using TwitchDownloaderCore.Interfaces; namespace TwitchDownloaderCLI.Tools { - public class CliTaskProgress : ITaskProgress + internal class CliTaskProgress : ITaskProgress { private const string STATUS_PREAMBLE = "[STATUS] - "; private const string VERBOSE_LOG_PREAMBLE = "[VERBOSE] - "; @@ -21,13 +23,20 @@ public class CliTaskProgress : ITaskProgress private TimeSpan _lastTime1 = new(-1); private TimeSpan _lastTime2 = new(-1); - public CliTaskProgress() + private readonly LogLevel _logLevel; + + public CliTaskProgress(LogLevel logLevel) { - // TODO: Take in ITwitchDownloaderArgs to configure log levels + if ((logLevel & LogLevel.None) == 0) + { + _logLevel = logLevel; + } } public void SetStatus(string status) { + if ((_logLevel & LogLevel.Status) == 0) return; + lock (this) { _status = status; @@ -39,6 +48,8 @@ public void SetStatus(string status) public void SetTemplateStatus(string status, int initialPercent) { + if ((_logLevel & LogLevel.Status) == 0) return; + lock (this) { _status = status; @@ -56,6 +67,8 @@ public void SetTemplateStatus(string status, int initialPercent) public void SetTemplateStatus(string status, int initialPercent, TimeSpan initialTime1, TimeSpan initialTime2) { + if ((_logLevel & LogLevel.Status) == 0) return; + lock (this) { _status = status; @@ -73,6 +86,8 @@ public void SetTemplateStatus(string status, int initialPercent, TimeSpan initia public void ReportProgress(int percent) { + if ((_logLevel & LogLevel.Status) == 0) return; + lock (this) { if ((!_lastWriteHadNewLine && _lastPercent == percent) @@ -91,6 +106,8 @@ public void ReportProgress(int percent) public void ReportProgress(int percent, TimeSpan time1, TimeSpan time2) { + if ((_logLevel & LogLevel.Status) == 0) return; + lock (this) { if ((!_lastWriteHadNewLine && _lastPercent == percent && _lastTime1 == time1 && _lastTime2 == time2) @@ -134,38 +151,88 @@ private int WriteSameLineMessage(string preamble, string message, int previousMe public void LogVerbose(string logMessage) { + if ((_logLevel & LogLevel.Verbose) == 0) return; + lock (this) { - // WriteNewLineMessage(VERBOSE_LOG_PREAMBLE, logMessage); + WriteNewLineMessage(VERBOSE_LOG_PREAMBLE, logMessage); + } + } + + public void LogVerbose(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Verbose) == 0) return; + + lock (this) + { + WriteNewLineMessage(VERBOSE_LOG_PREAMBLE, logMessage.ToStringAndClear()); } } public void LogInfo(string logMessage) { + if ((_logLevel & LogLevel.Info) == 0) return; + lock (this) { WriteNewLineMessage(INFO_LOG_PREAMBLE, logMessage); } } + public void LogInfo(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Info) == 0) return; + + lock (this) + { + WriteNewLineMessage(INFO_LOG_PREAMBLE, logMessage.ToStringAndClear()); + } + } + public void LogWarning(string logMessage) { + if ((_logLevel & LogLevel.Warning) == 0) return; + lock (this) { WriteNewLineMessage(WARNING_LOG_PREAMBLE, logMessage); } } + public void LogWarning(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Warning) == 0) return; + + lock (this) + { + WriteNewLineMessage(WARNING_LOG_PREAMBLE, logMessage.ToStringAndClear()); + } + } + public void LogError(string logMessage) { + if ((_logLevel & LogLevel.Error) == 0) return; + lock (this) { WriteNewLineMessage(ERROR_LOG_PREAMBLE, logMessage); } } + public void LogError(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Error) == 0) return; + + lock (this) + { + WriteNewLineMessage(ERROR_LOG_PREAMBLE, logMessage.ToStringAndClear()); + } + } + public void LogFfmpeg(string logMessage) { + if ((_logLevel & LogLevel.Ffmpeg) == 0) return; + lock (this) { WriteNewLineMessage(FFMPEG_LOG_PREAMBLE, logMessage); diff --git a/TwitchDownloaderCLI/Tools/PathUtils.cs b/TwitchDownloaderCLI/Tools/PathUtils.cs index 92e70e98..f83307ac 100644 --- a/TwitchDownloaderCLI/Tools/PathUtils.cs +++ b/TwitchDownloaderCLI/Tools/PathUtils.cs @@ -3,7 +3,7 @@ namespace TwitchDownloaderCLI.Tools { - public static class PathUtils + internal static class PathUtils { // https://stackoverflow.com/a/3856090/12204538 public static bool ExistsOnPATH(string fileName) diff --git a/TwitchDownloaderCLI/Tools/PreParseArgs.cs b/TwitchDownloaderCLI/Tools/PreParseArgs.cs index 7161ab22..af4f892d 100644 --- a/TwitchDownloaderCLI/Tools/PreParseArgs.cs +++ b/TwitchDownloaderCLI/Tools/PreParseArgs.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using TwitchDownloaderCLI.Models; namespace TwitchDownloaderCLI.Tools { @@ -8,7 +9,7 @@ internal static class PreParseArgs { internal static string[] Parse(string[] args, string processFileName) { - if (args.Any(x => x is "-m" or "--mode" or "--embed-emotes")) + if (args.Any(x => x is "-m" or "--mode" or "--embed-emotes" or "--silent")) { // A legacy syntax was used, convert to new syntax return Process(ConvertFromOldSyntax(args, processFileName)); @@ -46,6 +47,12 @@ private static string[] ConvertFromOldSyntax(string[] args, string processFileNa processedArgs = ConvertModeSyntax(processedArgs); } + if (args.Any(x => x is "--silent")) + { + Console.WriteLine("[INFO] The program has switched from --silent to log levels, consider using log levels instead. '--log-level None' will be applied to the current session. Run \'{0} help\' for more information.", processFileName); + processedArgs = ConvertSilentSyntax(processedArgs); + } + return processedArgs.ToArray(); } @@ -86,5 +93,19 @@ private static List ConvertModeSyntax(List args) return processedArgs.ToList(); } + + private static List ConvertSilentSyntax(List args) + { + for (var i = 0; i < args.Count; i++) + { + if (args[i].Equals("--silent")) + { + args[i] = "--log-level"; + args.Insert(i + 1, nameof(LogLevel.None)); + } + } + + return args; + } } } diff --git a/TwitchDownloaderCore/Interfaces/ITaskLogger.cs b/TwitchDownloaderCore/Interfaces/ITaskLogger.cs index c04032f3..facfcc20 100644 --- a/TwitchDownloaderCore/Interfaces/ITaskLogger.cs +++ b/TwitchDownloaderCore/Interfaces/ITaskLogger.cs @@ -1,12 +1,18 @@ +using System.Runtime.CompilerServices; + namespace TwitchDownloaderCore.Interfaces { public interface ITaskLogger { // TODO: Add DefaultInterpolatedStringHandler overloads once log levels are implemented for zero-alloc logging void LogVerbose(string logMessage); + void LogVerbose(DefaultInterpolatedStringHandler logMessage); void LogInfo(string logMessage); + void LogInfo(DefaultInterpolatedStringHandler logMessage); void LogWarning(string logMessage); + void LogWarning(DefaultInterpolatedStringHandler logMessage); void LogError(string logMessage); + void LogError(DefaultInterpolatedStringHandler logMessage); void LogFfmpeg(string logMessage); } } \ No newline at end of file diff --git a/TwitchDownloaderCore/Tools/StubTaskProgress.cs b/TwitchDownloaderCore/Tools/StubTaskProgress.cs index 854cc870..a044430e 100644 --- a/TwitchDownloaderCore/Tools/StubTaskProgress.cs +++ b/TwitchDownloaderCore/Tools/StubTaskProgress.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using TwitchDownloaderCore.Interfaces; namespace TwitchDownloaderCore.Tools @@ -11,12 +12,20 @@ private StubTaskProgress() { } public void LogVerbose(string logMessage) { } + public void LogVerbose(DefaultInterpolatedStringHandler logMessage) { } + public void LogInfo(string logMessage) { } + public void LogInfo(DefaultInterpolatedStringHandler logMessage) { } + public void LogWarning(string logMessage) { } + public void LogWarning(DefaultInterpolatedStringHandler logMessage) { } + public void LogError(string logMessage) { } + public void LogError(DefaultInterpolatedStringHandler logMessage) { } + public void LogFfmpeg(string logMessage) { } public void SetStatus(string status) { } diff --git a/TwitchDownloaderWPF/App.config b/TwitchDownloaderWPF/App.config index c8f358de..1786dc55 100644 --- a/TwitchDownloaderWPF/App.config +++ b/TwitchDownloaderWPF/App.config @@ -226,6 +226,9 @@ True + + 14 + \ No newline at end of file diff --git a/TwitchDownloaderWPF/Models/LogLevel.cs b/TwitchDownloaderWPF/Models/LogLevel.cs new file mode 100644 index 00000000..e3138023 --- /dev/null +++ b/TwitchDownloaderWPF/Models/LogLevel.cs @@ -0,0 +1,15 @@ +using System; + +namespace TwitchDownloaderWPF.Models +{ + [Flags] + internal enum LogLevel + { + None = 0, + Verbose = 1 << 0, + Info = 1 << 1, + Warning = 1 << 2, + Error = 1 << 3, + Ffmpeg = 1 << 4, + } +} \ No newline at end of file diff --git a/TwitchDownloaderWPF/PageChatDownload.xaml.cs b/TwitchDownloaderWPF/PageChatDownload.xaml.cs index f570ec91..f1dc7cc6 100644 --- a/TwitchDownloaderWPF/PageChatDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageChatDownload.xaml.cs @@ -13,6 +13,7 @@ using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; using TwitchDownloaderCore.TwitchObjects.Gql; +using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Services; using TwitchDownloaderWPF.Utils; @@ -517,7 +518,7 @@ private async void SplitBtnDownload_Click(object sender, RoutedEventArgs e) else if (radioTimestampNone.IsChecked == true) downloadOptions.TimeFormat = TimestampFormat.None; - var downloadProgress = new WpfTaskProgress(SetPercent, SetStatus, AppendLog); + var downloadProgress = new WpfTaskProgress((LogLevel)Settings.Default.LogLevels, SetPercent, SetStatus, AppendLog); var currentDownload = new ChatDownloader(downloadOptions, downloadProgress); btnGetInfo.IsEnabled = false; diff --git a/TwitchDownloaderWPF/PageChatRender.xaml.cs b/TwitchDownloaderWPF/PageChatRender.xaml.cs index 56ad929f..ed1e8e93 100644 --- a/TwitchDownloaderWPF/PageChatRender.xaml.cs +++ b/TwitchDownloaderWPF/PageChatRender.xaml.cs @@ -18,6 +18,7 @@ using TwitchDownloaderCore.Chat; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.TwitchObjects; +using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Utils; using WpfAnimatedGif; @@ -598,7 +599,7 @@ private async void SplitBtnRender_Click(object sender, RoutedEventArgs e) ChatRenderOptions options = GetOptions(saveFileDialog.FileName); - var renderProgress = new WpfTaskProgress(SetPercent, SetStatus, AppendLog, s => ffmpegLog.Add(s)); + var renderProgress = new WpfTaskProgress((LogLevel)Settings.Default.LogLevels, SetPercent, SetStatus, AppendLog, s => ffmpegLog.Add(s)); ChatRenderer currentRender = new ChatRenderer(options, renderProgress); try { diff --git a/TwitchDownloaderWPF/PageChatUpdate.xaml.cs b/TwitchDownloaderWPF/PageChatUpdate.xaml.cs index 64b99f4c..49bd926e 100644 --- a/TwitchDownloaderWPF/PageChatUpdate.xaml.cs +++ b/TwitchDownloaderWPF/PageChatUpdate.xaml.cs @@ -15,6 +15,7 @@ using TwitchDownloaderCore.Tools; using TwitchDownloaderCore.TwitchObjects; using TwitchDownloaderCore.TwitchObjects.Gql; +using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Services; using TwitchDownloaderWPF.Utils; @@ -522,7 +523,7 @@ private async void SplitBtnUpdate_Click(object sender, RoutedEventArgs e) { ChatUpdateOptions updateOptions = GetOptions(saveFileDialog.FileName); - var updateProgress = new WpfTaskProgress(SetPercent, SetStatus, AppendLog); + var updateProgress = new WpfTaskProgress((LogLevel)Settings.Default.LogLevels, SetPercent, SetStatus, AppendLog); var currentUpdate = new ChatUpdater(updateOptions, updateProgress); try { diff --git a/TwitchDownloaderWPF/PageClipDownload.xaml.cs b/TwitchDownloaderWPF/PageClipDownload.xaml.cs index e78e22ad..08aff9fd 100644 --- a/TwitchDownloaderWPF/PageClipDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageClipDownload.xaml.cs @@ -13,6 +13,7 @@ using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; using TwitchDownloaderCore.TwitchObjects.Gql; +using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Services; using TwitchDownloaderWPF.Utils; @@ -213,7 +214,7 @@ private async void SplitBtnDownload_Click(object sender, RoutedEventArgs e) ClipDownloadOptions downloadOptions = GetOptions(saveFileDialog.FileName); _cancellationTokenSource = new CancellationTokenSource(); - var downloadProgress = new WpfTaskProgress(SetPercent, SetStatus, AppendLog); + var downloadProgress = new WpfTaskProgress((LogLevel)Settings.Default.LogLevels, SetPercent, SetStatus, AppendLog); var currentDownload = new ClipDownloader(downloadOptions, downloadProgress); SetImage("Images/ppOverheat.gif", true); diff --git a/TwitchDownloaderWPF/PageVodDownload.xaml.cs b/TwitchDownloaderWPF/PageVodDownload.xaml.cs index 9202c23e..e5c0ef4a 100644 --- a/TwitchDownloaderWPF/PageVodDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageVodDownload.xaml.cs @@ -18,6 +18,7 @@ using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; using TwitchDownloaderCore.TwitchObjects.Gql; +using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Services; using TwitchDownloaderWPF.Utils; @@ -424,7 +425,7 @@ private async void SplitBtnDownloader_Click(object sender, RoutedEventArgs e) VideoDownloadOptions options = GetOptions(saveFileDialog.FileName, null); options.CacheCleanerCallback = HandleCacheCleanerCallback; - var downloadProgress = new WpfTaskProgress(SetPercent, SetStatus, AppendLog); + var downloadProgress = new WpfTaskProgress((LogLevel)Settings.Default.LogLevels, SetPercent, SetStatus, AppendLog); VideoDownloader currentDownload = new VideoDownloader(options, downloadProgress); _cancellationTokenSource = new CancellationTokenSource(); diff --git a/TwitchDownloaderWPF/Properties/Settings.Designer.cs b/TwitchDownloaderWPF/Properties/Settings.Designer.cs index 0cda5f22..1a245b3e 100644 --- a/TwitchDownloaderWPF/Properties/Settings.Designer.cs +++ b/TwitchDownloaderWPF/Properties/Settings.Designer.cs @@ -897,5 +897,17 @@ public bool EncodeClipMetadata { this["EncodeClipMetadata"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("14")] + public int LogLevels { + get { + return ((int)(this["LogLevels"])); + } + set { + this["LogLevels"] = value; + } + } } } diff --git a/TwitchDownloaderWPF/Properties/Settings.settings b/TwitchDownloaderWPF/Properties/Settings.settings index 42c4492d..72b1ecee 100644 --- a/TwitchDownloaderWPF/Properties/Settings.settings +++ b/TwitchDownloaderWPF/Properties/Settings.settings @@ -221,6 +221,9 @@ True + + 14 + diff --git a/TwitchDownloaderWPF/Utils/WpfTaskProgress.cs b/TwitchDownloaderWPF/Utils/WpfTaskProgress.cs index 294f3edf..02a8fae3 100644 --- a/TwitchDownloaderWPF/Utils/WpfTaskProgress.cs +++ b/TwitchDownloaderWPF/Utils/WpfTaskProgress.cs @@ -1,9 +1,10 @@ using System; +using System.Runtime.CompilerServices; using TwitchDownloaderCore.Interfaces; +using TwitchDownloaderWPF.Models; namespace TwitchDownloaderWPF.Utils { - // TODO: Implement log levels internal class WpfTaskProgress : ITaskProgress { private string _status; @@ -13,6 +14,8 @@ internal class WpfTaskProgress : ITaskProgress private TimeSpan _lastTime1 = new(-1); private TimeSpan _lastTime2 = new(-1); + private readonly LogLevel _logLevel; + private readonly Action _handlePercent; private readonly Action _handleStatus; private readonly Action _handleLog; @@ -24,14 +27,23 @@ public WpfTaskProgress(Action handlePercent) _handleStatus = null; _handleLog = null; _handleFfmpegLog = null; + + _logLevel = LogLevel.None; } - public WpfTaskProgress(Action handlePercent, Action handleStatus, Action handleLog, Action handleFfmpegLog = null) + public WpfTaskProgress(LogLevel logLevel, Action handlePercent, Action handleStatus, Action handleLog, Action handleFfmpegLog = null) { _handlePercent = handlePercent; _handleStatus = handleStatus; _handleLog = handleLog; _handleFfmpegLog = handleFfmpegLog; + + _logLevel = logLevel; + if (handleFfmpegLog is not null) + { + // TODO: Make this user configurable + _logLevel |= LogLevel.Ffmpeg; + } } public void SetStatus(string status) @@ -117,26 +129,64 @@ public void ReportProgress(int percent, TimeSpan time1, TimeSpan time2) public void LogVerbose(string logMessage) { - //_handleLog?.Invoke(logMessage); + if ((_logLevel & LogLevel.Verbose) == 0) return; + + _handleLog?.Invoke(logMessage); + } + + public void LogVerbose(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Verbose) == 0) return; + + _handleLog?.Invoke(logMessage.ToStringAndClear()); } public void LogInfo(string logMessage) { - _handleLog?.Invoke(logMessage); + if ((_logLevel & LogLevel.Info) == 0) return; + + _handleLog.Invoke(logMessage); + } + + public void LogInfo(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Info) == 0) return; + + _handleLog.Invoke(logMessage.ToStringAndClear()); } public void LogWarning(string logMessage) { + if ((_logLevel & LogLevel.Warning) == 0) return; + _handleLog?.Invoke(logMessage); } + public void LogWarning(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Warning) == 0) return; + + _handleLog?.Invoke(logMessage.ToStringAndClear()); + } + public void LogError(string logMessage) { + if ((_logLevel & LogLevel.Error) == 0) return; + _handleLog?.Invoke(Translations.Strings.ErrorLog + logMessage); } + public void LogError(DefaultInterpolatedStringHandler logMessage) + { + if ((_logLevel & LogLevel.Error) == 0) return; + + _handleLog?.Invoke(Translations.Strings.ErrorLog + logMessage.ToStringAndClear()); + } + public void LogFfmpeg(string logMessage) { + if ((_logLevel & LogLevel.Ffmpeg) == 0) return; + _handleFfmpegLog?.Invoke(logMessage); } } diff --git a/TwitchDownloaderWPF/WindowSettings.xaml b/TwitchDownloaderWPF/WindowSettings.xaml index 81ac7ba5..a3b5099b 100644 --- a/TwitchDownloaderWPF/WindowSettings.xaml +++ b/TwitchDownloaderWPF/WindowSettings.xaml @@ -67,6 +67,10 @@ + + + + diff --git a/TwitchDownloaderWPF/WindowSettings.xaml.cs b/TwitchDownloaderWPF/WindowSettings.xaml.cs index 5383a12f..1df4fb09 100644 --- a/TwitchDownloaderWPF/WindowSettings.xaml.cs +++ b/TwitchDownloaderWPF/WindowSettings.xaml.cs @@ -5,9 +5,11 @@ using System.Windows; using System.Windows.Controls; using HandyControl.Data; +using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Services; -using MessageBox = System.Windows.MessageBox; +using CheckComboBox = HandyControl.Controls.CheckComboBox; +using CheckComboBoxItem = HandyControl.Controls.CheckComboBoxItem; namespace TwitchDownloaderWPF { @@ -83,6 +85,20 @@ private void Window_Initialized(object sender, EventArgs e) { ComboLocale.SelectedIndex = selectedIndex; } + + ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = nameof(LogLevel.Verbose), Tag = LogLevel.Verbose }); + ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = nameof(LogLevel.Info), Tag = LogLevel.Info }); + ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = nameof(LogLevel.Warning), Tag = LogLevel.Warning }); + ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = nameof(LogLevel.Error), Tag = LogLevel.Error }); + // ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = "FFmpeg", Tag = LogLevel.Ffmpeg }); + var currentLogLevels = (LogLevel)Settings.Default.LogLevels; + foreach (CheckComboBoxItem item in ComboLogLevels.Items) + { + if (currentLogLevels.HasFlag((Enum)item.Tag)) + { + ComboLogLevels.SelectedItems.Add(item); + } + } } private void BtnClearCache_Click(object sender, RoutedEventArgs e) @@ -307,5 +323,16 @@ private void TextChatTemplate_OnTextChanged(object sender, TextChangedEventArgs Settings.Default.TemplateChat = TextChatTemplate.Text; } + + private void ComboLogLevels_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (!IsInitialized) + return; + + var newLogLevel = ComboLogLevels.SelectedItems + .Cast() + .Sum(item => (int)item.Tag); + Settings.Default.LogLevels = newLogLevel; + } } }