From 50b52d58e9865ac01e5364ec68247edb323db9b7 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Tue, 27 Oct 2026 07:54:25 +0000 Subject: Use the labels: seed signatures from them, and take them back The labels have been arriving and sitting there. Now the shadow run acts on all three kinds. A report is advertising pointed at where detection found nothing, so it becomes a signature in its own right -- that being the whole gap the library exists to close, since the scan accounts for about 38% of what the feeds imply is there. A mark saying a break is right adds no audio, because the break is harvested anyway. What it adds is that a person vouched for it. A mark saying a break is wrong does two things. It stops that audio being harvested, and it takes back any signature already held that matches it. That second half is the one that matters: a signature built from programme would cut the programme in every episode carrying it, until somebody noticed. So his "no" removes it outright rather than lowering a score. Being pointed at outranks partners and occasions both. Those are ways of inferring what a stretch of audio is; he was there. A confirmed signature is trusted at once and stays confirmed -- he does not un-hear it next week. A report whose end was never corrected is trimmed to its first fifteen seconds. An uncorrected end is wherever the 25-second jump landed rather than where the advertising stopped, so its tail may be programme -- and one ad unit is already far more audio than a landmark fingerprint needs to be distinctive. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B --- server/cmd/listend/shadow.go | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'server/cmd') diff --git a/server/cmd/listend/shadow.go b/server/cmd/listend/shadow.go index 7531115..03f4b71 100644 --- a/server/cmd/listend/shadow.go +++ b/server/cmd/listend/shadow.go @@ -56,8 +56,24 @@ func (w *worker) runShadow(ctx context.Context, show *store.Show, episodes []*st Occasion: show.FeedURL + "@" + show.DetectedBatch, AtUnix: time.Now().Unix(), } + // What the listener has said, indexed so each episode can pick up its own. Labels outlive + // the episodes they were given on, so most of these belong to batches long since rotated + // out; only the ones matching this batch's guids are of any use here. + labels := map[string][]signature.ShadowLabel{} + for _, l := range w.state.Labels() { + if l.FeedURL != show.FeedURL { + continue + } + labels[l.GUID] = append(labels[l.GUID], signature.ShadowLabel{ + StartMs: l.StartMs, + EndMs: l.EndMs, + Advertising: l.Verdict == store.LabelAd, + EndMeasured: l.EndMeasured, + }) + } + for _, e := range episodes { - ep := signature.ShadowEpisode{ID: e.ID, FeedURL: show.FeedURL} + ep := signature.ShadowEpisode{ID: e.ID, FeedURL: show.FeedURL, Labels: labels[e.GUID]} for _, br := range e.Breaks { ep.Breaks = append(ep.Breaks, signature.ShadowBreak{ StartMs: br.StartMs, @@ -81,11 +97,12 @@ func (w *worker) runShadow(ctx context.Context, show *store.Show, episodes []*st novel++ } } - log.Printf("%s: shadow -- learned %d, reinforced %d, library %d (%d worth matching); "+ + log.Printf("%s: shadow -- learned %d, reinforced %d%s, library %d (%d worth matching); "+ "would cut %d, %d of them new%s, took %s", - show.Title, report.Harvested, report.Reinforced, w.signatures.Len(), - len(w.signatures.Trusted(shadowMinOccasions)), len(report.WouldCut), novel, - skippedNote(report.Skipped), time.Since(started).Round(time.Second)) + show.Title, report.Harvested, report.Reinforced, disownedNote(report.Disowned), + w.signatures.Len(), len(w.signatures.Trusted(shadowMinOccasions)), + len(report.WouldCut), novel, skippedNote(report.Skipped), + time.Since(started).Round(time.Second)) // The novel ones are the whole point: what the library would add to what detection already // publishes. Logged individually so they can be listened to and judged. @@ -104,6 +121,15 @@ func (w *worker) runShadow(ctx context.Context, show *store.Show, episodes []*st } } +// disownedNote reports signatures taken back because the listener said that audio is the +// programme. Always logged when it happens: it is the library being told it was wrong. +func disownedNote(n int) string { + if n == 0 { + return "" + } + return fmt.Sprintf(", disowned %d", n) +} + func skippedNote(n int) string { if n == 0 { return "" -- cgit v1.2.3