Make long running steps cancellable and keep the queue from stalling the UI - #3
Merged
Kentarohakase merged 3 commits intoAug 12, 2026
Merged
Conversation
Kentarohakase
force-pushed
the
claude/code-review-improvements-gk09et
branch
from
August 12, 2026 00:56
3cf2be9 to
191e6e5
Compare
Kentarohakase
force-pushed
the
hardening/cancellation-and-ui-responsiveness
branch
from
August 12, 2026 00:56
fa1684e to
56e3df3
Compare
…stalling the UI Adding files never raised IsBusy, so while a dropped folder was analyzed Cancel stayed disabled, Start stayed enabled and further drops were accepted. The analysis now runs as a busy phase with its own cancellation source, and so does the processed preview render, which is a full FFmpeg pass and was equally unstoppable. Both report a cancellation as a normal outcome instead of letting it escape into the drop handler, which has no guard of its own. The two startup tasks were fire and forget. They are held now and cancelled on shutdown, and they check the token before writing view model state again, so a late answer can no longer raise change notifications into a torn down view. Their catch-all no longer hides a genuine defect: a cancellation stays silent, anything else is logged. Waiting for them in Dispose would deadlock the thread that has to run their continuations, so only the cancellation is issued. Finally, every property change of every queue item rebuilt the summary and re-raised all 24 command states. Progress is written on every FFmpeg tick and feeds neither, so the refresh is now limited to the properties that do.
The place a tool was found in was built as a German literal and handed straight to the app bar, so an English user read Benutzer-Tools next to an otherwise translated interface. The location now carries a resource key that is resolved on display. A failed probe was never cached. Since the probe is bounded by a timeout, a missing or unresponsive binary cost the full twenty seconds on every call. The failure is cached as well now, but only for half a minute, so a tool that is installed while the app runs is still picked up.
Kentarohakase
force-pushed
the
hardening/cancellation-and-ui-responsiveness
branch
from
August 12, 2026 01:01
56e3df3 to
b7f755d
Compare
Kentarohakase
changed the base branch from
claude/code-review-improvements-gk09et
to
main
August 12, 2026 01:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #2. Phase 2 of the improvement plan.
Fixed
LoadInputFilesAsyncguarded onIsBusybut never raised it. While a dropped folder was analyzed, Cancel was disabled, Start was enabled and further drops were accepted. It now runs as a busy phase with its own cancellation source, so the analysis can actually be stopped.PrepareRetryItemsAsyncfollows the same shape.CancellationToken.None. It now runs as a busy phase too — which is what makes Cancel reach it at all — and reports a cancellation as a normal outcome.MainWindow_OnDropisasync voidwith no guard, so an escapingOperationCanceledExceptionwould have reached the dispatcher handler. Both entry points handle it themselves.PrepareYtDlpAsyncandCheckForAppUpdateAsyncwere started with_ = …, never tracked and never cancelled. They are held in fields now, cancelled on shutdown, and check the token before writing view model state again. Their catch-all no longer hides real defects: a cancellation stays silent, anything else is logged.Changed
OnBatchItemPropertyChangedcalledUpdateBatchSummary()andRaiseCommandStates()for every property of every item, includingProgress, which is written on each FFmpeg tick.RaiseCommandStatesmakes WPF re-evaluate 24 predicates synchronously on the UI thread — several do file system calls, three allocate an array over the whole queue, and two build a complete filter plan. The refresh is now limited to the properties that actually feed the summary counts and the command predicates (Status,ComparisonReport,HasComparisonWarnings,OutputPath).Benutzer-Tools,App-OrdnerandTools-Ordnerwere built inToolDiscoveryServiceand handed straight to the app bar, so an English user read German labels in an otherwise translated interface.ToolLocationnow carries a resource key thatToolStatus.DisplayTextresolves.Two deviations from the plan
AnalyzeBatchItemsAsyncdisposing itsSemaphoreSlimwhile releasers were still running.Task.WhenAllwaits for all tasks to complete, not just until the first one fails, and every task releases in its ownfinallybefore completing — so theusingnever disposes early. No fix was needed and none was made.Disposecancels the startup tasks but does not await them. They resume on the dispatcher, so blocking that thread while it has to run their continuations would deadlock. Cancelling plus a token check before each state write achieves the same end without the deadlock.Verification
The UI behaviour is not covered by tests and needs a manual pass: drop a folder with many files and check Cancel works; start a batch export and check the window stays responsive; switch the language to English and check the tool source in the app bar.