// // MPPositive_PlayerLoadViewModel.swift // MusicPlayer // // Created by Mr.Zhou on 2024/5/9. // import UIKit ///播放器管理ViewModel class MPPositive_PlayerLoadViewModel: NSObject { /// 单曲常规列表 var songVideos:[MPPositive_SongItemModel]! ///随机播放列表 var randomVideos:[MPPositive_SongItemModel]! ///当前播放音乐ViewModel var currentVideo:MPPositive_SongViewModel!{ willSet{ DispatchQueue.main.asyncAfter(deadline: .now()) { [weak self] in guard let self = self else {return} if newValue != nil { MP_AnalyticsManager.shared.player_b_pvAction(newValue.song.videoId, videoname: newValue.title ?? "", artistname: newValue.song.shortBylineText ?? "") if currentVideo != nil { //当值变化时通知播放器页面,更新UI NotificationCenter.notificationKey.post(notificationName: .positive_player_reload, object: currentVideo) }else { //当值变化时通知播放器页面,更新UI NotificationCenter.notificationKey.post(notificationName: .positive_player_reload) } } } } } ///单曲播放队列ViewModel var listViewVideos:[MPPositive_SongViewModel]! ///异步请求组 var group:DispatchGroup? ///palyer管理model初始化方法 /// - Parameters: /// - songs: 全部歌曲列表 /// - firstVideoId: 需要播放的第一首歌 init(_ songs:[MPPositive_SongItemModel], currentVideoId: String) { super.init() self.songVideos = songs //根据列表生成一份随机播放列表 self.randomVideos = self.songVideos.shuffled() self.listViewVideos = [] } ///将选中Video的上1位,下两位项包括本身总计4项Video进行补全转为ViewModel,并播放这首音乐 func improveData(_ targetVideoId:String, isRandom:Bool = false) { //对于选中Video的集合 var array:[MPPositive_SongItemModel] = [] if isRandom { //获取targetVideoId的索引 guard let targetIndex = self.randomVideos.firstIndex(where: {$0.videoId == targetVideoId}) else { return } array.append(self.randomVideos[targetIndex]) //获取上一位 let previousIndex = targetIndex-1 if previousIndex >= 0 { array.append(self.randomVideos[previousIndex]) } let nextIndex = targetIndex+1 let lastIndex = targetIndex+2 if nextIndex < randomVideos.count { array.append(self.randomVideos[nextIndex]) } if lastIndex < randomVideos.count { array.append(self.randomVideos[lastIndex]) } }else { //获取targetVideoId的索引 guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else { return } array.append(self.songVideos[targetIndex]) //获取上一位 let previousIndex = targetIndex-1 if previousIndex >= 0 { array.append(self.songVideos[previousIndex]) } let nextIndex = targetIndex+1 let lastIndex = targetIndex+2 if nextIndex < songVideos.count { array.append(self.songVideos[nextIndex]) } if lastIndex < songVideos.count { array.append(self.songVideos[lastIndex]) } } //获取完成,优先检索ViewModel,看看是否已存在补完video let videoIDs = Set(listViewVideos.map({$0.song.videoId})) //比较videoID,去掉已经补完的内容 array = array.filter({!videoIDs.contains($0.videoId)}) group = DispatchGroup() //去重完毕,对剩下内容补完 for item in array { group?.enter() //补全歌词id和相关内容id if item.lyricsID == nil || item.relatedID == nil { improveDataforLycirsAndRelated(item) {[weak self] (result) in item.lyricsID = result.0 item.relatedID = result.1 self?.group?.leave() } }else { self.group?.leave() } group?.enter() //判断当前videoID是否进行过下载 if let resource = getDocumentsFileURL(item.videoId) { //下载过,resource直接填入 item.resourceUrls = [resource] //补全完成,转化为ViewModel,并添加进listViewVideos listViewVideos.append(.init(item)) self.group?.leave() }else { //没有下载过 //补全资源路径组和封面路径组 improveDataforResouceAndCover(item) {[weak self] resourceUrls, coverUrls in if let resourceUrls = resourceUrls { item.resourceUrls = resourceUrls.0 item.itags = resourceUrls.1 item.mimeTypes = resourceUrls.2 } item.coverUrls = coverUrls //补全完成,转化为ViewModel,并添加进listViewVideos self?.listViewVideos.append(.init(item)) self?.group?.leave() } } } group?.notify(queue: .main, execute: { [weak self] in //确定播放音乐 self?.currentVideo = self?.listViewVideos.first(where: {$0.song.videoId == targetVideoId}) //只保留最后四首 self?.listViewVideos = self?.listViewVideos.suffix(4) self?.group = nil }) } ///重新获取指定歌曲资源 func remakeImproveData(_ completion:@escaping (() -> Void)) { //当前歌曲不能播放,需要重新配置资源 improveDataforResouceAndCover(currentVideo.song) {[weak self] resourceUrls, coverUrls in guard let self = self else {return} if let resourceUrls = resourceUrls { currentVideo.song.resourceUrls = resourceUrls.0 currentVideo.song.itags = resourceUrls.1 currentVideo.song.mimeTypes = resourceUrls.2 } //成功更新资源,将重新补完的歌曲,放进listViewVideos中 listViewVideos.forEach({ item in if item.song.videoId == self.currentVideo.song.videoId { item.song.resourceUrls = self.currentVideo.song.resourceUrls item.song.itags = self.currentVideo.song.itags item.song.mimeTypes = self.currentVideo.song.mimeTypes } }) // currentVideo.resourceAsset = .init(url: .init(string: currentVideo.song.resourceUrls!.first!)!) // currentVideo.resourcePlayerItem = .init(asset: currentVideo.resourceAsset!) // currentVideo.resourcePlayerItem = .init(url: .init(string: (currentVideo.song.resourceUrls?.first ?? ""))!, bitrate: Int64(currentVideo.song.bitrates?.first ?? 0), title: currentVideo.title, videoId: currentVideo.song.videoId) currentVideo.configure() //当值变化时通知播放器页面,更新UI NotificationCenter.notificationKey.post(notificationName: .positive_player_reload) completion() } } ///移除选中的song,并更新listViewVideos,移除相同index的值 func removeData(_ targetVideoId:String) { let targetIndex = songVideos.firstIndex(where: {$0.videoId == targetVideoId}) ?? 0 //将选中的音乐移除,同时更新listView songVideos = songVideos.filter({$0.videoId != targetVideoId}) randomVideos = randomVideos.filter({$0.videoId != targetVideoId}) listViewVideos = listViewVideos.filter({$0.song.videoId != targetVideoId}) if currentVideo != nil { //判断是否当前音乐 if currentVideo.song.videoId == targetVideoId { //判断targetIndex是否大于最大音乐值 if targetIndex < songVideos.count { let videoId = songVideos[targetIndex].videoId ?? "" improveData(videoId) }else { //移除的是原来最后一首音乐,播放新的最后一首音乐 let videoId = songVideos.last?.videoId ?? "" improveData(videoId) } } } } //判断当前videoId是不是下载过 private func findVideoIdForDocument(_ videoId:String) -> Bool { return MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", videoId)).count != 0 } }