// // AVPlayerVC.swift // WOKA // // Created by Bilal on 05/07/2024. // import UIKit import AVFoundation class AVPlayerVC: UIViewController { @IBOutlet weak var viewControll: UIView! @IBOutlet weak var stackCtrView: UIStackView! @IBOutlet weak var loadingIndicator: UIActivityIndicatorView! @IBOutlet weak var sliderStack: UIStackView! @IBOutlet weak var tintView: UIView! @IBOutlet weak var videoTitle: UILabel! @IBOutlet weak var karaokeStack: UIStackView! @IBOutlet weak var playPauseRecordStack: UIStackView! @IBOutlet weak var karaokeLoading: UIActivityIndicatorView! @IBOutlet weak var reloadBtn: UIButton! @IBOutlet weak var img10SecBack: UIImageView! { didSet { self.img10SecBack.isUserInteractionEnabled = true self.img10SecBack.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap10SecBack))) } } @IBOutlet weak var imgPlay: UIImageView! { didSet { self.imgPlay.isUserInteractionEnabled = true self.imgPlay.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTapPlayPause))) } } @IBOutlet weak var img10SecFor: UIImageView! { didSet { self.img10SecFor.isUserInteractionEnabled = true self.img10SecFor.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap10SecNext))) } } @IBOutlet weak var lbCurrentTime: UILabel! @IBOutlet weak var lbTotalTime: UILabel! @IBOutlet weak var seekSlider: UISlider! { didSet { self.seekSlider.addTarget(self, action: #selector(onTapToSlide), for: .valueChanged) } } var videoURL : String? var titleVideo : String? var vm = AVPlayerVM() // MARK: - LifeCycle override func viewDidLoad() { super.viewDidLoad() vm.vc = self vm.initView() hideShowIndicator(isLoading: true) viewControll.addTapGesture { self.vm.timer?.invalidate() self.vm.showHideControls() } } override func viewDidAppear(_ animated: Bool) { self.vm.setVideoPlayer() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.vm.player?.pause() self.vm.player = nil } deinit { NotificationCenter.default.removeObserver(self) self.vm.player?.currentItem?.removeObserver(self, forKeyPath: "status") vm.player?.removeObserver(self, forKeyPath: "rate") if let playerItem = vm.player?.currentItem { playerItem.removeObserver(self, forKeyPath: "isPlaybackBufferEmpty") playerItem.removeObserver(self, forKeyPath: "isPlaybackLikelyToKeepUp") } } // MARK: - Button Clicks & Tap Handler @IBAction func reloadBtnTapped(_ sender: UIButton) { vm.isFinished = false vm.reloadVideo() } @IBAction func closeBtnTapped(_ sender: UIButton) { self.dismiss(animated: true) } @IBAction func startStopRecording(_ sender: LocalisedElementsButton) { } @IBAction func playPauseBtn(_ sender: LocalisedElementsButton) { } @objc func onTap10SecNext() { guard let currentTime = self.vm.player?.currentTime() else { return } let seekTime10Sec = CMTimeGetSeconds(currentTime).advanced(by: 10) let seekTime = CMTime(value: CMTimeValue(seekTime10Sec), timescale: 1) vm.timer?.invalidate() vm.timer = nil hideShowIndicator(isLoading: true) self.vm.player?.seek(to: seekTime, completionHandler: { [weak self] completed in if completed{ guard let self else{return} hideShowIndicator(isLoading: false) vm.startTimer() } }) } @objc func onTap10SecBack() { guard let currentTime = self.vm.player?.currentTime() else { return } vm.timer?.invalidate() vm.timer = nil hideShowIndicator(isLoading: true) let seekTime10Sec = CMTimeGetSeconds(currentTime).advanced(by: -10) let seekTime = CMTime(value: CMTimeValue(seekTime10Sec), timescale: 1) self.vm.player?.seek(to: seekTime, completionHandler: { [weak self] completed in if completed{ guard let self else{return} hideShowIndicator(isLoading: false) vm.startTimer() } }) } func hideShowIndicator(isLoading : Bool){ if isLoading{ self.imgPlay.isHidden = true self.loadingIndicator.startAnimating() }else{ self.imgPlay.isHidden = false self.loadingIndicator.stopAnimating() self.loadingIndicator.hidesWhenStopped = true } } @objc func onTapPlayPause() { if self.vm.player?.timeControlStatus == .playing { self.imgPlay.image = UIImage(systemName: "play.circle") self.vm.player?.pause() } else { self.imgPlay.image = UIImage(systemName: "pause.circle") self.vm.player?.play() } } @objc func onTapToSlide() { if vm.timer != nil{ vm.timer?.invalidate() vm.timer = nil } self.vm.isThumbSeek = true guard let duration = self.vm.player?.currentItem?.duration else { return } let value = Float64(self.seekSlider.value) * CMTimeGetSeconds(duration) if value.isNaN == false { let seekTime = CMTime(value: CMTimeValue(value), timescale: 1) self.vm.player?.seek(to: seekTime, completionHandler: { completed in if completed { self.vm.isThumbSeek = false self.vm.startTimer() } }) } } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "rate" { if let player = object as? AVPlayer { if player.rate == 0 { print("Player is paused") } else { print("Player is playing") hideShowIndicator(isLoading: false) imgPlay.image = UIImage(systemName: "pause.circle") } } } else if let playerItem = object as? AVPlayerItem { if keyPath == "isPlaybackBufferEmpty" { if playerItem.isPlaybackBufferEmpty { print("Loading") hideShowIndicator(isLoading: true) } } else if keyPath == "isPlaybackLikelyToKeepUp" { if playerItem.isPlaybackLikelyToKeepUp { print("finished") hideShowIndicator(isLoading: false) } } } } // // Observe for player item status changes // override func observeValue(forKeyPath keyPath: String?, // of object: Any?, // change: [NSKeyValueChangeKey : Any]?, // context: UnsafeMutableRawPointer?) { // if keyPath == "status" { // if let playerItem = object as? AVPlayerItem { // switch playerItem.status { // case .readyToPlay: // // Ready to play // print("Player item is ready to play.") // vm.player?.play() // hideShowIndicator(isLoading: false) // imgPlay.image = UIImage(systemName: "pause.circle") // case .failed: // // Failed // if let error = playerItem.error { // print("Player item error: \(error.localizedDescription)") // vm.handlePlayerError(error) // } // case .unknown: // // Unknown status // print("Player item status is unknown.") // @unknown default: // // Handle unexpected cases // print("Player item status is unknown.") // } // } // } // } // @objc private func onTapToggleScreen() { // if #available(iOS 16.0, *) { // guard let windowSceen = self.view.window?.windowScene else { return } // if windowSceen.interfaceOrientation == .portrait { // windowSceen.requestGeometryUpdate(.iOS(interfaceOrientations: .landscape)) { error in // print(error.localizedDescription) // } // } else { // windowSceen.requestGeometryUpdate(.iOS(interfaceOrientations: .portrait)) { error in // print(error.localizedDescription) // } // } // } else { // if UIDevice.current.orientation == .portrait { // let orientation = UIInterfaceOrientation.landscapeRight.rawValue // UIDevice.current.setValue(orientation, forKey: "orientation") // } else { // let orientation = UIInterfaceOrientation.portrait.rawValue // UIDevice.current.setValue(orientation, forKey: "orientation") // } // } // } }