feat: report authoritative playback progress from shared backend

This commit is contained in:
2026-10-11 19:29:35 +02:00
parent a4c38bfa51
commit cc99a2a4f8
14 changed files with 1369 additions and 62 deletions
+48 -3
View File
@@ -1,4 +1,5 @@
import AppKit
import Darwin
import MediaPlayer
@_cdecl("terebi_app_main")
@@ -92,6 +93,11 @@ private enum CLIInstanceCheck {
static func run(_ configuration: URL) {
let settings = try! JSONSerialization.jsonObject(with: Data(contentsOf: configuration)) as! [String: String]
let directory = URL(fileURLWithPath: settings["directory"]!, isDirectory: true)
let stderrURL = directory.appendingPathComponent("stderr-\(ProcessInfo.processInfo.processIdentifier).log")
precondition(FileManager.default.createFile(atPath: stderrURL.path, contents: nil))
let errorLog = try! FileHandle(forWritingTo: stderrURL)
precondition(dup2(errorLog.fileDescriptor, STDERR_FILENO) >= 0)
try! errorLog.close()
setenv("PATH", settings["path"]!, 1)
_ = NSApplication.shared
_ = NotificationCenter.default.addObserver(forName: NSApplication.willFinishLaunchingNotification,
@@ -115,17 +121,44 @@ private enum CLIInstanceCheck {
let windows = NSApp.windows.filter { $0.canBecomeMain && !($0 is NSPanel) && $0.contentView != nil }
for window in windows { window.alphaValue = 0 }
let request = directory.appendingPathComponent("keys-\(pid).json")
var pendingRequestID = ""
if let data = try? Data(contentsOf: request),
let command = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let id = command["id"] as? String, id != requestID, let window = windows.first {
pendingRequestID = id
if !NSApp.isActive || !window.isKeyWindow || !window.isMainWindow {
NSApp.activate()
// Synthetic native key requests need foreground focus; cooperative activation
// can be refused by the unrelated app currently active on the workstation.
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
window.makeMain()
return
}
} else {
requestID = id
if let media = command["media"] as? String { model.open(URL(fileURLWithPath: media)) }
if let action = command["action"] as? String {
switch action {
case "youtube":
model.openYouTube(command["url"] as! String)
case "seek":
let position = command["position"] as! Double
precondition(model.seekRelative(position - model.state.position), "Native seek must be accepted")
case "pause":
precondition(model.perform(.pause), "Native pause must be accepted")
case "play":
precondition(model.perform(.play), "Native play must be accepted")
case "quality":
precondition(model.canChangeYouTubeQuality, "Native quality change must be available")
model.setYouTubeQuality((command["height"] as! Int))
case "close":
model.closeFile()
case "quit":
timer?.invalidate()
precondition(model.perform(.quit), "Native quit must be accepted")
return
default:
fatalError("Unknown native check action \(action)")
}
}
if let events = command["events"] as? [[String: Any]] {
window.makeKey()
for input in events {
@@ -159,6 +192,7 @@ private enum CLIInstanceCheck {
}
}
}
}
}
let applications = NSWorkspace.shared.runningApplications.filter {
!$0.isTerminated && $0.bundleURL == Bundle.main.bundleURL
@@ -169,6 +203,13 @@ private enum CLIInstanceCheck {
"media": model.fileName ?? "",
"status": model.state.status.rawValue,
"position": model.state.position,
"duration": model.state.duration,
"seekPending": model.state.seek_pending,
"canSeek": model.canSeek,
"busy": model.isBusy,
"youtubeHeight": model.youtubeHeight ?? 0,
"canChangeQuality": model.canChangeYouTubeQuality,
"qualityHeights": model.youtubeHeights,
"windows": windows.count,
"hasAudio": model.state.has_audio,
"hasVideo": model.state.has_video,
@@ -177,6 +218,10 @@ private enum CLIInstanceCheck {
"automaticQuality": model.automaticYouTubeQuality,
"snapToEdges": model.compactWindow.snappingEnabled,
"requestID": requestID,
"pendingRequestID": pendingRequestID,
"active": NSApp.isActive,
"keyWindow": windows.first?.isKeyWindow ?? false,
"mainWindow": windows.first?.isMainWindow ?? false,
"editorText": editorText,
"compact": model.isCompact,
"onTop": model.isOnTop,