2022-06-18 12:39:49 +00:00
|
|
|
import CoreMedia
|
|
|
|
import Foundation
|
2022-08-28 17:18:49 +00:00
|
|
|
import SwiftUI
|
2022-06-18 12:39:49 +00:00
|
|
|
|
|
|
|
final class PlayerTimeModel: ObservableObject {
|
2022-08-28 17:18:49 +00:00
|
|
|
enum SeekType: Equatable {
|
|
|
|
case segmentSkip(String)
|
|
|
|
case segmentRestore
|
|
|
|
case userInteracted
|
|
|
|
case loopRestart
|
|
|
|
case backendSync
|
|
|
|
|
|
|
|
var presentable: Bool {
|
|
|
|
self != .backendSync
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-18 12:39:49 +00:00
|
|
|
static let timePlaceholder = "--:--"
|
|
|
|
|
|
|
|
@Published var currentTime = CMTime.zero
|
|
|
|
@Published var duration = CMTime.zero
|
|
|
|
|
2022-08-28 17:18:49 +00:00
|
|
|
@Published var lastSeekTime: CMTime?
|
|
|
|
@Published var lastSeekType: SeekType?
|
|
|
|
@Published var restoreSeekTime: CMTime?
|
|
|
|
|
|
|
|
@Published var gestureSeek = 0.0
|
|
|
|
@Published var gestureStart = 0.0
|
|
|
|
|
|
|
|
@Published var seekOSDDismissed = true
|
|
|
|
|
|
|
|
var player: PlayerModel!
|
2022-06-18 12:39:49 +00:00
|
|
|
|
2022-08-27 00:43:47 +00:00
|
|
|
var forceHours: Bool {
|
|
|
|
duration.seconds >= 60 * 60
|
|
|
|
}
|
|
|
|
|
2022-06-18 12:39:49 +00:00
|
|
|
var currentPlaybackTime: String {
|
|
|
|
if player?.currentItem.isNil ?? true || duration.seconds.isZero {
|
|
|
|
return Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
2022-08-27 00:43:47 +00:00
|
|
|
return currentTime.seconds.formattedAsPlaybackTime(allowZero: true, forceHours: forceHours) ?? Self.timePlaceholder
|
2022-06-18 12:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var durationPlaybackTime: String {
|
|
|
|
if player?.currentItem.isNil ?? true {
|
|
|
|
return Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
|
|
|
return duration.seconds.formattedAsPlaybackTime() ?? Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
|
|
|
var withoutSegmentsPlaybackTime: String {
|
2022-08-28 17:18:49 +00:00
|
|
|
guard let withoutSegmentsDuration = player?.playerItemDurationWithoutSponsorSegments?.seconds else { return Self.timePlaceholder }
|
|
|
|
return withoutSegmentsDuration.formattedAsPlaybackTime(forceHours: forceHours) ?? Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastSeekPlaybackTime: String {
|
|
|
|
guard let time = lastSeekTime else { return 0.formattedAsPlaybackTime(allowZero: true, forceHours: forceHours) ?? Self.timePlaceholder }
|
|
|
|
return time.seconds.formattedAsPlaybackTime(allowZero: true, forceHours: forceHours) ?? Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
|
|
|
var restoreSeekPlaybackTime: String {
|
|
|
|
guard let time = restoreSeekTime else { return Self.timePlaceholder }
|
|
|
|
return time.seconds.formattedAsPlaybackTime(allowZero: true, forceHours: forceHours) ?? Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
|
|
|
var gestureSeekDestinationTime: Double {
|
|
|
|
min(duration.seconds, max(0, gestureStart + gestureSeek))
|
|
|
|
}
|
|
|
|
|
|
|
|
var gestureSeekDestinationPlaybackTime: String {
|
|
|
|
guard gestureSeek != 0 else { return Self.timePlaceholder }
|
|
|
|
return gestureSeekDestinationTime.formattedAsPlaybackTime(allowZero: true, forceHours: forceHours) ?? Self.timePlaceholder
|
|
|
|
}
|
|
|
|
|
|
|
|
func onSeekGestureStart(completionHandler: (() -> Void)? = nil) {
|
|
|
|
player.backend.getTimeUpdates()
|
|
|
|
player.backend.updateControls {
|
|
|
|
self.gestureStart = self.currentTime.seconds
|
|
|
|
completionHandler?()
|
2022-06-18 12:39:49 +00:00
|
|
|
}
|
2022-08-28 17:18:49 +00:00
|
|
|
}
|
2022-06-18 12:39:49 +00:00
|
|
|
|
2022-08-28 17:18:49 +00:00
|
|
|
func onSeekGestureEnd() {
|
|
|
|
player.backend.updateControls()
|
|
|
|
player.backend.seek(to: gestureSeekDestinationTime, seekType: .userInteracted)
|
|
|
|
}
|
|
|
|
|
|
|
|
func registerSeek(at time: CMTime, type: SeekType, restore restoreTime: CMTime? = nil) {
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
withAnimation {
|
|
|
|
self?.lastSeekTime = time
|
|
|
|
self?.lastSeekType = type
|
|
|
|
self?.restoreSeekTime = restoreTime
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func restoreTime() {
|
|
|
|
guard let time = restoreSeekTime else { return }
|
|
|
|
switch lastSeekType {
|
|
|
|
case .segmentSkip:
|
|
|
|
player.restoreLastSkippedSegment()
|
|
|
|
default:
|
|
|
|
player?.backend.seek(to: time, seekType: .userInteracted)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resetSeek() {
|
|
|
|
withAnimation {
|
|
|
|
lastSeekTime = nil
|
|
|
|
lastSeekType = nil
|
|
|
|
}
|
2022-06-18 12:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func reset() {
|
|
|
|
currentTime = .zero
|
|
|
|
duration = .zero
|
2022-08-28 17:18:49 +00:00
|
|
|
resetSeek()
|
|
|
|
gestureSeek = 0
|
2022-06-18 12:39:49 +00:00
|
|
|
}
|
|
|
|
}
|