Highlights
A new audit command, faster verification, and a LAME 4.0 upgrade.
Faster verification
Verify now decodes FLACs concurrently across all CPUs and fails early: if the FLAC checks fail, the expensive decode checks are skipped entirely. Redundant checks were removed and elapsed timings added for tracing. #56
FLAC decode test and MD5 detection
Verify now runs a FLAC decode test and detects a missing embedded MD5, catching corrupt sources. Transcode fails on a non-zero FLAC decode exit. #56 #242
Audit command
The new audit command scans a directory or .torrent file for problematic path components before you upload. It flags non-UTF-8 bytes, invisible/zero-width and directional marks, leading periods, surrounding spaces, trailing slashes, slash lookalikes, and paths libtorrent would strip. It also suggests candidate decodings for mangled filenames with grapheme-aware diff previews. Audit by directory, single torrent, or tracker id.
LAME 4.0
The recommended LAME version is now 4.0, built from source in the Docker image.
Bug fixes
- Include remaster year in
EditionKeyduplicate detection #244 - Handle unparseable torrents as a source issue rather than crashing #243
- Only check the MD5 of CD sources during verify
- Clamp CPU count to at least 1
Changes
New Features
audit: flag leading period and surrounding spaces eea8742
- Add
LeadingPeriod,LeadingSpace, andTrailingSpacepath checks that
flag torrent folder and file components beginning with a period, beginning
with a space, or ending with a space- Match only the literal ASCII space, since Unicode invisible and
non-breaking whitespace is already reported by the invisible check- Add
ignore_leading_period,ignore_leading_space, and
ignore_trailing_spaceoptions toAuditOptions, gating each check- Add
leading_period,leading_space, andtrailing_spacedemo samples;
bump the asserted count from 24 to 27- Add the three
--ignore-*entries todocs/COMMANDS.mdand regenerate
docs/OPTIONS.mdand the config/docs snapshots
audit: flag unnecessary directional marks 4f040f4
- Add an
UnnecessaryDirectionalcheck that reports directional formatting
marks, gated on!contains_rtlso a mark is flagged only where no
right-to-left script makes it meaningful- Add
StrRtlExt::contains_rtlinrtl.rs, a conservative superset of the
right-to-left script blocks that never false-flags real RTL text- Add an
ignore_directionaloption toAuditOptions- Split directional marks out of
SanitizerRule::invisibleinto
directional, so they no longer double-report as invisible- Add
Sanitizer::non_printingformetadataandsource_provider, and add
directionaltoSanitizer::nameso filenames also strip the isolates- Add four RTL demo samples (necessary mark beside RTL, isolate,
U+202E
override spoof, inert RTL mark); bump the asserted count from 20 to 24
audit: audit a torrent by tracker id 71b4d88
- Accept a tracker id, a single
.torrentfile, or a directory as theaudit_argpositional (PATH | ID)- Download a torrent by id from the indexer API via
TorrentFileProvider, validating credentials and cache first- Classify input into
AuditMode::Id,File, orDirectorywithAuditArgs::to_mode- Make
execute_cliasync and registerConfigOptionson theAuditcommand- Regenerate
docs/COMMANDS.mdanddocs/OPTIONS.md
audit: scan torrent files for problematic paths a1f378c
- Add
auditcommand scanning a directory or single.torrentfile for problematic path components- Bencode-decode tolerant of non-UTF-8 bytes via
BencodeAdapter,RawString, andDecodedString- Suggest candidate decodings from the
encodingfield,chardetng, Windows-1252, and CESU-8- Flag non-UTF-8, invisible/zero-width, libtorrent-stripped, and unsafe path segments
- Flag decomposed (non-NFC) path components, ignorable via
ignore_nfd- Add
AuditArgsscan path andAuditOptionsignore flags, wired through DI andconfig- Derive
Serialize/DeserializeonSanitizerCharand exposeis_single_safe_segment- Add
Differ/DiffRendererrendering grapheme-aware diffs with hex-escaped non-UTF-8 bytes for suggestion previews- Add
cesu8,chardetng,encoding_rs,similar, andunicode-normalizationdependencies
verify: add FLAC decode test and missing MD5 detection #56 f2154d0
- Add
DecodeVerifierthat fully decodes every audio frame of each
source FLAC, surfacing truncation or corruption as a new
SourceIssue::DecodeErrorthat blocks verification- Decode test runs by default; add
--no-decode-testto skip it- Detect unset STREAMINFO MD5 signatures in
StreamVerifier, reporting
a newSourceIssue::MissingMd5that always blocks verification- Both issues are reportable under the "Other" report type
Bug Fixes
audit: flag a trailing forward slash as unsafe 2b24cd4
- Reject any
/inis_single_safe_segmentso a trailing separator, which
Path::componentssilently drops, is caught rather than treated as safe- A torrent name or path segment ending in
/now reportsUnsafeSegment,
matching the existing behavior for\- Add
torrent_auditortests for a forward slash, backslash, and trailing
slash, asserting bothRestrictedCharsandUnsafeSegment- Cover the
safe_candidatescaller with a trailing-slashInvalidFilePathcase- Add
unsafe-3andunsafe-4forward-slash demo samples
verify: only check MD5 of CD 023d4dd
audit: mark `audit_arg` as required d53a3da
- Add
#[options(required)]so the positional documents as a requiredString(default"") notOption<String>- Regenerate
docs/OPTIONS.mdand the docs command snapshot to match, fixing the failing snapshot tests
formats: include remaster year in `EditionKey` #244 086f392
- Add
remaster_yearfield so editions differing only by year no longer merge- Normalize
Some(0)toNoneso blank years from the tracker compare equal- Require year equality in
is_less_specific_thanalongside title and media- Add tests for distinct years, unset-year variants, and the possible-dupe year gate
verify: handle unparseable torrents as a source issue #243 a0daf99
- Add
SourceIssue::InvalidTorrentcarrying the parse error detail- Map
LavaTorrentError::MalformedTorrentsoverifyreturns an unverified result instead of a hard error- Keep bencode and I/O read failures as
Failureerrors- Return the issue through
TorrentVerifier, lettingbatchpersist status instead of re-skipping every run
runner: clamp cpus to at least 1 60a9011
- Add
RunnerOptions::get_cpusreturning a clampedusize- Route the semaphore factory through it so
--cpus 0no longer creates a zero-permitSemaphorethat deadlocks on first acquire- Use
usizefor cpus
transcode: fail on non-zero flac decode exit #242 1ba6a76
- A truncated source made
flacexit non-zero, but the decode failure
was only logged as a warning while the encode proceeded on the partial
stream, producing a truncated transcode that was marked complete and
uploaded- Return
Errwith newTranscodeAction::Decodeso the release is not
marked transcoded, no torrent is built, and batch mode continues to
the next source
Performance
verify: skip decode checks if FLAC checks invalid b8b4f01
verify: decode FLACs concurrently across cpus 0b31406
- Run each FLAC decode on the blocking pool, bounded by
RunnerOptions::get_cpus- Preserve input order with
buffered, so issues need no sort- Add async test covering a mixed batch with one truncated FLAC
Build
docker: build LAME `4.0` from source 0dcd385
- Add a
lamestage that builds LAME4.0from source, pinned with a
sha256, matching the existing FLAC and SoX_ng stages- Pass
--disable-decoderto avoid the newlibmpg123dependency, since
caesura only encodes- Downgrade the GCC 14
implicit-function-declarationand
incompatible-pointer-typeerrors triggered by LAME's inconsistent id3
frontend to warnings so the source builds on Alpine- Replace the unpinned
apk add lamein the dev and runtime stages with
the built artifacts
Documentation
cross: document the `cross` command b00976f
- Add a
crosscommand guide section covering cross-seeding a source onto a second indexer- Note it supports an unreleased feature and recommend qui's cross-seeding
- Document the
--cross-configfile and the--qbit-cross/--copy-cross-torrent-to/--dry-runrequirement- Link
SETUP.mdfor the qBittorrent connection and list the--qbit-cross-*placement overrides
Refactor
audit: rename lost extension to broken extension 05ed56d
- Rename the
ignore_lost_extensionoption toignore_broken_extension,
along with the--ignore-lost-extensionCLI flag and config key- Rename
AuditPathIssueKind::RestrictedCharstoRestrictedand
InvisibleCharstoInvisible- Derive
DisplayforAuditPathIssueKindviathiserrorinstead of a
hand-written impl, and drop theBrokenExtensionspecial-case render
branch in favor of the uniformContains {kind} charactersmessage- Rename the
audit_lost_extension,lost_extension, and
render_lost_extensionhelpers, thenon_utf8_lost_extension_*sample
producers, and the affected tests tobroken_extension- Add the missing
--ignore-directionalentry to thedocs/COMMANDS.md
check list and regeneratedocs/OPTIONS.mdand config/docs snapshots
verify: extract `ContentVerifier` from `VerifyCommand` cfc5bd0
- Add injectable
ContentVerifierowning the torrent hash check- Self-gate on
no_hash_checkand inline the source torrent fetch- Drop
hash_check/get_source_torrentand their deps fromVerifyCommand- Relocate the torrent download tests to
TorrentFileProvider
verify: extract `TorrentPieceHasher` from `TorrentVerifier` b8ecd72
- Move piece SHA-1 hashing into a
TorrentPieceHasheriterator- Reduce
verifyto zipping expected hashes against yielded digests- Add unit tests covering full, partial, empty and exact-multiple streams
verify: rename `no_decode_test` and standardize check logs 1625be2
- Rename
--no-decode-testflag to--no-decode-check- Standardize verify logs with bold
Checking/Checked/Skippedmarkers- Add trace logs to
ApiVerifier,DecodeVerifierandFlacVerifier
verify: add elapsed time trace to
TorrentVerifier09c44b2
verify: skip redundant verify checks 1c321d7
- If API check invalid: skip hash check, FLAC checks and reports
- If hash check invalid: skip FLAC checks and reports
- Extract FLAC-level checks from
VerifyCommandinto injectableFlacVerifier- Make
DecodeVerifierinjectable and decode the FLAC list in one pass- Add
Collector::collect_flacsfor the directory guard and centralized collection- Relocate
check_flac_count,check_subdirectory,check_path_lengthand their tests toFlacVerifier- Move collection tests to
collector_tests; rename decode tests todecode_flac_*
Tests
audit: rename demo samples by scenario and add slash lookalikes cc14d28
- Rename every
generate_audit_samplesproducer after its scenario, e.g.
libtorrent_foldertoltr_mark_in_folder,unsafe_trackto
backslash_in_track,single_file_albumtosingle_file_no_files_list_red- Add trailing-slash lookalike folder samples: backslash, division slash
U+2215, fullwidth solidusU+FF0F, and an overlong-encoded0xC0 0xAF- Bump the asserted sample count from 16 to 20
audit: assert exact issue-kind sets and rename by scenario 12ea290
- Add
AuditItem::get_issue_kindstest helper returning the full
HashSetof kinds present on an item- Replace
has_kind/has_path_kindpresence checks with exact-set
assert_eq!, surfacing kinds the old checks hid (a/or\also
reportsRestrictedCharsandLibtorrentStripped; a lost extension
also reportsNonUtf8)- Rename
torrent_auditortests after the input scenario, dropping the
execute_bytes_prefix and asserted-kind tokens- Drop redundant doc comments, keeping only the
non_utf8andcesu8
suggestion-recovery cases
pass the full suite in the Docker image 3123022
- Skip
sox_factory_sox_ng_falsewhenis_docker(), since the image
forcessox_ngand the case only applies outside Docker- Structural tests now all pass in the Docker image
- Deterministic tests stay host-only: inside the image the SoX_ng and FLAC
output diverges from the host-calibrated snapshots while the LAME MP3
output matches. Still failing deterministically in the image:
spectrogram_command_track_1sspectrogram_command_track_30sspectrogram_command_flac16_48spectrogram_command_flac16_441transcode_command_flac24_48transcode_command_flac24_96
Chores
docker: add `lame` to source dependency SBOM 39156df
- Add a
lamecomponent for the LAME4.0stage built from source in the
Dockerfile, matching the existingflacandsox_ngentries- Record the
LGPL-2.0-or-laterlicense,purl, website, and distribution URL
update recommended LAME version to `4.0` de26b97
- Drop
--vbr-newfrom the V0 encode; it has been the default VBR routine since LAME3.98, so it only altered the info-tag header- Regenerate transcode snapshots for LAME
4.0output- Bump recommended LAME to
4.0in docs- Docker image still uses the Alpine-packaged LAME; source pinning is not yet applied