1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
package transcript
import "fmt"
/*
Which of the advertising read out of an episode's words is fit to skip.
Everything else in this package produces candidates to look at. This is the one function that
decides what the phone will seek past, so it is deliberately narrow.
Two detectors produce candidates and they are not close in quality. Subtraction -- comparing an
episode's words against a second description of the same programme -- measured 92% to 104% of
what the declared-versus-served length gap implies, finding every pod, with its edges pulled back
off the programme. Reading the ad copy measured 0%, 0%, 15% and 29% on the same kind of episode.
Only the first is acted on.
**That 92-104% is the figure for a publisher's transcript**, which is a second description of the
programme written once. Against a refetch -- a second download of the same dynamically-assembled
file, carrying its own advertising at its own positions -- the same code recovers a median of 33%
across fifteen episodes, with a range of 20% to 146%. Measured on the live corpus 2026-11-02; see
docs/detection-findings.md. Nothing below distinguishes the two, which is a known gap, not a
decision.
The length gap is both the measure and the guard. It comes from the feed and the file rather
than from any detector, which makes it the one number able to contradict one.
*/
// overshootLimit is how much more than the lengths imply a candidate set may claim and still be
// published, as a percentage. 150 leaves room for a music sting at the edge of a pod without
// leaving room for having found the wrong thing entirely.
//
// It was set against the publisher-transcript spread, which runs to 104%, so it sat well clear of
// the distribution. Where it actually binds is the shows whose length gap is noise rather than a
// measurement -- Reply All's gaps run from -0.48 to +1.02 minutes across eight episodes, so a
// percentage of one of them means little either way. Raising this limit would not help and
// lowering it would not either; see docs/detection-findings.md for what would.
const overshootLimit = 150
// Publishable returns the candidates fit to skip, or nothing and the reason.
//
// A refusal is a different state from having found nothing, and the caller keeps them apart --
// the same distinction the ad scan's own refusals have needed twice before.
func Publishable(found []Candidate, servedMs, declaredMs int64, master Source) ([]Candidate, string) {
if len(found) == 0 {
return nil, ""
}
measured := make([]Candidate, 0, len(found))
for _, c := range found {
if len(c.Signals) == 1 && c.Signals[0] == SignalDifference {
measured = append(measured, c)
}
}
if len(measured) == 0 {
return nil, "found by reading the ad copy, which has never agreed with the lengths"
}
// What was subtracted decides whether this is a measurement or a guess. A publisher's
// transcript describes the programme once; a refetch is another copy of the same assembled
// file, with its own advertising in its own places, so the difference between them is two ad
// loads rather than one. Measured on the live corpus: 94-104% of the length gap from a
// published master, a median of 33% from a refetch.
//
// An allowlist, not a "not refetch" test. Sixteen episodes carry a master whose label is
// blank -- the field postdates the first masters written -- and a denylist published them on
// the strength of not being the one named bad kind.
if master != SourcePublished {
kind := string(master)
if kind == "" {
kind = "a master that does not say what it is"
} else {
kind = "a " + kind
}
return nil, fmt.Sprintf("subtracted from %s rather than the publisher's transcript, "+
"which is the only master measured accurate enough to skip on", kind)
}
gap := servedMs - declaredMs
if !HasInsertedAdvertising(servedMs, declaredMs) {
return nil, "the lengths say nothing was inserted into this copy, so there is " +
"nothing to check these against"
}
var total int64
for _, c := range measured {
total += c.EndMs - c.StartMs
}
if total*100 > gap*overshootLimit {
return nil, fmt.Sprintf(
"claims %s against the %s the lengths say was inserted, so it has found "+
"something other than advertising", clock(total), clock(gap))
}
return measured, ""
}
// recurringShareLimit is how much of an episode may recur before the result is refused, as a
// percentage. Thirty-five, the same share the ad scan refuses at and for the same reason: a
// re-aired episode, a two-parter or a show built around a formulaic segment would otherwise be
// eaten whole.
const recurringShareLimit = 35
/*
PublishableRecurring returns the recurring text fit to skip as the show's own audio.
Separate from [Publishable] because it publishes a different thing. Subtraction measures
advertising and the phone skips advertising unconditionally; this measures only that a show says
something every week, which is its sponsor reads and its furniture together, and nothing
available separates them -- ad copy appears in 5% of the advertising and 4% of the furniture,
and length and position overlap throughout. So it is published as the show's own audio, which
the listener's own setting governs, and the stronger claim is left unmade.
Guarded by share of the episode rather than by the length gap, because the episodes this is
worth most on are the ones with no gap at all: a show that bakes its reads into what it
publishes declares the length it serves, so the gap is blind to every advertisement in it.
*/
func PublishableRecurring(found []Candidate, episodeMs int64) ([]Candidate, string) {
recurring := make([]Candidate, 0, len(found))
for _, c := range found {
if len(c.Signals) == 1 && c.Signals[0] == SignalRecurring {
recurring = append(recurring, c)
}
}
if len(recurring) == 0 {
return nil, ""
}
if episodeMs <= 0 {
return nil, "the episode's length is unknown, so there is no share to judge this against"
}
var total int64
for _, c := range recurring {
total += c.EndMs - c.StartMs
}
if total*100 > episodeMs*recurringShareLimit {
return nil, fmt.Sprintf(
"%s of a %s episode recurs, which is too much of it to have been understood",
clock(total), clock(episodeMs))
}
return recurring, ""
}
|