diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-10-24 09:33:49 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-10-24 09:33:49 +0000 |
| commit | f5bf05bbc0480a7edcd098a8e40c6c16af5d5f68 (patch) | |
| tree | 05208e8a3ad180457b0a3619e9da2b1a29568cda /server/cmd | |
| parent | cd497f8972ab9a5225813f2106f89f255a291c35 (diff) | |
Fix the shadow run: resolve audio paths, and survive a bad file
The first real shadow run died on its first batch. Two faults, and
the second is the one worth keeping.
store.Episode.Path is relative to the audio directory. Detection joins
it before handing it to the decoder and this did not, so ffmpeg went
looking in the working directory and found nothing.
Then that single failure aborted the whole show's learning. Every
other per-item loop in this server isolates one item's failure --
the refresh sweep, the poll loop, the detection pass all say so in
their own comments -- and this one propagated instead. An unreadable
episode is now counted and stepped over.
Counted once per episode rather than once per failure: a file that
cannot be read fails both the matching pass and the harvesting pass,
and reporting it twice would overstate the damage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'server/cmd')
| -rw-r--r-- | server/cmd/listend/main.go | 12 | ||||
| -rw-r--r-- | server/cmd/listend/shadow.go | 13 |
2 files changed, 15 insertions, 10 deletions
diff --git a/server/cmd/listend/main.go b/server/cmd/listend/main.go index a556f5e..a4400d2 100644 --- a/server/cmd/listend/main.go +++ b/server/cmd/listend/main.go @@ -77,7 +77,6 @@ func main() { keep: *keep, mirror: &mirror.Mirror{AudioDir: audioDir, Probe: decoder}, decoder: decoder, - audioDir: audioDir, requests: make(chan request, 64), wakeups: make(chan struct{}, 1), } @@ -154,12 +153,11 @@ type worker struct { // signatures is the library the shadow run learns into. Nothing it holds is published yet. signatures *signature.Library signaturePath string - keep int - mirror *mirror.Mirror - decoder decode.Decoder - audioDir string - requests chan request - wakeups chan struct{} + keep int + mirror *mirror.Mirror + decoder decode.Decoder + requests chan request + wakeups chan struct{} } // wake asks for a poll now. Coalescing, so a burst of subscription updates is one poll. diff --git a/server/cmd/listend/shadow.go b/server/cmd/listend/shadow.go index e9376f1..7531115 100644 --- a/server/cmd/listend/shadow.go +++ b/server/cmd/listend/shadow.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "path/filepath" "sort" "time" @@ -68,7 +69,7 @@ func (w *worker) runShadow(ctx context.Context, show *store.Show, episodes []*st batch.Episodes = append(batch.Episodes, ep) } - report, err := signature.NewShadow(w.signatures, audioFor(w.decoder, episodes)).Run(ctx, batch) + report, err := signature.NewShadow(w.signatures, audioFor(w.decoder, w.mirror.AudioDir, episodes)).Run(ctx, batch) if err != nil { log.Printf("%s: shadow run failed: %v", show.Title, err) return @@ -118,10 +119,16 @@ const shadowMinOccasions = 2 // // The paths come from the batch rather than from a lookup: these are the episodes detection // just finished with, so their files are the ones it read and are certainly still there. -func audioFor(decoder decode.Decoder, episodes []*store.Episode) signature.Audio { +// +// store.Episode.Path is relative to the audio directory, which is what detection joins it with +// before handing it to the decoder. Passing it raw sent ffmpeg looking in the working directory, +// and the first real shadow run died on it. +func audioFor(decoder decode.Decoder, audioDir string, episodes []*store.Episode) signature.Audio { paths := make(map[string]string, len(episodes)) for _, e := range episodes { - paths[e.ID] = e.Path + if e.Path != "" { + paths[e.ID] = filepath.Join(audioDir, e.Path) + } } return episodeAudio{decoder: decoder, pathOf: func(id string) string { return paths[id] }} } |
