113 lines
3.5 KiB
Swift
113 lines
3.5 KiB
Swift
//
|
|
// NativePlayerViewController.swift
|
|
// JellyfinVideoPlayerDev
|
|
//
|
|
// Created by Ethan Pippin on 11/20/21.
|
|
//
|
|
|
|
import AVKit
|
|
import Combine
|
|
import JellyfinAPI
|
|
import UIKit
|
|
|
|
class NativePlayerViewController: AVPlayerViewController {
|
|
|
|
let viewModel: VideoPlayerViewModel
|
|
|
|
private var timeObserverToken: Any?
|
|
|
|
private var lastProgressTicks: Int64 = 0
|
|
|
|
init(viewModel: VideoPlayerViewModel) {
|
|
|
|
self.viewModel = viewModel
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
|
|
|
let player = AVPlayer(url: viewModel.hlsURL)
|
|
|
|
player.appliesMediaSelectionCriteriaAutomatically = false
|
|
player.currentItem?.externalMetadata = createMetadata()
|
|
|
|
let timeScale = CMTimeScale(NSEC_PER_SEC)
|
|
let time = CMTime(seconds: 5, preferredTimescale: timeScale)
|
|
|
|
timeObserverToken = player.addPeriodicTimeObserver(forInterval: time, queue: .main) { [weak self] time in
|
|
// print("Timer timed: \(time)")
|
|
|
|
if time.seconds != 0 {
|
|
self?.sendProgressReport(seconds: time.seconds)
|
|
}
|
|
}
|
|
|
|
self.player = player
|
|
|
|
self.allowsPictureInPicturePlayback = true
|
|
self.player?.allowsExternalPlayback = true
|
|
}
|
|
|
|
private func createMetadata() -> [AVMetadataItem] {
|
|
let allMetadata: [AVMetadataIdentifier: Any] = [
|
|
.commonIdentifierTitle: viewModel.title,
|
|
.iTunesMetadataTrackSubTitle: viewModel.subtitle ?? "",
|
|
.commonIdentifierArtwork: UIImage(data: try! Data(contentsOf: viewModel.item.getBackdropImage(maxWidth: 200)))?.pngData() as Any,
|
|
.commonIdentifierDescription: viewModel.item.overview ?? ""
|
|
]
|
|
|
|
return allMetadata.compactMap { createMetadataItem(for:$0, value:$1) }
|
|
}
|
|
|
|
private func createMetadataItem(for identifier: AVMetadataIdentifier,
|
|
value: Any) -> AVMetadataItem {
|
|
let item = AVMutableMetadataItem()
|
|
item.identifier = identifier
|
|
item.value = value as? NSCopying & NSObjectProtocol
|
|
// Specify "und" to indicate an undefined language.
|
|
item.extendedLanguageTag = "und"
|
|
return item.copy() as! AVMetadataItem
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
|
|
stop()
|
|
removePeriodicTimeObserver()
|
|
}
|
|
|
|
func removePeriodicTimeObserver() {
|
|
if let timeObserverToken = timeObserverToken {
|
|
player?.removeTimeObserver(timeObserverToken)
|
|
self.timeObserverToken = nil
|
|
}
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
player?.seek(to: CMTimeMake(value: viewModel.item.userData?.playbackPositionTicks ?? 0, timescale: 10_000_000), toleranceBefore: CMTimeMake(value: 5, timescale: 1), toleranceAfter: CMTimeMake(value: 5, timescale: 1), completionHandler: { _ in
|
|
self.play()
|
|
})
|
|
}
|
|
|
|
private func play() {
|
|
player?.play()
|
|
viewModel.sendPlayReport()
|
|
}
|
|
|
|
private func sendProgressReport(seconds: Double) {
|
|
viewModel.sendProgressReport()
|
|
}
|
|
|
|
private func stop() {
|
|
viewModel.sendStopReport()
|
|
}
|
|
}
|