fix(engine): cross-backend integrity guard with retry-then-damaged
A truncated debrid download (in-memory byte counter hit 100% while the
NFS write-back silently dropped most of the bytes) was marked completed.
The 1.1.6 fsync fix closed the debrid-specific hole; this generalizes the
guarantee so "completed" never means a corrupt file on ANY backend.
- IntegrityError + bounded retry: on a corrupt/short result the manager
re-downloads the same source up to 3x (clean start), then surfaces the
task as damaged ("corrupt download:" prefix) instead of completing it.
- verify (size mismatch / empty), debrid (incomplete / post-write / flush),
torrent (BytesMissing), usenet (par2 unrepairable / repair-failed) all
classify integrity failures so they route through the retry/damaged path.
- scanner: a file ffprobe can't read is emitted as a damaged library_item
(reason "unreadable") instead of being silently dropped from the sync.
- tests: manager retry-then-success + retry-exhausted-then-damaged,
verifying->resolving transition, damaged sync item.
This commit is contained in:
parent
271413e0f9
commit
a5f3f0914a
13 changed files with 400 additions and 91 deletions
|
|
@ -534,11 +534,18 @@ func (d *TorrentDownloader) pollDownload(ctx context.Context, t *torrent.Torrent
|
|||
default: // don't block if channel full
|
||||
}
|
||||
|
||||
// Check completion
|
||||
// Check completion. BytesCompleted counts only SHA1-VERIFIED pieces, so
|
||||
// torrent content can't be silently truncated — but assert nothing is
|
||||
// still missing (selective-file accounting, a piece that failed its last
|
||||
// hash) before declaring done. A non-zero remainder is an integrity
|
||||
// failure → the manager re-downloads (anacrolix re-checks pieces).
|
||||
if downloaded >= totalBytes {
|
||||
if isTTY {
|
||||
fmt.Fprintln(os.Stderr) // newline after \r progress
|
||||
}
|
||||
if missing := t.BytesMissing(); missing > 0 {
|
||||
return nil, integrityErr("truncated", "torrent reported complete but %s of verified pieces are still missing", formatBytes(missing))
|
||||
}
|
||||
log.Printf("[%s] download complete: %s", task.ID[:8], fileName)
|
||||
return &Result{}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue