File hashing in naming schemes: {crc32}/{md5}/{sha1} via Hash Files

Closes the last remaining gap in porting-roadmap item 1 (full naming-
scheme bindings) - user picked this from a short list of next-feature
options after the recent naming-scheme/Match usability work.

crates/aurora-curator-core/src/sfv: new compute_all_checksums(path),
alongside the existing compute_checksum(path, kind). Streams the file
ONCE, updating CRC32/MD5/SHA-1 hashers concurrently over each chunk,
rather than calling compute_checksum three times (three full reads of
what's often a multi-gigabyte video file). The SFV panel itself keeps
using compute_checksum directly - it only ever needs one algorithm at a
time, picked by the user, so computing the other two nobody asked for
would be wasteful in the other direction. 3 new tests: matches known
CRC32/MD5/SHA-1 test vectors together, agrees with compute_checksum on a
file bigger than the read buffer (catches a hasher only seeing the first
chunk), errors on a missing file.

src-tauri/src/sfv.rs: new NamingHashOutcome struct (plain
#[serde(rename_all = "camelCase")] struct, not a tagged enum - the class
of bug fixed in the nineteenth delivery doesn't apply here) and
compute_naming_hashes command, batching compute_all_checksums the same
blocking-thread way compute_checksums already does, with the same
per-file error isolation (one bad file doesn't abort the batch).
Registered in lib.rs.

RenamePanel.tsx: new runHashFiles() and a "Hash Files" button (new
HashIcon in Icons.tsx) next to Fetch Data. Deliberately NOT wired into
Match automatically like the rest of its local steps - hashing reads a
file's entire contents rather than a small header, so running it
unasked on every Match click for every loaded file would make Match
unpredictably slow. Its own busy state (hashing) disables Match/Fetch
Data/Hash Files against each other while any one is running. New "File
hashing" field-reference group ({crc32}/{md5}/{sha1} as clickable chips,
reusing the field-chip insert-at-cursor mechanism from the earlier
naming-scheme UX delivery) and updated inline help text.

Verified via cargo test --workspace (191 -> 193 in the core crate: 145 ->
148) + cargo check --workspace (clean) + npm run build (clean) + a
10-check Playwright script against npm run dev (mocking
window.__TAURI_INTERNALS__.invoke): confirmed the three field chips are
present, Hash Files calls compute_naming_hashes with every loaded path,
the status line correctly reports hashed-vs-failed counts, a computed
CRC32 reaches the live rename preview, a failed file's row doesn't
fabricate a hash, and clicking Hash Files with nothing loaded is handled
gracefully. Caught one test-mock bug along the way (not an app bug): an
early mock response for a successful hash omitted the `error` field
entirely rather than setting it to `null`, which the app correctly (per
the real Rust command's always-present Option<String> field) treated as
a truthy non-null value and skipped merging - fixed the mock to match
the real wire contract.

Updated README (status paragraph, project layout for sfv.rs's now-four
commands, a new "Hash Files" section placed after "Real media-file
probing", porting roadmap item 1's "still missing" list).
This commit is contained in:
Renamr Scaffold
2026-08-30 04:53:04 +00:00
parent 25dedc1c36
commit 6d98750642
6 changed files with 276 additions and 16 deletions
+40 -13
View File
@@ -35,7 +35,9 @@ file's own container header for its real resolution, frame rate, duration, and
codecs — no ffmpeg/MediaInfo dependency required — filling in `{resolution}`/
`{video codec}`/`{audio codec}` whenever the filename itself didn't already carry
that tag, plus `{width}`/`{height}`/`{fps}`/`{duration}`/`{file size}`, which have no
filename-tag equivalent at all; see "Real media-file probing" below. The Align
filename-tag equivalent at all; see "Real media-file probing" below. A separate **Hash
Files** button computes real `{crc32}`/`{md5}`/`{sha1}` checksums on demand (deliberately
not automatic, since it reads a file's entire contents — see "Hash Files" below). The Align
Up/Down buttons reorder the (shared) file list — the two
panes always stay positionally paired — and up to 9 naming schemes can be saved as
presets, recalled with a click or a `1`-`9` keypress (persisted locally, same pattern as
@@ -240,14 +242,18 @@ src-tauri/ Tauri app shell: exposes aurora-curator-core as commands
`browse_episodes` command backing the Episodes panel — series
search + one season's episode list, via a `build_tv_client` helper
now shared with `metadata.rs`'s `fetch_metadata`, so both read the
same stored TV-source setting and credential), `sfv.rs` (three
commands backing the SFV panel — `compute_checksums` over a batch
of loaded files with per-file error isolation, `save_checksum_file`
via a native "Save File" dialog, and `verify_checksum_file`, which
reads and parses a picked `.sfv`/`.md5`/`.sha1` file and resolves
each entry's bare filename relative to that file's own parent
directory — all wrapping `aurora-curator-core::sfv`, which does the
actual algorithm/format work),
same stored TV-source setting and credential), `sfv.rs` (four
commands backing the SFV panel and the Rename panel's Hash Files
button — `compute_checksums` over a batch of loaded files with
per-file error isolation, `save_checksum_file` via a native "Save
File" dialog, `verify_checksum_file`, which reads and parses a
picked `.sfv`/`.md5`/`.sha1` file and resolves each entry's bare
filename relative to that file's own parent directory, and
`compute_naming_hashes` — all four wrapping
`aurora-curator-core::sfv`, which does the actual algorithm/format
work; `compute_naming_hashes` calls the crate's
`compute_all_checksums` rather than `compute_checksum` three times,
streaming each file once instead of three),
`history.rs` (rename history/undo
log), `presets.rs` (up to 9 saved naming schemes) — all three
stored as JSON in the OS app-config directory — `list.rs` (a
@@ -601,6 +607,24 @@ its own **Resolution**/**Video codec** chip rows (populated from the same merged
filename-tag-or-probed value) so a batch can be narrowed by either, not just by
extension/kind/name/season.
## Hash Files
A naming-scheme binding source, same idea as real media-file probing above, but for
`{crc32}`/`{md5}`/`{sha1}` — reuses the SFV panel's own hasher
(`aurora_curator_core::sfv`) rather than a separate implementation. Unlike Match's other
local steps (filename parsing, container-header probing), this is deliberately **not**
wired into Match automatically: a checksum needs the file's *entire* contents streamed
through, not just a small header or filename, so hashing a batch of large video files
can take real time — running it unasked on every Match click would make Match
unpredictably slow for schemes that don't even use a hash field. Instead, the Rename
panel's New Names list has its own **Hash Files** button next to Fetch Data; clicking it
computes all three checksums for every loaded file in one pass per file (not three
separate passes) and fills in `{crc32}`/`{md5}`/`{sha1}` for whichever files succeeded,
reporting how many hashed vs. failed (a file that's moved or deleted between loading and
hashing, a permissions problem, etc. — kept per-file, so one bad file doesn't block the
rest of the batch). The naming-scheme editor's field-reference list has a "File hashing"
group with all three as clickable buttons.
## Porting roadmap (not yet done)
Roughly in priority order — each references the original FileBot source file it's
@@ -619,10 +643,13 @@ ported from, for anyone picking this up:
for the two container families it supports (MP4/M4V/MOV and MKV/WebM). Probed
bindings are now wired into all three panels that do local matching — Rename's
Match step, and the Filter and List panels' own scans (Filter also gained
Resolution/Video codec filter chips from it). Still missing: probing for other
containers (.avi, .wmv, ...), audio-only metadata (bitrate, sample rate, channel
layout), and file hashing (`{crc32}` reuses the SFV panel's own hasher today but
isn't wired into naming-scheme bindings yet).
Resolution/Video codec filter chips from it). File hashing is done too —
`{crc32}`/`{md5}`/`{sha1}` reuse the SFV panel's own hasher (all three computed in a
single pass over the file, rather than reading it three times), wired to the Rename
panel's own "Hash Files" button rather than Match, since a full-file read is much
slower than everything else Match does and shouldn't run unasked for every batch
(see "Hash Files" below). Still missing: probing for other containers (.avi, .wmv,
...) and audio-only metadata (bitrate, sample rate, channel layout).
2. **String/expression helper methods** — `net.filebot.format.ExpressionFormatMethods`
(636 lines): FileBot-specific Groovy string extensions like `.pad()`, `.sortName()`.
Ported so far (`crates/aurora-curator-core/src/format/functions.rs`): `any`, `all_of`,