Skip to content

v0.31.0

Latest

Choose a tag to compare

@github-actions github-actions released this 16 Jul 10:44
eea8742

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 EditionKey duplicate 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, and TrailingSpace path 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_space options to AuditOptions, gating each check
  • Add leading_period, leading_space, and trailing_space demo samples;
    bump the asserted count from 24 to 27
  • Add the three --ignore-* entries to docs/COMMANDS.md and regenerate
    docs/OPTIONS.md and the config/docs snapshots
audit: flag unnecessary directional marks 4f040f4
  • Add an UnnecessaryDirectional check that reports directional formatting
    marks, gated on !contains_rtl so a mark is flagged only where no
    right-to-left script makes it meaningful
  • Add StrRtlExt::contains_rtl in rtl.rs, a conservative superset of the
    right-to-left script blocks that never false-flags real RTL text
  • Add an ignore_directional option to AuditOptions
  • Split directional marks out of SanitizerRule::invisible into
    directional, so they no longer double-report as invisible
  • Add Sanitizer::non_printing for metadata and source_provider, and add
    directional to Sanitizer::name so 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 .torrent file, or a directory as the audit_arg positional (PATH | ID)
  • Download a torrent by id from the indexer API via TorrentFileProvider, validating credentials and cache first
  • Classify input into AuditMode::Id, File, or Directory with AuditArgs::to_mode
  • Make execute_cli async and register ConfigOptions on the Audit command
  • Regenerate docs/COMMANDS.md and docs/OPTIONS.md
audit: scan torrent files for problematic paths a1f378c
  • Add audit command scanning a directory or single .torrent file for problematic path components
  • Bencode-decode tolerant of non-UTF-8 bytes via BencodeAdapter, RawString, and DecodedString
  • Suggest candidate decodings from the encoding field, 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 AuditArgs scan path and AuditOptions ignore flags, wired through DI and config
  • Derive Serialize/Deserialize on SanitizerChar and expose is_single_safe_segment
  • Add Differ/DiffRenderer rendering grapheme-aware diffs with hex-escaped non-UTF-8 bytes for suggestion previews
  • Add cesu8, chardetng, encoding_rs, similar, and unicode-normalization dependencies
verify: add FLAC decode test and missing MD5 detection #56 f2154d0
  • Add DecodeVerifier that fully decodes every audio frame of each
    source FLAC, surfacing truncation or corruption as a new
    SourceIssue::DecodeError that blocks verification
  • Decode test runs by default; add --no-decode-test to skip it
  • Detect unset STREAMINFO MD5 signatures in StreamVerifier, reporting
    a new SourceIssue::MissingMd5 that 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 / in is_single_safe_segment so a trailing separator, which
    Path::components silently drops, is caught rather than treated as safe
  • A torrent name or path segment ending in / now reports UnsafeSegment,
    matching the existing behavior for \
  • Add torrent_auditor tests for a forward slash, backslash, and trailing
    slash, asserting both RestrictedChars and UnsafeSegment
  • Cover the safe_candidates caller with a trailing-slash InvalidFilePath case
  • Add unsafe-3 and unsafe-4 forward-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 required String (default "") not Option<String>
  • Regenerate docs/OPTIONS.md and the docs command snapshot to match, fixing the failing snapshot tests
formats: include remaster year in `EditionKey` #244 086f392
  • Add remaster_year field so editions differing only by year no longer merge
  • Normalize Some(0) to None so blank years from the tracker compare equal
  • Require year equality in is_less_specific_than alongside 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::InvalidTorrent carrying the parse error detail
  • Map LavaTorrentError::MalformedTorrent so verify returns an unverified result instead of a hard error
  • Keep bencode and I/O read failures as Failure errors
  • Return the issue through TorrentVerifier, letting batch persist status instead of re-skipping every run
runner: clamp cpus to at least 1 60a9011
  • Add RunnerOptions::get_cpus returning a clamped usize
  • Route the semaphore factory through it so --cpus 0 no longer creates a zero-permit Semaphore that deadlocks on first acquire
  • Use usize for cpus
transcode: fail on non-zero flac decode exit #242 1ba6a76
  • A truncated source made flac exit 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 Err with new TranscodeAction::Decode so 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 lame stage that builds LAME 4.0 from source, pinned with a
    sha256, matching the existing FLAC and SoX_ng stages
  • Pass --disable-decoder to avoid the new libmpg123 dependency, since
    caesura only encodes
  • Downgrade the GCC 14 implicit-function-declaration and
    incompatible-pointer-type errors triggered by LAME's inconsistent id3
    frontend to warnings so the source builds on Alpine
  • Replace the unpinned apk add lame in the dev and runtime stages with
    the built artifacts

Documentation

cross: document the `cross` command b00976f
  • Add a cross command 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-config file and the --qbit-cross/--copy-cross-torrent-to/--dry-run requirement
  • Link SETUP.md for the qBittorrent connection and list the --qbit-cross-* placement overrides

Refactor

audit: rename lost extension to broken extension 05ed56d
  • Rename the ignore_lost_extension option to ignore_broken_extension,
    along with the --ignore-lost-extension CLI flag and config key
  • Rename AuditPathIssueKind::RestrictedChars to Restricted and
    InvisibleChars to Invisible
  • Derive Display for AuditPathIssueKind via thiserror instead of a
    hand-written impl, and drop the BrokenExtension special-case render
    branch in favor of the uniform Contains {kind} characters message
  • Rename the audit_lost_extension, lost_extension, and
    render_lost_extension helpers, the non_utf8_lost_extension_* sample
    producers, and the affected tests to broken_extension
  • Add the missing --ignore-directional entry to the docs/COMMANDS.md
    check list and regenerate docs/OPTIONS.md and config/docs snapshots
verify: extract `ContentVerifier` from `VerifyCommand` cfc5bd0
  • Add injectable ContentVerifier owning the torrent hash check
  • Self-gate on no_hash_check and inline the source torrent fetch
  • Drop hash_check/get_source_torrent and their deps from VerifyCommand
  • Relocate the torrent download tests to TorrentFileProvider
verify: extract `TorrentPieceHasher` from `TorrentVerifier` b8ecd72
  • Move piece SHA-1 hashing into a TorrentPieceHasher iterator
  • Reduce verify to 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-test flag to --no-decode-check
  • Standardize verify logs with bold Checking/Checked/Skipped markers
  • Add trace logs to ApiVerifier, DecodeVerifier and FlacVerifier

verify: add elapsed time trace to TorrentVerifier 09c44b2

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 VerifyCommand into injectable FlacVerifier
  • Make DecodeVerifier injectable and decode the FLAC list in one pass
  • Add Collector::collect_flacs for the directory guard and centralized collection
  • Relocate check_flac_count, check_subdirectory, check_path_length and their tests to FlacVerifier
  • Move collection tests to collector_tests; rename decode tests to decode_flac_*

Tests

audit: rename demo samples by scenario and add slash lookalikes cc14d28
  • Rename every generate_audit_samples producer after its scenario, e.g.
    libtorrent_folder to ltr_mark_in_folder, unsafe_track to
    backslash_in_track, single_file_album to single_file_no_files_list_red
  • Add trailing-slash lookalike folder samples: backslash, division slash
    U+2215, fullwidth solidus U+FF0F, and an overlong-encoded 0xC0 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_kinds test helper returning the full
    HashSet of kinds present on an item
  • Replace has_kind/has_path_kind presence checks with exact-set
    assert_eq!, surfacing kinds the old checks hid (a / or \ also
    reports RestrictedChars and LibtorrentStripped; a lost extension
    also reports NonUtf8)
  • Rename torrent_auditor tests after the input scenario, dropping the
    execute_bytes_ prefix and asserted-kind tokens
  • Drop redundant doc comments, keeping only the non_utf8 and cesu8
    suggestion-recovery cases
pass the full suite in the Docker image 3123022
  • Skip sox_factory_sox_ng_false when is_docker(), since the image
    forces sox_ng and 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_1s
    • spectrogram_command_track_30s
    • spectrogram_command_flac16_48
    • spectrogram_command_flac16_441
    • transcode_command_flac24_48
    • transcode_command_flac24_96

Chores

docker: add `lame` to source dependency SBOM 39156df
  • Add a lame component for the LAME 4.0 stage built from source in the
    Dockerfile, matching the existing flac and sox_ng entries
  • Record the LGPL-2.0-or-later license, purl, website, and distribution URL
update recommended LAME version to `4.0` de26b97
  • Drop --vbr-new from the V0 encode; it has been the default VBR routine since LAME 3.98, so it only altered the info-tag header
  • Regenerate transcode snapshots for LAME 4.0 output
  • Bump recommended LAME to 4.0 in docs
  • Docker image still uses the Alpine-packaged LAME; source pinning is not yet applied