Files
terebi/Checks/automatic-quality-playback.swift
T
hl-valdemar 4576080955 fix: populate queue titles independently of playback extraction
The sidebar scheduler waited for playback and used full stream extraction for titles. Use independent utility-priority title requests, native YouTube oEmbed, and metadata-only extraction for generic sites. Preserve foreground title precedence and stale row protection.

Verified: make check-youtube check-queue check-resolved check-adaptive check-stream all.
2026-10-22 18:43:21 +02:00

198 lines
12 KiB
Swift

import AppKit
import TerebiConfiguration
@MainActor
private final class FixedQualityClock { var now = 1000.0 }
@main
@MainActor
struct AutomaticQualityPlaybackChecks {
static func require(_ condition: @autoclosure () -> Bool, _ message: String) {
if !condition() {
FileHandle.standardError.write(Data(("FAIL: " + message + "\n").utf8))
exit(1)
}
}
// Only the infrastructure watchdog uses elapsed time. Adaptation uses an injected clock;
// playback readiness and network stalls are observable media events, not sleeps.
static func until(_ model: PlayerModel, _ message: String, _ condition: () -> Bool) {
let deadline = ContinuousClock.now.advanced(by: .seconds(20))
while !condition(), ContinuousClock.now < deadline {
RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.01))
}
require(condition(), message + " (height=\(model.youtubeHeight ?? 0), status=\(model.state.status), error=\(model.errorText ?? "none"))")
}
static func polled(_ model: PlayerModel) {
let revision = model.state.revision
until(model, "native model processes a fresh playback snapshot") { model.state.revision != revision }
}
static func main() {
_ = NSApplication.shared
NSApp.setActivationPolicy(.prohibited)
let suite = "terebi.automatic-quality-playback-check.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suite)!
defer { defaults.removePersistentDomain(forName: suite) }
let clock = FixedQualityClock()
let model = PlayerModel(defaults: defaults, youtubeTitleResolver: { _ in nil }, qualityClock: { clock.now })
defer { model.shutdown() }
model.setVolume(0)
// Control only the external extractor; media decoding and all model transitions stay real.
guard let controlPath = ProcessInfo.processInfo.environment["TEREBI_QUALITY_CONTROL"] else {
require(false, "isolated extraction controls are available")
return
}
let control = URL(fileURLWithPath: controlPath, isDirectory: true)
func mark(_ name: String) {
require(FileManager.default.createFile(atPath: control.appendingPathComponent(name).path,
contents: Data()), "publish extraction control " + name)
}
func marked(_ name: String) -> Bool {
FileManager.default.fileExists(atPath: control.appendingPathComponent(name).path)
}
require(!model.automaticYouTubeQuality, "automatic quality is opt-in")
let window = NSWindow(contentRect: NSRect(x: -10_000, y: -10_000, width: 640, height: 360),
styleMask: [.titled, .closable, .miniaturizable, .resizable],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
defer { window.close() }
model.attachWindow(window)
let normalFrame = window.frame
let compactKey = try! KeyChord("p")
mark("gate-open")
model.openStream("https://www.youtube.com/watch?v=aqz-KE-bpKQ")
until(model, "initial YouTube resolution reaches the controlled extraction gate") {
model.resolvingStream && marked("entered-open")
}
model.handleKey(compactKey)
require(model.isCompact, "compact shortcut enters while the YouTube URL is unresolved")
until(model, "compact mode remains usable across empty playback polls while resolving") {
model.isCompact && !model.compactWindow.isTransitioning
}
require(window.level == .floating && window.frame.width < normalFrame.width,
"unresolved video has a usable compact window")
model.handleKey(compactKey)
until(model, "compact shortcut restores while YouTube resolution is still pending") {
!model.isCompact && !model.compactWindow.isTransitioning
}
require(window.frame == normalFrame && model.resolvingStream,
"resolution continues while restoring the original normal window")
require(model.perform(.enterCompact), "compact control action is available during resolution")
mark("release-open")
try! FileManager.default.removeItem(at: control.appendingPathComponent("gate-open"))
until(model, "default selects real 1080p and runs out of the gated network prefix") {
model.youtubeHeight == 1080 && model.canTogglePlayback &&
model.state.status == PLAYER_PLAYING && model.state.position > 0.5 && model.state.buffering
}
require(model.isCompact, "loading completion preserves the requested compact mode")
until(model, "decoded metadata updates compact geometry") {
!model.compactWindow.isTransitioning &&
abs(window.frame.width / window.frame.height - model.state.video_aspect_ratio) < 0.01
}
model.handleKey(compactKey)
until(model, "compact shortcut restores during network buffering") {
!model.isCompact && !model.compactWindow.isTransitioning
}
model.handleKey(compactKey)
until(model, "compact shortcut re-enters during network buffering") {
model.isCompact && !model.compactWindow.isTransitioning
}
clock.now = 2000
polled(model)
require(model.youtubeHeight == 1080 && !model.resolvingStream,
"disabled automatic quality leaves a stalled manual/default stream alone")
model.setAutomaticYouTubeQuality(true)
polled(model)
let position = model.state.position
clock.now = 2003
until(model, "persistent stall automatically reopens actual 720p media") {
model.youtubeHeight == 720 && model.canTogglePlayback && model.state.status == PLAYER_PLAYING &&
!model.state.seek_pending && (model.bufferedRange.map { $0.upperBound - model.state.position >= 20 } ?? false)
}
require(model.state.position >= position,
"automatic quality reopening does not reset acknowledged progress")
polled(model)
clock.now = 2063
until(model, "sustained health automatically restores actual 1080p media") {
model.youtubeHeight == 1080 && model.canTogglePlayback && model.state.status == PLAYER_PLAYING && !model.state.seek_pending
}
model.setYouTubeQuality(model.youtubeHeight)
require(!model.automaticYouTubeQuality && !model.isBusy && model.state.status == PLAYER_PLAYING,
"reselecting the current manual resolution disables auto without disrupting playback")
model.setAutomaticYouTubeQuality(true)
let reopened = PlayerModel(defaults: defaults, qualityClock: { clock.now })
require(!reopened.automaticYouTubeQuality, "session opt-in does not survive player restart")
reopened.shutdown()
model.setYouTubeQuality(nil)
require(!model.automaticYouTubeQuality && model.youtubeQuality == nil,
"default quality is also a manual override")
let manual = PlayerModel(defaults: defaults, configuration: PlayerConfiguration(automaticQuality: true),
qualityClock: { clock.now })
require(manual.automaticYouTubeQuality, "manual session override does not replace startup configuration")
manual.shutdown()
model.setYouTubeQuality(720)
until(model, "manual selection opens real healthy 720p media") {
model.youtubeHeight == 720 && model.canChangeYouTubeQuality && model.state.status == PLAYER_PLAYING &&
!model.state.seek_pending && (model.bufferedRange.map { $0.upperBound - model.state.position >= 20 } ?? false)
}
let selectedTitle = model.fileName
let selectedQuality = model.youtubeQuality
mark("gate-1080")
clock.now = 3000
model.setAutomaticYouTubeQuality(true)
polled(model)
let healthyPosition = model.state.position
until(model, "healthy native playback continues before the controlled upshift") {
model.state.position > healthyPosition + 0.02
}
clock.now = 3060
until(model, "automatic upshift reaches the selected extraction gate") {
marked("entered-1080") && model.resolvingStream
}
require(model.setPlayback(playing: false), "pause is accepted during automatic extraction")
mark("release-1080")
until(model, "cancelled extraction finishes and native pause is acknowledged") {
marked("finished-1080") && !model.resolvingStream && model.canTogglePlayback &&
model.state.status == PLAYER_PAUSED && !model.state.seek_pending
}
let cancelledPosition = model.state.position
for _ in 0..<3 {
polled(model)
require(model.state.status == PLAYER_PAUSED && abs(model.state.position - cancelledPosition) < 0.05 &&
model.state.has_video && model.state.has_audio &&
model.youtubeHeight == 720 && model.youtubeQuality == selectedQuality &&
model.fileName == selectedTitle && model.errorText == nil,
"late extraction cannot reopen different quality or resume deliberately paused media")
}
try! FileManager.default.removeItem(at: control.appendingPathComponent("gate-1080"))
require(model.seekRelative(37 - model.state.position), "original paused media accepts a nonzero seek")
until(model, "native original media acknowledges the seek") {
model.canChangeYouTubeQuality && model.state.status == PLAYER_PAUSED &&
!model.state.seek_pending && abs(model.state.position - 37) < 0.05
}
mark("fail-1080")
for playing in [false, true] {
require(model.setPlayback(playing: playing), "original playback state is selectable before quality failure")
until(model, "original native playback reaches the requested state before quality failure") {
model.canChangeYouTubeQuality && model.state.status == (playing ? PLAYER_PLAYING : PLAYER_PAUSED) &&
(!playing || model.state.position > 37.1)
}
let originalPosition = model.state.position
model.setYouTubeQuality(1080)
until(model, "failed manual quality switch restores the original native playback state") {
model.errorText != nil && !model.resolvingStream && model.canTogglePlayback &&
model.state.status == (playing ? PLAYER_PLAYING : PLAYER_PAUSED) &&
(!playing || model.state.position > originalPosition + 0.1)
}
require(model.state.has_video && model.state.has_audio && model.youtubeHeight == 720 &&
model.youtubeQuality == selectedQuality && model.fileName == selectedTitle &&
model.state.position >= originalPosition - 0.05 &&
model.state.position < originalPosition + (playing ? 2 : 0.05),
"failed manual quality switch retains original selected media and playback progress")
}
model.closeFile()
require(!model.isCompact && !model.compactWindow.isTransitioning,
"closing a streamed video restores its original window mode")
print("PASS native adaptive playback: resolving/loading/buffering compact shortcuts+controls/default-off/stalled downshift/healthy upshift/progress preservation/current+default manual override/session-only settings/cancelled extraction pause/failed manual quality playing+paused preservation")
}
}