对网络波动情况的初级调试
This commit is contained in:
parent
c8cf604c59
commit
26a46c83a3
@ -40,6 +40,7 @@
|
||||
CBB5F1F92BFC35D000CBF73A /* MPPositive_CollectionSongModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5F1F82BFC35D000CBF73A /* MPPositive_CollectionSongModel.swift */; };
|
||||
CBB5F1FB2BFC3DB600CBF73A /* MPPositive_CollectionListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5F1FA2BFC3DB600CBF73A /* MPPositive_CollectionListModel.swift */; };
|
||||
CBB5F1FD2BFC40E400CBF73A /* MPPositive_CollectionArtistModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5F1FC2BFC40E400CBF73A /* MPPositive_CollectionArtistModel.swift */; };
|
||||
CBB5F1FF2BFCB40000CBF73A /* MPPositive_Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5F1FE2BFCB40000CBF73A /* MPPositive_Debouncer.swift */; };
|
||||
CBB75B0B2BEF0BC400B3FF9A /* MPPositive_DownloadItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB75B0A2BEF0BC400B3FF9A /* MPPositive_DownloadItemModel.swift */; };
|
||||
CBB9F9DD2BEDCFEE008338DE /* MPPositive_JsonLyrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB9F9DC2BEDCFEE008338DE /* MPPositive_JsonLyrics.swift */; };
|
||||
CBB9F9DF2BEDDCC5008338DE /* MP_PlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB9F9DE2BEDDCC5008338DE /* MP_PlayerManager.swift */; };
|
||||
@ -229,6 +230,7 @@
|
||||
CBB5F1F82BFC35D000CBF73A /* MPPositive_CollectionSongModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_CollectionSongModel.swift; sourceTree = "<group>"; };
|
||||
CBB5F1FA2BFC3DB600CBF73A /* MPPositive_CollectionListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_CollectionListModel.swift; sourceTree = "<group>"; };
|
||||
CBB5F1FC2BFC40E400CBF73A /* MPPositive_CollectionArtistModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_CollectionArtistModel.swift; sourceTree = "<group>"; };
|
||||
CBB5F1FE2BFCB40000CBF73A /* MPPositive_Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_Debouncer.swift; sourceTree = "<group>"; };
|
||||
CBB75B0A2BEF0BC400B3FF9A /* MPPositive_DownloadItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_DownloadItemModel.swift; sourceTree = "<group>"; };
|
||||
CBB9F9DC2BEDCFEE008338DE /* MPPositive_JsonLyrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_JsonLyrics.swift; sourceTree = "<group>"; };
|
||||
CBB9F9DE2BEDDCC5008338DE /* MP_PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_PlayerManager.swift; sourceTree = "<group>"; };
|
||||
@ -964,6 +966,7 @@
|
||||
CBCB32192BD7578500802900 /* MP_LocationManager.swift */,
|
||||
CBBFA9172BBA83BA00057FD5 /* MP_CoreDataHandlerManager.swift */,
|
||||
CBE2C4C62BC783F700F283A7 /* MP_HUD.swift */,
|
||||
CBB5F1FE2BFCB40000CBF73A /* MPPositive_Debouncer.swift */,
|
||||
CBC687482BC2882B0023ECA6 /* MPTableManager.swift */,
|
||||
CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */,
|
||||
CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */,
|
||||
@ -1172,6 +1175,7 @@
|
||||
CB102F522BFAE73800E967D8 /* MPPositive_JsonRecommend.swift in Sources */,
|
||||
CBCB4FEF2BD11402009760B3 /* MPSideA_NavigationController.swift in Sources */,
|
||||
CBF456DD2BF1E72F00ABF761 /* MPPositive_SearchResultListViewModel.swift in Sources */,
|
||||
CBB5F1FF2BFCB40000CBF73A /* MPPositive_Debouncer.swift in Sources */,
|
||||
CBEB01832BF5D88400D45006 /* MPPositive_ArtistShowCollectionViewCell.swift in Sources */,
|
||||
CBD5E80C2BF33D0200A3EBED /* MPPositive_SearchResultTypeShowView.swift in Sources */,
|
||||
CBCB35212BD7ACE900802900 /* MPPositive_JsonBrowse.swift in Sources */,
|
||||
|
||||
@ -49,6 +49,18 @@
|
||||
ReferencedContainer = "container:MusicPlayer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "IDERedirectionPolicy"
|
||||
value = "oslogToStdio"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@ -22,6 +22,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
}
|
||||
}
|
||||
setAudioSupport()
|
||||
MP_NetWorkManager.shared.requestStatusToYouTube()
|
||||
IQKeyboardManager.shared.enable = true
|
||||
IQKeyboardManager.shared.shouldResignOnTouchOutside = true
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
|
||||
@ -83,6 +83,10 @@ extension NotificationCenter{
|
||||
case player_type_switch
|
||||
///用户清空了歌单
|
||||
case player_delete_list
|
||||
///网络状态切换-网络不可用
|
||||
case net_switch_notReachable
|
||||
///网络状态切换-网络可用
|
||||
case net_switch_reachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
MusicPlayer/MP/Common/Tool(工具封装)/MPPositive_Debouncer.swift
Normal file
33
MusicPlayer/MP/Common/Tool(工具封装)/MPPositive_Debouncer.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// MPPositive_Debouncer.swift
|
||||
// MusicPlayer
|
||||
//
|
||||
// Created by Mr.Zhou on 2024/5/21.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class MPPositive_Debouncer: NSObject {
|
||||
static let shared = MPPositive_Debouncer()
|
||||
//计时器
|
||||
private var timer: Timer?
|
||||
//计时值
|
||||
private var delay: TimeInterval
|
||||
|
||||
private override init() {
|
||||
delay = 0.4
|
||||
super.init()
|
||||
}
|
||||
deinit {
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
}
|
||||
func call(_ action:@escaping (() -> Void)) {
|
||||
// 取消之前的延迟调用
|
||||
timer?.invalidate()
|
||||
// 设置新的延迟调用
|
||||
timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { _ in
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,30 @@ class MP_NetWorkManager: NSObject {
|
||||
private let search = "/search"
|
||||
///YouTuBe资源键值
|
||||
private let youTubeKeys:[String] = ["MUSIC_VIDEO_TYPE_ATV","MUSIC_VIDEO_TYPE_OMV","MUSIC_PAGE_TYPE_ALBUM","MUSIC_PAGE_TYPE_ARTIST","MUSIC_PAGE_TYPE_PLAYLIST","MUSIC_PAGE_TYPE_TRACK_LYRICS","MUSIC_PAGE_TYPE_TRACK_RELATED"]
|
||||
///当前网络状态
|
||||
enum NetWorkStatus: String {
|
||||
case notReachable = "网络不可用"
|
||||
case unknown = "网络未知"
|
||||
case reachable = "网络可用"
|
||||
}
|
||||
private var netWorkStatu:NetWorkStatus = .reachable{
|
||||
willSet{
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//旧值为网络可用,新值为网络不可用,为断网第一时间,发出通知告知播放器
|
||||
if netWorkStatu == .reachable, newValue == .notReachable {
|
||||
print("网络不可用")
|
||||
NotificationCenter.notificationKey.post(notificationName: .net_switch_notReachable)
|
||||
}
|
||||
//旧值为网络不可用,新值为网络可用,为网络回复第一时间,发出通知告知播放器
|
||||
if netWorkStatu == .notReachable, newValue == .reachable {
|
||||
print("网络可用")
|
||||
NotificationCenter.notificationKey.post(notificationName: .net_switch_reachable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//MARK: - 固定参数
|
||||
//访问数据(首次首页预览时获得)
|
||||
private var visitorData:String?
|
||||
@ -133,24 +157,23 @@ class MP_NetWorkManager: NSObject {
|
||||
monitor.start(queue: queue)
|
||||
}
|
||||
///网络请求检测
|
||||
private func requestStatusToYouTube(_ isAilable:@escaping(Bool) -> Void) {
|
||||
func requestStatusToYouTube() {
|
||||
//设置一个节点
|
||||
let reachabilityManager = NetworkReachabilityManager(host: "https://music.youtube.com/")
|
||||
//通过ping节点确认是否能执行网络请求
|
||||
reachabilityManager?.startListening(onUpdatePerforming: { status in
|
||||
reachabilityManager?.startListening(onQueue: .main, onUpdatePerforming: { [weak self] status in
|
||||
guard let self = self else {return}
|
||||
switch status {
|
||||
case .unknown://未知状况
|
||||
isAilable(false)
|
||||
print("网络情况未知")
|
||||
netWorkStatu = .unknown
|
||||
case .notReachable://网络不可用
|
||||
isAilable(false)
|
||||
print("网络不可用")
|
||||
netWorkStatu = .notReachable
|
||||
case .reachable(.ethernetOrWiFi), .reachable(.cellular)://网络可用,且做出了分类
|
||||
//网络可用
|
||||
isAilable(true)
|
||||
netWorkStatu = .reachable
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
//MARK: - API请求
|
||||
extension MP_NetWorkManager {
|
||||
@ -189,11 +212,8 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostHomeBrowse(url, parameters: parameters)
|
||||
}
|
||||
}
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostHomeBrowse(url, parameters: parameters)
|
||||
}
|
||||
|
||||
}
|
||||
@ -255,14 +275,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostAlbumOrList(url, parameters: parameters) { results in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostAlbumOrList(url, parameters: parameters) { results in
|
||||
comletion(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求列表/专辑数据
|
||||
private func requestPostAlbumOrList(_ url:URL, parameters:Parameters, comletion:@escaping (MPPositive_ListAlbumListViewModel) -> Void) {
|
||||
//发送post请求,并将结果转为RootBrowses
|
||||
@ -315,14 +332,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostArtist(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostArtist(url, parameters: parameters) { result in
|
||||
comletion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求艺术家信息
|
||||
private func requestPostArtist(_ url:URL, parameters:Parameters, comletion:@escaping (MPPositive_ArtistViewModel) -> Void) {
|
||||
//发送post请求,并将结果转为RootBrowses
|
||||
@ -374,14 +388,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostArtistMore(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostArtistMore(url, parameters: parameters) { result in
|
||||
comletion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///请求艺术家更多数据
|
||||
private func requestPostArtistMore(_ url:URL, parameters:Parameters, comletion:@escaping (([MPPositive_BrowseItemViewModel], String?, String?)) -> Void) {
|
||||
//发送post请求,并将结果转为RootBrowses
|
||||
@ -430,14 +441,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostArtistMoreContinuation(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostArtistMoreContinuation(url, parameters: parameters) { result in
|
||||
comletion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///请求艺术家更多数据继续
|
||||
private func requestPostArtistMoreContinuation(_ url:URL, parameters:Parameters, comletion:@escaping (([MPPositive_BrowseItemViewModel], String?, String?)) -> Void) {
|
||||
//发送post请求,并将结果转为RootBrowses
|
||||
@ -487,16 +495,12 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
//发送next列表请求
|
||||
self.requestPostNextList(url, parameters: parameters) { listSongs in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostNextList(url, parameters: parameters) { listSongs in
|
||||
//成功拿到列表所有歌曲(内容尚不完善)
|
||||
completion(listSongs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求next列表
|
||||
private func requestPostNextList(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SongItemModel]) -> Void)) {
|
||||
//发送post请求
|
||||
@ -542,15 +546,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
//发送next列表歌词/相关内容请求
|
||||
self.requestPostNextLyricsAndRelated(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostNextLyricsAndRelated(url, parameters: parameters) { result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求请求Next歌词/相关内容
|
||||
private func requestPostNextLyricsAndRelated(_ url:URL, parameters:Parameters, completion:@escaping(((String?,String?)) -> Void)) {
|
||||
//发送post请求
|
||||
@ -596,14 +596,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
|
||||
completion(resourceUlrs, coverUrls)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求单曲/视频
|
||||
private func requestPostPlayer(_ url:URL, parameters:Parameters, completion:@escaping((([String],[String]), [String]?) -> Void)) {
|
||||
//发送post请求
|
||||
@ -651,14 +648,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostLyric(url, parameters: parameters) { lyrics in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostLyric(url, parameters: parameters) { lyrics in
|
||||
completion(lyrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求歌词
|
||||
private func requestPostLyric(_ url:URL, parameters:Parameters, completion:@escaping((String) -> Void)) {
|
||||
//发送post请求
|
||||
@ -701,14 +695,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostRecommend(url, parameters: parameters) { results in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostRecommend(url, parameters: parameters) { results in
|
||||
completion(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///请求相关内容
|
||||
private func requestPostRecommend(_ url:URL, parameters:Parameters, completion: @escaping ([MPPositive_RecommendListViewModel]) -> Void) {
|
||||
//发送post请求
|
||||
@ -757,14 +748,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostSearchSuggestions(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostSearchSuggestions(url, parameters: parameters) { result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求搜索建议
|
||||
private func requestPostSearchSuggestions(_ url:URL, parameters:Parameters, completion:@escaping(([[MPPositive_SearchSuggestionItemModel]]) -> Void)) {
|
||||
//发送post请求
|
||||
@ -811,14 +799,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostSearchPreviewResults(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostSearchPreviewResults(url, parameters: parameters) { result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求搜索预览结果
|
||||
private func requestPostSearchPreviewResults(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultListViewModel]) -> Void)) {
|
||||
//发送post请求
|
||||
@ -871,14 +856,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostSearchTypeResults(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostSearchTypeResults(url, parameters: parameters) { result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求搜索分类结果
|
||||
private func requestPostSearchTypeResults(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultItemViewModel], String?, String?))->Void) {
|
||||
//发送post请求
|
||||
@ -931,14 +913,11 @@ extension MP_NetWorkManager {
|
||||
]
|
||||
]
|
||||
]
|
||||
requestStatusToYouTube { isAvailable in
|
||||
if isAvailable == true {
|
||||
self.requestPostSearchTypeContinuation(url, parameters: parameters) { result in
|
||||
guard netWorkStatu != .notReachable else {return}
|
||||
requestPostSearchTypeContinuation(url, parameters: parameters) { result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//请求搜索分类继续结果
|
||||
private func requestPostSearchTypeContinuation(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultItemViewModel], String?, String?))->Void) {
|
||||
//发送post请求
|
||||
|
||||
@ -103,7 +103,10 @@ class MP_PlayerManager:NSObject{
|
||||
super.init()
|
||||
// 添加观察者,监听播放结束事件
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
|
||||
//监听用户切换了音乐
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload)
|
||||
//监听网络状态恢复可用
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_ :)), notificationName: .net_switch_reachable)
|
||||
}
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
@ -160,7 +163,23 @@ class MP_PlayerManager:NSObject{
|
||||
loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil)
|
||||
//当前缓冲值
|
||||
loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil)
|
||||
//是否具备足够播放的能力
|
||||
loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: [.old,.new], context: nil)
|
||||
//启动全部预加载
|
||||
loadPlayer.listViewVideos.forEach({$0.preloadAsset($0.resourceAsset)})
|
||||
}
|
||||
///网络状态恢复正常
|
||||
@objc private func netWorkReachableAction(_ sender:Notification) {
|
||||
//监听到网络状态恢复,检索当前播放器是否正在播放
|
||||
if loadPlayer.currentVideo != nil {
|
||||
//有音乐播放,获取当前播放进度
|
||||
let currentTime = loadPlayer.currentVideo!.resourcePlayerItem.currentTime()
|
||||
//手动调整播放时间点,以此重启播放器缓存
|
||||
player.play()
|
||||
playState = .Playing
|
||||
}
|
||||
}
|
||||
|
||||
//实现KVO监听
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
guard let keyPath = keyPath else {
|
||||
@ -174,14 +193,6 @@ class MP_PlayerManager:NSObject{
|
||||
if playState != .Playing {
|
||||
//当statuVlaue值等于playerItem准备播放的值,说明已经准备好播放
|
||||
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放")
|
||||
//还未播放当前音乐,启动播放
|
||||
print("开始播放音乐-\(loadPlayer.currentVideo?.title ?? "")")
|
||||
player.play()
|
||||
playState = .Playing
|
||||
//执行开始播放闭包
|
||||
if startActionBlock != nil {
|
||||
startActionBlock!()
|
||||
}
|
||||
}
|
||||
}else {
|
||||
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")")
|
||||
@ -211,7 +222,22 @@ class MP_PlayerManager:NSObject{
|
||||
}
|
||||
|
||||
case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放
|
||||
break
|
||||
if let playbackLikelyToKeepUp = change?[.newKey] as? Bool, playbackLikelyToKeepUp == true {
|
||||
if playState != .Playing {
|
||||
//还未播放当前音乐,启动播放
|
||||
print("播放音乐-\(loadPlayer.currentVideo?.title ?? "")")
|
||||
player.play()
|
||||
playState = .Playing
|
||||
//执行开始播放闭包
|
||||
if startActionBlock != nil {
|
||||
startActionBlock!()
|
||||
}
|
||||
}
|
||||
}else {
|
||||
//没有足够的数据支持播放
|
||||
player.pause()
|
||||
playState = .Null
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -324,7 +350,7 @@ class MP_PlayerManager:NSObject{
|
||||
switch playType {
|
||||
case .random://随机,播放随机列表内容
|
||||
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index - 1
|
||||
}
|
||||
@ -333,15 +359,15 @@ class MP_PlayerManager:NSObject{
|
||||
if nextIndex < 0 {
|
||||
//播放列表最后一首
|
||||
let last = loadPlayer.randomVideos.last
|
||||
loadPlayer.improveData(last?.videoId ?? "")
|
||||
loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
|
||||
}else {
|
||||
//查询列表对应单曲
|
||||
let song = loadPlayer.randomVideos[nextIndex]
|
||||
loadPlayer.improveData(song.videoId ?? "")
|
||||
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
|
||||
}
|
||||
default://常规播放或者单曲播放
|
||||
for (index, item) in loadPlayer.songVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index - 1
|
||||
}
|
||||
@ -366,7 +392,7 @@ class MP_PlayerManager:NSObject{
|
||||
switch playType {
|
||||
case .random:
|
||||
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index + 1
|
||||
}
|
||||
@ -375,15 +401,15 @@ class MP_PlayerManager:NSObject{
|
||||
if nextIndex > (loadPlayer.randomVideos.count-1) {
|
||||
//播放列表第一首
|
||||
let first = loadPlayer.randomVideos.first
|
||||
loadPlayer.improveData(first?.videoId ?? "")
|
||||
loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
|
||||
}else {
|
||||
//存在下一首,获取下一首ID,并播放
|
||||
let song = loadPlayer.randomVideos[nextIndex]
|
||||
loadPlayer.improveData(song.videoId ?? "")
|
||||
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
|
||||
}
|
||||
default:
|
||||
for (index, item) in loadPlayer.songVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index + 1
|
||||
}
|
||||
@ -411,7 +437,10 @@ class MP_PlayerManager:NSObject{
|
||||
//切歌时移除KVO监听
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges")
|
||||
// video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
|
||||
}
|
||||
if cacheValueBlock != nil {
|
||||
cacheValueBlock!(0, 1)
|
||||
}
|
||||
if loadPlayer.currentVideo != nil {
|
||||
//开始播放
|
||||
|
||||
@ -33,13 +33,19 @@ class MPPositive_SongViewModel: NSObject {
|
||||
var song:MPPositive_SongItemModel!
|
||||
///是否进行过预加载
|
||||
var isPloading:Bool = false
|
||||
// 标记为已取消
|
||||
private var isCancelled = false
|
||||
init(_ song:MPPositive_SongItemModel) {
|
||||
super.init()
|
||||
self.song = song
|
||||
configure()
|
||||
}
|
||||
deinit {
|
||||
|
||||
//释放内存
|
||||
resourcePlayerItem = nil
|
||||
resourceAsset = nil
|
||||
isCancelled = true
|
||||
print("\(title ?? "")被释放了")
|
||||
}
|
||||
//数据配置
|
||||
private func configure() {
|
||||
@ -76,9 +82,9 @@ class MPPositive_SongViewModel: NSObject {
|
||||
}
|
||||
reloadCollectionAndDownLoad()
|
||||
//执行预加载
|
||||
if isPloading == false {
|
||||
preloadAsset(resourceAsset)
|
||||
}
|
||||
// if isPloading == false {
|
||||
// preloadAsset(resourceAsset)
|
||||
// }
|
||||
}
|
||||
//页面状态更新
|
||||
func reloadCollectionAndDownLoad() {
|
||||
@ -91,12 +97,19 @@ class MPPositive_SongViewModel: NSObject {
|
||||
|
||||
//执行预加载
|
||||
func preloadAsset(_ asset:AVURLAsset) {
|
||||
guard isPloading == false else {
|
||||
return
|
||||
}
|
||||
self.isPloading = true
|
||||
//执行预加载
|
||||
if #available(iOS 16, *) {
|
||||
//ios16以上的情况
|
||||
Task{
|
||||
do{
|
||||
let playable = try await asset.load(.isPlayable)
|
||||
guard !isCancelled else {
|
||||
return
|
||||
}
|
||||
if playable == true {
|
||||
print("\(self.title ?? "")预加载成功")
|
||||
self.isPloading = true
|
||||
@ -105,8 +118,9 @@ class MPPositive_SongViewModel: NSObject {
|
||||
switch asset.status(of: .isPlayable) {
|
||||
case .failed(let erro):
|
||||
print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)")
|
||||
self.isPloading = false
|
||||
default:
|
||||
break
|
||||
self.isPloading = false
|
||||
}
|
||||
}
|
||||
}catch{
|
||||
@ -118,7 +132,7 @@ class MPPositive_SongViewModel: NSObject {
|
||||
let keys = ["playable"]
|
||||
asset.loadValuesAsynchronously(forKeys: keys) {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
guard let self = self, !isCancelled else {return}
|
||||
for key in keys {
|
||||
var error: NSError? = nil
|
||||
let status = asset.statusOfValue(forKey: key, error: &error)
|
||||
@ -131,8 +145,10 @@ class MPPositive_SongViewModel: NSObject {
|
||||
}
|
||||
case .failed:
|
||||
print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")")
|
||||
self.isPloading = false
|
||||
case .cancelled:
|
||||
print("\(title ?? "")预加载被取消了")
|
||||
self.isPloading = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -26,9 +26,8 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
///当前播放音乐ID
|
||||
var currentVideoId:String!
|
||||
///单曲ViewModel列表
|
||||
|
||||
///单曲播放队列ViewModel
|
||||
var listViewVideos:[MPPositive_SongViewModel]!
|
||||
///异步请求组
|
||||
var group:DispatchGroup?
|
||||
@ -42,17 +41,32 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
//根据列表生成一份随机播放列表
|
||||
self.randomVideos = self.songVideos.shuffled()
|
||||
self.listViewVideos = []
|
||||
self.currentVideoId = currentVideoId
|
||||
}
|
||||
|
||||
///将选中Video的上下2项包括本身总计3项Video进行补全转为ViewModel,并播放这首音乐
|
||||
func improveData(_ targetVideoId:String) {
|
||||
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
|
||||
if nextIndex < randomVideos.count {
|
||||
array.append(self.randomVideos[nextIndex])
|
||||
}
|
||||
}else {
|
||||
//获取targetVideoId的索引
|
||||
guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else {
|
||||
return
|
||||
}
|
||||
//对于选中Video的集合
|
||||
var array:[MPPositive_SongItemModel] = []
|
||||
array.append(self.songVideos[targetIndex])
|
||||
//获取上一位
|
||||
let previousIndex = targetIndex-1
|
||||
@ -63,6 +77,7 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
if nextIndex < songVideos.count {
|
||||
array.append(self.songVideos[nextIndex])
|
||||
}
|
||||
}
|
||||
//获取完成,优先检索ViewModel,看看是否已存在补完video
|
||||
let videoIDs = Set(listViewVideos.map({$0.song.videoId}))
|
||||
//比较videoID,去掉已经补完的内容
|
||||
@ -89,11 +104,12 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
}
|
||||
}
|
||||
group?.notify(queue: .main, execute: {
|
||||
//补完转化完毕,重新排序
|
||||
self.listViewVideos = self.listViewVideos.sorted(by: {$0.index < $1.index})
|
||||
//排序完成,确定播放音乐
|
||||
self.currentVideo = self.listViewVideos.first(where: {$0.song.videoId == targetVideoId})
|
||||
self.group = nil
|
||||
[weak self] in
|
||||
//确定播放音乐
|
||||
self?.currentVideo = self?.listViewVideos.first(where: {$0.song.videoId == targetVideoId})
|
||||
//只保留最后三首
|
||||
self?.listViewVideos = self?.listViewVideos.suffix(3)
|
||||
self?.group = nil
|
||||
})
|
||||
}
|
||||
///重新获取指定歌曲资源
|
||||
|
||||
@ -468,6 +468,9 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
|
||||
}
|
||||
//切换播放器状态(按顺序/随机/单曲)
|
||||
@objc private func typeClick(_ sender:UIButton) {
|
||||
MPPositive_Debouncer.shared.call {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//对播放器播放方式截形切换
|
||||
var value = MP_PlayerManager.shared.getPlayType().rawValue
|
||||
value += 1
|
||||
@ -476,18 +479,27 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
|
||||
}
|
||||
MP_PlayerManager.shared.setPlayType(.init(rawValue: value)!)
|
||||
}
|
||||
}
|
||||
//下一首
|
||||
@objc private func nextClick(_ sender:UIButton) {
|
||||
MPPositive_Debouncer.shared.call {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
coverView.sliderView.value = 0
|
||||
playBtn.isUserInteractionEnabled = false
|
||||
MP_PlayerManager.shared.nextEvent()
|
||||
}
|
||||
|
||||
}
|
||||
//上一首
|
||||
@objc private func previousClick(_ sender:UIButton) {
|
||||
MPPositive_Debouncer.shared.call {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
coverView.sliderView.value = 0
|
||||
playBtn.isUserInteractionEnabled = false
|
||||
MP_PlayerManager.shared.previousEvent()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -60,16 +60,37 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
lazy var durationLabel:UILabel = createLabel("00:00" ,font: .systemFont(ofSize: 12*width, weight: .medium), textColor: .init(hex: "#FFFFFF", alpha: 0.85), textAlignment: .left)
|
||||
///最大播放时间值Label
|
||||
lazy var maxTimesLabel:UILabel = createLabel("00:00" ,font: .systemFont(ofSize: 12*width, weight: .medium), textColor: .init(hex: "#FFFFFF", alpha: 0.6), textAlignment: .right)
|
||||
///断网提醒View
|
||||
private lazy var maskNotReachableView:UIView = {
|
||||
let maskView = UIView()
|
||||
maskView.backgroundColor = .init(hex: "#000000", alpha: 0.7)
|
||||
maskView.layer.masksToBounds = true
|
||||
maskView.layer.cornerRadius = 16*width
|
||||
//放置一个label
|
||||
let noticeLabel:UILabel = createLabel("The network connection is disconnected and the player will stop loading music. Please restore the network as soon as possible!", font: .systemFont(ofSize: 18, weight: .medium), textColor: .white, textAlignment: .center, lines: 0)
|
||||
maskView.addSubview(noticeLabel)
|
||||
noticeLabel.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
make.width.equalToSuperview().multipliedBy(0.7)
|
||||
}
|
||||
return maskView
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .clear
|
||||
configure()
|
||||
//添加监听
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkNotReachableAction(_:)), notificationName: .net_switch_notReachable)
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_:)), notificationName: .net_switch_reachable)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
//视图配置
|
||||
private func configure() {
|
||||
//配置封面图
|
||||
@ -80,6 +101,11 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(12*width)
|
||||
}
|
||||
addSubview(maskNotReachableView)
|
||||
maskNotReachableView.snp.makeConstraints { make in
|
||||
make.left.right.top.bottom.equalTo(coverImageView)
|
||||
}
|
||||
maskNotReachableView.isHidden = true
|
||||
//添加标题
|
||||
addSubview(titleLabel)
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
@ -128,6 +154,15 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
make.top.equalTo(sliderView.snp.bottom).offset(5*width)
|
||||
}
|
||||
}
|
||||
//网络不可用时触发
|
||||
@objc private func netWorkNotReachableAction(_ sender:Notification) {
|
||||
maskNotReachableView.isHidden = false
|
||||
}
|
||||
//网络可用时触发
|
||||
@objc private func netWorkReachableAction(_ sender:Notification) {
|
||||
maskNotReachableView.isHidden = true
|
||||
}
|
||||
|
||||
//调整音乐进度
|
||||
@objc private func seekProgressClick(_ sender: UISlider, forEvent event: UIEvent) {
|
||||
//获取touchEvent
|
||||
|
||||
Loading…
Reference in New Issue
Block a user