Skip to content

Commit

Permalink
Prompt user before overwriting files (#1085)
Browse files Browse the repository at this point in the history
* Create destination file immediately

* Fix potential NRE

* Implement FileOverwriteHandler for CLI

* Add GetNonCollidingName tests

* TwitchDownloaderArgs -> ITwitchDownloaderArgs & rearrange arg interfaces due to help text ordering

* Update help text

* Create WPF task file overwrite handler

* Throw when no output file is returned, refresh FileInfos before checking Exists property

* Genericize opening explorer for a given file

* Update ___Options output file property to reflect possibly new filename

* Add icon

* Add translations

* Annotate as windows only

* Update README

* Forgot to add it to the whole CLI

* Overwrite -> Collision

* Missed a few spots

* Update wording

* Use warning icon to match file explorer overwrite prompt

* Make "remember my choice" wording more clear

* Fix missing zh-cn translations
  • Loading branch information
ScrubN authored Jun 6, 2024
1 parent d3bf8e1 commit 961e542
Show file tree
Hide file tree
Showing 54 changed files with 960 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,12 @@ internal enum LogLevel
Error = 1 << 5,
Ffmpeg = 1 << 6,
}

public enum OverwriteBehavior
{
Overwrite,
Exit,
Rename,
Prompt,
}
}
7 changes: 6 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/CacheArgs.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
using CommandLine;
using TwitchDownloaderCLI.Models;

namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("cache", HelpText = "Manage the working cache")]
internal sealed class CacheArgs : TwitchDownloaderArgs
internal sealed class CacheArgs : ITwitchDownloaderArgs
{
[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; }

// Interface args
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
7 changes: 6 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/ChatDownloadArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("chatdownload", HelpText = "Downloads the chat from a VOD or clip")]
internal sealed class ChatDownloadArgs : TwitchDownloaderArgs
internal sealed class ChatDownloadArgs : IFileCollisionArgs, ITwitchDownloaderArgs
{
[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; }
Expand Down Expand Up @@ -45,5 +45,10 @@ internal sealed class ChatDownloadArgs : TwitchDownloaderArgs

[Option("temp-path", Default = "", HelpText = "Path to temporary folder to use for cache.")]
public string TempFolder { get; set; }

// Interface args
public OverwriteBehavior OverwriteBehavior { get; set; }
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
7 changes: 6 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/ChatRenderArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("chatrender", HelpText = "Renders a chat JSON as a video")]
internal sealed class ChatRenderArgs : TwitchDownloaderArgs
internal sealed class ChatRenderArgs : IFileCollisionArgs, ITwitchDownloaderArgs
{
[Option('i', "input", Required = true, HelpText = "Path to JSON chat file input.")]
public string InputFile { get; set; }
Expand Down Expand Up @@ -152,5 +152,10 @@ internal sealed class ChatRenderArgs : TwitchDownloaderArgs

[Option("scale-highlight-indent", Default = 1.0, HelpText = "Number to scale highlight indent size (sub messages).")]
public double ScaleAccentIndent { get; set; }

// Interface args
public OverwriteBehavior OverwriteBehavior { get; set; }
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
7 changes: 6 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/ChatUpdateArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("chatupdate", HelpText = "Updates the embedded emotes, badges, bits, and trims a chat JSON and/or converts a JSON chat to another format.")]
internal sealed class ChatUpdateArgs : TwitchDownloaderArgs
internal sealed class ChatUpdateArgs : IFileCollisionArgs, ITwitchDownloaderArgs
{
[Option('i', "input", Required = true, HelpText = "Path to input file. Valid extensions are: .json, .json.gz.")]
public string InputFile { get; set; }
Expand Down Expand Up @@ -42,5 +42,10 @@ internal sealed class ChatUpdateArgs : TwitchDownloaderArgs

[Option("temp-path", Default = "", HelpText = "Path to temporary folder to use for cache.")]
public string TempFolder { get; set; }

// Interface args
public OverwriteBehavior OverwriteBehavior { get; set; }
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
8 changes: 7 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using CommandLine;
using TwitchDownloaderCLI.Models;

namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("clipdownload", HelpText = "Downloads a clip from Twitch")]
internal sealed class ClipDownloadArgs : TwitchDownloaderArgs
internal sealed class ClipDownloadArgs : IFileCollisionArgs, ITwitchDownloaderArgs
{
[Option('u', "id", Required = true, HelpText = "The ID or URL of the clip to download.")]
public string Id { get; set; }
Expand All @@ -25,5 +26,10 @@ internal sealed class ClipDownloadArgs : TwitchDownloaderArgs

[Option("temp-path", Default = "", HelpText = "Path to temporary caching folder.")]
public string TempFolder { get; set; }

// Interface args
public OverwriteBehavior OverwriteBehavior { get; set; }
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
7 changes: 6 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/FfmpegArgs.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using CommandLine;
using TwitchDownloaderCLI.Models;

namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("ffmpeg", HelpText = "Manage standalone ffmpeg")]
internal sealed class FfmpegArgs : TwitchDownloaderArgs
internal sealed class FfmpegArgs : ITwitchDownloaderArgs
{
[Option('d', "download", Default = false, Required = false, HelpText = "Downloads FFmpeg as a standalone file.")]
public bool DownloadFfmpeg { get; set; }

// Interface args
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
11 changes: 11 additions & 0 deletions TwitchDownloaderCLI/Modes/Arguments/IFileCollisionArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using CommandLine;
using TwitchDownloaderCLI.Models;

namespace TwitchDownloaderCLI.Modes.Arguments
{
internal interface IFileCollisionArgs
{
[Option("collision", Default = OverwriteBehavior.Prompt, HelpText = "Sets the handling of output file name collisions. Valid values are: Overwrite, Exit, Rename, Prompt.")]
public OverwriteBehavior OverwriteBehavior { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace TwitchDownloaderCLI.Modes.Arguments
{
internal abstract class TwitchDownloaderArgs
internal interface ITwitchDownloaderArgs
{
[Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")]
public bool? ShowBanner { get; set; }
Expand Down
8 changes: 7 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
using CommandLine;
using TwitchDownloaderCLI.Models;

namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("tsmerge", HelpText = "Concatenates multiple .ts/.tsv/.tsa/.m2t/.m2ts (MPEG Transport Stream) files into a single file")]
internal sealed class TsMergeArgs : TwitchDownloaderArgs
internal sealed class TsMergeArgs : IFileCollisionArgs, ITwitchDownloaderArgs
{
[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; }

// Interface args
public OverwriteBehavior OverwriteBehavior { get; set; }
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
7 changes: 6 additions & 1 deletion TwitchDownloaderCLI/Modes/Arguments/VideoDownloadArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace TwitchDownloaderCLI.Modes.Arguments
{
[Verb("videodownload", HelpText = "Downloads a stream VOD from Twitch")]
internal sealed class VideoDownloadArgs : TwitchDownloaderArgs
internal sealed class VideoDownloadArgs : IFileCollisionArgs, ITwitchDownloaderArgs
{
[Option('u', "id", Required = true, HelpText = "The ID or URL of the VOD to download.")]
public string Id { get; set; }
Expand Down Expand Up @@ -35,5 +35,10 @@ internal sealed class VideoDownloadArgs : TwitchDownloaderArgs

[Option("temp-path", Default = "", HelpText = "Path to temporary caching folder.")]
public string TempFolder { get; set; }

// Interface args
public OverwriteBehavior OverwriteBehavior { get; set; }
public bool? ShowBanner { get; set; }
public LogLevel LogLevel { get; set; }
}
}
8 changes: 5 additions & 3 deletions TwitchDownloaderCLI/Modes/DownloadChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ internal static void Download(ChatDownloadArgs inputOptions)
{
var progress = new CliTaskProgress(inputOptions.LogLevel);

var downloadOptions = GetDownloadOptions(inputOptions, progress);
var collisionHandler = new FileCollisionHandler(inputOptions);
var downloadOptions = GetDownloadOptions(inputOptions, collisionHandler, progress);

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

private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOptions, ITaskLogger logger)
private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOptions, FileCollisionHandler collisionHandler, ITaskLogger logger)
{
if (inputOptions.Id is null)
{
Expand Down Expand Up @@ -64,7 +65,8 @@ private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOpti
BttvEmotes = (bool)inputOptions.BttvEmotes!,
FfzEmotes = (bool)inputOptions.FfzEmotes!,
StvEmotes = (bool)inputOptions.StvEmotes!,
TempFolder = inputOptions.TempFolder
TempFolder = inputOptions.TempFolder,
FileCollisionCallback = collisionHandler.HandleCollisionCallback,
};

return downloadOptions;
Expand Down
8 changes: 5 additions & 3 deletions TwitchDownloaderCLI/Modes/DownloadClip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ internal static void Download(ClipDownloadArgs inputOptions)
FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress);
}

var downloadOptions = GetDownloadOptions(inputOptions, progress);
var collisionHandler = new FileCollisionHandler(inputOptions);
var downloadOptions = GetDownloadOptions(inputOptions, collisionHandler, progress);

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

private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOptions, ITaskLogger logger)
private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOptions, FileCollisionHandler collisionHandler, ITaskLogger logger)
{
if (inputOptions.Id is null)
{
Expand All @@ -50,7 +51,8 @@ private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOpti
ThrottleKib = inputOptions.ThrottleKib,
FfmpegPath = string.IsNullOrWhiteSpace(inputOptions.FfmpegPath) ? FfmpegHandler.FfmpegExecutableName : Path.GetFullPath(inputOptions.FfmpegPath),
EncodeMetadata = inputOptions.EncodeMetadata!.Value,
TempFolder = inputOptions.TempFolder
TempFolder = inputOptions.TempFolder,
FileCollisionCallback = collisionHandler.HandleCollisionCallback,
};

return downloadOptions;
Expand Down
8 changes: 5 additions & 3 deletions TwitchDownloaderCLI/Modes/DownloadVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ internal static void Download(VideoDownloadArgs inputOptions)

FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress);

var downloadOptions = GetDownloadOptions(inputOptions, progress);
var collisionHandler = new FileCollisionHandler(inputOptions);
var downloadOptions = GetDownloadOptions(inputOptions, collisionHandler, progress);

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

private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOptions, ITaskLogger logger)
private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOptions, FileCollisionHandler collisionHandler, ITaskLogger logger)
{
if (inputOptions.Id is null)
{
Expand Down Expand Up @@ -76,7 +77,8 @@ private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOp
"Run 'TwitchDownloaderCLI cache help' for more information.");
return Array.Empty<DirectoryInfo>();
}
},
FileCollisionCallback = collisionHandler.HandleCollisionCallback,
};

return downloadOptions;
Expand Down
8 changes: 5 additions & 3 deletions TwitchDownloaderCLI/Modes/MergeTs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ internal static void Merge(TsMergeArgs inputOptions)

progress.LogInfo("The TS merger is experimental and is subject to change without notice in future releases.");

var mergeOptions = GetMergeOptions(inputOptions);
var collisionHandler = new FileCollisionHandler(inputOptions);
var mergeOptions = GetMergeOptions(inputOptions, collisionHandler);

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

private static TsMergeOptions GetMergeOptions(TsMergeArgs inputOptions)
private static TsMergeOptions GetMergeOptions(TsMergeArgs inputOptions, FileCollisionHandler collisionHandler)
{
TsMergeOptions mergeOptions = new()
{
OutputFile = inputOptions.OutputFile,
InputFile = inputOptions.InputList
InputFile = inputOptions.InputList,
FileCollisionCallback = collisionHandler.HandleCollisionCallback,
};

return mergeOptions;
Expand Down
7 changes: 5 additions & 2 deletions TwitchDownloaderCLI/Modes/RenderChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ internal static void Render(ChatRenderArgs inputOptions)

FfmpegHandler.DetectFfmpeg(inputOptions.FfmpegPath, progress);

var renderOptions = GetRenderOptions(inputOptions, progress);
var collisionHandler = new FileCollisionHandler(inputOptions);
var renderOptions = GetRenderOptions(inputOptions, collisionHandler, progress);

using var chatRenderer = new ChatRenderer(renderOptions, progress);
chatRenderer.ParseJsonAsync().Wait();
chatRenderer.RenderVideoAsync(new CancellationToken()).Wait();
}

private static ChatRenderOptions GetRenderOptions(ChatRenderArgs inputOptions, ITaskLogger logger)
private static ChatRenderOptions GetRenderOptions(ChatRenderArgs inputOptions, FileCollisionHandler collisionHandler, ITaskLogger logger)
{
ChatRenderOptions renderOptions = new()
{
Expand Down Expand Up @@ -93,6 +95,7 @@ private static ChatRenderOptions GetRenderOptions(ChatRenderArgs inputOptions, I
DisperseCommentOffsets = inputOptions.DisperseCommentOffsets,
AlternateMessageBackgrounds = inputOptions.AlternateMessageBackgrounds,
AdjustUsernameVisibility = inputOptions.AdjustUsernameVisibility,
FileCollisionCallback = collisionHandler.HandleCollisionCallback,
};

if (renderOptions.GenerateMask && renderOptions.BackgroundColor.Alpha == 255 && !(renderOptions.AlternateMessageBackgrounds! && renderOptions.AlternateBackgroundColor.Alpha != 255))
Expand Down
8 changes: 5 additions & 3 deletions TwitchDownloaderCLI/Modes/UpdateChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ internal static void Update(ChatUpdateArgs inputOptions)
{
var progress = new CliTaskProgress(inputOptions.LogLevel);

var updateOptions = GetUpdateOptions(inputOptions, progress);
var collisionHandler = new FileCollisionHandler(inputOptions);
var updateOptions = GetUpdateOptions(inputOptions, collisionHandler, progress);

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

private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions, ITaskLogger logger)
private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions, FileCollisionHandler collisionHandler, ITaskLogger logger)
{
if (!File.Exists(inputOptions.InputFile))
{
Expand Down Expand Up @@ -76,7 +77,8 @@ private static ChatUpdateOptions GetUpdateOptions(ChatUpdateArgs inputOptions, I
FfzEmotes = (bool)inputOptions.FfzEmotes!,
StvEmotes = (bool)inputOptions.StvEmotes!,
TextTimestampFormat = inputOptions.TimeFormat,
TempFolder = inputOptions.TempFolder
TempFolder = inputOptions.TempFolder,
FileCollisionCallback = collisionHandler.HandleCollisionCallback,
};

return updateOptions;
Expand Down
4 changes: 2 additions & 2 deletions TwitchDownloaderCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private static void Main(string[] args)
parserResult.WithNotParsed(errors => WriteHelpText(errors, parserResult, parser.Settings));

CoreLicensor.EnsureFilesExist(AppContext.BaseDirectory);
WriteApplicationBanner((TwitchDownloaderArgs)parserResult.Value);
WriteApplicationBanner((ITwitchDownloaderArgs)parserResult.Value);

parserResult
.WithParsed<VideoDownloadArgs>(DownloadVideo.Download)
Expand Down Expand Up @@ -74,7 +74,7 @@ private static void WriteHelpText(IEnumerable<Error> errors, ParserResult<object
Environment.Exit(1);
}

private static void WriteApplicationBanner(TwitchDownloaderArgs args)
private static void WriteApplicationBanner(ITwitchDownloaderArgs args)
{
if (args.ShowBanner == false || (args.LogLevel & LogLevel.None) != 0)
{
Expand Down
Loading

0 comments on commit 961e542

Please sign in to comment.