fix: respond to macOS system playback controls
This commit is contained in:
@@ -48,6 +48,7 @@ final class PlayerModel: ObservableObject {
|
||||
private var progressURL: URL?
|
||||
private var resumePosition: Double?
|
||||
private var lastSavedPosition: Double?
|
||||
private var systemPlayback: SystemPlaybackControls?
|
||||
|
||||
init(defaults: UserDefaults = .standard) {
|
||||
playbackProgress = PlaybackProgress(defaults: defaults)
|
||||
@@ -65,6 +66,7 @@ final class PlayerModel: ObservableObject {
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] _ in
|
||||
MainActor.assumeIsolated { self?.poll() }
|
||||
}
|
||||
systemPlayback = SystemPlaybackControls(model: self)
|
||||
}
|
||||
|
||||
var isBusy: Bool { resolvingYouTube || pendingRevision != nil || state.status == PLAYER_OPENING }
|
||||
@@ -138,6 +140,7 @@ final class PlayerModel: ObservableObject {
|
||||
let request = UUID()
|
||||
youtubeRequest = request
|
||||
resolvingYouTube = true
|
||||
systemPlayback?.update()
|
||||
commandError = nil
|
||||
revealControls()
|
||||
youtubeTask = Task { [weak self] in
|
||||
@@ -212,6 +215,7 @@ final class PlayerModel: ObservableObject {
|
||||
window?.representedURL = url
|
||||
window?.title = title
|
||||
revealControls()
|
||||
systemPlayback?.update()
|
||||
}
|
||||
|
||||
func open(_ url: URL) {
|
||||
@@ -236,10 +240,16 @@ final class PlayerModel: ObservableObject {
|
||||
}
|
||||
|
||||
func togglePlayback() {
|
||||
guard let handle, canTogglePlayback else { return }
|
||||
let accepted = state.status == PLAYER_PLAYING ? player_pause(handle) : player_play(handle)
|
||||
setPlayback(playing: state.status != PLAYER_PLAYING)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func setPlayback(playing: Bool) -> Bool {
|
||||
guard let handle, canTogglePlayback else { return false }
|
||||
let accepted = playing ? player_play(handle) : player_pause(handle)
|
||||
commandError = accepted ? nil : "Could not queue playback. Please try again."
|
||||
revealControls()
|
||||
return accepted
|
||||
}
|
||||
|
||||
func finishSeeking() {
|
||||
@@ -275,10 +285,12 @@ final class PlayerModel: ObservableObject {
|
||||
window?.representedURL = nil
|
||||
window?.title = "terebi"
|
||||
revealControls()
|
||||
systemPlayback?.update()
|
||||
}
|
||||
|
||||
func seekRelative(_ seconds: Double) {
|
||||
guard canSeek, let handle else { return }
|
||||
@discardableResult
|
||||
func seekRelative(_ seconds: Double) -> Bool {
|
||||
guard canSeek, let handle, seconds.isFinite else { return false }
|
||||
let target = min(max(0, (seekPending ? seekPosition : state.position) + seconds), duration)
|
||||
let accepted = player_seek(handle, target)
|
||||
if accepted {
|
||||
@@ -287,6 +299,7 @@ final class PlayerModel: ObservableObject {
|
||||
}
|
||||
commandError = accepted ? nil : "Could not queue the seek. Please try again."
|
||||
revealControls()
|
||||
return accepted
|
||||
}
|
||||
|
||||
func stepFrame(_ direction: Int32) {
|
||||
@@ -458,6 +471,7 @@ final class PlayerModel: ObservableObject {
|
||||
guard next.revision != revision, next.status != PLAYER_OPENING else { return }
|
||||
pendingRevision = nil
|
||||
}
|
||||
defer { systemPlayback?.update() }
|
||||
let statusChanged = state.status != next.status
|
||||
state = next
|
||||
if next.status == PLAYER_EMPTY || next.status == PLAYER_ERROR ||
|
||||
@@ -496,6 +510,8 @@ final class PlayerModel: ObservableObject {
|
||||
func shutdown() {
|
||||
saveProgress()
|
||||
cancelYouTubeResolution()
|
||||
systemPlayback?.shutdown()
|
||||
systemPlayback = nil
|
||||
compactWindow.restore(resumeFullScreen: false)
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
@@ -1151,7 +1167,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
#if !PLAYER_CHECK
|
||||
@main
|
||||
#endif
|
||||
@MainActor
|
||||
struct TerebiApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) private var delegate
|
||||
|
||||
Reference in New Issue
Block a user