// // GifvController.swift // Tusker // // Created by Shadowfacts on 3/18/24. // Copyright © 2024 Shadowfacts. All rights reserved. // import Foundation import AVKit import Combine @MainActor class GifvController { private let asset: AVAsset private(set) var item: AVPlayerItem let player: AVPlayer private var isGrayscale = false let presentationSizeSubject = PassthroughSubject() private var presentationSizeObservation: NSKeyValueObservation? init(asset: AVAsset) { self.asset = asset self.item = AVPlayerItem(asset: asset) self.player = AVPlayer(playerItem: item) self.isGrayscale = Preferences.shared.grayscaleImages player.isMuted = true NotificationCenter.default.addObserver(self, selector: #selector(restartItem), name: .AVPlayerItemDidPlayToEndTime, object: item) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) updatePresentationSizeObservation() } func play() { if player.rate == 0 { player.play() } } func pause() { player.pause() } private func updatePresentationSizeObservation() { presentationSizeObservation = item.observe(\.presentationSize, changeHandler: { [unowned self] item, _ in self.presentationSizeSubject.send(item.presentationSize) }) } @objc private func restartItem() { item.seek(to: .zero) { (success) in guard success else { return } self.player.play() } } @objc private func preferencesChanged() { if isGrayscale != Preferences.shared.grayscaleImages { isGrayscale = Preferences.shared.grayscaleImages item = GifvController.createItem(asset: asset) player.replaceCurrentItem(with: item) self.updatePresentationSizeObservation() player.play() } } private static func createItem(asset: AVAsset) -> AVPlayerItem { let item = AVPlayerItem(asset: asset) if Preferences.shared.grayscaleImages { #if os(visionOS) #warning("Use async AVVideoComposition CIFilter initializer") #else item.videoComposition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { (request) in let filter = CIFilter(name: "CIColorMonochrome")! filter.setValue(request.sourceImage, forKey: "inputImage") filter.setValue(CIColor(red: 0.85, green: 0.85, blue: 0.85), forKey: "inputColor") filter.setValue(1.0, forKey: "inputIntensity") request.finish(with: filter.outputImage!, context: nil) }) #endif } return item } }