Issue with downloading youtube videos with swift - swift

I am trying to download youtube video in my app and for that I used THIS and library included in that answer but my app is crashing with error :
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my swift code:
import UIKit
class ViewController: UIViewController {
let videoID = "hS5CfP8n_js"
var extractor = LBYouTubeExtractor()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func start(sender: AnyObject) {
var url: String = "https://www.youtube.com/watch?v=hS5CfP8n_js"
let testURL = NSURL(string:url)
if testURL != nil {
extractor = LBYouTubeExtractor(URL: testURL, quality: LBYouTubeVideoQualityLarge)
extractor.extractVideoURLWithCompletionBlock({(videoURL, error) in
if error == nil {
println(videoURL)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let data = NSData(contentsOfURL: videoURL)!
let pathToDocs = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
let fileName = "video_\(self.videoID).mp4"
let yourPath = pathToDocs.stringByAppendingPathComponent(fileName)
data.writeToFile(yourPath, atomically: true)
println("File \(fileName) successfully saved")
})
} else {
println("Failed extracting video URL using block due to error:\(error)")
}
})
}
}
}
my project is crashing at this line:
let data = NSData(contentsOfURL: videoURL)!
But I can get videoURL in console:
http%3A%2F%2Fr3---sn-tv0cgv5qc5oq-nu8e.googlevideo.com%2Fvideoplayback%3Fsver%3D3%26id%3D852e427cff27fe3b%26dur%3D55.681
... ty=small
I don't know what I am missing.
Here is my sample project.

Related

How do I load a Data object into a SCNScene?

I want to load a 3d usdz blob into a view, but since I only have the data object, I'm trying to initialize the scene with that with no luck.
To that, I initialize the SCNSceneSource() and then open it using .scene().
Now what I don't understand:
If I use a URL and load the scene directly - it works.
If I use a Data object on the same URL it doesn't.
Apple docs says, the data should be of type NSData but that seems wrong.
import SceneKit
let url = URL(string: "file:///Users/thilo/Desktop/Input/UU2.usdz")!
// working
let src_ok = SCNSceneSource(url: url)
let scn_ok = src_ok?.scene(options: nil, statusHandler: {
a,b,c,d in print("OK: \(a) \(b) \(String(describing: c)) \(d) ")
})
print("Ok: \(scn_ok)")
// Not working?
let data = try! Data(contentsOf: url)
let src_bad = SCNSceneSource(data: data)
let scn_bad = src_bad?.scene(options: nil, status handler: {
a,b,c,d in print("BAD: \(a) \(b) \(String(describing: c)) \(d) ")
})
print("Failed: \(scn_bad)")
running on Playground says:
Ok: Optional(<SCNScene: 0x6000038e1200>)
BAD: 0.0 SCNSceneSourceStatus(rawValue: 4) nil 0x000000016fa948bf
BAD: 0.0 SCNSceneSourceStatus(rawValue: 4) nil 0x000000016fa942af
BAD: 0.0 SCNSceneSourceStatus(rawValue: -1) Optional(Error Domain=NSCocoaErrorDomain Code=260 "Could not load the scene" UserInfo={NSLocalizedDescription=Could not load the scene, NSLocalizedRecoverySuggestion=An error occurred while parsing the COLLADA file. Please check that it has not been corrupted.}) 0x000000016fa942af
Failed: nil
What am I missing?
SCNSceneSource doesn't support .usdz in Data context
Official documentation says that SCNSceneSource object supports only .scn, .dae and .abc file formats. But it turns out that SceneKit doesn't support URL-loading of .usdz only in the context of working with Data. Thus, when working with Data, use files in the .scn format.
import SceneKit
import Cocoa
class GameViewController : NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "file:///Users/swift/Desktop/ship.scn") {
let data = try! Data(contentsOf: url)
let source = SCNSceneSource(data: data)
let sceneView = self.view as! SCNView
sceneView.scene = source?.scene()
}
}
}
To load .usdz using URL, try SCNSceneSource.init?(url: URL)
class GameViewController : NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "file:///Users/swift/Desktop/ship.usdz") {
let source = SCNSceneSource(url: url)
let sceneView = self.view as! SCNView
sceneView.scene = source?.scene()
}
}
}
Or use SCNScene object to load .usdz model
class GameViewController : NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(fileURLWithPath: "/Users/swift/Desktop/ship.usdz")
do {
let scene = try SCNScene(url: url)
let sceneView = self.view as! SCNView
sceneView.scene = scene
sceneView.autoenablesDefaultLighting = true
} catch {
print(error.localizedDescription)
}
}
}
Gathering from the comment "does not support usdz" my solution is:
to create a temporary file ( .usdz) seems to be required by the API...
and then manually remove the temporary file after loading.
First extend FileManager with the below code:
public extension FileManager {
func temporaryFileURL(fileName: String = UUID().uuidString,ext: String) -> URL? {
return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(fileName + ext)
}
}
For a limited hard-coded use case:
let fm = FileManager.default
let tempusdz = fm.temporaryFileURL(ext:".usdz")!
fm.createFile(atPath: tempusdz.path(), contents: sceneData)
let src = SCNSceneSource(url: tempusdz)
if let scene = src?.scene(options: nil) {
....
}
try? fm.removeItem(at: tempusdz)
of course this is a hack, because it will only work if the data is in usdz format.
Since usdz is a ZIP archive, maybe testing for a zip and then just doing the below is a better option:
let sceneData:Data? = data
var sceneSrc: SCNSceneSource? = nil
var tempURL:URL? = nil
if let dataStart = sceneData?.subdata(in: 0..<4),
let dataMagic = String(data: dataStart, encoding: String.Encoding.utf8) as String?,
dataMagic == "PK\u{3}\u{4}" {
let fm = FileManager.default
tempURL = fm.temporaryFileURL(ext: ".usdz")
if let tempURL {
fm.createFile(atPath: tempURL.path(), contents: sceneData)
sceneSrc = SCNSceneSource(url: tempURL)
}
} else {
sceneSrc = SCNSceneSource(data: sceneData!)
}
let scene = sceneSrc?.scene()
if let tempURL {
try? FileManager.default.removeItem(at: tempURL)
}
Does anyone knows a better solution?
Is there an easy way to check the type of the Data ?
potential solution could be to verify the format of the data object and ensure that it is a valid COLLADA file.
import Foundation
let url = URL(string: "file:///Users/thilo/Desktop/Input/UU2.usdz")!
let data = try! Data(contentsOf: url)
print("Data size: \(data.count)")
print("Data format: \(data.description)")
you usually get these types of errors when the data wasn't properly formatted

Can't play local audio mp3 file with AVAudioPlayer

I want to download and play the sound from Google Translate (Text to Speech).
The mp3 file's downloaded successfull. I tried to play it with AVAudioPlayer, but there's no sound on both simulator and my real iPhone
I use XCode 10.2.1, Swift 5. Test on simulator (XSMax) and iPhone XSMax
import UIKit
import AVFoundation
class TextToSpeechGoogleTranslate: AVAudioPlayer, AVAudioPlayerDelegate {
var player: AVAudioPlayer?
func speak() {
let fileURL = URL(string: "https://translate.google.com/translate_tts?ie=UTF-8&total=1&idx=0&client=tw-ob&tl=vi&q=Hello" )!
let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)!
let destinationFileUrl = documentsUrl.appendingPathComponent("voice.mp3")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try? FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
print(destinationFileUrl)
do {
self.player = try AVAudioPlayer(contentsOf: destinationFileUrl)
self.player!.delegate = self
self.player!.prepareToPlay()
self.player!.volume = 1.0
self.player!.play()
}
catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
catch {
print("AVAudioPlayer init failed")
}
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
}
}
Output:
Successfully downloaded. Status code: 200
file:///Users/macbook/Library/Developer/CoreSimulator/Devices/F43F9B03-674C-4EE1-8CAD-01B5145868DE/data/Containers/Data/Application/6D1A3310-386D-4706-9F1E-DFF536B2A43F/Documents/voice.mp3
I played that file in Finder. It's OK.
I just tried your code and it seems to work fine. Maybe the problem is in how you create the TextToSpeechGoogleTranslate object and call speak() on it. For example, here is what I tried:
class ViewController: UIViewController {
var test: TextToSpeechGoogleTranslate?
override func viewDidLoad() {
super.viewDidLoad()
test = TextToSpeechGoogleTranslate()
test!.speak()
}
}
If that doesn't help, can you post some code showing how you call this method?
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate {
var audioPlayer = TextToSpeechGoogleTranslate()
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer.delegate = self
audioPlayer!.speak()
}
}
1) create instance of your class
2) Confirm it's delegate
3) call your function after you received your data
4) while testing from device, don't forget to turn off silence mode (volume up)

Swift3 file preview not working

I think something changed within Swift that disabled me from previewing my files. It worked fine previously. If I click on say a PDF file in my app, I see the title of the PDF, but the content of PDF (preview) area does not show.
Below is my code & logs & also the screenshot. If anyone has an idea of where I can fix the issue, any help would be greatly appreciated.
// When file is clicked this method is called
#objc private func handleTapped() {
guard let url = self.file.fileUrl else { return }
if self.file.isDownloaded {
self.showDocumentController(url: self.file.urlInDocumentsDirectory! as NSURL)
return
}
SVProgressHUD.showProgress(0)
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("pig.png")
return (documentsURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(url, to: destination)
.downloadProgress { (download) in
DispatchQueue.main.async() {
SVProgressHUD.showProgress(Float(download.fractionCompleted))
}
}.validate(statusCode: 200..<300)
.response { (response) in
SVProgressHUD.dismiss()
guard response.response?.statusCode == 200 else { return }
let directoryURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let pathURL = URL(fileURLWithPath: directoryURL, isDirectory: true)
//pathURL: file:///var/mobile/Containers/Data/Application/6DDCCC30-107C-4613-B63D-18962C3D06D3/Documents/
guard let fileName = response.response?.suggestedFilename else { return }
//fileName: 05_기조강연_RobertMankin_BETTER+OFFICES+GREATER+INNOVATION.pdf
let fileURL = pathURL.appendingPathComponent(fileName)
//fileURL: file:///var/mobile/Containers/Data/Application/6DDCCC30-107C-4613-B63D-18962C3D06D3/Documents/05_%E1%84%80%E1%85%B5%E1%84%8C%E1%85%A9%E1%84%80%E1%85%A1%E1%86%BC%E1%84%8B%E1%85%A7%E1%86%AB_RobertMankin_BETTER+OFFICES+GREATER+INNOVATION.pdf
self.saveFileURL(url: fileURL as NSURL)
self.showDocumentController(url: fileURL as NSURL)
}
}
private func saveFileURL(url: NSURL) {
self.file.urlInDocumentsDirectory = url as URL
let realm = RealmService.defaultRealm
try! realm?.write {
realm?.add(self.file, update: true)
}
self.file = self.file.copyFromRealm()
}
private func showDocumentController(url: NSURL) {
let docController = UIDocumentInteractionController(url: url as URL)
docController.delegate = self
docController.presentPreview(animated: true)
}
// MARK: UIDocumentInteractionControllerDelegate methods
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
if let controller = UIApplication.shared.keyWindow?.topMostViewController() {
return controller
}
return UIViewController()
}
this is how the preview shows
Here Is The Code
import UIKit
import Alamofire
class ViewController: UIViewController, UIWebViewDelegate
{
#IBOutlet weak var WebView: UIWebView!
var NewsURL: String = ""
override func viewDidLoad()
{
super.viewDidLoad()
Self.LoadPdf()
}
func LoadPdf()
{
let url = NSURL (string: "\(http://)") //Your Pdf URL Here
let requestObj = NSURLRequest(URL: url!);
WebView.loadRequest(requestObj)
}
}

Impossible to stream video after downloading from Parse

It seems that I can't stream my videos once I downloaded them from Parse backend. However I know that the videos are working well as you can play it from Chrome but not from Safari ( I don't know why ...).
My code to play the video on the AVPlayer is also right since I test it for multiple NSUrl from local database.
Several posts about it confirm that there is a problem going on but no one has an answer.
iOS - Can't stream video from Parse Backend
Cant stream video (PFFile) from parse server
Thanks in advance if someone can help me.
The secret is to get the PFFIle data, save it locally with the proper extention (mov) and treaming the local file.
look at this entry [Cant stream video (PFFile) from parse server][1], the second anwser.
The PFFIle was created with the following code:
let videoPath = info[UIImagePickerControllerMediaURL] as! NSURL
let imageData = NSData (contentsOfURL:videoPath)! as NSData
do {
let file = try PFFile(name: "_mov", data: imageData, contentType: "video/quicktime")
videoSaveSelected(file )
} catch let error as NSError {
print("Error generating PFFile: \(error)")
}
This is the code for the player
import UIKit
import AVKit
import MobileCoreServices
import AssetsLibrary
import AVFoundation
import Parse
import ParseUI
class VideoPlayerViewController: UIViewController {
var videoUrl: NSURL!
var file: PFFile!
override func viewDidLoad() {
super.viewDidLoad()
// navigation
let backButtonCustom = UIButton( frame: CGRectMake(0, 0, 22, 22))
let backIcon = UIImage(named: "0836-arrow-left")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
backButtonCustom.setImage(backIcon, forState: UIControlState.Normal)
backButtonCustom.addTarget(self, action: #selector(ReqDataEntryLgViewController.doneButtonTapped), forControlEvents: UIControlEvents.TouchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButtonCustom)
let doneButton = UIBarButtonItem(title: "Done", style: .Done, target: self, action: #selector(ReqDataEntryLgViewController.doneButtonTapped))
self.navigationItem.setRightBarButtonItems([doneButton], animated: true)
// video player
let playerController = AVPlayerViewController()
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
// get data from PFFile in database
file!.getDataInBackgroundWithBlock({
(movieData: NSData?, error: NSError?) -> Void in
if (error == nil) {
let documentsPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let destinationPath:NSString = documentsPath.stringByAppendingString("/file.mov")
let filemanager = NSFileManager.defaultManager()
do {
try filemanager.removeItemAtPath(destinationPath as String)
} catch {
print("Ooops! Something went wrong: \(error)")
}
// write to local file
movieData!.writeToFile ( destinationPath as String, atomically:true)
// do {
// let attr : NSDictionary? = try NSFileManager.defaultManager().attributesOfItemAtPath(destinationPath as String)
//
// if let _attr = attr {
// let fileSize = _attr.fileSize()
// print ("Movie Size \(fileSize)" )
// }
// } catch {
// print("Ooops! Something went wrong getting size: \(error)")
// }
let playerItem = AVPlayerItem(asset: AVAsset(URL: NSURL(fileURLWithPath: destinationPath as String)))
let player = AVPlayer(playerItem: playerItem)
playerController.player = player
player.play()
} else {
print ("error on getting movie data \(error?.localizedDescription)")
}
})
}
func doneButtonTapped(){
navigationController?.popViewControllerAnimated(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Setup AVAudioPlayer with swift 1.2?

When I tried to setup AVaudioPlayer last time, I used this code:
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer {
var path = NSBundle.mainBundle().pathForResource(file, ofType:type)
var url = NSURL.fileURLWithPath(path!)
var error: NSError?
var audioPlayer:AVAudioPlayer?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
return audioPlayer!
}
var buttonBeep = AVAudioPlayer()
buttonBeep = self.setupAudioPlayerWithFile("buttonPush", type:"m4a")
And it worked perfectly. Now with Swift 1.2 it seems that I can't do that.
I also tried this code:
var button : AVAudioPlayer?
in didMoveToView:
if let button = self.setupAudioPlayerWithFile("button", type:"m4a") {
self.button = button
}
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer? {
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
var error: NSError?
var audioPlayer:AVAudioPlayer?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
return audioPlayer
}
It gives me an error on let url line - EXC_BAD_INSTRUCTION. I put my audio file in Supporting Files inside my project. What am I doing wrong?
UPDATE: that was my mistake. If you have the same problem — make sure you are adding sounds to a target of your app, not just in project
Try this code:
var backgroundMusicPlayer: AVAudioPlayer!
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
if (url == nil) {
println("Could not find file: \(filename)")
return
}
var error: NSError? = nil
backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
if backgroundMusicPlayer == nil {
println("Could not create audio player: \(error!)")
return
}
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
Use it this way:
playBackgroundMusic("button.m4a")
Hope it helps.
It was working fine with 1.2 and I didn't test it right now because I have updated my Xcode and I suggest you to use latest version of Xcode which have swift version 2.0.