# 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 */; };
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 */,

View File

@ -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"

View File

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

View File

@ -83,6 +83,10 @@ extension NotificationCenter{
case player_type_switch
///
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"
///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 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: -
//访
private var visitorData:String?
@ -133,21 +157,17 @@ class MP_NetWorkManager: NSObject {
monitor.start(queue: queue)
}
///
private func requestStatusToYouTube(_ isAilable:@escaping(Bool) -> Void) {
//
let reachabilityManager = NetworkReachabilityManager(host: "https://music.youtube.com/")
func requestStatusToYouTube() {
//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
}
})
}
@ -189,11 +209,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,12 +272,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostAlbumOrList(url, parameters: parameters) { results in
comletion(results)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostAlbumOrList(url, parameters: parameters) { results in
comletion(results)
}
}
///
@ -315,12 +329,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostArtist(url, parameters: parameters) { result in
comletion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostArtist(url, parameters: parameters) { result in
comletion(result)
}
}
//
@ -374,12 +385,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostArtistMore(url, parameters: parameters) { result in
comletion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostArtistMore(url, parameters: parameters) { result in
comletion(result)
}
}
///
@ -430,12 +438,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostArtistMoreContinuation(url, parameters: parameters) { result in
comletion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostArtistMoreContinuation(url, parameters: parameters) { result in
comletion(result)
}
}
///
@ -487,14 +492,10 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
//next
self.requestPostNextList(url, parameters: parameters) { listSongs in
//
completion(listSongs)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostNextList(url, parameters: parameters) { listSongs in
//
completion(listSongs)
}
}
//next
@ -542,13 +543,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
//next/
self.requestPostNextLyricsAndRelated(url, parameters: parameters) { result in
completion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostNextLyricsAndRelated(url, parameters: parameters) { result in
completion(result)
}
}
//Next/
@ -596,12 +593,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
completion(resourceUlrs, coverUrls)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
completion(resourceUlrs, coverUrls)
}
}
///
@ -651,12 +645,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostLyric(url, parameters: parameters) { lyrics in
completion(lyrics)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostLyric(url, parameters: parameters) { lyrics in
completion(lyrics)
}
}
//
@ -701,12 +692,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostRecommend(url, parameters: parameters) { results in
completion(results)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostRecommend(url, parameters: parameters) { results in
completion(results)
}
}
///
@ -757,12 +745,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostSearchSuggestions(url, parameters: parameters) { result in
completion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostSearchSuggestions(url, parameters: parameters) { result in
completion(result)
}
}
//
@ -811,12 +796,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostSearchPreviewResults(url, parameters: parameters) { result in
completion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostSearchPreviewResults(url, parameters: parameters) { result in
completion(result)
}
}
//
@ -871,12 +853,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostSearchTypeResults(url, parameters: parameters) { result in
completion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostSearchTypeResults(url, parameters: parameters) { result in
completion(result)
}
}
//
@ -931,12 +910,9 @@ extension MP_NetWorkManager {
]
]
]
requestStatusToYouTube { isAvailable in
if isAvailable == true {
self.requestPostSearchTypeContinuation(url, parameters: parameters) { result in
completion(result)
}
}
guard netWorkStatu != .notReachable else {return}
requestPostSearchTypeContinuation(url, parameters: parameters) { result in
completion(result)
}
}
//

View File

@ -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,24 @@ 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.seek(to: 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 +194,6 @@ class MP_PlayerManager:NSObject{
if playState != .Playing {
//statuVlaueplayerItem
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 +223,21 @@ class MP_PlayerManager:NSObject{
}
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:
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 {
//

View File

@ -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
}

View File

@ -26,9 +26,8 @@ class MPPositive_PlayerLoadViewModel: NSObject {
}
}
}
///ID
var currentVideoId:String!
///ViewModel
///ViewModel
var listViewVideos:[MPPositive_SongViewModel]!
///
var group:DispatchGroup?
@ -42,26 +41,42 @@ class MPPositive_PlayerLoadViewModel: NSObject {
//
self.randomVideos = self.songVideos.shuffled()
self.listViewVideos = []
self.currentVideoId = currentVideoId
}
///Video23VideoViewModel,
func improveData(_ targetVideoId:String) {
//targetVideoId
guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else {
return
}
func improveData(_ targetVideoId:String, isRandom:Bool = false) {
//Video
var array:[MPPositive_SongItemModel] = []
array.append(self.songVideos[targetIndex])
//
let previousIndex = targetIndex-1
if previousIndex >= 0 {
array.append(self.songVideos[previousIndex])
}
let nextIndex = targetIndex+1
if nextIndex < songVideos.count {
array.append(self.songVideos[nextIndex])
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
}
array.append(self.songVideos[targetIndex])
//
let previousIndex = targetIndex-1
if previousIndex >= 0 {
array.append(self.songVideos[previousIndex])
}
let nextIndex = targetIndex+1
if nextIndex < songVideos.count {
array.append(self.songVideos[nextIndex])
}
}
//ViewModelvideo
let videoIDs = Set(listViewVideos.map({$0.song.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
})
}
///

View File

@ -308,7 +308,7 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
coverView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle
lyricsView.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title
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.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false
}
@ -469,26 +469,38 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
}
////
@objc private func typeClick(_ sender:UIButton) {
//
var value = MP_PlayerManager.shared.getPlayType().rawValue
value += 1
if value > 2 {
value = 0
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
//
var value = MP_PlayerManager.shared.getPlayType().rawValue
value += 1
if value > 2 {
value = 0
}
MP_PlayerManager.shared.setPlayType(.init(rawValue: value)!)
}
MP_PlayerManager.shared.setPlayType(.init(rawValue: value)!)
}
//
@objc private func nextClick(_ sender:UIButton) {
coverView.sliderView.value = 0
playBtn.isUserInteractionEnabled = false
MP_PlayerManager.shared.nextEvent()
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) {
coverView.sliderView.value = 0
playBtn.isUserInteractionEnabled = false
MP_PlayerManager.shared.previousEvent()
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
coverView.sliderView.value = 0
playBtn.isUserInteractionEnabled = false
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)
///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
}()
@ -65,6 +80,12 @@ class MPPositive_PlayerCoverView: UIView {
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)
}
@ -77,6 +98,7 @@ class MPPositive_PlayerCoverView: UIView {
// restoreDownloadProgress()
}
public func restoreDownloadProgress() {
if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo,
@ -116,6 +138,11 @@ class MPPositive_PlayerCoverView: UIView {
deinit {
NotificationCenter.default.removeObserver(self)
}
//
private func configure() {
//
@ -126,6 +153,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
@ -178,6 +210,23 @@ class MPPositive_PlayerCoverView: UIView {
//
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) {
//touchEvent