I am making an app that I want it to pick a song from the iPod music files then add it to the bundle files (documents folder) so that I can use it with the need to access the phones music list again.
I have managed to pick the song and get its ID but I don't know how to add a file to my bundles files using Swift.
I have found answers a bit similar to this question but its all since iOS 4 and using Objective C not Swift.
This is the code which has the files ID:
let u: NSURL? =
self.mediaItem!.valueForProperty(MPMediaItemPropertyAssetURL) as! NSURL?
if u == nil {
return
}
The source of my answer is here, I didn't test the code:
func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
let item: MPMediaItem = mediaItemCollection.items[0]
let pathURL: NSURL? = item.valueForProperty(MPMediaItemPropertyAssetURL) as? NSURL
if pathURL == nil {
Alert.showPopupWithMessage("Unable to read DRM protected file.")
return
}
// Export the ipod library as .m4a file to local directory for remote upload
let exportSession = AVAssetExportSession(asset: AVAsset(URL: pathURL!), presetName: AVAssetExportPresetAppleM4A)
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.outputFileType = AVFileTypeAppleM4A
let fileUrl = //Where you want to save the file
exportSession?.outputURL = fileUrl
exportSession?.exportAsynchronouslyWithCompletionHandler({ () -> Void in
if exportSession!.status != AVAssetExportSessionStatus.Completed {
print("Export error")
}
})
}
Related
I have a mp4 video saved in a folder.
How can i copy the video from my app as a file, in order to paste it in other platforms like Telegram or the Finder to paste it as a new file?
At the moment this is the function i wrote but it pastes the video only in a iMessage textField.
func copyVideo() {
guard let url = TaskManager.shared.lastVideoURL else { return }
do {
let data = try Data(contentsOf: url)
let pasteboard = NSPasteboard.general
pasteboard.declareTypes([kUTTypeMPEG4 as NSPasteboard.PasteboardType], owner: nil)
print(pasteboard.setData(data, forType: kUTTypeMPEG4 as NSPasteboard.PasteboardType))
} catch {
print("error getting data from video \(error)")
}
}
What am I doing wrong?
After some test I found the solution.
This method copies the video as a file:
func copyVideo() {
guard let url = TaskManager.shared.lastVideoURL else { return }
if let fileRefURL = (url as NSURL).fileReferenceURL() as NSURL? {
print(fileRefURL)
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.writeObjects([fileRefURL])
pasteboard.setString(fileRefURL.relativeString, forType: .fileURL)
}
}
I've been trying to find out how to use UTIs (Unified Type Identifier) on macOS in swift, but have not been able to find anything. Does anyone know how I could use UTIs to get a list of video files in a directory that are playable by AVPlayer?
Using Hiroki Kato's UTIKit (https://github.com/cockscomb/UTIKit):
let dir = URL(fileURLWithPath: "./your_dir_here")
let mimeTypes = AVURLAsset.audiovisualMIMETypes()
let playableUtis = mimeTypes.map { UTI(mimeType: $0)! }
let dirContents = try! FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles])
let movies = dirContents.filter { UTI("public.movie") ~= UTI(filenameExtension: $0.pathExtension) }
let playableMovies = movies.filter {
guard let movieUti = UTI(filenameExtension: $0.pathExtension) else {
return false
}
return playableUtis.contains(movieUti)
}
Now playableMovies contains only movie-files playable by AVPlayer.
I'm trying to use an AVAudioPlayerNode to play sounds from the Assets.xcassets asset catalog, but I can't figure out how to do it.
I've been using AVAudioPlayer, which can be initialized with an NSDataAsset like this:
let sound = NSDataAsset(name: "beep")!
do {
let player = try AVAudioPlayer(data: sound.data, fileTypeHint: AVFileTypeWAVE)
player.prepareToPlay()
player.play()
} catch {
print("Failed to create AVAudioPlayer")
}
I want to use an AVAudioPlayerNode instead (for pitch shifting and other reasons). I can create the engine and hook up the node OK:
var engine = AVAudioEngine()
func playSound(named name: String) {
let mixer = engine.mainMixerNode
let playerNode = AVAudioPlayerNode()
engine.attach(playerNode)
engine.connect(playerNode, to: mixer, format: mixer.outputFormat(forBus: 0))
// play the file (this is what I don't know how to do)
}
It looks like the method to use for playing the file is playerNode.scheduleFile(). It takes an AVAudioFile, so I thought I'd try to make one. But the initializer for AVAudioFile wants a URL. As far as I can tell, assets in the asset catalog are not available by URL. I can get the data directly using NSDataAsset, but there doesn't seem to be any way to use it to populate an AVAudioFile.
Is it possible to play sounds from the asset catalog with an AVAudioPlayerNode? And if so, how?
OK so your problem is that you would like to get a URL from a file in your Asset catalog right?
I've looked around but only found this answer
As it says
It basically just gets image from assets, saves its data to disk and return file URL
You should probably change it to look for MP3 files (or WAV or whatever you prefer, maybe that could be an input parameter)
So you could end up with something like:
enum SoundType: String {
case mp3 = "mp3"
case wav = "wav"
}
class AssetExtractor {
static func createLocalUrl(forSoundNamed name: String, ofType type: SoundType = .mp3) -> URL? {
let fileManager = FileManager.default
let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let url = cacheDirectory.appendingPathComponent("\(name).\(type)")
guard fileManager.fileExists(atPath: url.path) else {
guard
let image = UIImage(named: name),
let data = UIImagePNGRepresentation(image)
else { return nil }
fileManager.createFile(atPath: url.path, contents: data, attributes: nil)
return url
}
return url
}
}
Maybe a bit far fetched but I haven't found any other options.
Hope that helps you.
Is it possible to copy a video file located at iOS photo library to our application's documents directory? I tried it using uiimagepickercontroller from where we get the NSUrl of the video file, then converting it into NSData and then writing it into the file. But unfortunately it is not working. I there any alternate method?
My intention is to load the video to OpenCV CvCapture.
Hey Isarathg this is classic use case of IOS devices. Which don't let you access any Photo Album assets directly using path value. To cross check my answer please just check FileExistsAtPath for your file like below -:
println(NSFileManager.defaultManager().fileExistsAtPath( urlvalue.path!))
O/P you will get => False
I also end up with this issue couple of days back After reading the whole IOS documentation. What I have figured it out "We can only access PhotoAlbum Assets if and only if we have PHImageManager session open". To cross check this statement please try below code -:
var currentVideofetch: PHFetchResult!
required init(coder aDecoder: NSCoder) {
let options = PHFetchOptions()
options.sortDescriptors = [
NSSortDescriptor(key: "creationDate", ascending: true)
]
currentVideofetch = PHAsset.fetchAssetsWithMediaType(.Video, options: options)
super.init(coder: aDecoder)
}
func checkImageExists(){
let asset = self.currentVideofetch.objectAtIndex(1) as? PHAsset
}
if let checkedAsset = asset {
PHImageManager.defaultManager().requestAVAssetForVideo(checkedAsset, options: nil, resultHandler: {[weak self](result: AVAsset!, audioMix: AVAudioMix!, info: [NSObject : AnyObject]!) in
println(NSFileManager.defaultManager().fileExistsAtPath(self.urlvalue.path!))
})
}
O/P you will get => True
After opening the PHImageManager session and then when i tried accessing the video with the path. It worked fine. Also able to successfully copy all videos files to our local app directory using the relative path of videos in Photo Album.
If you need i can send you my implementation for this. But not sure if it is correct way or not. But working fine for me.
Second and most effective solution i found is using
AVAssetExportSession
It works like a charm. My implementation is as follows -:
func importVideoToAppDir(videoURL: NSURL, videoFinalPath: NSURL, handler: ((NSURL?) -> Void)?) {
var assetDuration: CMTime!
var asset: AVAsset!
asset = AVAsset.assetWithURL(videoURL) as! AVAsset
assetDuration = asset!.duration
if (DirOperations.DeleteIfExists(videoTempPath) && DirOperations.DeleteIfExists(videoFinalPath)) {
let startTime = kCMTimeZero
let assetDurationSeconds = CMTimeGetSeconds(self.asset!.duration)
var range: CMTimeRange!
if assetDurationSeconds > Float64(maxDuration) {
let stopTime = CMTimeMakeWithSeconds(Float64(maxDuration), 1)
range = CMTimeRangeFromTimeToTime(startTime, stopTime)
} else {
let stopTime = CMTimeMakeWithSeconds(assetDurationSeconds, 1)
range = CMTimeRangeFromTimeToTime(startTime, stopTime)
}
var exporter :AVAssetExportSession = AVAssetExportSession(asset: self.asset, presetName: AVAssetExportPresetHighestQuality)
exporter.outputURL = videoFinalPath
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.timeRange = range
exporter.exportAsynchronouslyWithCompletionHandler { () -> Void in
switch exporter.status {
case AVAssetExportSessionStatus.Failed:
println("failed import video: \(exporter.error)")
handler?(nil)
case AVAssetExportSessionStatus.Cancelled:
println("cancelled import video: \(exporter.error)")
handler?(nil)
default:
println("completed import video")
println(videoFinalPath)
handler?(videoFinalPath)
}
}
}
}
func DeleteIfExists(path: NSURL) -> Bool {
var deleted = true
var error: NSError?
if (NSFileManager.defaultManager().fileExistsAtPath(path.path!)) {
deleted = NSFileManager.defaultManager().removeItemAtPath(path.path!, error: &error)
}
return deleted
}
Hope it helps.
I have the reference of a MPMediaItem when user selects a n audio from the iPod library. i am getting the asset url of that item by using
[mediaItem valueForProperty: MPMediaItemPropertyAssetURL]
But this is not giving me the exact physical location of the file, instead it is giving me an url w.r.t iPod library.
ipod-library://item/item.mp3?id=1840064795502796074
Is there a way to get the physical url of a song from iPod library?
EDIT - actually i want to extract NSData from the physical file and send it to my backend server, so i need the physical file URL and not the relative URL
This will work, but it's a bit slow. Also, it's written for the new MP classes:
// song is an instance of MPMediaItem
if let val = song.value(forKey: MPMediaItemPropertyAssetURL) as? URL {
let asset = AVURLAsset.init(url: val)
if asset.isExportable {
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A)
let exportPath: NSString = NSTemporaryDirectory().appendingFormat("/\(UUID().uuidString).m4a") as NSString
let exportUrl: NSURL = NSURL.fileURL(withPath: exportPath as String) as NSURL
exportSession?.outputURL = exportUrl as URL
exportSession?.outputFileType = AVFileTypeAppleM4A
exportSession?.exportAsynchronously(completionHandler: {
// do some stuff with the file
do {
try FileManager.default.removeItem(atPath: exportPath as String!)
} catch {
}
}