I am working on a swift application in which users have a link of videos like facebook etc and if the user wishes to download that he can just put a link in UITextfield and download it. The code I tried is
func downloadAndSaveVideo(videoUrl:String?,videoName:String) {
DispatchQueue.global(qos: .background).async {
guard let myUrl = videoUrl else {
print("Invalid Url")
return
}
Alamofire.request(myUrl).downloadProgress(closure: { (Progress) in
print(Progress)
self.progress_view.progress = Float(Progress.fractionCompleted)
}).responseData(completionHandler: { (responce) in
if let data = responce.result.value{
print(data)
let documentUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let videoUrl = documentUrl.appendingPathComponent(videoName)
do {
try data.write(to: videoUrl)
print("done")
}catch
{
print("error")
}
}
})
}
}
This code is working fine if I use a playable link like this. but if try to put facebook link like https://www.facebook.com/pagename/videos/962353737470453/ or youtube I am unable to download that video
can someone help me to how to download such videos or convert facebook video share link to watch or downloadable link so that I can download videos directly from my app.
Related
I have two viewcontrollers: GetStartedViewController (root viewController) and TrimVideoViewController. I pick a video from the root one using PHPicker and I need to play that video in the second viewController. The problem is that when I provide the url to AVFoundation nothing happens. I am at a loss.
I've scoured the internet and cannot find an answer to this.
I saw a single solution to this saying I need to copy and save to another directory and use that directory with AVFoundation. It still didn't work.
I also searched for a framework that could maybe play videos from gallery, but most of them only played using HTTP links.
can anyone help me with this?
Here's the GetStartedViewController
Here's the TrimVideoViewController
please take note that you have fetched the URL but you didn't set self.url = url
changing your vc!.videoURL = url to the following should make a difference
vc!.videoURL = url
Okay, after consulting with a more experienced iOS developer, we found that the url that was being passed by PHPicker was not suitable for AVFoundation. Instead we wrote this function that relocates the video and makes it suitable for AVFoundation.
private func getVideo(from itemProvider: NSItemProvider, typeIdentifier: String) {
itemProvider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { url, error in
if let error = error {
print(error.localizedDescription)
}
guard let url = url else { return }
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
guard let targetURL = documentsDirectory?.appendingPathComponent(url.lastPathComponent) else { return }
do {
if FileManager.default.fileExists(atPath: targetURL.path) {
try FileManager.default.removeItem(at: targetURL)
}
try FileManager.default.copyItem(at: url, to: targetURL)
DispatchQueue.main.async {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "TrimVideooViewController") as? TrimVideooViewController
vc!.videoURL = targetURL
self.navigationController?.pushViewController(vc!, animated: true)
}
} catch {
print(error.localizedDescription)
}
}
}
enjoy!
i am implementing pdf preview in my Swift app so i have decided to use third party library for Preview PDF i am using below library
Please Check Library Here
so first i am download url and store to document directory and than i am displaying it but pdf not previewed below is my code
func downloadFileFromURL(url: String) {
if let audioUrl = URL(string: url) {
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
print(destinationUrl)
let document = try! PDFDocument(filePath: destinationUrl.lastPathComponent, password: "")
self.collectionView.document = document
} else {
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
try FileManager.default.moveItem(at: location, to: destinationUrl)
print(destinationUrl)
let document = try! PDFDocument(filePath: destinationUrl.lastPathComponent, password: "")
self.collectionView.document = document
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
}
and inside viewDidLoad() i am implementing below code
downloadFileFromURL(url: "http://housedocs.house.gov/edlabor/AAHCA-BillText-071409.pdf")
but still pdf is not previewed can some tell me its the right way to preview pdf with UXMPdf
or suggest me best pdfviewer for Swift from which i can load pdf from URL
You have to specify the full path rather than the last path component.
And remove the ! inside a do - catch block.
let document = try PDFDocument(filePath: destinationUrl.path, password: "")
As the password parameter is unused I recommend to use the built-in initializer
let document = try PDFDocument(url: destinationUrl)
I have Used Following Code to Download Video from the URL. It is working fine for Downloading it the Video.
func downloadVideo(){
Alamofire.request("https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4").downloadProgress(closure:{ (progress) in
print(progress.fractionCompleted)
self.progressView.progress = Float(progress.fractionCompleted)
}).responseData{ (response) in
print(response.result)
print(response.result.value!)
print(response.result.description)
if let data = response.result.value {
let obj = String(data: data, encoding: .utf8)
let player = AVPlayer(url: URL(string: obj!)!)
self.playerController.player = player
self.addChildViewController(self.playerController)
self.view.addSubview(self.playerController.view)
self.playerController.view.frame = self.view.frame
player.play()
}
}
But I want to Play this Video in AVPlayer
I have Found that May be Video is Downloaded as NSData. am in Right.?
Can Anyone Help me How to Play this Video once it has Finished Downloading?
It Would be good if anyone Can suggest some another Code For
downloading video with Progress Bar and then Playing it in Swift 3.0.
TIA.
Afters Hours of Searching for this stuff
I came up With an easy and reliable way of Converting the NSData in URL so that it can be played by AVPlayer
func downloadVideo(){
Alamofire.request("https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4").downloadProgress(closure : { (progress) in
print(progress.fractionCompleted)
self.progressView.progress = Float(progress.fractionCompleted)
}).responseData{ (response) in
print(response)
print(response.result.value!)
print(response.result.description)
if let data = response.result.value {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let videoURL = documentsURL.appendingPathComponent("video.mp4")
do {
try data.write(to: videoURL)
} catch {
print("Something went wrong!")
}
print(videoURL)
let player = AVPlayer(url: videoURL as URL)
self.playerController.player = player
self.addChildViewController(self.playerController)
self.view.addSubview(self.playerController.view)
self.playerController.view.frame = self.view.frame
player.play()
}
}
}
You Can Use this Following Piece of Code to convert that
if let data = response.result.value {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let videoURL = documentsURL.appendingPathComponent("video.mp4")
do {
try data.write(to: videoURL) // Pass this videoURL to AVPlayer after Downloading Video it will be played.
} catch {
print("Something went wrong!")
}
print(videoURL)
As per the Request,
Here is the Source Code of the full implementation.
Download Source Code
P.S:- This was made thinking of a demo. Many of you will argue that this code can be optimised and can be done better, but yes this
is an demo and you can pick some of the part or whole depending upon
your Requirement
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'm currently displaying a video in my app and I want the user to be able to save it to its device gallery/album photo/camera roll.
Here it's what I'm doing but the video is not saved in the album :/
func downloadVideo(videoImageUrl:String)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//All stuff here
print("downloadVideo");
let url=NSURL(string: videoImageUrl);
let urlData=NSData(contentsOfURL: url!);
if((urlData) != nil)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0];
let fileName = videoImageUrl; //.stringByDeletingPathExtension
let filePath="\(documentsPath)/\(fileName)";
//saving is done on main thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
urlData?.writeToFile(filePath, atomically: true);
print("videoSaved");
})
}
})
}
I'va also look into this :
let url:NSURL = NSURL(string: fileURL)!;
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(url);
let assetPlaceHolder = assetChangeRequest!.placeholderForCreatedAsset;
let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection)
albumChangeRequest!.addAssets([assetPlaceHolder!])
}, completionHandler: saveVideoCallBack)
But I have the error "Unable to create data from file (null)". My "assetChangeRequest" is nil. I don't understand as my url is valid and when I go to it with a browser, it download a quick time file.
If anyone can help me, it would be appreciated ! I'm using Swift and targeting iOS 8.0 min.
Update
Wanted to update the answer for Swift 3 using URLSession and figured out that the answer already exists in related topic here. Use it.
Original Answer
The code below saves a video file to Camera Roll. I reused your code with a minor change - I removed let fileName = videoImageUrl; because it leads to incorrect file path.
I tested this code and it saved the asset into camera roll. You asked what to place into creationRequestForAssetFromVideoAtFileURL - put a link to downloaded video file as in the example below.
let videoImageUrl = "http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"
DispatchQueue.global(qos: .background).async {
if let url = URL(string: urlString),
let urlData = NSData(contentsOf: url) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
let filePath="\(documentsPath)/tempFile.mp4"
DispatchQueue.main.async {
urlData.write(toFile: filePath, atomically: true)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
}) { completed, error in
if completed {
print("Video is saved!")
}
}
}
}
}
Swift 3 version of the code from #Nimble:
DispatchQueue.global(qos: .background).async {
if let url = URL(string: urlString),
let urlData = NSData(contentsOf: url)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
let filePath="\(documentsPath)/tempFile.mp4"
DispatchQueue.main.async {
urlData.write(toFile: filePath, atomically: true)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
}) { completed, error in
if completed {
print("Video is saved!")
}
}
}
}
}
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: video.url!)}) {
saved, error in
if saved {
print("Save status SUCCESS")
}
}
following #Nimble and #Yuval Tal solution, it is much more preferable to use the URLSession dataTask(with:completionHandler:) method to download a file before writing it as stated in the warning section of NSData(contentsOf:) Apple documentation
Important
Don't use this synchronous initializer to request network-based URLs.
For network-based URLs, this method can block the current thread for
tens of seconds on a slow network, resulting in a poor user
experience, and in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the
dataTask(with:completionHandler:) method of the URLSession
a correct implementation could be :
let defaultSession = URLSession(configuration: .default)
var dataTask: URLSessionDataTask? = nil
func downloadAndSaveVideoToGallery(videoURL: String, id: String = "default") {
DispatchQueue.global(qos: .background).async {
if let url = URL(string: videoURL) {
let filePath = FileManager.default.temporaryDirectory.appendingPathComponent("\(id).mp4")
print("work started")
self.dataTask = self.defaultSession.dataTask(with: url, completionHandler: { [weak self] data, res, err in
DispatchQueue.main.async {
do {
try data?.write(to: filePath)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: filePath)
}) { completed, error in
if completed {
print("Saved to gallery !")
} else if let error = error {
print(error.localizedDescription)
}
}
} catch {
print(error.localizedDescription)
}
}
self?.dataTask = nil
})
self.dataTask?.resume()
}
}
}
One more advantage is that you can pause, resume and terminate your download by calling the corresponding method on dataTask: URLSessionDataTask .resume() .suspend() .cancel()