feat: support YouTube VTT subtitle selection
This commit is contained in:
@@ -211,7 +211,19 @@ struct CastPlaybackCheck {
|
||||
let suite = "terebi.cast-playback-check." + UUID().uuidString
|
||||
let defaults = UserDefaults(suiteName: suite)!
|
||||
defer { defaults.removePersistentDomain(forName: suite) }
|
||||
let model = PlayerModel(defaults: defaults, skipClock: { 100 })
|
||||
let youtubeSource = ProcessInfo.processInfo.environment["TEREBI_CAST_YOUTUBE_SOURCE"]
|
||||
let identity = youtubeSource == nil ? source : URL(string: "https://www.youtube.com/watch?v=AAAAAAAAAAA")!
|
||||
let model = PlayerModel(defaults: defaults, youtubeResolver: { url, _ in
|
||||
let base = youtubeSource!
|
||||
return YouTubeVideo(title: "YouTube Cast captions", video: .init(url: base + "/media/cache.mp4",
|
||||
headers: "X-Terebi-Fixture: combined\r\n"), audio: nil, duration: 120, isLive: false,
|
||||
webpageURL: url, height: 1080, availableHeights: [1080], chapters: [],
|
||||
subtitles: [
|
||||
.init(url: base + "/captions.vtt", format: "vtt", language: "en", label: "English",
|
||||
isDefault: false, headers: ["X-Subtitle": "caption-private"], selectionID: "youtube:manual:en"),
|
||||
.init(url: base + "/unavailable.vtt", format: "vtt", language: "fr", label: "Unavailable",
|
||||
isDefault: false, headers: [:], selectionID: "youtube:manual:fr")])
|
||||
}, youtubeTitleResolver: { _ in nil }, skipClock: { 100 })
|
||||
defer { model.shutdown() }
|
||||
if CommandLine.arguments.count == 5 {
|
||||
if CommandLine.arguments[3] == "queue" {
|
||||
@@ -223,7 +235,7 @@ struct CastPlaybackCheck {
|
||||
return
|
||||
}
|
||||
model.setVolume(0)
|
||||
model.open(source)
|
||||
model.open(identity)
|
||||
try await until(model, "local source plays") { model.state.status == PLAYER_PLAYING && model.canSeek }
|
||||
requirePlaybackPower(system: true, display: true, "local video prevents system and display sleep")
|
||||
require(model.setPlayback(playing: false), "local pause accepted")
|
||||
@@ -232,6 +244,10 @@ struct CastPlaybackCheck {
|
||||
require(model.seekRelative(9.25 - model.state.position), "local restoration position accepted")
|
||||
require(!model.canCast, "Cast cannot transfer an unacknowledged local seek")
|
||||
try await until(model, "local position acknowledged") { !model.state.seek_pending && abs(model.state.position - 9.25) < 0.15 }
|
||||
if youtubeSource != nil {
|
||||
model.selectSubtitle(0)
|
||||
try await until(model, "local YouTube caption selected before handoff") { model.subtitleText == "Automatic caption" }
|
||||
}
|
||||
model.cast(to: CastDevice(id: "loopback", name: "Loopback TV", host: "127.0.0.1", port: port))
|
||||
requirePlaybackPower(system: true, display: false, "pending Cast handoff protects the relay")
|
||||
try await until(model, "receiver load acknowledges paused handoff") {
|
||||
@@ -240,6 +256,12 @@ struct CastPlaybackCheck {
|
||||
requirePlaybackPower(system: false, display: false, "paused Cast permits sleep")
|
||||
require(abs(model.state.position - 9.25) < 0.15, "Cast starts at acknowledged local position")
|
||||
require(!model.canStepFrame && model.bufferedRange == nil, "local decoder-only controls are not exposed while casting")
|
||||
if youtubeSource != nil {
|
||||
require(!model.canSelectSubtitle && model.selectedSubtitle == 0 && model.subtitleText.isEmpty,
|
||||
"YouTube Cast disables local captions without losing the local choice")
|
||||
model.selectSubtitle(1)
|
||||
require(model.commandError == nil && model.selectedSubtitle == 0, "unsupported Cast captions cannot interrupt playback")
|
||||
}
|
||||
require(model.setPlayback(playing: true), "remote play accepted")
|
||||
try await until(model, "receiver play acknowledged") { model.state.status == PLAYER_PLAYING && model.playbackIntent == nil }
|
||||
requirePlaybackPower(system: true, display: false, "playing Cast protects the relay but permits display sleep")
|
||||
@@ -266,7 +288,12 @@ struct CastPlaybackCheck {
|
||||
abs(model.state.position - 24.25) < 0.15 && !model.isBusy
|
||||
}
|
||||
requirePlaybackPower(system: false, display: false, "paused local restoration permits sleep")
|
||||
require(abs((PlaybackProgress(defaults: defaults).position(for: source) ?? 0) - 24.25) < 0.15,
|
||||
if youtubeSource != nil {
|
||||
try await until(model, "return to Mac restores local YouTube caption selection") {
|
||||
model.canSelectSubtitle && model.selectedSubtitle == 0 && model.subtitleText == "Automatic caption"
|
||||
}
|
||||
}
|
||||
require(abs((PlaybackProgress(defaults: defaults).position(for: identity) ?? 0) - 24.25) < 0.15,
|
||||
"remote history is not overwritten by the abandoned local position")
|
||||
require(model.volume == 0, "local volume survives receiver volume changes")
|
||||
require(model.setPlayback(playing: true), "resume local playback before a failed Cast connection")
|
||||
|
||||
Reference in New Issue
Block a user