Skip to content

Commit

Permalink
Attempt to solve issue #22 - but this needs to be clarified
Browse files Browse the repository at this point in the history
  • Loading branch information
sandreas committed Aug 19, 2022
1 parent 34568c0 commit c48a58c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 15 deletions.
42 changes: 27 additions & 15 deletions tone/Commands/TagCommand.cs
@@ -1,17 +1,19 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Sandreas.AudioMetadata;
using tone.Services;
using Spectre.Console;
using Spectre.Console.Cli;
using tone.Commands.Settings;
using tone.DependencyInjection;
using tone.Directives;
using tone.Matchers;
using tone.Metadata.Taggers;

namespace tone.Commands;

public class TagCommand : AsyncCommand<TagCommandSettings>
public class TagCommand : CancellableAsyncCommand<TagCommandSettings>
{
private readonly DirectoryLoaderService _dirLoader;
private readonly SpectreConsoleService _console;
Expand All @@ -31,8 +33,10 @@ public class TagCommand : AsyncCommand<TagCommandSettings>
_api = api; // unused but necessary (Dependency Injection Initialisation)
}

public override async Task<int> ExecuteAsync(CommandContext context, TagCommandSettings settings)
public override async Task<int> ExecuteAsync(CommandContext context, TagCommandSettings settings,
CancellationToken cancellation)
{
// it throws a TaskCanceledException
if (_startup.HasErrors)
{
return _startup.ShowErrors(_console.Error.WriteLine);
Expand All @@ -44,11 +48,12 @@ public override async Task<int> ExecuteAsync(CommandContext context, TagCommandS
{
inputFiles = inputFiles.Where(f => _pathPatternMatcher.TryMatchSinglePattern(f.FullName, out _));
}

var inputFilesAsArray = inputFiles
.Apply(new OrderByDirective(settings.OrderBy))
.Apply(new LimitDirective(settings.Limit))
.ToArray();

var packages = _dirLoader.BuildPackages(inputFilesAsArray, _pathPatternMatcher, settings.Input).ToArray();
var fileCount = packages.Sum(p => p.Files.Count);

Expand All @@ -65,7 +70,7 @@ public override async Task<int> ExecuteAsync(CommandContext context, TagCommandS
return (int)ReturnCode.UserAbort;
}
}

var showDryRunMessage = false;

var tasks = packages.Select(p => Task.Run(async () =>
Expand All @@ -78,8 +83,6 @@ public override async Task<int> ExecuteAsync(CommandContext context, TagCommandS
};
var status = await _tagger.UpdateAsync(track);
if (!status)
{
_console.Error.WriteLine($"Could not update tags for file {file}: {status.Error}");
Expand All @@ -90,16 +93,22 @@ public override async Task<int> ExecuteAsync(CommandContext context, TagCommandS
var diffListing = track.Diff(currentMetadata);
if (diffListing.Count == 0)
{
if(settings.DryRun || !settings.Force){
if (settings.DryRun || !settings.Force)
{
_console.Write(new Rule($"[green]unchanged: {Markup.Escape(track.Path ?? "")}[/]")
.LeftAligned());
continue;
}
var path = Markup.Escape(track.Path ?? "");
var message = !track.Save() ? $"[red]Force update failed: {path}[/]" : $"[green]Forced update: {path}[/]";
var message = !track.Save()
? $"[red]Force update failed: {path}[/]"
: $"[green]Forced update: {path}[/]";
_console.Write(new Rule(message)
.LeftAligned());
} else {
}
else
{
showDryRunMessage = settings.DryRun;
var diffTable = new Table().Expand();
diffTable.Title = new TableTitle($"[blue]DIFF: {Markup.Escape(track.Path ?? "")}[/]");
Expand All @@ -114,23 +123,26 @@ public override async Task<int> ExecuteAsync(CommandContext context, TagCommandS
Markup.Escape(currentValue?.ToString() ?? "<null>")
);
}
if (settings.DryRun)
{
_console.Write(diffTable);
continue;
}
var path = Markup.Escape(track.Path ?? "");
var message = !track.Save() ? $"[red]Update failed: {path}[/]" : $"[green]Updated: {path}[/]";
var message = !track.Save()
? $"[red]Update failed: {path}[/]"
: $"[green]Updated: {path}[/]";
diffTable.Caption = new TableTitle(message);
_console.Write(diffTable);
}
}
}))
.ToList();

await Task.WhenAll(tasks);
}, cancellation))
.ToArray();

// https://stackoverflow.com/questions/27238232/how-can-i-cancel-task-whenall
await Task.Run(() => Task.WaitAll(tasks), cancellation);

if (showDryRunMessage)
{
Expand Down
15 changes: 15 additions & 0 deletions tone/DependencyInjection/CancellableAsyncCommand.cs
@@ -0,0 +1,15 @@
using System.Threading;
using System.Threading.Tasks;
using Spectre.Console.Cli;

namespace tone.DependencyInjection;

public abstract class CancellableAsyncCommand<TSettings> : AsyncCommand<TSettings>
where TSettings : CommandSettings
{
private readonly ConsoleAppCancellationTokenSource _cancellationTokenSource = new();

public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellation);

public sealed override async Task<int> ExecuteAsync(CommandContext context, TSettings settings) => await ExecuteAsync(context, settings, _cancellationTokenSource.Token);
}
42 changes: 42 additions & 0 deletions tone/DependencyInjection/ConsoleAppCancellationTokenSource.cs
@@ -0,0 +1,42 @@
using System;
using System.Threading;

namespace tone.DependencyInjection;

internal class ConsoleAppCancellationTokenSource
{
private readonly CancellationTokenSource _cts = new();

public CancellationToken Token => _cts.Token;

public ConsoleAppCancellationTokenSource()
{
Console.CancelKeyPress += OnCancelKeyPress;
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;

using var _ = _cts.Token.Register(() =>
{
AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
Console.CancelKeyPress -= OnCancelKeyPress;
});
}

private void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e)
{
// NOTE: cancel event, don't terminate the process
e.Cancel = true;

_cts.Cancel();
}

private void OnProcessExit(object? sender, EventArgs e)
{
if (_cts.IsCancellationRequested)
{
// NOTE: SIGINT (cancel key was pressed, this shouldn't ever actually hit however, as we remove the event handler upon cancellation of the `cancellationSource`)
return;
}

_cts.Cancel();
}
}

0 comments on commit c48a58c

Please sign in to comment.