// // MoreVC.swift // WOKA // // Created by MacBook Pro on 10/06/24. // import UIKit import AVFoundation class MoreVC: UIViewController { @IBOutlet weak var blogsCollectionView: UICollectionView! @IBOutlet weak var homeBtn: UIView! @IBOutlet weak var songTableView: UITableView! var vm = MoreVM() var timeObserverToken: Any? override func viewDidLoad() { super.viewDidLoad() vm.vc = self vm.initView() } override func viewWillAppear(_ animated: Bool) { NotificationCenter.default.post(name: .enableDisableSideBar, object: nil, userInfo: ["type": false]) } override func viewWillDisappear(_ animated: Bool) { NotificationCenter.default.post(name: .enableDisableSideBar, object: nil, userInfo: ["type": true]) vm.player?.pause() } @IBAction func playTrailerBtnTapped(_ sender: LocalisedElementsButton) { let item = JwPlayerItemCreate(url: APIEndPoints.StaticURLs.masilaUrl, poster: nil, titles: "Masila") JWPlayerManager.shared.presentPlayer(from: self, playerItems: [item], contentType: .trailer) } } // MARK: - TableView DataSource , Delegates extension MoreVC : TableViewSRC{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return vm.songData.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "SongListCell", for: indexPath) as! SongListCell let songData = vm.songData[indexPath.row] // let isActive = (indexPath.row == vm.currentIndexPlayingSong) if let index = vm.currentIndexPlayingSong , index == indexPath.row{ cell.setData(data: songData,playerStatus: vm.playerStatus, active: true, currentTime: currentTimePlayer) }else{ cell.setData(data: songData,playerStatus: .stopped , active: false, currentTime: 0) } return cell } func getFormattedTime(from player: AVPlayer) -> String { let currentTime = player.currentTime() let seconds = CMTimeGetSeconds(currentTime) if !seconds.isFinite { return "00:00:00" } let hours = Int(seconds) / 3600 let minutes = (Int(seconds) % 3600) / 60 let secs = Int(seconds) % 60 return String(format: "%02d:%02d:%02d", hours, minutes, secs) } func handleTap(indexPath : IndexPath){ } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) else { return } addAnimation(cell: cell) { [weak self] in guard let self else{return} /* this will declare if the song is playing or not */ if let playingIndex = vm.currentIndexPlayingSong { if indexPath.row == playingIndex{ // if same row is selected pause the row print(vm.playerStatus) switch vm.playerStatus{ case .play: // if player is playing pause it. print("Player is playing") vm.player?.pause() vm.playerStatus = .pause tableView.reloadRows(at: [IndexPath(row: vm.currentIndexPlayingSong!, section: 0)], with: .none) case .pause: vm.player?.play() vm.playerStatus = .resume tableView.reloadRows(at: [IndexPath(row: vm.currentIndexPlayingSong!, section: 0)], with: .none) case .loading: print("Player is loading") case .resume: print("Player is resume") default: break } }else{ /* this means other cell was playing now stop it and play this new cell first reload the playing cell and pause the audio */ //Reset Old Cell vm.playerStatus = .stopped tableView.reloadRows(at: [IndexPath(row: vm.currentIndexPlayingSong!, section: 0)], with: .none) //Update new cell vm.currentIndexPlayingSong = indexPath.row vm.playerStatus = .loading currentTimePlayer = 0 tableView.reloadRows(at: [indexPath], with: .none) let data = vm.songData[indexPath.row] startPlaying(song: data, at: indexPath) print("Other cell playing") } }else{ // if there is no playing audio before vm.playerStatus = .loading vm.currentIndexPlayingSong = indexPath.row currentTimePlayer = 0 tableView.reloadRows(at: [indexPath], with: .none) let data = vm.songData[indexPath.row] startPlaying(song: data, at: indexPath) print("First Play") } } } func addAnimation(cell : UITableViewCell, completion : @escaping () -> Void){ UIView.animate(withDuration: 0.1,animations: { cell.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) }, completion: { _ in UIView.animate(withDuration: 0.1) { cell.transform = CGAffineTransform.identity completion() } }) } func startPlaying(song: SongBlogDM.PaintDatum, at indexPath: IndexPath) { // Set up and start the player stopPlaying() if let urlString = song.contentMoreDetails?.first?.url , let url = URL(string: urlString){ vm.player = AVPlayer(url: url) vm.player?.play() vm.playerStatus = .play vm.currentIndexPlayingSong = indexPath.row addPeriodicTimeObserver() vm.observePlayer() } } func stopPlaying() { // Remove the periodic time observer removePeriodicTimeObserver() // Stop the player vm.player?.pause() // vm.player = nil vm.playerStatus = .stopped vm.currentIndexPlayingSong = nil } func removePeriodicTimeObserver() { if let token = timeObserverToken { vm.player?.removeTimeObserver(token) timeObserverToken = nil } } func addPeriodicTimeObserver() { let interval = CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) timeObserverToken = vm.player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in guard let self = self else { return } currentTimePlayer = Int(CMTimeGetSeconds(time)) if let currentPlayingIndex = self.vm.currentIndexPlayingSong, let cell = self.songTableView.cellForRow(at: IndexPath(row: currentPlayingIndex, section: 0)) as? SongListCell { cell.updateCurrentTime() } } } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 60 } } // MARK: - CollectionView DataSource Delegate extension MoreVC : CollectionViewSRC{ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return vm.blogData.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: K.CellIdentifier.Home.blogsCell, for: indexPath) as! BlogsCell cell.setData(data : vm.blogData[indexPath.row]) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let data = vm.blogData[indexPath.row] let sb = UIStoryboard(name: K.StoryBoard.theme, bundle: nil) let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Theme.blogDetailsVC) as! BlogDetailsVC vcPush.blogData = data vcPush.modalPresentationStyle = .overCurrentContext vcPush.modalTransitionStyle = .crossDissolve self.present(vcPush, animated: true) } } // MARK: - Collection Flow Layout extension MoreVC : UICollectionViewDelegateFlowLayout{ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 5 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return 5 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let itemsPerRow: CGFloat = 2.2 let paddingSpace = 5 * (itemsPerRow + 1) let availableWidth = collectionView.frame.width - paddingSpace let widthPerItem = availableWidth / itemsPerRow return CGSize(width: widthPerItem, height: widthPerItem) } } class VideoAVPlayer { var player: AVPlayer? var playerObserver: NSKeyValueObservation? var status = PlayerStatus.loading init(url: URL) { self.player = AVPlayer(url: url) observePlayer() } private func observePlayer() { playerObserver = player?.observe(\.timeControlStatus, options: [.new, .old], changeHandler: { [weak self] player, change in guard let self = self else { return } switch player.timeControlStatus { case .playing: print("Player is playing") self.status = .play case .paused: print("Player is paused") self.status = .pause case .waitingToPlayAtSpecifiedRate: print("Player is waiting to play at specified rate") self.status = .loading @unknown default: print("Unknown player status") } }) } deinit { playerObserver?.invalidate() } func play() { player?.play() } func pause() { player?.pause() } }