//
|
// PayMusicVC.swift
|
// XQMuse
|
//
|
// Created by 无故事王国 on 2024/8/22.
|
//
|
|
import UIKit
|
import JQTools
|
import AVFoundation
|
import MediaPlayer
|
|
class PayMusicVC: BaseVC {
|
|
private var coverImage:UIImageView!
|
private var label_name:UILabel!
|
private var btn_handle:UIButton!
|
private var audioPlayer:AudioPlayer!
|
|
private init() {
|
super.init(nibName: nil, bundle: nil)
|
}
|
|
internal required init?(coder: NSCoder) {
|
fatalError("init(coder:) has not been implemented")
|
}
|
|
override func viewDidLoad() {
|
super.viewDidLoad()
|
audioPlayer = AudioPlayer.getSharedInstance()
|
}
|
|
override func setUI() {
|
view.backgroundColor = UIColor(hexString: "#B1C998")
|
view.jq_cornerRadius = 23.25
|
|
coverImage = UIImageView(image: UIImage(named: "bg_home_1"))
|
coverImage.jq_cornerRadius = 18.25
|
coverImage.jq_borderColor = .black
|
coverImage.jq_borderWidth = 3.5
|
view.addSubview(coverImage)
|
coverImage.snp.makeConstraints { make in
|
make.left.equalTo(7.5)
|
make.width.height.equalTo(36.5)
|
make.centerY.equalToSuperview()
|
}
|
|
label_name = UILabel()
|
label_name.text = "--"
|
label_name.font = .systemFont(ofSize: 14.8, weight: .bold)
|
label_name.textColor = .white
|
view.addSubview(label_name)
|
label_name.snp.makeConstraints { make in
|
make.left.equalTo(self.coverImage.snp.right).offset(17.5)
|
make.centerY.equalToSuperview()
|
}
|
|
btn_handle = UIButton(type: .custom)
|
btn_handle.setImage(UIImage(named: "icon_play_purse"), for: .normal)
|
btn_handle.addTarget(self, action: #selector(tapHandleAction), for: .touchUpInside)
|
view.addSubview(btn_handle)
|
btn_handle.snp.makeConstraints { make in
|
make.right.equalToSuperview().offset(-24.5)
|
make.centerY.equalToSuperview()
|
make.width.height.equalTo(28)
|
}
|
}
|
|
static func show(){
|
let vc = PayMusicVC()
|
let tabBarHeight = JQ_currentViewController().navigationController?.tabBarController?.tabBar.height ?? 0
|
JQ_currentViewController().navigationController?.tabBarController?.addChild(vc)
|
JQ_currentViewController().navigationController?.tabBarController?.view.addSubview(vc.view)
|
vc.view.snp.makeConstraints { make in
|
make.left.equalTo(18.5)
|
make.right.equalTo(-18.5)
|
make.height.equalTo(46.5)
|
make.bottom.equalToSuperview().offset(-(tabBarHeight))
|
}
|
vc.startRunloopAni()
|
|
var testURL = [URL]()
|
testURL.append(URL(string: "https://downsc.chinaz.net/files/download/sound1/201206/1638.mp3")!)
|
testURL.append(URL(string: "https://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3")!)
|
testURL.append(URL(string: "https://www.cambridgeenglish.org/images/153149-movers-sample-listening-test-vol2.mp3")!)
|
vc.audioPlayer.playAt(firstPlayIndex: 2, urls: testURL)
|
|
}
|
|
|
private func startRunloopAni(){
|
// 创建旋转动画
|
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
rotationAnimation.fromValue = 0
|
rotationAnimation.toValue = CGFloat.pi * 2
|
rotationAnimation.duration = 5 // 动画持续时间
|
rotationAnimation.repeatCount = .greatestFiniteMagnitude // 无限重复
|
coverImage.layer.add(rotationAnimation, forKey: nil)
|
|
}
|
|
@objc func tapHandleAction(_ btn:UIButton){
|
|
|
}
|
}
|
|
class AudioPlayer {
|
private var player:AVPlayer?
|
private var playIndex:Int = 0 //播放的角标
|
private var cacheDirectory:URL!
|
private let session = URLSession.shared
|
private var urls = [URL]()
|
|
private static var _sharedInstance: AudioPlayer?
|
|
class func getSharedInstance() -> AudioPlayer {
|
guard let instance = _sharedInstance else {
|
_sharedInstance = AudioPlayer()
|
_sharedInstance!.cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("audios")
|
print("---:\(_sharedInstance!.cacheDirectory.absoluteString)")
|
|
if !FileManager.default.fileExists(atPath: _sharedInstance!.cacheDirectory!.absoluteString){
|
try? FileManager.default.createDirectory(at: _sharedInstance!.cacheDirectory, withIntermediateDirectories: false)
|
}
|
return _sharedInstance!
|
}
|
return instance
|
}
|
|
private init() {} // 私有化init方法
|
|
//销毁单例对象
|
class func destroy() {
|
_sharedInstance = nil
|
}
|
|
func playAt(firstPlayIndex:Int,urls:[URL]){
|
self.playIndex = firstPlayIndex
|
|
autoreleasepool{
|
for url in urls {
|
checkCacheAudio(from: url) { _, url in
|
self.urls.append(url)
|
}
|
}
|
player = AVPlayer(url: self.urls[firstPlayIndex])
|
player?.play()
|
}
|
|
self.player!.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 1), queue: DispatchQueue.main) { [unowned self](time) in
|
//当前正在播放的时间
|
let loadTime = CMTimeGetSeconds(time)
|
//视频总时间
|
let totalTime = CMTimeGetSeconds((self.player?.currentItem?.duration)!)
|
|
var dic = [String:Any]()
|
dic[MPMediaItemPropertyTitle] = "测试"
|
dic[MPNowPlayingInfoPropertyElapsedPlaybackTime] = loadTime
|
dic[MPNowPlayingInfoPropertyPlaybackRate] = 1
|
// 获取时长。item.duration.seconds 不凑效
|
let asset = self.player?.currentItem?.asset
|
dic[MPMediaItemPropertyPlaybackDuration] = CMTimeGetSeconds(asset!.duration)
|
// dic[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: CGSize(width: 50, height: 50), requestHandler: { s in
|
// return UIImage()
|
// })
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = dic
|
}
|
|
|
//播放完成
|
NotificationCenter.default.addObserver(self, selector: #selector(playbackEnd), name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
|
|
setLockScreen()
|
|
do {
|
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])
|
print("Playback OK")
|
try AVAudioSession.sharedInstance().setActive(true)
|
print("Session is Active")
|
} catch {
|
print(error)
|
}
|
}
|
|
func next(){
|
playIndex += 1
|
let index = min((urls.count - 1), playIndex)
|
player?.replaceCurrentItem(with: AVPlayerItem(url: urls[index]))
|
player?.play()
|
}
|
|
@objc private func playbackEnd(){
|
|
}
|
|
func previous(){
|
playIndex -= 1
|
let index = max(0, playIndex)
|
player?.replaceCurrentItem(with: AVPlayerItem(url: urls[index]))
|
player?.play()
|
}
|
|
|
func setLockScreen(){
|
// 锁屏进度控制
|
let center = MPRemoteCommandCenter.shared()
|
|
// 修改进度
|
center.changePlaybackPositionCommand.addTarget {[unowned self] event in
|
|
guard let event = event as? MPChangePlaybackPositionCommandEvent else {
|
return .commandFailed
|
}
|
|
self.player?.seek(to: CMTime(seconds: event.positionTime, preferredTimescale: 1),
|
toleranceBefore: CMTime(seconds: 0, preferredTimescale: 1),
|
toleranceAfter: CMTime(seconds: 0, preferredTimescale: 1))
|
|
// 更新锁屏信息
|
return .success
|
}
|
|
// 播放
|
center.playCommand.addTarget { event in
|
return .success
|
}
|
|
// 暂停
|
center.pauseCommand.addTarget { event in
|
return .success
|
}
|
|
// 下一首
|
center.nextTrackCommand.addTarget { event in
|
return .success
|
}
|
|
// 上一首
|
center.previousTrackCommand.addTarget { event in
|
return .success
|
}
|
}
|
|
|
// 下载视频并缓存,如果没有缓存,原路返回并异步下载
|
internal func checkCacheAudio(from url: URL, completion: @escaping (Bool,URL) -> Void) {
|
|
let videoCacheUrl = cacheDirectory.appendingPathComponent(url.lastPathComponent.jq_md5String() + "." + url.pathExtension)
|
|
// 检查缓存中是否已存在文件
|
if FileManager.default.fileExists(atPath: videoCacheUrl.path) {
|
completion(true,videoCacheUrl)
|
return
|
}else{
|
completion(false,url)
|
}
|
|
// 使用URLSession下载视频
|
let downloadTask = session.downloadTask(with: url) { tempLocalUrl, response, error in
|
if let tempLocalUrl = tempLocalUrl, error == nil {
|
do {
|
let temp = videoCacheUrl.appendingPathExtension(url.pathExtension)
|
try FileManager.default.moveItem(at: tempLocalUrl, to: temp)
|
} catch {
|
print("视频缓存失败:catch")
|
}
|
} else {
|
print("视频缓存失败:\(error?.localizedDescription ?? "")")
|
}
|
}
|
downloadTask.resume()
|
}
|
}
|
|
|
struct MusicProjectPlayInfoModel {
|
|
}
|