Kurdtvs-Live-Kurdish-TV-Kur.../Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewModel....

141 lines
4.7 KiB
Swift
Raw Normal View History

//
// MediaPreviewVideoViewModel.swift
// Mastodon
//
// Created by MainasuK on 2022-2-9.
//
import os.log
import UIKit
import AVKit
import Combine
import AlamofireImage
final class MediaPreviewVideoViewModel {
let logger = Logger(subsystem: "MediaPreviewVideoViewModel", category: "ViewModel")
var disposeBag = Set<AnyCancellable>()
// input
let context: AppContext
let item: Item
// output
public private(set) var player: AVPlayer?
private var playerLooper: AVPlayerLooper?
@Published var playbackState = PlaybackState.unknown
init(context: AppContext, item: Item) {
self.context = context
self.item = item
// end init
switch item {
case .video(let mediaContext):
guard let assertURL = mediaContext.assetURL else { return }
let playerItem = AVPlayerItem(url: assertURL)
let _player = AVPlayer(playerItem: playerItem)
self.player = _player
case .gif(let mediaContext):
guard let assertURL = mediaContext.assetURL else { return }
let playerItem = AVPlayerItem(url: assertURL)
let _player = AVQueuePlayer(playerItem: playerItem)
_player.isMuted = true
self.player = _player
if let templateItem = _player.items().first {
let _playerLooper = AVPlayerLooper(player: _player, templateItem: templateItem)
self.playerLooper = _playerLooper
}
}
guard let player = player else {
assertionFailure()
return
}
// setup player state observer
$playbackState
.receive(on: DispatchQueue.main)
.sink { [weak self] status in
guard let self = self else { return }
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): player state: \(status.description)")
switch status {
case .unknown, .buffering, .readyToPlay:
break
case .playing:
try? AVAudioSession.sharedInstance().setCategory(.playback)
try? AVAudioSession.sharedInstance().setActive(true)
case .paused, .stopped, .failed:
try? AVAudioSession.sharedInstance().setCategory(.ambient) // set to ambient to allow mixed (needed for GIFV)
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
}
}
.store(in: &disposeBag)
player.publisher(for: \.status, options: [.initial, .new])
.sink(receiveValue: { [weak self] status in
guard let self = self else { return }
switch status {
case .failed:
self.playbackState = .failed
case .readyToPlay:
self.playbackState = .readyToPlay
case .unknown:
self.playbackState = .unknown
@unknown default:
assertionFailure()
}
})
.store(in: &disposeBag)
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: nil)
.sink { [weak self] notification in
guard let self = self else { return }
guard let playerItem = notification.object as? AVPlayerItem,
let urlAsset = playerItem.asset as? AVURLAsset
else { return }
print(urlAsset.url)
guard urlAsset.url == item.assetURL else { return }
self.playbackState = .stopped
}
.store(in: &disposeBag)
}
}
extension MediaPreviewVideoViewModel {
enum Item {
case video(RemoteVideoContext)
case gif(RemoteGIFContext)
var previewURL: URL? {
switch self {
case .video(let mediaContext): return mediaContext.previewURL
case .gif(let mediaContext): return mediaContext.previewURL
}
}
var assetURL: URL? {
switch self {
case .video(let mediaContext): return mediaContext.assetURL
case .gif(let mediaContext): return mediaContext.assetURL
}
}
}
struct RemoteVideoContext {
let assetURL: URL?
let previewURL: URL?
// let thumbnail: UIImage?
}
struct RemoteGIFContext {
let assetURL: URL?
let previewURL: URL?
}
}