b面正式版1.0.2

This commit is contained in:
Mr.zhou 2024-05-31 17:05:17 +08:00
parent 3c20413986
commit 62deb230c0
85 changed files with 2184 additions and 697 deletions

View File

@ -42,8 +42,14 @@
CB24169B2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169A2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 = "<group>"; }; CB24169A2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_LoveArtistTableViewCell.swift; sourceTree = "<group>"; };
CB24169C2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_LoveSongsViewController.swift; sourceTree = "<group>"; }; CB24169C2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_LoveSongsViewController.swift; sourceTree = "<group>"; };
CB24169E2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_OfflineSongsViewController.swift; sourceTree = "<group>"; }; CB24169E2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_OfflineSongsViewController.swift; sourceTree = "<group>"; };
CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
CB48409D2C08738700341244 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_AnalyticsManager.swift; sourceTree = "<group>"; };
CB5661282BE09D0500CFD014 /* MPPositive_JsonPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_JsonPlayer.swift; sourceTree = "<group>"; }; CB5661282BE09D0500CFD014 /* MPPositive_JsonPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_JsonPlayer.swift; sourceTree = "<group>"; };
CB56612C2BE0DF8C00CFD014 /* MP_WebWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_WebWork.swift; sourceTree = "<group>"; }; CB56612C2BE0DF8C00CFD014 /* MP_WebWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_WebWork.swift; sourceTree = "<group>"; };
CBB5D31C2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_MusicItemShowTableViewCell.swift; sourceTree = "<group>"; }; CBB5D31C2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_MusicItemShowTableViewCell.swift; sourceTree = "<group>"; };
@ -421,7 +430,10 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
CB4840A52C0882D100341244 /* FirebaseCrashlytics in Frameworks */,
CB4840A32C0882D100341244 /* FirebaseAnalytics in Frameworks */,
639E3B772F558B3350DD56BA /* Pods_MusicPlayer.framework in Frameworks */, 639E3B772F558B3350DD56BA /* Pods_MusicPlayer.framework in Frameworks */,
CBAFC9EF2C09A1FE0054500E /* FirebaseRemoteConfig in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -456,6 +468,8 @@
009662382BB14A5B00FCA65F /* Assets.xcassets */, 009662382BB14A5B00FCA65F /* Assets.xcassets */,
0096623A2BB14A5B00FCA65F /* LaunchScreen.storyboard */, 0096623A2BB14A5B00FCA65F /* LaunchScreen.storyboard */,
0096623D2BB14A5B00FCA65F /* Info.plist */, 0096623D2BB14A5B00FCA65F /* Info.plist */,
CB48409D2C08738700341244 /* GoogleService-Info.plist */,
CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */,
); );
path = MusicPlayer; path = MusicPlayer;
sourceTree = "<group>"; sourceTree = "<group>";
@ -643,20 +657,20 @@
children = ( children = (
CBCB4FAE2BD11402009760B3 /* MPSideA_CenterViewController.swift */, CBCB4FAE2BD11402009760B3 /* MPSideA_CenterViewController.swift */,
CBCB4FAF2BD11402009760B3 /* MPSideA_CenterViewController.xib */, CBCB4FAF2BD11402009760B3 /* MPSideA_CenterViewController.xib */,
CBCB4FAC2BD11402009760B3 /* MPSideA_AboutViewController.swift */,
CBCB4FAD2BD11402009760B3 /* MPSideA_AboutViewController.xib */,
CBCB4FB02BD11402009760B3 /* MPSideA_DeleteViewController.swift */,
CBCB4FB12BD11402009760B3 /* MPSideA_DeleteViewController.xib */,
CBCB4FB22BD11402009760B3 /* MPSideA_MoreViewController.swift */, CBCB4FB22BD11402009760B3 /* MPSideA_MoreViewController.swift */,
CBCB4FB32BD11402009760B3 /* MPSideA_MoreViewController.xib */, CBCB4FB32BD11402009760B3 /* MPSideA_MoreViewController.xib */,
CBCB4FB42BD11402009760B3 /* MPSideA_PrivacyViewController.swift */, CBCB4FB02BD11402009760B3 /* MPSideA_DeleteViewController.swift */,
CBCB4FB52BD11402009760B3 /* MPSideA_PrivacyViewController.xib */, CBCB4FB12BD11402009760B3 /* MPSideA_DeleteViewController.xib */,
CBCB4FB62BD11402009760B3 /* MPSideA_RenameViewController.swift */, CBCB4FB62BD11402009760B3 /* MPSideA_RenameViewController.swift */,
CBCB4FB72BD11402009760B3 /* MPSideA_RenameViewController.xib */, CBCB4FB72BD11402009760B3 /* MPSideA_RenameViewController.xib */,
CBCB4FB82BD11402009760B3 /* MPSideA_ServiceViewController.swift */,
CBCB4FB92BD11402009760B3 /* MPSideA_ServiceViewController.xib */,
CBCB4FBA2BD11402009760B3 /* MPSideA_SettingViewController.swift */, CBCB4FBA2BD11402009760B3 /* MPSideA_SettingViewController.swift */,
CBCB4FBB2BD11402009760B3 /* MPSideA_SettingViewController.xib */, 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个人资源"; path = "Center个人资源";
sourceTree = "<group>"; sourceTree = "<group>";
@ -1018,6 +1032,7 @@
CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */, CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */,
CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */, CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */,
CB102F542BFAFA7200E967D8 /* MP_DownloadManager.swift */, CB102F542BFAFA7200E967D8 /* MP_DownloadManager.swift */,
CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */,
); );
path = "Tool(工具封装)"; path = "Tool(工具封装)";
sourceTree = "<group>"; sourceTree = "<group>";
@ -1076,6 +1091,11 @@
dependencies = ( dependencies = (
); );
name = MusicPlayer; name = MusicPlayer;
packageProductDependencies = (
CB4840A22C0882D100341244 /* FirebaseAnalytics */,
CB4840A42C0882D100341244 /* FirebaseCrashlytics */,
CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */,
);
productName = MusicPlayer; productName = MusicPlayer;
productReference = 009662292BB14A5A00FCA65F /* MusicPlayer.app */; productReference = 009662292BB14A5A00FCA65F /* MusicPlayer.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@ -1105,6 +1125,9 @@
Base, Base,
); );
mainGroup = 009662202BB14A5A00FCA65F; mainGroup = 009662202BB14A5A00FCA65F;
packageReferences = (
CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
productRefGroup = 0096622A2BB14A5A00FCA65F /* Products */; productRefGroup = 0096622A2BB14A5A00FCA65F /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@ -1129,6 +1152,7 @@
CBC54E572BC4D5D3003B1901 /* Shhh….mp3 in Resources */, CBC54E572BC4D5D3003B1901 /* Shhh….mp3 in Resources */,
CBCB500D2BD11402009760B3 /* MPSideA_CustomTabBarView.xib in Resources */, CBCB500D2BD11402009760B3 /* MPSideA_CustomTabBarView.xib in Resources */,
CBCB50072BD11402009760B3 /* MPSideA_PlayerViewController.xib in Resources */, CBCB50072BD11402009760B3 /* MPSideA_PlayerViewController.xib in Resources */,
CB48409E2C08738700341244 /* GoogleService-Info.plist in Resources */,
0096623C2BB14A5B00FCA65F /* LaunchScreen.storyboard in Resources */, 0096623C2BB14A5B00FCA65F /* LaunchScreen.storyboard in Resources */,
CBC54E5C2BC4D5D3003B1901 /* TV.mp3 in Resources */, CBC54E5C2BC4D5D3003B1901 /* TV.mp3 in Resources */,
CBCB50152BD11402009760B3 /* MPSideA_Home_FourthListCollectionViewCell.xib in Resources */, CBCB50152BD11402009760B3 /* MPSideA_Home_FourthListCollectionViewCell.xib in Resources */,
@ -1155,6 +1179,7 @@
CBCB50052BD11402009760B3 /* MPSideA_HomeViewController.xib in Resources */, CBCB50052BD11402009760B3 /* MPSideA_HomeViewController.xib in Resources */,
CBC54E642BC4D5D3003B1901 /* Seawater Surging.mp3 in Resources */, CBC54E642BC4D5D3003B1901 /* Seawater Surging.mp3 in Resources */,
CBCB500F2BD11402009760B3 /* MPSideA_CenterTableViewCell.xib in Resources */, CBCB500F2BD11402009760B3 /* MPSideA_CenterTableViewCell.xib in Resources */,
CB48409C2C08721600341244 /* PrivacyInfo.xcprivacy in Resources */,
CBCB4FF32BD11402009760B3 /* MPSideA_AboutViewController.xib in Resources */, CBCB4FF32BD11402009760B3 /* MPSideA_AboutViewController.xib in Resources */,
CBC54E582BC4D5D3003B1901 /* Shh Shh.mp3 in Resources */, CBC54E582BC4D5D3003B1901 /* Shh Shh.mp3 in Resources */,
CBC54E632BC4D5D3003B1901 /* Howling Wind.mp3 in Resources */, CBC54E632BC4D5D3003B1901 /* Howling Wind.mp3 in Resources */,
@ -1300,6 +1325,7 @@
CBE1CB4E2BDE4BD800701D57 /* MPPositive_ListAlbumListViewModel.swift in Sources */, CBE1CB4E2BDE4BD800701D57 /* MPPositive_ListAlbumListViewModel.swift in Sources */,
CB2416972C05D3C3007877F7 /* MPPositive_CollectionArtistViewModel.swift in Sources */, CB2416972C05D3C3007877F7 /* MPPositive_CollectionArtistViewModel.swift in Sources */,
CBD313572BD63B390015D227 /* MPPositive_HomeListSecondCollectionViewCell.swift in Sources */, CBD313572BD63B390015D227 /* MPPositive_HomeListSecondCollectionViewCell.swift in Sources */,
CB4840A02C087BD900341244 /* MP_AnalyticsManager.swift in Sources */,
0096622D2BB14A5A00FCA65F /* AppDelegate.swift in Sources */, 0096622D2BB14A5A00FCA65F /* AppDelegate.swift in Sources */,
CBC32A532BD8D9F300687171 /* MPPositive_BrowseItemModel.swift in Sources */, CBC32A532BD8D9F300687171 /* MPPositive_BrowseItemModel.swift in Sources */,
CBE16B952BF251FF005B7EE6 /* MPPositive_SearchSuggestionItemListModel.swift in Sources */, CBE16B952BF251FF005B7EE6 /* MPPositive_SearchSuggestionItemListModel.swift in Sources */,
@ -1522,15 +1548,21 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1.2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = T93S37G27F; DEVELOPMENT_TEAM = T93S37G27F;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = MusicPlayer/Info.plist; 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_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -1538,8 +1570,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.lux.musicplayer.MusicPlayer; PRODUCT_BUNDLE_IDENTIFIER = relax.offline.mp3.music;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@ -1560,15 +1592,21 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1.2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = T93S37G27F; DEVELOPMENT_TEAM = T93S37G27F;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = MusicPlayer/Info.plist; 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_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -1576,8 +1614,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.lux.musicplayer.MusicPlayer; PRODUCT_BUNDLE_IDENTIFIER = relax.offline.mp3.music;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@ -1613,6 +1651,35 @@
}; };
/* End XCConfigurationList section */ /* 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 */ /* Begin XCVersionGroup section */
009662352BB14A5A00FCA65F /* MusicPlayer.xcdatamodeld */ = { 009662352BB14A5A00FCA65F /* MusicPlayer.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;

View File

@ -49,6 +49,16 @@
ReferencedContainer = "container:MusicPlayer.xcodeproj"> ReferencedContainer = "container:MusicPlayer.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-FIRDebugEnabled"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "-FIRDebugDisabled"
isEnabled = "NO">
</CommandLineArgument>
</CommandLineArguments>
<EnvironmentVariables> <EnvironmentVariables>
<EnvironmentVariable <EnvironmentVariable
key = "IDERedirectionPolicy" key = "IDERedirectionPolicy"

View File

@ -0,0 +1,123 @@
{
"originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329",
"pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "748c7837511d0e6a507737353af268484e1745e2",
"version" : "1.2024011601.1"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "076b241a625e25eac22f8849be256dfb960fcdfe",
"version" : "10.19.1"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
"revision" : "8bcaf973b1d84e119b7c7c119abad72ed460979f",
"version" : "10.27.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "70df02431e216bed98dd461e0c4665889245ba70",
"version" : "10.27.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "a637d318ae7ae246b02d7305121275bc75ed5565",
"version" : "9.4.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6",
"version" : "7.13.3"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359",
"version" : "1.62.2"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "0382ca27f22fb3494cf657d8dc356dc282cd1193",
"version" : "3.4.1"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
"version" : "100.0.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
"version" : "2.30910.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "9f0c76544701845ad98716f3f6a774a892152bcb",
"version" : "1.26.0"
}
}
],
"version" : 3
}

View File

@ -10,6 +10,7 @@ import CoreData
import AVFoundation import AVFoundation
import Alamofire import Alamofire
import Tiercel import Tiercel
import FirebaseCore
@_exported import IQKeyboardManagerSwift @_exported import IQKeyboardManagerSwift
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
@ -25,8 +26,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
print("Users are not allowed to be notified of messages.") print("Users are not allowed to be notified of messages.")
} }
} }
//FireBase
FirebaseApp.configure()
// //
DownloadManager.shared.cancelAllTasksIfNeeded() MP_DownloadManager.shared.cancelAllTasksIfNeeded()
setAudioSupport() setAudioSupport()
MP_NetWorkManager.shared.requestStatusToYouTube() MP_NetWorkManager.shared.requestStatusToYouTube()
IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.enable = true
@ -34,12 +37,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = .init(hex: "#161616") window?.backgroundColor = .init(hex: "#161616")
switch_lunch() switch_lunch()
//
MP_AnalyticsManager.shared.user_launchAction()
return true return true
} }
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
if identifier == "com.yourApp.backgroundDownload" { if identifier == "com.yourApp.backgroundDownload" {
DownloadManager.shared.session = SessionManager("com.yourApp.backgroundDownload", configuration: .init()) MP_DownloadManager.shared.session = SessionManager("com.yourApp.backgroundDownload", configuration: .init())
DownloadManager.shared.session.completionHandler = completionHandler MP_DownloadManager.shared.session.completionHandler = completionHandler
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -1,15 +1,16 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "img_v3_02ae_ad486134-21d1-4b05-869c-06e3d548e40g.jpg",
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"filename" : "img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "3x" "scale" : "3x"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -5,12 +5,12 @@
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "img_v3_02ae_c419ebcc-7bcc-4018-a6f4-b4cb25f7d11g.png", "filename" : "img_v3_02b6_3f726db0-fde6-4335-a78b-2a4ba9365ccg.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"filename" : "img_v3_02ae_2efa5fb1-c28e-4299-b6c7-abe303cb6f8g.png", "filename" : "img_v3_02b6_97959922-6ac1-437f-aab5-c65850cdb09g.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "3x" "scale" : "3x"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

@ -5,12 +5,12 @@
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "Group_1597880487@2x.png", "filename" : "Group_1597880484@2x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"filename" : "Group_1597880487@3x.png", "filename" : "Group_1597880484@3x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "3x" "scale" : "3x"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyDIsVu-mThBUmZvq6FiVlcq6mTElUJTuhg</string>
<key>GCM_SENDER_ID</key>
<string>773095886766</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>relax.offline.mp3.music</string>
<key>PROJECT_ID</key>
<string>musiclax-ios</string>
<key>STORAGE_BUCKET</key>
<string>musiclax-ios.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:773095886766:ios:54ac9cf528ca696540823f</string>
</dict>
</plist>

View File

@ -2,39 +2,14 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>NSMicrophoneUsageDescription</key>
<string>&quot;Musicoo&quot; 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?</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>&quot;Musicoo&quot; 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?</string>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>
<array> <array>
<string>audio</string> <string>fetch</string>
<string>backgroundFetch</string>
</array> </array>
<key>NSLocationWhenInUseUsageDescription</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<string>&quot;Musicoo&quot; needs to obtain your location information in order to refine the preview music information provided to you!</string> <false/>
<key>NSLocationAlwaysUsageDescription</key> <key>NSUserTrackingUsageDescription</key>
<string>&quot;Musicoo&quot; needs to obtain your location information in order to refine the preview music information provided to you!</string> <string>&quot;Musiclax&quot; 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.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>&quot;Musicoo&quot; needs to obtain your location information in order to refine the preview music information provided to you!</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleURLName</key>
<string>com.lux.musicplayer.MusicPlayer</string>
</dict>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>backgroundFetch</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -16,9 +16,11 @@ class MP_LunchViewController: UIViewController {
// //
private var timer:CADisplayLink! private var timer:CADisplayLink!
// //
private lazy var maxTimes:TimeInterval = 8 private lazy var maxTimes:TimeInterval = 6
// //
private lazy var currentTimes:TimeInterval = 0 private lazy var currentTimes:TimeInterval = 0
//
private var completionBlock:(() -> Void)?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = .init(hex: "#000000") view.backgroundColor = .init(hex: "#000000")
@ -27,14 +29,60 @@ class MP_LunchViewController: UIViewController {
timer.preferredFramesPerSecond = 20 timer.preferredFramesPerSecond = 20
//线 //线
timer.add(to: RunLoop.current, forMode: .common) timer.add(to: RunLoop.current, forMode: .common)
// //idfa
timer.isPaused = false _ = requestTrackingAuthorization(self)
// //
MP_LocationManager.shared.setLocationPermission(self, complete: nil) MP_LocationManager.shared.setLocationPermission(self, complete: nil)
//youtube MP_AnalyticsManager.shared.getOpenStatus { [weak self] open in
MP_WebWork.shared.pingYoutubeHome() guard let self = self else {return}
if open {
NotificationCenter.notificationKey.add(observer: self, selector: #selector(jumpAction(_:)), notificationName: .js_edit_completion) //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 { deinit {
// //
@ -42,15 +90,9 @@ class MP_LunchViewController: UIViewController {
timer = nil timer = nil
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
} }
@objc private func jumpAction(_ sender:Notification) { override func viewWillDisappear(_ animated: Bool) {
DispatchQueue.main.async { super.viewWillDisappear(animated)
[weak self] in MP_AnalyticsManager.shared.launch_pvAction()
guard let self = self else {return}
//
timer.isPaused = true
//
accessAppdelegate.switch_positive()
}
} }
// //
@ -66,14 +108,9 @@ class MP_LunchViewController: UIViewController {
progressView.setProgress(value) progressView.setProgress(value)
} }
}else { }else {
// DispatchQueue.main.async { if completionBlock != nil {
// [weak self] in completionBlock!()
// guard let self = self else {return} }
// //
// timer.isPaused = true
// //
// accessAppdelegate.switch_positive()
// }
} }
} }
} }

View File

@ -118,7 +118,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{
private func album(){ private func album(){
DispatchQueue.main.async { 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 let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
} }
@ -141,7 +141,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{
private func camera(){ private func camera(){
DispatchQueue.main.async { 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 let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
} }
@ -164,7 +164,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{
private func video(){ private func video(){
DispatchQueue.main.async { 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 let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
} }

View File

@ -95,6 +95,8 @@ extension NotificationCenter{
case positive_nav_push case positive_nav_push
///bPop ///bPop
case positive_nav_pop case positive_nav_pop
///b
case netWork_error_deal
} }
} }
} }

View File

@ -7,6 +7,8 @@
import UIKit import UIKit
import Foundation import Foundation
import AVFoundation import AVFoundation
import AppTrackingTransparency
import AdSupport
@_exported import JXSegmentedView @_exported import JXSegmentedView
@_exported import JXPagingView @_exported import JXPagingView
//JXPagingListContainerViewextensionJXSegmentedViewListContainer //JXPagingListContainerViewextensionJXSegmentedViewListContainer
@ -38,15 +40,22 @@ let Phone_Model = UIDevice.current.model
let System_Version = UIDevice.current.systemVersion let System_Version = UIDevice.current.systemVersion
/// ///
let Language_first_local = NSLocale.preferredLanguages.first! 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 bottomPadding = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
/// ///
let placeholderImage:UIImage = UIImage(named: "Home First'placeholder")! 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: - //MARK: -
/// ///
@ -71,6 +80,20 @@ func getDocumentsFileURL(_ videoID: String) -> String? {
return nil return nil
} }
} }
///nextIDID
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 { func setTimesToMinSeconds(_ time:TimeInterval) -> String {
// //
@ -99,7 +122,7 @@ func authorize(observe:UIViewController) -> Bool{
}) })
default: () default: ()
DispatchQueue.main.async(execute: { () -> Void in 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 cancelAction = UIAlertAction(title:"Cancel", style: .cancel, handler:nil)
let settingsAction = UIAlertAction(title:"Settings", style: .default, handler: { let settingsAction = UIAlertAction(title:"Settings", style: .default, handler: {
(action) -> Void in (action) -> Void in
@ -143,3 +166,41 @@ func switchPlayTypeBtnIcon(_ btn:UIButton) {
btn.setBackgroundImage(UIImage(named: "Player_Single'logo"), for: .normal) 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
}
}

View File

@ -15,7 +15,7 @@ class MPPositive_Debouncer: NSObject {
private var delay: TimeInterval private var delay: TimeInterval
private override init() { private override init() {
delay = 0.4 delay = 0.5
super.init() super.init()
} }
deinit { deinit {

View File

@ -472,7 +472,6 @@ class MPSideA_MediaCenterManager {
center!.playCommand.addTarget(handler: { [weak self] (event) in center!.playCommand.addTarget(handler: { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem} guard let self = self else { return .noActionableNowPlayingItem}
if self.music != nil { if self.music != nil {
return .success return .success
}else { }else {
return .noActionableNowPlayingItem return .noActionableNowPlayingItem

View File

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

View File

@ -8,14 +8,14 @@
import Foundation import Foundation
import Foundation import Foundation
import Tiercel import Tiercel
class DownloadManager: NSObject { class MP_DownloadManager: NSObject {
static let shared = DownloadManager() static let shared = MP_DownloadManager()
var session: SessionManager! var session: SessionManager!
var progressHandlers: [URL: (CGFloat) -> Void] = [:] var progressHandlers: [String: (CGFloat) -> Void] = [:]
var completionHandlers: [URL: (Result<MPPositive_SongItemModel, Error>) -> Void] = [:] var completionHandlers: [URL: (Result<MPPositive_SongItemModel, Error>) -> Void] = [:]
var downloadTasks: [URL: URLSessionDownloadTask] = [:] var downloadTasks: [URL: URLSessionDownloadTask] = [:]
var progressStorage: [URL: CGFloat] = [:] // var progressStorage: [String: CGFloat] = [:] //
var songHandlers:[URL: MPPositive_SongItemModel] = [:] var songHandlers:[URL: MPPositive_SongItemModel] = [:]
private override init() { private override init() {
super.init() super.init()
@ -26,15 +26,15 @@ class DownloadManager: NSObject {
session = SessionManager("com.yourApp.backgroundDownload", configuration: configuration) session = SessionManager("com.yourApp.backgroundDownload", configuration: configuration)
} }
func downloadVideo(from url: URL, song:MPPositive_SongItemModel, progressHandler: @escaping (CGFloat) -> Void, completion: @escaping (Result<MPPositive_SongItemModel, Error>) -> Void) { func downloadVideo(from url: URL, song:MPPositive_SongItemModel, progressHandler: @escaping (CGFloat) -> Void, completion: @escaping (Result<MPPositive_SongItemModel, Error>) -> Void) {
progressHandlers[url] = progressHandler progressHandlers[song.videoId] = progressHandler
completionHandlers[url] = completion completionHandlers[url] = completion
songHandlers[url] = song songHandlers[url] = song
let downloadTask = session.download(url, headers: ["Accept-Encoding": "gzip, deflate"]) let downloadTask = session.download(url, headers: ["Accept-Encoding": "gzip, deflate"])
// //
downloadTask?.progress(handler: { [weak self] (task) in downloadTask?.progress(handler: { [weak self] (task) in
guard let self = self else {return} guard let self = self else {return}
progressHandlers[task.url]?(task.progress.fractionCompleted) progressHandlers[song.videoId]?(task.progress.fractionCompleted)
progressStorage[task.url] = task.progress.fractionCompleted progressStorage[song.videoId] = task.progress.fractionCompleted
}) })
// //
downloadTask?.success(handler: { [weak self] (task) in 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 { do {
try FileManager.default.moveItem(at: filePathUrl, to: fileURL) try FileManager.default.moveItem(at: filePathUrl, to: fileURL)
//VideoID //VideoID
completionHandlers[originalURL]?(.success(songHandlers[originalURL]!)) completionHandlers[originalURL]?(.success(songHandlers[originalURL]!))
progressStorage[originalURL] = nil // progressStorage[song.videoId] = nil //
} catch { } catch {
completionHandlers[originalURL]?(.failure(error)) completionHandlers[originalURL]?(.failure(error))
} }
//
if task.status == .succeeded {
session.remove(task, completely: true) {_ in
print("\(song.title ?? "")下载任务完成,移除任务")
}
}
}).failure(handler: { [weak self] (task) in }).failure(handler: { [weak self] (task) in
guard let self = self else {return} guard let self = self else {return}
// //
@ -70,10 +76,15 @@ class DownloadManager: NSObject {
if let error = task.error { if let error = task.error {
completionHandlers[originalURL]?(.failure(error)) completionHandlers[originalURL]?(.failure(error))
} }
if task.status == .failed {
session.cancel(task) { _ in
print("\(song.title ?? "")下载任务失败,取消任务")
}
}
}) })
} }
func getProgress(for url: URL) -> CGFloat? { func getProgress(for videoId: String) -> CGFloat? {
return progressStorage[url] return progressStorage[videoId]
} }
func cancelAllTasksIfNeeded() { func cancelAllTasksIfNeeded() {
// //
@ -85,11 +96,12 @@ class DownloadManager: NSObject {
func deleteFileDocuments(_ videoId:String, completion:@escaping((String) -> Void)) { func deleteFileDocuments(_ videoId:String, completion:@escaping((String) -> Void)) {
let downloadsURL = DocumentsURL.appendingPathComponent("Downloads") let downloadsURL = DocumentsURL.appendingPathComponent("Downloads")
let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4") let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4")
if FileManager.default.fileExists(atPath: fileURL.absoluteString) { if FileManager.default.fileExists(atPath: fileURL.path) {
do{ do{
try FileManager.default.removeItem(at: fileURL) try FileManager.default.removeItem(at: fileURL)
// //
completion(videoId) completion(videoId)
print("成功删除了\(videoId)文件")
}catch{ }catch{
print("删除文件时发生错误:\(error)") 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<URL, Error>) -> 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))
// }
// }
// }
//}

View File

@ -22,6 +22,15 @@ class MP_HUD: NSObject {
/// ///
case loading 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 ///HUD
static func text(_ text:String?,delay:TimeInterval,completion:(() -> Void)?){ static func text(_ text:String?,delay:TimeInterval,completion:(() -> Void)?){
showWithStatus(hudStatus: .onlyText, text: text, delay: delay, completion: completion) 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)?) { static func showWithStatus(hudStatus status: status, text: String?, delay: TimeInterval ,completion:(() -> Void)?) {
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.setDefaultMaskType(.clear) 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)) SVProgressHUD.setOffsetFromCenter(.init(horizontal: 0, vertical: 0))
switch status { switch status {
case .success: case .success:
@ -81,4 +91,5 @@ class MP_HUD: NSObject {
completion() completion()
} }
} }
} }

View File

@ -39,7 +39,7 @@ class MP_LocationManager: NSObject {
MP_LocationManager().requestLocationAuthorizaiton() MP_LocationManager().requestLocationAuthorizaiton()
case .restricted, .denied: case .restricted, .denied:
DispatchQueue.main.async { 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 CancelAction = UIAlertAction(title:"Cancel", style: .cancel)
let OKAction = UIAlertAction(title: "Settings", style: .default) { (action) in let OKAction = UIAlertAction(title: "Settings", style: .default) { (action) in
let url = URL(string: UIApplication.openSettingsURLString) let url = URL(string: UIApplication.openSettingsURLString)

View File

@ -21,6 +21,8 @@ class MP_NetWorkManager: NSObject {
/// ///
private let MPSession = Alamofire.Session(interceptor: MP_CustomRetrier()) private let MPSession = Alamofire.Session(interceptor: MP_CustomRetrier())
//MARK: - API //MARK: - API
///IP
private let iPInfo:String = "https://api.tikustok.com/app/common/getIPInfo"
/// ///
private let header:String = "https://music.youtube.com" private let header:String = "https://music.youtube.com"
/// ///
@ -37,6 +39,17 @@ class MP_NetWorkManager: NSObject {
private let search = "/search" private let search = "/search"
///YouTuBe ///YouTuBe
private let youTubeKeys:[String] = ["MUSIC_VIDEO_TYPE_ATV","MUSIC_VIDEO_TYPE_OMV","MUSIC_PAGE_TYPE_ALBUM","MUSIC_PAGE_TYPE_ARTIST","MUSIC_PAGE_TYPE_PLAYLIST","MUSIC_PAGE_TYPE_TRACK_LYRICS","MUSIC_PAGE_TYPE_TRACK_RELATED"] private let youTubeKeys:[String] = ["MUSIC_VIDEO_TYPE_ATV","MUSIC_VIDEO_TYPE_OMV","MUSIC_PAGE_TYPE_ALBUM","MUSIC_PAGE_TYPE_ARTIST","MUSIC_PAGE_TYPE_PLAYLIST","MUSIC_PAGE_TYPE_TRACK_LYRICS","MUSIC_PAGE_TYPE_TRACK_RELATED"]
///IP
private let banIPs:[String] = ["CN",
"HK",
"TW",
"JP",
"KR",
"GB",
"CH",
"BE",
"MO",
"SG"]
// //
enum NetWorkStatus: String { enum NetWorkStatus: String {
case notReachable = "网络不可用" case notReachable = "网络不可用"
@ -136,7 +149,7 @@ class MP_NetWorkManager: NSObject {
default:// default://
DispatchQueue.main.async { 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 let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
} }
@ -178,12 +191,50 @@ class MP_NetWorkManager: NSObject {
} }
//MARK: - API //MARK: - API
extension MP_NetWorkManager { 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: - //MARK: -
///YouTubemusic/ ///YouTubemusic/
func requestBrowseDatas() { func requestBrowseDatas() {
//continuationcontinuation, //continuationcontinuation,
// //
browseQueque = DispatchQueue(label: "com.request.browseQueque") browseQueque = DispatchQueue(label: "com.request.browseQueque")
visitorData = nil
//browse //browse
let path = header+point+browse let path = header+point+browse
//url //url
@ -239,7 +290,8 @@ extension MP_NetWorkManager {
} }
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
browseQueque = nil
} }
} }
} }
@ -265,7 +317,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -301,7 +353,7 @@ extension MP_NetWorkManager {
comletion(list) comletion(list)
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -322,7 +374,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -356,7 +408,7 @@ extension MP_NetWorkManager {
comletion(artist) comletion(artist)
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -378,7 +430,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -407,7 +459,7 @@ extension MP_NetWorkManager {
} }
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -431,7 +483,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -460,7 +512,7 @@ extension MP_NetWorkManager {
} }
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -485,7 +537,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -514,7 +566,7 @@ extension MP_NetWorkManager {
completion(listSongs) completion(listSongs)
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -536,7 +588,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -564,7 +616,7 @@ extension MP_NetWorkManager {
completion(result) completion(result)
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -592,6 +644,12 @@ extension MP_NetWorkManager {
"platform":"MOBILE", "platform":"MOBILE",
"browserVersion":"125.0.0.0", "browserVersion":"125.0.0.0",
// "userAgent": // "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): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -631,7 +689,7 @@ extension MP_NetWorkManager {
// "context":[ // "context":[
// "client":[ // "client":[
// "clientName": "WEB_REMIX", // "clientName": "WEB_REMIX",
//// "visitorData":visitorData, //// //"visitorData":visitorData,
//// "originalUrl":"https://music.youtube.com/watch?v=\(videoId)", //// "originalUrl":"https://music.youtube.com/watch?v=\(videoId)",
// //访 // //访
// "clientVersion": "1.\(currTimeDate).01.00", // "clientVersion": "1.\(currTimeDate).01.00",
@ -689,7 +747,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -715,7 +773,7 @@ extension MP_NetWorkManager {
completion(parsingLyrics(value) ?? "") completion(parsingLyrics(value) ?? "")
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -736,7 +794,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -765,7 +823,7 @@ extension MP_NetWorkManager {
} }
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -789,7 +847,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -817,7 +875,7 @@ extension MP_NetWorkManager {
} }
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -840,7 +898,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -870,7 +928,7 @@ extension MP_NetWorkManager {
} }
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -897,7 +955,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -926,7 +984,7 @@ extension MP_NetWorkManager {
case .failure(let error): case .failure(let error):
// //
print("Request failed: \(error)") handleError(url, error: error)
} }
} }
} }
@ -954,7 +1012,7 @@ extension MP_NetWorkManager {
"client":[ "client":[
//web //web
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"visitorData":visitorData, //"visitorData":visitorData,
//访 //访
"clientVersion": "1.\(currTimeDate).01.00", "clientVersion": "1.\(currTimeDate).01.00",
"platform":"MOBILE", "platform":"MOBILE",
@ -983,11 +1041,39 @@ extension MP_NetWorkManager {
case .failure(let error): 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: - //MARK: -
extension MP_NetWorkManager { extension MP_NetWorkManager {
@ -1819,30 +1905,35 @@ extension MP_NetWorkManager {
//MARK: - //MARK: -
/// ///
class MP_CustomRetrier: RequestInterceptor { class MP_CustomRetrier: RequestInterceptor {
// //
private let maximumRetryCount = 3
//
private var retryCounts: [String: Int] = [:] private var retryCounts: [String: Int] = [:]
func retry(_ request: Alamofire.Request, for session: Alamofire.Session, dueTo error: any Error, completion: @escaping (Alamofire.RetryResult) -> Void) { //
// id private let maxRetryCount: Int = 3
let requestID = request.id //
let currentRetryCount = retryCounts[requestID.uuidString] ?? 0 private let retryInterval: TimeInterval = 1.0
// //
if currentRetryCount < maximumRetryCount, let response = request.task?.response as? HTTPURLResponse, response.statusCode == 503 { func retry(_ request: Request, for session: Session, dueTo error: any Error, completion: @escaping (RetryResult) -> Void) {
// // URL便
retryCounts[requestID.uuidString] = currentRetryCount + 1 guard let url = request.request?.url?.absoluteString else {
// 1
completion(.retryWithDelay(1))
print("请求\(requestID.uuidString)执行重复请求")
} else {
//
retryCounts[requestID.uuidString] = nil
completion(.doNotRetry) 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
}
} }

View File

@ -10,6 +10,7 @@ import AVFoundation
import MediaPlayer import MediaPlayer
import AVKit import AVKit
import FreeStreamer import FreeStreamer
import Kingfisher
/// ///
enum MP_PlayerStateType:Int { enum MP_PlayerStateType:Int {
/// ///
@ -28,6 +29,13 @@ enum MP_PlayerPlayType:Int {
/// ///
case single = 2 case single = 2
} }
///
enum MP_TimerStateType:Int {
///
case Resume = 0
///
case Suspend = 1
}
/// ///
typealias MP_PlayTimerStartAction = () -> Void typealias MP_PlayTimerStartAction = () -> Void
@ -51,6 +59,14 @@ class MP_PlayerManager:NSObject{
static let shared = MP_PlayerManager() static let shared = MP_PlayerManager()
/// ///
private var player:AVPlayer = AVPlayer() private var player:AVPlayer = AVPlayer()
//
private var center:MPRemoteCommandCenter?
///
private var timer:DispatchSourceTimer?
///
private var times:TimeInterval = 0
///
private var queue:DispatchQueue?
///load ///load
var loadPlayer:MPPositive_PlayerLoadViewModel!{ var loadPlayer:MPPositive_PlayerLoadViewModel!{
didSet{ didSet{
@ -94,6 +110,8 @@ class MP_PlayerManager:NSObject{
} }
} }
///
private var timerType:MP_TimerStateType = .Suspend
/// ///
private var startActionBlock:MP_PlayTimerStartAction! private var startActionBlock:MP_PlayTimerStartAction!
/// ///
@ -102,12 +120,50 @@ class MP_PlayerManager:NSObject{
var cacheValueBlock:MP_PlayCacheValueAction! var cacheValueBlock:MP_PlayCacheValueAction!
private override init() { private override init() {
super.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.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
//
NotificationCenter.notificationKey.add(observer: self, selector: #selector(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload) NotificationCenter.notificationKey.add(observer: self, selector: #selector(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload)
//
NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_ :)), notificationName: .net_switch_reachable)
//
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 { deinit {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
center?.playCommand.removeTarget(self)
center?.pauseCommand.removeTarget(self)
center?.nextTrackCommand.removeTarget(self)
center?.previousTrackCommand.removeTarget(self)
} }
/// ///
/// - Parameters: /// - Parameters:
@ -133,35 +189,62 @@ class MP_PlayerManager:NSObject{
if startAction != nil { if startAction != nil {
startActionBlock = startAction startActionBlock = startAction
} }
//
let newItem = loadPlayer.currentVideo.resourcePlayerItem
//playerItem //playerItem
player.replaceCurrentItem(with: loadPlayer.currentVideo.resourcePlayerItem) player.replaceCurrentItem(with: newItem)
//0 if center == nil {
player.seek(to: .zero) setCommandCenter()
// }
let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1)) //
//线 startTimer()
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)
}
}
}
})
//PlayerItem //PlayerItem
// if loadPlayer.currentVideo?.isKVO == false {
loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil) //
// newItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil)
loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", 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 //KVO
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else { guard let keyPath = keyPath else {
@ -175,48 +258,58 @@ class MP_PlayerManager:NSObject{
if playState != .Playing { if playState != .Playing {
//statuVlaueplayerItem //statuVlaueplayerItem
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放") 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() player.play()
playState = .Playing 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 { if startActionBlock != nil {
startActionBlock!() startActionBlock!()
} }
} }
}else { }else {
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")") //
// player.pause()
loadPlayer.remakeImproveData { playState = .Null
[weak self] in
guard let self = self else {return}
//
play()
}
} }
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: default:
break 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: - //MARK: -
/// ///
private func getMusicDuration() -> TimeInterval { private func getMusicDuration() -> TimeInterval {
@ -325,7 +418,7 @@ class MP_PlayerManager:NSObject{
switch playType { switch playType {
case .random:// case .random://
for (index, item) in loadPlayer.randomVideos.enumerated() { for (index, item) in loadPlayer.randomVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index - 1 nextIndex = index - 1
} }
@ -334,15 +427,15 @@ class MP_PlayerManager:NSObject{
if nextIndex < 0 { if nextIndex < 0 {
// //
let last = loadPlayer.randomVideos.last let last = loadPlayer.randomVideos.last
loadPlayer.improveData(last?.videoId ?? "") loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
}else { }else {
// //
let song = loadPlayer.randomVideos[nextIndex] let song = loadPlayer.randomVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "") loadPlayer.improveData(song.videoId ?? "", isRandom: true)
} }
default:// default://
for (index, item) in loadPlayer.songVideos.enumerated() { for (index, item) in loadPlayer.songVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index - 1 nextIndex = index - 1
} }
@ -367,7 +460,7 @@ class MP_PlayerManager:NSObject{
switch playType { switch playType {
case .random: case .random:
for (index, item) in loadPlayer.randomVideos.enumerated() { for (index, item) in loadPlayer.randomVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index + 1 nextIndex = index + 1
} }
@ -376,15 +469,15 @@ class MP_PlayerManager:NSObject{
if nextIndex > (loadPlayer.randomVideos.count-1) { if nextIndex > (loadPlayer.randomVideos.count-1) {
// //
let first = loadPlayer.randomVideos.first let first = loadPlayer.randomVideos.first
loadPlayer.improveData(first?.videoId ?? "") loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
}else { }else {
//,ID //,ID
let song = loadPlayer.randomVideos[nextIndex] let song = loadPlayer.randomVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "") loadPlayer.improveData(song.videoId ?? "", isRandom: true)
} }
default: default:
for (index, item) in loadPlayer.songVideos.enumerated() { for (index, item) in loadPlayer.songVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo.song.videoId { if item.videoId == loadPlayer.currentVideo?.song.videoId {
// //
nextIndex = index + 1 nextIndex = index + 1
} }
@ -409,10 +502,16 @@ class MP_PlayerManager:NSObject{
player.pause() player.pause()
// //
if let video = sender.object as? MPPositive_SongViewModel { if let video = sender.object as? MPPositive_SongViewModel {
//KVO if video.isKVO == true {
video.resourcePlayerItem.removeObserver(self, forKeyPath: "status") //KVO
video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges") video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
// video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") 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 { if loadPlayer.currentVideo != nil {
// //
@ -450,4 +549,88 @@ class MP_PlayerManager:NSObject{
endAction!() 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
}
}
}
}
} }

View File

@ -58,7 +58,7 @@ class MP_WebWork:NSObject {
self.jsPath = self.jsPath + matchedString self.jsPath = self.jsPath + matchedString
print("Current base.JavaScript path:\(self.jsPath)") print("Current base.JavaScript path:\(self.jsPath)")
// //
MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists() // MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists()
// //
if let baseUrl = URL(string: self.jsPath) { if let baseUrl = URL(string: self.jsPath) {
let request = URLRequest(url: baseUrl) let request = URLRequest(url: baseUrl)
@ -177,7 +177,7 @@ extension MP_WebWork: WKNavigationDelegate, WKUIDelegate {
}else { }else {
print("注入代码完成") print("注入代码完成")
// //
NotificationCenter.notificationKey.post(notificationName: .js_edit_completion) // NotificationCenter.notificationKey.post(notificationName: .js_edit_completion)
} }
} }
} }

View File

@ -1,6 +1,38 @@
import Foundation 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 { struct JsonBrowses: Codable {
///访 ///访

View File

@ -18,7 +18,7 @@ class MPPositive_DownloadItemModel: NSManagedObject, MP_CoreDataManageableDelega
/// ///
@NSManaged var title:String? @NSManaged var title:String?
///// /////
@NSManaged var longBylineText:String? @NSManaged var longBylineText:String?
///() ///()
@NSManaged var lengthText:String? @NSManaged var lengthText:String?
/// ///

View File

@ -32,6 +32,10 @@ class MPPositive_SongViewModel: NSObject {
var isCollection:Bool? var isCollection:Bool?
/// ///
var isDlownd:Bool? var isDlownd:Bool?
///KVO
var isKVO:Bool = false
///
var isPreload:Bool = false
/// ///
var song:MPPositive_SongItemModel! var song:MPPositive_SongItemModel!
init(_ song:MPPositive_SongItemModel) { init(_ song:MPPositive_SongItemModel) {
@ -43,12 +47,20 @@ class MPPositive_SongViewModel: NSObject {
resourcePlayerItem = nil resourcePlayerItem = nil
resourcePlayerAsset = nil resourcePlayerAsset = nil
resourcePlayerURL = nil resourcePlayerURL = nil
isKVO = false
} }
// //
func configure() { func configure() {
reloadCollectionAndDownLoad() reloadCollectionAndDownLoad()
index = song.index index = song.index
//
if song.title != nil {
title = song.title!
}
//(/)
if song.shortBylineText != nil {
subtitle = song.shortBylineText!
}
if let first = song.resourceUrls?.first { if let first = song.resourceUrls?.first {
resourcePlayerURL = .init(string:first) resourcePlayerURL = .init(string:first)
resourcePlayerAsset = .init(url: resourcePlayerURL) resourcePlayerAsset = .init(url: resourcePlayerURL)
@ -60,14 +72,6 @@ class MPPositive_SongViewModel: NSObject {
if song.reviewUrls?.first != nil { if song.reviewUrls?.first != nil {
coverUrl = .init(string: song.reviewUrls!.last!) coverUrl = .init(string: song.reviewUrls!.last!)
} }
//
if song.title != nil {
title = song.title!
}
//(/)
if song.shortBylineText != nil {
subtitle = song.shortBylineText!
}
// //
if song.lyrics != nil { if song.lyrics != nil {
lyrics = song.lyrics lyrics = song.lyrics
@ -95,6 +99,10 @@ class MPPositive_SongViewModel: NSObject {
} }
// //
func preloadAsset(_ asset:AVURLAsset) { func preloadAsset(_ asset:AVURLAsset) {
guard isPreload == false else {
return
}
print("\(title ?? "")开始预加载")
// //
if #available(iOS 16, *) { if #available(iOS 16, *) {
//ios16 //ios16
@ -103,11 +111,13 @@ class MPPositive_SongViewModel: NSObject {
let playable = try await asset.load(.isPlayable) let playable = try await asset.load(.isPlayable)
if playable == true { if playable == true {
print("\(self.title ?? "")预加载成功") print("\(self.title ?? "")预加载成功")
isPreload = true
}else { }else {
// //
switch asset.status(of: .isPlayable) { switch asset.status(of: .isPlayable) {
case .failed(let erro): case .failed(let erro):
print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)") print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)")
preloadAsset(asset)
default: default:
break break
} }
@ -130,9 +140,11 @@ class MPPositive_SongViewModel: NSObject {
// key // key
DispatchQueue.main.async { DispatchQueue.main.async {
print("\(self.title ?? "")预加载成功") print("\(self.title ?? "")预加载成功")
self.isPreload = true
} }
case .failed: case .failed:
print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")") print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")")
preloadAsset(asset)
case .cancelled: case .cancelled:
print("\(title ?? "")预加载被取消了") print("\(title ?? "")预加载被取消了")
default: default:

View File

@ -23,6 +23,10 @@ class MPPositive_BrowseLoadViewModel: NSObject {
browseModuleLists = browseModuleLists.filter{($0.items.count != 0)} browseModuleLists = browseModuleLists.filter{($0.items.count != 0)}
//UI //UI
NotificationCenter.notificationKey.post(notificationName: .positive_browses_reload) NotificationCenter.notificationKey.post(notificationName: .positive_browses_reload)
if isCompleted == true {
//
MP_AnalyticsManager.shared.home_b_module_showsucces_actionAction()
}
} }
} }
/// ///

View File

@ -15,13 +15,18 @@ class MPPositive_PlayerLoadViewModel: NSObject {
///ViewModel ///ViewModel
var currentVideo:MPPositive_SongViewModel!{ var currentVideo:MPPositive_SongViewModel!{
willSet{ willSet{
if newValue != nil { DispatchQueue.main.asyncAfter(deadline: .now()) {
if currentVideo != nil { [weak self] in
//UI guard let self = self else {return}
NotificationCenter.notificationKey.post(notificationName: .positive_player_reload, object: currentVideo) if newValue != nil {
}else { MP_AnalyticsManager.shared.player_b_pvAction(newValue.song.videoId, videoname: newValue.title ?? "", artistname: newValue.song.shortBylineText ?? "")
//UI if currentVideo != nil {
NotificationCenter.notificationKey.post(notificationName: .positive_player_reload) //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 = [] self.listViewVideos = []
} }
///Video13VideoViewModel, ///Video14VideoViewModel,
func improveData(_ targetVideoId:String, isRandom:Bool = false) { func improveData(_ targetVideoId:String, isRandom:Bool = false) {
//Video //Video
var array:[MPPositive_SongItemModel] = [] var array:[MPPositive_SongItemModel] = []
@ -59,9 +64,13 @@ class MPPositive_PlayerLoadViewModel: NSObject {
array.append(self.randomVideos[previousIndex]) array.append(self.randomVideos[previousIndex])
} }
let nextIndex = targetIndex+1 let nextIndex = targetIndex+1
let lastIndex = targetIndex+2
if nextIndex < randomVideos.count { if nextIndex < randomVideos.count {
array.append(self.randomVideos[nextIndex]) array.append(self.randomVideos[nextIndex])
} }
if lastIndex < randomVideos.count {
array.append(self.randomVideos[lastIndex])
}
}else { }else {
//targetVideoId //targetVideoId
guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else { guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else {
@ -74,9 +83,13 @@ class MPPositive_PlayerLoadViewModel: NSObject {
array.append(self.songVideos[previousIndex]) array.append(self.songVideos[previousIndex])
} }
let nextIndex = targetIndex+1 let nextIndex = targetIndex+1
let lastIndex = targetIndex+2
if nextIndex < songVideos.count { if nextIndex < songVideos.count {
array.append(self.songVideos[nextIndex]) array.append(self.songVideos[nextIndex])
} }
if lastIndex < songVideos.count {
array.append(self.songVideos[lastIndex])
}
} }
//ViewModelvideo //ViewModelvideo
let videoIDs = Set(listViewVideos.map({$0.song.videoId})) let videoIDs = Set(listViewVideos.map({$0.song.videoId}))
@ -123,8 +136,8 @@ class MPPositive_PlayerLoadViewModel: NSObject {
[weak self] in [weak self] in
// //
self?.currentVideo = self?.listViewVideos.first(where: {$0.song.videoId == targetVideoId}) 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 self?.group = nil
}) })
} }
@ -180,19 +193,4 @@ class MPPositive_PlayerLoadViewModel: NSObject {
private func findVideoIdForDocument(_ videoId:String) -> Bool { private func findVideoIdForDocument(_ videoId:String) -> Bool {
return MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", videoId)).count != 0 return MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", videoId)).count != 0
} }
///nextIDID
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)
}
}
} }

View File

@ -29,6 +29,7 @@ class MPPositive_SearchResultsLoadViewModel: NSObject {
} }
// //
private func getSearchResults(_ text:String) { private func getSearchResults(_ text:String) {
MP_HUD.loading()
// //
let tag = MPPositive_SearchTagModel.create() let tag = MPPositive_SearchTagModel.create()
tag.date = Date().timeZone() tag.date = Date().timeZone()

View File

@ -17,12 +17,30 @@ class MPPositive_BaseViewController: MP_BaseViewController {
btn.addTarget(self, action: #selector(popActionClick(_ :)), for: .touchUpInside) btn.addTarget(self, action: #selector(popActionClick(_ :)), for: .touchUpInside)
return btn return btn
}() }()
//View
private lazy var errorView:UIView = createErrorView()
///
var errorBlock:(() -> Void)?
///
var retryBlock:(() -> Void)?
//View
lazy var navView:UIView = setTitleView() lazy var navView:UIView = setTitleView()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.addSubview(navView) 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 //titleView
private func setTitleView() -> UIView { private func setTitleView() -> UIView {
let topView = UIView(frame: .init(x: 0, y: statusBarHeight, width: screen_Width, height: 50*width)) 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) { @objc private func popActionClick(_ sender:UIButton) {
navigationController?.popViewController(animated: true) 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!()
}
}
} }

View File

@ -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) let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain)
tableView.backgroundColor = .clear tableView.backgroundColor = .clear
tableView.separatorStyle = .none tableView.separatorStyle = .none
tableView.contentInset = .init(top: 0, left: 0, bottom: bottomPadding, right: 0)
tableView.estimatedRowHeight = 200 tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension tableView.rowHeight = UITableView.automaticDimension
tableView.dataSource = self tableView.dataSource = self
tableView.delegate = self tableView.delegate = self
tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationTableViewCellID) tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID)
return tableView return tableView
}() }()
private let MPPositive_MoreOperationTableViewCellID = "MPPositive_MoreOperationTableViewCell" private let MPPositive_MoreOperationDownLoadTableViewCellID = "MPPositive_MoreOperationDownLoadTableViewCell"
private var song:MPPositive_SongItemModel!{ private var song:MPPositive_SongItemModel!{
didSet{ didSet{
iconImageView.kf.setImage(with: URL(string: song.reviewUrls?.last ?? ""), placeholder: placeholderImage) 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) { init(_ song:MPPositive_SongItemModel) {
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
self.song = song DispatchQueue.main.async {
[weak self] in
self?.song = song
}
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -87,7 +90,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = .init(hex: "#282A2C") view.backgroundColor = .init(hex: "#282A2C")
view.layer.masksToBounds = true view.layer.masksToBounds = true
view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMinXMaxYCorner] view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]
view.layer.cornerRadius = 18*width view.layer.cornerRadius = 18*width
configure() configure()
} }
@ -146,6 +149,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
} }
} }
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
MP_AnalyticsManager.shared.player_b_unlove_clickAction(song.videoId, videoname: song.title ?? "", artistname: song.shortBylineText ?? "")
}else{ }else{
self.collectionBtn.isSelected = true self.collectionBtn.isSelected = true
let item = MPPositive_CollectionSongModel.create() let item = MPPositive_CollectionSongModel.create()
@ -157,6 +161,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
item.relatedID = song.relatedID item.relatedID = song.relatedID
MPPositive_CollectionSongModel.save() MPPositive_CollectionSongModel.save()
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) 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 return 1
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 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.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 return cell
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -180,16 +191,95 @@ extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UIT
} }
} }
MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil) 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} guard let self = self else {return}
MP_HUD.progress("Loading...", delay: 0.5) { MP_HUD.progress("Loading...", delay: 0.5) {
self.isLoaded = false self.isLoaded = false
MP_HUD.text("Removed", delay: 1.0, completion: nil) MP_HUD.text("Removed", delay: 1.0, completion: nil)
if self.removeBlock != nil {
self.removeBlock!()
}
} }
} }
}else { }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
}
} }
} }
} }

View File

@ -66,6 +66,10 @@ class MPPositive_LibraryViewController: MPPositive_BaseViewController {
super.viewWillAppear(animated) super.viewWillAppear(animated)
reload() reload()
} }
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
MP_AnalyticsManager.shared.me_b_pvAction()
}
// //
private func reload() { private func reload() {
MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels { MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels {

View File

@ -7,7 +7,7 @@
import UIKit 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) private lazy var numbersLabel:UILabel = createLabel(font: .systemFont(ofSize: 18*width, weight: .regular), textColor: .white, textAlignment: .left)
///tableView ///tableView
private lazy var tableView:UITableView = { private lazy var tableView:UITableView = {
@ -67,6 +67,39 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
cell.songViewModel = MPPositive_LoadCoreModel.shared.songViewModels[indexPath.row] 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 return cell
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -93,4 +126,7 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
} }
} }
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
}
} }

View File

@ -60,13 +60,39 @@ class MPPositive_OfflineSongsViewController: MPPositive_BaseViewController {
} }
} }
//MARK: - tableView //MARK: - tableView
extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableViewDelegate { extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableViewDelegate, UIViewControllerTransitioningDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return MPPositive_LoadCoreModel.shared.loadViewModels.count return MPPositive_LoadCoreModel.shared.loadViewModels.count
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
cell.loadViewModel = MPPositive_LoadCoreModel.shared.loadViewModels[indexPath.row] 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 return cell
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -96,4 +122,7 @@ extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableV
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
} }
} }
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
}
} }

View File

@ -7,7 +7,7 @@
import UIKit 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)) private lazy var headView:MPPositive_ArtistShowHeaderView = .init(frame: .init(x: 0, y: 0, width: screen_Width, height: 385*width))
///Label ///Label
@ -61,17 +61,24 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController {
}() }()
private var artist:MPPositive_ArtistViewModel!{ private var artist:MPPositive_ArtistViewModel!{
didSet{ didSet{
if artist != nil { DispatchQueue.main.async {
pagingView.isHidden = false [weak self] in
// guard let self = self else {return}
headView.artistid = self.browseid if artist != nil {
headView.artist = artist pagingView.isHidden = false
//
// headView.artistid = self.browseid
dataSource.titles = artist.lists.compactMap({$0.title}) headView.artist = artist
nameLabel.text = artist.header?.title
dataSource.reloadData(selectedIndex: 0) //
segmentView.reloadData() 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) { init(_ browseId:String) {
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
browseid = browseId browseid = browseId
MP_HUD.loading()
// //
MP_NetWorkManager.shared.requestArtist(browseId) { [weak self] result in MP_NetWorkManager.shared.requestArtist(browseId) { [weak self] result in
DispatchQueue.main.async { guard let self = self else {return}
[weak self] in artist = result
guard let self = self else {return}
artist = result
}
} }
} }
@ -100,7 +105,33 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController {
super.viewDidLoad() super.viewDidLoad()
setPopBtn() setPopBtn()
setTitle("") 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() { private func configure() {
@ -174,7 +205,7 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{
switch item.browseItem.itemType { switch item.browseItem.itemType {
case .artist: case .artist:
// //
let artistVC = MPPositive_ArtistShowViewController(item.browseItem.browseId ?? "") let artistVC = MPPositive_ArtistShowViewController(item.browseItem.artistId ?? "")
navigationController?.pushViewController(artistVC, animated: true) navigationController?.pushViewController(artistVC, animated: true)
case .list: case .list:
// //
@ -199,6 +230,42 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{
break 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 return showView
} }
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
}
} }

View File

@ -38,13 +38,51 @@ class MPPositive_HomeViewController: MPPositive_BaseViewController{
// private var loadViewModel:MPPositive_BrowseLoadViewModel! // private var loadViewModel:MPPositive_BrowseLoadViewModel!
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
setTitle("Musicoo") setTitle("Musiclax")
confirgue() confirgue()
//
MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists()
NotificationCenter.notificationKey.add(observer: self, selector: #selector(reloadAction(_ :)), notificationName: .positive_browses_reload) 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 { deinit {
NotificationCenter.default.removeObserver(self) 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 //MARK: - UI
// //
private func confirgue() { private func confirgue() {
@ -71,13 +109,20 @@ class MPPositive_HomeViewController: MPPositive_BaseViewController{
@objc private func reloadAction(_ sender:Notification) { @objc private func reloadAction(_ sender:Notification) {
DispatchQueue.main.async { DispatchQueue.main.async {
[weak self] in [weak self] in
self?.tableView.reloadData() guard let self = self else {return}
if tableView.superview == nil {
confirgue()
}
removeErrorView()
MP_HUD.hideNow()
tableView.reloadData()
} }
} }
//MARK: - //MARK: -
// //
@objc private func menuRightClick(_ sender:UIButton) { @objc private func menuRightClick(_ sender:UIButton) {
let setVC = MPSideA_SettingViewController()
navigationController?.pushViewController(setVC, animated: true)
} }
} }
//MARK: - tableView //MARK: - tableView
@ -105,6 +150,7 @@ extension MPPositive_HomeViewController: UITableViewDataSource, UITableViewDeleg
cell.requestNextBlock = { cell.requestNextBlock = {
[weak self] (item) in [weak self] (item) in
guard let self = self else {return} guard let self = self else {return}
MP_AnalyticsManager.shared.home_b_module_clickAction(MPPositive_BrowseLoadViewModel.shared.browseModuleLists[indexPath.section].title)
switch item.browseItem.itemType { switch item.browseItem.itemType {
case .single: case .single:
/// ///

View File

@ -7,7 +7,7 @@
import UIKit import UIKit
///b ///b
class MPPositive_ListShowViewController: MPPositive_BaseViewController { class MPPositive_ListShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate {
// //
private lazy var coverImageView:UIImageView = { private lazy var coverImageView:UIImageView = {
let imageView:UIImageView = .init() let imageView:UIImageView = .init()
@ -31,8 +31,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
// //
private lazy var playListBtn:UIButton = { private lazy var playListBtn:UIButton = {
let btn = UIButton() let btn = UIButton()
btn.setBackgroundImage(UIImage(named: "List_UnPlay'logo"), for: .normal) btn.setBackgroundImage(UIImage(named: "List_ShufflePlay'logo"), for: .normal)
btn.setBackgroundImage(UIImage(named: "List_Played'logo"), for: .selected)
btn.isUserInteractionEnabled = false btn.isUserInteractionEnabled = false
return btn return btn
}() }()
@ -46,14 +45,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
btn.addTarget(self, action: #selector(collectionSwitchClick(_ :)), for: .touchUpInside) btn.addTarget(self, action: #selector(collectionSwitchClick(_ :)), for: .touchUpInside)
return btn 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/ //tableView/
private lazy var tableView:UITableView = { private lazy var tableView:UITableView = {
let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain) 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 [weak self] in
guard let self = self else {return} guard let self = self else {return}
if listOrAlbum != nil { if listOrAlbum != nil {
configure()
reload() reload()
tableView.reloadData() tableView.reloadData()
removeErrorView()
MP_HUD.hideNow()
} }
} }
} }
@ -95,12 +90,12 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
self.params = params self.params = params
self.centerTtitle = title self.centerTtitle = title
self.subtitle = subtitle self.subtitle = subtitle
MP_HUD.loading()
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
// //
MP_NetWorkManager.shared.requestAlbumOrListDatas(browseId, params: params) { [weak self] result in MP_NetWorkManager.shared.requestAlbumOrListDatas(browseId, params: params) { [weak self] result in
guard let self = self else {return} guard let self = self else {return}
listOrAlbum = result listOrAlbum = result
} }
} }
@ -111,7 +106,33 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
super.viewDidLoad() super.viewDidLoad()
setTitle("") setTitle("")
setPopBtn() 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) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
@ -128,7 +149,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
listOrAlbum.header?.setUrltoImage(coverImageView) listOrAlbum.header?.setUrltoImage(coverImageView)
titleLabel.text = listOrAlbum.header?.title titleLabel.text = listOrAlbum.header?.title
descriptionLabel.text = listOrAlbum.header?._description descriptionLabel.text = listOrAlbum.header?._description
playListNumberLabel.text = "Play all (\(listOrAlbum.items.count))" playListNumberLabel.text = "Play (\(listOrAlbum.items.count))"
} }
//MARK: - UI //MARK: - UI
// //
@ -173,16 +194,10 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
make.height.equalTo(32*width) make.height.equalTo(32*width)
make.bottom.equalTo(blurView.snp.bottom).offset(-29*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) view.addSubview(collectionListBtn)
collectionListBtn.snp.makeConstraints { make in collectionListBtn.snp.makeConstraints { make in
make.width.height.equalTo(24*width) make.width.height.equalTo(24*width)
make.right.equalToSuperview().offset(-56*width) make.right.equalToSuperview().offset(-18*width)
make.centerY.equalTo(playListView.snp.centerY) make.centerY.equalTo(playListView.snp.centerY)
} }
view.addSubview(tableView) view.addSubview(tableView)
@ -235,7 +250,20 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
//MARK: - //MARK: -
/// ///
@objc private func playListActionClick(_ sender:UITapGestureRecognizer) { @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) { @objc private func collectionSwitchClick(_ sender:UIButton) {
@ -262,10 +290,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels(nil) MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels(nil)
} }
} }
//
@objc private func collectionStatuClick(_ sender:UIButton) {
}
} }
//MARK: - tableView //MARK: - tableView
extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewDelegate { extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewDelegate {
@ -275,6 +300,39 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MusicItemShowTableViewCellID, for: indexPath) as! MPPositive_MusicItemShowTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MusicItemShowTableViewCellID, for: indexPath) as! MPPositive_MusicItemShowTableViewCell
cell.itemView = listOrAlbum.items[indexPath.row] 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 return cell
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 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)
}
} }

View File

@ -95,4 +95,28 @@ extension MPPositive_MoreContentViewController: UICollectionViewDataSource, UICo
return cell 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)
}
}
} }

View File

@ -123,25 +123,29 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
// //
MP_PlayerManager.shared.runActionBlock = { [weak self] (currentTime, duration) in MP_PlayerManager.shared.runActionBlock = { [weak self] (currentTime, duration) in
guard let self = self else { return } guard let self = self else { return }
// DispatchQueue.main.async {
coverView.durationLabel.text = setTimesToMinSeconds(currentTime) //
// self.coverView.durationLabel.text = setTimesToMinSeconds(currentTime)
let remain:TimeInterval = duration - currentTime //
coverView.maxTimesLabel.text = setTimesToMinSeconds(remain) let remain:TimeInterval = duration - currentTime
// self.coverView.maxTimesLabel.text = setTimesToMinSeconds(remain)
let value = currentTime/duration //
coverView.sliderView.value = Float(value) let value = currentTime/duration
self.coverView.sliderView.value = Float(value)
}
} }
// //
MP_PlayerManager.shared.cacheValueBlock = { [weak self] (value, duration) in MP_PlayerManager.shared.cacheValueBlock = { [weak self] (value, duration) in
guard let self = self else { return } guard let self = self else { return }
if value < duration { DispatchQueue.main.async {
// if value < duration {
let float = value/duration //
coverView.progressView.setProgress(Float(float), animated: false) let float = value/duration
}else { self.coverView.progressView.setProgress(Float(float), animated: false)
// }else {
coverView.progressView.setProgress(1, animated: false) //
self.coverView.progressView.setProgress(1, animated: false)
}
} }
} }
switch MP_PlayerManager.shared.getPlayState() { 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.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title
lyricsView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle lyricsView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle
lyricsView.lyricsLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics?.isEmpty == true ? "No Lyrics":MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics lyricsView.lyricsLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics?.isEmpty == true ? "No Lyrics":MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics
coverView.loadBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false coverView.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.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false
coverView.restoreDownloadProgress() coverView.restoreDownloadProgress()
} }

View File

@ -7,28 +7,35 @@
import UIKit import UIKit
/// ///
class MPPositive_RecommendViewController: MPPositive_BaseViewController { class MPPositive_RecommendViewController: MPPositive_BaseViewController,UIViewControllerTransitioningDelegate {
//load //load
private var loadRecommend:MPPositive_RecommendLoadViewModel!{ private var loadRecommend:MPPositive_RecommendLoadViewModel!{
didSet{ didSet{
if loadRecommend != nil { DispatchQueue.main.async {
membersView.isHidden = false [weak self] in
segmentView.isHidden = false guard let self = self else {return}
listContainerView.isHidden = false if loadRecommend != nil {
loadRecommend.resultReloadBlock = { membersView.isHidden = false
[weak self] in segmentView.isHidden = false
// listContainerView.isHidden = false
guard let self = self else {return} loadRecommend.resultReloadBlock = {
sectionLabel.text = loadRecommend.members.title [weak self] in
collectionView.reloadData() //
dataSource.titles = loadRecommend?.sectionLists.compactMap({$0.title}) ?? [] guard let self = self else {return}
dataSource.reloadData(selectedIndex: 0) sectionLabel.text = loadRecommend.members.title
segmentView.reloadData() 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 listContainerView.backgroundColor = .clear
return listContainerView return listContainerView
}() }()
private var browseId:String!
/// ///
/// - Parameter browseId: id /// - Parameter browseId: id
init(_ browseId:String) { init(_ browseId:String) {
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
self.browseId = browseId
MP_HUD.loading()
DispatchQueue.main.async { DispatchQueue.main.async {
[weak self] in [weak self] in
self?.loadRecommend = .init(browseId) self?.loadRecommend = .init(browseId)
@ -111,7 +121,32 @@ class MPPositive_RecommendViewController: MPPositive_BaseViewController {
super.viewDidLoad() super.viewDidLoad()
setTitle("Recommend") setTitle("Recommend")
setPopBtn() 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() { private func configure() {
view.addSubview(membersView) view.addSubview(membersView)
@ -208,6 +243,42 @@ extension MPPositive_RecommendViewController: JXSegmentedListContainerViewDataSo
break 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 return showView
} }
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
}
} }

View File

@ -7,7 +7,7 @@
import UIKit import UIKit
/// ///
class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController { class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate {
//MARK: - View //MARK: - View
//textField //textField
private lazy var searchTextField:UITextField = { private lazy var searchTextField:UITextField = {
@ -36,6 +36,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
btn.addTarget(self, action: #selector(backPopClick(_ :)), for: .touchUpInside) btn.addTarget(self, action: #selector(backPopClick(_ :)), for: .touchUpInside)
return btn return btn
}() }()
// //
private var debounceTimer: Timer? private var debounceTimer: Timer?
// //
@ -48,6 +49,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
// //
suggestionView.isHidden = false suggestionView.isHidden = false
suggestionView.suggestions = suggestionList.attributedTexts suggestionView.suggestions = suggestionList.attributedTexts
MP_AnalyticsManager.shared.search_sug_showAction()
}else { }else {
suggestionView.isHidden = true suggestionView.isHidden = true
suggestionView.suggestions = nil suggestionView.suggestions = nil
@ -70,6 +72,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
searchTextField.text = text searchTextField.text = text
searchText = text searchText = text
resultsShowView.loadModel = .init(text) resultsShowView.loadModel = .init(text)
MP_AnalyticsManager.shared.search_sug_clickAction(text)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -87,6 +90,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
searchText = text searchText = text
searchTextField.text = text searchTextField.text = text
resultsShowView.loadModel = .init(text) resultsShowView.loadModel = .init(text)
MP_AnalyticsManager.shared.search_sug_clickAction(text)
suggestionView.isHidden = true suggestionView.isHidden = true
} }
resultsShowView.scrollBlock = { resultsShowView.scrollBlock = {
@ -124,10 +128,52 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
break 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{ deinit{
debounceTimer = nil debounceTimer = nil
} }
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
}
private func configure() { private func configure() {
// //
let searchView = createSearchView() let searchView = createSearchView()
@ -246,6 +292,7 @@ extension MPPositive_SearchResultShowViewController:UITextFieldDelegate {
self.searchText = text self.searchText = text
// //
resultsShowView.loadModel = .init(text) resultsShowView.loadModel = .init(text)
MP_AnalyticsManager.shared.search_sug_clickAction(text)
suggestionView.isHidden = true suggestionView.isHidden = true
// //
view.endEditing(true) view.endEditing(true)

View File

@ -48,6 +48,7 @@ class MPPositive_SearchViewController: MPPositive_BaseViewController {
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
MP_AnalyticsManager.shared.search_pvAction()
} }
// //
private func configure() { private func configure() {

View File

@ -8,30 +8,41 @@
import UIKit import UIKit
import DownloadButton import DownloadButton
class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell { 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() let btn:PKDownloadButton = .init()
//
btn.isUserInteractionEnabled = false 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 return btn
}() }()
private lazy var titleLabel:UILabel = createLabel(font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left) private lazy var titleLabel:UILabel = createLabel(font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left)
var title:String!{ var title:String!{
didSet{ didSet{
iconImageView.image = UIImage(named: title)
titleLabel.text = title titleLabel.text = title
} }
} }
var progressBlock:(() -> Void)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none selectionStyle = .none
@ -53,8 +64,8 @@ class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell {
// Configure the view for the selected state // Configure the view for the selected state
} }
private func configure() { private func configure() {
contentView.addSubview(iconImageView) contentView.addSubview(loadBtn)
iconImageView.snp.makeConstraints { make in loadBtn.snp.makeConstraints { make in
make.width.height.equalTo(24*width) make.width.height.equalTo(24*width)
make.top.equalToSuperview().offset(12*width).priority(999) make.top.equalToSuperview().offset(12*width).priority(999)
make.bottom.equalToSuperview().offset(-12*width) make.bottom.equalToSuperview().offset(-12*width)
@ -63,7 +74,36 @@ class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell {
contentView.addSubview(titleLabel) contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview() 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
}
} }
} }
} }

View File

@ -24,14 +24,14 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell {
btn.addTarget(self, action: #selector(moreActionClick(_ :)), for: .touchUpInside) btn.addTarget(self, action: #selector(moreActionClick(_ :)), for: .touchUpInside)
return btn return btn
}() }()
/// // ///
private lazy var loadBtn:UIButton = { // private lazy var loadBtn:UIButton = {
let btn:UIButton = .init() // let btn:UIButton = .init()
btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) // btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) // btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside) // btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
return btn // return btn
}() // }()
var itemView:MPPositive_BrowseItemViewModel!{ var itemView:MPPositive_BrowseItemViewModel!{
didSet{ didSet{
itemView.setUrltoImage(iconImageView) itemView.setUrltoImage(iconImageView)
@ -74,17 +74,17 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell {
make.centerY.equalTo(iconImageView.snp.centerY) make.centerY.equalTo(iconImageView.snp.centerY)
make.right.equalToSuperview().offset(-18*width) make.right.equalToSuperview().offset(-18*width)
} }
contentView.addSubview(loadBtn) // contentView.addSubview(loadBtn)
loadBtn.snp.makeConstraints { make in // loadBtn.snp.makeConstraints { make in
make.width.height.equalTo(24*width) // make.width.height.equalTo(24*width)
make.centerY.equalTo(iconImageView.snp.centerY) // make.centerY.equalTo(iconImageView.snp.centerY)
make.right.equalToSuperview().offset(-54*width) // make.right.equalToSuperview().offset(-54*width)
} // }
contentView.addSubview(titleLabel) contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
make.top.equalTo(iconImageView.snp.top).offset(10*width) make.top.equalTo(iconImageView.snp.top).offset(10*width)
make.left.equalTo(iconImageView.snp.right).offset(12*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) contentView.addSubview(subtitleLabel)
subtitleLabel.snp.makeConstraints { make in subtitleLabel.snp.makeConstraints { make in
@ -93,10 +93,13 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell {
make.right.equalTo(titleLabel.snp.right) make.right.equalTo(titleLabel.snp.right)
} }
} }
var moreBlock:(() -> Void)?
// //
@objc private func moreActionClick(_ sender:UIButton) { @objc private func moreActionClick(_ sender:UIButton) {
guard moreBlock != nil else {
return
}
moreBlock!()
} }
// //
@objc private func loadActionClick(_ sender:UIButton) { @objc private func loadActionClick(_ sender:UIButton) {

View File

@ -66,6 +66,7 @@ class MPPositive_ArtistShowTypeView: UIView, JXPagingViewListViewDelegate {
fileprivate var listViewDidScrollCallback: ((UIScrollView) -> ())? fileprivate var listViewDidScrollCallback: ((UIScrollView) -> ())?
// //
var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)? var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)?
var moreBlock:((MPPositive_BrowseItemViewModel) -> Void)?
init(frame: CGRect, list:MPPositive_ArtistContentListViewModel) { init(frame: CGRect, list:MPPositive_ArtistContentListViewModel) {
super.init(frame: frame) super.init(frame: frame)
backgroundColor = .init(hex: "#151718") backgroundColor = .init(hex: "#151718")
@ -133,6 +134,15 @@ extension MPPositive_ArtistShowTypeView:UITableViewDataSource, UITableViewDelega
case .single: case .single:
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell
cell.itemView = sectionList.itemViews[indexPath.row] 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 return cell
case .list: case .list:
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell

View File

@ -41,10 +41,10 @@ class MPPositive_MusicItemShowTableViewCell: UITableViewCell {
itemView.setUrltoImage(coverImageView) itemView.setUrltoImage(coverImageView)
titleLabel.text = itemView.title titleLabel.text = itemView.title
subtitleLabel.text = itemView.subtitle subtitleLabel.text = itemView.subtitle
//
} }
} }
var moreBlock:(() -> Void)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none selectionStyle = .none
@ -101,7 +101,10 @@ class MPPositive_MusicItemShowTableViewCell: UITableViewCell {
} }
// //
@objc private func moreActionClick(_ sender:UIButton) { @objc private func moreActionClick(_ sender:UIButton) {
guard moreBlock != nil else {
return
}
moreBlock!()
} }
// //
@objc private func loadActionClick(_ sender:UIButton) { @objc private func loadActionClick(_ sender:UIButton) {

View File

@ -5,9 +5,9 @@
import UIKit import UIKit
import DownloadButton import DownloadButton
//BView(View) //BView(View)
class MPPositive_PlayerCoverView: UIView { class MPPositive_PlayerCoverView: UIView, PKDownloadButtonDelegate {
//View //View
private var loadView = CircularProgressView() // private var loadView = CircularProgressView()
/// ///
lazy var coverImageView:UIImageView = { lazy var coverImageView:UIImageView = {
@ -30,13 +30,13 @@ class MPPositive_PlayerCoverView: UIView {
return btn return btn
}() }()
/// ///
lazy var loadBtn:UIButton = { // lazy var loadBtn:UIButton = {
let btn:UIButton = .init() // let btn:UIButton = .init()
btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) // btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected) // btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside) // btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
return btn // return btn
}() // }()
/// ///
lazy var downloadButton:PKDownloadButton = { lazy var downloadButton:PKDownloadButton = {
let btn:PKDownloadButton = .init() let btn:PKDownloadButton = .init()
@ -50,10 +50,18 @@ class MPPositive_PlayerCoverView: UIView {
btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal) btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal)
btn.stopDownloadButton.isUserInteractionEnabled = false btn.stopDownloadButton.isUserInteractionEnabled = false
btn.stopDownloadButton.tintColor = UIColor(hex: "#80F988") 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.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 return btn
}() }()
@ -104,55 +112,30 @@ class MPPositive_PlayerCoverView: UIView {
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
// NotificationCenter.default.addObserver(self, selector: #selector(updateProgress(_:)), name: Notification.Name("DownloadProgressUpdated"), object: nil)
//
// //
// restoreDownloadProgress()
} }
//
public func restoreDownloadProgress() { public func restoreDownloadProgress() {
if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, guard let currentVideo = MP_PlayerManager.shared.loadPlayer?.currentVideo else {
let videoURLString = currentVideo.song.resourceUrls?.first, return
let videoURL = URL(string: videoURLString) { }
if let progress = DownloadManager.shared.getProgress(for: videoURL) { //video
//VideoID if let progress = MP_DownloadManager.shared.getProgress(for: currentVideo.song.videoId) {
addCircularProgressBar(over: loadBtn) DispatchQueue.main.async {
loadView.setProgress(to: progress) [weak self] in
self.layoutIfNeeded() guard let self = self else {return}
self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal) //URL
self.loadBtn.setImage(UIImage(named: "download"), for: .normal) downloadButton.state = .downloading
self.addCircularProgressBar(over: self.loadBtn) downloadButton.isUserInteractionEnabled = false
self.loadView.setProgress(to: progress) //
}else { downloadButton.stopDownloadButton.progress = progress
//videoID }
if (loadView.superview) != nil { }else {
loadView.removeFromSuperview() //
} downloadButton.state = .startDownload
self.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal) downloadButton.isUserInteractionEnabled = true
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)
}
}
}
deinit { deinit {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
} }
@ -185,16 +168,16 @@ class MPPositive_PlayerCoverView: UIView {
make.top.equalTo(titleLabel.snp.bottom).offset(6*width) make.top.equalTo(titleLabel.snp.bottom).offset(6*width)
} }
// //
addSubview(loadBtn) addSubview(downloadButton)
loadBtn.snp.makeConstraints { make in downloadButton.snp.makeConstraints { make in
make.right.equalTo(coverImageView.snp.right).offset(-12*width) make.right.equalTo(coverImageView.snp.right).offset(-12*width)
make.top.equalTo(coverImageView.snp.bottom).offset(47*width) make.top.equalTo(coverImageView.snp.bottom).offset(47*width)
make.width.height.equalTo(24*width) make.width.height.equalTo(24*width)
} }
addSubview(collectionSongBtn) addSubview(collectionSongBtn)
collectionSongBtn.snp.makeConstraints { make in collectionSongBtn.snp.makeConstraints { make in
make.right.equalTo(loadBtn.snp.left).offset(-20*width) make.right.equalTo(downloadButton.snp.left).offset(-20*width)
make.centerY.equalTo(loadBtn.snp.centerY) make.centerY.equalTo(downloadButton.snp.centerY)
make.width.height.equalTo(24*width) make.width.height.equalTo(24*width)
} }
addSubview(progressView) addSubview(progressView)
@ -219,8 +202,6 @@ class MPPositive_PlayerCoverView: UIView {
make.right.equalTo(sliderView.snp.right) make.right.equalTo(sliderView.snp.right)
make.top.equalTo(sliderView.snp.bottom).offset(5*width) make.top.equalTo(sliderView.snp.bottom).offset(5*width)
} }
NotificationCenter.default.addObserver(self, selector: #selector(updateProgress(_:)), name: Notification.Name("DownloadProgressUpdated"), object: nil)
// //
restoreDownloadProgress() restoreDownloadProgress()
} }
@ -272,6 +253,7 @@ class MPPositive_PlayerCoverView: UIView {
} }
MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) 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{ }else{
self.collectionSongBtn.isSelected = true self.collectionSongBtn.isSelected = true
@ -286,132 +268,94 @@ class MPPositive_PlayerCoverView: UIView {
MPPositive_CollectionSongModel.save() MPPositive_CollectionSongModel.save()
MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad() MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil) 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 { func downloadButtonTapped(_ downloadButton: PKDownloadButton!, currentState state: PKDownloadButtonState) {
addCircularProgressBar(over: sender) guard MP_PlayerManager.shared.loadPlayer?.currentVideo != nil, MP_PlayerManager.shared.loadPlayer?.currentVideo?.isDlownd == false else {
return
self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal) }
self.loadBtn.setImage(UIImage(named: "download"), for: .normal) //
switch state {
if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, case .startDownload: //
let videoURLString = currentVideo.song.resourceUrls?.first, //
let videoURL = URL(string: videoURLString) { downloadButton.state = .pending
DownloadManager.shared.downloadVideo(from: videoURL, song: MP_PlayerManager.shared.loadPlayer.currentVideo.song, progressHandler: { [weak self] progress in //
DispatchQueue.main.async { downloadButton.isUserInteractionEnabled = false
self?.loadView.setProgress(to: progress) //
NotificationCenter.default.post(name: Notification.Name("DownloadProgressUpdated"), object: nil, userInfo: ["url": videoURL, "progress": progress]) 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 print("完成了对\(song.title ?? "")的下载")
switch result { //
case .success(let song): downloadButton.state = .downloaded
let item = MPPositive_DownloadItemModel.create() downloadButton.isUserInteractionEnabled = false
// //
item.coverImage = song.coverUrls!.last MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
item.reviewImage = song.reviewUrls!.last MP_AnalyticsManager.shared.player_b_downloadsuccess_actionAction(item.videoId, videoname: item.title ?? "", artistname: item.shortBylineText ?? "")
item.title = song.title }
item.longBylineText = song.longBylineText case .failure(let error):
item.lengthText = song.lengthText //
item.shortBylineText = song.shortBylineText print("下载报错,错误详情\(error)")
item.lyrics = song.lyrics DispatchQueue.main.async {
item.lyricsID = song.lyricsID //
item.videoId = song.videoId downloadButton.state = .startDownload
item.relatedID = song.relatedID downloadButton.isUserInteractionEnabled = true
}
MPPositive_DownloadItemModel.save() MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil)
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)
}
})
} }
} }
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)
}
}
} }

View File

@ -35,7 +35,7 @@ class MPPositive_RecommendShowTypeView: UIView, JXSegmentedListContainerViewList
} }
// //
var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)? var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)?
var moreBlock:((MPPositive_BrowseItemViewModel) -> Void)?
init(_ frame:CGRect, list:MPPositive_RecommendListViewModel) { init(_ frame:CGRect, list:MPPositive_RecommendListViewModel) {
super.init(frame: frame) super.init(frame: frame)
backgroundColor = .clear backgroundColor = .clear
@ -82,6 +82,15 @@ extension MPPositive_RecommendShowTypeView:UITableViewDataSource, UITableViewDel
case .single: case .single:
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell
cell.itemView = sectionList.items[indexPath.row] 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 return cell
case .list: case .list:
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell

View File

@ -38,6 +38,7 @@ class MPPositive_SearchResultPreviewShowView: UIView, JXSegmentedListContainerVi
} }
var scrollBlock:(() -> Void)? var scrollBlock:(() -> Void)?
var chooseMoreIndexBlock:((Int) -> Void)? var chooseMoreIndexBlock:((Int) -> Void)?
var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
// //
var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)? var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
init(frame: CGRect, sectionLists:[MPPositive_SearchResultListViewModel]) { init(frame: CGRect, sectionLists:[MPPositive_SearchResultListViewModel]) {
@ -85,6 +86,13 @@ extension MPPositive_SearchResultPreviewShowView:UITableViewDataSource, UITableV
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
cell.itemView = sectionLists[indexPath.section].previewItemViews[indexPath.row] 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 return cell
} }
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

View File

@ -61,7 +61,7 @@ class MPPositive_SearchResultShowTableViewCell: UITableViewCell {
subtitleLabel.text = loadViewModel.subtitle subtitleLabel.text = loadViewModel.subtitle
} }
} }
var moreBlock:(() -> Void)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none selectionStyle = .none
@ -118,7 +118,9 @@ class MPPositive_SearchResultShowTableViewCell: UITableViewCell {
// //
@objc private func moreActionClick(_ sender:UIButton) { @objc private func moreActionClick(_ sender:UIButton) {
if moreBlock != nil {
moreBlock!()
}
} }
// //
@objc private func loadActionClick(_ sender:UIButton) { @objc private func loadActionClick(_ sender:UIButton) {

View File

@ -55,7 +55,7 @@ class MPPositive_SearchResultTypeShowView: UIView, JXSegmentedListContainerViewL
} }
} }
var scrollBlock:(() -> Void)? var scrollBlock:(() -> Void)?
var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
init(frame: CGRect, list:MPPositive_SearchResultListViewModel) { init(frame: CGRect, list:MPPositive_SearchResultListViewModel) {
super.init(frame: frame) super.init(frame: frame)
backgroundColor = .init(hex: "1A1A1A") backgroundColor = .init(hex: "1A1A1A")
@ -106,6 +106,13 @@ extension MPPositive_SearchResultTypeShowView:UITableViewDataSource, UITableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
cell.itemView = sectionList.allItemViews[indexPath.row] 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 return cell
} }
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

View File

@ -15,17 +15,21 @@ class MPPositive_SearchResultsShowView: UIView {
[weak self] in [weak self] in
guard let self = self else {return} guard let self = self else {return}
if loadModel == nil { if loadModel == nil {
isHidden = true emptyImageView.isHidden = false
}else { }else {
MP_AnalyticsManager.shared.search_result_pvAction()
loadModel.resultReloadBlock = { loadModel.resultReloadBlock = {
[weak self] in [weak self] in
// //
guard let self = self else {return} guard let self = self else {return}
MP_HUD.hideNow()
isHidden = false isHidden = false
MP_AnalyticsManager.shared.search_resultsuccess_actionAction()
dataSource.titles = loadModel?.sectionLists.compactMap({$0.title}) ?? [] dataSource.titles = loadModel?.sectionLists.compactMap({$0.title}) ?? []
dataSource.reloadData(selectedIndex: 0) dataSource.reloadData(selectedIndex: 0)
segmentView.reloadData() segmentView.reloadData()
emptyImageView.isHidden = !(dataSource.titles.count == 0) emptyImageView.isHidden = !(dataSource.titles.count == 0)
} }
} }
} }
@ -70,7 +74,7 @@ class MPPositive_SearchResultsShowView: UIView {
return listContainerView return listContainerView
}() }()
// //
private lazy var emptyImageView:UIImageView = { lazy var emptyImageView:UIImageView = {
let imageView = UIImageView(image: UIImage(named: "empty")) let imageView = UIImageView(image: UIImage(named: "empty"))
imageView.contentMode = .scaleAspectFill imageView.contentMode = .scaleAspectFill
return imageView return imageView
@ -78,6 +82,7 @@ class MPPositive_SearchResultsShowView: UIView {
var scrollBlock:(() -> Void)? var scrollBlock:(() -> Void)?
// //
var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)? var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
backgroundColor = .init(hex: "1A1A1A") backgroundColor = .init(hex: "1A1A1A")
@ -144,6 +149,13 @@ extension MPPositive_SearchResultsShowView: JXSegmentedListContainerViewDataSour
chooseItemBlock!(item) chooseItemBlock!(item)
} }
} }
showView.moreBlock = {
[weak self] (itemView) in
guard let self = self else {return}
if moreBlock != nil {
moreBlock!(itemView)
}
}
return showView return showView
}else { }else {
// //
@ -154,6 +166,13 @@ extension MPPositive_SearchResultsShowView: JXSegmentedListContainerViewDataSour
self?.scrollBlock!() self?.scrollBlock!()
} }
} }
showView.moreBlock = {
[weak self] (itemView) in
guard let self = self else {return}
if moreBlock != nil {
moreBlock!(itemView)
}
}
return showView return showView
} }
} }

View File

@ -10,34 +10,34 @@
<objects> <objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MPSideA_AboutViewController" customModule="MusicPlayer" customModuleProvider="target"> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MPSideA_AboutViewController" customModule="MusicPlayer" customModuleProvider="target">
<connections> <connections>
<outlet property="versionView" destination="4fU-O9-pas" id="cz8-Dk-J8d"/> <outlet property="versionView" destination="Wrk-iL-cGv" id="sjM-ZU-PyB"/>
<outlet property="view" destination="Cvd-ZP-KOW" id="sSX-FH-ZL7"/> <outlet property="view" destination="VwI-HU-Cye" id="KxG-DI-JCd"/>
</connections> </connections>
</placeholder> </placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="Cvd-ZP-KOW"> <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="VwI-HU-Cye">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Set'mask" translatesAutoresizingMaskIntoConstraints="NO" id="be2-1M-VPB"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Set'mask" translatesAutoresizingMaskIntoConstraints="NO" id="31j-Zf-qLz">
<rect key="frame" x="0.0" y="0.0" width="375" height="691"/> <rect key="frame" x="0.0" y="0.0" width="375" height="691"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="691" id="d8K-qP-7hT"> <constraint firstAttribute="height" constant="691" id="ES1-v2-JTn">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
</constraints> </constraints>
</imageView> </imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="puq-JQ-VG5"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5gw-lg-ffB">
<rect key="frame" x="16" y="40" width="42" height="42"/> <rect key="frame" x="16" y="40" width="42" height="42"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="42" id="Kld-pl-2rN"> <constraint firstAttribute="height" constant="42" id="6ds-ro-ddB">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstAttribute="height" constant="42" id="bc8-oI-D4b"> <constraint firstAttribute="width" constant="42" id="KQK-bd-ttH">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
@ -46,24 +46,24 @@
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" backgroundImage="Poplogo"/> <state key="normal" backgroundImage="Poplogo"/>
<connections> <connections>
<action selector="popClick:" destination="-1" eventType="touchUpInside" id="iM4-K0-ZQH"/> <action selector="popClick:" destination="-1" eventType="touchUpInside" id="Unh-Z8-VGv"/>
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YYU-ac-9gn"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wzc-Lr-xPw">
<rect key="frame" x="159.5" y="49" width="56" height="24"/> <rect key="frame" x="159.5" y="49" width="56" height="24"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ICON" translatesAutoresizingMaskIntoConstraints="NO" id="0xx-Pi-pKv"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ICON" translatesAutoresizingMaskIntoConstraints="NO" id="bdA-Qf-Wki">
<rect key="frame" x="147.5" y="148" width="80" height="80"/> <rect key="frame" x="147.5" y="148" width="80" height="80"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="80" id="L2T-XG-rgG"> <constraint firstAttribute="width" constant="80" id="6aP-Fe-KRS">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstAttribute="width" constant="80" id="zrl-r2-Cfr"> <constraint firstAttribute="height" constant="80" id="h6c-M8-B3C">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
@ -71,7 +71,7 @@
</constraints> </constraints>
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="15"/> <real key="value" value="12"/>
</userDefinedRuntimeAttribute> </userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor"> <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" red="0.50196078430000002" green="0.97647058819999999" blue="0.53333333329999999" alpha="0.75449892240000005" colorSpace="custom" customColorSpace="sRGB"/> <color key="value" red="0.50196078430000002" green="0.97647058819999999" blue="0.53333333329999999" alpha="0.75449892240000005" colorSpace="custom" customColorSpace="sRGB"/>
@ -81,31 +81,16 @@
</userDefinedRuntimeAttribute> </userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</imageView> </imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="MUSICOO'logo" translatesAutoresizingMaskIntoConstraints="NO" id="WxR-ti-5D3"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wrk-iL-cGv">
<rect key="frame" x="125.5" y="244" width="124" height="19"/> <rect key="frame" x="0.0" y="296" width="375" height="60"/>
<constraints>
<constraint firstAttribute="width" constant="124" id="7cc-mp-PhU">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes>
</constraint>
<constraint firstAttribute="height" constant="19" id="rBg-ye-ZbS">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes>
</constraint>
</constraints>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4fU-O9-pas">
<rect key="frame" x="0.0" y="303" width="375" height="60"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Current Version" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zjg-cK-5H2"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Current Version" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fvv-Xp-KVp">
<rect key="frame" x="15" y="22.5" width="90.5" height="15"/> <rect key="frame" x="15" y="22.5" width="90.5" height="15"/>
<fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="13"/> <fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="13"/>
<color key="textColor" white="1" alpha="0.40000000000000002" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="0.40000000000000002" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Version 1.0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Aeb-L1-sB0"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Version 1.0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="miJ-lx-eHK">
<rect key="frame" x="294" y="22" width="66" height="16"/> <rect key="frame" x="294" y="22" width="66" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/> <fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.61960784310000006" green="0.61960784310000006" blue="0.61960784310000006" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.61960784310000006" green="0.61960784310000006" blue="0.61960784310000006" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -114,66 +99,75 @@
</subviews> </subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstItem="zjg-cK-5H2" firstAttribute="centerY" secondItem="4fU-O9-pas" secondAttribute="centerY" id="Tls-lR-3ux"/> <constraint firstAttribute="trailing" secondItem="miJ-lx-eHK" secondAttribute="trailing" constant="15" id="844-fM-WCc">
<constraint firstItem="zjg-cK-5H2" firstAttribute="leading" secondItem="4fU-O9-pas" secondAttribute="leading" constant="15" id="Tn4-xz-DYE">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstAttribute="height" constant="60" id="i7v-sK-pM6"> <constraint firstItem="fvv-Xp-KVp" firstAttribute="leading" secondItem="Wrk-iL-cGv" secondAttribute="leading" constant="15" id="9oY-vh-dne">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstItem="Aeb-L1-sB0" firstAttribute="centerY" secondItem="4fU-O9-pas" secondAttribute="centerY" id="mhe-B3-Ieg"/> <constraint firstItem="miJ-lx-eHK" firstAttribute="centerY" secondItem="Wrk-iL-cGv" secondAttribute="centerY" id="Dr2-lQ-0pR"/>
<constraint firstAttribute="trailing" secondItem="Aeb-L1-sB0" secondAttribute="trailing" constant="15" id="oBb-b6-a4B"> <constraint firstItem="fvv-Xp-KVp" firstAttribute="centerY" secondItem="Wrk-iL-cGv" secondAttribute="centerY" id="kDT-hS-DXa"/>
<constraint firstAttribute="height" constant="60" id="yCe-QZ-3sw">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
</constraints> </constraints>
</view> </view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Musiclax" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Zfn-fz-UtF">
<rect key="frame" x="127" y="240" width="121" height="36"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="30"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews> </subviews>
<viewLayoutGuide key="safeArea" id="xLY-Ag-RuS"/> <viewLayoutGuide key="safeArea" id="UZJ-Fu-b9f"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="puq-JQ-VG5" firstAttribute="top" secondItem="xLY-Ag-RuS" secondAttribute="top" constant="20" id="1CC-el-OFh"> <constraint firstAttribute="trailing" secondItem="31j-Zf-qLz" secondAttribute="trailing" id="1SW-Lv-Whj"/>
<constraint firstItem="Wzc-Lr-xPw" firstAttribute="centerX" secondItem="VwI-HU-Cye" secondAttribute="centerX" id="25V-9B-pbe"/>
<constraint firstItem="31j-Zf-qLz" firstAttribute="leading" secondItem="VwI-HU-Cye" secondAttribute="leading" id="2tk-qP-f0r"/>
<constraint firstItem="bdA-Qf-Wki" firstAttribute="centerX" secondItem="VwI-HU-Cye" secondAttribute="centerX" id="CBC-w0-cBQ"/>
<constraint firstItem="bdA-Qf-Wki" firstAttribute="top" secondItem="Wzc-Lr-xPw" secondAttribute="bottom" constant="75" id="IVf-Cn-uxv">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstItem="4fU-O9-pas" firstAttribute="top" secondItem="WxR-ti-5D3" secondAttribute="bottom" constant="40" id="3Hk-O3-plC"/> <constraint firstItem="Wrk-iL-cGv" firstAttribute="trailing" secondItem="UZJ-Fu-b9f" secondAttribute="trailing" id="Vu6-Fm-dFV"/>
<constraint firstItem="be2-1M-VPB" firstAttribute="leading" secondItem="Cvd-ZP-KOW" secondAttribute="leading" id="9tf-ol-TR5"/> <constraint firstItem="Wrk-iL-cGv" firstAttribute="leading" secondItem="UZJ-Fu-b9f" secondAttribute="leading" id="Zo7-eO-o9U"/>
<constraint firstItem="YYU-ac-9gn" firstAttribute="centerX" secondItem="Cvd-ZP-KOW" secondAttribute="centerX" id="H5x-yw-4iD"/> <constraint firstItem="Zfn-fz-UtF" firstAttribute="centerX" secondItem="bdA-Qf-Wki" secondAttribute="centerX" id="cTy-pH-dNH"/>
<constraint firstItem="0xx-Pi-pKv" firstAttribute="centerX" secondItem="Cvd-ZP-KOW" secondAttribute="centerX" id="K2R-cQ-Zag"/> <constraint firstItem="Zfn-fz-UtF" firstAttribute="top" secondItem="bdA-Qf-Wki" secondAttribute="bottom" constant="12" id="dJs-I4-rFf">
<constraint firstItem="be2-1M-VPB" firstAttribute="top" secondItem="Cvd-ZP-KOW" secondAttribute="top" id="KWI-v2-z9D"/>
<constraint firstItem="WxR-ti-5D3" firstAttribute="centerX" secondItem="Cvd-ZP-KOW" secondAttribute="centerX" id="Luo-1Z-TZx"/>
<constraint firstItem="4fU-O9-pas" firstAttribute="leading" secondItem="xLY-Ag-RuS" secondAttribute="leading" id="PFV-mq-OZO"/>
<constraint firstItem="puq-JQ-VG5" firstAttribute="leading" secondItem="xLY-Ag-RuS" secondAttribute="leading" constant="16" id="isA-2d-RPM">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstAttribute="trailing" secondItem="be2-1M-VPB" secondAttribute="trailing" id="jLP-Wn-udz"/> <constraint firstItem="5gw-lg-ffB" firstAttribute="leading" secondItem="UZJ-Fu-b9f" secondAttribute="leading" constant="16" id="h5u-9Q-G99">
<constraint firstItem="4fU-O9-pas" firstAttribute="trailing" secondItem="xLY-Ag-RuS" secondAttribute="trailing" id="qFp-0z-pS8"/>
<constraint firstItem="WxR-ti-5D3" firstAttribute="top" secondItem="0xx-Pi-pKv" secondAttribute="bottom" constant="16" id="qvN-fj-QEO">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstItem="0xx-Pi-pKv" firstAttribute="top" secondItem="YYU-ac-9gn" secondAttribute="bottom" constant="75" id="ulb-gy-Y22"> <constraint firstItem="5gw-lg-ffB" firstAttribute="top" secondItem="UZJ-Fu-b9f" secondAttribute="top" constant="20" id="h74-pM-pC7">
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</constraint> </constraint>
<constraint firstItem="YYU-ac-9gn" firstAttribute="centerY" secondItem="puq-JQ-VG5" secondAttribute="centerY" id="vRO-0c-Ywd"/> <constraint firstItem="31j-Zf-qLz" firstAttribute="top" secondItem="VwI-HU-Cye" secondAttribute="top" id="jhp-qC-hsO"/>
<constraint firstItem="Wrk-iL-cGv" firstAttribute="top" secondItem="Zfn-fz-UtF" secondAttribute="bottom" constant="20" id="q9w-RQ-gNf">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
</userDefinedRuntimeAttributes>
</constraint>
<constraint firstItem="Wzc-Lr-xPw" firstAttribute="centerY" secondItem="5gw-lg-ffB" secondAttribute="centerY" id="uR5-5r-KkI"/>
</constraints> </constraints>
<point key="canvasLocation" x="130.40000000000001" y="-12.143928035982009"/> <point key="canvasLocation" x="130.40000000000001" y="-12.143928035982009"/>
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="ICON" width="1024" height="1024"/> <image name="ICON" width="256" height="256"/>
<image name="MUSICOO'logo" width="124" height="19"/>
<image name="Poplogo" width="42" height="42"/> <image name="Poplogo" width="42" height="42"/>
<image name="Set'mask" width="375" height="691"/> <image name="Set'mask" width="375" height="691"/>
</resources> </resources>

View File

@ -47,9 +47,9 @@ extension MPSideA_SettingViewController: UITableViewDataSource, UITableViewDeleg
navigationController?.pushViewController(aboutVC, animated: true) navigationController?.pushViewController(aboutVC, animated: true)
case 1://Feedback 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 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) MP_HUD.text("Successfully copied the e-mail address to the clipboard", delay: 1.0, completion: nil)
} }
alert.addAction(email) alert.addAction(email)
@ -62,7 +62,7 @@ extension MPSideA_SettingViewController: UITableViewDataSource, UITableViewDeleg
//icon //icon
let image = UIImage(named: "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 activityItems = [text,image as Any,url as Any]
// //
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities:nil) let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities:nil)

View File

@ -53,7 +53,7 @@ class MPSideA_HomeViewController: MPSideA_BaseViewController {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
// //
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
MP_AnalyticsManager.shared.home_a_pvAction()
} }
deinit { deinit {

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>3B52.1</string>
</array>
</dict>
</array>
</dict>
</plist>