// // MPPositive_MoreOperationsViewController.swift // MusicPlayer // // Created by Mr.Zhou on 2024/5/29. // import UIKit import Kingfisher ///更多操作 class MPPositive_MoreSongOperationsViewController: UIViewController, UIViewControllerTransitioningDelegate { //小角标图片 private lazy var indictorImageView:UIImageView = .init(image: .init(named: "Player_Indictor'logo")) //展示ICON private lazy var iconImageView:UIImageView = { let imageView:UIImageView = .init() imageView.contentMode = .scaleAspectFill imageView.layer.masksToBounds = true imageView.layer.cornerRadius = 10*width return imageView }() //标题Label private lazy var titleLabel:UILabel = createLabel("Title", font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left) //副标题 private lazy var subtitleLabel:UILabel = createLabel("Title", font: .systemFont(ofSize: 12*width, weight: .regular), textColor: .init(hex: "#666666"), textAlignment: .left) //是否收藏 private lazy var collectionBtn:UIButton = { let btn = UIButton() btn.setImage(UIImage(named: "Artist_Collection'logo"), for: .normal) btn.setImage(UIImage(named: "Artist_Collectioned'logo"), for: .selected) btn.setBackgroundImage(UIImage(named: "Artist_Collection'bg"), for: .normal) btn.setBackgroundImage(UIImage(named: "Artist_Collectioned'bg"), for: .selected) btn.addTarget(self, action: #selector(collectionClick(_ :)), for: .touchUpInside) return btn }() //分割线 private lazy var lineView:UIView = { let lineView:UIView = UIView() lineView.backgroundColor = .init(hex: "#444444") return lineView }() //展示tableView private lazy var tableView:UITableView = { let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain) tableView.backgroundColor = .clear tableView.separatorStyle = .none tableView.estimatedRowHeight = 200 tableView.rowHeight = UITableView.automaticDimension tableView.dataSource = self tableView.delegate = self tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID) tableView.register(MPPositive_MoreOperationShowTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationShowTableViewCellID) return tableView }() private let MPPositive_MoreOperationDownLoadTableViewCellID = "MPPositive_MoreOperationDownLoadTableViewCell" private let MPPositive_MoreOperationShowTableViewCellID = "MPPositive_MoreOperationShowTableViewCell" private var song:MPPositive_SongItemModel!{ didSet{ DispatchQueue.main.async { [weak self] in guard let self = self else {return} // MP_HUD.hideNow() view.isUserInteractionEnabled = true iconImageView.kf.setImage(with: URL(string: song.reviewUrls?.last ?? ""), placeholder: placeholderImage) titleLabel.text = song.title subtitleLabel.text = song.shortBylineText //判断该单曲是否收藏 MPPositive_CollectionSongModel.fetch(predicate: .init(format: "videoId == %@", (song.videoId ?? ""))) { [weak self] results in guard let self = self else {return} collectionBtn.isSelected = results.count != 0 } tableView.reloadData() } } } private var playList:MPPositive_CustomPlayListModel? ///是否展示下载/移除下载项 private var isShowDownload:Bool { if let statu = UserDefaults.standard.object(forKey: "isShowDownload") as? Bool { return statu }else { return true } } var removeBlock:(() -> Void)? var disMissBlock:(() -> Void)? var collectionBlock:(() -> Void)? init(_ song:MPPositive_SongItemModel) { super.init(nibName: nil, bundle: nil) DispatchQueue.main.async { [weak self] in self?.song = song } } //通过browseViewModel加载更多框 init(_ browseViewModel:MPPositive_BrowseItemViewModel) { super.init(nibName: nil, bundle: nil) view.isUserInteractionEnabled = false DispatchQueue.main.async { [weak self] in guard let self = self else {return} // MP_HUD.loading() //发起网络请求补全数据 MP_NetWorkManager.shared.requestNextList(browseViewModel.browseItem.playListId ?? "", videoId: browseViewModel.browseItem.videoId ?? "", clickTrackingParams: browseViewModel.browseItem.clickTrackingParams) { [weak self] listSongs in guard let first = listSongs.first(where: {$0.videoId == (browseViewModel.browseItem.videoId ?? "")}) else {return} let group = DispatchGroup() group.enter() improveDataforLycirsAndRelated(first) {[weak self] (result) in first.lyricsID = result.0 first.relatedID = result.1 group.leave() } group.enter() //补全资源路径组和封面路径组 improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in if let resourceUrls = resourceUrls { first.resourceUrls = resourceUrls.0 first.itags = resourceUrls.1 first.mimeTypes = resourceUrls.2 } first.coverUrls = coverUrls group.leave() } failure: {_ in group.leave() } group.notify(queue: .main, execute: { [weak self] in //补全了数据 guard let self = self else {return} song = first }) } } } //通过SearchResultItemViewModel加载更多框 init(_ searchResultItemViewModel:MPPositive_SearchResultItemViewModel) { super.init(nibName: nil, bundle: nil) view.isUserInteractionEnabled = false DispatchQueue.main.async { [weak self] in guard let self = self else {return} // MP_HUD.loading() //发起网络请求补全数据 MP_NetWorkManager.shared.requestNextList(searchResultItemViewModel.item.playListId ?? "", videoId: searchResultItemViewModel.item.videoId ?? "", clickTrackingParams: searchResultItemViewModel.item.clickTrackingParams) { [weak self] listSongs in guard let first = listSongs.first(where: {$0.videoId == searchResultItemViewModel.item.videoId}) else {return} let group = DispatchGroup() group.enter() improveDataforLycirsAndRelated(first) {[weak self] (result) in first.lyricsID = result.0 first.relatedID = result.1 group.leave() } group.enter() //补全资源路径组和封面路径组 improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in if let resourceUrls = resourceUrls { first.resourceUrls = resourceUrls.0 first.itags = resourceUrls.1 first.mimeTypes = resourceUrls.2 } first.coverUrls = coverUrls group.leave() } failure: {_ in group.leave() } group.notify(queue: .main, execute: { [weak self] in //补全了数据 guard let self = self else {return} song = first }) } } } //通过collectionSongViewModel加载更多框 init(_ collectionSongViewModel:MPPositive_CollectionSongViewModel) { super.init(nibName: nil, bundle: nil) view.isUserInteractionEnabled = false DispatchQueue.main.async { [weak self] in guard let self = self else {return} // MP_HUD.loading() //发起网络请求补全数据 MP_NetWorkManager.shared.requestNextList("", videoId: collectionSongViewModel.collectionSong.videoId ?? "", clickTrackingParams: nil){ [weak self] listSongs in guard let first = listSongs.first(where: {$0.videoId == collectionSongViewModel.collectionSong.videoId}) else {return} let group = DispatchGroup() group.enter() improveDataforLycirsAndRelated(first) {[weak self] (result) in first.lyricsID = result.0 first.relatedID = result.1 group.leave() } group.enter() //补全资源路径组和封面路径组 improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in if let resourceUrls = resourceUrls { first.resourceUrls = resourceUrls.0 first.itags = resourceUrls.1 first.mimeTypes = resourceUrls.2 } first.coverUrls = coverUrls group.leave() } failure: {_ in group.leave() } group.notify(queue: .main, execute: { [weak self] in //补全了数据 guard let self = self else {return} song = first }) } } } //从自定义歌单弹出 init(_ playList:MPPositive_CustomPlayListModel, video:MPPositive_CustomVideoModel?) { super.init(nibName: nil, bundle: nil) view.isUserInteractionEnabled = false DispatchQueue.main.async { [weak self] in guard let self = self else {return} self.playList = playList // MP_HUD.loading() //发起网络请求补全数据 MP_NetWorkManager.shared.requestNextList("", videoId: video?.videoId ?? "", clickTrackingParams: nil){ [weak self] listSongs in guard let first = listSongs.first(where: {$0.videoId == video?.videoId}) else {return} let group = DispatchGroup() group.enter() improveDataforLycirsAndRelated(first) {[weak self] (result) in first.lyricsID = result.0 first.relatedID = result.1 group.leave() } group.enter() //补全资源路径组和封面路径组 improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in if let resourceUrls = resourceUrls { first.resourceUrls = resourceUrls.0 first.itags = resourceUrls.1 first.mimeTypes = resourceUrls.2 } first.coverUrls = coverUrls group.leave() } failure: {_ in group.leave() } group.notify(queue: .main, execute: { [weak self] in //补全了数据 guard let self = self else {return} song = first }) } } } required init?(coder: NSCoder) { super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .init(hex: "#282A2C") view.layer.masksToBounds = true view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner] view.layer.cornerRadius = 18*width DispatchQueue.main.asyncAfter(deadline: .now() + 1) { if MP_NetWorkManager.shared.netWorkStatu != .reachable { MP_HUD.onlytext("Bad connection~", delay: 1.0, completion: nil) } } configure() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if disMissBlock != nil { disMissBlock!() } } private func configure() { view.addSubview(indictorImageView) indictorImageView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.width.equalTo(29*width) make.height.equalTo(4*width) make.top.equalToSuperview().offset(8*width) } view.addSubview(iconImageView) iconImageView.snp.makeConstraints { make in make.width.height.equalTo(50*width) make.left.equalToSuperview().offset(18*width) make.top.equalToSuperview().offset(32*width) } view.addSubview(collectionBtn) collectionBtn.snp.makeConstraints { make in make.width.height.equalTo(32*width) make.centerY.equalTo(iconImageView) make.right.equalToSuperview().offset(-18*width) } view.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(iconImageView).offset(8*width) make.left.equalTo(iconImageView.snp.right).offset(12*width) make.right.equalTo(collectionBtn.snp.left).offset(-12*width) } view.addSubview(subtitleLabel) subtitleLabel.snp.makeConstraints { make in make.left.right.equalTo(titleLabel) make.bottom.equalTo(iconImageView).offset(-8*width) } view.addSubview(lineView) lineView.snp.makeConstraints { make in make.width.equalTo(339*width) make.height.equalTo(1*width) make.centerX.equalToSuperview() make.top.equalTo(iconImageView.snp.bottom).offset(15*width) } view.addSubview(tableView) tableView.snp.makeConstraints { make in make.top.equalTo(lineView.snp.bottom).offset(8*width) make.left.right.bottom.equalToSuperview() } } //是否收藏 @objc private func collectionClick(_ sender:UIButton) { if self.collectionBtn.isSelected == true{ self.collectionBtn.isSelected = false MPPositive_CollectionSongModel.fetch(predicate: .init(format: "videoId == %@", (song.videoId ?? ""))) { [weak self] result in guard let self = self else {return} result.forEach { item in if item.videoId == self.song.videoId { MPPositive_CollectionSongModel.delete(item) } } MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) } MP_AnalyticsManager.shared.player_b_unlove_clickAction(song.videoId ?? "", videoname: song.title ?? "", artistname: song.shortBylineText ?? "") }else{ self.collectionBtn.isSelected = true let item = try? MPPositive_CollectionSongModel.create() item?.coverImage = URL(string: song.reviewUrls?.last ?? "") item?.title = song.title item?.subtitle = song.shortBylineText item?.videoId = song.videoId item?.lyricsID = song.lyricsID item?.relatedID = song.relatedID item?.addTime = Date() MPPositive_CollectionSongModel.save() MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) MP_AnalyticsManager.shared.player_b_love_clickAction(song.videoId ?? "", videoname: song.title ?? "", artistname: song.shortBylineText ?? "") } if MP_PlayerManager.shared.loadPlayer?.currentVideo != nil { MP_PlayerManager.shared.loadPlayer?.currentVideo?.reloadCollectionAndDownLoad() } if collectionBlock != nil { collectionBlock!() } } } //MARK: - tableView extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return isShowDownload ? 4:3 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if isShowDownload { switch indexPath.row { case 0: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell if let song = song { cell.song = song } return cell default: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationShowTableViewCellID, for: indexPath) as! MPPositive_MoreOperationShowTableViewCell switch indexPath.row { case 1://添加歌单/移除歌单 cell.title = playList != nil ? "Remove from playlist":"Add to playlist" case 2: cell.title = "Play next" default://举报 cell.title = "Report" } return cell } }else { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationShowTableViewCellID, for: indexPath) as! MPPositive_MoreOperationShowTableViewCell switch indexPath.row { case 0: cell.title = playList != nil ? "Remove from playlist":"Add to playlist" case 1: cell.title = "Play next" default: cell.title = "Report" } return cell } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if isShowDownload { switch indexPath.row { case 0: let cell = tableView.cellForRow(at: indexPath) as? MPPositive_MoreOperationDownLoadTableViewCell switch cell?.loadBtn.state { case .startDownload://开始状态,点击后进入下载状态 //检索当前网络状态 guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { MP_HUD.text("Bad connection~".localizableString(), delay: 2.0, completion: nil) return } //检索当前歌曲是否下载过 guard let song = song else { return } //切换为准备状态 cell?.loadBtn.state = .pending //事件打点 MP_AnalyticsManager.shared.player_b_download_clickAction(song.videoId ?? "", videoname: song.title ?? "", artistname: song.shortBylineText ?? "") //补全了数据,执行下载 MP_DownloadManager.shared.prepareVideoDownloadTask(from: song) case .downloaded://已下载,点击删除下载 let alertController = UIAlertController(title: "Delete This Song".localizableString(), message: "Are you sure you want to delete the offline resources of this song?".localizableString(), preferredStyle: .alert) let cancel = UIAlertAction(title: "Cancel".localizableString(), style: .cancel) alertController.addAction(cancel) let sure = UIAlertAction(title: "Confirm".localizableString(), style: .destructive) {[weak self](action) in guard let videoId = self?.song?.videoId else {return} //确定删除 MP_DownloadManager.shared.deleteFileDocuments(videoId) { videoId in MP_HUD.progress("Loading...".localizableString(), delay: 0.5) { MP_HUD.text("Removed".localizableString(), delay: 1.0, completion: nil) tableView.reloadData() self?.dismiss(animated: true) } } } alertController.addAction(sure) present(alertController, animated: true) default://取消下载 //点击取消下载 let alertController = UIAlertController(title: "Cancel Song Download Task".localizableString(), message: "Are you sure you want to cancel the download task of this song?".localizableString(), preferredStyle: .alert) let cancel = UIAlertAction(title: "Cancel".localizableString(), style: .cancel) alertController.addAction(cancel) let sure = UIAlertAction(title: "Confirm".localizableString(), style: .destructive) {[weak self](action) in guard let videoId = self?.song?.videoId else {return} //确定取消 MP_DownloadManager.shared.cancelDownloadTask(videoId) { videoId in MP_HUD.text("Cancel".localizableString(), delay: 1.0, completion: nil) tableView.reloadData() } } alertController.addAction(sure) present(alertController, animated: true) } if MP_PlayerManager.shared.loadPlayer?.currentVideo != nil { MP_PlayerManager.shared.loadPlayer?.currentVideo?.reloadCollectionAndDownLoad() } case 1://添加歌单/移除歌单 if playList != nil { //当前有歌单 //从歌单中移除 let array = playList?.videosArray.filter({$0.videoId != song.videoId}) playList?.addToRelationshipToCustomVideos(array ?? []) MPPositive_CustomPlayListModel.save() //重新设置 MPPositive_LoadCoreModel.shared.reloadCustomPlayLists(nil) dismiss(animated: true) { [weak self] in MP_HUD.onlytext("Success".localizableString(), delay: 1.0, completion: nil) } }else { //选择歌单 MPPositive_ModalType = .MoreOperations let chooseVC = MPPositive_ChoosePlayListViewController(song) chooseVC.disMissBlock = { [weak self] in self?.dismiss(animated: true) } chooseVC.transitioningDelegate = self chooseVC.modalPresentationStyle = .custom present(chooseVC, animated: true) } case 2://下一首播放 //检索播放列表,如果不存在播放列表,则禁止下一首播放 guard let load = MP_PlayerManager.shared.loadPlayer else { //不存在播放列表,禁止下一首播放 MP_HUD.onlytext("There is no playlist currently playing".localizableString(), delay: 1.0, completion: nil) return } //存在播放列表,判断当前歌曲是否存在于播放列表 guard load.songVideos?.contains(where: {$0.videoId == song.videoId}) == false else { //存在于播放列表中,不可再度添加 MP_HUD.onlytext("The current playlist already exists".localizableString(), delay: 1.0, completion: nil) return } if let s = song { MP_PlayerManager.shared.loadPlayer?.playNextAction(s) } default://举报 MP_HUD.onlytext("Your report has been submitted and we will process it as soon as possible.".localizableString(), delay: 1.0, completion: nil) } }else { switch indexPath.row { case 0: if playList != nil { //当前有歌单 //从歌单中移除 let array = playList?.videosArray.filter({$0.videoId != song.videoId}) playList?.addToRelationshipToCustomVideos(array ?? []) MPPositive_CustomPlayListModel.save() //重新设置 MPPositive_LoadCoreModel.shared.reloadCustomPlayLists(nil) dismiss(animated: true) { [weak self] in MP_HUD.onlytext("Success".localizableString(), delay: 1.0, completion: nil) } }else { //选择歌单 MPPositive_ModalType = .MoreOperations let chooseVC = MPPositive_ChoosePlayListViewController(song) chooseVC.disMissBlock = { [weak self] in self?.dismiss(animated: true) } chooseVC.transitioningDelegate = self chooseVC.modalPresentationStyle = .custom present(chooseVC, animated: true) } case 1: //检索播放列表,如果不存在播放列表,则禁止下一首播放 guard let load = MP_PlayerManager.shared.loadPlayer else { //不存在播放列表,禁止下一首播放 MP_HUD.onlytext("There is no playlist currently playing".localizableString(), delay: 1.0, completion: nil) return } //存在播放列表,判断当前歌曲是否存在于播放列表 guard load.songVideos?.contains(where: {$0.videoId == song.videoId}) == false else { //存在于播放列表中,不可再度添加 MP_HUD.onlytext("The current playlist already exists".localizableString(), delay: 1.0, completion: nil) return } if let s = song { MP_PlayerManager.shared.loadPlayer?.playNextAction(s) } default: MP_HUD.onlytext("Your report has been submitted and we will process it as soon as possible.".localizableString(), delay: 1.0, completion: nil) } } } func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) } }