From e97c7e56f46912b591ae26b946014099c8bf77a7 Mon Sep 17 00:00:00 2001 From: Bilal Date: Sat, 3 Aug 2024 16:59:40 +0530 Subject: [PATCH] - [x] Work on mylist data to be updated internally - [x] Share functionality --- WOKA.xcodeproj/project.pbxproj | 16 +- WOKA/Address/Address.storyboard | 3 +- .../Address/Controller/PaymentWebViewVC.swift | 12 +- WOKA/Audio Books/AudioBookDetailsVC.swift | 8 +- WOKA/Games/Controller/GamesDetailVC.swift | 8 +- WOKA/Home/Controller/ExploreWokaVC.swift | 2 +- WOKA/Home/Home.storyboard | 423 ++++++++++-------- WOKA/Karaoke/Controller/AVPlayerTesting.swift | 22 - WOKA/Karaoke/Controller/AVPlayerVC.swift | 265 ----------- .../Karaoke/Controller/KaraokeDetailsVC.swift | 8 +- .../Karaoke/Controller/TestingKaraokeVC.swift | 120 ----- WOKA/Karaoke/DumpFiles/AVPlayerTesting.swift | 22 + WOKA/Karaoke/DumpFiles/AVPlayerVC.swift | 265 +++++++++++ WOKA/Karaoke/DumpFiles/AVPlayerVM.swift | 247 ++++++++++ WOKA/Karaoke/DumpFiles/TestingKaraokeVC.swift | 120 +++++ WOKA/Karaoke/Karaoke.storyboard | 393 +--------------- WOKA/Karaoke/ViewModel/AVPlayerVM.swift | 247 ---------- WOKA/Shop/ViewModel/ProductDetailsVM.swift | 5 +- .../ViewModel/WebSeriesSeasonVM.swift | 12 +- 19 files changed, 938 insertions(+), 1260 deletions(-) delete mode 100644 WOKA/Karaoke/Controller/AVPlayerTesting.swift delete mode 100644 WOKA/Karaoke/Controller/AVPlayerVC.swift delete mode 100644 WOKA/Karaoke/Controller/TestingKaraokeVC.swift create mode 100644 WOKA/Karaoke/DumpFiles/AVPlayerTesting.swift create mode 100644 WOKA/Karaoke/DumpFiles/AVPlayerVC.swift create mode 100644 WOKA/Karaoke/DumpFiles/AVPlayerVM.swift create mode 100644 WOKA/Karaoke/DumpFiles/TestingKaraokeVC.swift delete mode 100644 WOKA/Karaoke/ViewModel/AVPlayerVM.swift diff --git a/WOKA.xcodeproj/project.pbxproj b/WOKA.xcodeproj/project.pbxproj index d91a26c..2cdf2a7 100644 --- a/WOKA.xcodeproj/project.pbxproj +++ b/WOKA.xcodeproj/project.pbxproj @@ -1725,6 +1725,7 @@ 9CB3D0832C37BA470062869D /* Karaoke */ = { isa = PBXGroup; children = ( + 9CFF27A62C5DF6BE0018C360 /* DumpFiles */, 9CB3D0892C37BA650062869D /* Model */, 9CB3D0882C37BA630062869D /* View */, 9CB3D0872C37BA610062869D /* ViewModel */, @@ -1739,11 +1740,8 @@ children = ( 9CB3D08A2C37BBA50062869D /* KaraokeListingVC.swift */, 9CB3D0902C37D6930062869D /* KaraokeDetailsVC.swift */, - 9C21F81D2C37E3CA0050BFCC /* AVPlayerVC.swift */, 52F4E8652C3D123B00778FBC /* JWKaraokePlayerVC.swift */, 525FC61C2C3D3DC30049145D /* AVAssetMods.swift */, - 525FC65C2C3D57D80049145D /* TestingKaraokeVC.swift */, - 9C8446862C40FC6E003E3E53 /* AVPlayerTesting.swift */, ); path = Controller; sourceTree = ""; @@ -1752,7 +1750,6 @@ isa = PBXGroup; children = ( 9CB3D08C2C37CDD50062869D /* KaraokeListingVM.swift */, - 9C21F8212C382A580050BFCC /* AVPlayerVM.swift */, ); path = ViewModel; sourceTree = ""; @@ -1844,6 +1841,17 @@ path = Sounds; sourceTree = ""; }; + 9CFF27A62C5DF6BE0018C360 /* DumpFiles */ = { + isa = PBXGroup; + children = ( + 9C8446862C40FC6E003E3E53 /* AVPlayerTesting.swift */, + 525FC65C2C3D57D80049145D /* TestingKaraokeVC.swift */, + 9C21F81D2C37E3CA0050BFCC /* AVPlayerVC.swift */, + 9C21F8212C382A580050BFCC /* AVPlayerVM.swift */, + ); + path = DumpFiles; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ diff --git a/WOKA/Address/Address.storyboard b/WOKA/Address/Address.storyboard index 2e04ae8..591d7f0 100644 --- a/WOKA/Address/Address.storyboard +++ b/WOKA/Address/Address.storyboard @@ -581,7 +581,8 @@ - + + diff --git a/WOKA/Address/Controller/PaymentWebViewVC.swift b/WOKA/Address/Controller/PaymentWebViewVC.swift index b9eee13..3f06d7f 100644 --- a/WOKA/Address/Controller/PaymentWebViewVC.swift +++ b/WOKA/Address/Controller/PaymentWebViewVC.swift @@ -8,24 +8,32 @@ import UIKit import WebKit -class PaymentWebViewVC: UIViewController { +class PaymentWebViewVC: UIViewController,WKNavigationDelegate { @IBOutlet weak var webView: WKWebView! var url : String? override func viewDidLoad() { super.viewDidLoad() + DispatchQueue.main.async { + Utilities.startProgressHUD() + } + if let url { let link = URL(string:url)! let request = URLRequest(url: link) webView.load(request) } - + webView.navigationDelegate = self //Observer to check the url changes webView.addObserver(self, forKeyPath: "URL", options: .new, context: nil) } + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + Utilities.dismissProgressHUD() + } + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if let key = change?[NSKeyValueChangeKey.newKey] { print("observeValue \(key)") // url value diff --git a/WOKA/Audio Books/AudioBookDetailsVC.swift b/WOKA/Audio Books/AudioBookDetailsVC.swift index e1978e0..19b4639 100644 --- a/WOKA/Audio Books/AudioBookDetailsVC.swift +++ b/WOKA/Audio Books/AudioBookDetailsVC.swift @@ -197,7 +197,13 @@ class AudioBookDetailsVC : UIViewController{ } shareView.addTapGesture { - print("share") + if let name = URL(string: "https://apps.apple.com/in/app/woka/id6465305185"), !name.absoluteString.isEmpty { + let objectsToShare = [name] + let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) + self.present(activityVC, animated: true, completion: nil) + } else { + // show alert for not available + } } } diff --git a/WOKA/Games/Controller/GamesDetailVC.swift b/WOKA/Games/Controller/GamesDetailVC.swift index 117b7e0..bb8c439 100644 --- a/WOKA/Games/Controller/GamesDetailVC.swift +++ b/WOKA/Games/Controller/GamesDetailVC.swift @@ -173,7 +173,13 @@ class GamesDetailVC: UIViewController { } shareView.addTapGesture { - print("share") + if let name = URL(string: "https://apps.apple.com/in/app/woka/id6465305185"), !name.absoluteString.isEmpty { + let objectsToShare = [name] + let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) + self.present(activityVC, animated: true, completion: nil) + } else { + // show alert for not available + } } } diff --git a/WOKA/Home/Controller/ExploreWokaVC.swift b/WOKA/Home/Controller/ExploreWokaVC.swift index 8e0963e..b992a7c 100644 --- a/WOKA/Home/Controller/ExploreWokaVC.swift +++ b/WOKA/Home/Controller/ExploreWokaVC.swift @@ -23,6 +23,7 @@ class ExploreWokaVC: UIViewController { @IBOutlet weak var shopBtn: UIButton! @IBOutlet weak var layerView: UIView! + @IBOutlet weak var bottomView: UIView! override func viewDidLoad() { super.viewDidLoad() @@ -198,5 +199,4 @@ extension UIView{ shapeLayer.path = path.cgPath } - } diff --git a/WOKA/Home/Home.storyboard b/WOKA/Home/Home.storyboard index 0811e60..5b8a922 100644 --- a/WOKA/Home/Home.storyboard +++ b/WOKA/Home/Home.storyboard @@ -571,10 +571,10 @@ - + - + @@ -587,209 +587,236 @@ - + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + @@ -847,6 +874,7 @@ + @@ -1355,6 +1383,7 @@ + diff --git a/WOKA/Karaoke/Controller/AVPlayerTesting.swift b/WOKA/Karaoke/Controller/AVPlayerTesting.swift deleted file mode 100644 index 13d8aa3..0000000 --- a/WOKA/Karaoke/Controller/AVPlayerTesting.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// AVPlayerTesting.swift -// WOKA -// -// Created by Bilal on 12/07/2024. -// - -import UIKit -import AVKit - -class AVPlayerTesting: AVPlayerViewController { - - override func viewDidLoad() { - super.viewDidLoad() - let videoURL = URL(string: "https://content.jwplatform.com/videos/699dmCGz-Ysj2G4DQ.mp4")! - let player = AVPlayer(url: videoURL) - self.player = player - player.play() - // Do any additional setup after loading the view. - } - -} diff --git a/WOKA/Karaoke/Controller/AVPlayerVC.swift b/WOKA/Karaoke/Controller/AVPlayerVC.swift deleted file mode 100644 index f9db629..0000000 --- a/WOKA/Karaoke/Controller/AVPlayerVC.swift +++ /dev/null @@ -1,265 +0,0 @@ -// -// 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") -// } -// } -// } -} diff --git a/WOKA/Karaoke/Controller/KaraokeDetailsVC.swift b/WOKA/Karaoke/Controller/KaraokeDetailsVC.swift index 28c9913..162d380 100644 --- a/WOKA/Karaoke/Controller/KaraokeDetailsVC.swift +++ b/WOKA/Karaoke/Controller/KaraokeDetailsVC.swift @@ -197,7 +197,13 @@ class KaraokeDetailsVC: UIViewController { } shareView.addTapGesture { - print("share") + if let name = URL(string: "https://apps.apple.com/in/app/woka/id6465305185"), !name.absoluteString.isEmpty { + let objectsToShare = [name] + let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) + self.present(activityVC, animated: true, completion: nil) + } else { + // show alert for not available + } } } diff --git a/WOKA/Karaoke/Controller/TestingKaraokeVC.swift b/WOKA/Karaoke/Controller/TestingKaraokeVC.swift deleted file mode 100644 index a6627d7..0000000 --- a/WOKA/Karaoke/Controller/TestingKaraokeVC.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// TestingKaraokeVC.swift -// WOKA -// -// Created by MacBook Pro on 09/07/24. -// - -import AVFAudio -import UIKit -import AVFoundation - -class TestingKaraokeVC : UIViewController{ - - var player: AVPlayer! - var audioEngine: AVAudioEngine! - var micInput: AVAudioInputNode! - var playerNode: AVAudioPlayerNode! - var audioFile: AVAudioFile! - var fileURL: URL! - - override func viewDidLoad() { - super.viewDidLoad() - - let videoURL = URL(string: "https://content.jwplatform.com/videos/DOhtETio-Ysj2G4DQ.mp4")! - setupPlayer(with: videoURL) - setupAudioEngine() - setupAudioFile() - } - - func setupPlayer(with url: URL) { - player = AVPlayer(url: url) - let playerLayer = AVPlayerLayer(player: player) - playerLayer.frame = view.bounds - view.layer.addSublayer(playerLayer) - player.play() - } - - func setupAudioEngine() { - audioEngine = AVAudioEngine() - micInput = audioEngine.inputNode - playerNode = AVAudioPlayerNode() - - audioEngine.attach(playerNode) - let format = micInput.inputFormat(forBus: 0) - - audioEngine.connect(micInput, to: audioEngine.mainMixerNode, format: format) - audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: format) - - try! audioEngine.start() - } - - func setupAudioFile() { - let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - fileURL = documentsDirectory.appendingPathComponent("output.m4a") - let format = audioEngine.mainMixerNode.outputFormat(forBus: 0) - - do { - audioFile = try AVAudioFile(forWriting: fileURL, settings: format.settings) - } catch { - print("Error creating audio file: \(error)") - } - } - - func startRecording() { - let mixer = audioEngine.mainMixerNode - let format = mixer.outputFormat(forBus: 0) - - mixer.installTap(onBus: 0, bufferSize: 1024, format: format) { (buffer, time) in - do { - try self.audioFile.write(from: buffer) - } catch { - print("Error writing audio buffer: \(error)") - } - } - } - - func stopRecording() { - let mixer = audioEngine.mainMixerNode - mixer.removeTap(onBus: 0) - - // Save the file to Files app - presentDocumentPicker() - } - - func presentDocumentPicker() { - // let documentPicker = UIDocumentPickerViewController(forExporting: [fileURL]) - // documentPicker.delegate = self - // present(documentPicker, animated: true, completion: nil) - - guard let mixedAudioURL = fileURL else { return } - DispatchQueue.main.async { - let documentPicker = UIDocumentPickerViewController(url: mixedAudioURL, in: .exportToService) - documentPicker.delegate = self - self.present(documentPicker, animated: true, completion: nil) - } - } - - @IBAction func startButtonPressed(_ sender: UIButton) { - startRecording() - } - - @IBAction func stopButtonPressed(_ sender: UIButton) { - stopRecording() - } -} - -extension TestingKaraokeVC: UIDocumentPickerDelegate { - func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { - guard let selectedURL = urls.first else { return } - do { - try FileManager.default.moveItem(at: fileURL, to: selectedURL) - } catch { - print("Error saving file to Files app: \(error)") - } - } - - func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { - print("Document picker was cancelled") - } -} diff --git a/WOKA/Karaoke/DumpFiles/AVPlayerTesting.swift b/WOKA/Karaoke/DumpFiles/AVPlayerTesting.swift new file mode 100644 index 0000000..6c7901a --- /dev/null +++ b/WOKA/Karaoke/DumpFiles/AVPlayerTesting.swift @@ -0,0 +1,22 @@ +//// +//// AVPlayerTesting.swift +//// WOKA +//// +//// Created by Bilal on 12/07/2024. +//// +// +//import UIKit +//import AVKit +// +//class AVPlayerTesting: AVPlayerViewController { +// +// override func viewDidLoad() { +// super.viewDidLoad() +// let videoURL = URL(string: "https://content.jwplatform.com/videos/699dmCGz-Ysj2G4DQ.mp4")! +// let player = AVPlayer(url: videoURL) +// self.player = player +// player.play() +// // Do any additional setup after loading the view. +// } +// +//} diff --git a/WOKA/Karaoke/DumpFiles/AVPlayerVC.swift b/WOKA/Karaoke/DumpFiles/AVPlayerVC.swift new file mode 100644 index 0000000..7c36c04 --- /dev/null +++ b/WOKA/Karaoke/DumpFiles/AVPlayerVC.swift @@ -0,0 +1,265 @@ +//// +//// 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") +//// } +//// } +//// } +//} diff --git a/WOKA/Karaoke/DumpFiles/AVPlayerVM.swift b/WOKA/Karaoke/DumpFiles/AVPlayerVM.swift new file mode 100644 index 0000000..f8102da --- /dev/null +++ b/WOKA/Karaoke/DumpFiles/AVPlayerVM.swift @@ -0,0 +1,247 @@ +//// +//// AVPlayerVM.swift +//// WOKA +//// +//// Created by Bilal on 05/07/2024. +//// +// +//import UIKit +//import AVKit +// +//class AVPlayerVM{ +// +// weak var vc : AVPlayerVC! +// +// var player : AVPlayer? = nil +// var isThumbSeek : Bool = false +// var timer : Timer? +// var isFinished = false{ +// didSet{ +// if isFinished{ +// timer?.invalidate() +// timer = nil +// vc.reloadBtn.isHidden = false +// vc.stackCtrView.isHidden = true +// vc.tintView.isHidden = false +// vc.sliderStack.isHidden = true +// }else{ +// vc.reloadBtn.isHidden = true +// vc.stackCtrView.isHidden = false +// vc.sliderStack.isHidden = false +// } +// } +// } +// +// private var playerLayer : AVPlayerLayer? = nil +// private var timeObserver : Any? = nil +// +// func initView(){ +// vc.seekSlider.transform = CGAffineTransform(scaleX: 0.85, y: 0.85) +// +// self.vc.videoTitle.text = vc.titleVideo +// setupKaraoke() +// +// } +// +// @objc func videoDidFinish() { +// self.isFinished = true +// +// } +// +// func setupKaraoke(){ +// guard let url = vc.videoURL else{return} +// hideShowKaraoke(isLoading: true) +// let avURL = URL(string: url)! +// let asset = AVAsset(url: avURL) +// +// let outputURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(vc.titleVideo?.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) +// if error == nil{ +// hideShowKaraoke(isLoading: 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.playPauseRecordStack.isHidden = true +// }else{ +// vc.karaokeLoading.stopAnimating() +// vc.karaokeLoading.hidesWhenStopped = true +// vc.karaokeStack.isHidden = true +// vc.playPauseRecordStack.isHidden = false +// } +// } +// } +// +// // MARK: - Setup Video Player +// +// func setVideoPlayer() { +// guard let videoURL = vc.videoURL, let url = URL(string: videoURL) else { return } +// +// if self.player == nil { +// +// // Create a subview for the video player +// let playerView = UIView(frame: vc.viewControll.bounds) +// playerView.backgroundColor = .clear // Make sure the background is clear +// vc.viewControll.addSubview(playerView) +// vc.viewControll.sendSubviewToBack(playerView) // Ensure the player view is below other UI elements +// +// // Initialize the player +// let playerItem = AVPlayerItem(url: url) +// player = AVPlayer(playerItem: playerItem) +// +// // Add observer for AVPlayerItemFailedToPlayToEndTimeNotification +// NotificationCenter.default.addObserver(self, +// selector: #selector(playerItemFailedToPlayToEndTime(_:)), +// name: .AVPlayerItemFailedToPlayToEndTime, +// object: playerItem) +// +// // Add observer for AVPlayerItem's status +// playerItem.addObserver(self.vc, +// forKeyPath: "status", +// options: [.new, .initial], +// context: nil) +// +// // Set up the player layer +// playerLayer = AVPlayerLayer(player: player) +// playerLayer?.videoGravity = .resizeAspectFill +// playerLayer?.frame = playerView.bounds +// +// if let playerLayer = playerLayer { +// playerView.layer.addSublayer(playerLayer) +// } +// +// // Add observer for play/pause +// player?.addObserver(self.vc, forKeyPath: "rate", options: [.new, .initial], context: nil) +// +// // Add observer for buffering +// playerItem.addObserver(self.vc, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .initial], context: nil) +// playerItem.addObserver(self.vc, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .initial], context: nil) +// +// // Add observer for video finished playing +// NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinish), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) +// self.setObserverToPlayer() +// startTimer() +// player?.play() +// } +// } +// +// func reloadVideo() { +// guard let videoURL = vc.videoURL, let url = URL(string: videoURL) else { return } +// +// // Remove existing observers +// NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem) +// player?.currentItem?.removeObserver(vc, forKeyPath: "status") +// player?.currentItem?.removeObserver(vc, forKeyPath: "isPlaybackBufferEmpty") +// player?.currentItem?.removeObserver(vc, forKeyPath: "isPlaybackLikelyToKeepUp") +// +// // Create a new player item +// let playerItem = AVPlayerItem(url: url) +// player?.replaceCurrentItem(with: playerItem) +// +// // Add observers again +// NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinish), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) +// playerItem.addObserver(vc, forKeyPath: "status", options: [.new, .initial], context: nil) +// playerItem.addObserver(vc, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .initial], context: nil) +// playerItem.addObserver(vc, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .initial], context: nil) +// +// player?.play() +// } +// +// func setObserverToPlayer() { +// let interval = CMTime(seconds: 0.3, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) +// timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { elapsed in +// self.updatePlayerTime() +// }) +// } +// +// +// // Handle notification +// @objc func playerItemFailedToPlayToEndTime(_ notification: Notification) { +// if let error = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? Error { +// print("Error: \(error.localizedDescription)") +// handlePlayerError(error) +// } +// } +// +// func handlePlayerError(_ error: Error) { +// // Update the UI or show an alert to the user +// Utilities.alertWithBtnCompletion(title: "Error", msgBody: error.localizedDescription, okBtnStr: "Retry?", vc: vc.self) { [weak self] isDone in +// guard let self else{return} +// player?.play() +// } +// } +// +// // MARK: - ShowHideControls +// func startTimer(){ +// timer = Timer.scheduledTimer(withTimeInterval: 4.5, repeats: false) { _ in +// self.showHideControls() +// } +// } +// +// func showHideControls(){ +// if isFinished{ +// vc.stackCtrView.isHidden = true +// vc.reloadBtn.isHidden.toggle() +// vc.sliderStack.isHidden = true +// }else{ +// vc.reloadBtn.isHidden = true +// vc.stackCtrView.isHidden.toggle() +// vc.sliderStack.isHidden.toggle() +// } +// +// vc.tintView.isHidden.toggle() +// +// if !vc.stackCtrView.isHidden{ +// startTimer() +// } +// } +// +// // MARK: - Update the player time Label with Slider +// +// private func updatePlayerTime() { +// guard let currentTime = self.player?.currentTime() else { return } +// guard let duration = self.player?.currentItem?.duration else { return } +// +// let currentTimeInSecond = CMTimeGetSeconds(currentTime) +// let durationTimeInSecond = CMTimeGetSeconds(duration) +// +// if self.isThumbSeek == false { +// self.vc.seekSlider.value = Float(currentTimeInSecond/durationTimeInSecond) +// } +// +// let value = Float64(self.vc.seekSlider.value) * CMTimeGetSeconds(duration) +// +// var hours = value / 3600 +// var mins = (value / 60).truncatingRemainder(dividingBy: 60) +// var secs = value.truncatingRemainder(dividingBy: 60) +// var timeformatter = NumberFormatter() +// timeformatter.minimumIntegerDigits = 2 +// timeformatter.minimumFractionDigits = 0 +// timeformatter.roundingMode = .down +// guard let hoursStr = timeformatter.string(from: NSNumber(value: hours)), let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else { +// return +// } +// self.vc.lbCurrentTime.text = "\(hoursStr):\(minsStr):\(secsStr)" +// +// hours = durationTimeInSecond / 3600 +// mins = (durationTimeInSecond / 60).truncatingRemainder(dividingBy: 60) +// secs = durationTimeInSecond.truncatingRemainder(dividingBy: 60) +// timeformatter = NumberFormatter() +// timeformatter.minimumIntegerDigits = 2 +// timeformatter.minimumFractionDigits = 0 +// timeformatter.roundingMode = .down +// guard let hoursStr = timeformatter.string(from: NSNumber(value: hours)), let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else { +// return +// } +// self.vc.lbTotalTime.text = "\(hoursStr):\(minsStr):\(secsStr)" +// } +//} diff --git a/WOKA/Karaoke/DumpFiles/TestingKaraokeVC.swift b/WOKA/Karaoke/DumpFiles/TestingKaraokeVC.swift new file mode 100644 index 0000000..f00689d --- /dev/null +++ b/WOKA/Karaoke/DumpFiles/TestingKaraokeVC.swift @@ -0,0 +1,120 @@ +//// +//// TestingKaraokeVC.swift +//// WOKA +//// +//// Created by MacBook Pro on 09/07/24. +//// +// +//import AVFAudio +//import UIKit +//import AVFoundation +// +//class TestingKaraokeVC : UIViewController{ +// +// var player: AVPlayer! +// var audioEngine: AVAudioEngine! +// var micInput: AVAudioInputNode! +// var playerNode: AVAudioPlayerNode! +// var audioFile: AVAudioFile! +// var fileURL: URL! +// +// override func viewDidLoad() { +// super.viewDidLoad() +// +// let videoURL = URL(string: "https://content.jwplatform.com/videos/DOhtETio-Ysj2G4DQ.mp4")! +// setupPlayer(with: videoURL) +// setupAudioEngine() +// setupAudioFile() +// } +// +// func setupPlayer(with url: URL) { +// player = AVPlayer(url: url) +// let playerLayer = AVPlayerLayer(player: player) +// playerLayer.frame = view.bounds +// view.layer.addSublayer(playerLayer) +// player.play() +// } +// +// func setupAudioEngine() { +// audioEngine = AVAudioEngine() +// micInput = audioEngine.inputNode +// playerNode = AVAudioPlayerNode() +// +// audioEngine.attach(playerNode) +// let format = micInput.inputFormat(forBus: 0) +// +// audioEngine.connect(micInput, to: audioEngine.mainMixerNode, format: format) +// audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: format) +// +// try! audioEngine.start() +// } +// +// func setupAudioFile() { +// let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] +// fileURL = documentsDirectory.appendingPathComponent("output.m4a") +// let format = audioEngine.mainMixerNode.outputFormat(forBus: 0) +// +// do { +// audioFile = try AVAudioFile(forWriting: fileURL, settings: format.settings) +// } catch { +// print("Error creating audio file: \(error)") +// } +// } +// +// func startRecording() { +// let mixer = audioEngine.mainMixerNode +// let format = mixer.outputFormat(forBus: 0) +// +// mixer.installTap(onBus: 0, bufferSize: 1024, format: format) { (buffer, time) in +// do { +// try self.audioFile.write(from: buffer) +// } catch { +// print("Error writing audio buffer: \(error)") +// } +// } +// } +// +// func stopRecording() { +// let mixer = audioEngine.mainMixerNode +// mixer.removeTap(onBus: 0) +// +// // Save the file to Files app +// presentDocumentPicker() +// } +// +// func presentDocumentPicker() { +// // let documentPicker = UIDocumentPickerViewController(forExporting: [fileURL]) +// // documentPicker.delegate = self +// // present(documentPicker, animated: true, completion: nil) +// +// guard let mixedAudioURL = fileURL else { return } +// DispatchQueue.main.async { +// let documentPicker = UIDocumentPickerViewController(url: mixedAudioURL, in: .exportToService) +// documentPicker.delegate = self +// self.present(documentPicker, animated: true, completion: nil) +// } +// } +// +// @IBAction func startButtonPressed(_ sender: UIButton) { +// startRecording() +// } +// +// @IBAction func stopButtonPressed(_ sender: UIButton) { +// stopRecording() +// } +//} +// +//extension TestingKaraokeVC: UIDocumentPickerDelegate { +// func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { +// guard let selectedURL = urls.first else { return } +// do { +// try FileManager.default.moveItem(at: fileURL, to: selectedURL) +// } catch { +// print("Error saving file to Files app: \(error)") +// } +// } +// +// func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { +// print("Document picker was cancelled") +// } +//} diff --git a/WOKA/Karaoke/Karaoke.storyboard b/WOKA/Karaoke/Karaoke.storyboard index e70e784..cdf8e14 100644 --- a/WOKA/Karaoke/Karaoke.storyboard +++ b/WOKA/Karaoke/Karaoke.storyboard @@ -13,9 +13,6 @@ Exo2-Bold - - Exo2-Medium - Exo2-Regular @@ -582,318 +579,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -959,7 +644,6 @@ - @@ -1032,74 +715,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1109,14 +725,10 @@ - - - - @@ -1129,8 +741,5 @@ - - - diff --git a/WOKA/Karaoke/ViewModel/AVPlayerVM.swift b/WOKA/Karaoke/ViewModel/AVPlayerVM.swift deleted file mode 100644 index 4136bba..0000000 --- a/WOKA/Karaoke/ViewModel/AVPlayerVM.swift +++ /dev/null @@ -1,247 +0,0 @@ -// -// AVPlayerVM.swift -// WOKA -// -// Created by Bilal on 05/07/2024. -// - -import UIKit -import AVKit - -class AVPlayerVM{ - - weak var vc : AVPlayerVC! - - var player : AVPlayer? = nil - var isThumbSeek : Bool = false - var timer : Timer? - var isFinished = false{ - didSet{ - if isFinished{ - timer?.invalidate() - timer = nil - vc.reloadBtn.isHidden = false - vc.stackCtrView.isHidden = true - vc.tintView.isHidden = false - vc.sliderStack.isHidden = true - }else{ - vc.reloadBtn.isHidden = true - vc.stackCtrView.isHidden = false - vc.sliderStack.isHidden = false - } - } - } - - private var playerLayer : AVPlayerLayer? = nil - private var timeObserver : Any? = nil - - func initView(){ - vc.seekSlider.transform = CGAffineTransform(scaleX: 0.85, y: 0.85) - - self.vc.videoTitle.text = vc.titleVideo - setupKaraoke() - - } - - @objc func videoDidFinish() { - self.isFinished = true - - } - - func setupKaraoke(){ - guard let url = vc.videoURL else{return} - hideShowKaraoke(isLoading: true) - let avURL = URL(string: url)! - let asset = AVAsset(url: avURL) - - let outputURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(vc.titleVideo?.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) - if error == nil{ - hideShowKaraoke(isLoading: 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.playPauseRecordStack.isHidden = true - }else{ - vc.karaokeLoading.stopAnimating() - vc.karaokeLoading.hidesWhenStopped = true - vc.karaokeStack.isHidden = true - vc.playPauseRecordStack.isHidden = false - } - } - } - - // MARK: - Setup Video Player - - func setVideoPlayer() { - guard let videoURL = vc.videoURL, let url = URL(string: videoURL) else { return } - - if self.player == nil { - - // Create a subview for the video player - let playerView = UIView(frame: vc.viewControll.bounds) - playerView.backgroundColor = .clear // Make sure the background is clear - vc.viewControll.addSubview(playerView) - vc.viewControll.sendSubviewToBack(playerView) // Ensure the player view is below other UI elements - - // Initialize the player - let playerItem = AVPlayerItem(url: url) - player = AVPlayer(playerItem: playerItem) - - // Add observer for AVPlayerItemFailedToPlayToEndTimeNotification - NotificationCenter.default.addObserver(self, - selector: #selector(playerItemFailedToPlayToEndTime(_:)), - name: .AVPlayerItemFailedToPlayToEndTime, - object: playerItem) - - // Add observer for AVPlayerItem's status - playerItem.addObserver(self.vc, - forKeyPath: "status", - options: [.new, .initial], - context: nil) - - // Set up the player layer - playerLayer = AVPlayerLayer(player: player) - playerLayer?.videoGravity = .resizeAspectFill - playerLayer?.frame = playerView.bounds - - if let playerLayer = playerLayer { - playerView.layer.addSublayer(playerLayer) - } - - // Add observer for play/pause - player?.addObserver(self.vc, forKeyPath: "rate", options: [.new, .initial], context: nil) - - // Add observer for buffering - playerItem.addObserver(self.vc, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .initial], context: nil) - playerItem.addObserver(self.vc, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .initial], context: nil) - - // Add observer for video finished playing - NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinish), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) - self.setObserverToPlayer() - startTimer() - player?.play() - } - } - - func reloadVideo() { - guard let videoURL = vc.videoURL, let url = URL(string: videoURL) else { return } - - // Remove existing observers - NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem) - player?.currentItem?.removeObserver(vc, forKeyPath: "status") - player?.currentItem?.removeObserver(vc, forKeyPath: "isPlaybackBufferEmpty") - player?.currentItem?.removeObserver(vc, forKeyPath: "isPlaybackLikelyToKeepUp") - - // Create a new player item - let playerItem = AVPlayerItem(url: url) - player?.replaceCurrentItem(with: playerItem) - - // Add observers again - NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinish), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) - playerItem.addObserver(vc, forKeyPath: "status", options: [.new, .initial], context: nil) - playerItem.addObserver(vc, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .initial], context: nil) - playerItem.addObserver(vc, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .initial], context: nil) - - player?.play() - } - - func setObserverToPlayer() { - let interval = CMTime(seconds: 0.3, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) - timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { elapsed in - self.updatePlayerTime() - }) - } - - - // Handle notification - @objc func playerItemFailedToPlayToEndTime(_ notification: Notification) { - if let error = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? Error { - print("Error: \(error.localizedDescription)") - handlePlayerError(error) - } - } - - func handlePlayerError(_ error: Error) { - // Update the UI or show an alert to the user - Utilities.alertWithBtnCompletion(title: "Error", msgBody: error.localizedDescription, okBtnStr: "Retry?", vc: vc.self) { [weak self] isDone in - guard let self else{return} - player?.play() - } - } - - // MARK: - ShowHideControls - func startTimer(){ - timer = Timer.scheduledTimer(withTimeInterval: 4.5, repeats: false) { _ in - self.showHideControls() - } - } - - func showHideControls(){ - if isFinished{ - vc.stackCtrView.isHidden = true - vc.reloadBtn.isHidden.toggle() - vc.sliderStack.isHidden = true - }else{ - vc.reloadBtn.isHidden = true - vc.stackCtrView.isHidden.toggle() - vc.sliderStack.isHidden.toggle() - } - - vc.tintView.isHidden.toggle() - - if !vc.stackCtrView.isHidden{ - startTimer() - } - } - - // MARK: - Update the player time Label with Slider - - private func updatePlayerTime() { - guard let currentTime = self.player?.currentTime() else { return } - guard let duration = self.player?.currentItem?.duration else { return } - - let currentTimeInSecond = CMTimeGetSeconds(currentTime) - let durationTimeInSecond = CMTimeGetSeconds(duration) - - if self.isThumbSeek == false { - self.vc.seekSlider.value = Float(currentTimeInSecond/durationTimeInSecond) - } - - let value = Float64(self.vc.seekSlider.value) * CMTimeGetSeconds(duration) - - var hours = value / 3600 - var mins = (value / 60).truncatingRemainder(dividingBy: 60) - var secs = value.truncatingRemainder(dividingBy: 60) - var timeformatter = NumberFormatter() - timeformatter.minimumIntegerDigits = 2 - timeformatter.minimumFractionDigits = 0 - timeformatter.roundingMode = .down - guard let hoursStr = timeformatter.string(from: NSNumber(value: hours)), let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else { - return - } - self.vc.lbCurrentTime.text = "\(hoursStr):\(minsStr):\(secsStr)" - - hours = durationTimeInSecond / 3600 - mins = (durationTimeInSecond / 60).truncatingRemainder(dividingBy: 60) - secs = durationTimeInSecond.truncatingRemainder(dividingBy: 60) - timeformatter = NumberFormatter() - timeformatter.minimumIntegerDigits = 2 - timeformatter.minimumFractionDigits = 0 - timeformatter.roundingMode = .down - guard let hoursStr = timeformatter.string(from: NSNumber(value: hours)), let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else { - return - } - self.vc.lbTotalTime.text = "\(hoursStr):\(minsStr):\(secsStr)" - } -} diff --git a/WOKA/Shop/ViewModel/ProductDetailsVM.swift b/WOKA/Shop/ViewModel/ProductDetailsVM.swift index 1cf9fd4..e737b39 100644 --- a/WOKA/Shop/ViewModel/ProductDetailsVM.swift +++ b/WOKA/Shop/ViewModel/ProductDetailsVM.swift @@ -107,10 +107,10 @@ class ProductDetailsVM{ Utilities.alert(title: "Error", message: data.message ?? K.ConstantString.unRecognised, viewController: self.vc) case 1: Utilities.dismissProgressHUD() + self.vc.addToCartBtn.setTitle("Remove", for: .normal) self.vc.toast(msg: data.message ?? "Added to cart", time: 1) { if let productDetails = self.productDetails{ CartDataCache.cartListData.append(CartListingDM.ResultData(id: productDetails.id, skuID: productDetails.skuID, productName: productDetails.productName, categoryMasterID: productDetails.categoryMasterID, subCategoryMasterID: productDetails.subCategoryMasterID, productPrice: productDetails.productPrice, remainStockQuantity: productDetails.remainStockQuantity, stockStatus: productDetails.stockStatus, taxValue: productDetails.taxValue, productQuantity: productDetails.remainStockQuantity, shopMasterDetail: productDetails.shopMasterDetail, shopImage: productDetails.shopImage, productFinalPrice: productDetails.productFinalPrice)) - self.vc.addToCartBtn.setTitle("Remove", for: .normal) self.updateCartIcon() } } @@ -149,13 +149,12 @@ class ProductDetailsVM{ Utilities.alert(title: "Error", message: data.message ?? K.ConstantString.unRecognised, viewController: self.vc) case 1: Utilities.dismissProgressHUD() + self.vc.addToCartBtn.setTitle("Add to Cart", for: .normal) self.vc.toast(msg: data.message ?? "Removed from cart", time: 1) { self.productDetails?.addedToCart = false - if let index = CartDataCache.cartListData.firstIndex(where: {$0.id == self.productDetails?.id}){ CartDataCache.cartListData.remove(at: index) self.updateCartIcon() - self.vc.addToCartBtn.setTitle("Add to Cart", for: .normal) } } default: diff --git a/WOKA/WebSeries/ViewModel/WebSeriesSeasonVM.swift b/WOKA/WebSeries/ViewModel/WebSeriesSeasonVM.swift index c0652fc..64ab0dd 100644 --- a/WOKA/WebSeries/ViewModel/WebSeriesSeasonVM.swift +++ b/WOKA/WebSeries/ViewModel/WebSeriesSeasonVM.swift @@ -43,7 +43,7 @@ class WebSeriesSeasonVM{ if AuthFunc.shareInstance.guestUserLoginPopUp() { return} guard let self ,let indexSelected, let categoryID, let showData, let showID = showData.id, let isFav = showData.markAsFavourite, let postType = showData.contentMoreDetails?.first?.postType else{return} - + if isFav { //remove LikeFavCommonFunc.shareInstance.removeFavourite(postID: showID, postType: postType, categoryID: categoryID, vc: self.vc) { isDone in @@ -78,7 +78,7 @@ class WebSeriesSeasonVM{ K.GVar.myListSoftReload = true }else{ // english guard let showData = self.showData else{return} - MyListDataTemp.shareInstance.webSeriesHindi.append(FavouriteListingDM.ResultData.ShowDatum(id: showData.id, title: showData.title, description: showData.description, thumbnailPath: showData.thumbnailPath, showType: showData.showType, totalSeasons: showData.totalSeasons, totalEpisodes: showData.totalEpisodes, categoryMasterID: showData.categoryMasterID, ageRangeMasterID: showData.ageRangeMasterID, genderMasterID: showData.genderMasterID, contentMoreDetails: showData.contentMoreDetails, markAsFavourite: showData.markAsFavourite, isLiked: showData.isLiked, likesCount: showData.likesCount, viewsCount: showData.viewsCount, bookmarkCount: showData.bookmarkCount, bookmarkCategoryIDS: "1")) + MyListDataTemp.shareInstance.favListingData?.showData?.append(FavouriteListingDM.ResultData.ShowDatum(id: showData.id, title: showData.title, description: showData.description, thumbnailPath: showData.thumbnailPath, showType: showData.showType, totalSeasons: showData.totalSeasons, totalEpisodes: showData.totalEpisodes, categoryMasterID: showData.categoryMasterID, ageRangeMasterID: showData.ageRangeMasterID, genderMasterID: showData.genderMasterID, contentMoreDetails: showData.contentMoreDetails, markAsFavourite: showData.markAsFavourite, isLiked: showData.isLiked, likesCount: showData.likesCount, viewsCount: showData.viewsCount, bookmarkCount: showData.bookmarkCount, bookmarkCategoryIDS: "1")) K.GVar.myListSoftReload = true } } @@ -136,7 +136,13 @@ class WebSeriesSeasonVM{ } vc.shareView.addTapGesture { - print("share") + if let name = URL(string: "https://apps.apple.com/in/app/woka/id6465305185"), !name.absoluteString.isEmpty { + let objectsToShare = [name] + let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) + self.vc.present(activityVC, animated: true, completion: nil) + } else { + // show alert for not available + } } }