Add overall progress, notifications, and UI polish

- Unify audio/subtitle selection: single-select, auto-default,
  cross-file propagation by exact name
- Show file size in queue, group divider and reordered columns in
  track table, drop accent highlight on row selection
- Add cumulative progress bar (files finished, MB copied) to the
  stripping window
- Add Windows toast notifications on stripping completion/cancel/error
- Restart the app after settings are changed; Cancel no longer
  clears the queue, only closes the window
- Add ConfigureAwait(false) throughout MkvService so concurrent
  strips don't starve the UI thread
- Hardcode scan/strip concurrency (10/5) instead of exposing it in
  Settings
This commit is contained in:
l4kr
2026-07-04 02:35:26 +02:00
parent 9b1bd51bd3
commit 05c496c10b
12 changed files with 272 additions and 68 deletions
+17 -11
View File
@@ -74,8 +74,8 @@ public class MkvService
// fill up and deadlock the child process indefinitely.
var stdoutTask = process.StandardOutput.ReadToEndAsync(ct);
var stderrTask = process.StandardError.ReadToEndAsync(ct);
await Task.WhenAll(stdoutTask, stderrTask);
await process.WaitForExitAsync(ct);
await Task.WhenAll(stdoutTask, stderrTask).ConfigureAwait(false);
await process.WaitForExitAsync(ct).ConfigureAwait(false);
string stdout = stdoutTask.Result;
string stderr = stderrTask.Result;
@@ -151,9 +151,15 @@ public class MkvService
});
}
for (int i = 0; i < items.Count; i++)
{
items[i].IsGroupStart = i > 0 && items[i].Type != items[i - 1].Type;
}
return items;
}
/// <summary>
/// Scans a single file and determines which audio/subtitle track IDs
/// should be kept. Fails if only English audio is present, or if the
@@ -164,7 +170,7 @@ public class MkvService
var scan = new ScanResult { FilePath = filePath };
try
{
var info = await IdentifyAsync(filePath, ct);
var info = await IdentifyAsync(filePath, ct).ConfigureAwait(false);
var audioTracks = info.Tracks.Where(t => t.Type == "audio").ToList();
var subtitleTracks = info.Tracks.Where(t => t.Type == "subtitles").ToList();
@@ -325,9 +331,9 @@ public class MkvService
{
byte[] buffer = new byte[1024 * 1024];
int read;
while ((read = await source.ReadAsync(buffer, ct)) > 0)
while ((read = await source.ReadAsync(buffer, ct).ConfigureAwait(false)) > 0)
{
await dest.WriteAsync(buffer.AsMemory(0, read), ct);
await dest.WriteAsync(buffer.AsMemory(0, read), ct).ConfigureAwait(false);
copied += read;
var elapsed = stopwatch.Elapsed;
@@ -450,11 +456,11 @@ public class MkvService
using var throughputCts = new CancellationTokenSource();
var throughputTask = MonitorThroughputAsync(tempFile, fileName, percentState, progress, throughputCts.Token);
await process.WaitForExitAsync(CancellationToken.None);
await process.WaitForExitAsync(CancellationToken.None).ConfigureAwait(false);
throughputCts.Cancel();
string stdout = await stdoutTask;
string stderr = await stderrTask;
try { await throughputTask; } catch (OperationCanceledException) { }
string stdout = await stdoutTask.ConfigureAwait(false);
string stderr = await stderrTask.ConfigureAwait(false);
try { await throughputTask.ConfigureAwait(false); } catch (OperationCanceledException) { }
if (wasKilled)
{
@@ -543,7 +549,7 @@ public class MkvService
{
var fullOutput = new System.Text.StringBuilder();
string? line;
while ((line = await process.StandardOutput.ReadLineAsync()) != null)
while ((line = await process.StandardOutput.ReadLineAsync().ConfigureAwait(false)) != null)
{
fullOutput.AppendLine(line);
var match = ProgressRegex.Match(line);
@@ -565,7 +571,7 @@ public class MkvService
{
while (true)
{
await Task.Delay(sampleInterval, stopToken);
await Task.Delay(sampleInterval, stopToken).ConfigureAwait(false);
long currentBytes = 0;
try { currentBytes = new FileInfo(tempFile).Length; } catch { }