使用Swift,是否可以将iPod库中的音乐保存到我的应用程序中?

9

当用户从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代码访问媒体库?

2个回答

10

参考 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
}

你可以看到,我将它放在了一个临时文件夹而不是文档文件夹中。此外,我使用 UUID 而不是自某个参考日期以来的秒数来生成临时文件。但基本思想是相同的。


非常感谢。我已经搜索了过去两周的代码...最终,我从你这里得到了它,并且它正常运行。非常感谢。我有一个小问题,当我检查exporter.status == .Completed并获取数据时,数据大小太大了,我怎样才能在不降低质量的情况下减小它呢? - user6777252
这是音频文件的本质,除非它只是一个非常短的剪辑(几秒钟),否则它们往往非常大。而且通常情况下,您不能在不降低质量和/或缩小提取的“timeRange”的情况下显著减小大小。坦率地说,出于这个原因,您通常不会将音乐存储在NSData/Data对象中(因为它们同时保存在内存中),而是通常流式传输音乐文件。 - Rob
实际上,我正在使用卡拉OK应用程序工作,所以我必须将iTunes选择的歌曲上传到PHP服务器。如果我上传低内存,它可以正常工作。但是,如果我上传iTunes歌曲,它就无法上传。因为内存太高了。有什么解决办法吗? - user6777252
使用URLSession的uploadTask方法上传文件,而不是NSData。 - Rob
很棒的答案!只需注意completionHandler(fileURL, nil)需要在DispatchQueue.main.async{}中调用。 - fullmoon
@Rob,你能更新一下你的Swift 5的答案吗?如果我想要将文件导出成mp3格式,或者更好的是以原本在音乐库中的格式导出,我需要怎么做? - VyacheslavBakinkskiy

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;
             }
         }
     }];

}

好的,我会用 Swift 检查并告诉你。 - user6777252
什么是数组音乐?[arrayMusic addObject:data]; 的意思是什么? - user6777252
我之前只是为了存储多个项目而使用了数组。你可以直接使用数据并上传它。 - Krishna Datt Shukla
嗨,Krishna,它显示如下: AVAssetExportSessionStatusFailed: Optional(Error Domain=NSURLErrorDomain Code=-3000 "Cannot create file" UserInfo={NSLocalizedDescription=Cannot create file, NSUnderlyingError=0x174259440 {Error Domain=NSOSStatusErrorDomain Code=-12115 "(null)"}}) - user6777252
你需要压缩数据。你可以尝试这个链接 - https://github.com/leemorgan/NSData-Compression - Krishna Datt Shukla
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接