Move path sanitization before directory handling in extractFile so
directory entries with "../" prefixes are rejected the same way file
entries already are. Add the same sanitization to extractSevenZipMultiPart
and both zip extraction loops in the Windows updater, which had no path
validation at all.
A failed download has no usable output file, so error notifications now
carry no buttons at all; only the done event offers open file / open
folder actions.
Terminal task notifications now carry actionable buttons and respond to
clicks instead of being inert:
- Done notifications offer Open File and Open Folder for single-file
tasks, and Open Folder only for folder tasks (e.g. multi-file
torrents), following the same rule as the task details page. Error
notifications offer Open Folder only, since the output is rarely
usable.
- Clicking the notification body brings the app window back to the
front (show + focus), covering the close-to-tray case.
- The click target is snapshotted into the notification (Windows: action
arguments; macOS/Linux: notification payload) so buttons keep working
after the task record is deleted, and a missing file is logged instead
of surfacing an OS error dialog.
- macOS resolves buttons through three pre-registered notification
categories (done single-file / done folder / error); Windows uses the
actions API of the pinned notification plugin fork; Linux uses
freedesktop notification actions.
- New openFolder label localized in all 22 ARB catalogs.
Cold-start activation is intentionally out of scope: when the process is
not running, OS activation opens the main window without dispatching the
button action.
Address the completion review findings on top of the upstream review
fixes:
- Store runs on the engine's checkpoint goroutine without the task
lock and serializes the returned value afterwards: take the fetcher
mutex and return a deep copy instead of the live state pointer,
matching the HTTP protocol's Store pattern
- Load the journal and per-run base counters before publishing the run
as active, so Stats/Progress never observe half-initialized fields
- Bind the journal fingerprint to the key IV as well: a rotated IV on
an otherwise identical plan would silently decrypt to garbage
- Keep every EXT-X-MEDIA member per group instead of only the first
one, so a group with a URI on a later member is still detected
- After a merge that raced with a pause, keep the staged segments and
exit silently instead of cleaning them up; drop a dead context
check and an unused helper, and make the MAP identity and merge
part-file names unambiguous
The merge pipeline concatenates stored bytes, so features it cannot
reproduce faithfully are now detected and rejected with a clear error
instead of silently succeeding with a broken output:
- EXT-X-GAP (a hole in the merged file) and EXT-X-DISCONTINUITY
(a broken timeline)
- separate audio renditions referenced by the selected variant: the
master EXT-X-MEDIA groups are parsed and an AUDIO group with its own
URI fails the resolve; unreferenced groups are ignored
- changing init sections: a repeated EXT-X-MAP is compared by all of
its attributes (URI and BYTERANGE), not just the URI
- encrypted EXT-X-MAP without an explicit IV: the spec does not define
a sequence-derived IV for init sections
- non-identity AES-128 KEYFORMAT values
The supported matrix is documented in the package doc.
Add regression tests for every rejection plus real media validation:
ffmpeg-generated TS segments and fMP4 init/fragments are committed as
fixtures, downloaded through the fetcher (including an AES-128 key
rotation variant), and the merged outputs are validated structurally -
TS on the 188 byte grid with continuity counter rules, PAT/PMT and PES
presence, fMP4 by walking the ISO-BMFF box hierarchy.
- Give every task a random staging identity persisted in the saved
state: two tasks downloading the same playlist into the same
directory get separate segment folders and journals instead of
deleting each other's progress
- Bind the journal to a fingerprint of the full download plan (media
URL, ordered segments, ranges, keys); foreign or unreadable journals
are discarded rather than applied to the wrong segments, and the
ledger is rewritten through a temp file + rename
- Legacy states without a staging identity migrate to a fresh folder
instead of reusing the shared URL-hash folder
- Merge into a unique temporary file next to the target, check write
and close errors, then remove-then-rename over the final name: a
failed merge preserves the staged segments for a retry and re-merging
after a crash stays idempotent
- Drop the staging folder when a URL patch invalidates the plan