You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 91cbb01
Browse filesBrowse the repository at this point in the historyBrowse files
feat: keep a concealed copy of damaged sources when asked (0.6.2) (#44)
## Problem
On the real library, 0.6.1's strict decode refuses 9 FLACs that have no clean copy on Soulseek today: Beatsteaks - Hail To The Freaks, Diana Ross & The Supremes - Baby Love, Die Toten Hosen - Tage wie diese and - Wannsee, Julieta Venegas - Limón y sal, Radiohead - High and Dry, Scott McKenzie - San Francisco, Simple Plan Feat. Sean Paul - Summer Paradise, and Steve Harley & Cockney Rebel - Make Me Smile. Refusing them means the phone has no copy of those songs at all. That is worse than the one-frame glitch the old release let through.
## Changes
- **The setting.** `corrupt_source: skip | encode_anyway`, either under `settings` (for every output) or on one output (overrides the global value). The default is `skip`, which is exactly the 0.6.1 behaviour.
- **What `encode_anyway` does.** The strict run fails and logs the same ERROR lines as today. Then one more encode runs without `-xerror` and `-err_detect crccheck+explode`, so ffmpeg conceals the damaged frames. If it works, the copy exists, one WARNING line says it was made from a damaged source, and its manifest row gets kind `tolerant`. A source that fails even that run stays refused. Timeouts and other non-decode failures get no tolerant run.
- **Failure memory.** The source is still remembered as failed, so the strict attempt is not repeated on every scan.
- **Clean replacement (from the CodeRabbit review).** A clean replacement has a new size or modification time. That clears the failure memory, and a plain scan now rebuilds the tolerant copy with the strict encode. Only `tolerant` rows are rebuilt this way.
- **How the caller tells failures apart.** `atomic_ffmpeg_encode` now returns `DECODE_ERROR_RC` for any failure that printed a decode error, so it can be told from a timeout or a disk error. It takes `strict=False` for the tolerant run, where only the exit code counts.
- **Release files.** Version 0.6.2, CHANGELOG entry, and docs for the setting in `docs/configuration.md` (a "Damaged sources" section), `docs/how-it-works.md` and `config.example.yaml`.
## Testing
- CI ran in `python:3.14-slim` with FFmpeg 7.1.5: 435 passed, 0 skipped, 97% coverage. Codecov patch is 96.15% and project 96.32%.
- **Both modes:**
- `skip` runs one strict encode, writes no output, and remembers the source.
- `encode_anyway`, set globally or per output, runs one strict encode and exactly one tolerant encode. The tolerant command has no strict flags. The copy is written with manifest kind `tolerant`, the ERROR is still logged, and exactly one WARNING appears. The source is remembered, and the next scan makes no encode at all.
- A source that also fails the tolerant run stays refused, with no WARNING. A timeout gets no tolerant run.
- **Real file:** a FLAC with 400 flipped bytes, on the local ffmpeg 9 and on 7.1 in CI, gets a tolerant AAC and is remembered as failed.
- **Red controls:** every new test failed against a deliberate break, 21 mutations in all, run with bytecode caching off.
- **Ruff:** `ruff format --check` is clean. `ruff check` still reports the 23 known `BLE001`/`S110` findings and nothing new.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,12 @@
2
2
3
3
All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses [Semantic Versioning](https://semver.org/).
4
4
5
+
## [0.6.2] - 2026-10-05
6
+
7
+
### Added
8
+
9
+
-`corrupt_source: skip | encode_anyway`, under `settings` or on one output (default `skip`, the 0.6.1 behaviour). Some damaged FLACs have no clean copy to replace them, and refusing them leaves the phone with no copy of the song at all. With `encode_anyway`, a source that fails the strict decode still gets the same ERROR line, then one more encode without `-xerror` and `-err_detect`, so FFmpeg conceals the damaged frames. That copy is logged with one WARNING saying it was made from a damaged source, and its manifest row has kind `tolerant`. The source is still remembered as failed, so the strict attempt is not repeated on every scan, and a source that fails even the tolerant encode stays refused. When the damaged file is later replaced by a clean one (a different size or modification time), the next scan rebuilds the tolerant copy with the strict encode.
10
+
5
11
## [0.6.1] - 2026-10-04
6
12
7
13
### Added
@@ -53,5 +59,6 @@ All notable changes to this project are documented here. The format follows [Kee
53
59
- Lossy copies are written through a temp file and renamed, like encodes, so a half-written copy is never visible.
54
60
-`force_reencode: true` purges the outputs once at startup. It used to purge them again on every periodic sync, which re-encoded the whole library every five minutes.
Edit `config.yaml` first so its outputs match the folders you mount; the example writes ALAC, MP3 and AAC. [Getting started](https://github.com/GeiserX/audio-transcode-watcher/blob/main/docs/getting-started.md) has Docker Compose and the full `docker run`.
Copy file name to clipboardExpand all lines: docs/configuration.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -60,8 +60,30 @@ settings:
60
60
61
61
# Seconds between periodic full syncs (default: 300)
62
62
sync_interval_seconds: 300
63
+
64
+
# What to do with a source that does not decode cleanly: skip | encode_anyway
65
+
# (default: skip). An output can set its own corrupt_source.
66
+
corrupt_source: skip
67
+
```
68
+
69
+
### Damaged sources
70
+
71
+
A source that fails the strict decode (see [Corrupt sources](how-it-works.md#corrupt-sources)) is handled by `corrupt_source`:
72
+
73
+
- `skip`(the default) writes nothing for it.
74
+
- `encode_anyway`logs the same ERROR, then encodes it once more without `-xerror` and `-err_detect`, letting FFmpeg conceal the damaged frames. The copy exists, one WARNING line says it was made from a damaged source, and its manifest row has kind `tolerant`. A source that fails even that run stays refused.
75
+
76
+
Set it under `settings` for every output, or on one output to override, for example only on the AAC output that a phone plays from:
77
+
78
+
```yaml
79
+
- name: aac-256
80
+
codec: aac
81
+
path: /music/aac
82
+
corrupt_source: encode_anyway
63
83
```
64
84
85
+
Either way the source is remembered as failed, so the strict attempt is not repeated on every scan. Replacing the file with a clean copy (a new size or modification time) clears that, and the next scan rebuilds the tolerant copy with the strict encode.
86
+
65
87
`whisper_fallback`and `whisper_model` were removed in 0.6.0 along with the Whisper lyrics fallback. A config that still sets them loads, and the first load logs one warning that they are ignored.
Copy file name to clipboardExpand all lines: docs/how-it-works.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,7 +17,7 @@ Each output folder holds `.atw-manifest.json`, a map from each output file to th
17
17
18
18
## Corrupt sources
19
19
20
-
FFmpeg runs with `-xerror` and `-err_detect crccheck+explode`, so a frame whose checksum does not match stops the encode. If a source does not decode cleanly, the encode fails even when FFmpeg exits 0 but printed a decode error. The error is logged with the file name and no output is written. The file is not tried again until its modification time changes, or the service restarts.
20
+
FFmpeg runs with `-xerror` and `-err_detect crccheck+explode`, so a frame whose checksum does not match stops the encode. If a source does not decode cleanly, the encode fails even when FFmpeg exits 0 but printed a decode error. The error is logged with the file name and no output is written, unless `corrupt_source: encode_anyway` is set; then the file is encoded once more with FFmpeg's error concealment and marked `tolerant` in the manifest (see [Damaged sources](configuration.md#damaged-sources)). The strict attempt is not repeated until the file's modification time changes, or the service restarts.
0 commit comments