From 191bc955e4dd5a8b71e592d1f7d824fd78d05077 Mon Sep 17 00:00:00 2001 From: Esa Kataja Date: Sat, 16 May 2026 20:34:11 +0300 Subject: [PATCH] chore: document watcher stability, collision refusal, EXDEV fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §9: add notes on destination-collision refusal (silent overwrite was bug #14) and cross-filesystem move fallback (bug #13). Both are now implemented but the manual didn't reflect them. §10: drop the "no atomic-write detection" warning. The watcher now requires mtime+size stability across two ticks before processing (bug #22), and quarantines a failing file for 5 minutes — so the copy-then-mv workaround is no longer a correctness requirement. --- MANUAL.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MANUAL.md b/MANUAL.md index e3de395..6258383 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -249,6 +249,10 @@ If any step from probing through encoding through renaming fails: The watcher continues with the next file; one bad rip won't stop the daemon. +**Destination collisions:** if a finished encode would land on a name that already exists in `paths.output`, the move is refused (no silent overwrite) and the source is routed to `paths.failed`. This is the path you'll hit when two sources sanitize to the same output filename — e.g. two re-rips of the same release, or two episodes that both come out as `SxxExx`. + +**Cross-filesystem moves:** every move (`paths.input → paths.output`, `… → paths.failed`, `… → paths.originals`) transparently falls back to copy + delete when the source and destination live on different mounts. You can put each path on a different drive without breaking the pipeline. + --- ## 10. Polling behavior @@ -256,7 +260,8 @@ The watcher continues with the next file; one bad rip won't stop the daemon. - Input directory is scanned every **15 seconds** (clamped to a 10–30 s range). - Only files matching `*.mkv` directly in `paths.input` are picked up — no recursion. - Files are processed **sequentially**, one at a time, in the order `filepath.Glob` returns them (alphabetical on Linux). -- There is no atomic-write detection. If you're copying a large file into `paths.input`, copy it to a different name first and `mv` it into place once complete, otherwise the watcher may try to encode a half-written file. +- **Partial-write protection:** the watcher requires a file's `mtime` and `size` to be identical on two consecutive scans before processing. A freshly dropped or still-copying file therefore waits at least one full tick (~15 s) before encoding begins. Copying a large file directly into `paths.input` is now safe; you no longer have to land it under a different name and `mv` into place (though doing so still works and shaves off the tick delay). +- **Failure quarantine:** if `processFile` returns an error, that file is skipped for 5 minutes (or until its `mtime` changes — e.g. you replace or `touch` it). Prevents a permanently-broken input from spamming the logs every 15 s. The quarantine is in-memory only; restarting the daemon clears it. ---