Skip to content

Commit

Permalink
New progress reporting system (#1010)
Browse files Browse the repository at this point in the history
* Replace IProgress<ProgressReport> with ITaskProgress and ITaskLogger in Core

* Replace Progress<ProgressReport> with CliTaskProgress in CLI

* Replace Progress<ProgressReport> with WpfTaskProgress in WPF

* Delete ProgressHandler and ProgressReport

* Use the progress reporter to ensure no race conditions

* Generics don't work :(

* Use distinct status template overloads

* Fix warning preamble

* Fix chat render time left calculation

* DRY

* Use logger instead of IProgress

* Missed a spot

* Use ITaskLogger

* Fix missing downloading status in chat downloader

* Simplify chat section progress reporting

* Progress should never be null - log levels should be used instead

* Move emoji decode failure log to Verbose
  • Loading branch information
ScrubN authored Mar 30, 2024
1 parent 057a905 commit ccf2abb
Show file tree
Hide file tree
Showing 33 changed files with 986 additions and 598 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.IO;
using TwitchDownloaderCLI.Modes.Arguments;

namespace TwitchDownloaderCLI.Tools
namespace TwitchDownloaderCLI.Modes
{
public static class CacheHandler
{
Expand Down
8 changes: 4 additions & 4 deletions TwitchDownloaderCLI/Modes/DownloadChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ internal static void Download(ChatDownloadArgs inputOptions)
{
var downloadOptions = GetDownloadOptions(inputOptions);

ChatDownloader chatDownloader = new(downloadOptions);
Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;
chatDownloader.DownloadAsync(progress, new CancellationToken()).Wait();
var progress = new CliTaskProgress();

var chatDownloader = new ChatDownloader(downloadOptions, progress);
chatDownloader.DownloadAsync(CancellationToken.None).Wait();
}

private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOptions)
Expand Down
9 changes: 4 additions & 5 deletions TwitchDownloaderCLI/Modes/DownloadClip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,16 @@ internal static class DownloadClip
{
internal static void Download(ClipDownloadArgs inputOptions)
{
var progress = new CliTaskProgress();

if (inputOptions.EncodeMetadata == true)
{
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath);
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress);
}

Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;

var downloadOptions = GetDownloadOptions(inputOptions);

ClipDownloader clipDownloader = new(downloadOptions, progress);
var clipDownloader = new ClipDownloader(downloadOptions, progress);
clipDownloader.DownloadAsync(new CancellationToken()).Wait();
}

Expand Down
11 changes: 5 additions & 6 deletions TwitchDownloaderCLI/Modes/DownloadVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,21 @@ internal static class DownloadVideo
{
internal static void Download(VideoDownloadArgs inputOptions)
{
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath);
var progress = new CliTaskProgress();

Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress);

var downloadOptions = GetDownloadOptions(inputOptions);
downloadOptions.CacheCleanerCallback = directoryInfos =>
{
Console.WriteLine(
$"[LOG] - {directoryInfos.Length} unmanaged video caches were found at '{downloadOptions.TempFolder}' and can be safely deleted. " +
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<DirectoryInfo>();
};

VideoDownloader videoDownloader = new(downloadOptions, progress);
var videoDownloader = new VideoDownloader(downloadOptions, progress);
videoDownloader.DownloadAsync(new CancellationToken()).Wait();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using Mono.Unix;
using System;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Mono.Unix;
using TwitchDownloaderCLI.Modes.Arguments;
using TwitchDownloaderCLI.Tools;
using TwitchDownloaderCore.Interfaces;
using Xabe.FFmpeg;
using Xabe.FFmpeg.Downloader;

namespace TwitchDownloaderCLI.Tools
namespace TwitchDownloaderCLI.Modes
{
public static class FfmpegHandler
{
Expand Down Expand Up @@ -55,14 +57,14 @@ private static void DownloadFfmpeg()
}
}

public static void DetectFfmpeg(string ffmpegPath)
public static void DetectFfmpeg(string ffmpegPath, ITaskLogger logger)
{
if (File.Exists(ffmpegPath) || File.Exists(FfmpegExecutableName) || PathUtils.ExistsOnPATH(FfmpegExecutableName))
{
return;
}

Console.WriteLine("[ERROR] - Unable to find FFmpeg, exiting. You can download FFmpeg automatically with the command \"TwitchDownloaderCLI ffmpeg -d\"");
logger.LogError("Unable to find FFmpeg, exiting. You can download FFmpeg automatically with the command \"TwitchDownloaderCLI ffmpeg -d\"");
Environment.Exit(1);
}

Expand Down
11 changes: 5 additions & 6 deletions TwitchDownloaderCLI/Modes/MergeTs.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Threading;
using System.Threading;
using TwitchDownloaderCLI.Modes.Arguments;
using TwitchDownloaderCLI.Tools;
using TwitchDownloaderCore;
Expand All @@ -11,13 +10,13 @@ internal static class MergeTs
{
internal static void Merge(TsMergeArgs inputOptions)
{
Console.WriteLine("[INFO] The TS merger is experimental and is subject to change without notice in future releases.");
var progress = new CliTaskProgress();

Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;
progress.LogInfo("The TS merger is experimental and is subject to change without notice in future releases.");

var mergeOptions = GetMergeOptions(inputOptions);
TsMerger tsMerger = new(mergeOptions, progress);

var tsMerger = new TsMerger(mergeOptions, progress);
tsMerger.MergeAsync(new CancellationToken()).Wait();
}

Expand Down
7 changes: 3 additions & 4 deletions TwitchDownloaderCLI/Modes/RenderChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ internal static class RenderChat
{
internal static void Render(ChatRenderArgs inputOptions)
{
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath);
var progress = new CliTaskProgress();

Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress);

var renderOptions = GetRenderOptions(inputOptions);
using ChatRenderer chatRenderer = new(renderOptions, progress);
using var chatRenderer = new ChatRenderer(renderOptions, progress);
chatRenderer.ParseJsonAsync().Wait();
chatRenderer.RenderVideoAsync(new CancellationToken()).Wait();
}
Expand Down
8 changes: 4 additions & 4 deletions TwitchDownloaderCLI/Modes/UpdateChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ internal static void Update(ChatUpdateArgs inputOptions)
{
var updateOptions = GetUpdateOptions(inputOptions);

ChatUpdater chatUpdater = new(updateOptions);
Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;
var progress = new CliTaskProgress();

var chatUpdater = new ChatUpdater(updateOptions, progress);
chatUpdater.ParseJsonAsync().Wait();
chatUpdater.UpdateAsync(progress, new CancellationToken()).Wait();
chatUpdater.UpdateAsync(new CancellationToken()).Wait();
}

private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions)
Expand Down
187 changes: 187 additions & 0 deletions TwitchDownloaderCLI/Tools/CliTaskProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
using System;
using TwitchDownloaderCore.Interfaces;

namespace TwitchDownloaderCLI.Tools
{
public class CliTaskProgress : ITaskProgress
{
private const string STATUS_PREAMBLE = "[STATUS] - ";
private const string VERBOSE_LOG_PREAMBLE = "[VERBOSE] - ";
private const string INFO_LOG_PREAMBLE = "[INFO] - ";
private const string WARNING_LOG_PREAMBLE = "[WARNING] - ";
private const string ERROR_LOG_PREAMBLE = "[ERROR] - ";
private const string FFMPEG_LOG_PREAMBLE = "<FFMPEG> ";

private string _status;
private bool _statusIsTemplate;

private bool _lastWriteHadNewLine = true;
private int _lastStatusLength;
private int _lastPercent = -1;
private TimeSpan _lastTime1 = new(-1);
private TimeSpan _lastTime2 = new(-1);

public CliTaskProgress()
{
// TODO: Take in ITwitchDownloaderArgs to configure log levels
}

public void SetStatus(string status)
{
lock (this)
{
_status = status;
_statusIsTemplate = false;

WriteNewLineMessage(STATUS_PREAMBLE, status);
}
}

public void SetTemplateStatus(string status, int initialPercent)
{
lock (this)
{
_status = status;
_statusIsTemplate = true;

if (!_lastWriteHadNewLine)
{
Console.WriteLine();
}

_lastPercent = -1; // Ensure that the progress report runs
ReportProgress(initialPercent);
}
}

public void SetTemplateStatus(string status, int initialPercent, TimeSpan initialTime1, TimeSpan initialTime2)
{
lock (this)
{
_status = status;
_statusIsTemplate = true;

if (!_lastWriteHadNewLine)
{
Console.WriteLine();
}

_lastPercent = -1; // Ensure that the progress report runs
ReportProgress(initialPercent, initialTime1, initialTime2);
}
}

public void ReportProgress(int percent)
{
lock (this)
{
if ((!_lastWriteHadNewLine && _lastPercent == percent)
|| !_statusIsTemplate)
{
return;
}

var status = string.Format(_status, percent);
_lastStatusLength = WriteSameLineMessage(STATUS_PREAMBLE, status, _lastStatusLength);

_lastWriteHadNewLine = false;
_lastPercent = percent;
}
}

public void ReportProgress(int percent, TimeSpan time1, TimeSpan time2)
{
lock (this)
{
if ((!_lastWriteHadNewLine && _lastPercent == percent && _lastTime1 == time1 && _lastTime2 == time2)
|| !_statusIsTemplate)
{
return;
}

var status = string.Format(_status, percent, time1, time2);
_lastStatusLength = WriteSameLineMessage(STATUS_PREAMBLE, status, _lastStatusLength);

_lastWriteHadNewLine = false;
_lastPercent = percent;
_lastTime1 = time1;
_lastTime2 = time2;
}
}

private int WriteSameLineMessage(string preamble, string message, int previousMessageLength)
{
if (!_lastWriteHadNewLine)
{
Console.Write('\r');
}

Console.Write(preamble);
Console.Write(message);

var messageLength = preamble.Length + message.Length;
if (messageLength < previousMessageLength)
{
// Ensure that the previous line is completely overwritten
for (var i = 0; i < previousMessageLength - messageLength; i++)
{
Console.Write(' ');
}
}

return messageLength;
}

public void LogVerbose(string logMessage)
{
lock (this)
{
// WriteNewLineMessage(VERBOSE_LOG_PREAMBLE, logMessage);
}
}

public void LogInfo(string logMessage)
{
lock (this)
{
WriteNewLineMessage(INFO_LOG_PREAMBLE, logMessage);
}
}

public void LogWarning(string logMessage)
{
lock (this)
{
WriteNewLineMessage(WARNING_LOG_PREAMBLE, logMessage);
}
}

public void LogError(string logMessage)
{
lock (this)
{
WriteNewLineMessage(ERROR_LOG_PREAMBLE, logMessage);
}
}

public void LogFfmpeg(string logMessage)
{
lock (this)
{
WriteNewLineMessage(FFMPEG_LOG_PREAMBLE, logMessage);
}
}

private void WriteNewLineMessage(string preamble, string message)
{
if (!_lastWriteHadNewLine)
{
Console.WriteLine();
}

Console.Write(preamble);
Console.WriteLine(message);
_lastWriteHadNewLine = true;
}
}
}
Loading

0 comments on commit ccf2abb

Please sign in to comment.