- Tc - 69 fixed - Tc - 70 fixed - TC 71 fixed - Added local ads to fm and more section - Added local ads to mylist - Fixed a bug for sync * Fixed the crashing by temporary updating the wokastaging with raw data
516 lines
19 KiB
Swift
516 lines
19 KiB
Swift
//
|
|
// 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<BaseResponseModel<BlogDM>, 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<BaseResponseModel<SongBlogDM>, 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
|
|
}
|
|
}
|