// // AddViewController.swift // MusicPlayer // // Created by Mr.Zhou on 2024/3/27. // import UIKit import AVFoundation import Photos class MPSideA_AddViewController: UIViewController { @IBOutlet weak var firstBtn: UIButton!{ didSet{ firstBtn.setTitle("Pick from photo Library".localizableString(), for: .normal) } } @IBOutlet weak var secondBtn: UIButton!{ didSet{ secondBtn.setTitle("Pick from files".localizableString(), for: .normal) } } override func viewDidLoad() { super.viewDidLoad() //设置圆角 view.layer.masksToBounds = true view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] view.layer.cornerRadius = 18*width } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.notificationKey.add(observer: self, selector: #selector(successfullCreateAction), notificationName: .sideA_creat_music) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) } //前往相册获取音视频文件 @IBAction func action1Click(_ sender: UIButton) { getVideo() } //前往文件夹获取音视频文件 @IBAction func action2Click(_ sender: UIButton) { var documentPicker:UIDocumentPickerViewController? if #available(iOS 14, *) { //访问原始路径,展示类型为视频,音频 documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.mp3,.mpeg4Movie,.mpeg4Audio, .audio, .movie], asCopy: false) }else { //允许展示的数据格式(视频,音频) let types:[String] = ["public.movie","public.audio"] // 创建并配置UIDocumentPickerViewController实例 documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .open) } documentPicker!.delegate = self documentPicker!.modalPresentationStyle = .formSheet // 展示文档选择器 present(documentPicker!, animated: true) } //创建一个新的音乐实体 private func createMusic(_ title:String, duration:Double, cover:UIImage, url:URL) { //创建一个沙盒地址存放视频/音频 let number = Int(arc4random_uniform(8999) + 1000) let path = "\(number)\(Date().timeZone().toString(.custom("HH:mm:ss-MMMdd-YYYY")))\(title)" //视频沙盒保存地址 let videoSaveUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!).appendingPathComponent(path).path let urlAsset = AVURLAsset(url:url) let session = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetHighestQuality) session?.outputURL = URL(fileURLWithPath: videoSaveUrl) session?.outputFileType = .mp4 session?.exportAsynchronously(completionHandler: { let exportState = session?.status switch exportState{ case .failed: print("Export Failed:\(String(describing: session?.error))") break case .completed: //导出完成 print("Export Complete") break default: break } }) //处理完成 let music = MPSideA_MusicModel.create() music.identifier = Date().timeZone().toString(.custom("HH:mm:ss/MM-dd/YYYY")) //获取路径最后一个组件作为标题 music.title = title music.duration = duration music.cover = cover.pngData()! music.isLocal = false music.path = path music.creationTime = Date().timeZone() music.lastTime = nil MPSideA_MusicModel.save() print("Resources have been successfully accessed") } @objc private func successfullCreateAction() { DispatchQueue.main.async { [weak self] in self?.dismiss(animated: true) } } } //MARK: - 相册与文件选择处理 extension MPSideA_AddViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate, UIDocumentPickerDelegate { //取消 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) { print("Failure to complete selection event") } } //获取选择的图像内容 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { picker.dismiss(animated: true) {[weak self] in let videoUrl = info[.mediaURL] as! URL print(videoUrl) //根据路径设置图片 let asset = AVURLAsset(url: videoUrl, options: nil) print("Select the real path of the file:\(videoUrl)") let data = self?.setPathToData(videoUrl, asset: asset) self?.createMusic(data!.2, duration: data!.0, cover: data!.1, url: videoUrl) NotificationCenter.notificationKey.post(notificationName: .sideA_creat_music) // //更新选择视频 // picker.dismiss(animated: true) { // [weak self] in // //获取资源库信息 // guard let passet = info[.phAsset] as? PHAsset else { // print("Selected resources failed to load") // return // } // //视频数据选项 // let options:PHVideoRequestOptions = .init() // //视频保持原始版本 // options.version = .original // options.deliveryMode = .automatic // //开通网络访问权限 //// options.isNetworkAccessAllowed = true // PHImageManager.default().requestAVAsset(forVideo: passet, options: options) { [weak self] (asset, audioMix, info) in // var videoUrl:URL? // guard let urlAsset = asset as? AVURLAsset else { // print("Selected resources failed to load") // return // } // videoUrl = urlAsset.url as URL // print("Select the real path of the file:\(videoUrl!)") // let data = self?.setPathToData(videoUrl!, asset: urlAsset) // self?.createMusic(data!.2, duration: data!.0, cover: data!.1, path: videoUrl!) // NotificationCenter.notificationKey.post(notificationName: .creat_music) // } // } } } //文件选择 func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let selectedFileURL = urls.first else { return } progressData(selectedFileURL) } func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { // 用户取消选取 print("Failure to complete selection event") } //安全协议加载用户选择的数据 private func progressData(_ url:URL) { //获取是否能够安全访问路径权限 let authozied = url.startAccessingSecurityScopedResource() if authozied == true { //允许安全访问,通过文件协调器读取文件真实地址 let fileCoordinator = NSFileCoordinator() fileCoordinator.coordinate(readingItemAt: url, options: .withoutChanges, error: nil) { [weak self] (newUrl) in // 处理选取的音频文件 print("Select the real path of the file:\(newUrl)") let data = self?.setPathToData(newUrl, asset: .init(url: newUrl)) self?.createMusic(data!.2, duration: data!.0, cover: data!.1, url: newUrl) NotificationCenter.notificationKey.post(notificationName: .sideA_creat_music) } }else { print("Selected resources failed to load") } //停止安全访问权限 url.stopAccessingSecurityScopedResource() } //根据真实路径获取视频/音频长度,第一帧,标题 private func setPathToData(_ url:URL, asset:AVURLAsset) -> (TimeInterval,UIImage,String){ //长度 var duration:TimeInterval = 0 let time = asset.duration // if #available(iOS 16, *) { // do { // time = try await asset.load(.duration) // } catch { // print("Conversion to time failed") // } // }else { // time = asset.duration // } duration = CMTimeGetSeconds(time) //获取第一帧 let gen = AVAssetImageGenerator(asset: asset) gen.appliesPreferredTrackTransform = true //获取时间点 let firstTime = CMTimeMakeWithSeconds(0.0, preferredTimescale: 1) var actualTime:CMTime = CMTimeMakeWithSeconds(0, preferredTimescale: 0) var firstImage:UIImage! do { //获取第一帧作为封面 let image = try gen.copyCGImage(at: firstTime, actualTime: &actualTime) firstImage = UIImage(cgImage: image) } catch { //获取失败 print("Failed to get file the first image") //给予初始值 firstImage = .init(named: "End Of Days") } //获取地址最后一组内容作为标题 let title:String = url.lastPathComponent return (duration,firstImage,title) } }