I'm working on a project in swift3 and I have a particular UIViewController to download an mp3 to my filemanager and using that path saved I wants to play an mp3 using AVPlayer. My code doesn't work, I think Im missing something. How would I achieve this?. My code to download the file to the filemanager as below
func downloadSong() {
if let audioUrl = URL(string: "https://www.googleapis.com/download/storage/v1/b/feisty-beacon-159305.appspot.com/o/Aal%20Izz%20Well%20-%20Remix(MyMp3Song).mp3?generation=1490097740630162&alt=media") {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!//since it sys first this may only plays the first item
// destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print("destinationUrl is :",destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
self.dest = destinationUrl.path
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("file path is :",destinationUrl.path)
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
}
And once I save that file, using its file path which is "destinationUrl.path" I initiate my player as bellow in a different UIViewController. As for now I have hardcoded the path I save. The code as bellow.
override func viewDidLoad() {
super.viewDidLoad()
//path I have saved in file manager is set to the url
let url = NSURL.fileURL(withPath:"/Users/auxenta/Library/Developer/CoreSimulator/Devices/F3840294-04AA-46BE-9E46-4342452AFB69/data/Containers/Data/Application/670C0EA1-B375-498E-8847-8707D391D7BF/Documents/Aal Izz Well - Remix(MyMp3Song).mp3") as NSURL
self.playerItem = AVPlayerItem(url: url as URL)
self.player=AVPlayer(playerItem: self.playerItem!)
let playerLayer=AVPlayerLayer(player: self.player!)
playerLayer.frame = CGRect(x: 0, y: 0, width: 10, height: 50) // actually this player layer is not visible
self.view.layer.addSublayer(playerLayer)
}
#IBAction func playBtnPressed(_ sender: Any) {
if player?.rate == 0 // this means if its not playing
{
player!.play()
print("playing")
playbutton.setImage(UIImage(named: "pausebutton"), for: UIControlState.normal)
//trackTime
trackTime()
} else {
// getFileFromFieManager()
print("pause")
player!.pause()
playbutton.setImage(UIImage(named: "playbutton"), for: UIControlState.normal)
}
}
Your problem is the URL set to the AVPlayer. In fact hardcoding a path in iOS doesn't work, it can change at any time.
You need to use code it the same way as you destinationUrl:
if let audioUrl = URL(string: "https://www.googleapis.com/download/storage/v1/b/feisty-beacon-159305.appspot.com/o/Aal%20Izz%20Well%20-%20Remix(MyMp3Song).mp3?generation=1490097740630162&alt=media") {
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!//since it sys first this may only plays the first item
// destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print("destinationUrl is :",destinationUrl)
self.playerItem = AVPlayerItem(url: destinationUrl)
self.player=AVPlayer(playerItem: self.playerItem!)
let playerLayer=AVPlayerLayer(player: self.player!)
.
.
.
}
Normally it should work. Hope it helps.
You can use codes in the below. I hope I could helped you out. First you need to create path for the source file that you wanted to be play
let path = Bundle.main.path(forResource: "yourFileName", ofType: "mp3")
let soundUrl = URL(fileURLWithPath: path!)
do{
try btnSound = AVAudioPlayer(contentsOf: soundUrl)
btnSound.prepareToPlay()
}
catch let err as NSError{
print(err.debugDescription)
}
After you created these in the viewDidLoad method. You can create function for the playing sound like;
func playSound() {
if btnSound.isPlaying {
btnSound.stop()
}
btnSound.play()
}
after all of these you can able to play mp3 files in swift.
Related
I try to save given video locally after then I need those saved videos for playing video in my app. I can't handle the saving video. Here is my saving try :
func saveVideoDocumentDirectory(url : URL){
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(".MOV")
do{
let videoData = try Data(contentsOf: url)
fileManager.createFile(atPath: paths as String, contents: videoData, attributes: nil)
}catch{
//
}
}
here is the get file try
func getVideo(){
let fileManager = FileManager.default
let videoPAth = (self.getDirectoryPath() as NSString).appendingPathComponent(".MOV")
if fileManager.fileExists(atPath: videoPAth){
print(videoPAth)
play(url: URL(string: videoPAth)!)
}else{
print("No Video")
}
}
here is my play video func :
func play(url : URL)
{
let player = AVPlayer(url: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
Instead of Filemanager.createFile(), try using write instead.
let videoData = try Data(contentsOf: url)
try videoData.write(to: paths, options: .atomic)
Also, I recommend creating a folder first (from this answer).
extension URL {
static func createFolder(folderName: String) -> URL? {
let fileManager = FileManager.default
// Get document directory for device, this should succeed
if let documentDirectory = fileManager.urls(for: .documentDirectory,
in: .userDomainMask).first {
// Construct a URL with desired folder name
let folderURL = documentDirectory.appendingPathComponent(folderName)
// If folder URL does not exist, create it
if !fileManager.fileExists(atPath: folderURL.path) {
do {
// Attempt to create folder
try fileManager.createDirectory(atPath: folderURL.path,
withIntermediateDirectories: true,
attributes: nil)
} catch {
// Creation failed. Print error & return nil
print(error.localizedDescription)
return nil
}
}
// Folder either exists, or was created. Return URL
return folderURL
}
// Will only be called if document directory not found
return nil
}
}
Then, you can save like this:
guard let folderURL = URL.createFolder(folderName: "StoredVideos") else {
print("Can't create url")
return
}
let permanentFileURL = folderURL.appendingPathComponent(nameOfYourFile).appendingPathExtension("MOV")
let videoData = try Data(contentsOf: url)
try videoData.write(to: permanentFileURL, options: .atomic)
This will save you the hassle of NSSearchPathForDirectoriesInDomains.
I am working in an audio player application, and also I am having a offline download option, when the user click the download button it should start download and save it into local. I have saved it using file manager URLSession.
I have tried of taking and separating the destination url. I am using a jukebox third party for playing the song. the sample file location is
file:///var/mobile/Containers/Data/Application/BBB9AF1C-D87C-4C40-9F29-AD89062A20E2/Documents/05-KARMA-YOGA.mp3
if let audioUrl = URL(string: audioTobedownloaded) {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
the actual thing is how should I play the audio which I have downloaded in file manager.
func findFilesWith(extensionType: String) -> [URL]{
var matches = [URL]()
let fileManager = FileManager.default
let files = fileManager.enumerator(atPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
// *** this section here adds all files with the chosen extension to an array ***
for item in files!
{
let fileURL = item as! URL
if (fileURL.pathExtension == extensionType)
{
matches.append(fileURL)
}
}
return matches
}
You can use this method to get all kinds of file with respective to the extension type.
In your case use mp3 as extension type
Try the below method to play audio from the fetched url
func play(url:URL) {
print("playing \(url)")
do {
let player = try AVAudioPlayer(contentsOf: url)
player.prepareToPlay()
player.volume = 1.0
player.play()
} catch let error as NSError {
print(error.localizedDescription)
} catch {
print("AVAudioPlayer init failed")
}
}
I was trying to read a file xyz.m4a and trying to play it. But I have an error
2018-04-24 18:11:20.010927+0530 Demo[10807:690526] NSURLConnection finished with error - code -1002
2018-04-24 18:11:20.327008+0530 Demo[10807:688427] CFURLCopyResourcePropertyForKey failed because it was passed an URL which has no scheme
Error: The file “xyz.m4a” couldn’t be opened.
Here is my code:
func audioMethod() {
let fileName = "xyz.m4a",
audioFile = getDocumentsURL().appendingPathComponent(fileName)
if !FileManager.default.fileExists(atPath: audioFile.path) {
print("We have no file !!!!!")
}
do {
let audioData = try Data(contentsOf: audioFile)
if audioPlayer == nil {
audioPlayer = try AVAudioPlayer.init(data: audioData, fileTypeHint: "m4a")
}
audioPlayer?.play()
} catch let error {
print("Error: " + error.localizedDescription)
}
}
func getDocumentsURL() -> URL {
let urlStr = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let url = URL(string: urlStr!)
return url!
}
If I pass that URL direct in AVAudioPlayer as AVAudioPlayer.init(contentsOf: audioFile) then it works fine.
I have tried everything. But nothing works. Please let me know what is wrong with my code.
if let path = Bundle.main.path(forResource: "videoplayback", ofType: "mp4"){
let video = AVPlayer(url: URL(fileURLWithPath:path))
let videoPlayer = AVPlayerViewController()
videoPlayer.player = video
present(videoPlayer,animated: true,completion: {
video.play()
})
}
Add that Video in File Explorer
Then try with above code
I have uploaded some songs in firebase Storage directly,I just want to stream the song in AVAudioPlayer.
Below is the code which I am trying:
var mainRef: FIRStorageReference {
return FIRStorage.storage().reference(forURL: "gs://musicapp-d840c.appspot.com")
}
var audioStorageRef: FIRStorageReference{
return mainRef.child("SongsPath")
}
audioStorageRef.downloadURL { url, error in
if let error = error {
print(error.localizedDescription)
} else {
if let url = url {
do {
self.audioPlayer = try AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: String(describing: url)) as URL)
self.audioPlayer.play()
} catch {}
let storyboard = UIStoryboard(name: "AudioPlayer", bundle: nil)
let audioVc = storyboard.instantiateViewController(withIdentifier: "AudioPlayerViewController") as! AudioPlayerViewController
audioVc.playThisSong = String(describing: url)
self.present(audioVc, animated: false, completion: nil)
}
}
}
Here the song url from the firebase is passing but it is skipping the self.audioPlayer.play. ,I just want to stream the audio. Can I get a proper solution for this?
This is not an answer for streaming.
This is an answer for downloading the file, storing it locally, and playing the audio after the file has finished downloading.
Get a Firebase storage reference using a path string with the file extension. Get a file url to store it on the device using the same path string that we use for the Firebase storage reference.
Initiate the download task using write(toFile: URL). Store the download task in a variable to add observers. When the download is successful, play the audio.
In Swift 4:
var player: AVAudioPlayer?
let pathString = "SongsPath.mp3"
let storageReference = Storage.storage().reference().child(pathString)
let fileUrls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
guard let fileUrl = fileUrls.first?.appendingPathComponent(pathString) else {
return
}
let downloadTask = storageReference.write(toFile: fileUrl)
downloadTask.observe(.success) { _ in
do {
self.player = try AVAudioPlayer(contentsOf: fileUrl)
self.player?.prepareToPlay()
self.player?.play()
} catch let error {
print(error.localizedDescription)
}
}
This is minimal code. Implement error handling as you see fit.
Firebase example of downloading locally
I want to know how I can play file was downloaded in app , I download a music file then I want to play it in the app , I found this func and use it to download .
Thanks at all.
func downloadSong (_ sender:UIButton) {
if let url = URL(string: startURL) {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(url.lastPathComponent)
print(destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
// if the file doesn't exist
} else {
print("Downloading started")
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: url, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
print("Downloading finished")
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
}
Try AVAudioPlayer with your destinationUrl
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:destinationUrl error:nil];
player.numberOfLoops = -1;
[player play];
Swift ver:
let player = try? AVAudioPlayer(contentsOf: destinationUrl)
player?.numberOfLoops = -1;
player?.play()