Latest Results
feat(link): auto-group split archives into one package (task 31) (#144)
* feat(link): auto-group split archives into one package (task 31)
Cluster Link-Grabber URLs by detected archive base name (`*.partNN.rar`,
`*.rNN`, `*.7z.NNN`, `*.zip.NNN`, `*.tar.{gz,bz2,xz}.NNN`) and create one
`Package` per cluster with `source_type = SplitArchive` and
`external_id = "split-archive:{base}"` for idempotent re-resolve. New
`GroupSplitArchivesCommand` handler + `link_group_split_archives` Tauri
IPC mirror the playlist grouper flow.
Gaps in the part numbering emit a new `DomainEvent::SplitArchiveIncomplete
{ package_id, base_name, missing_parts }` event (forwarded to the
frontend as `split-archive-incomplete`) so the UI can warn the user
before extraction blocks.
Frontend `SplitArchiveLinkInput` / `SplitArchiveGroupResult` types added
in `src/types/media.ts`. 25 service unit tests (matcher fixtures +
grouping integration) + 3 handler tests cover the contract.
Refs PRD-v2 §P1.12, PRD §6.3.
* refactor(link): harden split-archive grouper from /simplify review
Code-review pass on the task-31 split-archive grouper:
- Format-namespaced `external_id` (`split-archive:{format_tag}:{base}`)
so a RAR set and a ZIP set sharing a base name produce two distinct
packages instead of silently collapsing under one row. Replaces the
previous base-only key documented as "acceptable" in the test that
has now been rewritten to assert the correct behaviour.
- New `application/services/group_lock` module shared by
`PlaylistGrouper` and `SplitArchiveGrouper`, removing a duplicated
OnceLock + poisoned-mutex-recovery pair.
- `group_one_base` now releases the grouper lock before publishing
`PackageCreated` / `SplitArchiveIncomplete` events so synchronous
subscribers cannot block other concurrent grouping calls. Same
refactor also collapses the duplicated reuse-vs-created branching
into a single tail block.
- `MAX_LINKS = 500` cap on `group_all` mirrors `MAX_URLS` in
`resolve_links` so a runaway IPC payload cannot allocate unbounded
cluster state. New `test_group_all_caps_link_count_to_avoid_dos`
test covers the path.
- `SplitArchiveFormat`, `DetectedPart`, `detect_from_filename`
demoted to `pub(crate)`: they were exported but only consumed
inside the module. Reduces the public API surface.
* fix(link): detect legacy RAR header + release playlist lock before publish
Legacy multi-volume RAR sets ship the first volume as `name.rar`
followed by `name.r00`, `name.r01`, … The previous matcher only
captured `.rNN`, so a valid `name.rar + name.r00` set produced a
single-part cluster and was dropped by MIN_PARTS_TO_GROUP. Add
`match_legacy_rar_header` (part 0), order it after the other matchers
so the more specific patterns still win, and extend
`compute_missing_parts` to walk from 0 for LegacyRar so a missing
header is reported as `rar` instead of silently dropped.
PlaylistGrouper used to hold the shared grouper lock across
`event_bus.publish`, which can block re-entrant grouping paths and
matches the bug already fixed in SplitArchiveGrouper. Scope the lock
to the find-then-save window and publish after the guard drops.
Addresses CodeRabbit, Codex, and Cubic review feedback on PR #144.
* fix(link): bound part indices + gate on distinct part numbers
Three converging review findings on PR #144:
1. `MIN_PARTS_TO_GROUP` was checked against raw link count, so two
mirrors of `movie.part01.rar` slipped through the singleton guard
and produced a misleading "complete" group with `missing_parts`
empty. Replace `parts.len()` with the distinct `part_num` count.
2. The modern RAR (`\d+`) and legacy RAR (`\d{2,}`) regexes accepted
unbounded suffixes, letting a hostile `name.part1000000000.rar`
force `compute_missing_parts` into a multi-billion-step iteration.
Add `MAX_PART_INDEX = 10_000` and reject anything above it inside
`detect_from_filename` (after the matcher cascade so a rejected
index does not silently cascade into `match_legacy_rar_header`).
3. Defensive `checked_add` on `raw_num + 1` in `match_legacy_rar` so
the (now-impossible after the cap) `u32` overflow path is closed.
Addresses CodeRabbit, Codex, and Cubic feedback.
* docs(changelog): correct split-archive test count (31, not 35)
---------
Co-authored-by: Mathieu Piton <27002047+mpiton@users.noreply.github.com> Latest Branches
-23%
feat/task-33-detect-duplicates +19%
feat/task-31-split-archives N/A
codspeed-wizard-1777816068872 © 2026 CodSpeed Technology