//// //// AVPlayerVM.swift //// WOKA //// //// Created by Bilal on 05/07/2024. //// // //import UIKit //import AVKit // //class AVPlayerVM{ // // weak var vc : AVPlayerVC! // // var player : AVPlayer? = nil // var isThumbSeek : Bool = false // var timer : Timer? // var isFinished = false{ // didSet{ // if isFinished{ // timer?.invalidate() // timer = nil // vc.reloadBtn.isHidden = false // vc.stackCtrView.isHidden = true // vc.tintView.isHidden = false // vc.sliderStack.isHidden = true // }else{ // vc.reloadBtn.isHidden = true // vc.stackCtrView.isHidden = false // vc.sliderStack.isHidden = false // } // } // } // // private var playerLayer : AVPlayerLayer? = nil // private var timeObserver : Any? = nil // // func initView(){ // vc.seekSlider.transform = CGAffineTransform(scaleX: 0.85, y: 0.85) // // self.vc.videoTitle.text = vc.titleVideo // setupKaraoke() // // } // // @objc func videoDidFinish() { // self.isFinished = true // // } // // func setupKaraoke(){ // guard let url = vc.videoURL else{return} // hideShowKaraoke(isLoading: true) // let avURL = URL(string: url)! // let asset = AVAsset(url: avURL) // // let outputURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(vc.titleVideo?.trimmingCharacters(in: .whitespaces) ?? "extractedAudio").m4a") // FileManager.default.clearTmpDirectory() // asset.writeAudioTrackToURL(outputURL) { [weak self] isDone, error,url in // guard let self else{return} // print(isDone, error , url) // if error == nil{ // hideShowKaraoke(isLoading: false) // } // } // } // // func hideShowKaraoke(isLoading : Bool){ // DispatchQueue.main.async { [weak self] in // guard let self else{return} // if isLoading{ // vc.karaokeLoading.startAnimating() // vc.karaokeStack.isHidden = false // vc.playPauseRecordStack.isHidden = true // }else{ // vc.karaokeLoading.stopAnimating() // vc.karaokeLoading.hidesWhenStopped = true // vc.karaokeStack.isHidden = true // vc.playPauseRecordStack.isHidden = false // } // } // } // // // MARK: - Setup Video Player // // func setVideoPlayer() { // guard let videoURL = vc.videoURL, let url = URL(string: videoURL) else { return } // // if self.player == nil { // // // Create a subview for the video player // let playerView = UIView(frame: vc.viewControll.bounds) // playerView.backgroundColor = .clear // Make sure the background is clear // vc.viewControll.addSubview(playerView) // vc.viewControll.sendSubviewToBack(playerView) // Ensure the player view is below other UI elements // // // Initialize the player // let playerItem = AVPlayerItem(url: url) // player = AVPlayer(playerItem: playerItem) // // // Add observer for AVPlayerItemFailedToPlayToEndTimeNotification // NotificationCenter.default.addObserver(self, // selector: #selector(playerItemFailedToPlayToEndTime(_:)), // name: .AVPlayerItemFailedToPlayToEndTime, // object: playerItem) // // // Add observer for AVPlayerItem's status // playerItem.addObserver(self.vc, // forKeyPath: "status", // options: [.new, .initial], // context: nil) // // // Set up the player layer // playerLayer = AVPlayerLayer(player: player) // playerLayer?.videoGravity = .resizeAspectFill // playerLayer?.frame = playerView.bounds // // if let playerLayer = playerLayer { // playerView.layer.addSublayer(playerLayer) // } // // // Add observer for play/pause // player?.addObserver(self.vc, forKeyPath: "rate", options: [.new, .initial], context: nil) // // // Add observer for buffering // playerItem.addObserver(self.vc, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .initial], context: nil) // playerItem.addObserver(self.vc, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .initial], context: nil) // // // Add observer for video finished playing // NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinish), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) // self.setObserverToPlayer() // startTimer() // player?.play() // } // } // // func reloadVideo() { // guard let videoURL = vc.videoURL, let url = URL(string: videoURL) else { return } // // // Remove existing observers // NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem) // player?.currentItem?.removeObserver(vc, forKeyPath: "status") // player?.currentItem?.removeObserver(vc, forKeyPath: "isPlaybackBufferEmpty") // player?.currentItem?.removeObserver(vc, forKeyPath: "isPlaybackLikelyToKeepUp") // // // Create a new player item // let playerItem = AVPlayerItem(url: url) // player?.replaceCurrentItem(with: playerItem) // // // Add observers again // NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinish), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) // playerItem.addObserver(vc, forKeyPath: "status", options: [.new, .initial], context: nil) // playerItem.addObserver(vc, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .initial], context: nil) // playerItem.addObserver(vc, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .initial], context: nil) // // player?.play() // } // // func setObserverToPlayer() { // let interval = CMTime(seconds: 0.3, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) // timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { elapsed in // self.updatePlayerTime() // }) // } // // // // Handle notification // @objc func playerItemFailedToPlayToEndTime(_ notification: Notification) { // if let error = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? Error { // print("Error: \(error.localizedDescription)") // handlePlayerError(error) // } // } // // func handlePlayerError(_ error: Error) { // // Update the UI or show an alert to the user // Utilities.alertWithBtnCompletion(title: "Error", msgBody: error.localizedDescription, okBtnStr: "Retry?", vc: vc.self) { [weak self] isDone in // guard let self else{return} // player?.play() // } // } // // // MARK: - ShowHideControls // func startTimer(){ // timer = Timer.scheduledTimer(withTimeInterval: 4.5, repeats: false) { _ in // self.showHideControls() // } // } // // func showHideControls(){ // if isFinished{ // vc.stackCtrView.isHidden = true // vc.reloadBtn.isHidden.toggle() // vc.sliderStack.isHidden = true // }else{ // vc.reloadBtn.isHidden = true // vc.stackCtrView.isHidden.toggle() // vc.sliderStack.isHidden.toggle() // } // // vc.tintView.isHidden.toggle() // // if !vc.stackCtrView.isHidden{ // startTimer() // } // } // // // MARK: - Update the player time Label with Slider // // private func updatePlayerTime() { // guard let currentTime = self.player?.currentTime() else { return } // guard let duration = self.player?.currentItem?.duration else { return } // // let currentTimeInSecond = CMTimeGetSeconds(currentTime) // let durationTimeInSecond = CMTimeGetSeconds(duration) // // if self.isThumbSeek == false { // self.vc.seekSlider.value = Float(currentTimeInSecond/durationTimeInSecond) // } // // let value = Float64(self.vc.seekSlider.value) * CMTimeGetSeconds(duration) // // var hours = value / 3600 // var mins = (value / 60).truncatingRemainder(dividingBy: 60) // var secs = value.truncatingRemainder(dividingBy: 60) // var timeformatter = NumberFormatter() // timeformatter.minimumIntegerDigits = 2 // timeformatter.minimumFractionDigits = 0 // timeformatter.roundingMode = .down // guard let hoursStr = timeformatter.string(from: NSNumber(value: hours)), let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else { // return // } // self.vc.lbCurrentTime.text = "\(hoursStr):\(minsStr):\(secsStr)" // // hours = durationTimeInSecond / 3600 // mins = (durationTimeInSecond / 60).truncatingRemainder(dividingBy: 60) // secs = durationTimeInSecond.truncatingRemainder(dividingBy: 60) // timeformatter = NumberFormatter() // timeformatter.minimumIntegerDigits = 2 // timeformatter.minimumFractionDigits = 0 // timeformatter.roundingMode = .down // guard let hoursStr = timeformatter.string(from: NSNumber(value: hours)), let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else { // return // } // self.vc.lbTotalTime.text = "\(hoursStr):\(minsStr):\(secsStr)" // } //}