Skip to content

Slskd release quality is taken from peer-advertised attributes and never verified #85

Description

@chodeus

A slskd release is labelled [FLAC 24bit] on the strength of the sharing peer's advertised file attributes alone. Nothing checks that claim — not before the download, not after it, not before import.

What prompted this

2026-02-12  grabbed  'Muse - Resistance [FLAC 24bit] [WEB]'  indexer=Slskd

Every file that arrived is 16-bit / 44.1 kHz, ~885–1123 kbps, and all three carry a comment tag of hd24bit.com. The audio is genuinely lossless — spectrals are clean to 22.05 kHz with no shelf and no blocks — it simply was never 24-bit. The peer said 24, so the title said 24.

Where the number comes from

slskd search result   → SlskdFileData.BitDepth        SlsdkRecords.cs:27   (peer-advertised)
AnalyzeAudioQuality   → most-common value             SlskdItemsParser.cs:792-800
AlbumData.BitDepth    = BitDepth ?? 0                 SlskdItemsParser.cs:268
ConstructTitle()      → $" [{Codec} {BitDepth}bit]"   AlbumData.cs:131-132

No verification anywhere on that path.

Why it cannot be fully solved before the download

Soulseek offers no way to read a file header before transferring; advertised attributes are the only pre-download signal. A plausibility check is still possible — FLAC essentially never compresses below roughly half of bitDepth × sampleRate × channels, and AnalyzeAudioQuality already computes totalSize and totalDuration — but it only catches egregious claims. A folder advertised as 24/96 at an implied ~900 kbps is provably impossible; 24/44.1 against 16/44.1 overlaps and cannot be separated this way. That makes a post-download check the only authoritative one.

Three gaps

1. Sample rate is collected and then discarded. AnalyzeAudioQuality returns it and FormatQualityInfo formats it into qualityInfo at SlskdItemsParser.cs:223 — which is never read again. SampleRate never reaches AlbumData. The field the plausibility check needs is thrown away, and the dead assignment should go either way.

2. Most-common bit depth wins, nulls included. directory.GroupBy(f => f.BitDepth) at SlskdItemsParser.cs:799 takes the majority, so a folder mixing 24- and 16-bit files advertises the majority depth and misdescribes the rest. It should claim a depth only when every audio file agrees. The existing fallback is already correct when a peer sends nothing at all: BitDepth ?? 0 produces a bare [FLAC].

3. Nothing checks the files once they land. CorruptionScanner already runs ffmpeg over every downloaded file and opens it with TagLib, and PreImportTagger already rewrites tags. Reading Properties.BitsPerSample and AudioSampleRate at that point costs almost nothing. The existing AcoustID gate verifies which recording a file is, so a verification stage exists — it is simply aimed at identity rather than quality.

Proposed

  • Carry SampleRate through to AlbumData alongside BitDepth, or drop the dead qualityInfo if it is not going to be used.
  • Require agreement across all audio files before claiming a bit depth; fall back to the bare codec otherwise.
  • Reject an impossible claim pre-download by comparing implied bitrate against the floor for the advertised depth and rate.
  • Verify real bit depth and sample rate in post-processing, and either correct what is reported or fail the item when it does not match what was advertised.

Not affected

Lidarr re-detects quality from the real files at import, so the library record is correct — the three tracks are stored as FLAC with sampleRate 44.1kHz and no bit depth. A false claim cannot poison upgrade decisions. The cost is in release selection, where a bogus 24-bit claim can outrank a genuine 24-bit candidate and waste the transfer, and in what the operator is told when judging whether a file is what it says it is.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions