Player controls UI changes

WIP on controls

Chapters

working

Add previews variable

Add lists ids

WIP
This commit is contained in:
Arkadiusz Fal
2022-06-18 14:39:49 +02:00
parent 9c98cf9558
commit 321c265a11
60 changed files with 2524 additions and 1320 deletions

View File

@@ -11,6 +11,8 @@ final class AVPlayerBackend: PlayerBackend {
var model: PlayerModel!
var controls: PlayerControlsModel!
var playerTime: PlayerTimeModel!
var networkState: NetworkStateModel!
var stream: Stream?
var video: Video?
@@ -31,6 +33,11 @@ final class AVPlayerBackend: PlayerBackend {
avPlayer.timeControlStatus == .playing
}
var isSeeking: Bool {
// TODO: implement this maybe?
false
}
var playerItemDuration: CMTime? {
avPlayer.currentItem?.asset.duration
}
@@ -52,9 +59,10 @@ final class AVPlayerBackend: PlayerBackend {
private var timeObserverThrottle = Throttle(interval: 2)
init(model: PlayerModel, controls: PlayerControlsModel?) {
init(model: PlayerModel, controls: PlayerControlsModel?, playerTime: PlayerTimeModel?) {
self.model = model
self.controls = controls
self.playerTime = playerTime
addFrequentTimeObserver()
addInfrequentTimeObserver()
@@ -493,8 +501,8 @@ final class AVPlayerBackend: PlayerBackend {
return
}
self.controls.duration = self.playerItemDuration ?? .zero
self.controls.currentTime = self.currentTime ?? .zero
self.playerTime.duration = self.playerItemDuration ?? .zero
self.playerTime.currentTime = self.currentTime ?? .zero
#if !os(tvOS)
self.model.updateNowPlayingInfo()
@@ -581,4 +589,5 @@ final class AVPlayerBackend: PlayerBackend {
func stopControlsUpdates() {}
func setNeedsDrawing(_: Bool) {}
func setSize(_: Double, _: Double) {}
func updateNetworkState() {}
}

View File

@@ -12,6 +12,8 @@ final class MPVBackend: PlayerBackend {
var model: PlayerModel!
var controls: PlayerControlsModel!
var playerTime: PlayerTimeModel!
var networkState: NetworkStateModel!
var stream: Stream?
var video: Video?
@@ -24,17 +26,22 @@ final class MPVBackend: PlayerBackend {
return
}
self.controls.isLoadingVideo = self.isLoadingVideo
self.controls?.isLoadingVideo = self.isLoadingVideo
self.updateNetworkState()
if !self.isLoadingVideo {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
self?.handleEOF = true
}
}
self.model?.objectWillChange.send()
}
}}
var isPlaying = true { didSet {
updateNetworkState()
if isPlaying {
startClientUpdates()
} else {
@@ -49,6 +56,15 @@ final class MPVBackend: PlayerBackend {
}
#endif
}}
var isSeeking = false {
didSet {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.model.isSeeking = self.isSeeking
}
}
}
var playerItemDuration: CMTime?
#if !os(macOS)
@@ -88,9 +104,16 @@ final class MPVBackend: PlayerBackend {
client?.cacheDuration ?? 0
}
init(model: PlayerModel, controls: PlayerControlsModel? = nil) {
init(
model: PlayerModel,
controls: PlayerControlsModel? = nil,
playerTime: PlayerTimeModel? = nil,
networkState: NetworkStateModel? = nil
) {
self.model = model
self.controls = controls
self.playerTime = playerTime
self.networkState = networkState
clientTimer = .init(timeInterval: Self.controlsUpdateInterval)
clientTimer.eventHandler = getClientUpdates
@@ -155,7 +178,6 @@ final class MPVBackend: PlayerBackend {
if !preservingTime,
let segment = self.model.sponsorBlock.segments.first,
segment.end > 4,
self.model.lastSkipped.isNil
{
self.seek(to: segment.endTime) { finished in
@@ -202,7 +224,7 @@ final class MPVBackend: PlayerBackend {
let fileToLoad = self.model.musicMode ? stream.audioAsset.url : stream.videoAsset.url
let audioTrack = self.model.musicMode ? nil : stream.audioAsset.url
self.client.loadFile(fileToLoad, audio: audioTrack, time: time) { [weak self] _ in
self.client?.loadFile(fileToLoad, audio: audioTrack, time: time) { [weak self] _ in
self?.isLoadingVideo = true
self?.pause()
}
@@ -229,7 +251,7 @@ final class MPVBackend: PlayerBackend {
isPlaying = true
startClientUpdates()
if controls.presentingControls {
if controls?.presentingControls ?? false {
startControlsUpdates()
}
@@ -254,7 +276,7 @@ final class MPVBackend: PlayerBackend {
}
func seek(to time: CMTime, completionHandler: ((Bool) -> Void)?) {
client.seek(to: time) { [weak self] _ in
client?.seek(to: time) { [weak self] _ in
self?.getClientUpdates()
self?.updateControls()
completionHandler?(true)
@@ -262,7 +284,7 @@ final class MPVBackend: PlayerBackend {
}
func seek(relative time: CMTime, completionHandler: ((Bool) -> Void)? = nil) {
client.seek(relative: time) { [weak self] _ in
client?.seek(relative: time) { [weak self] _ in
self?.getClientUpdates()
self?.updateControls()
completionHandler?(true)
@@ -280,13 +302,7 @@ final class MPVBackend: PlayerBackend {
}
func enterFullScreen() {
model.toggleFullscreen(controls?.playingFullscreen ?? false)
#if os(iOS)
if Defaults[.lockOrientationInFullScreen] {
Orientation.lockOrientation(.landscape, andRotateTo: UIDevice.current.orientation.isLandscape ? nil : .landscapeRight)
}
#endif
model.toggleFullscreen(model?.playingFullScreen ?? false)
}
func exitFullScreen() {}
@@ -297,15 +313,13 @@ final class MPVBackend: PlayerBackend {
guard model.presentingPlayer else {
return
}
DispatchQueue.main.async { [weak self] in
guard let self = self else {
return
}
self.logger.info("updating controls")
self.controls.currentTime = self.currentTime ?? .zero
self.controls.duration = self.playerItemDuration ?? .zero
self.playerTime.currentTime = self.currentTime ?? .zero
self.playerTime.duration = self.playerItemDuration ?? .zero
}
}
@@ -375,13 +389,22 @@ final class MPVBackend: PlayerBackend {
case MPV_EVENT_PLAYBACK_RESTART:
isLoadingVideo = false
isSeeking = false
onFileLoaded?()
startClientUpdates()
onFileLoaded = nil
case MPV_EVENT_PAUSE:
updateNetworkState()
case MPV_EVENT_UNPAUSE:
isLoadingVideo = false
isSeeking = false
updateNetworkState()
case MPV_EVENT_SEEK:
isSeeking = true
case MPV_EVENT_END_FILE:
DispatchQueue.main.async { [weak self] in
@@ -417,18 +440,41 @@ final class MPVBackend: PlayerBackend {
}
func setSize(_ width: Double, _ height: Double) {
self.client?.setSize(width, height)
client?.setSize(width, height)
}
func addVideoTrack(_ url: URL) {
self.client?.addVideoTrack(url)
client?.addVideoTrack(url)
}
func setVideoToAuto() {
self.client?.setVideoToAuto()
client?.setVideoToAuto()
}
func setVideoToNo() {
self.client?.setVideoToNo()
client?.setVideoToNo()
}
func updateNetworkState() {
guard let client = client, let networkState = networkState else {
return
}
DispatchQueue.main.async {
networkState.pausedForCache = client.pausedForCache
networkState.cacheDuration = client.cacheDuration
networkState.bufferingState = client.bufferingState
}
if networkState.needsUpdates {
dispatchNetworkUpdate()
}
}
func dispatchNetworkUpdate() {
print("dispatching network update")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
self?.updateNetworkState()
}
}
}

View File

@@ -45,6 +45,9 @@ final class MPVClient: ObservableObject {
checkError(mpv_set_option_string(mpv, "input-media-keys", "yes"))
#endif
checkError(mpv_set_option_string(mpv, "cache-pause-initial", "yes"))
checkError(mpv_set_option_string(mpv, "cache-secs", "20"))
checkError(mpv_set_option_string(mpv, "cache-pause-wait", "2"))
checkError(mpv_set_option_string(mpv, "hwdec", "auto-safe"))
checkError(mpv_set_option_string(mpv, "vo", "libmpv"))
@@ -167,6 +170,10 @@ final class MPVClient: ObservableObject {
CMTime.secondsInDefaultTimescale(mpv.isNil ? -1 : getDouble("duration"))
}
var pausedForCache: Bool {
mpv.isNil ? false : getFlag("paused-for-cache")
}
func seek(relative time: CMTime, completionHandler: ((Bool) -> Void)? = nil) {
guard !seeking else {
logger.warning("ignoring seek, another in progress")
@@ -262,6 +269,12 @@ final class MPVClient: ObservableObject {
Int(getString("track-list/count") ?? "-1") ?? -1
}
private func getFlag(_ name: String) -> Bool {
var data = Int64()
mpv_get_property(mpv, name, MPV_FORMAT_FLAG, &data)
return data > 0
}
private func setFlagAsync(_ name: String, _ flag: Bool) {
var data: Int = flag ? 1 : 0
mpv_set_property_async(mpv, 0, name, MPV_FORMAT_FLAG, &data)

View File

@@ -5,6 +5,8 @@ import Foundation
protocol PlayerBackend {
var model: PlayerModel! { get set }
var controls: PlayerControlsModel! { get set }
var playerTime: PlayerTimeModel! { get set }
var networkState: NetworkStateModel! { get set }
var stream: Stream? { get set }
var video: Video? { get set }
@@ -14,6 +16,7 @@ protocol PlayerBackend {
var isLoadingVideo: Bool { get }
var isPlaying: Bool { get }
var isSeeking: Bool { get }
var playerItemDuration: CMTime? { get }
func bestPlayable(_ streams: [Stream], maxResolution: ResolutionSetting) -> Stream?
@@ -49,6 +52,8 @@ protocol PlayerBackend {
func startControlsUpdates()
func stopControlsUpdates()
func updateNetworkState()
func setNeedsDrawing(_ needsDrawing: Bool)
func setSize(_ width: Double, _ height: Double)
}

View File

@@ -13,4 +13,8 @@ enum PlayerBackendType: String, CaseIterable, Defaults.Serializable {
return "AVPlayer"
}
}
var supportsNetworkStateBufferingDetails: Bool {
self == .mpv
}
}