diff --git a/MusicPlayer.xcodeproj/project.pbxproj b/MusicPlayer.xcodeproj/project.pbxproj index cf1d904..cbea268 100644 --- a/MusicPlayer.xcodeproj/project.pbxproj +++ b/MusicPlayer.xcodeproj/project.pbxproj @@ -42,8 +42,14 @@ CB24169B2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169A2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift */; }; CB24169D2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169C2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift */; }; CB24169F2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169E2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift */; }; + CB48409C2C08721600341244 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */; }; + CB48409E2C08738700341244 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = CB48409D2C08738700341244 /* GoogleService-Info.plist */; }; + CB4840A02C087BD900341244 /* MP_AnalyticsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */; }; + CB4840A32C0882D100341244 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = CB4840A22C0882D100341244 /* FirebaseAnalytics */; }; + CB4840A52C0882D100341244 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = CB4840A42C0882D100341244 /* FirebaseCrashlytics */; }; CB5661292BE09D0500CFD014 /* MPPositive_JsonPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB5661282BE09D0500CFD014 /* MPPositive_JsonPlayer.swift */; }; CB56612D2BE0DF8C00CFD014 /* MP_WebWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB56612C2BE0DF8C00CFD014 /* MP_WebWork.swift */; }; + CBAFC9EF2C09A1FE0054500E /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */; }; CBB5D31D2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5D31C2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift */; }; CBB5D31F2BDF711600CC333D /* MPPositive_SongItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5D31E2BDF711600CC333D /* MPPositive_SongItemModel.swift */; }; CBB5D3222BDF80C800CC333D /* MPPositive_PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5D3212BDF80C800CC333D /* MPPositive_PlayerViewController.swift */; }; @@ -248,6 +254,9 @@ CB24169A2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_LoveArtistTableViewCell.swift; sourceTree = ""; }; CB24169C2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_LoveSongsViewController.swift; sourceTree = ""; }; CB24169E2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_OfflineSongsViewController.swift; sourceTree = ""; }; + CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + CB48409D2C08738700341244 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_AnalyticsManager.swift; sourceTree = ""; }; CB5661282BE09D0500CFD014 /* MPPositive_JsonPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_JsonPlayer.swift; sourceTree = ""; }; CB56612C2BE0DF8C00CFD014 /* MP_WebWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_WebWork.swift; sourceTree = ""; }; CBB5D31C2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_MusicItemShowTableViewCell.swift; sourceTree = ""; }; @@ -421,7 +430,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CB4840A52C0882D100341244 /* FirebaseCrashlytics in Frameworks */, + CB4840A32C0882D100341244 /* FirebaseAnalytics in Frameworks */, 639E3B772F558B3350DD56BA /* Pods_MusicPlayer.framework in Frameworks */, + CBAFC9EF2C09A1FE0054500E /* FirebaseRemoteConfig in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -456,6 +468,8 @@ 009662382BB14A5B00FCA65F /* Assets.xcassets */, 0096623A2BB14A5B00FCA65F /* LaunchScreen.storyboard */, 0096623D2BB14A5B00FCA65F /* Info.plist */, + CB48409D2C08738700341244 /* GoogleService-Info.plist */, + CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */, ); path = MusicPlayer; sourceTree = ""; @@ -643,20 +657,20 @@ children = ( CBCB4FAE2BD11402009760B3 /* MPSideA_CenterViewController.swift */, CBCB4FAF2BD11402009760B3 /* MPSideA_CenterViewController.xib */, - CBCB4FAC2BD11402009760B3 /* MPSideA_AboutViewController.swift */, - CBCB4FAD2BD11402009760B3 /* MPSideA_AboutViewController.xib */, - CBCB4FB02BD11402009760B3 /* MPSideA_DeleteViewController.swift */, - CBCB4FB12BD11402009760B3 /* MPSideA_DeleteViewController.xib */, CBCB4FB22BD11402009760B3 /* MPSideA_MoreViewController.swift */, CBCB4FB32BD11402009760B3 /* MPSideA_MoreViewController.xib */, - CBCB4FB42BD11402009760B3 /* MPSideA_PrivacyViewController.swift */, - CBCB4FB52BD11402009760B3 /* MPSideA_PrivacyViewController.xib */, + CBCB4FB02BD11402009760B3 /* MPSideA_DeleteViewController.swift */, + CBCB4FB12BD11402009760B3 /* MPSideA_DeleteViewController.xib */, CBCB4FB62BD11402009760B3 /* MPSideA_RenameViewController.swift */, CBCB4FB72BD11402009760B3 /* MPSideA_RenameViewController.xib */, - CBCB4FB82BD11402009760B3 /* MPSideA_ServiceViewController.swift */, - CBCB4FB92BD11402009760B3 /* MPSideA_ServiceViewController.xib */, CBCB4FBA2BD11402009760B3 /* MPSideA_SettingViewController.swift */, CBCB4FBB2BD11402009760B3 /* MPSideA_SettingViewController.xib */, + CBCB4FAC2BD11402009760B3 /* MPSideA_AboutViewController.swift */, + CBCB4FAD2BD11402009760B3 /* MPSideA_AboutViewController.xib */, + CBCB4FB82BD11402009760B3 /* MPSideA_ServiceViewController.swift */, + CBCB4FB92BD11402009760B3 /* MPSideA_ServiceViewController.xib */, + CBCB4FB42BD11402009760B3 /* MPSideA_PrivacyViewController.swift */, + CBCB4FB52BD11402009760B3 /* MPSideA_PrivacyViewController.xib */, ); path = "Center(个人资源)"; sourceTree = ""; @@ -1018,6 +1032,7 @@ CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */, CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */, CB102F542BFAFA7200E967D8 /* MP_DownloadManager.swift */, + CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */, ); path = "Tool(工具封装)"; sourceTree = ""; @@ -1076,6 +1091,11 @@ dependencies = ( ); name = MusicPlayer; + packageProductDependencies = ( + CB4840A22C0882D100341244 /* FirebaseAnalytics */, + CB4840A42C0882D100341244 /* FirebaseCrashlytics */, + CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */, + ); productName = MusicPlayer; productReference = 009662292BB14A5A00FCA65F /* MusicPlayer.app */; productType = "com.apple.product-type.application"; @@ -1105,6 +1125,9 @@ Base, ); mainGroup = 009662202BB14A5A00FCA65F; + packageReferences = ( + CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 0096622A2BB14A5A00FCA65F /* Products */; projectDirPath = ""; projectRoot = ""; @@ -1129,6 +1152,7 @@ CBC54E572BC4D5D3003B1901 /* Shhh….mp3 in Resources */, CBCB500D2BD11402009760B3 /* MPSideA_CustomTabBarView.xib in Resources */, CBCB50072BD11402009760B3 /* MPSideA_PlayerViewController.xib in Resources */, + CB48409E2C08738700341244 /* GoogleService-Info.plist in Resources */, 0096623C2BB14A5B00FCA65F /* LaunchScreen.storyboard in Resources */, CBC54E5C2BC4D5D3003B1901 /* TV.mp3 in Resources */, CBCB50152BD11402009760B3 /* MPSideA_Home_FourthListCollectionViewCell.xib in Resources */, @@ -1155,6 +1179,7 @@ CBCB50052BD11402009760B3 /* MPSideA_HomeViewController.xib in Resources */, CBC54E642BC4D5D3003B1901 /* Seawater Surging.mp3 in Resources */, CBCB500F2BD11402009760B3 /* MPSideA_CenterTableViewCell.xib in Resources */, + CB48409C2C08721600341244 /* PrivacyInfo.xcprivacy in Resources */, CBCB4FF32BD11402009760B3 /* MPSideA_AboutViewController.xib in Resources */, CBC54E582BC4D5D3003B1901 /* Shh Shh.mp3 in Resources */, CBC54E632BC4D5D3003B1901 /* Howling Wind.mp3 in Resources */, @@ -1300,6 +1325,7 @@ CBE1CB4E2BDE4BD800701D57 /* MPPositive_ListAlbumListViewModel.swift in Sources */, CB2416972C05D3C3007877F7 /* MPPositive_CollectionArtistViewModel.swift in Sources */, CBD313572BD63B390015D227 /* MPPositive_HomeListSecondCollectionViewCell.swift in Sources */, + CB4840A02C087BD900341244 /* MP_AnalyticsManager.swift in Sources */, 0096622D2BB14A5A00FCA65F /* AppDelegate.swift in Sources */, CBC32A532BD8D9F300687171 /* MPPositive_BrowseItemModel.swift in Sources */, CBE16B952BF251FF005B7EE6 /* MPPositive_SearchSuggestionItemListModel.swift in Sources */, @@ -1522,15 +1548,21 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = T93S37G27F; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MusicPlayer/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Musicoo; + INFOPLIST_KEY_CFBundleDisplayName = Musiclax; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!"; + INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!"; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"Musiclax\" requires you to turn on the microphone to recognize surrounding decibels and automatically turn on white noise for you. Do you allow this application to obtain your microphone permissions?"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "\"Musiclax\" requires opening your album to obtain photos, which are used to add your custom white noise. Do you want to allow this application to obtain your album permissions?"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -1538,8 +1570,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.lux.musicplayer.MusicPlayer; + MARKETING_VERSION = 1.0.2; + PRODUCT_BUNDLE_IDENTIFIER = relax.offline.mp3.music; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -1560,15 +1592,21 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = T93S37G27F; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MusicPlayer/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Musicoo; + INFOPLIST_KEY_CFBundleDisplayName = Musiclax; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!"; + INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!"; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"Musiclax\" requires you to turn on the microphone to recognize surrounding decibels and automatically turn on white noise for you. Do you allow this application to obtain your microphone permissions?"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "\"Musiclax\" requires opening your album to obtain photos, which are used to add your custom white noise. Do you want to allow this application to obtain your album permissions?"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -1576,8 +1614,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.lux.musicplayer.MusicPlayer; + MARKETING_VERSION = 1.0.2; + PRODUCT_BUNDLE_IDENTIFIER = relax.offline.mp3.music; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -1613,6 +1651,35 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 10.27.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CB4840A22C0882D100341244 /* FirebaseAnalytics */ = { + isa = XCSwiftPackageProductDependency; + package = CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAnalytics; + }; + CB4840A42C0882D100341244 /* FirebaseCrashlytics */ = { + isa = XCSwiftPackageProductDependency; + package = CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCrashlytics; + }; + CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */ = { + isa = XCSwiftPackageProductDependency; + package = CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseRemoteConfig; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ 009662352BB14A5A00FCA65F /* MusicPlayer.xcdatamodeld */ = { isa = XCVersionGroup; diff --git a/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer.xcscheme b/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer.xcscheme index 54aea04..b9ab31a 100644 --- a/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer.xcscheme +++ b/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer.xcscheme @@ -49,6 +49,16 @@ ReferencedContainer = "container:MusicPlayer.xcodeproj"> + + + + + + Void) { if identifier == "com.yourApp.backgroundDownload" { - DownloadManager.shared.session = SessionManager("com.yourApp.backgroundDownload", configuration: .init()) - DownloadManager.shared.session.completionHandler = completionHandler + MP_DownloadManager.shared.session = SessionManager("com.yourApp.backgroundDownload", configuration: .init()) + MP_DownloadManager.shared.session.completionHandler = completionHandler } } diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-1024.png index d50120b..e48335b 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-1024.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png index ea0d228..728ec5b 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png index 39f5418..64e479a 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png index cea2d48..0d4dac8 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png index 41f7012..7ebc301 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png index 380210b..b421c10 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png index db5d05f..3af1e4a 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png index c11ac49..28b09e4 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png index c6039c7..a63a7f8 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png index c6039c7..a63a7f8 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png index 0752db9..2a33b60 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png index b88e66b..036e55a 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png index aaa732c..5c2f0ef 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png index a29fb22..95cf423 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png index 59dda5c..65654dc 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png index c321735..0b02f66 100644 Binary files a/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png and b/MusicPlayer/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/ICON.imageset/Contents.json b/MusicPlayer/Assets.xcassets/ICON.imageset/Contents.json index 70d9fd9..b10c220 100644 --- a/MusicPlayer/Assets.xcassets/ICON.imageset/Contents.json +++ b/MusicPlayer/Assets.xcassets/ICON.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "filename" : "img_v3_02ae_ad486134-21d1-4b05-869c-06e3d548e40g.jpg", "idiom" : "universal", "scale" : "1x" }, { + "filename" : "img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png", "idiom" : "universal", "scale" : "3x" } diff --git a/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02ae_ad486134-21d1-4b05-869c-06e3d548e40g.jpg b/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02ae_ad486134-21d1-4b05-869c-06e3d548e40g.jpg deleted file mode 100644 index 495a750..0000000 Binary files a/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02ae_ad486134-21d1-4b05-869c-06e3d548e40g.jpg and /dev/null differ diff --git a/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png b/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png new file mode 100644 index 0000000..ceb2fae Binary files /dev/null and b/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png differ diff --git a/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png b/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png new file mode 100644 index 0000000..ceb2fae Binary files /dev/null and b/MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png differ diff --git a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/Contents.json b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/Contents.json index ac68911..a927c8f 100644 --- a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/Contents.json +++ b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "img_v3_02ae_c419ebcc-7bcc-4018-a6f4-b4cb25f7d11g.png", + "filename" : "img_v3_02b6_3f726db0-fde6-4335-a78b-2a4ba9365ccg.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_v3_02ae_2efa5fb1-c28e-4299-b6c7-abe303cb6f8g.png", + "filename" : "img_v3_02b6_97959922-6ac1-437f-aab5-c65850cdb09g.png", "idiom" : "universal", "scale" : "3x" } diff --git a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02ae_2efa5fb1-c28e-4299-b6c7-abe303cb6f8g.png b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02ae_2efa5fb1-c28e-4299-b6c7-abe303cb6f8g.png deleted file mode 100644 index 47eedb3..0000000 Binary files a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02ae_2efa5fb1-c28e-4299-b6c7-abe303cb6f8g.png and /dev/null differ diff --git a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02ae_c419ebcc-7bcc-4018-a6f4-b4cb25f7d11g.png b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02ae_c419ebcc-7bcc-4018-a6f4-b4cb25f7d11g.png deleted file mode 100644 index 6ae537b..0000000 Binary files a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02ae_c419ebcc-7bcc-4018-a6f4-b4cb25f7d11g.png and /dev/null differ diff --git a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02b6_3f726db0-fde6-4335-a78b-2a4ba9365ccg.png b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02b6_3f726db0-fde6-4335-a78b-2a4ba9365ccg.png new file mode 100644 index 0000000..d8b8d24 Binary files /dev/null and b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02b6_3f726db0-fde6-4335-a78b-2a4ba9365ccg.png differ diff --git a/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02b6_97959922-6ac1-437f-aab5-c65850cdb09g.png b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02b6_97959922-6ac1-437f-aab5-c65850cdb09g.png new file mode 100644 index 0000000..780c67a Binary files /dev/null and b/MusicPlayer/Assets.xcassets/Lunch/Lunch'bg.imageset/img_v3_02b6_97959922-6ac1-437f-aab5-c65850cdb09g.png differ diff --git a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Contents.json b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Contents.json index ab8b539..a8cc34a 100644 --- a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Contents.json +++ b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "Group_1597880487@2x.png", + "filename" : "Group_1597880484@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Group_1597880487@3x.png", + "filename" : "Group_1597880484@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@2x.png b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@2x.png new file mode 100644 index 0000000..a76b0ed Binary files /dev/null and b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@2x.png differ diff --git a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@3x.png b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@3x.png new file mode 100644 index 0000000..619afdc Binary files /dev/null and b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@3x.png differ diff --git a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880487@2x.png b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880487@2x.png deleted file mode 100644 index f4862d5..0000000 Binary files a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880487@2x.png and /dev/null differ diff --git a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880487@3x.png b/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880487@3x.png deleted file mode 100644 index 1a7783e..0000000 Binary files a/MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880487@3x.png and /dev/null differ diff --git a/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/Contents.json b/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/Contents.json deleted file mode 100644 index 320c9ff..0000000 --- a/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "MUSICOO@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "MUSICOO@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/MUSICOO@2x.png b/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/MUSICOO@2x.png deleted file mode 100644 index 89acfc1..0000000 Binary files a/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/MUSICOO@2x.png and /dev/null differ diff --git a/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/MUSICOO@3x.png b/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/MUSICOO@3x.png deleted file mode 100644 index fa72ce7..0000000 Binary files a/MusicPlayer/Assets.xcassets/SideA/Center/MUSICOO'logo.imageset/MUSICOO@3x.png and /dev/null differ diff --git a/MusicPlayer/GoogleService-Info.plist b/MusicPlayer/GoogleService-Info.plist new file mode 100644 index 0000000..f6c548f --- /dev/null +++ b/MusicPlayer/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyDIsVu-mThBUmZvq6FiVlcq6mTElUJTuhg + GCM_SENDER_ID + 773095886766 + PLIST_VERSION + 1 + BUNDLE_ID + relax.offline.mp3.music + PROJECT_ID + musiclax-ios + STORAGE_BUCKET + musiclax-ios.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:773095886766:ios:54ac9cf528ca696540823f + + \ No newline at end of file diff --git a/MusicPlayer/Info.plist b/MusicPlayer/Info.plist index 8f0456f..5452127 100644 --- a/MusicPlayer/Info.plist +++ b/MusicPlayer/Info.plist @@ -2,39 +2,14 @@ - UIViewControllerBasedStatusBarAppearance - - UIStatusBarStyle - UIStatusBarStyleLightContent - NSMicrophoneUsageDescription - "Musicoo" requires you to turn on the microphone to recognize surrounding decibels and automatically turn on white noise for you. Do you allow this application to obtain your microphone permissions? - NSPhotoLibraryUsageDescription - "Musicoo" requires opening your album to obtain photos, which are used to add your custom white noise. Do you want to allow this application to obtain your album permissions? UIBackgroundModes - audio + fetch + backgroundFetch - NSLocationWhenInUseUsageDescription - "Musicoo" needs to obtain your location information in order to refine the preview music information provided to you! - NSLocationAlwaysUsageDescription - "Musicoo" needs to obtain your location information in order to refine the preview music information provided to you! - NSLocationAlwaysAndWhenInUseUsageDescription - "Musicoo" needs to obtain your location information in order to refine the preview music information provided to you! - CFBundleURLTypes - - - CFBundleURLSchemes - - myapp - - CFBundleURLName - com.lux.musicplayer.MusicPlayer - - - UIBackgroundModes - - fetch - backgroundFetch - + UIViewControllerBasedStatusBarAppearance + + NSUserTrackingUsageDescription + "Musiclax" needs to request tracking permissions to provide a personalized advertising experience. We respect and protect your privacy and will not sell your data to third parties. diff --git a/MusicPlayer/MP/Common/Base(公用基类)/Controllers/MP_LunchViewController.swift b/MusicPlayer/MP/Common/Base(公用基类)/Controllers/MP_LunchViewController.swift index 50f135f..c668fbf 100644 --- a/MusicPlayer/MP/Common/Base(公用基类)/Controllers/MP_LunchViewController.swift +++ b/MusicPlayer/MP/Common/Base(公用基类)/Controllers/MP_LunchViewController.swift @@ -16,9 +16,11 @@ class MP_LunchViewController: UIViewController { //帧计时器 private var timer:CADisplayLink! //最大计时值 - private lazy var maxTimes:TimeInterval = 8 + private lazy var maxTimes:TimeInterval = 6 //当前计时值 private lazy var currentTimes:TimeInterval = 0 + //计时器结束事件 + private var completionBlock:(() -> Void)? override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .init(hex: "#000000") @@ -27,14 +29,60 @@ class MP_LunchViewController: UIViewController { timer.preferredFramesPerSecond = 20 //开辟线程 timer.add(to: RunLoop.current, forMode: .common) - //启动计时器 - timer.isPaused = false + //获取idfa + _ = requestTrackingAuthorization(self) //获取定位权限 MP_LocationManager.shared.setLocationPermission(self, complete: nil) - //获取youtube网站信息 - MP_WebWork.shared.pingYoutubeHome() - - NotificationCenter.notificationKey.add(observer: self, selector: #selector(jumpAction(_:)), notificationName: .js_edit_completion) + MP_AnalyticsManager.shared.getOpenStatus { [weak self] open in + guard let self = self else {return} + if open { + //根据ip值确定进入那个页面 + MP_NetWorkManager.shared.requestIPInfo { statu in + if statu == true { + //允许进入b面 + print("BLog") + self.completionBlock = { + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + //停止计时器 + timer.isPaused = true + //加载完毕,判断并跳转 + accessAppdelegate.switch_positive() + } + } + }else { + print("ALog") + //打开A面 + self.completionBlock = { + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + //停止计时器 + timer.isPaused = true + //加载完毕,判断并跳转 + accessAppdelegate.switch_aSide() + } + } + } + } + }else { + print("ALog") + //打开A面 + completionBlock = { + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + //停止计时器 + timer.isPaused = true + //加载完毕,判断并跳转 + accessAppdelegate.switch_aSide() + } + } + } + } + //启动计时器 + timer.isPaused = false } deinit { //销毁计时器 @@ -42,15 +90,9 @@ class MP_LunchViewController: UIViewController { timer = nil NotificationCenter.default.removeObserver(self) } - @objc private func jumpAction(_ sender:Notification) { - DispatchQueue.main.async { - [weak self] in - guard let self = self else {return} - //停止计时器 - timer.isPaused = true - //加载完毕,判断并跳转 - accessAppdelegate.switch_positive() - } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + MP_AnalyticsManager.shared.launch_pvAction() } //计时器执行事件 @@ -66,14 +108,9 @@ class MP_LunchViewController: UIViewController { progressView.setProgress(value) } }else { -// DispatchQueue.main.async { -// [weak self] in -// guard let self = self else {return} -// //停止计时器 -// timer.isPaused = true -// //加载完毕,判断并跳转 -// accessAppdelegate.switch_positive() -// } + if completionBlock != nil { + completionBlock!() + } } } } diff --git a/MusicPlayer/MP/Common/Extension(扩展)/ImagePicker.swift b/MusicPlayer/MP/Common/Extension(扩展)/ImagePicker.swift index c81c968..e183b74 100644 --- a/MusicPlayer/MP/Common/Extension(扩展)/ImagePicker.swift +++ b/MusicPlayer/MP/Common/Extension(扩展)/ImagePicker.swift @@ -118,7 +118,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{ private func album(){ DispatchQueue.main.async { //次要处理 - let alertController = UIAlertController(title: "Access album permission request", message: "“Musicoo”currently does not have permission to access the album and cannot obtain album data. If you want to use the photo album function, please click “Settings” to allow this App to obtain permission to access the photo album", preferredStyle: .alert) + let alertController = UIAlertController(title: "Access album permission request", message: "“Musiclax”currently does not have permission to access the album and cannot obtain album data. If you want to use the photo album function, please click “Settings” to allow this App to obtain permission to access the photo album", preferredStyle: .alert) let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in } @@ -141,7 +141,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{ private func camera(){ DispatchQueue.main.async { //次要处理 - let alertController = UIAlertController(title: "Access camera permission request", message: "“Musicoo”does not have access to the camera, and cannot call camera functions. If you want to use the camera function, please click “Settings” to allow this App to gain access to the camera", preferredStyle: .alert) + let alertController = UIAlertController(title: "Access camera permission request", message: "“Musiclax”does not have access to the camera, and cannot call camera functions. If you want to use the camera function, please click “Settings” to allow this App to gain access to the camera", preferredStyle: .alert) let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in } @@ -164,7 +164,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{ private func video(){ DispatchQueue.main.async { //次要处理 - let alertController = UIAlertController(title: "Access album permission request", message: "“Musicoo” needs to open your album to get the photos that will be used to add your custom white noise. Please go to ”Settings“ to turn on this permission!", preferredStyle: .alert) + let alertController = UIAlertController(title: "Access album permission request", message: "“Musiclax” needs to open your album to get the photos that will be used to add your custom white noise. Please go to ”Settings“ to turn on this permission!", preferredStyle: .alert) let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in } diff --git a/MusicPlayer/MP/Common/Extension(扩展)/Notification.swift b/MusicPlayer/MP/Common/Extension(扩展)/Notification.swift index 28ca76e..4bcedf2 100644 --- a/MusicPlayer/MP/Common/Extension(扩展)/Notification.swift +++ b/MusicPlayer/MP/Common/Extension(扩展)/Notification.swift @@ -95,6 +95,8 @@ extension NotificationCenter{ case positive_nav_push ///b面Pop时 case positive_nav_pop + ///b面网络请求报错 + case netWork_error_deal } } } diff --git a/MusicPlayer/MP/Common/Macro(宏定义与全局量)/Macro.swift b/MusicPlayer/MP/Common/Macro(宏定义与全局量)/Macro.swift index 34bc674..49ac272 100644 --- a/MusicPlayer/MP/Common/Macro(宏定义与全局量)/Macro.swift +++ b/MusicPlayer/MP/Common/Macro(宏定义与全局量)/Macro.swift @@ -7,6 +7,8 @@ import UIKit import Foundation import AVFoundation +import AppTrackingTransparency +import AdSupport @_exported import JXSegmentedView @_exported import JXPagingView //给JXPagingListContainerView添加extension,表示遵从JXSegmentedViewListContainer的协议 @@ -38,15 +40,22 @@ let Phone_Model = UIDevice.current.model let System_Version = UIDevice.current.systemVersion ///获取当前系统语言 let Language_first_local = NSLocale.preferredLanguages.first! - +///当前应用版本号 +var app_Version:String{ + if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String { + return version + }else { + return "1.0.0" + } +} ///底部安全区域 let bottomPadding = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0 ///全局占位图 let placeholderImage:UIImage = UIImage(named: "Home First'placeholder")! ///隐私政策网址 -let privacyUrl:URL = .init(string: "https://musicoo.app/privacy")! +let privacyUrl:URL = .init(string: "https://musiclax.mystrikingly.com/privacy")! ///用户协议网址 -let serviceUrl:URL = .init(string: "https://musicoo.app/terms")! +let serviceUrl:URL = .init(string: "https://musiclax.mystrikingly.com/terms")! //MARK: - 全局变量与方法 ///总事件闭包 @@ -71,6 +80,20 @@ func getDocumentsFileURL(_ videoID: String) -> String? { return nil } } +///调用next对单曲数据歌词ID与相关ID补全 +func improveDataforLycirsAndRelated(_ song:MPPositive_SongItemModel, completion:@escaping(((String?,String?)) -> Void)) { + //单曲补全需要再次调用next接口 + MP_NetWorkManager.shared.requestNextLyricsAndRelated(song){ result in + completion(result) + } +} +///调用player对资源路径和封面路径补全 +func improveDataforResouceAndCover(_ song:MPPositive_SongItemModel, completion:@escaping((([String],[Int],[String]), [String]?) -> Void)) { + //单曲补全需要调用player接口 + MP_NetWorkManager.shared.requestAndroidPlayer(song.videoId, playlistId: "") { resourceUrls, coverUrls in + completion(resourceUrls,coverUrls) + } +} ///转时分值 func setTimesToMinSeconds(_ time:TimeInterval) -> String { //设置分钟 @@ -99,7 +122,7 @@ func authorize(observe:UIViewController) -> Bool{ }) default: () DispatchQueue.main.async(execute: { () -> Void in - let alertController = UIAlertController(title: "Get Microphone Access",message: "“Musicoo” asks you to turn on your microphone to recognize the decibels around you and turns on white noise for you automatically. Please go to the “Settings” page to turn on the microphone permission",preferredStyle: .alert) + let alertController = UIAlertController(title: "Get Microphone Access",message: "“Musiclax” asks you to turn on your microphone to recognize the decibels around you and turns on white noise for you automatically. Please go to the “Settings” page to turn on the microphone permission",preferredStyle: .alert) let cancelAction = UIAlertAction(title:"Cancel", style: .cancel, handler:nil) let settingsAction = UIAlertAction(title:"Settings", style: .default, handler: { (action) -> Void in @@ -143,3 +166,41 @@ func switchPlayTypeBtnIcon(_ btn:UIButton) { btn.setBackgroundImage(UIImage(named: "Player_Single'logo"), for: .normal) } } +///请求广告授权 +func requestTrackingAuthorization(_ observe:UIViewController) -> Bool { + if #available(iOS 14, *) { + // 检查当前的跟踪管理器状态 + let status = ATTrackingManager.trackingAuthorizationStatus + switch status { + case .notDetermined: + // 处理未知授权状态 + print("未知的跟踪状态") + ATTrackingManager.requestTrackingAuthorization { status in + let isAuthorized = status == .authorized + DispatchQueue.main.async { + _ = requestTrackingAuthorization(observe) + } + } + case .authorized: + // 用户授予了权限,可以获取 IDFA + print("用户授权跟踪") + return true + case .denied: + print("用户拒绝跟踪") + default:() + print("跟踪状态受限") + } + return false + } else { + return true + } +} +///获取IDFA +func getIDFA(_ observe:UIViewController) -> UUID? { + if requestTrackingAuthorization(observe) { + let idfa = ASIdentifierManager.shared().advertisingIdentifier + return idfa + }else { + return nil + } +} diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MPPositive_Debouncer.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MPPositive_Debouncer.swift index 6a8f06e..6b86854 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MPPositive_Debouncer.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MPPositive_Debouncer.swift @@ -15,7 +15,7 @@ class MPPositive_Debouncer: NSObject { private var delay: TimeInterval private override init() { - delay = 0.4 + delay = 0.5 super.init() } deinit { diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MPSideA_MediaCenterManager.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MPSideA_MediaCenterManager.swift index 2bb1ca4..b21ade1 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MPSideA_MediaCenterManager.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MPSideA_MediaCenterManager.swift @@ -472,7 +472,6 @@ class MPSideA_MediaCenterManager { center!.playCommand.addTarget(handler: { [weak self] (event) in guard let self = self else { return .noActionableNowPlayingItem} if self.music != nil { - return .success }else { return .noActionableNowPlayingItem diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_AnalyticsManager.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_AnalyticsManager.swift new file mode 100644 index 0000000..a80e042 --- /dev/null +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_AnalyticsManager.swift @@ -0,0 +1,257 @@ +// +// MP_AnalyticsManager.swift +// MusicPlayer +// +// Created by Mr.Zhou on 2024/5/30. +// + +import UIKit +import FirebaseAnalytics +import FirebaseCrashlytics +import FirebaseRemoteConfig +/// 埋点管理工具 +class MP_AnalyticsManager: NSObject { + static let shared = MP_AnalyticsManager() + //MARK: - 配置开关 + private let remoteConfig = RemoteConfig.remoteConfig() + //MARK: - 事件名 + //用户启动 + private let user_launch:String = "user_launch" + //app闪退 + private let app_crash:String = "app_crash" + //启动页曝光 + private let launch_pv:String = "launch_pv" + //A面首页曝光 + private let home_a_pv:String = "home_a_pv" + //B面首页曝光 + private let home_b_pv:String = "home_b_pv" + //首页资源曝光成功 + private let home_b_module_showsucces_action:String = "home_b_module_showsucces_action" + //点击首页模块 + private let home_b_module_click:String = "home_b_module_click" + //B面我的曝光 + private let me_b_pv = "me_b_pv" + //B面播放器曝光 + private let player_b_pv = "player_b_pv" + //统计加载时间 + private let player_b_delay_action = "player_b_delay_action" + //播放成功 + private let player_b_success_action = "player_b_success_action" + //点击收藏 + private let player_b_love_click = "player_b_love_click" + //取消收藏 + private let player_b_unlove_click = "player_b_unlove_click" + //点击下载 + private let player_b_download_click = "player_b_download_click" + //下载成功 + private let player_b_downloadsuccess_action = "player_b_downloadsuccess_action" + //B面搜索曝光 + private let search_pv = "search_pv" + //搜索SUG曝光 + private let search_sug_show = "search_sug_show" + //点击sug结果 + private let search_sug_click = "search_sug_click" + //搜索结果曝光 + private let search_result_pv = "search_result_pv" + //搜索有结果 + private let search_resultsuccess_action = "search_resultsuccess_action" + private override init() { + super.init() + Crashlytics.crashlytics().log(app_crash) + //为配置设置默认值 +// remoteConfig.setDefaults(["openStatus":false as NSObject]) + } + //MARK: - A/B + func getOpenStatus(_ completion:@escaping ((Bool) -> Void)) { + //每次启动都要获取值 + let duration:TimeInterval = 0 + remoteConfig.fetch(withExpirationDuration: duration) { status, error in + if status == .success { + //配置获取成功 + self.remoteConfig.activate { changed, error in + if error == nil{ + let js = self.remoteConfig.configValue(forKey: "openStatus").jsonValue as! [String:Any] + let value = js["versionCode"] as! String + if value == app_Version { + //是同一个版本,当前根据开关状态确认进入页面是什么 + let open = js["enter"] as! Bool + if open { + //进入b面 + completion(true) + }else { + //进入a面 + completion(false) + } + }else { + //不是同一个版本,直接进入B面 + completion(true) + } + } + } + }else { + //配置获取失败,查看缓存内容 + if let js = self.remoteConfig.configValue(forKey: "openStatus").jsonValue as? [String:Any] { + //存在缓存数据,调用缓存数据内容 + let value = js["versionCode"] as! String + if value == app_Version { + //是同一个版本,当前根据开关状态确认进入页面是什么 + let open = js["enter"] as! Bool + if open { + //进入b面 + completion(true) + }else { + //进入a面 + completion(false) + } + }else { + //不是同一个版本,直接进入B面 + completion(true) + } + }else { + //不存在,直接进入A面 + completion(false) + } + } + } + } + //MARK: - 事件日志 + ///执行用户启动日志 + func user_launchAction(){ + Analytics.logEvent(user_launch, parameters: nil) + } + ///启动页曝光 + func launch_pvAction(){ + Analytics.logEvent(launch_pv, parameters: nil) + } + ///A面首页曝光 + func home_a_pvAction(){ + Analytics.logEvent(home_a_pv, parameters: nil) + } + ///B面首页曝光 + func home_b_pvAction(){ + Analytics.logEvent(home_b_pv, parameters: nil) + } + ///首页资源曝光成功 + func home_b_module_showsucces_actionAction(){ + Analytics.logEvent(home_b_module_showsucces_action, parameters: nil) + } + + /// 点击首页模块 + /// - Parameter modulename: 模块名 + func home_b_module_clickAction(_ modulename:String){ + Analytics.logEvent(home_b_module_click, parameters: ["modulename":modulename]) + } + ///B面我的曝光 + func me_b_pvAction(){ + Analytics.logEvent(me_b_pv, parameters: nil) + } + + /// B面播放器曝光 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_pvAction(_ videoid:String, videoname:String, artistname:String){ + Analytics.logEvent(player_b_pv, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname + ]) + } + + ///统计加载时间 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_delay_actionAction(_ videoid:String, videoname:String, artistname:String, delay:String){ + Analytics.logEvent(player_b_delay_action, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname, + "delay":delay + ]) + } + ///播放成功 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_success_actionAction(_ videoid:String, videoname:String, artistname:String){ + Analytics.logEvent(player_b_success_action, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname + ]) + } + ///点击收藏 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_love_clickAction(_ videoid:String, videoname:String, artistname:String){ + Analytics.logEvent(player_b_love_click, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname + ]) + } + ///取消收藏 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_unlove_clickAction(_ videoid:String, videoname:String, artistname:String){ + Analytics.logEvent(player_b_unlove_click, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname + ]) + } + ///点击下载 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_download_clickAction(_ videoid:String, videoname:String, artistname:String){ + Analytics.logEvent(player_b_download_click, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname + ]) + } + ///下载成功 + /// - Parameters: + /// - videoid: 音乐id + /// - videoname: 音乐名 + /// - artistname: 歌手 + func player_b_downloadsuccess_actionAction(_ videoid:String, videoname:String, artistname:String){ + Analytics.logEvent(player_b_downloadsuccess_action, parameters: [ + "videoid":videoid, + "videoname":videoname, + "artistname":artistname + ]) + } + ///B面搜索曝光 + func search_pvAction(){ + Analytics.logEvent(search_pv, parameters: nil) + } + ///搜索SUG曝光 + func search_sug_showAction(){ + Analytics.logEvent(search_sug_show, parameters: nil) + } + + /// 点击sug结果 + /// - Parameter sugname: 搜索标签名 + func search_sug_clickAction(_ sugname:String){ + Analytics.logEvent(search_sug_click, parameters: ["sugname":sugname]) + } + ///搜索结果曝光 + func search_result_pvAction(){ + Analytics.logEvent(search_result_pv, parameters: nil) + } + ///搜索有结果 + func search_resultsuccess_actionAction(){ + Analytics.logEvent(search_resultsuccess_action, parameters: nil) + } +} diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_DownloadManager.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_DownloadManager.swift index 897cb11..a050ecd 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MP_DownloadManager.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_DownloadManager.swift @@ -8,14 +8,14 @@ import Foundation import Foundation import Tiercel -class DownloadManager: NSObject { - static let shared = DownloadManager() +class MP_DownloadManager: NSObject { + static let shared = MP_DownloadManager() var session: SessionManager! - var progressHandlers: [URL: (CGFloat) -> Void] = [:] + var progressHandlers: [String: (CGFloat) -> Void] = [:] var completionHandlers: [URL: (Result) -> Void] = [:] var downloadTasks: [URL: URLSessionDownloadTask] = [:] - var progressStorage: [URL: CGFloat] = [:] // 新增进度存储 + var progressStorage: [String: CGFloat] = [:] // 新增进度存储 var songHandlers:[URL: MPPositive_SongItemModel] = [:] private override init() { super.init() @@ -26,15 +26,15 @@ class DownloadManager: NSObject { session = SessionManager("com.yourApp.backgroundDownload", configuration: configuration) } func downloadVideo(from url: URL, song:MPPositive_SongItemModel, progressHandler: @escaping (CGFloat) -> Void, completion: @escaping (Result) -> Void) { - progressHandlers[url] = progressHandler + progressHandlers[song.videoId] = progressHandler completionHandlers[url] = completion songHandlers[url] = song let downloadTask = session.download(url, headers: ["Accept-Encoding": "gzip, deflate"]) //配置进度条 downloadTask?.progress(handler: { [weak self] (task) in guard let self = self else {return} - progressHandlers[task.url]?(task.progress.fractionCompleted) - progressStorage[task.url] = task.progress.fractionCompleted + progressHandlers[song.videoId]?(task.progress.fractionCompleted) + progressStorage[song.videoId] = task.progress.fractionCompleted }) //配置任务完成事件 downloadTask?.success(handler: { [weak self] (task) in @@ -54,15 +54,21 @@ class DownloadManager: NSObject { } } - let fileURL = downloadsURL.appendingPathComponent("\(MP_PlayerManager.shared.loadPlayer.currentVideo.song.videoId ?? "").mp4") + let fileURL = downloadsURL.appendingPathComponent("\(song.videoId ?? "").mp4") do { try FileManager.default.moveItem(at: filePathUrl, to: fileURL) //回调VideoID completionHandlers[originalURL]?(.success(songHandlers[originalURL]!)) - progressStorage[originalURL] = nil // 清除已完成任务的进度记录 + progressStorage[song.videoId] = nil // 清除已完成任务的进度记录 } catch { completionHandlers[originalURL]?(.failure(error)) } + //移除任务 + if task.status == .succeeded { + session.remove(task, completely: true) {_ in + print("\(song.title ?? "")下载任务完成,移除任务") + } + } }).failure(handler: { [weak self] (task) in guard let self = self else {return} //任务下载失败 @@ -70,10 +76,15 @@ class DownloadManager: NSObject { if let error = task.error { completionHandlers[originalURL]?(.failure(error)) } + if task.status == .failed { + session.cancel(task) { _ in + print("\(song.title ?? "")下载任务失败,取消任务") + } + } }) } - func getProgress(for url: URL) -> CGFloat? { - return progressStorage[url] + func getProgress(for videoId: String) -> CGFloat? { + return progressStorage[videoId] } func cancelAllTasksIfNeeded() { // 根据需求,取消所有任务,或者根据任务状态进行过滤 @@ -85,11 +96,12 @@ class DownloadManager: NSObject { func deleteFileDocuments(_ videoId:String, completion:@escaping((String) -> Void)) { let downloadsURL = DocumentsURL.appendingPathComponent("Downloads") let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4") - if FileManager.default.fileExists(atPath: fileURL.absoluteString) { + if FileManager.default.fileExists(atPath: fileURL.path) { do{ try FileManager.default.removeItem(at: fileURL) //文件删除成功 completion(videoId) + print("成功删除了\(videoId)文件") }catch{ print("删除文件时发生错误:\(error)") } @@ -98,44 +110,3 @@ class DownloadManager: NSObject { } } } - - - - -//class DownloadManager { -// -// static let shared = DownloadManager() -// -// private init() {} -// -// func downloadVideo(from url: URL, videoId: String, progressView: CircularProgressView, completion: @escaping (Result) -> Void) { -// let destination: DownloadRequest.Destination = { _, _ in -// let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] -// let downloadsURL = documentsURL.appendingPathComponent("Downloads") -// -// // 检查并创建 Downloads 文件夹 -// if !FileManager.default.fileExists(atPath: downloadsURL.path) { -// do { -// try FileManager.default.createDirectory(at: downloadsURL, withIntermediateDirectories: true, attributes: nil) -// } catch { -// completion(.failure(error)) -// return (downloadsURL, [.removePreviousFile, .createIntermediateDirectories]) -// } -// } -// -// let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4") -// return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) -// } -// -// AF.download(url, to: destination).downloadProgress { progress in -// progressView.setProgress(to: CGFloat(progress.fractionCompleted)) -// }.response { response in -// if let error = response.error { -// completion(.failure(error)) -// } else if let filePath = response.fileURL { -// completion(.success(filePath)) -// } -// } -// } -//} - diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_HUD.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_HUD.swift index c68df0e..755ac6f 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MP_HUD.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_HUD.swift @@ -22,6 +22,15 @@ class MP_HUD: NSObject { ///加载数据 case loading } + ///直接展示HUD,纯等待 + static func loading(){ + SVProgressHUD.setDefaultStyle(.light) + SVProgressHUD.setDefaultMaskType(.clear) + SVProgressHUD.setBackgroundColor(.init(hex: "#80F988", alpha: 0.1)) + SVProgressHUD.setForegroundColor(.init(hex: "#80F988")) + SVProgressHUD.setOffsetFromCenter(.init(horizontal: 0, vertical: 0)) + SVProgressHUD.show() + } ///文本HUD static func text(_ text:String?,delay:TimeInterval,completion:(() -> Void)?){ showWithStatus(hudStatus: .onlyText, text: text, delay: delay, completion: completion) @@ -56,7 +65,8 @@ class MP_HUD: NSObject { static func showWithStatus(hudStatus status: status, text: String?, delay: TimeInterval ,completion:(() -> Void)?) { SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultMaskType(.clear) - SVProgressHUD.setBackgroundColor(.white) + SVProgressHUD.setBackgroundColor(.init(hex: "#80F988", alpha: 0.1)) + SVProgressHUD.setForegroundColor(.init(hex: "#80F988")) SVProgressHUD.setOffsetFromCenter(.init(horizontal: 0, vertical: 0)) switch status { case .success: @@ -81,4 +91,5 @@ class MP_HUD: NSObject { completion() } } + } diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_LocationManager.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_LocationManager.swift index 82beaf3..1404e33 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MP_LocationManager.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_LocationManager.swift @@ -39,7 +39,7 @@ class MP_LocationManager: NSObject { MP_LocationManager().requestLocationAuthorizaiton() case .restricted, .denied: DispatchQueue.main.async { - let alertController = UIAlertController(title: "Location permission request", message: "“Musicoo” needs to obtain your location information in order to refine the preview music information provided to you!", preferredStyle: .alert) + let alertController = UIAlertController(title: "Location permission request", message: "“Musiclax” needs to obtain your location information in order to refine the preview music information provided to you!", preferredStyle: .alert) let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) let OKAction = UIAlertAction(title: "Settings", style: .default) { (action) in let url = URL(string: UIApplication.openSettingsURLString) diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_NetWorkManager.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_NetWorkManager.swift index ae452a6..c38ef1f 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MP_NetWorkManager.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_NetWorkManager.swift @@ -21,6 +21,8 @@ class MP_NetWorkManager: NSObject { ///会话实例 private let MPSession = Alamofire.Session(interceptor: MP_CustomRetrier()) //MARK: - API接口 + ///IP获取 + private let iPInfo:String = "https://api.tikustok.com/app/common/getIPInfo" ///域名链接 private let header:String = "https://music.youtube.com" ///端点 @@ -37,6 +39,17 @@ 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"] + ///禁止接入IP信息组 + private let banIPs:[String] = ["CN", + "HK", + "TW", + "JP", + "KR", + "GB", + "CH", + "BE", + "MO", + "SG"] //网络状态 enum NetWorkStatus: String { case notReachable = "网络不可用" @@ -136,7 +149,7 @@ class MP_NetWorkManager: NSObject { default://网络权限出现问题 DispatchQueue.main.async { //次要处理 - let alertController = UIAlertController(title: "Access network request", message: "”Musicoo“ needs to be loaded via a network request. Please click “Settings” to allow this application to gain access to the network.", preferredStyle: .alert) + let alertController = UIAlertController(title: "Access network request", message: "”Musiclax“ needs to be loaded via a network request. Please click “Settings” to allow this application to gain access to the network.", preferredStyle: .alert) let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in } @@ -178,12 +191,50 @@ class MP_NetWorkManager: NSObject { } //MARK: - API请求 extension MP_NetWorkManager { + //MARK: - 请求iP信息 + ///请求IP信息 + func requestIPInfo(_ completion:@escaping((Bool) -> Void)) { + //拼接出browse路径 + let path = iPInfo + //设置url + guard let url = URL(string: path) else { + print("Url is Incorrect") + return + } + requestPostIPInfo(url) { open in + completion(open) + } + } + private func requestPostIPInfo(_ url:URL, completion:@escaping((Bool) -> Void)) { + MPSession.request(url, method: .get, encoding: JSONEncoding.default).responseDecodable(of: JsonIPInfo.self) { [weak self] (response) in + guard let self = self else {return} + switch response.result { + case .success(let value): + guard let data = value.data, let code = data.isoCode else { + return + } + if banIPs.contains(code) == true { + //包含,是禁止区域 + completion(false) + }else { + //不包含,是通行区域 + completion(true) + } + case .failure(let error): + // 请求失败,处理错误 + handleError(url, error: error) + completion(true) + } + } + } + //MARK: - 请求首页预览 ///向YouTubemusic请求预览/首页数据 func requestBrowseDatas() { //实行串行异步队列,进行多次请求。由于第一次之后的请求都必须携带对应的continuation编码,所以串行队列。直到最后一次请求的continuation值为空,销毁队列 // 实例化串行队列 browseQueque = DispatchQueue(label: "com.request.browseQueque") + visitorData = nil //拼接出browse路径 let path = header+point+browse //设置url @@ -239,7 +290,8 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) + browseQueque = nil } } } @@ -265,7 +317,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -301,7 +353,7 @@ extension MP_NetWorkManager { comletion(list) case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -322,7 +374,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -356,7 +408,7 @@ extension MP_NetWorkManager { comletion(artist) case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -378,7 +430,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -407,7 +459,7 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -431,7 +483,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -460,7 +512,7 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -485,7 +537,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -514,7 +566,7 @@ extension MP_NetWorkManager { completion(listSongs) case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -536,7 +588,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -564,7 +616,7 @@ extension MP_NetWorkManager { completion(result) case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -592,6 +644,12 @@ extension MP_NetWorkManager { "platform":"MOBILE", "browserVersion":"125.0.0.0", // "userAgent": +// "clientName": "ANDROID_MUSIC", +// "clientVersion": "5.28.1", +// "platform": "MOBILE", +// "androidSdkVersion":"30", +// "userAgent": "com.google.android.apps.youtube.music/5.28.1 (Linux; U; Android 11) gzip" + ] ] ] @@ -612,7 +670,7 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -631,7 +689,7 @@ extension MP_NetWorkManager { // "context":[ // "client":[ // "clientName": "WEB_REMIX", -//// "visitorData":visitorData, +//// //"visitorData":visitorData, //// "originalUrl":"https://music.youtube.com/watch?v=\(videoId)", // //当前访问版本(日期值) // "clientVersion": "1.\(currTimeDate).01.00", @@ -689,7 +747,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -715,7 +773,7 @@ extension MP_NetWorkManager { completion(parsingLyrics(value) ?? "") case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -736,7 +794,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -765,7 +823,7 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -789,7 +847,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -817,7 +875,7 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -840,7 +898,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -870,7 +928,7 @@ extension MP_NetWorkManager { } case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -897,7 +955,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -926,7 +984,7 @@ extension MP_NetWorkManager { case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } @@ -954,7 +1012,7 @@ extension MP_NetWorkManager { "client":[ //web端 "clientName": "WEB_REMIX", - "visitorData":visitorData, + //"visitorData":visitorData, //当前访问版本(日期值) "clientVersion": "1.\(currTimeDate).01.00", "platform":"MOBILE", @@ -983,11 +1041,39 @@ extension MP_NetWorkManager { case .failure(let error): // 请求失败,处理错误 - print("Request failed: \(error)") + handleError(url, error: error) } } } - + ///错误处理方法 + private func handleError(_ url: URL, error:AFError) { + // 根据错误类型处理 + if let statusCode = error.responseCode { + switch statusCode { + case 400...499: + print("\(url)请求错误,错误码: \(statusCode)。") + case 500...599: + print("\(url)服务器错误,错误码: \(statusCode)。") + default: + print("\(url)其他 HTTP 错误,错误码: \(statusCode)。") + } + } else if let underlyingError = error.underlyingError as? URLError { + switch underlyingError.code { + case .notConnectedToInternet: + print("\(url)网络连接不可用,请检查你的网络设置。") + case .timedOut: + print("\(url)请求超时,请稍后重试。") + default: + print("\(url)NSURL 错误,错误码: \(underlyingError.code.rawValue)。") + } + } else { + print("\(url)未知错误: \(error.localizedDescription)") + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + //统一发一个报错通知 + NotificationCenter.notificationKey.post(notificationName: .netWork_error_deal) + } + } } //MARK: - 数据解析 extension MP_NetWorkManager { @@ -1819,30 +1905,35 @@ extension MP_NetWorkManager { //MARK: - 自动重试 ///重试策略类 class MP_CustomRetrier: RequestInterceptor { - // 尝试重试次数的最大值 - private let maximumRetryCount = 3 - // 请求与其对应的重试次数 + // 追踪重试次数的字典 private var retryCounts: [String: Int] = [:] - func retry(_ request: Alamofire.Request, for session: Alamofire.Session, dueTo error: any Error, completion: @escaping (Alamofire.RetryResult) -> Void) { - // 根据请求的id获取当前的重试次数 - let requestID = request.id - let currentRetryCount = retryCounts[requestID.uuidString] ?? 0 - // 判断是否需要重试,可以基于错误类型或请求状态码来决定 - if currentRetryCount < maximumRetryCount, let response = request.task?.response as? HTTPURLResponse, response.statusCode == 503 { - // 更新请求的重试次数 - retryCounts[requestID.uuidString] = currentRetryCount + 1 - // 在1秒后重试此请求 - completion(.retryWithDelay(1)) - print("请求\(requestID.uuidString)执行重复请求") - } else { - // 不重试,清除此请求的重试记录 - retryCounts[requestID.uuidString] = nil + // 最大重试次数 + private let maxRetryCount: Int = 3 + // 重试间隔时间 + private let retryInterval: TimeInterval = 1.0 + //重试策略 + func retry(_ request: Request, for session: Session, dueTo error: any Error, completion: @escaping (RetryResult) -> Void) { + // 检查请求是否具有URL以便于在字典中进行追踪 + guard let url = request.request?.url?.absoluteString else { completion(.doNotRetry) + return + } + // 获取这个 URL 当前的重试次数 + let currentRetryCount = retryCounts[url] ?? 0 + + // 如果未超过最大重试次数,那么决定重试 + if currentRetryCount < maxRetryCount { + // 更新重试次数 + retryCounts[url] = currentRetryCount + 1 + print("进行对\(url)访问的第\(currentRetryCount)次重试") + // 告诉 Alamofire 重试并包含间隔时间 + completion(.retryWithDelay(retryInterval)) + } else { + // 如果达到最大重试次数,则不再重试 + completion(.doNotRetry) + print("访问\(url)失败,不在进行重试") +// // 移除追踪记录 +// retryCounts[url] = nil } } - // 在请求成功完成时,清除重试计数 - func requestDidFinish(_ request: Request) { - let requestID = request.id - retryCounts[requestID.uuidString] = nil - } } diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_PlayerManager.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_PlayerManager.swift index 62bc3df..371c056 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MP_PlayerManager.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_PlayerManager.swift @@ -10,6 +10,7 @@ import AVFoundation import MediaPlayer import AVKit import FreeStreamer +import Kingfisher ///播放器播放状态 enum MP_PlayerStateType:Int { ///未启动 @@ -28,6 +29,13 @@ enum MP_PlayerPlayType:Int { ///单曲播放(当前音乐无限循环) case single = 2 } +///计时器状态 +enum MP_TimerStateType:Int { + ///运行 + case Resume = 0 + ///暂停 + case Suspend = 1 +} ///播放器启动时执行事件(播放的音乐) typealias MP_PlayTimerStartAction = () -> Void @@ -51,6 +59,14 @@ class MP_PlayerManager:NSObject{ static let shared = MP_PlayerManager() ///播放器 private var player:AVPlayer = AVPlayer() + //远程控制中心 + private var center:MPRemoteCommandCenter? + ///延迟计时器 + private var timer:DispatchSourceTimer? + ///延迟计时值 + private var times:TimeInterval = 0 + ///计时器队列 + private var queue:DispatchQueue? ///load模块 var loadPlayer:MPPositive_PlayerLoadViewModel!{ didSet{ @@ -94,6 +110,8 @@ class MP_PlayerManager:NSObject{ } } + ///计时器状态 + private var timerType:MP_TimerStateType = .Suspend ///播放器启动时执行事件记录 private var startActionBlock:MP_PlayTimerStartAction! ///播放器运行时执行事件记录 @@ -102,12 +120,50 @@ class MP_PlayerManager:NSObject{ var cacheValueBlock:MP_PlayCacheValueAction! private override init() { super.init() + //初始化计时器 + timer?.cancel() + queue = DispatchQueue(label: "com.playerTimer.timer",attributes: .concurrent) + timer = DispatchSource.makeTimerSource(queue: queue) + //设置计时器每0.1秒触发一次,误差在0.01秒 + timer?.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(10)) + // 设置计时器触发时执行的回调 + timer?.setEventHandler(handler: { [weak self] in + //将计时值增加0.1秒 + self?.times += 0.1 + }) // 添加观察者,监听播放结束事件 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) + //设置一个秒为刻度的时间值 + let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1)) + //为播放器添加运行时主线程每秒触发事件 + player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in + guard let self = self else { return } + cacheLoadTimes() + //转化为当前播放进度秒值 + let currentDuration = CMTimeGetSeconds(time) + //获取当前播放音乐资源的最大时间值 + let maxDuration = getMusicDuration() + if maxDuration.isNaN == false { + //判断当值进度是否超越最大时间值 + if currentDuration <= maxDuration { + //没有,执行运行时时间 + if runActionBlock != nil { + runActionBlock!(currentDuration, maxDuration) + } + } + } + }) } deinit { NotificationCenter.default.removeObserver(self) + center?.playCommand.removeTarget(self) + center?.pauseCommand.removeTarget(self) + center?.nextTrackCommand.removeTarget(self) + center?.previousTrackCommand.removeTarget(self) } /// 开始播放音乐 /// - Parameters: @@ -133,35 +189,62 @@ class MP_PlayerManager:NSObject{ if startAction != nil { startActionBlock = startAction } + //启动播放器 + let newItem = loadPlayer.currentVideo.resourcePlayerItem //覆盖播放器原有的playerItem - player.replaceCurrentItem(with: loadPlayer.currentVideo.resourcePlayerItem) - //将进度回归为0 - player.seek(to: .zero) - //设置一个秒为刻度的时间值 - let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1)) - //为播放器添加运行时主线程每秒触发事件 - player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in - guard let self = self else { return } - //转化为当前播放进度秒值 - let currentDuration = CMTimeGetSeconds(time) - //获取当前播放音乐资源的最大时间值 - let maxDuration = getMusicDuration() - if maxDuration.isNaN == false { - //判断当值进度是否超越最大时间值 - if currentDuration <= maxDuration { - //没有,执行运行时时间 - if runActionBlock != nil { - runActionBlock!(currentDuration, maxDuration) - } - } - } - }) + player.replaceCurrentItem(with: newItem) + if center == nil { + setCommandCenter() + } + //启动计时器 + startTimer() //对当前播放PlayerItem设置监听状态 - //准备状态 - loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil) - //当前缓冲值 - loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil) + if loadPlayer.currentVideo?.isKVO == false { + //准备状态 + newItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil) + //当前缓冲值 + newItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil) + //是否具备足够播放的能力 + newItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: [.old,.new], context: nil) + loadPlayer.currentVideo.isKVO = true + //将进度回归为0 + player.seek(to: .zero) + updateNowPlayingInfo() + } } + ///启动计时器 + func startTimer() { + guard timerType == .Suspend else { + return + } + times = 0 + //运行计时器 + timer?.resume() + timerType = .Resume + } + ///暂停计时器 + func suspendTimer() { + guard timerType == .Resume else { + return + } + //暂停计时器 + timer?.suspend() + timerType = .Suspend + } + + ///网络状态恢复正常 + @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 { @@ -175,48 +258,58 @@ class MP_PlayerManager:NSObject{ if playState != .Playing { //当statuVlaue值等于playerItem准备播放的值,说明已经准备好播放 print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放") + } + }else { + print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")") + //重新 + play() + } + case "loadedTimeRanges"://当前缓冲进度 + cacheLoadTimes() + case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放 + if let playbackLikelyToKeepUp = change?[.newKey] as? Bool, playbackLikelyToKeepUp == true { + if playState != .Playing { //还未播放当前音乐,启动播放 - print("开始播放音乐-\(loadPlayer.currentVideo?.title ?? "")") player.play() playState = .Playing + //暂停计时器,并获取延时值 + suspendTimer() + let times = Int(self.times) + let msTimes = times*1000 + MP_AnalyticsManager.shared.player_b_delay_actionAction(loadPlayer.currentVideo?.song.videoId ?? "", videoname: loadPlayer.currentVideo?.title ?? "", artistname: loadPlayer.currentVideo?.song.shortBylineText ?? "", delay: "\(msTimes)ms") + MP_AnalyticsManager.shared.player_b_success_actionAction(loadPlayer.currentVideo?.song.videoId ?? "", videoname: loadPlayer.currentVideo?.title ?? "", artistname: loadPlayer.currentVideo?.song.shortBylineText ?? "") //执行开始播放闭包 if startActionBlock != nil { startActionBlock!() } } }else { - print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")") - //资源更新,重新配置一下相关内容 - loadPlayer.remakeImproveData { - [weak self] in - guard let self = self else {return} - //重新播放 - play() - } + //没有足够的数据支持播放 + player.pause() + playState = .Null } - case "loadedTimeRanges"://当前缓冲进度 - //获取当前播放Item的缓冲值组 - if let timeRanges = loadPlayer.currentVideo?.resourcePlayerItem.loadedTimeRanges.map({$0.timeRangeValue}), let first = timeRanges.first { - //获取开始时间的秒数 - let startSeconds = first.start.seconds - //获取缓冲区的持续时间 - let durationSeconds = first.duration.seconds - //计算当前缓冲总时间 - let bufferedSeconds = startSeconds + durationSeconds - //获取当前播放音乐资源的最大时间值 - let maxDuration = getMusicDuration() - //传递缓存值 - if cacheValueBlock != nil { - cacheValueBlock!(bufferedSeconds, maxDuration) - } - } - - case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放 - break default: break } } + //获取缓冲值 + private func cacheLoadTimes() { + //获取当前播放Item的缓冲值组 + if let timeRanges = loadPlayer.currentVideo?.resourcePlayerItem.loadedTimeRanges.map({$0.timeRangeValue}), let first = timeRanges.first { + //获取开始时间的秒数 + let startSeconds = first.start.seconds + //获取缓冲区的持续时间 + let durationSeconds = first.duration.seconds + //计算当前缓冲总时间 + let bufferedSeconds = startSeconds + durationSeconds + //获取当前播放音乐资源的最大时间值 + let maxDuration = getMusicDuration() + //传递缓存值 + if cacheValueBlock != nil { + cacheValueBlock!(bufferedSeconds, maxDuration) + } + } + } //MARK: - 获取当前音乐总长度 ///获取音乐资源总时长 private func getMusicDuration() -> TimeInterval { @@ -325,7 +418,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 } @@ -334,15 +427,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 } @@ -367,7 +460,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 } @@ -376,15 +469,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 } @@ -409,10 +502,16 @@ class MP_PlayerManager:NSObject{ player.pause() //优先获取传递的值 if let video = sender.object as? MPPositive_SongViewModel { - //切歌时移除KVO监听 - video.resourcePlayerItem.removeObserver(self, forKeyPath: "status") - video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges") - // video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") + if video.isKVO == true { + //切歌时移除KVO监听 + video.resourcePlayerItem.removeObserver(self, forKeyPath: "status") + video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges") + video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") + video.isKVO = false + } + } + if cacheValueBlock != nil { + cacheValueBlock!(0, 1) } if loadPlayer.currentVideo != nil { //开始播放 @@ -450,4 +549,88 @@ class MP_PlayerManager:NSObject{ endAction!() } } + //MARK: - 远程中心 + private func setCommandCenter() { + // 实例化远程控制中心 + center = MPRemoteCommandCenter.shared() + //设置控制中心各项操作 + //播放 + center!.playCommand.addTarget(handler: { [weak self] (event) in + guard let self = self else { return .noActionableNowPlayingItem} + if loadPlayer.currentVideo != nil { + resume() + return .success + }else { + return .noActionableNowPlayingItem + } + }) + //暂停 + center!.pauseCommand.addTarget(handler: { [weak self] (event) in + guard let self = self else { return .noActionableNowPlayingItem} + if loadPlayer.currentVideo != nil { + pause() + return .success + }else { + return .noActionableNowPlayingItem + } + }) + //上一首 + center!.previousTrackCommand.addTarget { [weak self] (event) in + guard let self = self else { return .noActionableNowPlayingItem} + if loadPlayer.currentVideo != nil { + previousEvent() + return .success + }else { + return .noActionableNowPlayingItem + } + } + //下一首歌 + center!.nextTrackCommand.addTarget { [weak self] (event) in + guard let self = self else { return .noActionableNowPlayingItem} + if loadPlayer.currentVideo != nil { + nextEvent() + return .success + }else { + return .noActionableNowPlayingItem + } + } + } + //设置远程中心内容更新 + func updateNowPlayingInfo() { + //设置info字典信息 + var info = [String:Any]() + //展示标题 + info[MPMediaItemPropertyTitle] = loadPlayer.currentVideo?.title ?? "" + //设置艺术家 + info[MPMediaItemPropertyArtist] = loadPlayer.currentVideo?.song?.shortBylineText ?? "" + //设置专辑 + info[MPMediaItemPropertyAlbumTitle] = loadPlayer.currentVideo?.song?.longBylineText + //设置歌曲时长 + info[MPMediaItemPropertyPlaybackDuration] = getMusicDuration() + let reviewURL = URL(string: loadPlayer.currentVideo?.song?.reviewUrls?.last ?? "")! + KingfisherManager.shared.retrieveImage(with: reviewURL) { result in + switch result { + case .success(let imageResult): + let image = imageResult.image + info[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { size in + return image + }) + // 确保更新MPNowPlayingInfoCenter的操作在主线程中 + DispatchQueue.main.async { + //更新远程中心 + MPNowPlayingInfoCenter.default().nowPlayingInfo = info + } + case .failure(_): + info[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: placeholderImage.size, requestHandler: { size in + return placeholderImage + }) + // 确保更新MPNowPlayingInfoCenter的操作在主线程中 + DispatchQueue.main.async { + //更新远程中心 + MPNowPlayingInfoCenter.default().nowPlayingInfo = info + } + } + } + } } + diff --git a/MusicPlayer/MP/Common/Tool(工具封装)/MP_WebWork.swift b/MusicPlayer/MP/Common/Tool(工具封装)/MP_WebWork.swift index 77ea236..1449bbf 100644 --- a/MusicPlayer/MP/Common/Tool(工具封装)/MP_WebWork.swift +++ b/MusicPlayer/MP/Common/Tool(工具封装)/MP_WebWork.swift @@ -58,7 +58,7 @@ class MP_WebWork:NSObject { self.jsPath = self.jsPath + matchedString print("Current base.JavaScript path:\(self.jsPath)") //开始获取首页内容 - MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists() +// MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists() //切换地址 if let baseUrl = URL(string: self.jsPath) { let request = URLRequest(url: baseUrl) @@ -177,7 +177,7 @@ extension MP_WebWork: WKNavigationDelegate, WKUIDelegate { }else { print("注入代码完成") //发布切换启动页 - NotificationCenter.notificationKey.post(notificationName: .js_edit_completion) +// NotificationCenter.notificationKey.post(notificationName: .js_edit_completion) } } } diff --git a/MusicPlayer/MP/MPPositive/Models/JsonStructs(js文件结构)/MPPositive_JsonBrowse.swift b/MusicPlayer/MP/MPPositive/Models/JsonStructs(js文件结构)/MPPositive_JsonBrowse.swift index 666ba26..687c41b 100644 --- a/MusicPlayer/MP/MPPositive/Models/JsonStructs(js文件结构)/MPPositive_JsonBrowse.swift +++ b/MusicPlayer/MP/MPPositive/Models/JsonStructs(js文件结构)/MPPositive_JsonBrowse.swift @@ -1,6 +1,38 @@ import Foundation + +///IP信息结构 +struct JsonIPInfo: Codable { + let data:Datas? + let message:String? + let status:String? + enum CodingKeys: String, CodingKey { + case data = "data" + case message = "message" + case status = "status" + } + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + data = try values.decodeIfPresent(Datas.self, forKey: .data) + message = try values.decodeIfPresent(String.self, forKey: .message) + status = try values.decodeIfPresent(String.self, forKey: .status) + } + struct Datas: Codable { + let isoCode:String? + let ip:String? + enum CodingKeys: String, CodingKey { + case isoCode = "isoCode" + case ip = "ip" + } + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isoCode = try values.decodeIfPresent(String.self, forKey: .isoCode) + ip = try values.decodeIfPresent(String.self, forKey: .ip) + } + } +} + ///预览结构体 struct JsonBrowses: Codable { ///获取访问数据 diff --git a/MusicPlayer/MP/MPPositive/Models/Models/MPPositive_DownloadItemModel.swift b/MusicPlayer/MP/MPPositive/Models/Models/MPPositive_DownloadItemModel.swift index b223e8d..9097303 100644 --- a/MusicPlayer/MP/MPPositive/Models/Models/MPPositive_DownloadItemModel.swift +++ b/MusicPlayer/MP/MPPositive/Models/Models/MPPositive_DownloadItemModel.swift @@ -18,7 +18,7 @@ class MPPositive_DownloadItemModel: NSManagedObject, MP_CoreDataManageableDelega ///标题(单曲标题) @NSManaged var title:String? ///长文本标题(作者/播放次数/点赞次数) - @NSManaged var longBylineText:String? + @NSManaged var longBylineText:String? ///单曲长度文本(歌曲长度) @NSManaged var lengthText:String? ///署名文本(歌手) diff --git a/MusicPlayer/MP/MPPositive/Models/ViewModels/ListViewModels/MPPositive_SongViewModel.swift b/MusicPlayer/MP/MPPositive/Models/ViewModels/ListViewModels/MPPositive_SongViewModel.swift index b9a6559..a191823 100644 --- a/MusicPlayer/MP/MPPositive/Models/ViewModels/ListViewModels/MPPositive_SongViewModel.swift +++ b/MusicPlayer/MP/MPPositive/Models/ViewModels/ListViewModels/MPPositive_SongViewModel.swift @@ -32,6 +32,10 @@ class MPPositive_SongViewModel: NSObject { var isCollection:Bool? ///是否下载 var isDlownd:Bool? + ///是否被KVO监听 + var isKVO:Bool = false + ///是否完成了预加载 + var isPreload:Bool = false ///音乐实体 var song:MPPositive_SongItemModel! init(_ song:MPPositive_SongItemModel) { @@ -43,12 +47,20 @@ class MPPositive_SongViewModel: NSObject { resourcePlayerItem = nil resourcePlayerAsset = nil resourcePlayerURL = nil + isKVO = false } //数据配置 func configure() { reloadCollectionAndDownLoad() index = song.index - + //标题 + if song.title != nil { + title = song.title! + } + //副标题(作者/专辑) + if song.shortBylineText != nil { + subtitle = song.shortBylineText! + } if let first = song.resourceUrls?.first { resourcePlayerURL = .init(string:first) resourcePlayerAsset = .init(url: resourcePlayerURL) @@ -60,14 +72,6 @@ class MPPositive_SongViewModel: NSObject { if song.reviewUrls?.first != nil { coverUrl = .init(string: song.reviewUrls!.last!) } - //标题 - if song.title != nil { - title = song.title! - } - //副标题(作者/专辑) - if song.shortBylineText != nil { - subtitle = song.shortBylineText! - } //歌词 if song.lyrics != nil { lyrics = song.lyrics @@ -95,6 +99,10 @@ class MPPositive_SongViewModel: NSObject { } //执行预加载 func preloadAsset(_ asset:AVURLAsset) { + guard isPreload == false else { + return + } + print("\(title ?? "")开始预加载") //执行预加载 if #available(iOS 16, *) { //ios16以上的情况 @@ -103,11 +111,13 @@ class MPPositive_SongViewModel: NSObject { let playable = try await asset.load(.isPlayable) if playable == true { print("\(self.title ?? "")预加载成功") + isPreload = true }else { //检索预加载失败原因 switch asset.status(of: .isPlayable) { case .failed(let erro): print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)") + preloadAsset(asset) default: break } @@ -130,9 +140,11 @@ class MPPositive_SongViewModel: NSObject { // key成功加载,资源准备就绪 DispatchQueue.main.async { print("\(self.title ?? "")预加载成功") + self.isPreload = true } case .failed: print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")") + preloadAsset(asset) case .cancelled: print("\(title ?? "")预加载被取消了") default: diff --git a/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_BrowseLoadViewModel.swift b/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_BrowseLoadViewModel.swift index 09d0be1..84e3275 100644 --- a/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_BrowseLoadViewModel.swift +++ b/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_BrowseLoadViewModel.swift @@ -23,6 +23,10 @@ class MPPositive_BrowseLoadViewModel: NSObject { browseModuleLists = browseModuleLists.filter{($0.items.count != 0)} //通知首页刷新UI NotificationCenter.notificationKey.post(notificationName: .positive_browses_reload) + if isCompleted == true { + //加载完毕后 + MP_AnalyticsManager.shared.home_b_module_showsucces_actionAction() + } } } ///刷新预览数据 diff --git a/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_PlayerLoadViewModel.swift b/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_PlayerLoadViewModel.swift index c945d32..06b5e10 100644 --- a/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_PlayerLoadViewModel.swift +++ b/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_PlayerLoadViewModel.swift @@ -15,13 +15,18 @@ class MPPositive_PlayerLoadViewModel: NSObject { ///当前播放音乐ViewModel var currentVideo:MPPositive_SongViewModel!{ willSet{ - if newValue != nil { - if currentVideo != nil { - //当值变化时通知播放器页面,更新UI - NotificationCenter.notificationKey.post(notificationName: .positive_player_reload, object: currentVideo) - }else { - //当值变化时通知播放器页面,更新UI - NotificationCenter.notificationKey.post(notificationName: .positive_player_reload) + DispatchQueue.main.asyncAfter(deadline: .now()) { + [weak self] in + guard let self = self else {return} + if newValue != nil { + MP_AnalyticsManager.shared.player_b_pvAction(newValue.song.videoId, videoname: newValue.title ?? "", artistname: newValue.song.shortBylineText ?? "") + if currentVideo != nil { + //当值变化时通知播放器页面,更新UI + NotificationCenter.notificationKey.post(notificationName: .positive_player_reload, object: currentVideo) + }else { + //当值变化时通知播放器页面,更新UI + NotificationCenter.notificationKey.post(notificationName: .positive_player_reload) + } } } } @@ -43,7 +48,7 @@ class MPPositive_PlayerLoadViewModel: NSObject { self.listViewVideos = [] } - ///将选中Video的上下1项包括本身总计3项Video进行补全转为ViewModel,并播放这首音乐 + ///将选中Video的上1位,下两位项包括本身总计4项Video进行补全转为ViewModel,并播放这首音乐 func improveData(_ targetVideoId:String, isRandom:Bool = false) { //对于选中Video的集合 var array:[MPPositive_SongItemModel] = [] @@ -59,9 +64,13 @@ class MPPositive_PlayerLoadViewModel: NSObject { array.append(self.randomVideos[previousIndex]) } let nextIndex = targetIndex+1 + let lastIndex = targetIndex+2 if nextIndex < randomVideos.count { array.append(self.randomVideos[nextIndex]) } + if lastIndex < randomVideos.count { + array.append(self.randomVideos[lastIndex]) + } }else { //获取targetVideoId的索引 guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else { @@ -74,9 +83,13 @@ class MPPositive_PlayerLoadViewModel: NSObject { array.append(self.songVideos[previousIndex]) } let nextIndex = targetIndex+1 + let lastIndex = targetIndex+2 if nextIndex < songVideos.count { array.append(self.songVideos[nextIndex]) } + if lastIndex < songVideos.count { + array.append(self.songVideos[lastIndex]) + } } //获取完成,优先检索ViewModel,看看是否已存在补完video let videoIDs = Set(listViewVideos.map({$0.song.videoId})) @@ -123,8 +136,8 @@ class MPPositive_PlayerLoadViewModel: NSObject { [weak self] in //确定播放音乐 self?.currentVideo = self?.listViewVideos.first(where: {$0.song.videoId == targetVideoId}) - //只保留最后三首 - self?.listViewVideos = self?.listViewVideos.suffix(3) + //只保留最后四首 + self?.listViewVideos = self?.listViewVideos.suffix(4) self?.group = nil }) } @@ -180,19 +193,4 @@ class MPPositive_PlayerLoadViewModel: NSObject { private func findVideoIdForDocument(_ videoId:String) -> Bool { return MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", videoId)).count != 0 } - - ///调用next对单曲数据歌词ID与相关ID补全 - private func improveDataforLycirsAndRelated(_ song:MPPositive_SongItemModel, completion:@escaping(((String?,String?)) -> Void)) { - //单曲补全需要再次调用next接口 - MP_NetWorkManager.shared.requestNextLyricsAndRelated(song){ result in - completion(result) - } - } - ///调用player对资源路径和封面路径补全 - private func improveDataforResouceAndCover(_ song:MPPositive_SongItemModel, completion:@escaping((([String],[Int],[String]), [String]?) -> Void)) { - //单曲补全需要调用player接口 - MP_NetWorkManager.shared.requestAndroidPlayer(song.videoId, playlistId: "") { resourceUrls, coverUrls in - completion(resourceUrls,coverUrls) - } - } } diff --git a/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_SearchResultsLoadViewModel.swift b/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_SearchResultsLoadViewModel.swift index 3966c0a..3ec11de 100644 --- a/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_SearchResultsLoadViewModel.swift +++ b/MusicPlayer/MP/MPPositive/Models/ViewModels/LoadViewModels/MPPositive_SearchResultsLoadViewModel.swift @@ -29,6 +29,7 @@ class MPPositive_SearchResultsLoadViewModel: NSObject { } //根据用户输入文本内容请求搜索接口 private func getSearchResults(_ text:String) { + MP_HUD.loading() //同时新增一个搜索历史 let tag = MPPositive_SearchTagModel.create() tag.date = Date().timeZone() diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_BaseViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_BaseViewController.swift index 171e44a..0ab61e3 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_BaseViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_BaseViewController.swift @@ -17,12 +17,30 @@ class MPPositive_BaseViewController: MP_BaseViewController { btn.addTarget(self, action: #selector(popActionClick(_ :)), for: .touchUpInside) return btn }() + //网络加载失败的View + private lazy var errorView:UIView = createErrorView() + ///报错回调 + var errorBlock:(() -> Void)? + ///重试回调 + var retryBlock:(() -> Void)? + + //导航View lazy var navView:UIView = setTitleView() - + override func viewDidLoad() { super.viewDidLoad() view.addSubview(navView) } + deinit { + } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + NotificationCenter.notificationKey.add(observer: self, selector: #selector(errorAction(_ :)), notificationName: .netWork_error_deal) + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + NotificationCenter.default.removeObserver(self) + } //设置顶部titleView private func setTitleView() -> UIView { let topView = UIView(frame: .init(x: 0, y: statusBarHeight, width: screen_Width, height: 50*width)) @@ -55,4 +73,78 @@ class MPPositive_BaseViewController: MP_BaseViewController { @objc private func popActionClick(_ sender:UIButton) { navigationController?.popViewController(animated: true) } + //报错errorView + private func createErrorView() -> UIView{ + let errorView = UIView() + errorView.layer.masksToBounds = true + errorView.layer.borderColor = UIColor(hex: "#80F988").cgColor + errorView.layer.borderWidth = 1*width + errorView.layer.cornerRadius = 18*width + errorView.backgroundColor = .clear + //使用其他内容 + //一个提示框 + let iconImageView = UIImageView(image: .init(named: "empty")) + errorView.addSubview(iconImageView) + iconImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.centerY.equalToSuperview().multipliedBy(0.65) + make.width.equalTo(211*width) + make.height.equalTo(172*width) + } + let label = createLabel("Failure to obtain data", font: .systemFont(ofSize: 13*width, weight: .regular), textColor: .white, textAlignment: .center) + errorView.addSubview(label) + label.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(iconImageView.snp.bottom).offset(8*width) + } + let retryBtn:UIButton = .init() + retryBtn.setTitle("Retry", for: .normal) + retryBtn.setTitleColor(.white, for: .normal) + retryBtn.titleLabel?.font = .systemFont(ofSize: 15*width, weight: .medium) + retryBtn.backgroundColor = .init(hex: "#80F988") + retryBtn.layer.masksToBounds = true + retryBtn.layer.cornerRadius = 16*width + retryBtn.addTarget(self, action: #selector(retryClick(_ :)), for: .touchUpInside) + errorView.addSubview(retryBtn) + retryBtn.snp.makeConstraints { make in + make.width.equalTo(180*width) + make.height.equalTo(45*width) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-15*width) + } + return errorView + } + //设置报错View + func setErrorView() { + view.addSubview(errorView) + errorView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.equalTo(300*width) + make.height.equalTo(280*width) + } + } + //移除报错View + func removeErrorView() { + if errorView.superview != nil { + errorView.removeFromSuperview() + } + } + //处理报错 + @objc private func errorAction(_ sender:Notification){ + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + MP_HUD.text("Failure to obtain data", delay: 1.0){ [weak self] in + //执行报错回调闭包 + if self?.errorBlock != nil { + self?.errorBlock!() + } + } + } + } + @objc private func retryClick(_ sender:UIButton) { + if retryBlock != nil { + retryBlock!() + } + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_MoreSongOperationsViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_MoreSongOperationsViewController.swift index 3f1acf2..6f0c56c 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_MoreSongOperationsViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Base(基类,导航栏,标签栏)/MPPositive_MoreSongOperationsViewController.swift @@ -46,15 +46,14 @@ class MPPositive_MoreSongOperationsViewController: UIViewController { let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain) tableView.backgroundColor = .clear tableView.separatorStyle = .none - tableView.contentInset = .init(top: 0, left: 0, bottom: bottomPadding, right: 0) tableView.estimatedRowHeight = 200 tableView.rowHeight = UITableView.automaticDimension tableView.dataSource = self tableView.delegate = self - tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationTableViewCellID) + tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID) return tableView }() - private let MPPositive_MoreOperationTableViewCellID = "MPPositive_MoreOperationTableViewCell" + private let MPPositive_MoreOperationDownLoadTableViewCellID = "MPPositive_MoreOperationDownLoadTableViewCell" private var song:MPPositive_SongItemModel!{ didSet{ iconImageView.kf.setImage(with: URL(string: song.reviewUrls?.last ?? ""), placeholder: placeholderImage) @@ -74,9 +73,13 @@ class MPPositive_MoreSongOperationsViewController: UIViewController { } } } + var removeBlock:(() -> Void)? init(_ song:MPPositive_SongItemModel) { super.init(nibName: nil, bundle: nil) - self.song = song + DispatchQueue.main.async { + [weak self] in + self?.song = song + } } required init?(coder: NSCoder) { @@ -87,7 +90,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController { super.viewDidLoad() view.backgroundColor = .init(hex: "#282A2C") view.layer.masksToBounds = true - view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMinXMaxYCorner] + view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner] view.layer.cornerRadius = 18*width configure() } @@ -146,6 +149,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController { } } MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) + MP_AnalyticsManager.shared.player_b_unlove_clickAction(song.videoId, videoname: song.title ?? "", artistname: song.shortBylineText ?? "") }else{ self.collectionBtn.isSelected = true let item = MPPositive_CollectionSongModel.create() @@ -157,6 +161,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController { item.relatedID = song.relatedID MPPositive_CollectionSongModel.save() MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) + MP_AnalyticsManager.shared.player_b_love_clickAction(song.videoId, videoname: song.title ?? "", artistname: song.shortBylineText ?? "") } } } @@ -166,8 +171,14 @@ extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UIT return 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell cell.title = isLoaded ? "Remove Download":"Add Download" + cell.restoreDownloadProgress(song) + cell.progressBlock = { + [weak self] in + guard let self = self else {return} + tableView.reloadRows(at: [.init(item: 0, section: 0)], with: .none) + } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -180,16 +191,95 @@ extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UIT } } MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil) - DownloadManager.shared.deleteFileDocuments(song.videoId) { [weak self] videoId in + MP_DownloadManager.shared.deleteFileDocuments(song.videoId) { [weak self] videoId in guard let self = self else {return} MP_HUD.progress("Loading...", delay: 0.5) { self.isLoaded = false MP_HUD.text("Removed", delay: 1.0, completion: nil) + if self.removeBlock != nil { + self.removeBlock!() + } } } }else { //进行下载 - + let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell + //判断当前下载状态 + switch cell.loadBtn.state { + case .startDownload://未下载 + MP_AnalyticsManager.shared.player_b_download_clickAction(song.videoId ?? "", videoname: song.title ?? "", artistname: song.shortBylineText ?? "") + //切换为准备状态 + cell.loadBtn.state = .pending + tableView.reloadData() + //当开始下载时 + guard let videoURL = URL(string: song.resourceUrls?.first ?? "") else { + MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.0) {[weak self] in + cell.loadBtn.state = .startDownload + self?.isLoaded = false + } + return + } + //执行下载 + MP_DownloadManager.shared.downloadVideo(from: videoURL, song: song) { [weak self] progress in + DispatchQueue.main.async { + guard let self = self else { + //不是同一个 + cell.loadBtn.state = (MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", (self?.song.videoId ?? ""))).count != 0) ? .downloaded:.startDownload + tableView.reloadData() + return + } + cell.loadBtn.state = .downloading + cell.loadBtn.stopDownloadButton.progress = progress + tableView.reloadData() + } + } completion: { [weak self] result in + //下载结束,判断成功或失败 + switch result { + case .success(let song): + //添加数据 + let item = MPPositive_DownloadItemModel.create() + item.coverImage = song.coverUrls!.last + item.reviewImage = song.reviewUrls!.last + item.title = song.title + item.longBylineText = song.longBylineText + item.lengthText = song.lengthText + item.shortBylineText = song.shortBylineText + item.lyrics = song.lyrics + item.lyricsID = song.lyricsID + item.videoId = song.videoId + item.relatedID = song.relatedID + MPPositive_DownloadItemModel.save() + DispatchQueue.main.async { + //回归主线程,判断下载的是否为当前歌曲 + if song.videoId == self?.song.videoId { + //是当前这首,刷新一下当前播放音乐的下载状态 + self?.isLoaded = true + }else { + //不是这首,那就不管他 + self?.isLoaded = false + } + print("完成了对\(song.title ?? "")的下载") + //按钮变为下载结束状态 + cell.loadBtn.state = .downloaded + tableView.reloadData() + //更新数据库管理类 + MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil) + MP_AnalyticsManager.shared.player_b_downloadsuccess_actionAction(item.videoId, videoname: item.title ?? "", artistname: item.shortBylineText ?? "") + } + case .failure(let error): + //失败了,打印错误 + print("下载报错,错误详情\(error)") + DispatchQueue.main.async { + //按钮回归可用状态 + cell.loadBtn.state = .startDownload + tableView.reloadData() + } + MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil) + } + } + default: + break + } } } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LibraryViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LibraryViewController.swift index 6b4d110..532c271 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LibraryViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LibraryViewController.swift @@ -66,6 +66,10 @@ class MPPositive_LibraryViewController: MPPositive_BaseViewController { super.viewWillAppear(animated) reload() } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + MP_AnalyticsManager.shared.me_b_pvAction() + } //刷新列表 private func reload() { MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels { diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LoveSongsViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LoveSongsViewController.swift index 6b7e69a..a73ad2e 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LoveSongsViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_LoveSongsViewController.swift @@ -7,7 +7,7 @@ import UIKit -class MPPositive_LoveSongsViewController: MPPositive_BaseViewController { +class MPPositive_LoveSongsViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate { private lazy var numbersLabel:UILabel = createLabel(font: .systemFont(ofSize: 18*width, weight: .regular), textColor: .white, textAlignment: .left) ///tableView private lazy var tableView:UITableView = { @@ -67,6 +67,39 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell cell.songViewModel = MPPositive_LoadCoreModel.shared.songViewModels[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else {return} + MPPositive_Debouncer.shared.call { + MP_NetWorkManager.shared.requestNextList("", videoId: MPPositive_LoadCoreModel.shared.songViewModels[indexPath.row].collectionSong.videoId ?? ""){ [weak self] listSongs in + guard let first = listSongs.first else {return} + let group = DispatchGroup() + group.enter() + improveDataforLycirsAndRelated(first) {[weak self] (result) in + first.lyricsID = result.0 + first.relatedID = result.1 + group.leave() + } + group.enter() + //补全资源路径组和封面路径组 + improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in + first.resourceUrls = resourceUrls.0 + first.itags = resourceUrls.1 + first.mimeTypes = resourceUrls.2 + first.coverUrls = coverUrls + group.leave() + } + group.notify(queue: .main, execute: { + [weak self] in + MPPositive_ModalType = .MoreOperations + let moreVC = MPPositive_MoreSongOperationsViewController(first) + moreVC.transitioningDelegate = self + moreVC.modalPresentationStyle = .custom + self?.present(moreVC, animated: true) + }) + } + } + } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -93,4 +126,7 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) } } + func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_OfflineSongsViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_OfflineSongsViewController.swift index e0339bd..616977d 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_OfflineSongsViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Center(个人曲库页)/MPPositive_OfflineSongsViewController.swift @@ -60,13 +60,39 @@ class MPPositive_OfflineSongsViewController: MPPositive_BaseViewController { } } //MARK: - tableView -extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableViewDelegate { +extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableViewDelegate, UIViewControllerTransitioningDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return MPPositive_LoadCoreModel.shared.loadViewModels.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell cell.loadViewModel = MPPositive_LoadCoreModel.shared.loadViewModels[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else {return} + MPPositive_Debouncer.shared.call { + let item = MPPositive_LoadCoreModel.shared.loadViewModels[indexPath.row] + let song = MPPositive_SongItemModel() + song.coverUrls = [item.loadItem.coverImage] + song.reviewUrls = [item.loadItem.reviewImage] + song.title = item.loadItem.title + song.longBylineText = item.loadItem.longBylineText + song.lengthText = item.loadItem.lengthText + song.shortBylineText = item.loadItem.shortBylineText + song.lyrics = item.loadItem.lyrics + song.lyricsID = item.loadItem.lyricsID + song.videoId = item.loadItem.videoId + song.relatedID = item.loadItem.relatedID + MPPositive_ModalType = .MoreOperations + let moreVC = MPPositive_MoreSongOperationsViewController(song) + moreVC.removeBlock = { + self.reload() + } + moreVC.transitioningDelegate = self + moreVC.modalPresentationStyle = .custom + self.present(moreVC, animated: true) + } + } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -96,4 +122,7 @@ extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableV NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) } } + func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ArtistShowViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ArtistShowViewController.swift index de4f434..e7b5c45 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ArtistShowViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ArtistShowViewController.swift @@ -7,7 +7,7 @@ import UIKit -class MPPositive_ArtistShowViewController: MPPositive_BaseViewController { +class MPPositive_ArtistShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate { ///头视图 private lazy var headView:MPPositive_ArtistShowHeaderView = .init(frame: .init(x: 0, y: 0, width: screen_Width, height: 385*width)) ///艺术家Label @@ -61,17 +61,24 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController { }() private var artist:MPPositive_ArtistViewModel!{ didSet{ - if artist != nil { - pagingView.isHidden = false - //更新头部视图数据 - headView.artistid = self.browseid - headView.artist = artist - - //更新分页数据源 - dataSource.titles = artist.lists.compactMap({$0.title}) - nameLabel.text = artist.header?.title - dataSource.reloadData(selectedIndex: 0) - segmentView.reloadData() + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + if artist != nil { + pagingView.isHidden = false + //更新头部视图数据 + headView.artistid = self.browseid + headView.artist = artist + + //更新分页数据源 + dataSource.titles = artist.lists.compactMap({$0.title}) + nameLabel.text = artist.header?.title + dataSource.reloadData(selectedIndex: 0) + segmentView.reloadData() + configure() + removeErrorView() + MP_HUD.hideNow() + } } } } @@ -83,13 +90,11 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController { init(_ browseId:String) { super.init(nibName: nil, bundle: nil) browseid = browseId + MP_HUD.loading() //进行网络请求 MP_NetWorkManager.shared.requestArtist(browseId) { [weak self] result in - DispatchQueue.main.async { - [weak self] in - guard let self = self else {return} - artist = result - } + guard let self = self else {return} + artist = result } } @@ -100,7 +105,33 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController { super.viewDidLoad() setPopBtn() setTitle("") - configure() + errorBlock = { + [weak self] in + guard let self = self else { + return + } + //移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试 + view.subviews.forEach { item in + if item != self.navView { + //移除 + if item.superview != nil { + item.removeFromSuperview() + } + } + } + //添加报错View + setErrorView() + MP_HUD.hideNow() + } + retryBlock = { + [weak self] in + guard let self = self else {return} + MP_HUD.loading() + MP_NetWorkManager.shared.requestArtist(self.browseid) { [weak self] result in + guard let self = self else {return} + artist = result + } + } } private func configure() { @@ -174,7 +205,7 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{ switch item.browseItem.itemType { case .artist: //用户查看艺术家 - let artistVC = MPPositive_ArtistShowViewController(item.browseItem.browseId ?? "") + let artistVC = MPPositive_ArtistShowViewController(item.browseItem.artistId ?? "") navigationController?.pushViewController(artistVC, animated: true) case .list: //列表专辑 @@ -199,6 +230,42 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{ break } } + showView.moreBlock = { + [weak self] (itemView) in + guard let self = self else {return} + MPPositive_Debouncer.shared.call { + MP_NetWorkManager.shared.requestNextList("", videoId: itemView.browseItem.videoId ?? ""){ [weak self] listSongs in + guard let first = listSongs.first else {return} + let group = DispatchGroup() + group.enter() + improveDataforLycirsAndRelated(first) {[weak self] (result) in + first.lyricsID = result.0 + first.relatedID = result.1 + group.leave() + } + group.enter() + //补全资源路径组和封面路径组 + improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in + first.resourceUrls = resourceUrls.0 + first.itags = resourceUrls.1 + first.mimeTypes = resourceUrls.2 + first.coverUrls = coverUrls + group.leave() + } + group.notify(queue: .main, execute: { + [weak self] in + MPPositive_ModalType = .MoreOperations + let moreVC = MPPositive_MoreSongOperationsViewController(first) + moreVC.transitioningDelegate = self + moreVC.modalPresentationStyle = .custom + self?.present(moreVC, animated: true) + }) + } + } + } return showView } + func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_HomeViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_HomeViewController.swift index f0f9ea5..a76a689 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_HomeViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_HomeViewController.swift @@ -38,13 +38,51 @@ class MPPositive_HomeViewController: MPPositive_BaseViewController{ // private var loadViewModel:MPPositive_BrowseLoadViewModel! override func viewDidLoad() { super.viewDidLoad() - setTitle("Musicoo") + setTitle("Musiclax") confirgue() + //获取首页 + MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists() NotificationCenter.notificationKey.add(observer: self, selector: #selector(reloadAction(_ :)), notificationName: .positive_browses_reload) + MP_HUD.loading() + errorBlock = { + [weak self] in + guard let self = self else { + return + } + //移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试 + view.subviews.forEach { item in + if item != self.navView { + //移除 + if item.superview != nil { + item.removeFromSuperview() + } + } + } + //添加报错View + setErrorView() + MP_HUD.hideNow() + } + retryBlock = { + [weak self] in + guard let self = self else {return} + MP_HUD.loading() + DispatchQueue.main.async { + //获取首页 + MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists() + } + } } deinit { NotificationCenter.default.removeObserver(self) } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + MP_AnalyticsManager.shared.home_b_pvAction() + } //MARK: - UI生成与配置 //配置 private func confirgue() { @@ -71,13 +109,20 @@ class MPPositive_HomeViewController: MPPositive_BaseViewController{ @objc private func reloadAction(_ sender:Notification) { DispatchQueue.main.async { [weak self] in - self?.tableView.reloadData() + guard let self = self else {return} + if tableView.superview == nil { + confirgue() + } + removeErrorView() + MP_HUD.hideNow() + tableView.reloadData() } } //MARK: - 点击 //点击顶部右侧弹出菜单 @objc private func menuRightClick(_ sender:UIButton) { - + let setVC = MPSideA_SettingViewController() + navigationController?.pushViewController(setVC, animated: true) } } //MARK: - tableView @@ -105,6 +150,7 @@ extension MPPositive_HomeViewController: UITableViewDataSource, UITableViewDeleg cell.requestNextBlock = { [weak self] (item) in guard let self = self else {return} + MP_AnalyticsManager.shared.home_b_module_clickAction(MPPositive_BrowseLoadViewModel.shared.browseModuleLists[indexPath.section].title) switch item.browseItem.itemType { case .single: //单曲/视频跳转 diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ListShowViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ListShowViewController.swift index 24f4b8f..a3855c6 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ListShowViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_ListShowViewController.swift @@ -7,7 +7,7 @@ import UIKit ///b面列表歌单展示 -class MPPositive_ListShowViewController: MPPositive_BaseViewController { +class MPPositive_ListShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate { //顶部封面图(呈现歌单封面) private lazy var coverImageView:UIImageView = { let imageView:UIImageView = .init() @@ -31,8 +31,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { //播放全部列表按钮 private lazy var playListBtn:UIButton = { let btn = UIButton() - btn.setBackgroundImage(UIImage(named: "List_UnPlay'logo"), for: .normal) - btn.setBackgroundImage(UIImage(named: "List_Played'logo"), for: .selected) + btn.setBackgroundImage(UIImage(named: "List_ShufflePlay'logo"), for: .normal) btn.isUserInteractionEnabled = false return btn }() @@ -46,14 +45,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { btn.addTarget(self, action: #selector(collectionSwitchClick(_ :)), for: .touchUpInside) return btn }() - //播放状态按钮 - private lazy var playTypeBtn:UIButton = { - let btn = UIButton() - btn.setBackgroundImage(UIImage(named: "List_NormolPlay'logo"), for: .normal) - btn.setBackgroundImage(UIImage(named: "List_ShufflePlay'logo"), for: .selected) - btn.addTarget(self, action: #selector(collectionStatuClick(_ :)), for: .touchUpInside) - return btn - }() + //tableView(歌单/专辑详情展示) private lazy var tableView:UITableView = { let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain) @@ -75,8 +67,11 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { [weak self] in guard let self = self else {return} if listOrAlbum != nil { + configure() reload() tableView.reloadData() + removeErrorView() + MP_HUD.hideNow() } } } @@ -95,12 +90,12 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { self.params = params self.centerTtitle = title self.subtitle = subtitle + MP_HUD.loading() super.init(nibName: nil, bundle: nil) //发起网络请求 MP_NetWorkManager.shared.requestAlbumOrListDatas(browseId, params: params) { [weak self] result in guard let self = self else {return} listOrAlbum = result - } } @@ -111,7 +106,33 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { super.viewDidLoad() setTitle("") setPopBtn() - configure() + errorBlock = { + [weak self] in + guard let self = self else { + return + } + //移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试 + view.subviews.forEach { item in + if item != self.navView { + //移除 + if item.superview != nil { + item.removeFromSuperview() + } + } + } + //添加报错View + setErrorView() + MP_HUD.hideNow() + } + retryBlock = { + [weak self] in + guard let self = self else {return} + MP_HUD.loading() + MP_NetWorkManager.shared.requestAlbumOrListDatas(self.browseid, params: self.params) { [weak self] result in + guard let self = self else {return} + listOrAlbum = result + } + } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -128,7 +149,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { listOrAlbum.header?.setUrltoImage(coverImageView) titleLabel.text = listOrAlbum.header?.title descriptionLabel.text = listOrAlbum.header?._description - playListNumberLabel.text = "Play all (\(listOrAlbum.items.count))" + playListNumberLabel.text = "Play (\(listOrAlbum.items.count))" } //MARK: - UI生成与配置 //页面配置 @@ -173,16 +194,10 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { make.height.equalTo(32*width) make.bottom.equalTo(blurView.snp.bottom).offset(-29*width) } - view.addSubview(playTypeBtn) - playTypeBtn.snp.makeConstraints { make in - make.width.height.equalTo(24*width) - make.right.equalToSuperview().offset(-18*width) - make.centerY.equalTo(playListView.snp.centerY) - } view.addSubview(collectionListBtn) collectionListBtn.snp.makeConstraints { make in make.width.height.equalTo(24*width) - make.right.equalToSuperview().offset(-56*width) + make.right.equalToSuperview().offset(-18*width) make.centerY.equalTo(playListView.snp.centerY) } view.addSubview(tableView) @@ -235,7 +250,20 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { //MARK: - 点击事件 //播放/暂停列表 @objc private func playListActionClick(_ sender:UITapGestureRecognizer) { - + MPPositive_Debouncer.shared.call { + [weak self] in + guard let self = self, let item = listOrAlbum.items.randomElement() else {return} + //触发next请求,优先获取列表全部单曲基础数据(不完善) + MP_NetWorkManager.shared.requestNextList(item.browseItem.playListId ?? "", videoId: item.browseItem.videoId ?? ""){ [weak self] listSongs in + guard let self = self else {return} + //回掉的数据并不完善,生成一个playerloadViewModel + let lodaViewModel = MPPositive_PlayerLoadViewModel(listSongs, currentVideoId: item.browseItem.videoId ?? "") + lodaViewModel.improveData(item.browseItem.videoId ?? "") + MP_PlayerManager.shared.loadPlayer = lodaViewModel + //发布弹出音乐播放器的通知 + NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) + } + } } //切换当前列表收藏状态 @objc private func collectionSwitchClick(_ sender:UIButton) { @@ -262,10 +290,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController { MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels(nil) } } - //更改当前列表播放状态 - @objc private func collectionStatuClick(_ sender:UIButton) { - - } + } //MARK: - tableView extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewDelegate { @@ -275,6 +300,39 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MusicItemShowTableViewCellID, for: indexPath) as! MPPositive_MusicItemShowTableViewCell cell.itemView = listOrAlbum.items[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else {return} + MPPositive_Debouncer.shared.call { + MP_NetWorkManager.shared.requestNextList("", videoId: self.listOrAlbum.items[indexPath.row].browseItem.videoId ?? ""){ [weak self] listSongs in + guard let first = listSongs.first else {return} + let group = DispatchGroup() + group.enter() + improveDataforLycirsAndRelated(first) {[weak self] (result) in + first.lyricsID = result.0 + first.relatedID = result.1 + group.leave() + } + group.enter() + //补全资源路径组和封面路径组 + improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in + first.resourceUrls = resourceUrls.0 + first.itags = resourceUrls.1 + first.mimeTypes = resourceUrls.2 + first.coverUrls = coverUrls + group.leave() + } + group.notify(queue: .main, execute: { + [weak self] in + MPPositive_ModalType = .MoreOperations + let moreVC = MPPositive_MoreSongOperationsViewController(first) + moreVC.transitioningDelegate = self + moreVC.modalPresentationStyle = .custom + self?.present(moreVC, animated: true) + }) + } + } + } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -293,4 +351,7 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD } } } + func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_MoreContentViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_MoreContentViewController.swift index 857cd7a..fb78a7a 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_MoreContentViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Home(首页,各项列表页,艺术家页)/MPPositive_MoreContentViewController.swift @@ -95,4 +95,28 @@ extension MPPositive_MoreContentViewController: UICollectionViewDataSource, UICo return cell } } + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + MP_AnalyticsManager.shared.home_b_module_clickAction(browseModuleList.title) + switch showType { + case .Single: + //单曲/视频跳转 + MPPositive_Debouncer.shared.call { + [weak self] in + guard let self = self else {return} + //触发next请求,优先获取列表全部单曲基础数据(不完善) + MP_NetWorkManager.shared.requestNextList(browseModuleList.items[indexPath.row].browseItem.playListId ?? "", videoId: browseModuleList.items[indexPath.row].browseItem.videoId ?? ""){ [weak self] listSongs in + guard let self = self else {return} + //回掉的数据并不完善,生成一个playerloadViewModel + let lodaViewModel = MPPositive_PlayerLoadViewModel(listSongs, currentVideoId: browseModuleList.items[indexPath.row].browseItem.videoId ?? "") + lodaViewModel.improveData(browseModuleList.items[indexPath.row].browseItem.videoId ?? "") + MP_PlayerManager.shared.loadPlayer = lodaViewModel + NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) + } + } + default: + //列表专辑 + let listVC = MPPositive_ListShowViewController(browseModuleList.items[indexPath.row].browseItem.browseId ?? "", params: browseModuleList.items[indexPath.row].browseItem.params ?? "", title: browseModuleList.items[indexPath.row].title ?? "", subtitle: browseModuleList.items[indexPath.row].subtitle ?? "") + navigationController?.pushViewController(listVC, animated: true) + } + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_PlayerViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_PlayerViewController.swift index 7c4f837..23b2dfc 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_PlayerViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_PlayerViewController.swift @@ -123,25 +123,29 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont //打开时检索播放器状态,好调整内容 MP_PlayerManager.shared.runActionBlock = { [weak self] (currentTime, duration) in guard let self = self else { return } - //展示当前时间 - coverView.durationLabel.text = setTimesToMinSeconds(currentTime) - //展示剩余时间 - let remain:TimeInterval = duration - currentTime - coverView.maxTimesLabel.text = setTimesToMinSeconds(remain) - //调整进度条内容 - let value = currentTime/duration - coverView.sliderView.value = Float(value) + DispatchQueue.main.async { + //展示当前时间 + self.coverView.durationLabel.text = setTimesToMinSeconds(currentTime) + //展示剩余时间 + let remain:TimeInterval = duration - currentTime + self.coverView.maxTimesLabel.text = setTimesToMinSeconds(remain) + //调整进度条内容 + let value = currentTime/duration + self.coverView.sliderView.value = Float(value) + } } //当缓存变化时 MP_PlayerManager.shared.cacheValueBlock = { [weak self] (value, duration) in guard let self = self else { return } - if value < duration { - //进度缓存中 - let float = value/duration - coverView.progressView.setProgress(Float(float), animated: false) - }else { - //进度缓存满了 - coverView.progressView.setProgress(1, animated: false) + DispatchQueue.main.async { + if value < duration { + //进度缓存中 + let float = value/duration + self.coverView.progressView.setProgress(Float(float), animated: false) + }else { + //进度缓存满了 + self.coverView.progressView.setProgress(1, animated: false) + } } } switch MP_PlayerManager.shared.getPlayState() { @@ -308,7 +312,8 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont 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?.isEmpty == true ? "No Lyrics":MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics - coverView.loadBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false + coverView.downloadButton.state = (MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) ? .downloaded:.startDownload + coverView.downloadButton.isUserInteractionEnabled = !(MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) coverView.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false coverView.restoreDownloadProgress() } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_RecommendViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_RecommendViewController.swift index 835505e..1698bf4 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_RecommendViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Player(播放器)/MPPositive_RecommendViewController.swift @@ -7,28 +7,35 @@ import UIKit ///相关内容推荐 -class MPPositive_RecommendViewController: MPPositive_BaseViewController { +class MPPositive_RecommendViewController: MPPositive_BaseViewController,UIViewControllerTransitioningDelegate { //load模块 private var loadRecommend:MPPositive_RecommendLoadViewModel!{ didSet{ - if loadRecommend != nil { - membersView.isHidden = false - segmentView.isHidden = false - listContainerView.isHidden = false - loadRecommend.resultReloadBlock = { - [weak self] in - //刷新分页控制器 - guard let self = self else {return} - sectionLabel.text = loadRecommend.members.title - collectionView.reloadData() - dataSource.titles = loadRecommend?.sectionLists.compactMap({$0.title}) ?? [] - dataSource.reloadData(selectedIndex: 0) - segmentView.reloadData() + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + if loadRecommend != nil { + membersView.isHidden = false + segmentView.isHidden = false + listContainerView.isHidden = false + loadRecommend.resultReloadBlock = { + [weak self] in + //刷新分页控制器 + guard let self = self else {return} + sectionLabel.text = loadRecommend.members.title + collectionView.reloadData() + dataSource.titles = loadRecommend?.sectionLists.compactMap({$0.title}) ?? [] + dataSource.reloadData(selectedIndex: 0) + segmentView.reloadData() + } + configure() + removeErrorView() + MP_HUD.hideNow() + }else { + membersView.isHidden = true + segmentView.isHidden = true + listContainerView.isHidden = true } - }else { - membersView.isHidden = true - segmentView.isHidden = true - listContainerView.isHidden = true } } } @@ -93,10 +100,13 @@ class MPPositive_RecommendViewController: MPPositive_BaseViewController { listContainerView.backgroundColor = .clear return listContainerView }() + private var browseId:String! /// 初始化 /// - Parameter browseId: 相关内容id init(_ browseId:String) { super.init(nibName: nil, bundle: nil) + self.browseId = browseId + MP_HUD.loading() DispatchQueue.main.async { [weak self] in self?.loadRecommend = .init(browseId) @@ -111,7 +121,32 @@ class MPPositive_RecommendViewController: MPPositive_BaseViewController { super.viewDidLoad() setTitle("Recommend") setPopBtn() - configure() + errorBlock = { + [weak self] in + guard let self = self else { + return + } + //移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试 + view.subviews.forEach { item in + if item != self.navView { + //移除 + if item.superview != nil { + item.removeFromSuperview() + } + } + } + //添加报错View + setErrorView() + MP_HUD.hideNow() + } + retryBlock = { + [weak self] in + guard let self = self else {return} + MP_HUD.loading() + DispatchQueue.main.async { + self.loadRecommend = .init(self.browseId) + } + } } private func configure() { view.addSubview(membersView) @@ -208,6 +243,42 @@ extension MPPositive_RecommendViewController: JXSegmentedListContainerViewDataSo break } } + showView.moreBlock = { + [weak self] (itemView) in + guard let self = self else {return} + MPPositive_Debouncer.shared.call { + MP_NetWorkManager.shared.requestNextList("", videoId: itemView.browseItem.videoId ?? ""){ [weak self] listSongs in + guard let first = listSongs.first else {return} + let group = DispatchGroup() + group.enter() + improveDataforLycirsAndRelated(first) {[weak self] (result) in + first.lyricsID = result.0 + first.relatedID = result.1 + group.leave() + } + group.enter() + //补全资源路径组和封面路径组 + improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in + first.resourceUrls = resourceUrls.0 + first.itags = resourceUrls.1 + first.mimeTypes = resourceUrls.2 + first.coverUrls = coverUrls + group.leave() + } + group.notify(queue: .main, execute: { + [weak self] in + MPPositive_ModalType = .MoreOperations + let moreVC = MPPositive_MoreSongOperationsViewController(first) + moreVC.transitioningDelegate = self + moreVC.modalPresentationStyle = .custom + self?.present(moreVC, animated: true) + }) + } + } + } return showView } + func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) + } } diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchResultShowViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchResultShowViewController.swift index b259165..90223ec 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchResultShowViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchResultShowViewController.swift @@ -7,7 +7,7 @@ import UIKit ///搜索结果控制器 -class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { +class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate { //MARK: - 导航View中的控件 //顶部搜索textField private lazy var searchTextField:UITextField = { @@ -36,6 +36,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { btn.addTarget(self, action: #selector(backPopClick(_ :)), for: .touchUpInside) return btn }() + //搜索限定计时器 private var debounceTimer: Timer? //对用户展示的搜索建议组 @@ -48,6 +49,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { //搜索建议组存在,将其显现 suggestionView.isHidden = false suggestionView.suggestions = suggestionList.attributedTexts + MP_AnalyticsManager.shared.search_sug_showAction() }else { suggestionView.isHidden = true suggestionView.suggestions = nil @@ -70,6 +72,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { searchTextField.text = text searchText = text resultsShowView.loadModel = .init(text) + MP_AnalyticsManager.shared.search_sug_clickAction(text) } required init?(coder: NSCoder) { @@ -87,6 +90,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { searchText = text searchTextField.text = text resultsShowView.loadModel = .init(text) + MP_AnalyticsManager.shared.search_sug_clickAction(text) suggestionView.isHidden = true } resultsShowView.scrollBlock = { @@ -124,10 +128,52 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { break } } + resultsShowView.moreBlock = { + [weak self] (itemView) in + guard let self = self else {return} + MPPositive_Debouncer.shared.call { + MP_NetWorkManager.shared.requestNextList("", videoId: itemView.item.videoId ?? ""){ [weak self] listSongs in + guard let first = listSongs.first else {return} + let group = DispatchGroup() + group.enter() + improveDataforLycirsAndRelated(first) {[weak self] (result) in + first.lyricsID = result.0 + first.relatedID = result.1 + group.leave() + } + group.enter() + //补全资源路径组和封面路径组 + improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in + first.resourceUrls = resourceUrls.0 + first.itags = resourceUrls.1 + first.mimeTypes = resourceUrls.2 + first.coverUrls = coverUrls + group.leave() + } + group.notify(queue: .main, execute: { + [weak self] in + MPPositive_ModalType = .MoreOperations + let moreVC = MPPositive_MoreSongOperationsViewController(first) + moreVC.transitioningDelegate = self + moreVC.modalPresentationStyle = .custom + self?.present(moreVC, animated: true) + }) + } + } + } + errorBlock = { + [weak self] in + MP_HUD.hideNow() + self?.resultsShowView.isHidden = false + self?.resultsShowView.emptyImageView.isHidden = false + } } deinit{ debounceTimer = nil } + func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) + } private func configure() { //配置导航栏 let searchView = createSearchView() @@ -246,6 +292,7 @@ extension MPPositive_SearchResultShowViewController:UITextFieldDelegate { self.searchText = text //用户输入了文本 resultsShowView.loadModel = .init(text) + MP_AnalyticsManager.shared.search_sug_clickAction(text) suggestionView.isHidden = true //停止输入 view.endEditing(true) diff --git a/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchViewController.swift b/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchViewController.swift index d866723..45d153a 100644 --- a/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchViewController.swift +++ b/MusicPlayer/MP/MPPositive/ViewControllers/Search(搜索页)/MPPositive_SearchViewController.swift @@ -48,6 +48,7 @@ class MPPositive_SearchViewController: MPPositive_BaseViewController { } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) + MP_AnalyticsManager.shared.search_pvAction() } //配置 private func configure() { diff --git a/MusicPlayer/MP/MPPositive/Views/Base/MPPositive_MoreOperationDownLoadTableViewCell.swift b/MusicPlayer/MP/MPPositive/Views/Base/MPPositive_MoreOperationDownLoadTableViewCell.swift index 4c19b9e..0ea80e8 100644 --- a/MusicPlayer/MP/MPPositive/Views/Base/MPPositive_MoreOperationDownLoadTableViewCell.swift +++ b/MusicPlayer/MP/MPPositive/Views/Base/MPPositive_MoreOperationDownLoadTableViewCell.swift @@ -8,30 +8,41 @@ import UIKit import DownloadButton class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell { - //特殊图片(展示预览图片) - private lazy var iconImageView:UIImageView = { - let imageView:UIImageView = .init() - imageView.contentMode = .scaleAspectFill - imageView.layer.masksToBounds = true - return imageView - }() //设置下载按钮 - private lazy var LoadBtn:PKDownloadButton = { + lazy var loadBtn:PKDownloadButton = { let btn:PKDownloadButton = .init() - //禁止交互 btn.isUserInteractionEnabled = false - btn.downloadedButton.cleanDefaultAppearance() -// btn.downloadedButton.setBackgroundImage(UIImage(named: ""), for: <#T##UIControl.State#>) + //开始下载状态 + btn.startDownloadButton.cleanDefaultAppearance() + btn.startDownloadButton.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) + //下载结束状态 + btn.downloadedButton.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal) + btn.downloadedButton.isUserInteractionEnabled = false + //停止下载状态 + btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal) + btn.stopDownloadButton.isUserInteractionEnabled = false + btn.stopDownloadButton.tintColor = UIColor(hex: "#80F988") + btn.stopDownloadButton.stopButtonWidth = 1 + btn.stopDownloadButton.stopButton.backgroundColor = .clear + btn.stopDownloadButton.stopButton.tintColor = .clear + btn.stopDownloadButton.filledLineWidth = 3*width + btn.stopDownloadButton.filledLineStyleOuter = true + + //加载状态设置 + btn.pendingView.tintColor = UIColor(hex: "#80F988") + btn.pendingView.radius = 12*width + btn.pendingView.emptyLineRadians = 2*width + btn.pendingView.spinTime = 3 return btn }() private lazy var titleLabel:UILabel = createLabel(font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left) var title:String!{ didSet{ - iconImageView.image = UIImage(named: title) titleLabel.text = title } } + var progressBlock:(() -> Void)? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none @@ -53,8 +64,8 @@ class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell { // Configure the view for the selected state } private func configure() { - contentView.addSubview(iconImageView) - iconImageView.snp.makeConstraints { make in + contentView.addSubview(loadBtn) + loadBtn.snp.makeConstraints { make in make.width.height.equalTo(24*width) make.top.equalToSuperview().offset(12*width).priority(999) make.bottom.equalToSuperview().offset(-12*width) @@ -63,7 +74,36 @@ class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell { contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.left.equalTo(iconImageView.snp.right).offset(12*width) + make.left.equalTo(loadBtn.snp.right).offset(12*width) + } + } + //对于下载按钮状态的刷新变动 + public func restoreDownloadProgress(_ song:MPPositive_SongItemModel?) { + guard let song = song else { + return + } + //判断当前播放video是否正在下载 + if let progress = MP_DownloadManager.shared.getProgress(for: song.videoId) { + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + //是正在下载的URL + loadBtn.state = .downloading + //调整下载的进度 + loadBtn.stopDownloadButton.progress = progress + if progressBlock != nil { + progressBlock!() + } + } + }else { + //不是正在下载的内容,判断是否已经下载 + if MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", song.videoId)).count != 0 { + //下载内容 + loadBtn.state = .downloaded + }else { + //不是下载内容 + loadBtn.state = .startDownload + } } } } diff --git a/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowSongTableViewCell.swift b/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowSongTableViewCell.swift index 74d83dd..de81c6b 100644 --- a/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowSongTableViewCell.swift +++ b/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowSongTableViewCell.swift @@ -24,14 +24,14 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell { btn.addTarget(self, action: #selector(moreActionClick(_ :)), for: .touchUpInside) return btn }() - ///下载状态按钮 - private lazy var loadBtn:UIButton = { - let btn:UIButton = .init() - btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) - btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) - btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside) - return btn - }() +// ///下载状态按钮 +// private lazy var loadBtn:UIButton = { +// let btn:UIButton = .init() +// btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) +// btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) +// btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside) +// return btn +// }() var itemView:MPPositive_BrowseItemViewModel!{ didSet{ itemView.setUrltoImage(iconImageView) @@ -74,17 +74,17 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell { make.centerY.equalTo(iconImageView.snp.centerY) make.right.equalToSuperview().offset(-18*width) } - contentView.addSubview(loadBtn) - loadBtn.snp.makeConstraints { make in - make.width.height.equalTo(24*width) - make.centerY.equalTo(iconImageView.snp.centerY) - make.right.equalToSuperview().offset(-54*width) - } +// contentView.addSubview(loadBtn) +// loadBtn.snp.makeConstraints { make in +// make.width.height.equalTo(24*width) +// make.centerY.equalTo(iconImageView.snp.centerY) +// make.right.equalToSuperview().offset(-54*width) +// } contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(iconImageView.snp.top).offset(10*width) make.left.equalTo(iconImageView.snp.right).offset(12*width) - make.right.equalTo(loadBtn.snp.left).offset(-10*width) + make.right.equalTo(moreBtn.snp.left).offset(-10*width) } contentView.addSubview(subtitleLabel) subtitleLabel.snp.makeConstraints { make in @@ -93,10 +93,13 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell { make.right.equalTo(titleLabel.snp.right) } } - + var moreBlock:(() -> Void)? //点击更多 @objc private func moreActionClick(_ sender:UIButton) { - + guard moreBlock != nil else { + return + } + moreBlock!() } //点击下载 @objc private func loadActionClick(_ sender:UIButton) { diff --git a/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowTypeView.swift b/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowTypeView.swift index fab2cb4..ef0bf3f 100644 --- a/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowTypeView.swift +++ b/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_ArtistShowTypeView.swift @@ -66,6 +66,7 @@ class MPPositive_ArtistShowTypeView: UIView, JXPagingViewListViewDelegate { fileprivate var listViewDidScrollCallback: ((UIScrollView) -> ())? //选中内容 var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)? + var moreBlock:((MPPositive_BrowseItemViewModel) -> Void)? init(frame: CGRect, list:MPPositive_ArtistContentListViewModel) { super.init(frame: frame) backgroundColor = .init(hex: "#151718") @@ -133,6 +134,15 @@ extension MPPositive_ArtistShowTypeView:UITableViewDataSource, UITableViewDelega case .single: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell cell.itemView = sectionList.itemViews[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else { + return + } + if moreBlock != nil { + moreBlock!(sectionList.itemViews[indexPath.row]) + } + } return cell case .list: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell diff --git a/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_MusicItemShowTableViewCell.swift b/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_MusicItemShowTableViewCell.swift index 3330017..b73ad61 100644 --- a/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_MusicItemShowTableViewCell.swift +++ b/MusicPlayer/MP/MPPositive/Views/Home/MPPositive_MusicItemShowTableViewCell.swift @@ -41,10 +41,10 @@ class MPPositive_MusicItemShowTableViewCell: UITableViewCell { itemView.setUrltoImage(coverImageView) titleLabel.text = itemView.title subtitleLabel.text = itemView.subtitle - //判断是否下载过 } } + var moreBlock:(() -> Void)? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none @@ -101,7 +101,10 @@ class MPPositive_MusicItemShowTableViewCell: UITableViewCell { } //点击更多 @objc private func moreActionClick(_ sender:UIButton) { - + guard moreBlock != nil else { + return + } + moreBlock!() } //点击下载 @objc private func loadActionClick(_ sender:UIButton) { diff --git a/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_PlayerCoverView.swift b/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_PlayerCoverView.swift index 3bef541..c4e74c0 100644 --- a/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_PlayerCoverView.swift +++ b/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_PlayerCoverView.swift @@ -5,9 +5,9 @@ import UIKit import DownloadButton //B面播放器封面View(封面,标题,副标题,收藏,下载,进度条View) -class MPPositive_PlayerCoverView: UIView { +class MPPositive_PlayerCoverView: UIView, PKDownloadButtonDelegate { //下载进度条View - private var loadView = CircularProgressView() +// private var loadView = CircularProgressView() ///封面 lazy var coverImageView:UIImageView = { @@ -30,13 +30,13 @@ class MPPositive_PlayerCoverView: UIView { return btn }() ///下载按钮 - lazy var loadBtn:UIButton = { - let btn:UIButton = .init() - btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) - btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) - btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside) - return btn - }() +// lazy var loadBtn:UIButton = { +// let btn:UIButton = .init() +// btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) +// btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) +// btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside) +// return btn +// }() ///下载按钮 lazy var downloadButton:PKDownloadButton = { let btn:PKDownloadButton = .init() @@ -50,10 +50,18 @@ class MPPositive_PlayerCoverView: UIView { btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal) btn.stopDownloadButton.isUserInteractionEnabled = false btn.stopDownloadButton.tintColor = UIColor(hex: "#80F988") - btn.stopDownloadButton.filledLineWidth = 1.5 + btn.stopDownloadButton.stopButtonWidth = 1 + btn.stopDownloadButton.stopButton.backgroundColor = .clear + btn.stopDownloadButton.stopButton.tintColor = .clear + btn.stopDownloadButton.filledLineWidth = 3*width btn.stopDownloadButton.filledLineStyleOuter = true + //加载状态设置 -// btn.pendingView.tintColor = . + btn.pendingView.tintColor = UIColor(hex: "#80F988") + btn.pendingView.radius = 12*width + btn.pendingView.emptyLineRadians = 2*width + btn.pendingView.spinTime = 3 + btn.delegate = self return btn }() @@ -104,55 +112,30 @@ class MPPositive_PlayerCoverView: UIView { } required init?(coder: NSCoder) { super.init(coder: coder) -// NotificationCenter.default.addObserver(self, selector: #selector(updateProgress(_:)), name: Notification.Name("DownloadProgressUpdated"), object: nil) -// -// // 恢复进度 -// restoreDownloadProgress() } - + //对于下载按钮状态的刷新变动 public func restoreDownloadProgress() { - if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, - let videoURLString = currentVideo.song.resourceUrls?.first, - let videoURL = URL(string: videoURLString) { - if let progress = DownloadManager.shared.getProgress(for: videoURL) { - //判断当前是正在下载的VideoID - addCircularProgressBar(over: loadBtn) - loadView.setProgress(to: progress) - self.layoutIfNeeded() - self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal) - self.loadBtn.setImage(UIImage(named: "download"), for: .normal) - self.addCircularProgressBar(over: self.loadBtn) - self.loadView.setProgress(to: progress) - }else { - //不是当前下载的videoID - if (loadView.superview) != nil { - loadView.removeFromSuperview() - } - self.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) - self.loadBtn.setImage(UIImage(), for: .normal) - self.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) - } - } - } - - @objc private func updateProgress(_ notification: Notification) { - if let userInfo = notification.userInfo, - let url = userInfo["url"] as? URL, - let progress = userInfo["progress"] as? CGFloat, - let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, - let videoURLString = currentVideo.song.resourceUrls?.first, - let videoURL = URL(string: videoURLString), - videoURL == url { - loadView.setProgress(to: progress) - if loadView.progress.isEqual(to: 1.0){ - self.loadView.removeFromSuperview() - self.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal) - self.loadBtn.setImage(UIImage(named: ""), for: .normal) - } - - } - } + guard let currentVideo = MP_PlayerManager.shared.loadPlayer?.currentVideo else { + return + } + //判断当前播放video是否正在下载 + if let progress = MP_DownloadManager.shared.getProgress(for: currentVideo.song.videoId) { + DispatchQueue.main.async { + [weak self] in + guard let self = self else {return} + //是正在下载的URL + downloadButton.state = .downloading + downloadButton.isUserInteractionEnabled = false + //调整下载的进度 + downloadButton.stopDownloadButton.progress = progress + } + }else { + //不是正在下载的内容 + downloadButton.state = .startDownload + downloadButton.isUserInteractionEnabled = true + } + } deinit { NotificationCenter.default.removeObserver(self) } @@ -185,16 +168,16 @@ class MPPositive_PlayerCoverView: UIView { make.top.equalTo(titleLabel.snp.bottom).offset(6*width) } //配置下载和收藏按钮 - addSubview(loadBtn) - loadBtn.snp.makeConstraints { make in + addSubview(downloadButton) + downloadButton.snp.makeConstraints { make in make.right.equalTo(coverImageView.snp.right).offset(-12*width) make.top.equalTo(coverImageView.snp.bottom).offset(47*width) make.width.height.equalTo(24*width) } addSubview(collectionSongBtn) collectionSongBtn.snp.makeConstraints { make in - make.right.equalTo(loadBtn.snp.left).offset(-20*width) - make.centerY.equalTo(loadBtn.snp.centerY) + make.right.equalTo(downloadButton.snp.left).offset(-20*width) + make.centerY.equalTo(downloadButton.snp.centerY) make.width.height.equalTo(24*width) } addSubview(progressView) @@ -219,8 +202,6 @@ class MPPositive_PlayerCoverView: UIView { make.right.equalTo(sliderView.snp.right) make.top.equalTo(sliderView.snp.bottom).offset(5*width) } - NotificationCenter.default.addObserver(self, selector: #selector(updateProgress(_:)), name: Notification.Name("DownloadProgressUpdated"), object: nil) - // 恢复进度 restoreDownloadProgress() } @@ -272,6 +253,7 @@ class MPPositive_PlayerCoverView: UIView { } MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) + MP_AnalyticsManager.shared.player_b_unlove_clickAction(MP_PlayerManager.shared.loadPlayer.currentVideo?.song.videoId ?? "", videoname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.title ?? "", artistname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.shortBylineText ?? "") } }else{ self.collectionSongBtn.isSelected = true @@ -286,132 +268,94 @@ class MPPositive_PlayerCoverView: UIView { MPPositive_CollectionSongModel.save() MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) + MP_AnalyticsManager.shared.player_b_love_clickAction(MP_PlayerManager.shared.loadPlayer.currentVideo?.song.videoId ?? "", videoname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.title ?? "", artistname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.shortBylineText ?? "") } } } - @objc private func loadActionClick(_ sender: UIButton) { - if MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd == false { - addCircularProgressBar(over: sender) - - self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal) - self.loadBtn.setImage(UIImage(named: "download"), for: .normal) - - if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, - let videoURLString = currentVideo.song.resourceUrls?.first, - let videoURL = URL(string: videoURLString) { - DownloadManager.shared.downloadVideo(from: videoURL, song: MP_PlayerManager.shared.loadPlayer.currentVideo.song, progressHandler: { [weak self] progress in - DispatchQueue.main.async { - self?.loadView.setProgress(to: progress) - NotificationCenter.default.post(name: Notification.Name("DownloadProgressUpdated"), object: nil, userInfo: ["url": videoURL, "progress": progress]) + //下载按钮代理 + func downloadButtonTapped(_ downloadButton: PKDownloadButton!, currentState state: PKDownloadButtonState) { + guard MP_PlayerManager.shared.loadPlayer?.currentVideo != nil, MP_PlayerManager.shared.loadPlayer?.currentVideo?.isDlownd == false else { + return + } + //当前音乐没有下载 + switch state { + case .startDownload: //开始状态,点击时准备加载 + //切换为准备状态 + downloadButton.state = .pending + //禁止用户操作 + downloadButton.isUserInteractionEnabled = false + //当开始下载时 + guard let currentVideo = MP_PlayerManager.shared.loadPlayer?.currentVideo, let videoURL = currentVideo.resourcePlayerURL else { + MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.0) { + downloadButton.state = .startDownload + downloadButton.isUserInteractionEnabled = true + } + return + } + MP_AnalyticsManager.shared.player_b_download_clickAction(currentVideo.song.videoId, videoname: currentVideo.song.title ?? "", artistname: currentVideo.song.shortBylineText ?? "") + //执行下载 + MP_DownloadManager.shared.downloadVideo(from: videoURL, song: currentVideo.song) { [weak self] progress in + guard let self = self, currentVideo.song.videoId == MP_PlayerManager.shared.loadPlayer.currentVideo.song.videoId else { + //不是同一个 + downloadButton.state = (MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) ? .downloaded:.startDownload + downloadButton.isUserInteractionEnabled = !(MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) + return + } + downloadButton.state = .downloading + downloadButton.stopDownloadButton.progress = progress + } completion: { [weak self] result in + guard let self = self else {return} + //下载结束,判断成功或失败 + switch result { + case .success(let song): + //添加数据 + let item = MPPositive_DownloadItemModel.create() + item.coverImage = song.coverUrls!.last + item.reviewImage = song.reviewUrls!.last + item.title = song.title + item.longBylineText = song.longBylineText + item.lengthText = song.lengthText + item.shortBylineText = song.shortBylineText + item.lyrics = song.lyrics + item.lyricsID = song.lyricsID + item.videoId = song.videoId + item.relatedID = song.relatedID + MPPositive_DownloadItemModel.save() + DispatchQueue.main.async { + //回归主线程,判断下载的是否为当前歌曲 + if song.videoId == MP_PlayerManager.shared.loadPlayer?.currentVideo?.song.videoId { + //是当前这首,刷新一下当前播放音乐的下载状态 + MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() + }else { + //不是这首,那就不管他 } - }, completion: { [weak self] result in - switch result { - case .success(let song): - let item = MPPositive_DownloadItemModel.create() - //检索 - item.coverImage = song.coverUrls!.last - item.reviewImage = song.reviewUrls!.last - item.title = song.title - item.longBylineText = song.longBylineText - item.lengthText = song.lengthText - item.shortBylineText = song.shortBylineText - item.lyrics = song.lyrics - item.lyricsID = song.lyricsID - item.videoId = song.videoId - item.relatedID = song.relatedID - - MPPositive_DownloadItemModel.save() - DispatchQueue.main.async { - self?.loadView.removeFromSuperview() - MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() - self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal) - self?.loadBtn.setImage(UIImage(named: ""), for: .normal) - print("完成了对\(song.title ?? "")的下载") - MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil) - } - case .failure(let error): - print("Download failed with error: \(error)") - DispatchQueue.main.async { - self?.loadView.removeFromSuperview() - self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) - self?.loadBtn.setImage(UIImage(named: ""), for: .normal) - } - MP_HUD.text("Download timed out, please download again", delay: 1.5, completion: nil) - } - }) + print("完成了对\(song.title ?? "")的下载") + //按钮变为下载结束状态 + downloadButton.state = .downloaded + downloadButton.isUserInteractionEnabled = false + //更新数据库管理类 + MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil) + MP_AnalyticsManager.shared.player_b_downloadsuccess_actionAction(item.videoId, videoname: item.title ?? "", artistname: item.shortBylineText ?? "") + } + case .failure(let error): + //失败了,打印错误 + print("下载报错,错误详情\(error)") + DispatchQueue.main.async { + //按钮回归可用状态 + downloadButton.state = .startDownload + downloadButton.isUserInteractionEnabled = true + } + MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil) } } + case .pending://准备状态 + break + case .downloading: + break + case .downloaded: + break + @unknown default: + break } - - -// //点击下载 -// @objc private func loadActionClick(_ sender:UIButton) { -// -// if MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd == false{ -// -// // 添加圆形进度条到下载按钮位置 -// addCircularProgressBar(over: sender) -// -// self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal) -// self.loadBtn.setImage(UIImage(named: "download"), for: .normal) -// -// //下载,检索当前播放音乐是否存在 -// if MP_PlayerManager.shared.loadPlayer.currentVideo != nil { -// -// // 下载视频 -// if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, let videoURLString = currentVideo.song.resourceUrls?.first, let videoURL = URL(string: videoURLString) { -// let videoId = currentVideo.song.videoId ?? "default_video_id" -// DownloadManager.shared.downloadVideo(from: videoURL, videoId: videoId, progressView: loadView, completion: { [weak self] result in -// switch result { -// case .success(let fileURL): -// let item = MPPositive_DownloadItemModel.create() -// item.resourcePath = "\(fileURL)" -// item.coverImage = URL(string: MP_PlayerManager.shared.loadPlayer.currentVideo.song.coverUrls!.first!) -// item.reviewImage = URL(string: MP_PlayerManager.shared.loadPlayer.currentVideo.song.reviewUrls!.first!) -// item.title = MP_PlayerManager.shared.loadPlayer.currentVideo.song.title -// item.longBylineText = MP_PlayerManager.shared.loadPlayer.currentVideo.song.longBylineText -// item.lengthText = MP_PlayerManager.shared.loadPlayer.currentVideo.song.lengthText -// item.shortBylineText = MP_PlayerManager.shared.loadPlayer.currentVideo.song.shortBylineText -// item.lyrics = MP_PlayerManager.shared.loadPlayer.currentVideo.lyrics -// item.videoId = MP_PlayerManager.shared.loadPlayer.currentVideo.song.videoId -// item.relatedID = MP_PlayerManager.shared.loadPlayer.currentVideo.song.relatedID -// -// MPPositive_DownloadItemModel.save() -// DispatchQueue.main.async { -// MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() -// self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal) -// self?.loadBtn.setImage(UIImage(named: ""), for: .normal) -// } -// -// self?.loadView.removeFromSuperview() -// case .failure(let error): -// print("Download failed with error: \(error)") -// self?.loadView.removeFromSuperview() -// DispatchQueue.main.async { -// self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) -// self?.loadBtn.setImage(UIImage(named: ""), for: .normal) -// } -// MP_HUD.text("Download timed out, please download again", delay: 1.5, completion: nil) -// } -// }) -// } -// } -// } -// -// } -// // 添加圆形进度条 - - private func addCircularProgressBar(over button: UIButton) { - - loadView.removeFromSuperview() // 移除先前的进度视图(如果有) - - loadView = CircularProgressView(frame: button.bounds) - addSubview(loadView) - loadView.snp.makeConstraints { make in - make.center.equalTo(button) - make.width.height.equalTo(button) - } - } - - + } } diff --git a/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_RecommendShowTypeView.swift b/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_RecommendShowTypeView.swift index dd4573e..509a963 100644 --- a/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_RecommendShowTypeView.swift +++ b/MusicPlayer/MP/MPPositive/Views/Player/MPPositive_RecommendShowTypeView.swift @@ -35,7 +35,7 @@ class MPPositive_RecommendShowTypeView: UIView, JXSegmentedListContainerViewList } //选中内容 var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)? - + var moreBlock:((MPPositive_BrowseItemViewModel) -> Void)? init(_ frame:CGRect, list:MPPositive_RecommendListViewModel) { super.init(frame: frame) backgroundColor = .clear @@ -82,6 +82,15 @@ extension MPPositive_RecommendShowTypeView:UITableViewDataSource, UITableViewDel case .single: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell cell.itemView = sectionList.items[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else { + return + } + if moreBlock != nil { + moreBlock!(sectionList.items[indexPath.row]) + } + } return cell case .list: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell diff --git a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultPreviewShowView.swift b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultPreviewShowView.swift index e7ea238..9ee4c14 100644 --- a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultPreviewShowView.swift +++ b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultPreviewShowView.swift @@ -38,6 +38,7 @@ class MPPositive_SearchResultPreviewShowView: UIView, JXSegmentedListContainerVi } var scrollBlock:(() -> Void)? var chooseMoreIndexBlock:((Int) -> Void)? + var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)? //选中内容 var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)? init(frame: CGRect, sectionLists:[MPPositive_SearchResultListViewModel]) { @@ -85,6 +86,13 @@ extension MPPositive_SearchResultPreviewShowView:UITableViewDataSource, UITableV func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell cell.itemView = sectionLists[indexPath.section].previewItemViews[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else {return} + if moreBlock != nil { + moreBlock!(sectionLists[indexPath.section].previewItemViews[indexPath.row]) + } + } return cell } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { diff --git a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultShowTableViewCell.swift b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultShowTableViewCell.swift index 1ab2164..52926bc 100644 --- a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultShowTableViewCell.swift +++ b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultShowTableViewCell.swift @@ -61,7 +61,7 @@ class MPPositive_SearchResultShowTableViewCell: UITableViewCell { subtitleLabel.text = loadViewModel.subtitle } } - + var moreBlock:(() -> Void)? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none @@ -118,7 +118,9 @@ class MPPositive_SearchResultShowTableViewCell: UITableViewCell { //点击更多 @objc private func moreActionClick(_ sender:UIButton) { - + if moreBlock != nil { + moreBlock!() + } } //点击下载 @objc private func loadActionClick(_ sender:UIButton) { diff --git a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultTypeShowView.swift b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultTypeShowView.swift index bd8a81c..283ac09 100644 --- a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultTypeShowView.swift +++ b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultTypeShowView.swift @@ -55,7 +55,7 @@ class MPPositive_SearchResultTypeShowView: UIView, JXSegmentedListContainerViewL } } var scrollBlock:(() -> Void)? - + var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)? init(frame: CGRect, list:MPPositive_SearchResultListViewModel) { super.init(frame: frame) backgroundColor = .init(hex: "1A1A1A") @@ -106,6 +106,13 @@ extension MPPositive_SearchResultTypeShowView:UITableViewDataSource, UITableView func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell cell.itemView = sectionList.allItemViews[indexPath.row] + cell.moreBlock = { + [weak self] in + guard let self = self else {return} + if moreBlock != nil { + moreBlock!(sectionList.allItemViews[indexPath.row]) + } + } return cell } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { diff --git a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultsShowView.swift b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultsShowView.swift index 5482bea..bdf678a 100644 --- a/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultsShowView.swift +++ b/MusicPlayer/MP/MPPositive/Views/Search/MPPositive_SearchResultsShowView.swift @@ -15,17 +15,21 @@ class MPPositive_SearchResultsShowView: UIView { [weak self] in guard let self = self else {return} if loadModel == nil { - isHidden = true + emptyImageView.isHidden = false }else { + MP_AnalyticsManager.shared.search_result_pvAction() loadModel.resultReloadBlock = { [weak self] in //刷新分页控制器 guard let self = self else {return} + MP_HUD.hideNow() isHidden = false + MP_AnalyticsManager.shared.search_resultsuccess_actionAction() dataSource.titles = loadModel?.sectionLists.compactMap({$0.title}) ?? [] dataSource.reloadData(selectedIndex: 0) segmentView.reloadData() emptyImageView.isHidden = !(dataSource.titles.count == 0) + } } } @@ -70,7 +74,7 @@ class MPPositive_SearchResultsShowView: UIView { return listContainerView }() //数据空图片 - private lazy var emptyImageView:UIImageView = { + lazy var emptyImageView:UIImageView = { let imageView = UIImageView(image: UIImage(named: "empty")) imageView.contentMode = .scaleAspectFill return imageView @@ -78,6 +82,7 @@ class MPPositive_SearchResultsShowView: UIView { var scrollBlock:(() -> Void)? //选中内容 var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)? + var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)? override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .init(hex: "1A1A1A") @@ -144,6 +149,13 @@ extension MPPositive_SearchResultsShowView: JXSegmentedListContainerViewDataSour chooseItemBlock!(item) } } + showView.moreBlock = { + [weak self] (itemView) in + guard let self = self else {return} + if moreBlock != nil { + moreBlock!(itemView) + } + } return showView }else { //展示分类结果 @@ -154,6 +166,13 @@ extension MPPositive_SearchResultsShowView: JXSegmentedListContainerViewDataSour self?.scrollBlock!() } } + showView.moreBlock = { + [weak self] (itemView) in + guard let self = self else {return} + if moreBlock != nil { + moreBlock!(itemView) + } + } return showView } } diff --git a/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_AboutViewController.xib b/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_AboutViewController.xib index efe219d..ef21b6c 100644 --- a/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_AboutViewController.xib +++ b/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_AboutViewController.xib @@ -10,34 +10,34 @@ - - + + - + - + - + - - - + - + + + + + - - - - - - - - + + + + - - - + - + - + + + + + + + - - + diff --git a/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_SettingViewController.swift b/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_SettingViewController.swift index 822178e..607a2db 100644 --- a/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_SettingViewController.swift +++ b/MusicPlayer/MP/MPSideA/ViewControllers/Center(个人资源)/MPSideA_SettingViewController.swift @@ -47,9 +47,9 @@ extension MPSideA_SettingViewController: UITableViewDataSource, UITableViewDeleg navigationController?.pushViewController(aboutVC, animated: true) case 1://Feedback let alert = UIAlertController(title: "Feedback", message: "If you have any comments or suggestions, please contact us at the following e-mail address", preferredStyle: .actionSheet) - let email = UIAlertAction(title: "support@musicoo.app", style: .default) { (_) in + let email = UIAlertAction(title: "marketing@lux-ad.com", style: .default) { (_) in //将邮箱复制到剪切板中 - UIPasteboard.general.string = "support@musicoo.app" + UIPasteboard.general.string = "marketing@lux-ad.com" MP_HUD.text("Successfully copied the e-mail address to the clipboard", delay: 1.0, completion: nil) } alert.addAction(email) @@ -62,7 +62,7 @@ extension MPSideA_SettingViewController: UITableViewDataSource, UITableViewDeleg //分享图片icon let image = UIImage(named: "ICON") //设置分享路径 - let url = URL(string: "https://musicoo.app/") + let url = URL(string: "https://musiclax.mystrikingly.com/") let activityItems = [text,image as Any,url as Any] //弹出分享框 let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities:nil) diff --git a/MusicPlayer/MP/MPSideA/ViewControllers/Home(音乐资源列表)/MPSideA_HomeViewController.swift b/MusicPlayer/MP/MPSideA/ViewControllers/Home(音乐资源列表)/MPSideA_HomeViewController.swift index 2ccbc22..8103eb4 100644 --- a/MusicPlayer/MP/MPSideA/ViewControllers/Home(音乐资源列表)/MPSideA_HomeViewController.swift +++ b/MusicPlayer/MP/MPSideA/ViewControllers/Home(音乐资源列表)/MPSideA_HomeViewController.swift @@ -53,7 +53,7 @@ class MPSideA_HomeViewController: MPSideA_BaseViewController { super.viewWillDisappear(animated) //移除所有通知 NotificationCenter.default.removeObserver(self) - + MP_AnalyticsManager.shared.home_a_pvAction() } deinit { diff --git a/MusicPlayer/PrivacyInfo.xcprivacy b/MusicPlayer/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..289f2fa --- /dev/null +++ b/MusicPlayer/PrivacyInfo.xcprivacy @@ -0,0 +1,27 @@ + + + + + NSPrivacyTracking + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + 3B52.1 + + + + +