# Conflicts:
#	MusicPlayer/MP/MPPositive/Views/Player/MPPositive_PlayerCoverView.swift
This commit is contained in:
忆海16 2024-05-22 17:53:33 +08:00
commit 249ec76e3b
11 changed files with 309 additions and 157 deletions

View File

@ -40,6 +40,7 @@
CBB5F1F92BFC35D000CBF73A /* MPPositive_CollectionSongModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5F1F82BFC35D000CBF73A /* MPPositive_CollectionSongModel.swift */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; CBB9F9DE2BEDDCC5008338DE /* MP_PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_PlayerManager.swift; sourceTree = "<group>"; };
@ -964,6 +966,7 @@
CBCB32192BD7578500802900 /* MP_LocationManager.swift */, CBCB32192BD7578500802900 /* MP_LocationManager.swift */,
CBBFA9172BBA83BA00057FD5 /* MP_CoreDataHandlerManager.swift */, CBBFA9172BBA83BA00057FD5 /* MP_CoreDataHandlerManager.swift */,
CBE2C4C62BC783F700F283A7 /* MP_HUD.swift */, CBE2C4C62BC783F700F283A7 /* MP_HUD.swift */,
CBB5F1FE2BFCB40000CBF73A /* MPPositive_Debouncer.swift */,
CBC687482BC2882B0023ECA6 /* MPTableManager.swift */, CBC687482BC2882B0023ECA6 /* MPTableManager.swift */,
CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */, CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */,
CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */, CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */,
@ -1172,6 +1175,7 @@
CB102F522BFAE73800E967D8 /* MPPositive_JsonRecommend.swift in Sources */, CB102F522BFAE73800E967D8 /* MPPositive_JsonRecommend.swift in Sources */,
CBCB4FEF2BD11402009760B3 /* MPSideA_NavigationController.swift in Sources */, CBCB4FEF2BD11402009760B3 /* MPSideA_NavigationController.swift in Sources */,
CBF456DD2BF1E72F00ABF761 /* MPPositive_SearchResultListViewModel.swift in Sources */, CBF456DD2BF1E72F00ABF761 /* MPPositive_SearchResultListViewModel.swift in Sources */,
CBB5F1FF2BFCB40000CBF73A /* MPPositive_Debouncer.swift in Sources */,
CBEB01832BF5D88400D45006 /* MPPositive_ArtistShowCollectionViewCell.swift in Sources */, CBEB01832BF5D88400D45006 /* MPPositive_ArtistShowCollectionViewCell.swift in Sources */,
CBD5E80C2BF33D0200A3EBED /* MPPositive_SearchResultTypeShowView.swift in Sources */, CBD5E80C2BF33D0200A3EBED /* MPPositive_SearchResultTypeShowView.swift in Sources */,
CBCB35212BD7ACE900802900 /* MPPositive_JsonBrowse.swift in Sources */, CBCB35212BD7ACE900802900 /* MPPositive_JsonBrowse.swift in Sources */,

View File

@ -49,6 +49,18 @@
ReferencedContainer = "container:MusicPlayer.xcodeproj"> ReferencedContainer = "container:MusicPlayer.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "IDERedirectionPolicy"
value = "oslogToStdio"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import CoreData import CoreData
import AVFoundation import AVFoundation
import Alamofire
@_exported import IQKeyboardManagerSwift @_exported import IQKeyboardManagerSwift
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
@ -22,6 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
} }
setAudioSupport() setAudioSupport()
MP_NetWorkManager.shared.requestStatusToYouTube()
IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.shouldResignOnTouchOutside = true IQKeyboardManager.shared.shouldResignOnTouchOutside = true
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
@ -91,7 +93,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
window?.rootViewController = tabBarVC window?.rootViewController = tabBarVC
window?.makeKeyAndVisible() window?.makeKeyAndVisible()
} }
// MARK: - Core Data stack // MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = { lazy var persistentContainer: NSPersistentContainer = {
/* /*

View File

@ -83,6 +83,10 @@ extension NotificationCenter{
case player_type_switch case player_type_switch
/// ///
case player_delete_list case player_delete_list
///-
case net_switch_notReachable
///-
case net_switch_reachable
} }
} }
} }

View 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()
}
}
}

View File

@ -37,6 +37,30 @@ class MP_NetWorkManager: NSObject {
private let search = "/search" private let search = "/search"
///YouTuBe ///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"] 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 let reachabilityManager:NetworkReachabilityManager = NetworkReachabilityManager(host: "https://music.youtube.com/")!
///
var netWorkStatu:NetWorkStatus!{
willSet{
//
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: - //MARK: -
//访 //访
private var visitorData:String? private var visitorData:String?
@ -133,21 +157,17 @@ class MP_NetWorkManager: NSObject {
monitor.start(queue: queue) monitor.start(queue: queue)
} }
/// ///
private func requestStatusToYouTube(_ isAilable:@escaping(Bool) -> Void) { func requestStatusToYouTube() {
//
let reachabilityManager = NetworkReachabilityManager(host: "https://music.youtube.com/")
//ping //ping
reachabilityManager?.startListening(onUpdatePerforming: { status in reachabilityManager.startListening(onQueue: .main, onUpdatePerforming: { [weak self] status in
guard let self = self else {return}
switch status { switch status {
case .unknown:// case .unknown://
isAilable(false) netWorkStatu = .unknown
print("网络情况未知")
case .notReachable:// case .notReachable://
isAilable(false) netWorkStatu = .notReachable
print("网络不可用")
case .reachable(.ethernetOrWiFi), .reachable(.cellular):// case .reachable(.ethernetOrWiFi), .reachable(.cellular)://
// netWorkStatu = .reachable
isAilable(true)
} }
}) })
} }
@ -189,11 +209,8 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostHomeBrowse(url, parameters: parameters)
self.requestPostHomeBrowse(url, parameters: parameters)
}
}
} }
} }
@ -255,14 +272,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostAlbumOrList(url, parameters: parameters) { results in
self.requestPostAlbumOrList(url, parameters: parameters) { results in
comletion(results) comletion(results)
} }
} }
}
}
/// ///
private func requestPostAlbumOrList(_ url:URL, parameters:Parameters, comletion:@escaping (MPPositive_ListAlbumListViewModel) -> Void) { private func requestPostAlbumOrList(_ url:URL, parameters:Parameters, comletion:@escaping (MPPositive_ListAlbumListViewModel) -> Void) {
//postRootBrowses //postRootBrowses
@ -315,14 +329,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostArtist(url, parameters: parameters) { result in
self.requestPostArtist(url, parameters: parameters) { result in
comletion(result) comletion(result)
} }
} }
}
}
// //
private func requestPostArtist(_ url:URL, parameters:Parameters, comletion:@escaping (MPPositive_ArtistViewModel) -> Void) { private func requestPostArtist(_ url:URL, parameters:Parameters, comletion:@escaping (MPPositive_ArtistViewModel) -> Void) {
//postRootBrowses //postRootBrowses
@ -374,14 +385,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostArtistMore(url, parameters: parameters) { result in
self.requestPostArtistMore(url, parameters: parameters) { result in
comletion(result) comletion(result)
} }
} }
}
}
/// ///
private func requestPostArtistMore(_ url:URL, parameters:Parameters, comletion:@escaping (([MPPositive_BrowseItemViewModel], String?, String?)) -> Void) { private func requestPostArtistMore(_ url:URL, parameters:Parameters, comletion:@escaping (([MPPositive_BrowseItemViewModel], String?, String?)) -> Void) {
//postRootBrowses //postRootBrowses
@ -430,14 +438,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostArtistMoreContinuation(url, parameters: parameters) { result in
self.requestPostArtistMoreContinuation(url, parameters: parameters) { result in
comletion(result) comletion(result)
} }
} }
}
}
/// ///
private func requestPostArtistMoreContinuation(_ url:URL, parameters:Parameters, comletion:@escaping (([MPPositive_BrowseItemViewModel], String?, String?)) -> Void) { private func requestPostArtistMoreContinuation(_ url:URL, parameters:Parameters, comletion:@escaping (([MPPositive_BrowseItemViewModel], String?, String?)) -> Void) {
//postRootBrowses //postRootBrowses
@ -487,16 +492,12 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostNextList(url, parameters: parameters) { listSongs in
//next
self.requestPostNextList(url, parameters: parameters) { listSongs in
// //
completion(listSongs) completion(listSongs)
} }
} }
}
}
//next //next
private func requestPostNextList(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SongItemModel]) -> Void)) { private func requestPostNextList(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SongItemModel]) -> Void)) {
//post //post
@ -542,15 +543,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostNextLyricsAndRelated(url, parameters: parameters) { result in
//next/
self.requestPostNextLyricsAndRelated(url, parameters: parameters) { result in
completion(result) completion(result)
} }
} }
}
}
//Next/ //Next/
private func requestPostNextLyricsAndRelated(_ url:URL, parameters:Parameters, completion:@escaping(((String?,String?)) -> Void)) { private func requestPostNextLyricsAndRelated(_ url:URL, parameters:Parameters, completion:@escaping(((String?,String?)) -> Void)) {
//post //post
@ -596,14 +593,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
self.requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
completion(resourceUlrs, coverUrls) completion(resourceUlrs, coverUrls)
} }
} }
}
}
/// ///
private func requestPostPlayer(_ url:URL, parameters:Parameters, completion:@escaping((([String],[String]), [String]?) -> Void)) { private func requestPostPlayer(_ url:URL, parameters:Parameters, completion:@escaping((([String],[String]), [String]?) -> Void)) {
//post //post
@ -651,14 +645,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostLyric(url, parameters: parameters) { lyrics in
self.requestPostLyric(url, parameters: parameters) { lyrics in
completion(lyrics) completion(lyrics)
} }
} }
}
}
// //
private func requestPostLyric(_ url:URL, parameters:Parameters, completion:@escaping((String) -> Void)) { private func requestPostLyric(_ url:URL, parameters:Parameters, completion:@escaping((String) -> Void)) {
//post //post
@ -701,14 +692,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostRecommend(url, parameters: parameters) { results in
self.requestPostRecommend(url, parameters: parameters) { results in
completion(results) completion(results)
} }
} }
}
}
/// ///
private func requestPostRecommend(_ url:URL, parameters:Parameters, completion: @escaping ([MPPositive_RecommendListViewModel]) -> Void) { private func requestPostRecommend(_ url:URL, parameters:Parameters, completion: @escaping ([MPPositive_RecommendListViewModel]) -> Void) {
//post //post
@ -757,14 +745,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostSearchSuggestions(url, parameters: parameters) { result in
self.requestPostSearchSuggestions(url, parameters: parameters) { result in
completion(result) completion(result)
} }
} }
}
}
// //
private func requestPostSearchSuggestions(_ url:URL, parameters:Parameters, completion:@escaping(([[MPPositive_SearchSuggestionItemModel]]) -> Void)) { private func requestPostSearchSuggestions(_ url:URL, parameters:Parameters, completion:@escaping(([[MPPositive_SearchSuggestionItemModel]]) -> Void)) {
//post //post
@ -811,14 +796,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostSearchPreviewResults(url, parameters: parameters) { result in
self.requestPostSearchPreviewResults(url, parameters: parameters) { result in
completion(result) completion(result)
} }
} }
}
}
// //
private func requestPostSearchPreviewResults(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultListViewModel]) -> Void)) { private func requestPostSearchPreviewResults(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultListViewModel]) -> Void)) {
//post //post
@ -871,14 +853,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostSearchTypeResults(url, parameters: parameters) { result in
self.requestPostSearchTypeResults(url, parameters: parameters) { result in
completion(result) completion(result)
} }
} }
}
}
// //
private func requestPostSearchTypeResults(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultItemViewModel], String?, String?))->Void) { private func requestPostSearchTypeResults(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultItemViewModel], String?, String?))->Void) {
//post //post
@ -931,14 +910,11 @@ extension MP_NetWorkManager {
] ]
] ]
] ]
requestStatusToYouTube { isAvailable in guard netWorkStatu != .notReachable else {return}
if isAvailable == true { requestPostSearchTypeContinuation(url, parameters: parameters) { result in
self.requestPostSearchTypeContinuation(url, parameters: parameters) { result in
completion(result) completion(result)
} }
} }
}
}
// //
private func requestPostSearchTypeContinuation(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultItemViewModel], String?, String?))->Void) { private func requestPostSearchTypeContinuation(_ url:URL, parameters:Parameters, completion:@escaping (([MPPositive_SearchResultItemViewModel], String?, String?))->Void) {
//post //post

View File

@ -103,7 +103,10 @@ class MP_PlayerManager:NSObject{
super.init() super.init()
// //
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem) 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(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload)
//
NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_ :)), notificationName: .net_switch_reachable)
} }
deinit { deinit {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
@ -160,7 +163,24 @@ class MP_PlayerManager:NSObject{
loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil) 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: "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.seek(to: currentTime)
player.play()
playState = .Playing
}
}
//KVO //KVO
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else { guard let keyPath = keyPath else {
@ -174,14 +194,6 @@ class MP_PlayerManager:NSObject{
if playState != .Playing { if playState != .Playing {
//statuVlaueplayerItem //statuVlaueplayerItem
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放") print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放")
//
print("开始播放音乐-\(loadPlayer.currentVideo?.title ?? "")")
player.play()
playState = .Playing
//
if startActionBlock != nil {
startActionBlock!()
}
} }
}else { }else {
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")") print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")")
@ -211,7 +223,21 @@ class MP_PlayerManager:NSObject{
} }
case "playbackLikelyToKeepUp":// case "playbackLikelyToKeepUp"://
break if let playbackLikelyToKeepUp = change?[.newKey] as? Bool, playbackLikelyToKeepUp == true {
if playState != .Playing {
//
player.play()
playState = .Playing
//
if startActionBlock != nil {
startActionBlock!()
}
}
}else {
//
player.pause()
playState = .Null
}
default: default:
break break
} }
@ -324,7 +350,7 @@ class MP_PlayerManager:NSObject{
switch playType { switch playType {
case .random:// case .random://
for (index, item) in loadPlayer.randomVideos.enumerated() { for (index, item) in loadPlayer.randomVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index - 1 nextIndex = index - 1
} }
@ -333,15 +359,15 @@ class MP_PlayerManager:NSObject{
if nextIndex < 0 { if nextIndex < 0 {
// //
let last = loadPlayer.randomVideos.last let last = loadPlayer.randomVideos.last
loadPlayer.improveData(last?.videoId ?? "") loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
}else { }else {
// //
let song = loadPlayer.randomVideos[nextIndex] let song = loadPlayer.randomVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "") loadPlayer.improveData(song.videoId ?? "", isRandom: true)
} }
default:// default://
for (index, item) in loadPlayer.songVideos.enumerated() { for (index, item) in loadPlayer.songVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index - 1 nextIndex = index - 1
} }
@ -366,7 +392,7 @@ class MP_PlayerManager:NSObject{
switch playType { switch playType {
case .random: case .random:
for (index, item) in loadPlayer.randomVideos.enumerated() { for (index, item) in loadPlayer.randomVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index + 1 nextIndex = index + 1
} }
@ -375,15 +401,15 @@ class MP_PlayerManager:NSObject{
if nextIndex > (loadPlayer.randomVideos.count-1) { if nextIndex > (loadPlayer.randomVideos.count-1) {
// //
let first = loadPlayer.randomVideos.first let first = loadPlayer.randomVideos.first
loadPlayer.improveData(first?.videoId ?? "") loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
}else { }else {
//,ID //,ID
let song = loadPlayer.randomVideos[nextIndex] let song = loadPlayer.randomVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "") loadPlayer.improveData(song.videoId ?? "", isRandom: true)
} }
default: default:
for (index, item) in loadPlayer.songVideos.enumerated() { for (index, item) in loadPlayer.songVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index + 1 nextIndex = index + 1
} }
@ -411,7 +437,10 @@ class MP_PlayerManager:NSObject{
//KVO //KVO
video.resourcePlayerItem.removeObserver(self, forKeyPath: "status") video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges") 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 { if loadPlayer.currentVideo != nil {
// //

View File

@ -33,13 +33,19 @@ class MPPositive_SongViewModel: NSObject {
var song:MPPositive_SongItemModel! var song:MPPositive_SongItemModel!
/// ///
var isPloading:Bool = false var isPloading:Bool = false
//
private var isCancelled = false
init(_ song:MPPositive_SongItemModel) { init(_ song:MPPositive_SongItemModel) {
super.init() super.init()
self.song = song self.song = song
configure() configure()
} }
deinit { deinit {
//
resourcePlayerItem = nil
resourceAsset = nil
isCancelled = true
print("\(title ?? "")被释放了")
} }
// //
private func configure() { private func configure() {
@ -76,9 +82,9 @@ class MPPositive_SongViewModel: NSObject {
} }
reloadCollectionAndDownLoad() reloadCollectionAndDownLoad()
// //
if isPloading == false { // if isPloading == false {
preloadAsset(resourceAsset) // preloadAsset(resourceAsset)
} // }
} }
// //
func reloadCollectionAndDownLoad() { func reloadCollectionAndDownLoad() {
@ -91,12 +97,19 @@ class MPPositive_SongViewModel: NSObject {
// //
func preloadAsset(_ asset:AVURLAsset) { func preloadAsset(_ asset:AVURLAsset) {
guard isPloading == false else {
return
}
self.isPloading = true
// //
if #available(iOS 16, *) { if #available(iOS 16, *) {
//ios16 //ios16
Task{ Task{
do{ do{
let playable = try await asset.load(.isPlayable) let playable = try await asset.load(.isPlayable)
guard !isCancelled else {
return
}
if playable == true { if playable == true {
print("\(self.title ?? "")预加载成功") print("\(self.title ?? "")预加载成功")
self.isPloading = true self.isPloading = true
@ -105,8 +118,9 @@ class MPPositive_SongViewModel: NSObject {
switch asset.status(of: .isPlayable) { switch asset.status(of: .isPlayable) {
case .failed(let erro): case .failed(let erro):
print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)") print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)")
self.isPloading = false
default: default:
break self.isPloading = false
} }
} }
}catch{ }catch{
@ -118,7 +132,7 @@ class MPPositive_SongViewModel: NSObject {
let keys = ["playable"] let keys = ["playable"]
asset.loadValuesAsynchronously(forKeys: keys) { asset.loadValuesAsynchronously(forKeys: keys) {
[weak self] in [weak self] in
guard let self = self else {return} guard let self = self, !isCancelled else {return}
for key in keys { for key in keys {
var error: NSError? = nil var error: NSError? = nil
let status = asset.statusOfValue(forKey: key, error: &error) let status = asset.statusOfValue(forKey: key, error: &error)
@ -131,8 +145,10 @@ class MPPositive_SongViewModel: NSObject {
} }
case .failed: case .failed:
print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")") print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")")
self.isPloading = false
case .cancelled: case .cancelled:
print("\(title ?? "")预加载被取消了") print("\(title ?? "")预加载被取消了")
self.isPloading = false
default: default:
break break
} }

View File

@ -26,9 +26,8 @@ class MPPositive_PlayerLoadViewModel: NSObject {
} }
} }
} }
///ID
var currentVideoId:String! ///ViewModel
///ViewModel
var listViewVideos:[MPPositive_SongViewModel]! var listViewVideos:[MPPositive_SongViewModel]!
/// ///
var group:DispatchGroup? var group:DispatchGroup?
@ -42,17 +41,32 @@ class MPPositive_PlayerLoadViewModel: NSObject {
// //
self.randomVideos = self.songVideos.shuffled() self.randomVideos = self.songVideos.shuffled()
self.listViewVideos = [] self.listViewVideos = []
self.currentVideoId = currentVideoId
} }
///Video23VideoViewModel, ///Video23VideoViewModel,
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 //targetVideoId
guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else { guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else {
return return
} }
//Video
var array:[MPPositive_SongItemModel] = []
array.append(self.songVideos[targetIndex]) array.append(self.songVideos[targetIndex])
// //
let previousIndex = targetIndex-1 let previousIndex = targetIndex-1
@ -63,6 +77,7 @@ class MPPositive_PlayerLoadViewModel: NSObject {
if nextIndex < songVideos.count { if nextIndex < songVideos.count {
array.append(self.songVideos[nextIndex]) array.append(self.songVideos[nextIndex])
} }
}
//ViewModelvideo //ViewModelvideo
let videoIDs = Set(listViewVideos.map({$0.song.videoId})) let videoIDs = Set(listViewVideos.map({$0.song.videoId}))
//videoID, //videoID,
@ -89,11 +104,12 @@ class MPPositive_PlayerLoadViewModel: NSObject {
} }
} }
group?.notify(queue: .main, execute: { group?.notify(queue: .main, execute: {
// [weak self] in
self.listViewVideos = self.listViewVideos.sorted(by: {$0.index < $1.index}) //
// self?.currentVideo = self?.listViewVideos.first(where: {$0.song.videoId == targetVideoId})
self.currentVideo = self.listViewVideos.first(where: {$0.song.videoId == targetVideoId}) //
self.group = nil self?.listViewVideos = self?.listViewVideos.suffix(3)
self?.group = nil
}) })
} }
/// ///

View File

@ -308,7 +308,7 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
coverView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle coverView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle
lyricsView.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title lyricsView.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title
lyricsView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle lyricsView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle
lyricsView.lyricsLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics ?? "No Lyrics" lyricsView.lyricsLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics?.isEmpty == true ? "No Lyrics":MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics
coverView.loadBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false coverView.loadBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false
coverView.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false coverView.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false
} }
@ -469,6 +469,9 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
} }
//// ////
@objc private func typeClick(_ sender:UIButton) { @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 var value = MP_PlayerManager.shared.getPlayType().rawValue
value += 1 value += 1
@ -477,18 +480,27 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
} }
MP_PlayerManager.shared.setPlayType(.init(rawValue: value)!) MP_PlayerManager.shared.setPlayType(.init(rawValue: value)!)
} }
}
// //
@objc private func nextClick(_ sender:UIButton) { @objc private func nextClick(_ sender:UIButton) {
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
coverView.sliderView.value = 0 coverView.sliderView.value = 0
playBtn.isUserInteractionEnabled = false playBtn.isUserInteractionEnabled = false
MP_PlayerManager.shared.nextEvent() MP_PlayerManager.shared.nextEvent()
}
} }
// //
@objc private func previousClick(_ sender:UIButton) { @objc private func previousClick(_ sender:UIButton) {
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
coverView.sliderView.value = 0 coverView.sliderView.value = 0
playBtn.isUserInteractionEnabled = false playBtn.isUserInteractionEnabled = false
MP_PlayerManager.shared.previousEvent() MP_PlayerManager.shared.previousEvent()
} }
}
} }

View File

@ -57,6 +57,21 @@ 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) lazy var durationLabel:UILabel = createLabel("00:00" ,font: .systemFont(ofSize: 12*width, weight: .medium), textColor: .init(hex: "#FFFFFF", alpha: 0.85), textAlignment: .left)
///Label ///Label
lazy var maxTimesLabel:UILabel = createLabel("00:00" ,font: .systemFont(ofSize: 12*width, weight: .medium), textColor: .init(hex: "#FFFFFF", alpha: 0.6), textAlignment: .right) 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
}()
@ -65,6 +80,12 @@ class MPPositive_PlayerCoverView: UIView {
backgroundColor = .clear backgroundColor = .clear
configure() 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)
} }
@ -77,6 +98,7 @@ class MPPositive_PlayerCoverView: UIView {
// restoreDownloadProgress() // restoreDownloadProgress()
} }
public func restoreDownloadProgress() { public func restoreDownloadProgress() {
if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo,
@ -116,6 +138,11 @@ class MPPositive_PlayerCoverView: UIView {
deinit {
NotificationCenter.default.removeObserver(self)
}
// //
private func configure() { private func configure() {
// //
@ -126,6 +153,11 @@ class MPPositive_PlayerCoverView: UIView {
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(12*width) 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) addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
@ -178,6 +210,23 @@ class MPPositive_PlayerCoverView: UIView {
// //
restoreDownloadProgress() restoreDownloadProgress()
} }
//
@objc private func netWorkNotReachableAction(_ sender:Notification) {
DispatchQueue.main.async {
[weak self] in
guard let self = self else {return}
maskNotReachableView.isHidden = false
}
}
//
@objc private func netWorkReachableAction(_ sender:Notification) {
DispatchQueue.main.async {
[weak self] in
guard let self = self else {return}
maskNotReachableView.isHidden = true
}
}
// //
@objc private func seekProgressClick(_ sender: UISlider, forEvent event: UIEvent) { @objc private func seekProgressClick(_ sender: UISlider, forEvent event: UIEvent) {
//touchEvent //touchEvent