User Guide
Complete guide to using FLAC Detective effectively.
Table of Contents
Basic Usage
Analyze a Directory
# Analyze current directory
flac-detective .
# Analyze specific folder
flac-detective /path/to/music
# Analyze with full path (Windows)
flac-detective "C:\Users\YourName\Music\FLAC"
Analyze a Single File
flac-detective /path/to/file.flac
Recursive Analysis
FLAC Detective automatically scans subdirectories:
Music/
├── Artist 1/
│ ├── Album A/
│ │ ├── 01 - Track.flac ← Analyzed
│ │ └── 02 - Track.flac ← Analyzed
│ └── Album B/
│ └── 01 - Track.flac ← Analyzed
└── Artist 2/
└── Album C/
└── 01 - Track.flac ← Analyzed
All .flac files are found and analyzed automatically.
Command-Line Options
Output Formats
# Default: Text report to console + file
flac-detective /music
# JSON output (for automation)
flac-detective /music --format json
# Save to specific file
flac-detective /music --output custom-report.txt
# JSON to file
flac-detective /music --format json --output results.json
# CSV — library triage: one row per file, ranked most-suspicious first
flac-detective /music --format csv --output triage.csv
# HTML — a visual report you double-click to open (spectrum plots for flagged files)
flac-detective /music --format html --output report.html
Four formats, four jobs:
|
For |
Notes |
|---|---|---|
|
reading |
human-friendly report + console summary |
|
automation |
full result objects, parse with |
|
triaging a whole library |
one row per file, sorted by score (most suspicious first); open in any spreadsheet to sort/filter. Columns: |
|
seeing the evidence |
a single self-contained |
When a scan finds suspicious files, the console summary also prints the top suspects ranked by score, so you immediately see what to check first.
Fake hi-res is a second verdict, on its own line. A file can be genuine lossless
and still not be what its label says: CD audio upsampled to 96 or 192 kHz, or 16-bit
audio padded into a 24-bit (or 32-bit) container. Since v2.2.0 the text report lists
those too — in the default report as 🎚️ FAKE HI-RES under “FILES TO LOOK AT”, in
--advanced as a “FAKE HI-RES FILES” table with the verdict (UPSAMPLED,
PADDED_DEPTH, UPSAMPLED_AND_PADDED) and the reading behind it. The CSV carries it
as hires_verdict, the GUI as its “Hi-res” column. It never changes the transcode
verdict. 32-bit integer FLAC (FLAC 1.4+) is analysed too.
Analysis Options
# Verbose mode (show detailed rule execution)
flac-detective /music --verbose
# Shorter windows (15 seconds per window, faster; reads less of each file)
flac-detective /music --sample-duration 15
# The default, 30 seconds, is the reading every published figure was measured
# at. A longer sample is not more accurate: it reads different audio, and a
# verdict that changes with the number is sitting on a reading boundary.
# Deep mode: run the ML rule (CNN) on every file
flac-detective /music --deep
What a normal scan already does for AAC and Vorbis (since v1.20.0). Before it clears a file the quick rules found nothing on, a normal scan asks Rule 13, which reads the MDCT grid AAC and Vorbis encoders leave behind. On a bench of 33 loud CD tracks it catches 32 of 33 AAC 256 (ffmpeg) and 33 of 33 Vorbis q6 transcodes, for about 4 s more per file. Since v1.20.1 it also asks on files with a low cutoff, which is where a low-bitrate Vorbis encode lands (
-q1: 31 of 33 caught without the ML extra, 33 with it). Since v2.1.0 it also reads Opus, Vorbis at every quality (-q10included) and HE-AAC v2 in a normal scan (issue #12). It does not read Apple’s AAC encoder (the iTunes and Apple Music format) well, nor MP3 320 / VBR: those still need--deep. HE-AAC v1 at low bitrates (64 kbps) is mostly out of reach.When to use
--deep. A normal scan keeps things fast by skipping the ML rule (Rule 12, the CNN) on files the quick heuristic rules and Rule 13 clear. That’s the right default for the common case — but it’s also where a high-bitrate MP3 or Apple AAC transcode hides: those leave no heuristic trace, so they sail through as AUTHENTIC.--deepruns the CNN on every file; when it is highly confident a full-range file is a transcode, the verdict is lifted to WARNING (“worth checking”, never an outright “fake”). The price is speed — every file is decoded and run through the CNN, so a large library takes noticeably longer. Reach for it when you specifically suspect high-bitrate MP3 or Apple AAC sources, or for a thorough second pass on a collection a fast scan called clean. It does not help with band-limited recordings (early-music, 1920s, solo acoustic) — that’s a genuine signal limit no tool can cross.
--deep(the GUI’s “Deep scan” box) is not an on/off switch for the CNN. With the[ml]extra installed, Rule 12 runs on every file the fast rules leave in doubt, deep or not, so seeing a CNN line in a normal scan is expected. Deep only adds the files the fast rules clear at once. To scan with no CNN at all, install without the extra (pip uninstall torch torchaudio): Rule 12 is then a no-op.
Note: Auto-repair of corrupted FLAC files is enabled by default — no flag is needed, and it’s lossless and hi-fi-safe. It triggers only on files that can’t be decoded at all; it rebuilds a valid FLAC with the exact same PCM samples (Xiph’s reference
flactool), preserves tags/artwork, verifies the result, and analyses that copy from the temp directory. Your library is not written to unless you pass--repair-in-place, which replaces the broken file and keeps a.corrupted.bakbeside it. Healthy files are never rewritten. See Technical Details → Repair for the full procedure.
The update check, and installing the update
Since v2.3.0 the tool asks PyPI once a day whether a newer release exists and, if so, prints one line at the end of the run (the GUI shows an “Update available” link in its header). Since v2.4.0 it also installs it when you say so:
flac-detective --update # check PyPI now and install the newer release, nothing scanned
beet flacdetective --update # the same, into beets' Python
On a terminal, a run that ends with the notice asks Install it now? [y/N] — No is
the default, and nothing is asked when the output is piped, in --format json without
--output, or with --progress-events. The GUI shows an Install vX button next to
the link, confirms with a dialog that names the exact command, and tells you to restart
when done. The install runs the same interpreter this copy runs in
(python -m pip install --upgrade flac-detective), or pipx upgrade flac-detective
when this copy was installed by pipx, and it believes a fresh interpreter’s version,
not the installer’s exit code. A source checkout (git pull) and the Docker image
(docker pull) are told what to do instead of being touched. Nothing is ever installed
without that explicit yes.
On Windows the install happens right after you exit. Windows will not let pip
replace flac-detective.exe while it is running (and a failed attempt used to leave no
package at all), so the tool hands the install to a small detached process that waits
for FLAC Detective to exit, then runs pip and checks the version. The CLI tells you so
and returns at once; the GUI offers to close. The next time you start FLAC Detective it
prints what happened (Update installed: FLAC Detective 2.4.0 (was 2.3.0).); the
installer’s full output is in update-install.log beside the update-check cache.
What leaves your machine is one small GET on
https://pypi.org/pypi/flac-detective/json — the same page pip reads — with a
User-Agent naming this tool and its version; nothing about your files, your paths or
your verdicts. The answer is cached for 24 hours (%LOCALAPPDATA%\flac-detective\ on
Windows, ~/.cache/flac-detective/ elsewhere), the request times out after two seconds
and runs on its own thread while the scan does, and any failure is silent: no network,
no notice, no delay. To switch it off:
flac-detective /music --no-update-check # this run
export FLAC_DETECTIVE_NO_UPDATE_CHECK=1 # every run (the Docker image sets it)
Combining Options
# Verbose + JSON
flac-detective /music --verbose --format json
# Fast scan with custom output
flac-detective /music --sample-duration 15 --output quick-scan.txt
# Let a successful lossless repair replace an undecodable file (keeps a .corrupted.bak)
flac-detective /music --repair-in-place
Progress inside a file (--progress-events)
The progress bar counts finished files, which tells you nothing while a single
long track is being analysed. An hour-long file is one tick that arrives when it is
already over, so an application driving flac-detective as a subprocess has nothing
to show in between and looks frozen.
--progress-events reports the stages within each file, one JSON object per line:
flac-detective /music --progress-events - # to stderr
flac-detective /music --progress-events events.ndjson # to a file
Each line looks like this:
{"event": "stage", "file": "/music/track.flac", "stage": "spectrum", "index": 3, "total": 6}
The stages, always in this order: prepare (copy to a local temp file, or decode a
non-native container with ffmpeg), metadata, spectrum, quality, scoring and
done. done is emitted exactly once per file whatever the outcome, including a
file that failed, so an interface can always release what it is waiting on.
Which stage is the long one depends on the file. On a long track it is quality,
which reads the audio to measure clipping, DC offset and silence. On a typical library
file scoring can dominate instead, when Rule 1’s FLAC-equivalent re-encode and the
heavy rules actually run.
The steps inside quality are reported too, as a second kind of event:
{"event": "substage", "file": "...", "stage": "quality", "detail": "silence", "index": 4, "total": 6}
The steps are clipping, dc_offset, silence, bit_depth, upsampling, and a
substage carries the index/total of its parent stage.
Note that clipping, dc_offset and silence arrive together: since 1.15.0 they
are computed in a single pass over the audio, so all three genuinely start at the same
moment. Announcing them one after another would describe a sequence that no longer
happens.
Position inside the scan
That single pass is the longest step of an analysis, and it cannot be broken into smaller named steps. So it reports where it is, as a third kind of event:
{"event": "scan", "file": "...", "stage": "quality", "index": 4, "total": 6, "frames": 26460000, "frames_total": 52920000}
frames out of frames_total is the only honest measure of progress the engine has:
it is a position in this one traversal, counted in audio frames, not a fraction of the
whole analysis. Positions only move forward, never exceed the total, and the last one
is always exactly frames_total, so an interface can tell a finished scan from a
stalled one.
At most 21 of these per file — one every 5% of the audio, plus the final exact position. On a 20-minute track that brings the longest silence between two events down to about 5 s, from 16 s.
Filtering by event keeps every earlier integration working: "stage" gives the six
stages exactly as 1.14.0 emitted them, "substage" the named steps as 1.14.1 did, and
"scan" is simply new.
If you only care about the six stages, filter on event == "stage" and you will see
exactly what you saw before — same order, same indices, same keys. Substages are added
next to stage events, never woven into them.
There is no percentage, on purpose. Which rules run depends on what the earlier
ones found: the authentic fast path returns before the expensive half, --deep
bypasses it, Rule 1’s container test is skipped where nothing reads the ratio, and
Rules 11–15 each have their own entry condition. The time left inside a file is not
knowable when the file is opened, so index/total is a position in a fixed list of
stages, never a fraction of the work. A number here would be a progress bar that lies.
- writes to stderr, never stdout, because stdout carries the report under
--format json and has to stay parseable. stderr also carries the banner and the log,
so match the lines you want and ignore the rest — every event line starts with
{"event":
for line in proc.stderr:
if line.startswith('{"event"'):
handle(json.loads(line))
Give a file path instead if you want a stream with nothing else in it. Lines are flushed as they happen, so tailing the file works; the file is truncated at start.
From Python, pass a callback instead — same stages, no subprocess:
from flac_detective import FLACAnalyzer
def show(event):
if event.frames is not None:
pct = 100 * event.frames / event.frames_total
print(f"{event.stage} scan {pct:.0f}%")
return
where = event.stage if event.detail is None else f"{event.stage}/{event.detail}"
print(f"{where} ({event.index}/{event.total}) {event.file}")
result = FLACAnalyzer().analyze_file("track.flac", on_progress=show)
The callback only observes: anything it raises is swallowed, and the verdict is the same whether you pass one or not.
Understanding Results
The Four Verdicts
✅ AUTHENTIC (Score ≤ 30)
Meaning: Genuine lossless audio, very high confidence
Typical characteristics:
Cutoff frequency at or near Nyquist (22050 Hz for 44.1kHz)
No compression artifacts detected
High spectral variance
May have analog source signatures (vinyl, cassette)
Example:
VERDICT: AUTHENTIC ✅ (Score: 12/150)
REASON: Full spectrum preserved, no artifacts
RECOMMENDATION: File is genuine lossless. Keep as-is.
Action: Keep the file, it’s authentic.
❓ WARNING (Score 31-54)
Meaning: Uncertain case, manual review needed
Typical characteristics:
Cutoff slightly below Nyquist (20-21 kHz)
Some minor artifacts but protected by other rules
Could be legitimate high-quality source
Could be borderline transcode
Example:
VERDICT: WARNING ❓ (Score: 45/150)
REASON: Cutoff at 20500 Hz, minor artifacts
RECOMMENDATION: Verify with external tools. Could be legitimate.
Action:
Listen to the file carefully
Check source credibility
Use complementary tool (e.g., Spek for visual spectral analysis)
Consider re-downloading if suspicious
⚠️ SUSPICIOUS (Score 55-85)
Meaning: High confidence MP3 transcode
Typical characteristics:
MP3 spectral signature detected (Rule 1: +50 pts)
Cutoff matches known MP3 bitrate (128-256 kbps)
Compression artifacts present
Consistent patterns across file
Example:
VERDICT: SUSPICIOUS ⚠️ (Score: 72/150)
REASON: MP3 192 kbps signature detected
ANALYSIS:
✓ Rule 1: +50 pts - CBR pattern at 192 kbps
✓ Rule 2: +15 pts - Cutoff at 19500 Hz
RECOMMENDATION: High confidence transcode. Replace if possible.
Action:
Replace file from better source if available
Accept quality loss if no alternative
Document for future reference
❌ FAKE_CERTAIN (Score ≥ 86)
Meaning: Definite MP3 transcode, absolute confidence
Typical characteristics:
Multiple MP3 indicators confirmed
Strong compression artifacts
Suspicious metadata (fake 24-bit)
Consistent MP3 patterns throughout
Example:
VERDICT: FAKE_CERTAIN ❌ (Score: 103/150)
REASON: Multiple MP3 signatures confirmed
ANALYSIS:
✓ Rule 1: +50 pts - MP3 128 kbps signature
✓ Rule 2: +30 pts - Cutoff at 16000 Hz
✓ Rule 4: +30 pts - Suspicious 24-bit encoding
✓ Rule 13: +45 pts - MDCT quantisation grid detected
RECOMMENDATION: Definitely transcoded. Delete and replace.
Action:
Delete the file
Find authentic source
Report to distributor if applicable
Understanding Rule Contributions
Each rule can add or subtract points:
Positive Rules (Add points → Indicates fake):
Rule 1: MP3 Spectral Signature (+50)
Rule 2: Low Cutoff Frequency (+30)
Rule 4: Suspicious 24-bit (+30)
Rule 13: MDCT frame alignment (+25 / +55, by peak ratio)
Rule 10: Multi-Segment Consistency (+20)
(Rule 3, “Bitrate Mismatch”, was removed in v1.10: it compared the bitrate Rule 1 had inferred, and across 978 measured files it never once fired without Rule 1.)
Protection Rules (Subtract points → Protects authentic):
Rule 5: High Variance Protection (-40)
Rule 6: High Quality Protection (-30)
Rule 7: Vinyl & Silence Analysis (-100)
Rule 8: Nyquist Exception (-50)
Rule 11: Cassette Detection (-40, and Rule 1 is not run)
Optional ML Rule (requires pip install "flac-detective[ml]"):
Rule 12: CNN Classifier — sharpens borderline verdicts on suspect files; not applied unless the ML extra is installed
Report File Format
FLAC Detective saves a detailed text report:
FLAC AUTHENTICITY ANALYSIS REPORT
Generated: 2026-06-05 14:30:22
Analyzer Version: 1.3.0
Sample Duration: 30.0s
Scan Path: /music/collection
======================================================================
SUMMARY
======================================================================
Total files analyzed: 245
Authentic files: 215 (87.8%)
Warning files: 18 (7.3%)
Suspicious files: 9 (3.7%)
Fake certain files: 3 (1.2%)
======================================================================
DETAILED RESULTS
======================================================================
[Individual file entries follow...]
Real-World Examples
Example 1: Cleaning Your Library
Scenario: You have 1000 FLAC files and want to identify fakes.
# Run analysis
flac-detective ~/Music/FLAC > analysis.txt
# Review summary
head -30 analysis.txt
# Find all fakes
grep "FAKE_CERTAIN" analysis.txt
# Find suspicious files
grep -E "SUSPICIOUS|FAKE_CERTAIN" analysis.txt
Result interpretation:
950 AUTHENTIC → Keep these
35 WARNING → Review manually
12 SUSPICIOUS → Replace if possible
3 FAKE_CERTAIN → Delete immediately
Example 2: Verifying Downloads
Scenario: You downloaded an album from the internet.
# Analyze the album
flac-detective "/downloads/Pink Floyd - DSOTM (2011 Remaster)"
# Check results
Outcomes:
All AUTHENTIC → Safe to import to library
Some WARNING → Check source reputation, verify samples
Any SUSPICIOUS/FAKE → Find better source
Example 3: Pre-Upload Quality Check
Scenario: Before uploading to Bandcamp/streaming service.
# Verify your mastered album
flac-detective "/projects/my-album/masters"
# Ensure all files are AUTHENTIC
# Fix any issues before upload
Example 4: Marketplace Purchase Verification
Scenario: Bought FLAC collection from seller.
# Verify authenticity
flac-detective "/purchased/jazz-collection"
# Calculate fake percentage
# >20% fakes → Request refund
# <5% fakes → Accept (normal variation)
Batch Processing
Process Multiple Folders
#!/bin/bash
# analyze-all.sh
for artist in ~/Music/FLAC/*/; do
echo "Analyzing: $(basename "$artist")"
flac-detective "$artist" --output "reports/$(basename "$artist").txt"
done
Find and Move Suspicious Files
#!/bin/bash
# quarantine-fakes.sh
mkdir -p ~/Music/quarantine
# Run analysis and save
flac-detective ~/Music/FLAC --format json > results.json
# Parse JSON and move files (requires jq)
jq -r '.files[] | select(.score >= 55) | .filepath' results.json | while read file; do
mv "$file" ~/Music/quarantine/
done
Parallel Processing (Advanced)
#!/bin/bash
# Fast parallel analysis using GNU parallel
find ~/Music -name "*.flac" | parallel -j 4 "flac-detective {} --format json >> results.jsonl"
Docker Usage
Basic Docker Commands
# Pull latest image
docker pull ghcr.io/guillain-rdcde/flac_detective:latest
# Analyze directory
docker run --rm \
-v /path/to/music:/data \
ghcr.io/guillain-rdcde/flac_detective:latest \
/data
# Save report outside container
docker run --rm \
-v /path/to/music:/data \
-v /path/to/reports:/reports \
ghcr.io/guillain-rdcde/flac_detective:latest \
/data --output /reports/report.txt
# Read-only music mount: keep progress.json (resume state), the report and the
# log in a writable volume with --work-dir. (Without it the scan still completes
# — flac-detective falls back to /tmp inside the container — but everything it
# wrote is gone when the container exits.)
docker run --rm \
-v /path/to/music:/data:ro \
-v /path/to/reports:/reports \
ghcr.io/guillain-rdcde/flac_detective:latest \
/data --work-dir /reports
Docker Compose
Create docker-compose.yml (Compose v2+ no longer requires a top-level version: key):
services:
flac-detective:
image: ghcr.io/guillain-rdcde/flac_detective:latest
volumes:
- ./music:/data
- ./reports:/reports
command: /data --output /reports/analysis.txt
Run:
docker-compose up
Windows Docker Examples
# Windows PowerShell
docker run --rm `
-v "C:\Users\YourName\Music":/data `
ghcr.io/guillain-rdcde/flac_detective:latest `
/data
Best Practices
1. Regular Library Audits
# Monthly check
flac-detective ~/Music/FLAC --output "reports/audit-$(date +%Y%m).txt"
2. Verify Before Archiving
Always check files before long-term storage:
flac-detective /staging/to-archive
# Only archive if all AUTHENTIC
3. Document Questionable Files
Keep a log of WARNING files:
grep "WARNING" report.txt > warnings.txt
# Review periodically
4. Use Multiple Tools for Confirmation
FLAC Detective is excellent but not perfect:
Visual inspection with Spek for spectral analysis
Listen with quality headphones for audible artifacts
Cross-reference with multiple sources when uncertain
5. Understand Your Sources
Different sources have different characteristics:
Vinyl rips: May trigger Rule 7 (protected)
Cassette transfers: May trigger Rule 11 (protected)
High-quality MP3 320: Rule 6 provides some protection
Streaming rips: Often SUSPICIOUS or FAKE_CERTAIN
Troubleshooting
Issue: High False Positive Rate
Symptoms: Many authentic files marked as SUSPICIOUS
Causes:
Vinyl or cassette sources
Aggressive mastering
Special audio processing
Solution: Check if files are protected by Rules 7 or 11. If not, file a bug report with samples.
Issue: Slow Performance
Symptoms: Analysis takes >10 seconds per file
Causes:
Network drives
Very large files (>100MB)
Slow disk I/O
Solutions:
# Use shorter sample duration
flac-detective /music --sample-duration 15
# Copy to local disk first
cp -r /network/music /tmp/local-copy
flac-detective /tmp/local-copy
Issue: “ModuleNotFoundError”
Symptoms: Python import errors
Solution:
# Reinstall dependencies
pip install --upgrade flac-detective
# Or install from source
pip install -e ".[dev]"
Issue: Corrupted FLAC Files
Symptoms: “FLAC decoder error” messages
Solution: Auto-repair is enabled by default — just re-run the command and the tool will attempt to repair unreadable files transparently. For manual repair outside FLAC Detective:
flac --verify --decode-through-errors file.flac
Issue: Docker Volume Permissions
Symptoms: “Permission denied” in Docker
Solution (Linux):
# Run with current user
docker run --rm \
--user $(id -u):$(id -g) \
-v /path/to/music:/data \
ghcr.io/guillain-rdcde/flac_detective:latest \
/data
Advanced Tips
Customize Thresholds (Python)
from flac_detective import FLACAnalyzer
analyzer = FLACAnalyzer(sample_duration=45.0)
# Analyze with custom parameters
Integration with Music Players
# Create playlist of authentic files
flac-detective ~/Music --format json | jq -r '.files[] | select(.score <= 30) | .filepath' > authentic.m3u
Automated Quality Monitoring
# Cron job: daily library check
0 2 * * * /usr/local/bin/flac-detective ~/Music --output ~/reports/daily-$(date +\%Y\%m\%d).txt
Getting More Help
Detailed technical info: Technical Details
Python API usage: API Reference
Development & bugs: Contributing
Installation issues: Getting Started
Happy analyzing! For questions, visit GitHub Issues.