Files
Woka_Native_iOS/WOKA/Karaoke/ViewModel/JWKaraokePlayerVM.swift
BilalKhanWDI bbddd1336f - Added google ad banner for webseries.
- Added google ad for karaoke
- Added google ad for karaoke record ui
- Added google ad for fm
- Made a delegate function to handle the ad received or failure
- Added ads to games webview bottom banner
2024-09-16 19:58:03 +05:30

303 lines
12 KiB
Swift

//
// JWKaraokePlayerVM.swift
// WOKA
//
// Created by Bilal on 09/08/2024.
//
import UIKit
import AVFAudio
import AVFoundation
import GoogleMobileAds
class JWKaraokePlayerVM{
weak var vc : JWKaraokePlayerVC!
var mixedAudioURL: URL?
var audioURLFromMP4 : URL?
var recordedAudioURL: URL?
var audioRecorder: AVAudioRecorder?
var videoTitle : String?
var videoUrl : String?
//for mapping the post
var postID : Int?
//Start and end time for trimming the audio
var startTime : TimeInterval?
var endTime : TimeInterval?
var startTimeStamp = Date()
var headerBannerView = GADBannerView()
func initView(){
AdReusable.sharedInstance.setupBannerAd(bannerView: self.headerBannerView, in: vc.adView, adUnitID: K.GoogleAdIDs.themeTwo, viewController: self.vc)
startTimeStamp = Date()
vc.downloadRecordingBtn.isEnabled = false
hideShowKaraoke(isLoading: true)
}
// MARK: - Document Directory Save & Fetch audio for recording
func saveToFilesApp() {
guard let mixedAudioURL = mixedAudioURL else { return }
DispatchQueue.main.async {
let documentPicker = UIDocumentPickerViewController(url: mixedAudioURL, in: .exportToService)
documentPicker.delegate = self.vc
self.vc.present(documentPicker, animated: true, completion: nil)
}
}
func setupKaraoke(){
guard let videoUrl else{return}
let avURL = URL(string: "https://content.jwplatform.com/videos/699dmCGz-UwFX5S0R.mp4")!
let asset = AVAsset(url: avURL)
hideShowKaraoke(isLoading: true)
let outputURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(videoTitle?.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)
DispatchQueue.main.async {
if error == nil && isDone{
self.hideShowKaraoke(isLoading: false)
self.audioURLFromMP4 = url
self.setupAudio()
}else{
print("errrrrr", error)
self.vc.karaokeLoading.stopAnimating()
self.vc.karaokeStack.isHidden = true
self.vc.startStopRecordingStack.isHidden = true
self.vc.retryKaraokeBtn.isHidden = 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.startStopRecordingStack.isHidden = true
}else{
vc.karaokeLoading.stopAnimating()
vc.karaokeLoading.hidesWhenStopped = true
vc.karaokeStack.isHidden = true
vc.startStopRecordingStack.isHidden = false
}
}
}
// MARK: - App LifeCycle HAndler
func updateKaraokeVideoView(){
if let postID {
let duration = DateFormatterLib.dateDifferenceINT(date1: startTimeStamp, date2: Date())
AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.karaokeVideo.rawValue, duration: duration, catID: 0) { isDone in
if isDone{
K.GVar.reloadContinueKaraoke = true
}
}
}
}
// MARK: - AudioRecording
func setupAudio() {
// FileManager.default.clearTmpDirectory()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playAndRecord, mode: .default,options: .defaultToSpeaker)
try audioSession.setActive(true)
// // URL of the downloaded M4A file
// guard let audioURL = Bundle.main.url(forResource: "Sample_audio", withExtension: "m4a") else {
// print("Audio file not found.")
// return
// }
// Initialize AVAudioPlayer with the downloaded M4A file
// player = try AVAudioPlayer(contentsOf: audioURLFromMP4!)
// player?.prepareToPlay()
// Define settings for the audio recorder
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let outputURL = documentsDirectory.appendingPathComponent("recordedAudio.m4a")
let settings: [String: Any] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
// Initialize AVAudioRecorder with the output URL and settings
audioRecorder = try AVAudioRecorder(url: outputURL, settings: settings)
audioRecorder?.prepareToRecord()
recordedAudioURL = outputURL // Store the recorded audio URL
} catch {
print("Error setting up audio: \(error.localizedDescription)")
}
}
func startRecording() {
startTime = vc.player.time.position.round(to: 1)
vc.interfaceBehavior = .hidden
guard let audioRecorder = audioRecorder else { return }
audioRecorder.record()
}
func stopRecording() {
endTime = vc.player.time.position.round(to: 1)
vc.interfaceBehavior = .normal
guard let audioRecorder = audioRecorder else { return }
audioRecorder.stop()
vc.player.pause()
// Mix the recorded audio with the downloaded M4A file
guard let startTime , let endTime else{return}
mixAudio(start: startTime, stop: endTime)
}
func playMixedAudio() {
guard let mixedAudioURL = mixedAudioURL else { return }
do {
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationURL = documentsDirectoryURL.appendingPathComponent("xyze.m4a")
// Check if file already exists
if FileManager.default.fileExists(atPath: destinationURL.path) {
// Delete the existing file
try FileManager.default.removeItem(at: destinationURL)
}
// Copy the new file
try FileManager.default.copyItem(at: mixedAudioURL, to: destinationURL)
// Play the audio
let playerKAraoke = AVPlayer(url: destinationURL)
// Adding a completion handler to check if the player starts playing
let playerObserver = playerKAraoke.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1), queue: .main) { time in
print("Playing audio at time: \(time.seconds)")
}
// Observing when playback finishes
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: playerKAraoke.currentItem, queue: .main) { [weak self] _ in
print("Audio finished playing")
guard let self else{return}
self.vc.playBtn.setTitle("Play", for: .normal)
self.vc.playBtn.setImage(UIImage(named: "PlayButtonSmall"), for: .normal)
// Disable Recording while playing, hide controls for jwplayer
self.vc.interfaceBehavior = .normal
self.vc.startRecordBtn.isEnabled = true
self.vc.downloadRecordingBtn.isEnabled = true
// Remove observer
playerKAraoke.removeTimeObserver(playerObserver)
}
playerKAraoke.volume = 1.0
playerKAraoke.play()
print("Audio is playing...")
// Uncomment this block if you need to configure the audio session and play using AVAudioPlayer
/*
// Configure the audio session
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
try audioSession.setActive(true)
let audioPlayer = try AVAudioPlayer(contentsOf: mixedAudioURL)
audioPlayer.volume = 1.0
audioPlayer.play()
*/
} catch {
print("Error: \(error.localizedDescription)")
}
}
func mixAudio(start : TimeInterval, stop : TimeInterval) {
let totalTime = stop - start
guard let recordedAudioURL = recordedAudioURL else { return }
guard let playerURL = audioURLFromMP4 else { return }
Utilities.startProgressHUD(msg: "Mixing Audio")
let composition = AVMutableComposition()
let compositionDuration = CMTime(seconds: totalTime, preferredTimescale: 1)
// Add the recorded audio from 0 to 10 seconds
let recordedAudioAsset = AVURLAsset(url: recordedAudioURL)
let recordedAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try recordedAudioTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: compositionDuration), of: recordedAudioAsset.tracks(withMediaType: .audio)[0], at: CMTime.zero)
} catch {
Utilities.dismissProgressHUD()
print("Error adding recorded audio track: \(error.localizedDescription)")
}
// Add the downloaded M4A file from 10 to 20 seconds
let playerAsset = AVURLAsset(url: playerURL)
let playerTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try playerTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime(seconds: start, preferredTimescale: 1), duration: compositionDuration), of: playerAsset.tracks(withMediaType: .audio)[0], at: CMTime.zero)
} catch {
Utilities.dismissProgressHUD()
print("Error adding player audio track: \(error.localizedDescription)")
}
// Example usage:
// Export the mixed audio
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileName = "\((self.videoTitle?.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: ".", with: "") ?? "Audio") + Date().timeIntervalSince1970.toString()).m4a"
let filePath = documentsDirectory.appendingPathComponent(fileName)
deleteFileIfExist(at: filePath)
mixedAudioURL = filePath
guard let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A) else { return }
exportSession.outputURL = mixedAudioURL
exportSession.outputFileType = .m4a
exportSession.exportAsynchronously {
if exportSession.status == .completed {
print("Mixing audio completed.")
Utilities.dismissProgressHUD()
DispatchQueue.main.async { [weak self] in
guard let self else{return}
vc.playBtn.isEnabled = true
vc.downloadRecordingBtn.isEnabled = true
}
} else if exportSession.status == .failed {
print("Mixing audio failed.", exportSession.error?.localizedDescription)
}
}
}
func deleteFileIfExist(at url: URL) {
let fileManager = FileManager.default
do {
// Check if the file exists
if fileManager.fileExists(atPath: url.path) {
// Attempt to delete the file
try fileManager.removeItem(at: url)
print("File deleted successfully.")
} else {
print("File does not exist at path: \(url.path)")
}
} catch {
print("Error deleting file: \(error.localizedDescription)")
}
}
}