clone

a desktop tv and program guide for over-the-air broadcasts: what's on air, what your tuner gets, live tv, recordings

main
airwaves / internal / vchan / ytloudness.go
ytloudness.go 1.6K
1package vchan23// YouTube videos' loudness is measured as they air and kept in the4// catalog, so the next airing starts at the right level.56// ytAssumed is the loudness taken for a video not yet measured: YouTube's7// own playback level, which most uploads are near.8const ytAssumed = -14.0910// ytMemory remembers videos' loudness in the catalog.11type ytMemory struct{ y *YouTube }1213func (m ytMemory) recall(id string) (loudness, bool) {14 m.y.mu.Lock()15 defer m.y.mu.Unlock()16 if v := m.y.videos[id]; v != nil && v.Loudness != nil {17 return *v.Loudness, true18 }19 return loudness{}, false20}2122func (m ytMemory) remember(id string, l loudness) {23 m.y.mu.Lock()24 defer m.y.mu.Unlock()25 if v := m.y.videos[id]; v != nil {26 v.Loudness = &l27 m.y.catDirty = true28 }29}3031// level is how an airing's sound is leveled, nil for not at all.32func (y *YouTube) level(id string) *itemLevel {33 if y.Loudness == 0 {34 return nil35 }36 return &itemLevel{Target: y.Loudness, Key: id, Memory: ytMemory{y}, Assumed: ytAssumed}37}3839// Leveled is how many of the videos scheduled from now on, or a40// playlist's, have had their loudness measured as they aired, of how41// many.42func (y *YouTube) Leveled() (measured, total int) {43 y.mu.Lock()44 defer y.mu.Unlock()45 y.tend()46 if y.isPlaylist() {47 seq, _ := y.sequence()48 for _, v := range seq {49 if v.Loudness != nil {50 measured++51 }52 }53 return measured, len(seq)54 }55 now := y.clock()56 seen := map[string]bool{}57 for _, a := range y.plan.Airings {58 if !a.End.After(now) || seen[a.ID] {59 continue60 }61 seen[a.ID] = true62 total++63 if v := y.videos[a.ID]; v != nil && v.Loudness != nil {64 measured++65 }66 }67 return measured, total68}