// // MoreVM.swift // WOKA // // Created by MacBook Pro on 10/06/24. // import UIKit import AVFoundation import GoogleMobileAds enum PlayerStatus{ case play case loading case pause case resume case stopped } var currentTimePlayer = Int() var totalTime = Int() class MoreVM{ weak var vc : MoreVC! var blogData = [BlogDM.Blog]() var songData = [SongBlogDM.PaintDatum]() var player : AVPlayer? var playerObserver: NSKeyValueObservation? var playerStatus = PlayerStatus.loading var currentIndexPlayingSong : Int? var headerBannerView = GADBannerView() func initView(){ getBLogs() setupAudioSession() getSong() setupCell() vc.songTableView.showsVerticalScrollIndicator = false vc.songTableView.showsHorizontalScrollIndicator = false vc.homeBtn.addTapGesture { [weak self] in guard let self else{return} DispatchQueue.main.async{ self.vc.dismiss(animated: true) { PersistentStorage.shared.addOthersCount() } } } /* First check if webSeries ad is present via slug, then check for Local Ads, if not then check google ads. */ if let adsData = AuthFunc.shareInstance.adsData, let moreAd = adsData.result?.filter({$0.slug == AdsEnum.more.rawValue}).first{ // check if ads data contains LocalAD for webseries if let advertisement = moreAd.advertisement,let bannerImage = advertisement.bannerImage{ vc.customAdImage.imageURL(bannerImage, color: .white) vc.customAdImage.alpha = 0 let height = UIScreen.main.bounds.width * 0.55 vc.customAdHeight.constant = height vc.trailerTask.isHidden = true vc.customAdImage.isHidden = false UIView.animate(withDuration: 0.2, animations: { [weak self] in guard let self else{return} vc.customAdImage.alpha = 1 }) vc.customAdImage.addTapGesture { if let adID = moreAd.id{ PersistentStorage.shared.addAdsCount(adID: adID ,clicks: 1) } if let adLink = moreAd.advertisement?.adLink ,let url = URL(string: adLink), UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) } } }else if moreAd.googleAd != nil{ /* Show google ads with dispatch queue. */ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: { [weak self] in guard let self else{return} AdReusable.sharedInstance.setupBannerAd(bannerView: headerBannerView, in: vc.trailerTask, adUnitID: K.GoogleAdIDs.splashBanner2, viewController: self.vc, height: 5, width: 15) }) } } } func setupAudioSession() { let session = AVAudioSession.sharedInstance() do { try session.setCategory(.playback, mode: .spokenAudio) try session.setActive(true) } catch { print("Failed to set up audio session") } } func observePlayer() { playerObserver = self.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 OOO") self.playerStatus = .play vc.songTableView.reloadRows(at: [IndexPath(row: currentIndexPlayingSong ?? 0, section: 0)], with: .none) case .paused: print("Player is paused OOO") // self.playerStatus = .pause if self.player?.currentTime() == self.player?.currentItem?.duration { print("Player finished playing") self.playerStatus = .stopped if let currentIndexPlayingSong{ self.vc.songTableView.reloadRows(at: [IndexPath(row: currentIndexPlayingSong, section: 0)], with: .none) self.currentIndexPlayingSong = nil }else{ self.vc.songTableView.reloadData() } } else { print("Player is paused") self.playerStatus = .pause } case .waitingToPlayAtSpecifiedRate: print("Player is waiting to play at specified rate OOO") self.playerStatus = .loading @unknown default: print("Unknown player status OOO") } }) } func setupCell(){ vc.blogsCollectionView.register(UINib(nibName: K.CellIdentifier.Home.blogsCell, bundle: nil), forCellWithReuseIdentifier: K.CellIdentifier.Home.blogsCell) vc.blogsCollectionView.delegate = vc.self vc.blogsCollectionView.dataSource = vc.self vc.songTableView.register(UINib(nibName: K.CellIdentifier.Home.songListCell, bundle: nil), forCellReuseIdentifier: K.CellIdentifier.Home.songListCell) vc.songTableView.delegate = vc.self vc.songTableView.dataSource = vc.self } // MARK: - Get BLogs Data func getBLogs(){ vc.blogActivityIndicator.startAnimating() NetworkManager.shareInstance.apiRequest(url: APIEndPoints.Home.blogs, method: .get) { [weak self](result : Result, NetworkManager.APIError>) in guard let self else{return} Utilities.dismissProgressHUD() switch result{ case .success(let data): switch data.success{ case 0: /* Error */ vc.blogActivityIndicator.stopAnimating() vc.blogRetryBtn.isHidden = false vc.toast(msg: data.message ?? "Unrecognised error" , time: 1.5) case 1: vc.blogActivityIndicator.stopAnimating() vc.blogRetryBtn.isHidden = true guard let data = data.data?.blogs else{return} blogData = data vc.blogsCollectionView.reloadData() default: vc.blogActivityIndicator.stopAnimating() vc.blogRetryBtn.isHidden = false } case .failure(let error): vc.blogActivityIndicator.stopAnimating() vc.blogRetryBtn.isHidden = false vc.toast(msg: error.localizedDescription , time: 1.5) } } } func getSong(){ vc.songActivityIndicator.startAnimating() NetworkManager.shareInstance.apiRequest(url: APIEndPoints.Home.song_listing, method: .post) { [weak self](result : Result, NetworkManager.APIError>) in guard let self else{return} Utilities.dismissProgressHUD() switch result{ case .success(let data): switch data.success{ case 0: /* Error */ vc.songActivityIndicator.stopAnimating() vc.songRetry.isHidden = false vc.toast(msg: data.message ?? "Unrecognised error" , time: 1.5) case 1: vc.songActivityIndicator.stopAnimating() vc.songRetry.isHidden = true guard let data = data.data?.paintData else{return} songData = data vc.songTableView.reloadData() break default: vc.songActivityIndicator.stopAnimating() vc.songRetry.isHidden = false } case .failure(let error): vc.songActivityIndicator.stopAnimating() vc.songRetry.isHidden = false vc.toast(msg: error.localizedDescription , time: 1.5) } } } } // MARK: - Handle Media Player for background extension MoreVM{ func handleInlinePlay(indexPath : Int){ // guard let self = self.vc else{return} PersistentStorage.shared.addOthersCount() /* this will declare if the song is playing or not */ if let playingIndex = currentIndexPlayingSong { if indexPath == playingIndex{ // if same row is selected pause the row print(playerStatus) switch playerStatus{ case .play: // if player is playing pause it. print("Player is playing") player?.pause() playerStatus = .pause vc.songTableView.reloadRows(at: [IndexPath(row: currentIndexPlayingSong!, section: 0)], with: .none) case .pause: player?.play() playerStatus = .resume vc.songTableView.reloadRows(at: [IndexPath(row: 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 playerStatus = .stopped vc.songTableView.reloadRows(at: [IndexPath(row: currentIndexPlayingSong!, section: 0)], with: .none) //Update new cell currentIndexPlayingSong = indexPath playerStatus = .loading currentTimePlayer = 0 vc.songTableView.reloadRows(at: [IndexPath(row: indexPath, section: 0)], with: .none) let data = songData[indexPath] vc.startPlaying(song: data, at: IndexPath(row: indexPath, section: 0)) print("Other cell playing") } }else{ // if there is no playing audio before playerStatus = .loading currentIndexPlayingSong = indexPath currentTimePlayer = 0 vc.songTableView.reloadRows(at: [IndexPath(row: indexPath, section: 0)], with: .none) let data = songData[indexPath] vc.startPlaying(song: data, at: IndexPath(row: indexPath, section: 0)) print("First Play") } } func stopMPNowPlayin(){ MPNowPlayingInfoCenter.default().nowPlayingInfo = [:] UIApplication.shared.endReceivingRemoteControlEvents() let commandCenter = MPRemoteCommandCenter.shared() // Disable play/pause commands commandCenter.playCommand.removeTarget(nil) commandCenter.pauseCommand.removeTarget(nil) } func setupNowPlayingInfo(songTitle : String) { let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default() var nowPlayingInfo: [String: Any] = [:] // Set the media title, artist, and album name nowPlayingInfo[MPMediaItemPropertyTitle] = songTitle nowPlayingInfo[MPMediaItemPropertyArtist] = "WOKA Songs" nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = "" nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = true if let originalImage = UIImage(named: "ExploreWoka") { let artworkImage = imageWithBackground(color: #colorLiteral(red: 0.4495816827, green: 0.2344398499, blue: 0.8120074868, alpha: 1), originalImage: originalImage) let artwork = MPMediaItemArtwork(boundsSize: originalImage.size) { size in return artworkImage ?? originalImage } nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork } // Set the duration and current playback position if let currentItem = player?.currentItem { nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentItem.asset.duration.seconds nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime().seconds nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate } // Assign the nowPlayingInfo to the center nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo // updateNowPlayingInfo() } func setupRemoteCommandCenter() { let commandCenter = MPRemoteCommandCenter.shared() // Enable play command commandCenter.playCommand.addTarget { [weak self] event in guard let self else{return .commandFailed} if player?.rate == 0.0 { player?.play() return .success } return .commandFailed } // Enable pause command commandCenter.pauseCommand.addTarget { [weak self] event in guard let self else{return .commandFailed} if player?.rate == 1.0 { // player?.pause() if let currentIndexPlayingSong{ handleInlinePlay(indexPath: currentIndexPlayingSong) } return .success } return .commandFailed } // Enable next and previous track commands if needed // commandCenter.nextTrackCommand.isEnabled = true // commandCenter.previousTrackCommand.isEnabled = true // // // if let currentIndexPlayingSong = currentIndexPlayingSong{ // if currentIndexPlayingSong == 0{ // commandCenter.previousTrackCommand.isEnabled = false // } // // if currentIndexPlayingSong == songData.count - 1{ // commandCenter.nextTrackCommand.isEnabled = false // } // } // // // // Enable next command // commandCenter.nextTrackCommand.addTarget { [unowned self] event in // commandCenter.previousTrackCommand.isEnabled = true // if let currentIndexPlayingSong = currentIndexPlayingSong{ // if currentIndexPlayingSong == songData.count - 1{ // //if the song played is the last index disable the next track // commandCenter.nextTrackCommand.isEnabled = false // return .commandFailed // } // // handleInlinePlay(indexPath: currentIndexPlayingSong + 1) // setupNowPlayingInfo(songTitle: songData[currentIndexPlayingSong + 1].title ?? "WOKA") // updateNowPlayingInfo() // return .success // } // return .commandFailed // } // // // Enable pause command // commandCenter.previousTrackCommand.addTarget { [unowned self] event in // commandCenter.nextTrackCommand.isEnabled = true // if let currentIndexPlayingSong = currentIndexPlayingSong{ // if currentIndexPlayingSong == 0{ // //if the song played is the last index disable the previous track // commandCenter.previousTrackCommand.isEnabled = false // return .commandFailed // } // // setupNowPlayingInfo(songTitle: songData[currentIndexPlayingSong - 1].title ?? "WOKA") // handleInlinePlay(indexPath: currentIndexPlayingSong - 1) // updateNowPlayingInfo() // return .success // } // return .commandFailed // } } func imageWithBackground(color: UIColor, originalImage: UIImage) -> UIImage? { let size = originalImage.size UIGraphicsBeginImageContextWithOptions(size, false, originalImage.scale) let rect = CGRect(origin: CGPoint.zero, size: size) // Fill the background with a color color.setFill() UIRectFill(rect) // Draw the original image on top originalImage.draw(in: rect) // Capture the new image with background let imageWithBackground = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageWithBackground } func updateNowPlayingInfo() { if let currentItem = player?.currentItem { var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:] nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime().seconds nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } } } import UIKit import MediaPlayer extension UIView { private struct AssociatedKeys { static var loadingView = "loadingView" } private var loadingView: LoadingView? { get { return objc_getAssociatedObject(self, AssociatedKeys.loadingView) as? LoadingView } set { objc_setAssociatedObject(self, AssociatedKeys.loadingView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } func showLoading() { if loadingView == nil { let newLoadingView = LoadingView(frame: self.bounds) newLoadingView.translatesAutoresizingMaskIntoConstraints = false self.addSubview(newLoadingView) NSLayoutConstraint.activate([ newLoadingView.leadingAnchor.constraint(equalTo: self.leadingAnchor), newLoadingView.trailingAnchor.constraint(equalTo: self.trailingAnchor), newLoadingView.topAnchor.constraint(equalTo: self.topAnchor), newLoadingView.bottomAnchor.constraint(equalTo: self.bottomAnchor) ]) loadingView = newLoadingView } loadingView?.startLoading() } func hideLoading() { loadingView?.stopLoading() } } class LoadingView: UIView { private var activityIndicator: UIActivityIndicatorView! override init(frame: CGRect) { super.init(frame: frame) setupView() } required init?(coder: NSCoder) { super.init(coder: coder) setupView() } private func setupView() { self.backgroundColor = .clear activityIndicator = UIActivityIndicatorView(style: .medium) activityIndicator.translatesAutoresizingMaskIntoConstraints = false activityIndicator.color = .white self.addSubview(activityIndicator) NSLayoutConstraint.activate([ activityIndicator.centerXAnchor.constraint(equalTo: self.centerXAnchor), activityIndicator.centerYAnchor.constraint(equalTo: self.centerYAnchor), activityIndicator.heightAnchor.constraint(equalToConstant: self.frame.height - 10), activityIndicator.widthAnchor.constraint(equalToConstant: self.frame.width - 10) ]) } func startLoading() { activityIndicator.startAnimating() self.isHidden = false } func stopLoading() { activityIndicator.stopAnimating() self.isHidden = true } }