【问题标题】:Is it possible to save a music from the Ipod library into my app with swift?是否可以使用 swift 将 Ipod 库中的音乐保存到我的应用程序中?
【发布时间】:2016-12-02 04:40:40
【问题描述】:

当用户从 iPod 库中选择音频时,我有一个 MPMediaItem 的引用。我正在使用

获取该项目的资产 URL
 let url = item.valueForProperty(MPMediaItemPropertyAssetURL)

但这并没有给我文件的确切物理位置,而是给我一个带有 iPod 库的 URL。

ipod-library://item/item.mp3?id=1840064795502796074

有没有办法从 iPod 库中获取歌曲的物理 URL?

编辑 - 实际上我想从物理文件中提取 NSData 并将其发送到我的后端服务器,所以我需要物理文件 URL 而不是相对 URL

MPmediaPickerController 正在工作,我选择了歌曲并播放,但我不想播放歌曲。我已尝试将音频文件上传到服务器。我在列表音频中使用了 MPMedia Picker 视图,当我要选择将上传到服务器(HTTP)的音频时,我该怎么做???如何使用 Swift 代码访问媒体库?

【问题讨论】:

    标签: ios swift audio-player ituneslibrary


    【解决方案1】:

    改编 Krishna 的答案,它使用 AVAssetExportSessionMPMediaItem 保存到文件中,您可以在 Swift 3 中执行以下操作:

    /// Export MPMediaItem to temporary file.
    ///
    /// - Parameters:
    ///   - assetURL: The `assetURL` of the `MPMediaItem`.
    ///   - completionHandler: Closure to be called when the export is done. The parameters are a boolean `success`, the `URL` of the temporary file, and an optional `Error` if there was any problem. The parameters of the closure are:
    ///
    ///   - fileURL: The `URL` of the temporary file created for the exported results.
    ///   - error: The `Error`, if any, of the asynchronous export process.
    
    func export(_ assetURL: URL, completionHandler: @escaping (_ fileURL: URL?, _ error: Error?) -> ()) {
        let asset = AVURLAsset(url: assetURL)
        guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
            completionHandler(nil, ExportError.unableToCreateExporter)
            return
        }
    
        let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
            .appendingPathComponent(NSUUID().uuidString)
            .appendingPathExtension("m4a")
    
        exporter.outputURL = fileURL
        exporter.outputFileType = "com.apple.m4a-audio"
    
        exporter.exportAsynchronously {
            if exporter.status == .completed {
                completionHandler(fileURL, nil)
            } else {
                completionHandler(nil, exporter.error)
            }
        }
    }
    
    func exampleUsage(with mediaItem: MPMediaItem) {
        if let assetURL = mediaItem.assetURL {
            export(assetURL) { fileURL, error in
                guard let fileURL = fileURL, error == nil else {
                    print("export failed: \(error)")
                    return
                }
    
                // use fileURL of temporary file here
                print("\(fileURL)")
            }
        }
    }
    
    enum ExportError: Error {
        case unableToCreateExporter
    }
    

    或者,在 Swift 2 中:

    /// Export MPMediaItem to temporary file.
    ///
    /// - Parameters:
    ///   - assetURL: The `assetURL` of the `MPMediaItem`.
    ///   - completionHandler: Closure to be called when the export is done. The parameters are a boolean `success`, the `URL` of the temporary file, and an optional `Error` if there was any problem. The parameters of the closure are:
    ///
    ///   - fileURL: The `URL` of the temporary file created for the exported results.
    ///   - error: The `Error`, if any, of the asynchronous export process.
    
    func export(assetURL: NSURL, completionHandler: (NSURL?, ErrorType?) -> ()) {
        let asset = AVURLAsset(URL: assetURL)
        guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
            completionHandler(nil, ExportError.unableToCreateExporter)
            return
        }
    
        let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
            .URLByAppendingPathComponent(NSUUID().UUIDString)!
            .URLByAppendingPathExtension("m4a")
    
        exporter.outputURL = fileURL
        exporter.outputFileType = "com.apple.m4a-audio"
    
        exporter.exportAsynchronouslyWithCompletionHandler {
            if exporter.status == .Completed {
                completionHandler(fileURL, nil)
            } else {
                completionHandler(nil, exporter.error)
            }
        }
    }
    
    func exampleUsage(with mediaItem: MPMediaItem) {
        if let assetURL = mediaItem.assetURL {
            export(assetURL) { fileURL, error in
                guard let fileURL = fileURL where error == nil else {
                    print("export failed: \(error)")
                    return
                }
    
                // use fileURL of temporary file here
                print("\(fileURL)")
            }
        }
    }
    
    enum ExportError: ErrorType {
        case unableToCreateExporter
    }
    

    如您所见,我将它放在一个临时文件夹而不是 Documents 文件夹中。此外,我使用 UUID 而不是自某个参考日期以来的秒数来生成临时文件。但是思路基本一样。

    【讨论】:

    • 非常感谢。我搜索了过去两周的代码......最后,我从你那里得到它,它工作正常。非常感谢你。我有一个小小的疑问,我 exporter.status == .Completed 并获得了一个数据..数据太大了如何在没有歌曲质量的情况下减少它...
    • 这是音频文件的本质,它们往往非常大,除非它只是一个非常短的剪辑(几秒钟)。作为一般规则,您不能在不降低质量和/或减少提取的timeRange 的情况下显着减小大小。坦率地说,出于这个原因,您通常不会将音乐存储在 NSData/Data 对象中(因为它们同时保存在内存中),而是我们通常流式传输音乐文件。
    • 实际上,我正在使用卡拉 OK 应用程序,所以我必须将 iTunes 选择的歌曲上传到 PHP 服务器。如果我上传内存不足,它工作正常。但如果我上传 iTunes 歌曲,它不会上传。因为内存太大,有什么办法???
    • 将 URLSession uploadTask 用于文件而不是 NSData
    • 很好的答案!只是要注意,completionHandler(fileURL, nil) 需要在 DispatchQueue.main.async{ 中调用
    【解决方案2】:

    从库中选择歌曲后,将您的 MPMediaItem 对象转换为 NSData 并使用多部分表单数据将其上传到服务器。

    将 MPMediaItem 转换为 NSData

    -( void)mediaItemToData : (MPMediaItem * ) curItem
    {
        NSURL *url = [curItem valueForProperty: MPMediaItemPropertyAssetURL];
        AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL: url options:nil];
    
        AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: songAsset
                                                                          presetName:AVAssetExportPresetAppleM4A];
    
        exporter.outputFileType =   @"com.apple.m4a-audio";
    
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString * myDocumentsDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    
        [[NSDate date] timeIntervalSince1970];
        NSTimeInterval seconds = [[NSDate date] timeIntervalSince1970];
        NSString *intervalSeconds = [NSString stringWithFormat:@"%0.0f",seconds];
    
        NSString * fileName = [NSString stringWithFormat:@"%@.m4a",intervalSeconds];
    
        NSString *exportFile = [myDocumentsDirectory stringByAppendingPathComponent:fileName];
    
        NSURL *exportURL = [NSURL fileURLWithPath:exportFile];
        exporter.outputURL = exportURL;
    
        // do the export
        // (completion handler block omitted)
        [exporter exportAsynchronouslyWithCompletionHandler:
         ^{
             int exportStatus = exporter.status;
    
             switch (exportStatus)
             {
                 case AVAssetExportSessionStatusFailed:
                 {
                     NSError *exportError = exporter.error;
                     NSLog (@"AVAssetExportSessionStatusFailed: %@", exportError);
                     break;
                 }
                 case AVAssetExportSessionStatusCompleted:
                 {
                     NSLog (@"AVAssetExportSessionStatusCompleted");
    
                     NSData *data = [NSData dataWithContentsOfFile: [myDocumentsDirectory
                                                                     stringByAppendingPathComponent:fileName]];
    
                     [arrayMusic addObject:data];
                     data = nil;
    
                     break;
                 }
                 case AVAssetExportSessionStatusUnknown:
                 {
                     NSLog (@"AVAssetExportSessionStatusUnknown"); break;
                 }
                 case AVAssetExportSessionStatusExporting:
                 {
                     NSLog (@"AVAssetExportSessionStatusExporting"); break;
                 }
                 case AVAssetExportSessionStatusCancelled:
                 {
                     NSLog (@"AVAssetExportSessionStatusCancelled"); break;
                 }
                 case AVAssetExportSessionStatusWaiting:
                 {
                     NSLog (@"AVAssetExportSessionStatusWaiting"); break;
                 }
                 default:
                 {
                     NSLog (@"didn't get export status"); break;
                 }
             }
         }];
    
    }
    

    【讨论】:

    • 如果我选择一首歌曲,我会得到标题和 MPMediaItemPropertyAssetURLvalue。如果我转换成数据它是 nil...如何获取 MPMediaItem 的 NSData?
    • 好的..我会尽快入住并告诉你
    • 数组音乐是什么意思?[arrayMusic addObject:data]; ?
    • 我只将数组用于多个项目。您可以简单地使用数据并上传。
    • 嗨 Krishna,它的显示如下: AVAssetExportSessionStatusFailed: Optional(Error Domain=NSURLErrorDomain Code=-3000 "Cannot create file" UserInfo={NSLocalizedDescription=Cannot create file, NSUnderlyingError=0x174259440 {Error Domain=NSOSStatusErrorDomain Code =-12115 "(null)"}}) 这个
    猜你喜欢
    • 1970-01-01
    • 2012-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多