2022-02-16 20:23:11 +00:00
|
|
|
import CoreMedia
|
|
|
|
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 currentTime = CMTime.zero
|
|
|
|
@Published var duration = CMTime.zero
|
|
|
|
@Published var presentingControls = false { didSet { handlePresentationChange() } }
|
|
|
|
@Published var timer: Timer?
|
|
|
|
@Published var playingFullscreen = false
|
|
|
|
|
2022-03-27 19:24:32 +00:00
|
|
|
private var throttle = Throttle(interval: 1)
|
|
|
|
|
2022-02-16 20:23:11 +00:00
|
|
|
var player: PlayerModel!
|
|
|
|
|
|
|
|
var playbackTime: String {
|
|
|
|
guard let current = currentTime.seconds.formattedAsPlaybackTime(),
|
|
|
|
let duration = duration.seconds.formattedAsPlaybackTime()
|
|
|
|
else {
|
|
|
|
return "--:-- / --:--"
|
|
|
|
}
|
|
|
|
|
|
|
|
var withoutSegments = ""
|
|
|
|
if let withoutSegmentsDuration = playerItemDurationWithoutSponsorSegments,
|
|
|
|
self.duration.seconds != withoutSegmentsDuration
|
|
|
|
{
|
|
|
|
withoutSegments = " (\(withoutSegmentsDuration.formattedAsPlaybackTime() ?? "--:--"))"
|
|
|
|
}
|
|
|
|
|
|
|
|
return "\(current) / \(duration)\(withoutSegments)"
|
|
|
|
}
|
|
|
|
|
|
|
|
var playerItemDurationWithoutSponsorSegments: Double? {
|
|
|
|
guard let duration = player.playerItemDurationWithoutSponsorSegments else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return duration.seconds
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlePresentationChange() {
|
|
|
|
if presentingControls {
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
2022-05-27 23:23:50 +00:00
|
|
|
self?.player?.backend.startControlsUpdates()
|
2022-02-16 20:23:11 +00:00
|
|
|
self?.resetTimer()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
player.backend.stopControlsUpdates()
|
|
|
|
timer?.invalidate()
|
|
|
|
timer = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-02-16 20:23:11 +00:00
|
|
|
withAnimation(PlayerControls.animation) {
|
|
|
|
presentingControls = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func hide() {
|
2022-03-27 19:24:32 +00:00
|
|
|
player?.backend.stopControlsUpdates()
|
|
|
|
|
|
|
|
guard !(player?.currentItem.isNil ?? true) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard presentingControls else {
|
|
|
|
return
|
|
|
|
}
|
2022-02-16 20:23:11 +00:00
|
|
|
withAnimation(PlayerControls.animation) {
|
|
|
|
presentingControls = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func toggle() {
|
2022-02-21 20:57:12 +00:00
|
|
|
withAnimation(PlayerControls.animation) {
|
2022-02-16 20:23:11 +00:00
|
|
|
presentingControls.toggle()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func reset() {
|
|
|
|
currentTime = .zero
|
|
|
|
duration = .zero
|
|
|
|
}
|
|
|
|
|
|
|
|
func resetTimer() {
|
2022-03-27 11:42:20 +00:00
|
|
|
if !presentingControls {
|
|
|
|
show()
|
|
|
|
}
|
|
|
|
|
2022-02-16 20:23:11 +00:00
|
|
|
removeTimer()
|
|
|
|
timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
|
|
|
|
withAnimation(PlayerControls.animation) { [weak self] in
|
|
|
|
self?.presentingControls = false
|
|
|
|
self?.player.backend.stopControlsUpdates()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeTimer() {
|
|
|
|
timer?.invalidate()
|
|
|
|
timer = nil
|
|
|
|
}
|
2022-03-27 19:24:32 +00:00
|
|
|
|
|
|
|
func update() {
|
|
|
|
throttle.execute { [weak self] in
|
|
|
|
self?.player?.backend.updateControls()
|
|
|
|
}
|
|
|
|
}
|
2022-02-16 20:23:11 +00:00
|
|
|
}
|