You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudmusic/cloudmusic/PlayController.swift

296 lines
11 KiB

//
// PlayController.swift
// cloudmusic
//
// Created by Qihua Pan on 2020/7/3.
// Copyright © 2020 Qihua Pan. All rights reserved.
//
import UIKit
import AVFoundation
import MediaPlayer
class PlayController: UIViewController {
@IBOutlet weak var musicName: UILabel!
@IBOutlet weak var progress: UIProgressView!
@IBOutlet weak var timeLabel: UILabel!
@IBOutlet weak var playButton: UIButton!
@IBOutlet weak var prevButton: UIButton!
@IBOutlet weak var nextButton: UIButton!
var activeIndex:Int = -1
var playList:[Music] = []
private var avplayer: AVPlayer!//
private var playerItem: AVPlayerItem!//
var timeObserver: Any! //
@IBAction func pressPlay(_ sender: UIButton) {
if self.avplayer.rate>0{
self.avplayer.pause()
self.playButton.setImage(UIImage(named: "play"), for: .normal)
}else{
self.avplayer.play()
self.playButton.setImage(UIImage(named: "pause"), for: .normal)
}
}
@IBAction func pressNext(_ sender: UIButton) {
if self.activeIndex + 1 == self.playList.count{
self.activeIndex = 0
}else{
self.activeIndex += 1
}
self.play()
}
@IBAction func pressPrev(_ sender: UIButton) {
if self.activeIndex - 1 == -1{
self.activeIndex = self.playList.count-1
}else{
self.activeIndex -= 1
}
self.play()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
progress.transform = CGAffineTransform(scaleX: 1, y: 3.0)
self.playList=self.getPlayList()
if(self.playList.count>0){
self.activeIndex=0
self.play()
}
}
override func viewDidAppear(_ animated: Bool) {
self.playList=self.getPlayList()
if self.playList.count==0{
self.musicName.text=""
self.alertModal(message: "当前播放列表为空!")
}
if self.playList.count<2{
self.prevButton.isHidden=true
self.nextButton.isHidden=true
}else{
self.prevButton.isHidden=false
self.nextButton.isHidden=false
}
}
func addPlayerItemObserver(){
// AVPlayerItemstatus
playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
// AVPlayerItemloadedTimeRanges
playerItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
// NotificationCenter.default.addObserver(self,
// selector: #selector(playerItemDidReachEnd(notification:)),
// name: .AVPlayerItemDidPlayToEndTime, object: playerItem)
}
/// KVO
///
/// - parameter keyPath:
/// - parameter object:
/// - parameter change:
/// - parameter context:
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard let object = object as? AVPlayerItem else { return }
guard let keyPath = keyPath else { return }
if keyPath == "status" {
if object.status == .readyToPlay { //
self.avplayer.play()
self.playButton.setImage(UIImage(named: "pause"), for: .normal)
} else if object.status == .failed || object.status == .unknown {
self.alertModal(message: "解析音频失败")
}
} else if keyPath == "loadedTimeRanges" {
let loadedTime = availableDurationWithplayerItem()
let f = CMTimeGetSeconds(object.duration)/loadedTime
self.progress.progress=Float(f)
if f==1.0{
self.progress.isHidden=true
}
print("当前加载进度\(CMTimeGetSeconds(object.duration)/loadedTime)")
}
}
func addProgressObserver(){
// .
timeObserver = self.avplayer.addPeriodicTimeObserver(forInterval: CMTimeMake(value: Int64(1.0),
timescale: Int32(1.0)),
queue: DispatchQueue.main) { [weak self] (time: CMTime) in
self?.updateProgress(time)
}
}
func updateProgress(_ time: CMTime) {
// CMTimeGetSecondsCMTimeCMTimeNaN
guard let playerItem = playerItem else {
return
}
let currentTime = CMTimeGetSeconds(time)
let totalTime = CMTimeGetSeconds(playerItem.duration)
//
self.timeLabel.text = "\(self.formatPlayTime(seconds: CMTimeGetSeconds(time)))/\(self.formatPlayTime(seconds: totalTime))"
self.progress.setProgress(Float(currentTime/totalTime), animated: true)
print("当前已经播放\(self.formatPlayTime(seconds: CMTimeGetSeconds(time)))")
}
func availableDurationWithplayerItem() -> TimeInterval {
guard let loadedTimeRanges = self.avplayer.currentItem?.loadedTimeRanges,
let first = loadedTimeRanges.first else {
fatalError()
}
//
let timeRange = first.timeRangeValue
let startSeconds = CMTimeGetSeconds(timeRange.start) //
let durationSecound = CMTimeGetSeconds(timeRange.duration)//
let result = startSeconds + durationSecound//
return result
}
//
func formatPlayTime(seconds: Double) -> String {
if !seconds.isNaN && !seconds.isInfinite{
let min = Int(seconds / 60)
let sec = Int(seconds.truncatingRemainder(dividingBy: 60))
return String(format: "%02d:%02d", min, sec)
}else{
return ""
}
}
func saveLocal(url: String,music: Music){
MusicRequest.saveMusic(url: url,music: music, callback: {(progress,url) in
debugPrint("Download Progress: \(progress.fractionCompleted)")
if progress.isFinished,let name=music.name {
debugPrint("歌曲\(name)下载完毕, 文件绝对路径=\(url.absoluteString)")
// if FileManager.default.fileExists(atPath: url.absoluteString){
// let filepath=url.absoluteString
// music.filepath = filepath
// self.saveContext()
// }
let filepath=url.absoluteString
music.filepath = filepath
self.saveContext()
}
})
}
func play(){
self.play(music: self.playList[self.activeIndex])
}
func play1(){
// if let url=URL(string:url){
// let asset = AVURLAsset(url: url)
// self.playerItem = AVPlayerItem(asset: asset)
// self.avplayer = AVPlayer(playerItem: self.playerItem)
// // ,kvo
// self.addPlayerItemObserver()
// self.addProgressObserver()
// }else if let name=self.playList[self.activeIndex].name{
// self.alertModal(message:"🎵\(name)")
// }
}
func play(url:String,name:String){
if let url=URL(string:url){
let asset = AVURLAsset(url: url)
self.playerItem = AVPlayerItem(asset: asset)
self.avplayer = AVPlayer(playerItem: self.playerItem)
// ,kvo
self.addPlayerItemObserver()
self.addProgressObserver()
}else {
self.alertModal(message:"无法解析🎵\(name)的播放地址")
}
}
func play(index:Int){
self.activeIndex=index
self.play()
}
func play(music:Music){
self.progress.isHidden=false
if let name=music.name{
self.musicName.text=name
if let filepath=music.filepath{
debugPrint("\(name)本地播放地址\(filepath)")
self.play(url: filepath,name:name)
}else if let url=music.url{
debugPrint("\(name)流式播放地址\(url)")
self.play(url: url,name:name)
self.saveLocal(url: url, music: music)
}else{
debugPrint("查找\(name)流式播放地址")
MusicRequest.searchUrl(id: music.id, callback: {(json) in
debugPrint("json:\(String(describing: json))")
if let res=json{
if res["code"]==200{
if res["data"].array != nil && res["data"].array!.count>0 && res["data"][0]["url"].stringValue.count>0{
let url = res["data"][0]["url"].stringValue
debugPrint("音乐🎵播放地址:\(url)")
music.url=url
self.saveContext()
self.saveLocal(url: url, music: music)
self.play(url: url,name:name)
}else{
self.alertModal(message:"无法解析🎵\(String(describing: name))的播放地址")
}
}else{
self.alertModal(message:"无法查找🎵\(String(describing: name))的播放地址")
}
}
})
}
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}