2022-06-29 21:43:39 +00:00
|
|
|
import Combine
|
2022-02-16 20:23:11 +00:00
|
|
|
import CoreMedia
|
2022-07-11 16:30:12 +00:00
|
|
|
import Defaults
|
2022-02-16 20:23:11 +00:00
|
|
|
import Foundation
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
final class PlayerControlsModel: ObservableObject {
|
2022-05-21 20:58:11 +00:00
|
|
|
@Published var isLoadingVideo = false
|
2022-02-16 20:23:11 +00:00
|
|
|
@Published var isPlaying = true
|
|
|
|
@Published var presentingControls = false { didSet { handlePresentationChange() } }
|
2022-07-11 16:30:12 +00:00
|
|
|
@Published var presentingControlsOverlay = false { didSet { handleSettingsOverlayPresentationChange() } }
|
|
|
|
@Published var presentingDetailsOverlay = false { didSet { handleDetailsOverlayPresentationChange() } }
|
2022-08-14 16:53:03 +00:00
|
|
|
var timer: Timer?
|
2022-02-16 20:23:11 +00:00
|
|
|
|
2022-06-29 21:43:39 +00:00
|
|
|
#if os(tvOS)
|
|
|
|
var reporter = PassthroughSubject<String, Never>()
|
|
|
|
#endif
|
|
|
|
|
2022-02-16 20:23:11 +00:00
|
|
|
var player: PlayerModel!
|
|
|
|
|
2022-06-18 12:39:49 +00:00
|
|
|
init(
|
|
|
|
isLoadingVideo: Bool = false,
|
|
|
|
isPlaying: Bool = true,
|
|
|
|
presentingControls: Bool = false,
|
|
|
|
presentingControlsOverlay: Bool = false,
|
2022-07-10 17:51:46 +00:00
|
|
|
presentingDetailsOverlay: Bool = false,
|
2022-06-18 12:39:49 +00:00
|
|
|
timer: Timer? = nil,
|
|
|
|
player: PlayerModel? = nil
|
|
|
|
) {
|
|
|
|
self.isLoadingVideo = isLoadingVideo
|
|
|
|
self.isPlaying = isPlaying
|
|
|
|
self.presentingControls = presentingControls
|
|
|
|
self.presentingControlsOverlay = presentingControlsOverlay
|
2022-07-10 17:51:46 +00:00
|
|
|
self.presentingDetailsOverlay = presentingDetailsOverlay
|
2022-06-18 12:39:49 +00:00
|
|
|
self.timer = timer
|
|
|
|
self.player = player
|
2022-02-16 20:23:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func handlePresentationChange() {
|
2022-07-10 17:51:46 +00:00
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
guard let self = self else { return }
|
|
|
|
if self.presentingControls {
|
|
|
|
self.player?.backend.startControlsUpdates()
|
|
|
|
self.resetTimer()
|
|
|
|
} else {
|
|
|
|
self.player?.backend.stopControlsUpdates()
|
|
|
|
self.timer?.invalidate()
|
|
|
|
self.timer = nil
|
2022-02-16 20:23:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-11 16:30:12 +00:00
|
|
|
func handleSettingsOverlayPresentationChange() {
|
|
|
|
player?.backend.setNeedsNetworkStateUpdates(presentingControlsOverlay && Defaults[.showMPVPlaybackStats])
|
2022-07-05 17:20:25 +00:00
|
|
|
if presentingControlsOverlay {
|
|
|
|
removeTimer()
|
|
|
|
} else {
|
|
|
|
resetTimer()
|
|
|
|
}
|
2022-06-24 23:39:29 +00:00
|
|
|
}
|
|
|
|
|
2022-07-11 16:30:12 +00:00
|
|
|
func handleDetailsOverlayPresentationChange() {
|
|
|
|
if presentingDetailsOverlay {
|
|
|
|
removeTimer()
|
|
|
|
} else {
|
|
|
|
resetTimer()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-10 17:51:46 +00:00
|
|
|
var presentingOverlays: Bool {
|
|
|
|
presentingDetailsOverlay || presentingControlsOverlay
|
|
|
|
}
|
|
|
|
|
|
|
|
func hideOverlays() {
|
|
|
|
presentingDetailsOverlay = false
|
|
|
|
presentingControlsOverlay = false
|
|
|
|
}
|
|
|
|
|
2022-02-16 20:23:11 +00:00
|
|
|
func show() {
|
2022-03-27 19:24:32 +00:00
|
|
|
guard !(player?.currentItem.isNil ?? true) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard !presentingControls else {
|
|
|
|
return
|
|
|
|
}
|
2022-02-21 20:57:12 +00:00
|
|
|
|
2022-06-16 17:44:39 +00:00
|
|
|
player.backend.updateControls()
|
2022-02-16 20:23:11 +00:00
|
|
|
withAnimation(PlayerControls.animation) {
|
|
|
|
presentingControls = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func hide() {
|
2022-06-07 21:27:48 +00:00
|
|
|
guard let player = player,
|
|
|
|
!player.musicMode
|
|
|
|
else {
|
|
|
|
return
|
|
|
|
}
|
2022-03-27 19:24:32 +00:00
|
|
|
|
2022-06-07 21:27:48 +00:00
|
|
|
player.backend.stopControlsUpdates()
|
|
|
|
|
|
|
|
guard !player.currentItem.isNil else {
|
2022-03-27 19:24:32 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard presentingControls else {
|
|
|
|
return
|
|
|
|
}
|
2022-02-16 20:23:11 +00:00
|
|
|
withAnimation(PlayerControls.animation) {
|
|
|
|
presentingControls = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func toggle() {
|
2022-06-07 21:27:48 +00:00
|
|
|
presentingControls ? hide() : show()
|
2022-02-16 20:23:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func resetTimer() {
|
|
|
|
removeTimer()
|
2022-06-07 21:27:48 +00:00
|
|
|
|
2022-06-07 22:05:02 +00:00
|
|
|
guard let player = player, !player.musicMode else {
|
2022-06-07 21:27:48 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-16 20:23:11 +00:00
|
|
|
timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
|
|
|
|
withAnimation(PlayerControls.animation) { [weak self] in
|
|
|
|
self?.presentingControls = false
|
|
|
|
self?.player.backend.stopControlsUpdates()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-29 14:38:37 +00:00
|
|
|
func startPiP(startImmediately: Bool = true) {
|
|
|
|
if player.activeBackend == .mpv {
|
|
|
|
player.avPlayerBackend.switchToMPVOnPipClose = true
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !os(macOS)
|
|
|
|
player.exitFullScreen()
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if player.activeBackend != PlayerBackendType.appleAVPlayer {
|
|
|
|
player.saveTime { [weak player] in
|
|
|
|
player?.changeActiveBackend(from: .mpv, to: .appleAVPlayer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak player] in
|
|
|
|
player?.avPlayerBackend.startPictureInPictureOnPlay = true
|
|
|
|
if startImmediately {
|
|
|
|
player?.pipController?.startPictureInPicture()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-16 20:23:11 +00:00
|
|
|
func removeTimer() {
|
|
|
|
timer?.invalidate()
|
|
|
|
timer = nil
|
|
|
|
}
|
2022-03-27 19:24:32 +00:00
|
|
|
|
|
|
|
func update() {
|
2022-06-16 17:44:39 +00:00
|
|
|
player?.backend.updateControls()
|
2022-03-27 19:24:32 +00:00
|
|
|
}
|
2022-02-16 20:23:11 +00:00
|
|
|
}
|