b面正式版1.0.2
@ -42,8 +42,14 @@
|
||||
CB24169B2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169A2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift */; };
|
||||
CB24169D2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169C2C05E89C007877F7 /* MPPositive_LoveSongsViewController.swift */; };
|
||||
CB24169F2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24169E2C05EF1C007877F7 /* MPPositive_OfflineSongsViewController.swift */; };
|
||||
CB48409C2C08721600341244 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */; };
|
||||
CB48409E2C08738700341244 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = CB48409D2C08738700341244 /* GoogleService-Info.plist */; };
|
||||
CB4840A02C087BD900341244 /* MP_AnalyticsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */; };
|
||||
CB4840A32C0882D100341244 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = CB4840A22C0882D100341244 /* FirebaseAnalytics */; };
|
||||
CB4840A52C0882D100341244 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = CB4840A42C0882D100341244 /* FirebaseCrashlytics */; };
|
||||
CB5661292BE09D0500CFD014 /* MPPositive_JsonPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB5661282BE09D0500CFD014 /* MPPositive_JsonPlayer.swift */; };
|
||||
CB56612D2BE0DF8C00CFD014 /* MP_WebWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB56612C2BE0DF8C00CFD014 /* MP_WebWork.swift */; };
|
||||
CBAFC9EF2C09A1FE0054500E /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */; };
|
||||
CBB5D31D2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5D31C2BDF4E9600CC333D /* MPPositive_MusicItemShowTableViewCell.swift */; };
|
||||
CBB5D31F2BDF711600CC333D /* MPPositive_SongItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5D31E2BDF711600CC333D /* MPPositive_SongItemModel.swift */; };
|
||||
CBB5D3222BDF80C800CC333D /* MPPositive_PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB5D3212BDF80C800CC333D /* MPPositive_PlayerViewController.swift */; };
|
||||
@ -248,6 +254,9 @@
|
||||
CB24169A2C05E34D007877F7 /* MPPositive_LoveArtistTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_LoveArtistTableViewCell.swift; sourceTree = "<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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -421,7 +430,10 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CB4840A52C0882D100341244 /* FirebaseCrashlytics in Frameworks */,
|
||||
CB4840A32C0882D100341244 /* FirebaseAnalytics in Frameworks */,
|
||||
639E3B772F558B3350DD56BA /* Pods_MusicPlayer.framework in Frameworks */,
|
||||
CBAFC9EF2C09A1FE0054500E /* FirebaseRemoteConfig in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -456,6 +468,8 @@
|
||||
009662382BB14A5B00FCA65F /* Assets.xcassets */,
|
||||
0096623A2BB14A5B00FCA65F /* LaunchScreen.storyboard */,
|
||||
0096623D2BB14A5B00FCA65F /* Info.plist */,
|
||||
CB48409D2C08738700341244 /* GoogleService-Info.plist */,
|
||||
CB48409B2C08721600341244 /* PrivacyInfo.xcprivacy */,
|
||||
);
|
||||
path = MusicPlayer;
|
||||
sourceTree = "<group>";
|
||||
@ -643,20 +657,20 @@
|
||||
children = (
|
||||
CBCB4FAE2BD11402009760B3 /* MPSideA_CenterViewController.swift */,
|
||||
CBCB4FAF2BD11402009760B3 /* MPSideA_CenterViewController.xib */,
|
||||
CBCB4FAC2BD11402009760B3 /* MPSideA_AboutViewController.swift */,
|
||||
CBCB4FAD2BD11402009760B3 /* MPSideA_AboutViewController.xib */,
|
||||
CBCB4FB02BD11402009760B3 /* MPSideA_DeleteViewController.swift */,
|
||||
CBCB4FB12BD11402009760B3 /* MPSideA_DeleteViewController.xib */,
|
||||
CBCB4FB22BD11402009760B3 /* MPSideA_MoreViewController.swift */,
|
||||
CBCB4FB32BD11402009760B3 /* MPSideA_MoreViewController.xib */,
|
||||
CBCB4FB42BD11402009760B3 /* MPSideA_PrivacyViewController.swift */,
|
||||
CBCB4FB52BD11402009760B3 /* MPSideA_PrivacyViewController.xib */,
|
||||
CBCB4FB02BD11402009760B3 /* MPSideA_DeleteViewController.swift */,
|
||||
CBCB4FB12BD11402009760B3 /* MPSideA_DeleteViewController.xib */,
|
||||
CBCB4FB62BD11402009760B3 /* MPSideA_RenameViewController.swift */,
|
||||
CBCB4FB72BD11402009760B3 /* MPSideA_RenameViewController.xib */,
|
||||
CBCB4FB82BD11402009760B3 /* MPSideA_ServiceViewController.swift */,
|
||||
CBCB4FB92BD11402009760B3 /* MPSideA_ServiceViewController.xib */,
|
||||
CBCB4FBA2BD11402009760B3 /* MPSideA_SettingViewController.swift */,
|
||||
CBCB4FBB2BD11402009760B3 /* MPSideA_SettingViewController.xib */,
|
||||
CBCB4FAC2BD11402009760B3 /* MPSideA_AboutViewController.swift */,
|
||||
CBCB4FAD2BD11402009760B3 /* MPSideA_AboutViewController.xib */,
|
||||
CBCB4FB82BD11402009760B3 /* MPSideA_ServiceViewController.swift */,
|
||||
CBCB4FB92BD11402009760B3 /* MPSideA_ServiceViewController.xib */,
|
||||
CBCB4FB42BD11402009760B3 /* MPSideA_PrivacyViewController.swift */,
|
||||
CBCB4FB52BD11402009760B3 /* MPSideA_PrivacyViewController.xib */,
|
||||
);
|
||||
path = "Center(个人资源)";
|
||||
sourceTree = "<group>";
|
||||
@ -1018,6 +1032,7 @@
|
||||
CBD958D12BB6600500666B0D /* MP_PlayerSlider.swift */,
|
||||
CB102F532BFAFA7200E967D8 /* MP_CircularProgressView.swift */,
|
||||
CB102F542BFAFA7200E967D8 /* MP_DownloadManager.swift */,
|
||||
CB48409F2C087BD900341244 /* MP_AnalyticsManager.swift */,
|
||||
);
|
||||
path = "Tool(工具封装)";
|
||||
sourceTree = "<group>";
|
||||
@ -1076,6 +1091,11 @@
|
||||
dependencies = (
|
||||
);
|
||||
name = MusicPlayer;
|
||||
packageProductDependencies = (
|
||||
CB4840A22C0882D100341244 /* FirebaseAnalytics */,
|
||||
CB4840A42C0882D100341244 /* FirebaseCrashlytics */,
|
||||
CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */,
|
||||
);
|
||||
productName = MusicPlayer;
|
||||
productReference = 009662292BB14A5A00FCA65F /* MusicPlayer.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
@ -1105,6 +1125,9 @@
|
||||
Base,
|
||||
);
|
||||
mainGroup = 009662202BB14A5A00FCA65F;
|
||||
packageReferences = (
|
||||
CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
|
||||
);
|
||||
productRefGroup = 0096622A2BB14A5A00FCA65F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@ -1129,6 +1152,7 @@
|
||||
CBC54E572BC4D5D3003B1901 /* Shhh….mp3 in Resources */,
|
||||
CBCB500D2BD11402009760B3 /* MPSideA_CustomTabBarView.xib in Resources */,
|
||||
CBCB50072BD11402009760B3 /* MPSideA_PlayerViewController.xib in Resources */,
|
||||
CB48409E2C08738700341244 /* GoogleService-Info.plist in Resources */,
|
||||
0096623C2BB14A5B00FCA65F /* LaunchScreen.storyboard in Resources */,
|
||||
CBC54E5C2BC4D5D3003B1901 /* TV.mp3 in Resources */,
|
||||
CBCB50152BD11402009760B3 /* MPSideA_Home_FourthListCollectionViewCell.xib in Resources */,
|
||||
@ -1155,6 +1179,7 @@
|
||||
CBCB50052BD11402009760B3 /* MPSideA_HomeViewController.xib in Resources */,
|
||||
CBC54E642BC4D5D3003B1901 /* Seawater Surging.mp3 in Resources */,
|
||||
CBCB500F2BD11402009760B3 /* MPSideA_CenterTableViewCell.xib in Resources */,
|
||||
CB48409C2C08721600341244 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
CBCB4FF32BD11402009760B3 /* MPSideA_AboutViewController.xib in Resources */,
|
||||
CBC54E582BC4D5D3003B1901 /* Shh Shh.mp3 in Resources */,
|
||||
CBC54E632BC4D5D3003B1901 /* Howling Wind.mp3 in Resources */,
|
||||
@ -1300,6 +1325,7 @@
|
||||
CBE1CB4E2BDE4BD800701D57 /* MPPositive_ListAlbumListViewModel.swift in Sources */,
|
||||
CB2416972C05D3C3007877F7 /* MPPositive_CollectionArtistViewModel.swift in Sources */,
|
||||
CBD313572BD63B390015D227 /* MPPositive_HomeListSecondCollectionViewCell.swift in Sources */,
|
||||
CB4840A02C087BD900341244 /* MP_AnalyticsManager.swift in Sources */,
|
||||
0096622D2BB14A5A00FCA65F /* AppDelegate.swift in Sources */,
|
||||
CBC32A532BD8D9F300687171 /* MPPositive_BrowseItemModel.swift in Sources */,
|
||||
CBE16B952BF251FF005B7EE6 /* MPPositive_SearchSuggestionItemListModel.swift in Sources */,
|
||||
@ -1522,15 +1548,21 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.2;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
DEVELOPMENT_TEAM = T93S37G27F;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = MusicPlayer/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Musicoo;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Musiclax;
|
||||
INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!";
|
||||
INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!";
|
||||
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"Musiclax\" requires you to turn on the microphone to recognize surrounding decibels and automatically turn on white noise for you. Do you allow this application to obtain your microphone permissions?";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "\"Musiclax\" requires opening your album to obtain photos, which are used to add your custom white noise. Do you want to allow this application to obtain your album permissions?";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
@ -1538,8 +1570,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lux.musicplayer.MusicPlayer;
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = relax.offline.mp3.music;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
@ -1560,15 +1592,21 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.2;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
DEVELOPMENT_TEAM = T93S37G27F;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = MusicPlayer/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Musicoo;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Musiclax;
|
||||
INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!";
|
||||
INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!";
|
||||
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "\"Musiclax\" needs to obtain your location information in order to refine the preview music information provided to you!";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "\"Musiclax\" requires you to turn on the microphone to recognize surrounding decibels and automatically turn on white noise for you. Do you allow this application to obtain your microphone permissions?";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "\"Musiclax\" requires opening your album to obtain photos, which are used to add your custom white noise. Do you want to allow this application to obtain your album permissions?";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
@ -1576,8 +1614,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lux.musicplayer.MusicPlayer;
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = relax.offline.mp3.music;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
@ -1613,6 +1651,35 @@
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 10.27.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
CB4840A22C0882D100341244 /* FirebaseAnalytics */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
|
||||
productName = FirebaseAnalytics;
|
||||
};
|
||||
CB4840A42C0882D100341244 /* FirebaseCrashlytics */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
|
||||
productName = FirebaseCrashlytics;
|
||||
};
|
||||
CBAFC9EE2C09A1FE0054500E /* FirebaseRemoteConfig */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CB4840A12C0882D100341244 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
|
||||
productName = FirebaseRemoteConfig;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
009662352BB14A5A00FCA65F /* MusicPlayer.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
|
||||
@ -49,6 +49,16 @@
|
||||
ReferencedContainer = "container:MusicPlayer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "-FIRDebugEnabled"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "-FIRDebugDisabled"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "IDERedirectionPolicy"
|
||||
|
||||
123
MusicPlayer.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal 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
|
||||
}
|
||||
@ -10,6 +10,7 @@ import CoreData
|
||||
import AVFoundation
|
||||
import Alamofire
|
||||
import Tiercel
|
||||
import FirebaseCore
|
||||
@_exported import IQKeyboardManagerSwift
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
@ -25,8 +26,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
print("Users are not allowed to be notified of messages.")
|
||||
}
|
||||
}
|
||||
//生成一个FireBase实例
|
||||
FirebaseApp.configure()
|
||||
//启动前销毁所有的下载任务
|
||||
DownloadManager.shared.cancelAllTasksIfNeeded()
|
||||
MP_DownloadManager.shared.cancelAllTasksIfNeeded()
|
||||
setAudioSupport()
|
||||
MP_NetWorkManager.shared.requestStatusToYouTube()
|
||||
IQKeyboardManager.shared.enable = true
|
||||
@ -34,12 +37,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window?.backgroundColor = .init(hex: "#161616")
|
||||
switch_lunch()
|
||||
//执行用户启动事件日志
|
||||
MP_AnalyticsManager.shared.user_launchAction()
|
||||
return true
|
||||
}
|
||||
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
|
||||
if identifier == "com.yourApp.backgroundDownload" {
|
||||
DownloadManager.shared.session = SessionManager("com.yourApp.backgroundDownload", configuration: .init())
|
||||
DownloadManager.shared.session.completionHandler = completionHandler
|
||||
MP_DownloadManager.shared.session = SessionManager("com.yourApp.backgroundDownload", configuration: .init())
|
||||
MP_DownloadManager.shared.session.completionHandler = completionHandler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 870 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 9.5 KiB |
@ -1,15 +1,16 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "img_v3_02ae_ad486134-21d1-4b05-869c-06e3d548e40g.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 578 KiB |
BIN
MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g 1.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
MusicPlayer/Assets.xcassets/ICON.imageset/img_v3_02b6_9dd8ad60-2766-4b89-9959-401f232d926g.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
@ -5,12 +5,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "img_v3_02ae_c419ebcc-7bcc-4018-a6f4-b4cb25f7d11g.png",
|
||||
"filename" : "img_v3_02b6_3f726db0-fde6-4335-a78b-2a4ba9365ccg.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "img_v3_02ae_2efa5fb1-c28e-4299-b6c7-abe303cb6f8g.png",
|
||||
"filename" : "img_v3_02b6_97959922-6ac1-437f-aab5-c65850cdb09g.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 860 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 242 KiB |
@ -5,12 +5,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1597880487@2x.png",
|
||||
"filename" : "Group_1597880484@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1597880487@3x.png",
|
||||
"filename" : "Group_1597880484@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
||||
BIN
MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
MusicPlayer/Assets.xcassets/Positive/Home/List_ShufflePlay'logo.imageset/Group_1597880484@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
@ -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
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
30
MusicPlayer/GoogleService-Info.plist
Normal 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>
|
||||
@ -2,39 +2,14 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleLightContent</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>"Musicoo" requires you to turn on the microphone to recognize surrounding decibels and automatically turn on white noise for you. Do you allow this application to obtain your microphone permissions?</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>"Musicoo" requires opening your album to obtain photos, which are used to add your custom white noise. Do you want to allow this application to obtain your album permissions?</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
<string>fetch</string>
|
||||
<string>backgroundFetch</string>
|
||||
</array>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>"Musicoo" needs to obtain your location information in order to refine the preview music information provided to you!</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>"Musicoo" needs to obtain your location information in order to refine the preview music information provided to you!</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>"Musicoo" 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>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSUserTrackingUsageDescription</key>
|
||||
<string>"Musiclax" needs to request tracking permissions to provide a personalized advertising experience. We respect and protect your privacy and will not sell your data to third parties.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -16,9 +16,11 @@ class MP_LunchViewController: UIViewController {
|
||||
//帧计时器
|
||||
private var timer:CADisplayLink!
|
||||
//最大计时值
|
||||
private lazy var maxTimes:TimeInterval = 8
|
||||
private lazy var maxTimes:TimeInterval = 6
|
||||
//当前计时值
|
||||
private lazy var currentTimes:TimeInterval = 0
|
||||
//计时器结束事件
|
||||
private var completionBlock:(() -> Void)?
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .init(hex: "#000000")
|
||||
@ -27,14 +29,60 @@ class MP_LunchViewController: UIViewController {
|
||||
timer.preferredFramesPerSecond = 20
|
||||
//开辟线程
|
||||
timer.add(to: RunLoop.current, forMode: .common)
|
||||
//启动计时器
|
||||
timer.isPaused = false
|
||||
//获取idfa
|
||||
_ = requestTrackingAuthorization(self)
|
||||
//获取定位权限
|
||||
MP_LocationManager.shared.setLocationPermission(self, complete: nil)
|
||||
//获取youtube网站信息
|
||||
MP_WebWork.shared.pingYoutubeHome()
|
||||
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(jumpAction(_:)), notificationName: .js_edit_completion)
|
||||
MP_AnalyticsManager.shared.getOpenStatus { [weak self] open in
|
||||
guard let self = self else {return}
|
||||
if open {
|
||||
//根据ip值确定进入那个页面
|
||||
MP_NetWorkManager.shared.requestIPInfo { statu in
|
||||
if statu == true {
|
||||
//允许进入b面
|
||||
print("BLog")
|
||||
self.completionBlock = {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//停止计时器
|
||||
timer.isPaused = true
|
||||
//加载完毕,判断并跳转
|
||||
accessAppdelegate.switch_positive()
|
||||
}
|
||||
}
|
||||
}else {
|
||||
print("ALog")
|
||||
//打开A面
|
||||
self.completionBlock = {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//停止计时器
|
||||
timer.isPaused = true
|
||||
//加载完毕,判断并跳转
|
||||
accessAppdelegate.switch_aSide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
print("ALog")
|
||||
//打开A面
|
||||
completionBlock = {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//停止计时器
|
||||
timer.isPaused = true
|
||||
//加载完毕,判断并跳转
|
||||
accessAppdelegate.switch_aSide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//启动计时器
|
||||
timer.isPaused = false
|
||||
}
|
||||
deinit {
|
||||
//销毁计时器
|
||||
@ -42,15 +90,9 @@ class MP_LunchViewController: UIViewController {
|
||||
timer = nil
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
@objc private func jumpAction(_ sender:Notification) {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//停止计时器
|
||||
timer.isPaused = true
|
||||
//加载完毕,判断并跳转
|
||||
accessAppdelegate.switch_positive()
|
||||
}
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
MP_AnalyticsManager.shared.launch_pvAction()
|
||||
}
|
||||
|
||||
//计时器执行事件
|
||||
@ -66,14 +108,9 @@ class MP_LunchViewController: UIViewController {
|
||||
progressView.setProgress(value)
|
||||
}
|
||||
}else {
|
||||
// DispatchQueue.main.async {
|
||||
// [weak self] in
|
||||
// guard let self = self else {return}
|
||||
// //停止计时器
|
||||
// timer.isPaused = true
|
||||
// //加载完毕,判断并跳转
|
||||
// accessAppdelegate.switch_positive()
|
||||
// }
|
||||
if completionBlock != nil {
|
||||
completionBlock!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{
|
||||
private func album(){
|
||||
DispatchQueue.main.async {
|
||||
//次要处理
|
||||
let alertController = UIAlertController(title: "Access album permission request", message: "“Musicoo”currently does not have permission to access the album and cannot obtain album data. If you want to use the photo album function, please click “Settings” to allow this App to obtain permission to access the photo album", preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "Access album permission request", message: "“Musiclax”currently does not have permission to access the album and cannot obtain album data. If you want to use the photo album function, please click “Settings” to allow this App to obtain permission to access the photo album", preferredStyle: .alert)
|
||||
let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
|
||||
|
||||
}
|
||||
@ -141,7 +141,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{
|
||||
private func camera(){
|
||||
DispatchQueue.main.async {
|
||||
//次要处理
|
||||
let alertController = UIAlertController(title: "Access camera permission request", message: "“Musicoo”does not have access to the camera, and cannot call camera functions. If you want to use the camera function, please click “Settings” to allow this App to gain access to the camera", preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "Access camera permission request", message: "“Musiclax”does not have access to the camera, and cannot call camera functions. If you want to use the camera function, please click “Settings” to allow this App to gain access to the camera", preferredStyle: .alert)
|
||||
let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
|
||||
|
||||
}
|
||||
@ -164,7 +164,7 @@ extension UIImagePickerControllerDelegate where Self:UIViewController{
|
||||
private func video(){
|
||||
DispatchQueue.main.async {
|
||||
//次要处理
|
||||
let alertController = UIAlertController(title: "Access album permission request", message: "“Musicoo” needs to open your album to get the photos that will be used to add your custom white noise. Please go to ”Settings“ to turn on this permission!", preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "Access album permission request", message: "“Musiclax” needs to open your album to get the photos that will be used to add your custom white noise. Please go to ”Settings“ to turn on this permission!", preferredStyle: .alert)
|
||||
let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
|
||||
|
||||
}
|
||||
|
||||
@ -95,6 +95,8 @@ extension NotificationCenter{
|
||||
case positive_nav_push
|
||||
///b面Pop时
|
||||
case positive_nav_pop
|
||||
///b面网络请求报错
|
||||
case netWork_error_deal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
import UIKit
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
import AppTrackingTransparency
|
||||
import AdSupport
|
||||
@_exported import JXSegmentedView
|
||||
@_exported import JXPagingView
|
||||
//给JXPagingListContainerView添加extension,表示遵从JXSegmentedViewListContainer的协议
|
||||
@ -38,15 +40,22 @@ let Phone_Model = UIDevice.current.model
|
||||
let System_Version = UIDevice.current.systemVersion
|
||||
///获取当前系统语言
|
||||
let Language_first_local = NSLocale.preferredLanguages.first!
|
||||
|
||||
///当前应用版本号
|
||||
var app_Version:String{
|
||||
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
|
||||
return version
|
||||
}else {
|
||||
return "1.0.0"
|
||||
}
|
||||
}
|
||||
///底部安全区域
|
||||
let bottomPadding = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
|
||||
///全局占位图
|
||||
let placeholderImage:UIImage = UIImage(named: "Home First'placeholder")!
|
||||
///隐私政策网址
|
||||
let privacyUrl:URL = .init(string: "https://musicoo.app/privacy")!
|
||||
let privacyUrl:URL = .init(string: "https://musiclax.mystrikingly.com/privacy")!
|
||||
///用户协议网址
|
||||
let serviceUrl:URL = .init(string: "https://musicoo.app/terms")!
|
||||
let serviceUrl:URL = .init(string: "https://musiclax.mystrikingly.com/terms")!
|
||||
|
||||
//MARK: - 全局变量与方法
|
||||
///总事件闭包
|
||||
@ -71,6 +80,20 @@ func getDocumentsFileURL(_ videoID: String) -> String? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
///调用next对单曲数据歌词ID与相关ID补全
|
||||
func improveDataforLycirsAndRelated(_ song:MPPositive_SongItemModel, completion:@escaping(((String?,String?)) -> Void)) {
|
||||
//单曲补全需要再次调用next接口
|
||||
MP_NetWorkManager.shared.requestNextLyricsAndRelated(song){ result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
///调用player对资源路径和封面路径补全
|
||||
func improveDataforResouceAndCover(_ song:MPPositive_SongItemModel, completion:@escaping((([String],[Int],[String]), [String]?) -> Void)) {
|
||||
//单曲补全需要调用player接口
|
||||
MP_NetWorkManager.shared.requestAndroidPlayer(song.videoId, playlistId: "") { resourceUrls, coverUrls in
|
||||
completion(resourceUrls,coverUrls)
|
||||
}
|
||||
}
|
||||
///转时分值
|
||||
func setTimesToMinSeconds(_ time:TimeInterval) -> String {
|
||||
//设置分钟
|
||||
@ -99,7 +122,7 @@ func authorize(observe:UIViewController) -> Bool{
|
||||
})
|
||||
default: ()
|
||||
DispatchQueue.main.async(execute: { () -> Void in
|
||||
let alertController = UIAlertController(title: "Get Microphone Access",message: "“Musicoo” asks you to turn on your microphone to recognize the decibels around you and turns on white noise for you automatically. Please go to the “Settings” page to turn on the microphone permission",preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "Get Microphone Access",message: "“Musiclax” asks you to turn on your microphone to recognize the decibels around you and turns on white noise for you automatically. Please go to the “Settings” page to turn on the microphone permission",preferredStyle: .alert)
|
||||
let cancelAction = UIAlertAction(title:"Cancel", style: .cancel, handler:nil)
|
||||
let settingsAction = UIAlertAction(title:"Settings", style: .default, handler: {
|
||||
(action) -> Void in
|
||||
@ -143,3 +166,41 @@ func switchPlayTypeBtnIcon(_ btn:UIButton) {
|
||||
btn.setBackgroundImage(UIImage(named: "Player_Single'logo"), for: .normal)
|
||||
}
|
||||
}
|
||||
///请求广告授权
|
||||
func requestTrackingAuthorization(_ observe:UIViewController) -> Bool {
|
||||
if #available(iOS 14, *) {
|
||||
// 检查当前的跟踪管理器状态
|
||||
let status = ATTrackingManager.trackingAuthorizationStatus
|
||||
switch status {
|
||||
case .notDetermined:
|
||||
// 处理未知授权状态
|
||||
print("未知的跟踪状态")
|
||||
ATTrackingManager.requestTrackingAuthorization { status in
|
||||
let isAuthorized = status == .authorized
|
||||
DispatchQueue.main.async {
|
||||
_ = requestTrackingAuthorization(observe)
|
||||
}
|
||||
}
|
||||
case .authorized:
|
||||
// 用户授予了权限,可以获取 IDFA
|
||||
print("用户授权跟踪")
|
||||
return true
|
||||
case .denied:
|
||||
print("用户拒绝跟踪")
|
||||
default:()
|
||||
print("跟踪状态受限")
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
///获取IDFA
|
||||
func getIDFA(_ observe:UIViewController) -> UUID? {
|
||||
if requestTrackingAuthorization(observe) {
|
||||
let idfa = ASIdentifierManager.shared().advertisingIdentifier
|
||||
return idfa
|
||||
}else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ class MPPositive_Debouncer: NSObject {
|
||||
private var delay: TimeInterval
|
||||
|
||||
private override init() {
|
||||
delay = 0.4
|
||||
delay = 0.5
|
||||
super.init()
|
||||
}
|
||||
deinit {
|
||||
|
||||
@ -472,7 +472,6 @@ class MPSideA_MediaCenterManager {
|
||||
center!.playCommand.addTarget(handler: { [weak self] (event) in
|
||||
guard let self = self else { return .noActionableNowPlayingItem}
|
||||
if self.music != nil {
|
||||
|
||||
return .success
|
||||
}else {
|
||||
return .noActionableNowPlayingItem
|
||||
|
||||
257
MusicPlayer/MP/Common/Tool(工具封装)/MP_AnalyticsManager.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -8,14 +8,14 @@
|
||||
import Foundation
|
||||
import Foundation
|
||||
import Tiercel
|
||||
class DownloadManager: NSObject {
|
||||
static let shared = DownloadManager()
|
||||
class MP_DownloadManager: NSObject {
|
||||
static let shared = MP_DownloadManager()
|
||||
|
||||
var session: SessionManager!
|
||||
var progressHandlers: [URL: (CGFloat) -> Void] = [:]
|
||||
var progressHandlers: [String: (CGFloat) -> Void] = [:]
|
||||
var completionHandlers: [URL: (Result<MPPositive_SongItemModel, Error>) -> Void] = [:]
|
||||
var downloadTasks: [URL: URLSessionDownloadTask] = [:]
|
||||
var progressStorage: [URL: CGFloat] = [:] // 新增进度存储
|
||||
var progressStorage: [String: CGFloat] = [:] // 新增进度存储
|
||||
var songHandlers:[URL: MPPositive_SongItemModel] = [:]
|
||||
private override init() {
|
||||
super.init()
|
||||
@ -26,15 +26,15 @@ class DownloadManager: NSObject {
|
||||
session = SessionManager("com.yourApp.backgroundDownload", configuration: configuration)
|
||||
}
|
||||
func downloadVideo(from url: URL, song:MPPositive_SongItemModel, progressHandler: @escaping (CGFloat) -> Void, completion: @escaping (Result<MPPositive_SongItemModel, Error>) -> Void) {
|
||||
progressHandlers[url] = progressHandler
|
||||
progressHandlers[song.videoId] = progressHandler
|
||||
completionHandlers[url] = completion
|
||||
songHandlers[url] = song
|
||||
let downloadTask = session.download(url, headers: ["Accept-Encoding": "gzip, deflate"])
|
||||
//配置进度条
|
||||
downloadTask?.progress(handler: { [weak self] (task) in
|
||||
guard let self = self else {return}
|
||||
progressHandlers[task.url]?(task.progress.fractionCompleted)
|
||||
progressStorage[task.url] = task.progress.fractionCompleted
|
||||
progressHandlers[song.videoId]?(task.progress.fractionCompleted)
|
||||
progressStorage[song.videoId] = task.progress.fractionCompleted
|
||||
})
|
||||
//配置任务完成事件
|
||||
downloadTask?.success(handler: { [weak self] (task) in
|
||||
@ -54,15 +54,21 @@ class DownloadManager: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
let fileURL = downloadsURL.appendingPathComponent("\(MP_PlayerManager.shared.loadPlayer.currentVideo.song.videoId ?? "").mp4")
|
||||
let fileURL = downloadsURL.appendingPathComponent("\(song.videoId ?? "").mp4")
|
||||
do {
|
||||
try FileManager.default.moveItem(at: filePathUrl, to: fileURL)
|
||||
//回调VideoID
|
||||
completionHandlers[originalURL]?(.success(songHandlers[originalURL]!))
|
||||
progressStorage[originalURL] = nil // 清除已完成任务的进度记录
|
||||
progressStorage[song.videoId] = nil // 清除已完成任务的进度记录
|
||||
} catch {
|
||||
completionHandlers[originalURL]?(.failure(error))
|
||||
}
|
||||
//移除任务
|
||||
if task.status == .succeeded {
|
||||
session.remove(task, completely: true) {_ in
|
||||
print("\(song.title ?? "")下载任务完成,移除任务")
|
||||
}
|
||||
}
|
||||
}).failure(handler: { [weak self] (task) in
|
||||
guard let self = self else {return}
|
||||
//任务下载失败
|
||||
@ -70,10 +76,15 @@ class DownloadManager: NSObject {
|
||||
if let error = task.error {
|
||||
completionHandlers[originalURL]?(.failure(error))
|
||||
}
|
||||
if task.status == .failed {
|
||||
session.cancel(task) { _ in
|
||||
print("\(song.title ?? "")下载任务失败,取消任务")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
func getProgress(for url: URL) -> CGFloat? {
|
||||
return progressStorage[url]
|
||||
func getProgress(for videoId: String) -> CGFloat? {
|
||||
return progressStorage[videoId]
|
||||
}
|
||||
func cancelAllTasksIfNeeded() {
|
||||
// 根据需求,取消所有任务,或者根据任务状态进行过滤
|
||||
@ -85,11 +96,12 @@ class DownloadManager: NSObject {
|
||||
func deleteFileDocuments(_ videoId:String, completion:@escaping((String) -> Void)) {
|
||||
let downloadsURL = DocumentsURL.appendingPathComponent("Downloads")
|
||||
let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4")
|
||||
if FileManager.default.fileExists(atPath: fileURL.absoluteString) {
|
||||
if FileManager.default.fileExists(atPath: fileURL.path) {
|
||||
do{
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
//文件删除成功
|
||||
completion(videoId)
|
||||
print("成功删除了\(videoId)文件")
|
||||
}catch{
|
||||
print("删除文件时发生错误:\(error)")
|
||||
}
|
||||
@ -98,44 +110,3 @@ class DownloadManager: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//class DownloadManager {
|
||||
//
|
||||
// static let shared = DownloadManager()
|
||||
//
|
||||
// private init() {}
|
||||
//
|
||||
// func downloadVideo(from url: URL, videoId: String, progressView: CircularProgressView, completion: @escaping (Result<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))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
@ -22,6 +22,15 @@ class MP_HUD: NSObject {
|
||||
///加载数据
|
||||
case loading
|
||||
}
|
||||
///直接展示HUD,纯等待
|
||||
static func loading(){
|
||||
SVProgressHUD.setDefaultStyle(.light)
|
||||
SVProgressHUD.setDefaultMaskType(.clear)
|
||||
SVProgressHUD.setBackgroundColor(.init(hex: "#80F988", alpha: 0.1))
|
||||
SVProgressHUD.setForegroundColor(.init(hex: "#80F988"))
|
||||
SVProgressHUD.setOffsetFromCenter(.init(horizontal: 0, vertical: 0))
|
||||
SVProgressHUD.show()
|
||||
}
|
||||
///文本HUD
|
||||
static func text(_ text:String?,delay:TimeInterval,completion:(() -> Void)?){
|
||||
showWithStatus(hudStatus: .onlyText, text: text, delay: delay, completion: completion)
|
||||
@ -56,7 +65,8 @@ class MP_HUD: NSObject {
|
||||
static func showWithStatus(hudStatus status: status, text: String?, delay: TimeInterval ,completion:(() -> Void)?) {
|
||||
SVProgressHUD.setDefaultStyle(.light)
|
||||
SVProgressHUD.setDefaultMaskType(.clear)
|
||||
SVProgressHUD.setBackgroundColor(.white)
|
||||
SVProgressHUD.setBackgroundColor(.init(hex: "#80F988", alpha: 0.1))
|
||||
SVProgressHUD.setForegroundColor(.init(hex: "#80F988"))
|
||||
SVProgressHUD.setOffsetFromCenter(.init(horizontal: 0, vertical: 0))
|
||||
switch status {
|
||||
case .success:
|
||||
@ -81,4 +91,5 @@ class MP_HUD: NSObject {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ class MP_LocationManager: NSObject {
|
||||
MP_LocationManager().requestLocationAuthorizaiton()
|
||||
case .restricted, .denied:
|
||||
DispatchQueue.main.async {
|
||||
let alertController = UIAlertController(title: "Location permission request", message: "“Musicoo” needs to obtain your location information in order to refine the preview music information provided to you!", preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "Location permission request", message: "“Musiclax” needs to obtain your location information in order to refine the preview music information provided to you!", preferredStyle: .alert)
|
||||
let CancelAction = UIAlertAction(title:"Cancel", style: .cancel)
|
||||
let OKAction = UIAlertAction(title: "Settings", style: .default) { (action) in
|
||||
let url = URL(string: UIApplication.openSettingsURLString)
|
||||
|
||||
@ -21,6 +21,8 @@ class MP_NetWorkManager: NSObject {
|
||||
///会话实例
|
||||
private let MPSession = Alamofire.Session(interceptor: MP_CustomRetrier())
|
||||
//MARK: - API接口
|
||||
///IP获取
|
||||
private let iPInfo:String = "https://api.tikustok.com/app/common/getIPInfo"
|
||||
///域名链接
|
||||
private let header:String = "https://music.youtube.com"
|
||||
///端点
|
||||
@ -37,6 +39,17 @@ class MP_NetWorkManager: NSObject {
|
||||
private let search = "/search"
|
||||
///YouTuBe资源键值
|
||||
private let youTubeKeys:[String] = ["MUSIC_VIDEO_TYPE_ATV","MUSIC_VIDEO_TYPE_OMV","MUSIC_PAGE_TYPE_ALBUM","MUSIC_PAGE_TYPE_ARTIST","MUSIC_PAGE_TYPE_PLAYLIST","MUSIC_PAGE_TYPE_TRACK_LYRICS","MUSIC_PAGE_TYPE_TRACK_RELATED"]
|
||||
///禁止接入IP信息组
|
||||
private let banIPs:[String] = ["CN",
|
||||
"HK",
|
||||
"TW",
|
||||
"JP",
|
||||
"KR",
|
||||
"GB",
|
||||
"CH",
|
||||
"BE",
|
||||
"MO",
|
||||
"SG"]
|
||||
//网络状态
|
||||
enum NetWorkStatus: String {
|
||||
case notReachable = "网络不可用"
|
||||
@ -136,7 +149,7 @@ class MP_NetWorkManager: NSObject {
|
||||
default://网络权限出现问题
|
||||
DispatchQueue.main.async {
|
||||
//次要处理
|
||||
let alertController = UIAlertController(title: "Access network request", message: "”Musicoo“ needs to be loaded via a network request. Please click “Settings” to allow this application to gain access to the network.", preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "Access network request", message: "”Musiclax“ needs to be loaded via a network request. Please click “Settings” to allow this application to gain access to the network.", preferredStyle: .alert)
|
||||
let CancelAction = UIAlertAction(title:"Cancel", style: .cancel) { (Cancel) in
|
||||
|
||||
}
|
||||
@ -178,12 +191,50 @@ class MP_NetWorkManager: NSObject {
|
||||
}
|
||||
//MARK: - API请求
|
||||
extension MP_NetWorkManager {
|
||||
//MARK: - 请求iP信息
|
||||
///请求IP信息
|
||||
func requestIPInfo(_ completion:@escaping((Bool) -> Void)) {
|
||||
//拼接出browse路径
|
||||
let path = iPInfo
|
||||
//设置url
|
||||
guard let url = URL(string: path) else {
|
||||
print("Url is Incorrect")
|
||||
return
|
||||
}
|
||||
requestPostIPInfo(url) { open in
|
||||
completion(open)
|
||||
}
|
||||
}
|
||||
private func requestPostIPInfo(_ url:URL, completion:@escaping((Bool) -> Void)) {
|
||||
MPSession.request(url, method: .get, encoding: JSONEncoding.default).responseDecodable(of: JsonIPInfo.self) { [weak self] (response) in
|
||||
guard let self = self else {return}
|
||||
switch response.result {
|
||||
case .success(let value):
|
||||
guard let data = value.data, let code = data.isoCode else {
|
||||
return
|
||||
}
|
||||
if banIPs.contains(code) == true {
|
||||
//包含,是禁止区域
|
||||
completion(false)
|
||||
}else {
|
||||
//不包含,是通行区域
|
||||
completion(true)
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
handleError(url, error: error)
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - 请求首页预览
|
||||
///向YouTubemusic请求预览/首页数据
|
||||
func requestBrowseDatas() {
|
||||
//实行串行异步队列,进行多次请求。由于第一次之后的请求都必须携带对应的continuation编码,所以串行队列。直到最后一次请求的continuation值为空,销毁队列
|
||||
// 实例化串行队列
|
||||
browseQueque = DispatchQueue(label: "com.request.browseQueque")
|
||||
visitorData = nil
|
||||
//拼接出browse路径
|
||||
let path = header+point+browse
|
||||
//设置url
|
||||
@ -239,7 +290,8 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
browseQueque = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,7 +317,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -301,7 +353,7 @@ extension MP_NetWorkManager {
|
||||
comletion(list)
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -322,7 +374,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -356,7 +408,7 @@ extension MP_NetWorkManager {
|
||||
comletion(artist)
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -378,7 +430,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -407,7 +459,7 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,7 +483,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -460,7 +512,7 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -485,7 +537,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -514,7 +566,7 @@ extension MP_NetWorkManager {
|
||||
completion(listSongs)
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -536,7 +588,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -564,7 +616,7 @@ extension MP_NetWorkManager {
|
||||
completion(result)
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -592,6 +644,12 @@ extension MP_NetWorkManager {
|
||||
"platform":"MOBILE",
|
||||
"browserVersion":"125.0.0.0",
|
||||
// "userAgent":
|
||||
// "clientName": "ANDROID_MUSIC",
|
||||
// "clientVersion": "5.28.1",
|
||||
// "platform": "MOBILE",
|
||||
// "androidSdkVersion":"30",
|
||||
// "userAgent": "com.google.android.apps.youtube.music/5.28.1 (Linux; U; Android 11) gzip"
|
||||
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -612,7 +670,7 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -631,7 +689,7 @@ extension MP_NetWorkManager {
|
||||
// "context":[
|
||||
// "client":[
|
||||
// "clientName": "WEB_REMIX",
|
||||
//// "visitorData":visitorData,
|
||||
//// //"visitorData":visitorData,
|
||||
//// "originalUrl":"https://music.youtube.com/watch?v=\(videoId)",
|
||||
// //当前访问版本(日期值)
|
||||
// "clientVersion": "1.\(currTimeDate).01.00",
|
||||
@ -689,7 +747,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -715,7 +773,7 @@ extension MP_NetWorkManager {
|
||||
completion(parsingLyrics(value) ?? "")
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -736,7 +794,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -765,7 +823,7 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -789,7 +847,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -817,7 +875,7 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -840,7 +898,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -870,7 +928,7 @@ extension MP_NetWorkManager {
|
||||
}
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -897,7 +955,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -926,7 +984,7 @@ extension MP_NetWorkManager {
|
||||
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -954,7 +1012,7 @@ extension MP_NetWorkManager {
|
||||
"client":[
|
||||
//web端
|
||||
"clientName": "WEB_REMIX",
|
||||
"visitorData":visitorData,
|
||||
//"visitorData":visitorData,
|
||||
//当前访问版本(日期值)
|
||||
"clientVersion": "1.\(currTimeDate).01.00",
|
||||
"platform":"MOBILE",
|
||||
@ -983,11 +1041,39 @@ extension MP_NetWorkManager {
|
||||
|
||||
case .failure(let error):
|
||||
// 请求失败,处理错误
|
||||
print("Request failed: \(error)")
|
||||
handleError(url, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///错误处理方法
|
||||
private func handleError(_ url: URL, error:AFError) {
|
||||
// 根据错误类型处理
|
||||
if let statusCode = error.responseCode {
|
||||
switch statusCode {
|
||||
case 400...499:
|
||||
print("\(url)请求错误,错误码: \(statusCode)。")
|
||||
case 500...599:
|
||||
print("\(url)服务器错误,错误码: \(statusCode)。")
|
||||
default:
|
||||
print("\(url)其他 HTTP 错误,错误码: \(statusCode)。")
|
||||
}
|
||||
} else if let underlyingError = error.underlyingError as? URLError {
|
||||
switch underlyingError.code {
|
||||
case .notConnectedToInternet:
|
||||
print("\(url)网络连接不可用,请检查你的网络设置。")
|
||||
case .timedOut:
|
||||
print("\(url)请求超时,请稍后重试。")
|
||||
default:
|
||||
print("\(url)NSURL 错误,错误码: \(underlyingError.code.rawValue)。")
|
||||
}
|
||||
} else {
|
||||
print("\(url)未知错误: \(error.localizedDescription)")
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
//统一发一个报错通知
|
||||
NotificationCenter.notificationKey.post(notificationName: .netWork_error_deal)
|
||||
}
|
||||
}
|
||||
}
|
||||
//MARK: - 数据解析
|
||||
extension MP_NetWorkManager {
|
||||
@ -1819,30 +1905,35 @@ extension MP_NetWorkManager {
|
||||
//MARK: - 自动重试
|
||||
///重试策略类
|
||||
class MP_CustomRetrier: RequestInterceptor {
|
||||
// 尝试重试次数的最大值
|
||||
private let maximumRetryCount = 3
|
||||
// 请求与其对应的重试次数
|
||||
// 追踪重试次数的字典
|
||||
private var retryCounts: [String: Int] = [:]
|
||||
func retry(_ request: Alamofire.Request, for session: Alamofire.Session, dueTo error: any Error, completion: @escaping (Alamofire.RetryResult) -> Void) {
|
||||
// 根据请求的id获取当前的重试次数
|
||||
let requestID = request.id
|
||||
let currentRetryCount = retryCounts[requestID.uuidString] ?? 0
|
||||
// 判断是否需要重试,可以基于错误类型或请求状态码来决定
|
||||
if currentRetryCount < maximumRetryCount, let response = request.task?.response as? HTTPURLResponse, response.statusCode == 503 {
|
||||
// 更新请求的重试次数
|
||||
retryCounts[requestID.uuidString] = currentRetryCount + 1
|
||||
// 在1秒后重试此请求
|
||||
completion(.retryWithDelay(1))
|
||||
print("请求\(requestID.uuidString)执行重复请求")
|
||||
} else {
|
||||
// 不重试,清除此请求的重试记录
|
||||
retryCounts[requestID.uuidString] = nil
|
||||
// 最大重试次数
|
||||
private let maxRetryCount: Int = 3
|
||||
// 重试间隔时间
|
||||
private let retryInterval: TimeInterval = 1.0
|
||||
//重试策略
|
||||
func retry(_ request: Request, for session: Session, dueTo error: any Error, completion: @escaping (RetryResult) -> Void) {
|
||||
// 检查请求是否具有URL以便于在字典中进行追踪
|
||||
guard let url = request.request?.url?.absoluteString else {
|
||||
completion(.doNotRetry)
|
||||
return
|
||||
}
|
||||
// 获取这个 URL 当前的重试次数
|
||||
let currentRetryCount = retryCounts[url] ?? 0
|
||||
|
||||
// 如果未超过最大重试次数,那么决定重试
|
||||
if currentRetryCount < maxRetryCount {
|
||||
// 更新重试次数
|
||||
retryCounts[url] = currentRetryCount + 1
|
||||
print("进行对\(url)访问的第\(currentRetryCount)次重试")
|
||||
// 告诉 Alamofire 重试并包含间隔时间
|
||||
completion(.retryWithDelay(retryInterval))
|
||||
} else {
|
||||
// 如果达到最大重试次数,则不再重试
|
||||
completion(.doNotRetry)
|
||||
print("访问\(url)失败,不在进行重试")
|
||||
// // 移除追踪记录
|
||||
// retryCounts[url] = nil
|
||||
}
|
||||
}
|
||||
// 在请求成功完成时,清除重试计数
|
||||
func requestDidFinish(_ request: Request) {
|
||||
let requestID = request.id
|
||||
retryCounts[requestID.uuidString] = nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import AVFoundation
|
||||
import MediaPlayer
|
||||
import AVKit
|
||||
import FreeStreamer
|
||||
import Kingfisher
|
||||
///播放器播放状态
|
||||
enum MP_PlayerStateType:Int {
|
||||
///未启动
|
||||
@ -28,6 +29,13 @@ enum MP_PlayerPlayType:Int {
|
||||
///单曲播放(当前音乐无限循环)
|
||||
case single = 2
|
||||
}
|
||||
///计时器状态
|
||||
enum MP_TimerStateType:Int {
|
||||
///运行
|
||||
case Resume = 0
|
||||
///暂停
|
||||
case Suspend = 1
|
||||
}
|
||||
|
||||
///播放器启动时执行事件(播放的音乐)
|
||||
typealias MP_PlayTimerStartAction = () -> Void
|
||||
@ -51,6 +59,14 @@ class MP_PlayerManager:NSObject{
|
||||
static let shared = MP_PlayerManager()
|
||||
///播放器
|
||||
private var player:AVPlayer = AVPlayer()
|
||||
//远程控制中心
|
||||
private var center:MPRemoteCommandCenter?
|
||||
///延迟计时器
|
||||
private var timer:DispatchSourceTimer?
|
||||
///延迟计时值
|
||||
private var times:TimeInterval = 0
|
||||
///计时器队列
|
||||
private var queue:DispatchQueue?
|
||||
///load模块
|
||||
var loadPlayer:MPPositive_PlayerLoadViewModel!{
|
||||
didSet{
|
||||
@ -94,6 +110,8 @@ class MP_PlayerManager:NSObject{
|
||||
|
||||
}
|
||||
}
|
||||
///计时器状态
|
||||
private var timerType:MP_TimerStateType = .Suspend
|
||||
///播放器启动时执行事件记录
|
||||
private var startActionBlock:MP_PlayTimerStartAction!
|
||||
///播放器运行时执行事件记录
|
||||
@ -102,12 +120,50 @@ class MP_PlayerManager:NSObject{
|
||||
var cacheValueBlock:MP_PlayCacheValueAction!
|
||||
private override init() {
|
||||
super.init()
|
||||
//初始化计时器
|
||||
timer?.cancel()
|
||||
queue = DispatchQueue(label: "com.playerTimer.timer",attributes: .concurrent)
|
||||
timer = DispatchSource.makeTimerSource(queue: queue)
|
||||
//设置计时器每0.1秒触发一次,误差在0.01秒
|
||||
timer?.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(10))
|
||||
// 设置计时器触发时执行的回调
|
||||
timer?.setEventHandler(handler: { [weak self] in
|
||||
//将计时值增加0.1秒
|
||||
self?.times += 0.1
|
||||
})
|
||||
// 添加观察者,监听播放结束事件
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
|
||||
//监听用户切换了音乐
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload)
|
||||
//监听网络状态恢复可用
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_ :)), notificationName: .net_switch_reachable)
|
||||
//设置一个秒为刻度的时间值
|
||||
let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1))
|
||||
//为播放器添加运行时主线程每秒触发事件
|
||||
player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in
|
||||
guard let self = self else { return }
|
||||
cacheLoadTimes()
|
||||
//转化为当前播放进度秒值
|
||||
let currentDuration = CMTimeGetSeconds(time)
|
||||
//获取当前播放音乐资源的最大时间值
|
||||
let maxDuration = getMusicDuration()
|
||||
if maxDuration.isNaN == false {
|
||||
//判断当值进度是否超越最大时间值
|
||||
if currentDuration <= maxDuration {
|
||||
//没有,执行运行时时间
|
||||
if runActionBlock != nil {
|
||||
runActionBlock!(currentDuration, maxDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
center?.playCommand.removeTarget(self)
|
||||
center?.pauseCommand.removeTarget(self)
|
||||
center?.nextTrackCommand.removeTarget(self)
|
||||
center?.previousTrackCommand.removeTarget(self)
|
||||
}
|
||||
/// 开始播放音乐
|
||||
/// - Parameters:
|
||||
@ -133,35 +189,62 @@ class MP_PlayerManager:NSObject{
|
||||
if startAction != nil {
|
||||
startActionBlock = startAction
|
||||
}
|
||||
//启动播放器
|
||||
let newItem = loadPlayer.currentVideo.resourcePlayerItem
|
||||
//覆盖播放器原有的playerItem
|
||||
player.replaceCurrentItem(with: loadPlayer.currentVideo.resourcePlayerItem)
|
||||
//将进度回归为0
|
||||
player.seek(to: .zero)
|
||||
//设置一个秒为刻度的时间值
|
||||
let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1))
|
||||
//为播放器添加运行时主线程每秒触发事件
|
||||
player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in
|
||||
guard let self = self else { return }
|
||||
//转化为当前播放进度秒值
|
||||
let currentDuration = CMTimeGetSeconds(time)
|
||||
//获取当前播放音乐资源的最大时间值
|
||||
let maxDuration = getMusicDuration()
|
||||
if maxDuration.isNaN == false {
|
||||
//判断当值进度是否超越最大时间值
|
||||
if currentDuration <= maxDuration {
|
||||
//没有,执行运行时时间
|
||||
if runActionBlock != nil {
|
||||
runActionBlock!(currentDuration, maxDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
player.replaceCurrentItem(with: newItem)
|
||||
if center == nil {
|
||||
setCommandCenter()
|
||||
}
|
||||
//启动计时器
|
||||
startTimer()
|
||||
//对当前播放PlayerItem设置监听状态
|
||||
//准备状态
|
||||
loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil)
|
||||
//当前缓冲值
|
||||
loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil)
|
||||
if loadPlayer.currentVideo?.isKVO == false {
|
||||
//准备状态
|
||||
newItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil)
|
||||
//当前缓冲值
|
||||
newItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil)
|
||||
//是否具备足够播放的能力
|
||||
newItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: [.old,.new], context: nil)
|
||||
loadPlayer.currentVideo.isKVO = true
|
||||
//将进度回归为0
|
||||
player.seek(to: .zero)
|
||||
updateNowPlayingInfo()
|
||||
}
|
||||
}
|
||||
///启动计时器
|
||||
func startTimer() {
|
||||
guard timerType == .Suspend else {
|
||||
return
|
||||
}
|
||||
times = 0
|
||||
//运行计时器
|
||||
timer?.resume()
|
||||
timerType = .Resume
|
||||
}
|
||||
///暂停计时器
|
||||
func suspendTimer() {
|
||||
guard timerType == .Resume else {
|
||||
return
|
||||
}
|
||||
//暂停计时器
|
||||
timer?.suspend()
|
||||
timerType = .Suspend
|
||||
}
|
||||
|
||||
///网络状态恢复正常
|
||||
@objc private func netWorkReachableAction(_ sender:Notification) {
|
||||
//监听到网络状态恢复,检索当前播放器是否正在播放
|
||||
if loadPlayer?.currentVideo != nil {
|
||||
//有音乐播放,获取当前播放进度
|
||||
let currentTime = loadPlayer.currentVideo!.resourcePlayerItem.currentTime()
|
||||
//手动调整播放时间点,以此重启播放器缓存
|
||||
player.seek(to: currentTime)
|
||||
player.play()
|
||||
playState = .Playing
|
||||
}
|
||||
}
|
||||
|
||||
//实现KVO监听
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
guard let keyPath = keyPath else {
|
||||
@ -175,48 +258,58 @@ class MP_PlayerManager:NSObject{
|
||||
if playState != .Playing {
|
||||
//当statuVlaue值等于playerItem准备播放的值,说明已经准备好播放
|
||||
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放")
|
||||
}
|
||||
}else {
|
||||
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")")
|
||||
//重新
|
||||
play()
|
||||
}
|
||||
case "loadedTimeRanges"://当前缓冲进度
|
||||
cacheLoadTimes()
|
||||
case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放
|
||||
if let playbackLikelyToKeepUp = change?[.newKey] as? Bool, playbackLikelyToKeepUp == true {
|
||||
if playState != .Playing {
|
||||
//还未播放当前音乐,启动播放
|
||||
print("开始播放音乐-\(loadPlayer.currentVideo?.title ?? "")")
|
||||
player.play()
|
||||
playState = .Playing
|
||||
//暂停计时器,并获取延时值
|
||||
suspendTimer()
|
||||
let times = Int(self.times)
|
||||
let msTimes = times*1000
|
||||
MP_AnalyticsManager.shared.player_b_delay_actionAction(loadPlayer.currentVideo?.song.videoId ?? "", videoname: loadPlayer.currentVideo?.title ?? "", artistname: loadPlayer.currentVideo?.song.shortBylineText ?? "", delay: "\(msTimes)ms")
|
||||
MP_AnalyticsManager.shared.player_b_success_actionAction(loadPlayer.currentVideo?.song.videoId ?? "", videoname: loadPlayer.currentVideo?.title ?? "", artistname: loadPlayer.currentVideo?.song.shortBylineText ?? "")
|
||||
//执行开始播放闭包
|
||||
if startActionBlock != nil {
|
||||
startActionBlock!()
|
||||
}
|
||||
}
|
||||
}else {
|
||||
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")")
|
||||
//资源更新,重新配置一下相关内容
|
||||
loadPlayer.remakeImproveData {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//重新播放
|
||||
play()
|
||||
}
|
||||
//没有足够的数据支持播放
|
||||
player.pause()
|
||||
playState = .Null
|
||||
}
|
||||
case "loadedTimeRanges"://当前缓冲进度
|
||||
//获取当前播放Item的缓冲值组
|
||||
if let timeRanges = loadPlayer.currentVideo?.resourcePlayerItem.loadedTimeRanges.map({$0.timeRangeValue}), let first = timeRanges.first {
|
||||
//获取开始时间的秒数
|
||||
let startSeconds = first.start.seconds
|
||||
//获取缓冲区的持续时间
|
||||
let durationSeconds = first.duration.seconds
|
||||
//计算当前缓冲总时间
|
||||
let bufferedSeconds = startSeconds + durationSeconds
|
||||
//获取当前播放音乐资源的最大时间值
|
||||
let maxDuration = getMusicDuration()
|
||||
//传递缓存值
|
||||
if cacheValueBlock != nil {
|
||||
cacheValueBlock!(bufferedSeconds, maxDuration)
|
||||
}
|
||||
}
|
||||
|
||||
case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
//获取缓冲值
|
||||
private func cacheLoadTimes() {
|
||||
//获取当前播放Item的缓冲值组
|
||||
if let timeRanges = loadPlayer.currentVideo?.resourcePlayerItem.loadedTimeRanges.map({$0.timeRangeValue}), let first = timeRanges.first {
|
||||
//获取开始时间的秒数
|
||||
let startSeconds = first.start.seconds
|
||||
//获取缓冲区的持续时间
|
||||
let durationSeconds = first.duration.seconds
|
||||
//计算当前缓冲总时间
|
||||
let bufferedSeconds = startSeconds + durationSeconds
|
||||
//获取当前播放音乐资源的最大时间值
|
||||
let maxDuration = getMusicDuration()
|
||||
//传递缓存值
|
||||
if cacheValueBlock != nil {
|
||||
cacheValueBlock!(bufferedSeconds, maxDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
//MARK: - 获取当前音乐总长度
|
||||
///获取音乐资源总时长
|
||||
private func getMusicDuration() -> TimeInterval {
|
||||
@ -325,7 +418,7 @@ class MP_PlayerManager:NSObject{
|
||||
switch playType {
|
||||
case .random://随机,播放随机列表内容
|
||||
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index - 1
|
||||
}
|
||||
@ -334,15 +427,15 @@ class MP_PlayerManager:NSObject{
|
||||
if nextIndex < 0 {
|
||||
//播放列表最后一首
|
||||
let last = loadPlayer.randomVideos.last
|
||||
loadPlayer.improveData(last?.videoId ?? "")
|
||||
loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
|
||||
}else {
|
||||
//查询列表对应单曲
|
||||
let song = loadPlayer.randomVideos[nextIndex]
|
||||
loadPlayer.improveData(song.videoId ?? "")
|
||||
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
|
||||
}
|
||||
default://常规播放或者单曲播放
|
||||
for (index, item) in loadPlayer.songVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index - 1
|
||||
}
|
||||
@ -367,7 +460,7 @@ class MP_PlayerManager:NSObject{
|
||||
switch playType {
|
||||
case .random:
|
||||
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index + 1
|
||||
}
|
||||
@ -376,15 +469,15 @@ class MP_PlayerManager:NSObject{
|
||||
if nextIndex > (loadPlayer.randomVideos.count-1) {
|
||||
//播放列表第一首
|
||||
let first = loadPlayer.randomVideos.first
|
||||
loadPlayer.improveData(first?.videoId ?? "")
|
||||
loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
|
||||
}else {
|
||||
//存在下一首,获取下一首ID,并播放
|
||||
let song = loadPlayer.randomVideos[nextIndex]
|
||||
loadPlayer.improveData(song.videoId ?? "")
|
||||
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
|
||||
}
|
||||
default:
|
||||
for (index, item) in loadPlayer.songVideos.enumerated() {
|
||||
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
||||
//找到播放音乐的索引
|
||||
nextIndex = index + 1
|
||||
}
|
||||
@ -409,10 +502,16 @@ class MP_PlayerManager:NSObject{
|
||||
player.pause()
|
||||
//优先获取传递的值
|
||||
if let video = sender.object as? MPPositive_SongViewModel {
|
||||
//切歌时移除KVO监听
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges")
|
||||
// video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
|
||||
if video.isKVO == true {
|
||||
//切歌时移除KVO监听
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges")
|
||||
video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
|
||||
video.isKVO = false
|
||||
}
|
||||
}
|
||||
if cacheValueBlock != nil {
|
||||
cacheValueBlock!(0, 1)
|
||||
}
|
||||
if loadPlayer.currentVideo != nil {
|
||||
//开始播放
|
||||
@ -450,4 +549,88 @@ class MP_PlayerManager:NSObject{
|
||||
endAction!()
|
||||
}
|
||||
}
|
||||
//MARK: - 远程中心
|
||||
private func setCommandCenter() {
|
||||
// 实例化远程控制中心
|
||||
center = MPRemoteCommandCenter.shared()
|
||||
//设置控制中心各项操作
|
||||
//播放
|
||||
center!.playCommand.addTarget(handler: { [weak self] (event) in
|
||||
guard let self = self else { return .noActionableNowPlayingItem}
|
||||
if loadPlayer.currentVideo != nil {
|
||||
resume()
|
||||
return .success
|
||||
}else {
|
||||
return .noActionableNowPlayingItem
|
||||
}
|
||||
})
|
||||
//暂停
|
||||
center!.pauseCommand.addTarget(handler: { [weak self] (event) in
|
||||
guard let self = self else { return .noActionableNowPlayingItem}
|
||||
if loadPlayer.currentVideo != nil {
|
||||
pause()
|
||||
return .success
|
||||
}else {
|
||||
return .noActionableNowPlayingItem
|
||||
}
|
||||
})
|
||||
//上一首
|
||||
center!.previousTrackCommand.addTarget { [weak self] (event) in
|
||||
guard let self = self else { return .noActionableNowPlayingItem}
|
||||
if loadPlayer.currentVideo != nil {
|
||||
previousEvent()
|
||||
return .success
|
||||
}else {
|
||||
return .noActionableNowPlayingItem
|
||||
}
|
||||
}
|
||||
//下一首歌
|
||||
center!.nextTrackCommand.addTarget { [weak self] (event) in
|
||||
guard let self = self else { return .noActionableNowPlayingItem}
|
||||
if loadPlayer.currentVideo != nil {
|
||||
nextEvent()
|
||||
return .success
|
||||
}else {
|
||||
return .noActionableNowPlayingItem
|
||||
}
|
||||
}
|
||||
}
|
||||
//设置远程中心内容更新
|
||||
func updateNowPlayingInfo() {
|
||||
//设置info字典信息
|
||||
var info = [String:Any]()
|
||||
//展示标题
|
||||
info[MPMediaItemPropertyTitle] = loadPlayer.currentVideo?.title ?? ""
|
||||
//设置艺术家
|
||||
info[MPMediaItemPropertyArtist] = loadPlayer.currentVideo?.song?.shortBylineText ?? ""
|
||||
//设置专辑
|
||||
info[MPMediaItemPropertyAlbumTitle] = loadPlayer.currentVideo?.song?.longBylineText
|
||||
//设置歌曲时长
|
||||
info[MPMediaItemPropertyPlaybackDuration] = getMusicDuration()
|
||||
let reviewURL = URL(string: loadPlayer.currentVideo?.song?.reviewUrls?.last ?? "")!
|
||||
KingfisherManager.shared.retrieveImage(with: reviewURL) { result in
|
||||
switch result {
|
||||
case .success(let imageResult):
|
||||
let image = imageResult.image
|
||||
info[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { size in
|
||||
return image
|
||||
})
|
||||
// 确保更新MPNowPlayingInfoCenter的操作在主线程中
|
||||
DispatchQueue.main.async {
|
||||
//更新远程中心
|
||||
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
|
||||
}
|
||||
case .failure(_):
|
||||
info[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: placeholderImage.size, requestHandler: { size in
|
||||
return placeholderImage
|
||||
})
|
||||
// 确保更新MPNowPlayingInfoCenter的操作在主线程中
|
||||
DispatchQueue.main.async {
|
||||
//更新远程中心
|
||||
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ class MP_WebWork:NSObject {
|
||||
self.jsPath = self.jsPath + matchedString
|
||||
print("Current base.JavaScript path:\(self.jsPath)")
|
||||
//开始获取首页内容
|
||||
MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists()
|
||||
// MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists()
|
||||
//切换地址
|
||||
if let baseUrl = URL(string: self.jsPath) {
|
||||
let request = URLRequest(url: baseUrl)
|
||||
@ -177,7 +177,7 @@ extension MP_WebWork: WKNavigationDelegate, WKUIDelegate {
|
||||
}else {
|
||||
print("注入代码完成")
|
||||
//发布切换启动页
|
||||
NotificationCenter.notificationKey.post(notificationName: .js_edit_completion)
|
||||
// NotificationCenter.notificationKey.post(notificationName: .js_edit_completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,38 @@
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
///IP信息结构
|
||||
struct JsonIPInfo: Codable {
|
||||
let data:Datas?
|
||||
let message:String?
|
||||
let status:String?
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data = "data"
|
||||
case message = "message"
|
||||
case status = "status"
|
||||
}
|
||||
init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
data = try values.decodeIfPresent(Datas.self, forKey: .data)
|
||||
message = try values.decodeIfPresent(String.self, forKey: .message)
|
||||
status = try values.decodeIfPresent(String.self, forKey: .status)
|
||||
}
|
||||
struct Datas: Codable {
|
||||
let isoCode:String?
|
||||
let ip:String?
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case isoCode = "isoCode"
|
||||
case ip = "ip"
|
||||
}
|
||||
init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
isoCode = try values.decodeIfPresent(String.self, forKey: .isoCode)
|
||||
ip = try values.decodeIfPresent(String.self, forKey: .ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///预览结构体
|
||||
struct JsonBrowses: Codable {
|
||||
///获取访问数据
|
||||
|
||||
@ -18,7 +18,7 @@ class MPPositive_DownloadItemModel: NSManagedObject, MP_CoreDataManageableDelega
|
||||
///标题(单曲标题)
|
||||
@NSManaged var title:String?
|
||||
///长文本标题(作者/播放次数/点赞次数)
|
||||
@NSManaged var longBylineText:String?
|
||||
@NSManaged var longBylineText:String?
|
||||
///单曲长度文本(歌曲长度)
|
||||
@NSManaged var lengthText:String?
|
||||
///署名文本(歌手)
|
||||
|
||||
@ -32,6 +32,10 @@ class MPPositive_SongViewModel: NSObject {
|
||||
var isCollection:Bool?
|
||||
///是否下载
|
||||
var isDlownd:Bool?
|
||||
///是否被KVO监听
|
||||
var isKVO:Bool = false
|
||||
///是否完成了预加载
|
||||
var isPreload:Bool = false
|
||||
///音乐实体
|
||||
var song:MPPositive_SongItemModel!
|
||||
init(_ song:MPPositive_SongItemModel) {
|
||||
@ -43,12 +47,20 @@ class MPPositive_SongViewModel: NSObject {
|
||||
resourcePlayerItem = nil
|
||||
resourcePlayerAsset = nil
|
||||
resourcePlayerURL = nil
|
||||
isKVO = false
|
||||
}
|
||||
//数据配置
|
||||
func configure() {
|
||||
reloadCollectionAndDownLoad()
|
||||
index = song.index
|
||||
|
||||
//标题
|
||||
if song.title != nil {
|
||||
title = song.title!
|
||||
}
|
||||
//副标题(作者/专辑)
|
||||
if song.shortBylineText != nil {
|
||||
subtitle = song.shortBylineText!
|
||||
}
|
||||
if let first = song.resourceUrls?.first {
|
||||
resourcePlayerURL = .init(string:first)
|
||||
resourcePlayerAsset = .init(url: resourcePlayerURL)
|
||||
@ -60,14 +72,6 @@ class MPPositive_SongViewModel: NSObject {
|
||||
if song.reviewUrls?.first != nil {
|
||||
coverUrl = .init(string: song.reviewUrls!.last!)
|
||||
}
|
||||
//标题
|
||||
if song.title != nil {
|
||||
title = song.title!
|
||||
}
|
||||
//副标题(作者/专辑)
|
||||
if song.shortBylineText != nil {
|
||||
subtitle = song.shortBylineText!
|
||||
}
|
||||
//歌词
|
||||
if song.lyrics != nil {
|
||||
lyrics = song.lyrics
|
||||
@ -95,6 +99,10 @@ class MPPositive_SongViewModel: NSObject {
|
||||
}
|
||||
//执行预加载
|
||||
func preloadAsset(_ asset:AVURLAsset) {
|
||||
guard isPreload == false else {
|
||||
return
|
||||
}
|
||||
print("\(title ?? "")开始预加载")
|
||||
//执行预加载
|
||||
if #available(iOS 16, *) {
|
||||
//ios16以上的情况
|
||||
@ -103,11 +111,13 @@ class MPPositive_SongViewModel: NSObject {
|
||||
let playable = try await asset.load(.isPlayable)
|
||||
if playable == true {
|
||||
print("\(self.title ?? "")预加载成功")
|
||||
isPreload = true
|
||||
}else {
|
||||
//检索预加载失败原因
|
||||
switch asset.status(of: .isPlayable) {
|
||||
case .failed(let erro):
|
||||
print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)")
|
||||
preloadAsset(asset)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -130,9 +140,11 @@ class MPPositive_SongViewModel: NSObject {
|
||||
// key成功加载,资源准备就绪
|
||||
DispatchQueue.main.async {
|
||||
print("\(self.title ?? "")预加载成功")
|
||||
self.isPreload = true
|
||||
}
|
||||
case .failed:
|
||||
print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")")
|
||||
preloadAsset(asset)
|
||||
case .cancelled:
|
||||
print("\(title ?? "")预加载被取消了")
|
||||
default:
|
||||
|
||||
@ -23,6 +23,10 @@ class MPPositive_BrowseLoadViewModel: NSObject {
|
||||
browseModuleLists = browseModuleLists.filter{($0.items.count != 0)}
|
||||
//通知首页刷新UI
|
||||
NotificationCenter.notificationKey.post(notificationName: .positive_browses_reload)
|
||||
if isCompleted == true {
|
||||
//加载完毕后
|
||||
MP_AnalyticsManager.shared.home_b_module_showsucces_actionAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
///刷新预览数据
|
||||
|
||||
@ -15,13 +15,18 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
///当前播放音乐ViewModel
|
||||
var currentVideo:MPPositive_SongViewModel!{
|
||||
willSet{
|
||||
if newValue != nil {
|
||||
if currentVideo != nil {
|
||||
//当值变化时通知播放器页面,更新UI
|
||||
NotificationCenter.notificationKey.post(notificationName: .positive_player_reload, object: currentVideo)
|
||||
}else {
|
||||
//当值变化时通知播放器页面,更新UI
|
||||
NotificationCenter.notificationKey.post(notificationName: .positive_player_reload)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if newValue != nil {
|
||||
MP_AnalyticsManager.shared.player_b_pvAction(newValue.song.videoId, videoname: newValue.title ?? "", artistname: newValue.song.shortBylineText ?? "")
|
||||
if currentVideo != nil {
|
||||
//当值变化时通知播放器页面,更新UI
|
||||
NotificationCenter.notificationKey.post(notificationName: .positive_player_reload, object: currentVideo)
|
||||
}else {
|
||||
//当值变化时通知播放器页面,更新UI
|
||||
NotificationCenter.notificationKey.post(notificationName: .positive_player_reload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,7 +48,7 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
self.listViewVideos = []
|
||||
}
|
||||
|
||||
///将选中Video的上下1项包括本身总计3项Video进行补全转为ViewModel,并播放这首音乐
|
||||
///将选中Video的上1位,下两位项包括本身总计4项Video进行补全转为ViewModel,并播放这首音乐
|
||||
func improveData(_ targetVideoId:String, isRandom:Bool = false) {
|
||||
//对于选中Video的集合
|
||||
var array:[MPPositive_SongItemModel] = []
|
||||
@ -59,9 +64,13 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
array.append(self.randomVideos[previousIndex])
|
||||
}
|
||||
let nextIndex = targetIndex+1
|
||||
let lastIndex = targetIndex+2
|
||||
if nextIndex < randomVideos.count {
|
||||
array.append(self.randomVideos[nextIndex])
|
||||
}
|
||||
if lastIndex < randomVideos.count {
|
||||
array.append(self.randomVideos[lastIndex])
|
||||
}
|
||||
}else {
|
||||
//获取targetVideoId的索引
|
||||
guard let targetIndex = self.songVideos.firstIndex(where: {$0.videoId == targetVideoId}) else {
|
||||
@ -74,9 +83,13 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
array.append(self.songVideos[previousIndex])
|
||||
}
|
||||
let nextIndex = targetIndex+1
|
||||
let lastIndex = targetIndex+2
|
||||
if nextIndex < songVideos.count {
|
||||
array.append(self.songVideos[nextIndex])
|
||||
}
|
||||
if lastIndex < songVideos.count {
|
||||
array.append(self.songVideos[lastIndex])
|
||||
}
|
||||
}
|
||||
//获取完成,优先检索ViewModel,看看是否已存在补完video
|
||||
let videoIDs = Set(listViewVideos.map({$0.song.videoId}))
|
||||
@ -123,8 +136,8 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
[weak self] in
|
||||
//确定播放音乐
|
||||
self?.currentVideo = self?.listViewVideos.first(where: {$0.song.videoId == targetVideoId})
|
||||
//只保留最后三首
|
||||
self?.listViewVideos = self?.listViewVideos.suffix(3)
|
||||
//只保留最后四首
|
||||
self?.listViewVideos = self?.listViewVideos.suffix(4)
|
||||
self?.group = nil
|
||||
})
|
||||
}
|
||||
@ -180,19 +193,4 @@ class MPPositive_PlayerLoadViewModel: NSObject {
|
||||
private func findVideoIdForDocument(_ videoId:String) -> Bool {
|
||||
return MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", videoId)).count != 0
|
||||
}
|
||||
|
||||
///调用next对单曲数据歌词ID与相关ID补全
|
||||
private func improveDataforLycirsAndRelated(_ song:MPPositive_SongItemModel, completion:@escaping(((String?,String?)) -> Void)) {
|
||||
//单曲补全需要再次调用next接口
|
||||
MP_NetWorkManager.shared.requestNextLyricsAndRelated(song){ result in
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
///调用player对资源路径和封面路径补全
|
||||
private func improveDataforResouceAndCover(_ song:MPPositive_SongItemModel, completion:@escaping((([String],[Int],[String]), [String]?) -> Void)) {
|
||||
//单曲补全需要调用player接口
|
||||
MP_NetWorkManager.shared.requestAndroidPlayer(song.videoId, playlistId: "") { resourceUrls, coverUrls in
|
||||
completion(resourceUrls,coverUrls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ class MPPositive_SearchResultsLoadViewModel: NSObject {
|
||||
}
|
||||
//根据用户输入文本内容请求搜索接口
|
||||
private func getSearchResults(_ text:String) {
|
||||
MP_HUD.loading()
|
||||
//同时新增一个搜索历史
|
||||
let tag = MPPositive_SearchTagModel.create()
|
||||
tag.date = Date().timeZone()
|
||||
|
||||
@ -17,12 +17,30 @@ class MPPositive_BaseViewController: MP_BaseViewController {
|
||||
btn.addTarget(self, action: #selector(popActionClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
//网络加载失败的View
|
||||
private lazy var errorView:UIView = createErrorView()
|
||||
///报错回调
|
||||
var errorBlock:(() -> Void)?
|
||||
///重试回调
|
||||
var retryBlock:(() -> Void)?
|
||||
|
||||
//导航View
|
||||
lazy var navView:UIView = setTitleView()
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.addSubview(navView)
|
||||
}
|
||||
deinit {
|
||||
}
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(errorAction(_ :)), notificationName: .netWork_error_deal)
|
||||
}
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
//设置顶部titleView
|
||||
private func setTitleView() -> UIView {
|
||||
let topView = UIView(frame: .init(x: 0, y: statusBarHeight, width: screen_Width, height: 50*width))
|
||||
@ -55,4 +73,78 @@ class MPPositive_BaseViewController: MP_BaseViewController {
|
||||
@objc private func popActionClick(_ sender:UIButton) {
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
//报错errorView
|
||||
private func createErrorView() -> UIView{
|
||||
let errorView = UIView()
|
||||
errorView.layer.masksToBounds = true
|
||||
errorView.layer.borderColor = UIColor(hex: "#80F988").cgColor
|
||||
errorView.layer.borderWidth = 1*width
|
||||
errorView.layer.cornerRadius = 18*width
|
||||
errorView.backgroundColor = .clear
|
||||
//使用其他内容
|
||||
//一个提示框
|
||||
let iconImageView = UIImageView(image: .init(named: "empty"))
|
||||
errorView.addSubview(iconImageView)
|
||||
iconImageView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.centerY.equalToSuperview().multipliedBy(0.65)
|
||||
make.width.equalTo(211*width)
|
||||
make.height.equalTo(172*width)
|
||||
}
|
||||
let label = createLabel("Failure to obtain data", font: .systemFont(ofSize: 13*width, weight: .regular), textColor: .white, textAlignment: .center)
|
||||
errorView.addSubview(label)
|
||||
label.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(iconImageView.snp.bottom).offset(8*width)
|
||||
}
|
||||
let retryBtn:UIButton = .init()
|
||||
retryBtn.setTitle("Retry", for: .normal)
|
||||
retryBtn.setTitleColor(.white, for: .normal)
|
||||
retryBtn.titleLabel?.font = .systemFont(ofSize: 15*width, weight: .medium)
|
||||
retryBtn.backgroundColor = .init(hex: "#80F988")
|
||||
retryBtn.layer.masksToBounds = true
|
||||
retryBtn.layer.cornerRadius = 16*width
|
||||
retryBtn.addTarget(self, action: #selector(retryClick(_ :)), for: .touchUpInside)
|
||||
errorView.addSubview(retryBtn)
|
||||
retryBtn.snp.makeConstraints { make in
|
||||
make.width.equalTo(180*width)
|
||||
make.height.equalTo(45*width)
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalToSuperview().offset(-15*width)
|
||||
}
|
||||
return errorView
|
||||
}
|
||||
//设置报错View
|
||||
func setErrorView() {
|
||||
view.addSubview(errorView)
|
||||
errorView.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
make.width.equalTo(300*width)
|
||||
make.height.equalTo(280*width)
|
||||
}
|
||||
}
|
||||
//移除报错View
|
||||
func removeErrorView() {
|
||||
if errorView.superview != nil {
|
||||
errorView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
//处理报错
|
||||
@objc private func errorAction(_ sender:Notification){
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MP_HUD.text("Failure to obtain data", delay: 1.0){ [weak self] in
|
||||
//执行报错回调闭包
|
||||
if self?.errorBlock != nil {
|
||||
self?.errorBlock!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@objc private func retryClick(_ sender:UIButton) {
|
||||
if retryBlock != nil {
|
||||
retryBlock!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,15 +46,14 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
|
||||
let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain)
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.separatorStyle = .none
|
||||
tableView.contentInset = .init(top: 0, left: 0, bottom: bottomPadding, right: 0)
|
||||
tableView.estimatedRowHeight = 200
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationTableViewCellID)
|
||||
tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID)
|
||||
return tableView
|
||||
}()
|
||||
private let MPPositive_MoreOperationTableViewCellID = "MPPositive_MoreOperationTableViewCell"
|
||||
private let MPPositive_MoreOperationDownLoadTableViewCellID = "MPPositive_MoreOperationDownLoadTableViewCell"
|
||||
private var song:MPPositive_SongItemModel!{
|
||||
didSet{
|
||||
iconImageView.kf.setImage(with: URL(string: song.reviewUrls?.last ?? ""), placeholder: placeholderImage)
|
||||
@ -74,9 +73,13 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
}
|
||||
var removeBlock:(() -> Void)?
|
||||
init(_ song:MPPositive_SongItemModel) {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
self.song = song
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
self?.song = song
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -87,7 +90,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .init(hex: "#282A2C")
|
||||
view.layer.masksToBounds = true
|
||||
view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMinXMaxYCorner]
|
||||
view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]
|
||||
view.layer.cornerRadius = 18*width
|
||||
configure()
|
||||
}
|
||||
@ -146,6 +149,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
|
||||
MP_AnalyticsManager.shared.player_b_unlove_clickAction(song.videoId, videoname: song.title ?? "", artistname: song.shortBylineText ?? "")
|
||||
}else{
|
||||
self.collectionBtn.isSelected = true
|
||||
let item = MPPositive_CollectionSongModel.create()
|
||||
@ -157,6 +161,7 @@ class MPPositive_MoreSongOperationsViewController: UIViewController {
|
||||
item.relatedID = song.relatedID
|
||||
MPPositive_CollectionSongModel.save()
|
||||
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
|
||||
MP_AnalyticsManager.shared.player_b_love_clickAction(song.videoId, videoname: song.title ?? "", artistname: song.shortBylineText ?? "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,8 +171,14 @@ extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UIT
|
||||
return 1
|
||||
}
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell
|
||||
cell.title = isLoaded ? "Remove Download":"Add Download"
|
||||
cell.restoreDownloadProgress(song)
|
||||
cell.progressBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
tableView.reloadRows(at: [.init(item: 0, section: 0)], with: .none)
|
||||
}
|
||||
return cell
|
||||
}
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
@ -180,16 +191,95 @@ extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UIT
|
||||
}
|
||||
}
|
||||
MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
|
||||
DownloadManager.shared.deleteFileDocuments(song.videoId) { [weak self] videoId in
|
||||
MP_DownloadManager.shared.deleteFileDocuments(song.videoId) { [weak self] videoId in
|
||||
guard let self = self else {return}
|
||||
MP_HUD.progress("Loading...", delay: 0.5) {
|
||||
self.isLoaded = false
|
||||
MP_HUD.text("Removed", delay: 1.0, completion: nil)
|
||||
if self.removeBlock != nil {
|
||||
self.removeBlock!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
//进行下载
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationDownLoadTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell
|
||||
//判断当前下载状态
|
||||
switch cell.loadBtn.state {
|
||||
case .startDownload://未下载
|
||||
MP_AnalyticsManager.shared.player_b_download_clickAction(song.videoId ?? "", videoname: song.title ?? "", artistname: song.shortBylineText ?? "")
|
||||
//切换为准备状态
|
||||
cell.loadBtn.state = .pending
|
||||
tableView.reloadData()
|
||||
//当开始下载时
|
||||
guard let videoURL = URL(string: song.resourceUrls?.first ?? "") else {
|
||||
MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.0) {[weak self] in
|
||||
cell.loadBtn.state = .startDownload
|
||||
self?.isLoaded = false
|
||||
}
|
||||
return
|
||||
}
|
||||
//执行下载
|
||||
MP_DownloadManager.shared.downloadVideo(from: videoURL, song: song) { [weak self] progress in
|
||||
DispatchQueue.main.async {
|
||||
guard let self = self else {
|
||||
//不是同一个
|
||||
cell.loadBtn.state = (MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", (self?.song.videoId ?? ""))).count != 0) ? .downloaded:.startDownload
|
||||
tableView.reloadData()
|
||||
return
|
||||
}
|
||||
cell.loadBtn.state = .downloading
|
||||
cell.loadBtn.stopDownloadButton.progress = progress
|
||||
tableView.reloadData()
|
||||
}
|
||||
} completion: { [weak self] result in
|
||||
//下载结束,判断成功或失败
|
||||
switch result {
|
||||
case .success(let song):
|
||||
//添加数据
|
||||
let item = MPPositive_DownloadItemModel.create()
|
||||
item.coverImage = song.coverUrls!.last
|
||||
item.reviewImage = song.reviewUrls!.last
|
||||
item.title = song.title
|
||||
item.longBylineText = song.longBylineText
|
||||
item.lengthText = song.lengthText
|
||||
item.shortBylineText = song.shortBylineText
|
||||
item.lyrics = song.lyrics
|
||||
item.lyricsID = song.lyricsID
|
||||
item.videoId = song.videoId
|
||||
item.relatedID = song.relatedID
|
||||
MPPositive_DownloadItemModel.save()
|
||||
DispatchQueue.main.async {
|
||||
//回归主线程,判断下载的是否为当前歌曲
|
||||
if song.videoId == self?.song.videoId {
|
||||
//是当前这首,刷新一下当前播放音乐的下载状态
|
||||
self?.isLoaded = true
|
||||
}else {
|
||||
//不是这首,那就不管他
|
||||
self?.isLoaded = false
|
||||
}
|
||||
print("完成了对\(song.title ?? "")的下载")
|
||||
//按钮变为下载结束状态
|
||||
cell.loadBtn.state = .downloaded
|
||||
tableView.reloadData()
|
||||
//更新数据库管理类
|
||||
MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
|
||||
MP_AnalyticsManager.shared.player_b_downloadsuccess_actionAction(item.videoId, videoname: item.title ?? "", artistname: item.shortBylineText ?? "")
|
||||
}
|
||||
case .failure(let error):
|
||||
//失败了,打印错误
|
||||
print("下载报错,错误详情\(error)")
|
||||
DispatchQueue.main.async {
|
||||
//按钮回归可用状态
|
||||
cell.loadBtn.state = .startDownload
|
||||
tableView.reloadData()
|
||||
}
|
||||
MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +66,10 @@ class MPPositive_LibraryViewController: MPPositive_BaseViewController {
|
||||
super.viewWillAppear(animated)
|
||||
reload()
|
||||
}
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
MP_AnalyticsManager.shared.me_b_pvAction()
|
||||
}
|
||||
//刷新列表
|
||||
private func reload() {
|
||||
MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class MPPositive_LoveSongsViewController: MPPositive_BaseViewController {
|
||||
class MPPositive_LoveSongsViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate {
|
||||
private lazy var numbersLabel:UILabel = createLabel(font: .systemFont(ofSize: 18*width, weight: .regular), textColor: .white, textAlignment: .left)
|
||||
///tableView
|
||||
private lazy var tableView:UITableView = {
|
||||
@ -67,6 +67,39 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
|
||||
cell.songViewModel = MPPositive_LoadCoreModel.shared.songViewModels[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MPPositive_Debouncer.shared.call {
|
||||
MP_NetWorkManager.shared.requestNextList("", videoId: MPPositive_LoadCoreModel.shared.songViewModels[indexPath.row].collectionSong.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let first = listSongs.first else {return}
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
improveDataforLycirsAndRelated(first) {[weak self] (result) in
|
||||
first.lyricsID = result.0
|
||||
first.relatedID = result.1
|
||||
group.leave()
|
||||
}
|
||||
group.enter()
|
||||
//补全资源路径组和封面路径组
|
||||
improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in
|
||||
first.resourceUrls = resourceUrls.0
|
||||
first.itags = resourceUrls.1
|
||||
first.mimeTypes = resourceUrls.2
|
||||
first.coverUrls = coverUrls
|
||||
group.leave()
|
||||
}
|
||||
group.notify(queue: .main, execute: {
|
||||
[weak self] in
|
||||
MPPositive_ModalType = .MoreOperations
|
||||
let moreVC = MPPositive_MoreSongOperationsViewController(first)
|
||||
moreVC.transitioningDelegate = self
|
||||
moreVC.modalPresentationStyle = .custom
|
||||
self?.present(moreVC, animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return cell
|
||||
}
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
@ -93,4 +126,7 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView
|
||||
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
|
||||
}
|
||||
}
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,13 +60,39 @@ class MPPositive_OfflineSongsViewController: MPPositive_BaseViewController {
|
||||
}
|
||||
}
|
||||
//MARK: - tableView
|
||||
extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableViewDelegate {
|
||||
extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableViewDelegate, UIViewControllerTransitioningDelegate {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return MPPositive_LoadCoreModel.shared.loadViewModels.count
|
||||
}
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
|
||||
cell.loadViewModel = MPPositive_LoadCoreModel.shared.loadViewModels[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MPPositive_Debouncer.shared.call {
|
||||
let item = MPPositive_LoadCoreModel.shared.loadViewModels[indexPath.row]
|
||||
let song = MPPositive_SongItemModel()
|
||||
song.coverUrls = [item.loadItem.coverImage]
|
||||
song.reviewUrls = [item.loadItem.reviewImage]
|
||||
song.title = item.loadItem.title
|
||||
song.longBylineText = item.loadItem.longBylineText
|
||||
song.lengthText = item.loadItem.lengthText
|
||||
song.shortBylineText = item.loadItem.shortBylineText
|
||||
song.lyrics = item.loadItem.lyrics
|
||||
song.lyricsID = item.loadItem.lyricsID
|
||||
song.videoId = item.loadItem.videoId
|
||||
song.relatedID = item.loadItem.relatedID
|
||||
MPPositive_ModalType = .MoreOperations
|
||||
let moreVC = MPPositive_MoreSongOperationsViewController(song)
|
||||
moreVC.removeBlock = {
|
||||
self.reload()
|
||||
}
|
||||
moreVC.transitioningDelegate = self
|
||||
moreVC.modalPresentationStyle = .custom
|
||||
self.present(moreVC, animated: true)
|
||||
}
|
||||
}
|
||||
return cell
|
||||
}
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
@ -96,4 +122,7 @@ extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableV
|
||||
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
|
||||
}
|
||||
}
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class MPPositive_ArtistShowViewController: MPPositive_BaseViewController {
|
||||
class MPPositive_ArtistShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate {
|
||||
///头视图
|
||||
private lazy var headView:MPPositive_ArtistShowHeaderView = .init(frame: .init(x: 0, y: 0, width: screen_Width, height: 385*width))
|
||||
///艺术家Label
|
||||
@ -61,17 +61,24 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController {
|
||||
}()
|
||||
private var artist:MPPositive_ArtistViewModel!{
|
||||
didSet{
|
||||
if artist != nil {
|
||||
pagingView.isHidden = false
|
||||
//更新头部视图数据
|
||||
headView.artistid = self.browseid
|
||||
headView.artist = artist
|
||||
|
||||
//更新分页数据源
|
||||
dataSource.titles = artist.lists.compactMap({$0.title})
|
||||
nameLabel.text = artist.header?.title
|
||||
dataSource.reloadData(selectedIndex: 0)
|
||||
segmentView.reloadData()
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if artist != nil {
|
||||
pagingView.isHidden = false
|
||||
//更新头部视图数据
|
||||
headView.artistid = self.browseid
|
||||
headView.artist = artist
|
||||
|
||||
//更新分页数据源
|
||||
dataSource.titles = artist.lists.compactMap({$0.title})
|
||||
nameLabel.text = artist.header?.title
|
||||
dataSource.reloadData(selectedIndex: 0)
|
||||
segmentView.reloadData()
|
||||
configure()
|
||||
removeErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,13 +90,11 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController {
|
||||
init(_ browseId:String) {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
browseid = browseId
|
||||
MP_HUD.loading()
|
||||
//进行网络请求
|
||||
MP_NetWorkManager.shared.requestArtist(browseId) { [weak self] result in
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
artist = result
|
||||
}
|
||||
guard let self = self else {return}
|
||||
artist = result
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +105,33 @@ class MPPositive_ArtistShowViewController: MPPositive_BaseViewController {
|
||||
super.viewDidLoad()
|
||||
setPopBtn()
|
||||
setTitle("")
|
||||
configure()
|
||||
errorBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
//移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试
|
||||
view.subviews.forEach { item in
|
||||
if item != self.navView {
|
||||
//移除
|
||||
if item.superview != nil {
|
||||
item.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
//添加报错View
|
||||
setErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}
|
||||
retryBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MP_HUD.loading()
|
||||
MP_NetWorkManager.shared.requestArtist(self.browseid) { [weak self] result in
|
||||
guard let self = self else {return}
|
||||
artist = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
@ -174,7 +205,7 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{
|
||||
switch item.browseItem.itemType {
|
||||
case .artist:
|
||||
//用户查看艺术家
|
||||
let artistVC = MPPositive_ArtistShowViewController(item.browseItem.browseId ?? "")
|
||||
let artistVC = MPPositive_ArtistShowViewController(item.browseItem.artistId ?? "")
|
||||
navigationController?.pushViewController(artistVC, animated: true)
|
||||
case .list:
|
||||
//列表专辑
|
||||
@ -199,6 +230,42 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{
|
||||
break
|
||||
}
|
||||
}
|
||||
showView.moreBlock = {
|
||||
[weak self] (itemView) in
|
||||
guard let self = self else {return}
|
||||
MPPositive_Debouncer.shared.call {
|
||||
MP_NetWorkManager.shared.requestNextList("", videoId: itemView.browseItem.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let first = listSongs.first else {return}
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
improveDataforLycirsAndRelated(first) {[weak self] (result) in
|
||||
first.lyricsID = result.0
|
||||
first.relatedID = result.1
|
||||
group.leave()
|
||||
}
|
||||
group.enter()
|
||||
//补全资源路径组和封面路径组
|
||||
improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in
|
||||
first.resourceUrls = resourceUrls.0
|
||||
first.itags = resourceUrls.1
|
||||
first.mimeTypes = resourceUrls.2
|
||||
first.coverUrls = coverUrls
|
||||
group.leave()
|
||||
}
|
||||
group.notify(queue: .main, execute: {
|
||||
[weak self] in
|
||||
MPPositive_ModalType = .MoreOperations
|
||||
let moreVC = MPPositive_MoreSongOperationsViewController(first)
|
||||
moreVC.transitioningDelegate = self
|
||||
moreVC.modalPresentationStyle = .custom
|
||||
self?.present(moreVC, animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return showView
|
||||
}
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,13 +38,51 @@ class MPPositive_HomeViewController: MPPositive_BaseViewController{
|
||||
// private var loadViewModel:MPPositive_BrowseLoadViewModel!
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setTitle("Musicoo")
|
||||
setTitle("Musiclax")
|
||||
confirgue()
|
||||
//获取首页
|
||||
MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists()
|
||||
NotificationCenter.notificationKey.add(observer: self, selector: #selector(reloadAction(_ :)), notificationName: .positive_browses_reload)
|
||||
MP_HUD.loading()
|
||||
errorBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
//移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试
|
||||
view.subviews.forEach { item in
|
||||
if item != self.navView {
|
||||
//移除
|
||||
if item.superview != nil {
|
||||
item.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
//添加报错View
|
||||
setErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}
|
||||
retryBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MP_HUD.loading()
|
||||
DispatchQueue.main.async {
|
||||
//获取首页
|
||||
MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists()
|
||||
}
|
||||
}
|
||||
}
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
}
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
MP_AnalyticsManager.shared.home_b_pvAction()
|
||||
}
|
||||
//MARK: - UI生成与配置
|
||||
//配置
|
||||
private func confirgue() {
|
||||
@ -71,13 +109,20 @@ class MPPositive_HomeViewController: MPPositive_BaseViewController{
|
||||
@objc private func reloadAction(_ sender:Notification) {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
self?.tableView.reloadData()
|
||||
guard let self = self else {return}
|
||||
if tableView.superview == nil {
|
||||
confirgue()
|
||||
}
|
||||
removeErrorView()
|
||||
MP_HUD.hideNow()
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
//MARK: - 点击
|
||||
//点击顶部右侧弹出菜单
|
||||
@objc private func menuRightClick(_ sender:UIButton) {
|
||||
|
||||
let setVC = MPSideA_SettingViewController()
|
||||
navigationController?.pushViewController(setVC, animated: true)
|
||||
}
|
||||
}
|
||||
//MARK: - tableView
|
||||
@ -105,6 +150,7 @@ extension MPPositive_HomeViewController: UITableViewDataSource, UITableViewDeleg
|
||||
cell.requestNextBlock = {
|
||||
[weak self] (item) in
|
||||
guard let self = self else {return}
|
||||
MP_AnalyticsManager.shared.home_b_module_clickAction(MPPositive_BrowseLoadViewModel.shared.browseModuleLists[indexPath.section].title)
|
||||
switch item.browseItem.itemType {
|
||||
case .single:
|
||||
//单曲/视频跳转
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import UIKit
|
||||
///b面列表歌单展示
|
||||
class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
class MPPositive_ListShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate {
|
||||
//顶部封面图(呈现歌单封面)
|
||||
private lazy var coverImageView:UIImageView = {
|
||||
let imageView:UIImageView = .init()
|
||||
@ -31,8 +31,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
//播放全部列表按钮
|
||||
private lazy var playListBtn:UIButton = {
|
||||
let btn = UIButton()
|
||||
btn.setBackgroundImage(UIImage(named: "List_UnPlay'logo"), for: .normal)
|
||||
btn.setBackgroundImage(UIImage(named: "List_Played'logo"), for: .selected)
|
||||
btn.setBackgroundImage(UIImage(named: "List_ShufflePlay'logo"), for: .normal)
|
||||
btn.isUserInteractionEnabled = false
|
||||
return btn
|
||||
}()
|
||||
@ -46,14 +45,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
btn.addTarget(self, action: #selector(collectionSwitchClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
//播放状态按钮
|
||||
private lazy var playTypeBtn:UIButton = {
|
||||
let btn = UIButton()
|
||||
btn.setBackgroundImage(UIImage(named: "List_NormolPlay'logo"), for: .normal)
|
||||
btn.setBackgroundImage(UIImage(named: "List_ShufflePlay'logo"), for: .selected)
|
||||
btn.addTarget(self, action: #selector(collectionStatuClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
|
||||
//tableView(歌单/专辑详情展示)
|
||||
private lazy var tableView:UITableView = {
|
||||
let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain)
|
||||
@ -75,8 +67,11 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if listOrAlbum != nil {
|
||||
configure()
|
||||
reload()
|
||||
tableView.reloadData()
|
||||
removeErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,12 +90,12 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
self.params = params
|
||||
self.centerTtitle = title
|
||||
self.subtitle = subtitle
|
||||
MP_HUD.loading()
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
//发起网络请求
|
||||
MP_NetWorkManager.shared.requestAlbumOrListDatas(browseId, params: params) { [weak self] result in
|
||||
guard let self = self else {return}
|
||||
listOrAlbum = result
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +106,33 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
super.viewDidLoad()
|
||||
setTitle("")
|
||||
setPopBtn()
|
||||
configure()
|
||||
errorBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
//移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试
|
||||
view.subviews.forEach { item in
|
||||
if item != self.navView {
|
||||
//移除
|
||||
if item.superview != nil {
|
||||
item.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
//添加报错View
|
||||
setErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}
|
||||
retryBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MP_HUD.loading()
|
||||
MP_NetWorkManager.shared.requestAlbumOrListDatas(self.browseid, params: self.params) { [weak self] result in
|
||||
guard let self = self else {return}
|
||||
listOrAlbum = result
|
||||
}
|
||||
}
|
||||
}
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
@ -128,7 +149,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
listOrAlbum.header?.setUrltoImage(coverImageView)
|
||||
titleLabel.text = listOrAlbum.header?.title
|
||||
descriptionLabel.text = listOrAlbum.header?._description
|
||||
playListNumberLabel.text = "Play all (\(listOrAlbum.items.count))"
|
||||
playListNumberLabel.text = "Play (\(listOrAlbum.items.count))"
|
||||
}
|
||||
//MARK: - UI生成与配置
|
||||
//页面配置
|
||||
@ -173,16 +194,10 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
make.height.equalTo(32*width)
|
||||
make.bottom.equalTo(blurView.snp.bottom).offset(-29*width)
|
||||
}
|
||||
view.addSubview(playTypeBtn)
|
||||
playTypeBtn.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(24*width)
|
||||
make.right.equalToSuperview().offset(-18*width)
|
||||
make.centerY.equalTo(playListView.snp.centerY)
|
||||
}
|
||||
view.addSubview(collectionListBtn)
|
||||
collectionListBtn.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(24*width)
|
||||
make.right.equalToSuperview().offset(-56*width)
|
||||
make.right.equalToSuperview().offset(-18*width)
|
||||
make.centerY.equalTo(playListView.snp.centerY)
|
||||
}
|
||||
view.addSubview(tableView)
|
||||
@ -235,7 +250,20 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
//MARK: - 点击事件
|
||||
//播放/暂停列表
|
||||
@objc private func playListActionClick(_ sender:UITapGestureRecognizer) {
|
||||
|
||||
MPPositive_Debouncer.shared.call {
|
||||
[weak self] in
|
||||
guard let self = self, let item = listOrAlbum.items.randomElement() else {return}
|
||||
//触发next请求,优先获取列表全部单曲基础数据(不完善)
|
||||
MP_NetWorkManager.shared.requestNextList(item.browseItem.playListId ?? "", videoId: item.browseItem.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let self = self else {return}
|
||||
//回掉的数据并不完善,生成一个playerloadViewModel
|
||||
let lodaViewModel = MPPositive_PlayerLoadViewModel(listSongs, currentVideoId: item.browseItem.videoId ?? "")
|
||||
lodaViewModel.improveData(item.browseItem.videoId ?? "")
|
||||
MP_PlayerManager.shared.loadPlayer = lodaViewModel
|
||||
//发布弹出音乐播放器的通知
|
||||
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
|
||||
}
|
||||
}
|
||||
}
|
||||
//切换当前列表收藏状态
|
||||
@objc private func collectionSwitchClick(_ sender:UIButton) {
|
||||
@ -262,10 +290,7 @@ class MPPositive_ListShowViewController: MPPositive_BaseViewController {
|
||||
MPPositive_LoadCoreModel.shared.reloadCollectionListViewModels(nil)
|
||||
}
|
||||
}
|
||||
//更改当前列表播放状态
|
||||
@objc private func collectionStatuClick(_ sender:UIButton) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//MARK: - tableView
|
||||
extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewDelegate {
|
||||
@ -275,6 +300,39 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MusicItemShowTableViewCellID, for: indexPath) as! MPPositive_MusicItemShowTableViewCell
|
||||
cell.itemView = listOrAlbum.items[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MPPositive_Debouncer.shared.call {
|
||||
MP_NetWorkManager.shared.requestNextList("", videoId: self.listOrAlbum.items[indexPath.row].browseItem.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let first = listSongs.first else {return}
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
improveDataforLycirsAndRelated(first) {[weak self] (result) in
|
||||
first.lyricsID = result.0
|
||||
first.relatedID = result.1
|
||||
group.leave()
|
||||
}
|
||||
group.enter()
|
||||
//补全资源路径组和封面路径组
|
||||
improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in
|
||||
first.resourceUrls = resourceUrls.0
|
||||
first.itags = resourceUrls.1
|
||||
first.mimeTypes = resourceUrls.2
|
||||
first.coverUrls = coverUrls
|
||||
group.leave()
|
||||
}
|
||||
group.notify(queue: .main, execute: {
|
||||
[weak self] in
|
||||
MPPositive_ModalType = .MoreOperations
|
||||
let moreVC = MPPositive_MoreSongOperationsViewController(first)
|
||||
moreVC.transitioningDelegate = self
|
||||
moreVC.modalPresentationStyle = .custom
|
||||
self?.present(moreVC, animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return cell
|
||||
}
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
@ -293,4 +351,7 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD
|
||||
}
|
||||
}
|
||||
}
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,4 +95,28 @@ extension MPPositive_MoreContentViewController: UICollectionViewDataSource, UICo
|
||||
return cell
|
||||
}
|
||||
}
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
MP_AnalyticsManager.shared.home_b_module_clickAction(browseModuleList.title)
|
||||
switch showType {
|
||||
case .Single:
|
||||
//单曲/视频跳转
|
||||
MPPositive_Debouncer.shared.call {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//触发next请求,优先获取列表全部单曲基础数据(不完善)
|
||||
MP_NetWorkManager.shared.requestNextList(browseModuleList.items[indexPath.row].browseItem.playListId ?? "", videoId: browseModuleList.items[indexPath.row].browseItem.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let self = self else {return}
|
||||
//回掉的数据并不完善,生成一个playerloadViewModel
|
||||
let lodaViewModel = MPPositive_PlayerLoadViewModel(listSongs, currentVideoId: browseModuleList.items[indexPath.row].browseItem.videoId ?? "")
|
||||
lodaViewModel.improveData(browseModuleList.items[indexPath.row].browseItem.videoId ?? "")
|
||||
MP_PlayerManager.shared.loadPlayer = lodaViewModel
|
||||
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
|
||||
}
|
||||
}
|
||||
default:
|
||||
//列表专辑
|
||||
let listVC = MPPositive_ListShowViewController(browseModuleList.items[indexPath.row].browseItem.browseId ?? "", params: browseModuleList.items[indexPath.row].browseItem.params ?? "", title: browseModuleList.items[indexPath.row].title ?? "", subtitle: browseModuleList.items[indexPath.row].subtitle ?? "")
|
||||
navigationController?.pushViewController(listVC, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,25 +123,29 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
|
||||
//打开时检索播放器状态,好调整内容
|
||||
MP_PlayerManager.shared.runActionBlock = { [weak self] (currentTime, duration) in
|
||||
guard let self = self else { return }
|
||||
//展示当前时间
|
||||
coverView.durationLabel.text = setTimesToMinSeconds(currentTime)
|
||||
//展示剩余时间
|
||||
let remain:TimeInterval = duration - currentTime
|
||||
coverView.maxTimesLabel.text = setTimesToMinSeconds(remain)
|
||||
//调整进度条内容
|
||||
let value = currentTime/duration
|
||||
coverView.sliderView.value = Float(value)
|
||||
DispatchQueue.main.async {
|
||||
//展示当前时间
|
||||
self.coverView.durationLabel.text = setTimesToMinSeconds(currentTime)
|
||||
//展示剩余时间
|
||||
let remain:TimeInterval = duration - currentTime
|
||||
self.coverView.maxTimesLabel.text = setTimesToMinSeconds(remain)
|
||||
//调整进度条内容
|
||||
let value = currentTime/duration
|
||||
self.coverView.sliderView.value = Float(value)
|
||||
}
|
||||
}
|
||||
//当缓存变化时
|
||||
MP_PlayerManager.shared.cacheValueBlock = { [weak self] (value, duration) in
|
||||
guard let self = self else { return }
|
||||
if value < duration {
|
||||
//进度缓存中
|
||||
let float = value/duration
|
||||
coverView.progressView.setProgress(Float(float), animated: false)
|
||||
}else {
|
||||
//进度缓存满了
|
||||
coverView.progressView.setProgress(1, animated: false)
|
||||
DispatchQueue.main.async {
|
||||
if value < duration {
|
||||
//进度缓存中
|
||||
let float = value/duration
|
||||
self.coverView.progressView.setProgress(Float(float), animated: false)
|
||||
}else {
|
||||
//进度缓存满了
|
||||
self.coverView.progressView.setProgress(1, animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
switch MP_PlayerManager.shared.getPlayState() {
|
||||
@ -308,7 +312,8 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
|
||||
lyricsView.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title
|
||||
lyricsView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle
|
||||
lyricsView.lyricsLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics?.isEmpty == true ? "No Lyrics":MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics
|
||||
coverView.loadBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false
|
||||
coverView.downloadButton.state = (MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) ? .downloaded:.startDownload
|
||||
coverView.downloadButton.isUserInteractionEnabled = !(MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false)
|
||||
coverView.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false
|
||||
coverView.restoreDownloadProgress()
|
||||
}
|
||||
|
||||
@ -7,28 +7,35 @@
|
||||
|
||||
import UIKit
|
||||
///相关内容推荐
|
||||
class MPPositive_RecommendViewController: MPPositive_BaseViewController {
|
||||
class MPPositive_RecommendViewController: MPPositive_BaseViewController,UIViewControllerTransitioningDelegate {
|
||||
//load模块
|
||||
private var loadRecommend:MPPositive_RecommendLoadViewModel!{
|
||||
didSet{
|
||||
if loadRecommend != nil {
|
||||
membersView.isHidden = false
|
||||
segmentView.isHidden = false
|
||||
listContainerView.isHidden = false
|
||||
loadRecommend.resultReloadBlock = {
|
||||
[weak self] in
|
||||
//刷新分页控制器
|
||||
guard let self = self else {return}
|
||||
sectionLabel.text = loadRecommend.members.title
|
||||
collectionView.reloadData()
|
||||
dataSource.titles = loadRecommend?.sectionLists.compactMap({$0.title}) ?? []
|
||||
dataSource.reloadData(selectedIndex: 0)
|
||||
segmentView.reloadData()
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if loadRecommend != nil {
|
||||
membersView.isHidden = false
|
||||
segmentView.isHidden = false
|
||||
listContainerView.isHidden = false
|
||||
loadRecommend.resultReloadBlock = {
|
||||
[weak self] in
|
||||
//刷新分页控制器
|
||||
guard let self = self else {return}
|
||||
sectionLabel.text = loadRecommend.members.title
|
||||
collectionView.reloadData()
|
||||
dataSource.titles = loadRecommend?.sectionLists.compactMap({$0.title}) ?? []
|
||||
dataSource.reloadData(selectedIndex: 0)
|
||||
segmentView.reloadData()
|
||||
}
|
||||
configure()
|
||||
removeErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}else {
|
||||
membersView.isHidden = true
|
||||
segmentView.isHidden = true
|
||||
listContainerView.isHidden = true
|
||||
}
|
||||
}else {
|
||||
membersView.isHidden = true
|
||||
segmentView.isHidden = true
|
||||
listContainerView.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,10 +100,13 @@ class MPPositive_RecommendViewController: MPPositive_BaseViewController {
|
||||
listContainerView.backgroundColor = .clear
|
||||
return listContainerView
|
||||
}()
|
||||
private var browseId:String!
|
||||
/// 初始化
|
||||
/// - Parameter browseId: 相关内容id
|
||||
init(_ browseId:String) {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
self.browseId = browseId
|
||||
MP_HUD.loading()
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
self?.loadRecommend = .init(browseId)
|
||||
@ -111,7 +121,32 @@ class MPPositive_RecommendViewController: MPPositive_BaseViewController {
|
||||
super.viewDidLoad()
|
||||
setTitle("Recommend")
|
||||
setPopBtn()
|
||||
configure()
|
||||
errorBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
//移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试
|
||||
view.subviews.forEach { item in
|
||||
if item != self.navView {
|
||||
//移除
|
||||
if item.superview != nil {
|
||||
item.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
//添加报错View
|
||||
setErrorView()
|
||||
MP_HUD.hideNow()
|
||||
}
|
||||
retryBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
MP_HUD.loading()
|
||||
DispatchQueue.main.async {
|
||||
self.loadRecommend = .init(self.browseId)
|
||||
}
|
||||
}
|
||||
}
|
||||
private func configure() {
|
||||
view.addSubview(membersView)
|
||||
@ -208,6 +243,42 @@ extension MPPositive_RecommendViewController: JXSegmentedListContainerViewDataSo
|
||||
break
|
||||
}
|
||||
}
|
||||
showView.moreBlock = {
|
||||
[weak self] (itemView) in
|
||||
guard let self = self else {return}
|
||||
MPPositive_Debouncer.shared.call {
|
||||
MP_NetWorkManager.shared.requestNextList("", videoId: itemView.browseItem.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let first = listSongs.first else {return}
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
improveDataforLycirsAndRelated(first) {[weak self] (result) in
|
||||
first.lyricsID = result.0
|
||||
first.relatedID = result.1
|
||||
group.leave()
|
||||
}
|
||||
group.enter()
|
||||
//补全资源路径组和封面路径组
|
||||
improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in
|
||||
first.resourceUrls = resourceUrls.0
|
||||
first.itags = resourceUrls.1
|
||||
first.mimeTypes = resourceUrls.2
|
||||
first.coverUrls = coverUrls
|
||||
group.leave()
|
||||
}
|
||||
group.notify(queue: .main, execute: {
|
||||
[weak self] in
|
||||
MPPositive_ModalType = .MoreOperations
|
||||
let moreVC = MPPositive_MoreSongOperationsViewController(first)
|
||||
moreVC.transitioningDelegate = self
|
||||
moreVC.modalPresentationStyle = .custom
|
||||
self?.present(moreVC, animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return showView
|
||||
}
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import UIKit
|
||||
///搜索结果控制器
|
||||
class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
|
||||
class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate {
|
||||
//MARK: - 导航View中的控件
|
||||
//顶部搜索textField
|
||||
private lazy var searchTextField:UITextField = {
|
||||
@ -36,6 +36,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
|
||||
btn.addTarget(self, action: #selector(backPopClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
|
||||
//搜索限定计时器
|
||||
private var debounceTimer: Timer?
|
||||
//对用户展示的搜索建议组
|
||||
@ -48,6 +49,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
|
||||
//搜索建议组存在,将其显现
|
||||
suggestionView.isHidden = false
|
||||
suggestionView.suggestions = suggestionList.attributedTexts
|
||||
MP_AnalyticsManager.shared.search_sug_showAction()
|
||||
}else {
|
||||
suggestionView.isHidden = true
|
||||
suggestionView.suggestions = nil
|
||||
@ -70,6 +72,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
|
||||
searchTextField.text = text
|
||||
searchText = text
|
||||
resultsShowView.loadModel = .init(text)
|
||||
MP_AnalyticsManager.shared.search_sug_clickAction(text)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -87,6 +90,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
|
||||
searchText = text
|
||||
searchTextField.text = text
|
||||
resultsShowView.loadModel = .init(text)
|
||||
MP_AnalyticsManager.shared.search_sug_clickAction(text)
|
||||
suggestionView.isHidden = true
|
||||
}
|
||||
resultsShowView.scrollBlock = {
|
||||
@ -124,10 +128,52 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
|
||||
break
|
||||
}
|
||||
}
|
||||
resultsShowView.moreBlock = {
|
||||
[weak self] (itemView) in
|
||||
guard let self = self else {return}
|
||||
MPPositive_Debouncer.shared.call {
|
||||
MP_NetWorkManager.shared.requestNextList("", videoId: itemView.item.videoId ?? ""){ [weak self] listSongs in
|
||||
guard let first = listSongs.first else {return}
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
improveDataforLycirsAndRelated(first) {[weak self] (result) in
|
||||
first.lyricsID = result.0
|
||||
first.relatedID = result.1
|
||||
group.leave()
|
||||
}
|
||||
group.enter()
|
||||
//补全资源路径组和封面路径组
|
||||
improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in
|
||||
first.resourceUrls = resourceUrls.0
|
||||
first.itags = resourceUrls.1
|
||||
first.mimeTypes = resourceUrls.2
|
||||
first.coverUrls = coverUrls
|
||||
group.leave()
|
||||
}
|
||||
group.notify(queue: .main, execute: {
|
||||
[weak self] in
|
||||
MPPositive_ModalType = .MoreOperations
|
||||
let moreVC = MPPositive_MoreSongOperationsViewController(first)
|
||||
moreVC.transitioningDelegate = self
|
||||
moreVC.modalPresentationStyle = .custom
|
||||
self?.present(moreVC, animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
errorBlock = {
|
||||
[weak self] in
|
||||
MP_HUD.hideNow()
|
||||
self?.resultsShowView.isHidden = false
|
||||
self?.resultsShowView.emptyImageView.isHidden = false
|
||||
}
|
||||
}
|
||||
deinit{
|
||||
debounceTimer = nil
|
||||
}
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting)
|
||||
}
|
||||
private func configure() {
|
||||
//配置导航栏
|
||||
let searchView = createSearchView()
|
||||
@ -246,6 +292,7 @@ extension MPPositive_SearchResultShowViewController:UITextFieldDelegate {
|
||||
self.searchText = text
|
||||
//用户输入了文本
|
||||
resultsShowView.loadModel = .init(text)
|
||||
MP_AnalyticsManager.shared.search_sug_clickAction(text)
|
||||
suggestionView.isHidden = true
|
||||
//停止输入
|
||||
view.endEditing(true)
|
||||
|
||||
@ -48,6 +48,7 @@ class MPPositive_SearchViewController: MPPositive_BaseViewController {
|
||||
}
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
MP_AnalyticsManager.shared.search_pvAction()
|
||||
}
|
||||
//配置
|
||||
private func configure() {
|
||||
|
||||
@ -8,30 +8,41 @@
|
||||
import UIKit
|
||||
import DownloadButton
|
||||
class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell {
|
||||
//特殊图片(展示预览图片)
|
||||
private lazy var iconImageView:UIImageView = {
|
||||
let imageView:UIImageView = .init()
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.layer.masksToBounds = true
|
||||
return imageView
|
||||
}()
|
||||
//设置下载按钮
|
||||
private lazy var LoadBtn:PKDownloadButton = {
|
||||
lazy var loadBtn:PKDownloadButton = {
|
||||
let btn:PKDownloadButton = .init()
|
||||
//禁止交互
|
||||
btn.isUserInteractionEnabled = false
|
||||
btn.downloadedButton.cleanDefaultAppearance()
|
||||
// btn.downloadedButton.setBackgroundImage(UIImage(named: ""), for: <#T##UIControl.State#>)
|
||||
//开始下载状态
|
||||
btn.startDownloadButton.cleanDefaultAppearance()
|
||||
btn.startDownloadButton.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
//下载结束状态
|
||||
btn.downloadedButton.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal)
|
||||
btn.downloadedButton.isUserInteractionEnabled = false
|
||||
//停止下载状态
|
||||
btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal)
|
||||
btn.stopDownloadButton.isUserInteractionEnabled = false
|
||||
btn.stopDownloadButton.tintColor = UIColor(hex: "#80F988")
|
||||
btn.stopDownloadButton.stopButtonWidth = 1
|
||||
btn.stopDownloadButton.stopButton.backgroundColor = .clear
|
||||
btn.stopDownloadButton.stopButton.tintColor = .clear
|
||||
btn.stopDownloadButton.filledLineWidth = 3*width
|
||||
btn.stopDownloadButton.filledLineStyleOuter = true
|
||||
|
||||
//加载状态设置
|
||||
btn.pendingView.tintColor = UIColor(hex: "#80F988")
|
||||
btn.pendingView.radius = 12*width
|
||||
btn.pendingView.emptyLineRadians = 2*width
|
||||
btn.pendingView.spinTime = 3
|
||||
return btn
|
||||
}()
|
||||
|
||||
private lazy var titleLabel:UILabel = createLabel(font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left)
|
||||
var title:String!{
|
||||
didSet{
|
||||
iconImageView.image = UIImage(named: title)
|
||||
titleLabel.text = title
|
||||
}
|
||||
}
|
||||
var progressBlock:(() -> Void)?
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
@ -53,8 +64,8 @@ class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell {
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
private func configure() {
|
||||
contentView.addSubview(iconImageView)
|
||||
iconImageView.snp.makeConstraints { make in
|
||||
contentView.addSubview(loadBtn)
|
||||
loadBtn.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(24*width)
|
||||
make.top.equalToSuperview().offset(12*width).priority(999)
|
||||
make.bottom.equalToSuperview().offset(-12*width)
|
||||
@ -63,7 +74,36 @@ class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell {
|
||||
contentView.addSubview(titleLabel)
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.left.equalTo(iconImageView.snp.right).offset(12*width)
|
||||
make.left.equalTo(loadBtn.snp.right).offset(12*width)
|
||||
}
|
||||
}
|
||||
//对于下载按钮状态的刷新变动
|
||||
public func restoreDownloadProgress(_ song:MPPositive_SongItemModel?) {
|
||||
guard let song = song else {
|
||||
return
|
||||
}
|
||||
//判断当前播放video是否正在下载
|
||||
if let progress = MP_DownloadManager.shared.getProgress(for: song.videoId) {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//是正在下载的URL
|
||||
loadBtn.state = .downloading
|
||||
//调整下载的进度
|
||||
loadBtn.stopDownloadButton.progress = progress
|
||||
if progressBlock != nil {
|
||||
progressBlock!()
|
||||
}
|
||||
}
|
||||
}else {
|
||||
//不是正在下载的内容,判断是否已经下载
|
||||
if MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", song.videoId)).count != 0 {
|
||||
//下载内容
|
||||
loadBtn.state = .downloaded
|
||||
}else {
|
||||
//不是下载内容
|
||||
loadBtn.state = .startDownload
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell {
|
||||
btn.addTarget(self, action: #selector(moreActionClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
///下载状态按钮
|
||||
private lazy var loadBtn:UIButton = {
|
||||
let btn:UIButton = .init()
|
||||
btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
|
||||
btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
// ///下载状态按钮
|
||||
// private lazy var loadBtn:UIButton = {
|
||||
// let btn:UIButton = .init()
|
||||
// btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
// btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
|
||||
// btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
|
||||
// return btn
|
||||
// }()
|
||||
var itemView:MPPositive_BrowseItemViewModel!{
|
||||
didSet{
|
||||
itemView.setUrltoImage(iconImageView)
|
||||
@ -74,17 +74,17 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell {
|
||||
make.centerY.equalTo(iconImageView.snp.centerY)
|
||||
make.right.equalToSuperview().offset(-18*width)
|
||||
}
|
||||
contentView.addSubview(loadBtn)
|
||||
loadBtn.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(24*width)
|
||||
make.centerY.equalTo(iconImageView.snp.centerY)
|
||||
make.right.equalToSuperview().offset(-54*width)
|
||||
}
|
||||
// contentView.addSubview(loadBtn)
|
||||
// loadBtn.snp.makeConstraints { make in
|
||||
// make.width.height.equalTo(24*width)
|
||||
// make.centerY.equalTo(iconImageView.snp.centerY)
|
||||
// make.right.equalToSuperview().offset(-54*width)
|
||||
// }
|
||||
contentView.addSubview(titleLabel)
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(iconImageView.snp.top).offset(10*width)
|
||||
make.left.equalTo(iconImageView.snp.right).offset(12*width)
|
||||
make.right.equalTo(loadBtn.snp.left).offset(-10*width)
|
||||
make.right.equalTo(moreBtn.snp.left).offset(-10*width)
|
||||
}
|
||||
contentView.addSubview(subtitleLabel)
|
||||
subtitleLabel.snp.makeConstraints { make in
|
||||
@ -93,10 +93,13 @@ class MPPositive_ArtistShowSongTableViewCell: UITableViewCell {
|
||||
make.right.equalTo(titleLabel.snp.right)
|
||||
}
|
||||
}
|
||||
|
||||
var moreBlock:(() -> Void)?
|
||||
//点击更多
|
||||
@objc private func moreActionClick(_ sender:UIButton) {
|
||||
|
||||
guard moreBlock != nil else {
|
||||
return
|
||||
}
|
||||
moreBlock!()
|
||||
}
|
||||
//点击下载
|
||||
@objc private func loadActionClick(_ sender:UIButton) {
|
||||
|
||||
@ -66,6 +66,7 @@ class MPPositive_ArtistShowTypeView: UIView, JXPagingViewListViewDelegate {
|
||||
fileprivate var listViewDidScrollCallback: ((UIScrollView) -> ())?
|
||||
//选中内容
|
||||
var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)?
|
||||
var moreBlock:((MPPositive_BrowseItemViewModel) -> Void)?
|
||||
init(frame: CGRect, list:MPPositive_ArtistContentListViewModel) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .init(hex: "#151718")
|
||||
@ -133,6 +134,15 @@ extension MPPositive_ArtistShowTypeView:UITableViewDataSource, UITableViewDelega
|
||||
case .single:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell
|
||||
cell.itemView = sectionList.itemViews[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
if moreBlock != nil {
|
||||
moreBlock!(sectionList.itemViews[indexPath.row])
|
||||
}
|
||||
}
|
||||
return cell
|
||||
case .list:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell
|
||||
|
||||
@ -41,10 +41,10 @@ class MPPositive_MusicItemShowTableViewCell: UITableViewCell {
|
||||
itemView.setUrltoImage(coverImageView)
|
||||
titleLabel.text = itemView.title
|
||||
subtitleLabel.text = itemView.subtitle
|
||||
//判断是否下载过
|
||||
|
||||
}
|
||||
}
|
||||
var moreBlock:(() -> Void)?
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
@ -101,7 +101,10 @@ class MPPositive_MusicItemShowTableViewCell: UITableViewCell {
|
||||
}
|
||||
//点击更多
|
||||
@objc private func moreActionClick(_ sender:UIButton) {
|
||||
|
||||
guard moreBlock != nil else {
|
||||
return
|
||||
}
|
||||
moreBlock!()
|
||||
}
|
||||
//点击下载
|
||||
@objc private func loadActionClick(_ sender:UIButton) {
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
import UIKit
|
||||
import DownloadButton
|
||||
//B面播放器封面View(封面,标题,副标题,收藏,下载,进度条View)
|
||||
class MPPositive_PlayerCoverView: UIView {
|
||||
class MPPositive_PlayerCoverView: UIView, PKDownloadButtonDelegate {
|
||||
//下载进度条View
|
||||
private var loadView = CircularProgressView()
|
||||
// private var loadView = CircularProgressView()
|
||||
|
||||
///封面
|
||||
lazy var coverImageView:UIImageView = {
|
||||
@ -30,13 +30,13 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
return btn
|
||||
}()
|
||||
///下载按钮
|
||||
lazy var loadBtn:UIButton = {
|
||||
let btn:UIButton = .init()
|
||||
btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
|
||||
btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
|
||||
return btn
|
||||
}()
|
||||
// lazy var loadBtn:UIButton = {
|
||||
// let btn:UIButton = .init()
|
||||
// btn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
// btn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
|
||||
// btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
|
||||
// return btn
|
||||
// }()
|
||||
///下载按钮
|
||||
lazy var downloadButton:PKDownloadButton = {
|
||||
let btn:PKDownloadButton = .init()
|
||||
@ -50,10 +50,18 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal)
|
||||
btn.stopDownloadButton.isUserInteractionEnabled = false
|
||||
btn.stopDownloadButton.tintColor = UIColor(hex: "#80F988")
|
||||
btn.stopDownloadButton.filledLineWidth = 1.5
|
||||
btn.stopDownloadButton.stopButtonWidth = 1
|
||||
btn.stopDownloadButton.stopButton.backgroundColor = .clear
|
||||
btn.stopDownloadButton.stopButton.tintColor = .clear
|
||||
btn.stopDownloadButton.filledLineWidth = 3*width
|
||||
btn.stopDownloadButton.filledLineStyleOuter = true
|
||||
|
||||
//加载状态设置
|
||||
// btn.pendingView.tintColor = .
|
||||
btn.pendingView.tintColor = UIColor(hex: "#80F988")
|
||||
btn.pendingView.radius = 12*width
|
||||
btn.pendingView.emptyLineRadians = 2*width
|
||||
btn.pendingView.spinTime = 3
|
||||
btn.delegate = self
|
||||
return btn
|
||||
}()
|
||||
|
||||
@ -104,55 +112,30 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
}
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
// NotificationCenter.default.addObserver(self, selector: #selector(updateProgress(_:)), name: Notification.Name("DownloadProgressUpdated"), object: nil)
|
||||
//
|
||||
// // 恢复进度
|
||||
// restoreDownloadProgress()
|
||||
}
|
||||
|
||||
|
||||
//对于下载按钮状态的刷新变动
|
||||
public func restoreDownloadProgress() {
|
||||
if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo,
|
||||
let videoURLString = currentVideo.song.resourceUrls?.first,
|
||||
let videoURL = URL(string: videoURLString) {
|
||||
if let progress = DownloadManager.shared.getProgress(for: videoURL) {
|
||||
//判断当前是正在下载的VideoID
|
||||
addCircularProgressBar(over: loadBtn)
|
||||
loadView.setProgress(to: progress)
|
||||
self.layoutIfNeeded()
|
||||
self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal)
|
||||
self.loadBtn.setImage(UIImage(named: "download"), for: .normal)
|
||||
self.addCircularProgressBar(over: self.loadBtn)
|
||||
self.loadView.setProgress(to: progress)
|
||||
}else {
|
||||
//不是当前下载的videoID
|
||||
if (loadView.superview) != nil {
|
||||
loadView.removeFromSuperview()
|
||||
}
|
||||
self.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
self.loadBtn.setImage(UIImage(), for: .normal)
|
||||
self.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func updateProgress(_ notification: Notification) {
|
||||
if let userInfo = notification.userInfo,
|
||||
let url = userInfo["url"] as? URL,
|
||||
let progress = userInfo["progress"] as? CGFloat,
|
||||
let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo,
|
||||
let videoURLString = currentVideo.song.resourceUrls?.first,
|
||||
let videoURL = URL(string: videoURLString),
|
||||
videoURL == url {
|
||||
loadView.setProgress(to: progress)
|
||||
if loadView.progress.isEqual(to: 1.0){
|
||||
self.loadView.removeFromSuperview()
|
||||
self.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal)
|
||||
self.loadBtn.setImage(UIImage(named: ""), for: .normal)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
guard let currentVideo = MP_PlayerManager.shared.loadPlayer?.currentVideo else {
|
||||
return
|
||||
}
|
||||
//判断当前播放video是否正在下载
|
||||
if let progress = MP_DownloadManager.shared.getProgress(for: currentVideo.song.videoId) {
|
||||
DispatchQueue.main.async {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
//是正在下载的URL
|
||||
downloadButton.state = .downloading
|
||||
downloadButton.isUserInteractionEnabled = false
|
||||
//调整下载的进度
|
||||
downloadButton.stopDownloadButton.progress = progress
|
||||
}
|
||||
}else {
|
||||
//不是正在下载的内容
|
||||
downloadButton.state = .startDownload
|
||||
downloadButton.isUserInteractionEnabled = true
|
||||
}
|
||||
}
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
@ -185,16 +168,16 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(6*width)
|
||||
}
|
||||
//配置下载和收藏按钮
|
||||
addSubview(loadBtn)
|
||||
loadBtn.snp.makeConstraints { make in
|
||||
addSubview(downloadButton)
|
||||
downloadButton.snp.makeConstraints { make in
|
||||
make.right.equalTo(coverImageView.snp.right).offset(-12*width)
|
||||
make.top.equalTo(coverImageView.snp.bottom).offset(47*width)
|
||||
make.width.height.equalTo(24*width)
|
||||
}
|
||||
addSubview(collectionSongBtn)
|
||||
collectionSongBtn.snp.makeConstraints { make in
|
||||
make.right.equalTo(loadBtn.snp.left).offset(-20*width)
|
||||
make.centerY.equalTo(loadBtn.snp.centerY)
|
||||
make.right.equalTo(downloadButton.snp.left).offset(-20*width)
|
||||
make.centerY.equalTo(downloadButton.snp.centerY)
|
||||
make.width.height.equalTo(24*width)
|
||||
}
|
||||
addSubview(progressView)
|
||||
@ -219,8 +202,6 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
make.right.equalTo(sliderView.snp.right)
|
||||
make.top.equalTo(sliderView.snp.bottom).offset(5*width)
|
||||
}
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateProgress(_:)), name: Notification.Name("DownloadProgressUpdated"), object: nil)
|
||||
|
||||
// 恢复进度
|
||||
restoreDownloadProgress()
|
||||
}
|
||||
@ -272,6 +253,7 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
}
|
||||
MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
|
||||
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
|
||||
MP_AnalyticsManager.shared.player_b_unlove_clickAction(MP_PlayerManager.shared.loadPlayer.currentVideo?.song.videoId ?? "", videoname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.title ?? "", artistname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.shortBylineText ?? "")
|
||||
}
|
||||
}else{
|
||||
self.collectionSongBtn.isSelected = true
|
||||
@ -286,132 +268,94 @@ class MPPositive_PlayerCoverView: UIView {
|
||||
MPPositive_CollectionSongModel.save()
|
||||
MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
|
||||
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
|
||||
MP_AnalyticsManager.shared.player_b_love_clickAction(MP_PlayerManager.shared.loadPlayer.currentVideo?.song.videoId ?? "", videoname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.title ?? "", artistname: MP_PlayerManager.shared.loadPlayer.currentVideo?.song.shortBylineText ?? "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@objc private func loadActionClick(_ sender: UIButton) {
|
||||
if MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd == false {
|
||||
addCircularProgressBar(over: sender)
|
||||
|
||||
self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal)
|
||||
self.loadBtn.setImage(UIImage(named: "download"), for: .normal)
|
||||
|
||||
if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo,
|
||||
let videoURLString = currentVideo.song.resourceUrls?.first,
|
||||
let videoURL = URL(string: videoURLString) {
|
||||
DownloadManager.shared.downloadVideo(from: videoURL, song: MP_PlayerManager.shared.loadPlayer.currentVideo.song, progressHandler: { [weak self] progress in
|
||||
DispatchQueue.main.async {
|
||||
self?.loadView.setProgress(to: progress)
|
||||
NotificationCenter.default.post(name: Notification.Name("DownloadProgressUpdated"), object: nil, userInfo: ["url": videoURL, "progress": progress])
|
||||
//下载按钮代理
|
||||
func downloadButtonTapped(_ downloadButton: PKDownloadButton!, currentState state: PKDownloadButtonState) {
|
||||
guard MP_PlayerManager.shared.loadPlayer?.currentVideo != nil, MP_PlayerManager.shared.loadPlayer?.currentVideo?.isDlownd == false else {
|
||||
return
|
||||
}
|
||||
//当前音乐没有下载
|
||||
switch state {
|
||||
case .startDownload: //开始状态,点击时准备加载
|
||||
//切换为准备状态
|
||||
downloadButton.state = .pending
|
||||
//禁止用户操作
|
||||
downloadButton.isUserInteractionEnabled = false
|
||||
//当开始下载时
|
||||
guard let currentVideo = MP_PlayerManager.shared.loadPlayer?.currentVideo, let videoURL = currentVideo.resourcePlayerURL else {
|
||||
MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.0) {
|
||||
downloadButton.state = .startDownload
|
||||
downloadButton.isUserInteractionEnabled = true
|
||||
}
|
||||
return
|
||||
}
|
||||
MP_AnalyticsManager.shared.player_b_download_clickAction(currentVideo.song.videoId, videoname: currentVideo.song.title ?? "", artistname: currentVideo.song.shortBylineText ?? "")
|
||||
//执行下载
|
||||
MP_DownloadManager.shared.downloadVideo(from: videoURL, song: currentVideo.song) { [weak self] progress in
|
||||
guard let self = self, currentVideo.song.videoId == MP_PlayerManager.shared.loadPlayer.currentVideo.song.videoId else {
|
||||
//不是同一个
|
||||
downloadButton.state = (MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) ? .downloaded:.startDownload
|
||||
downloadButton.isUserInteractionEnabled = !(MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false)
|
||||
return
|
||||
}
|
||||
downloadButton.state = .downloading
|
||||
downloadButton.stopDownloadButton.progress = progress
|
||||
} completion: { [weak self] result in
|
||||
guard let self = self else {return}
|
||||
//下载结束,判断成功或失败
|
||||
switch result {
|
||||
case .success(let song):
|
||||
//添加数据
|
||||
let item = MPPositive_DownloadItemModel.create()
|
||||
item.coverImage = song.coverUrls!.last
|
||||
item.reviewImage = song.reviewUrls!.last
|
||||
item.title = song.title
|
||||
item.longBylineText = song.longBylineText
|
||||
item.lengthText = song.lengthText
|
||||
item.shortBylineText = song.shortBylineText
|
||||
item.lyrics = song.lyrics
|
||||
item.lyricsID = song.lyricsID
|
||||
item.videoId = song.videoId
|
||||
item.relatedID = song.relatedID
|
||||
MPPositive_DownloadItemModel.save()
|
||||
DispatchQueue.main.async {
|
||||
//回归主线程,判断下载的是否为当前歌曲
|
||||
if song.videoId == MP_PlayerManager.shared.loadPlayer?.currentVideo?.song.videoId {
|
||||
//是当前这首,刷新一下当前播放音乐的下载状态
|
||||
MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
|
||||
}else {
|
||||
//不是这首,那就不管他
|
||||
}
|
||||
}, completion: { [weak self] result in
|
||||
switch result {
|
||||
case .success(let song):
|
||||
let item = MPPositive_DownloadItemModel.create()
|
||||
//检索
|
||||
item.coverImage = song.coverUrls!.last
|
||||
item.reviewImage = song.reviewUrls!.last
|
||||
item.title = song.title
|
||||
item.longBylineText = song.longBylineText
|
||||
item.lengthText = song.lengthText
|
||||
item.shortBylineText = song.shortBylineText
|
||||
item.lyrics = song.lyrics
|
||||
item.lyricsID = song.lyricsID
|
||||
item.videoId = song.videoId
|
||||
item.relatedID = song.relatedID
|
||||
|
||||
MPPositive_DownloadItemModel.save()
|
||||
DispatchQueue.main.async {
|
||||
self?.loadView.removeFromSuperview()
|
||||
MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
|
||||
self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal)
|
||||
self?.loadBtn.setImage(UIImage(named: ""), for: .normal)
|
||||
print("完成了对\(song.title ?? "")的下载")
|
||||
MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
|
||||
}
|
||||
case .failure(let error):
|
||||
print("Download failed with error: \(error)")
|
||||
DispatchQueue.main.async {
|
||||
self?.loadView.removeFromSuperview()
|
||||
self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
self?.loadBtn.setImage(UIImage(named: ""), for: .normal)
|
||||
}
|
||||
MP_HUD.text("Download timed out, please download again", delay: 1.5, completion: nil)
|
||||
}
|
||||
})
|
||||
print("完成了对\(song.title ?? "")的下载")
|
||||
//按钮变为下载结束状态
|
||||
downloadButton.state = .downloaded
|
||||
downloadButton.isUserInteractionEnabled = false
|
||||
//更新数据库管理类
|
||||
MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
|
||||
MP_AnalyticsManager.shared.player_b_downloadsuccess_actionAction(item.videoId, videoname: item.title ?? "", artistname: item.shortBylineText ?? "")
|
||||
}
|
||||
case .failure(let error):
|
||||
//失败了,打印错误
|
||||
print("下载报错,错误详情\(error)")
|
||||
DispatchQueue.main.async {
|
||||
//按钮回归可用状态
|
||||
downloadButton.state = .startDownload
|
||||
downloadButton.isUserInteractionEnabled = true
|
||||
}
|
||||
MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil)
|
||||
}
|
||||
}
|
||||
case .pending://准备状态
|
||||
break
|
||||
case .downloading:
|
||||
break
|
||||
case .downloaded:
|
||||
break
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
// //点击下载
|
||||
// @objc private func loadActionClick(_ sender:UIButton) {
|
||||
//
|
||||
// if MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd == false{
|
||||
//
|
||||
// // 添加圆形进度条到下载按钮位置
|
||||
// addCircularProgressBar(over: sender)
|
||||
//
|
||||
// self.loadBtn.setBackgroundImage(UIImage(named: ""), for: .normal)
|
||||
// self.loadBtn.setImage(UIImage(named: "download"), for: .normal)
|
||||
//
|
||||
// //下载,检索当前播放音乐是否存在
|
||||
// if MP_PlayerManager.shared.loadPlayer.currentVideo != nil {
|
||||
//
|
||||
// // 下载视频
|
||||
// if let currentVideo = MP_PlayerManager.shared.loadPlayer.currentVideo, let videoURLString = currentVideo.song.resourceUrls?.first, let videoURL = URL(string: videoURLString) {
|
||||
// let videoId = currentVideo.song.videoId ?? "default_video_id"
|
||||
// DownloadManager.shared.downloadVideo(from: videoURL, videoId: videoId, progressView: loadView, completion: { [weak self] result in
|
||||
// switch result {
|
||||
// case .success(let fileURL):
|
||||
// let item = MPPositive_DownloadItemModel.create()
|
||||
// item.resourcePath = "\(fileURL)"
|
||||
// item.coverImage = URL(string: MP_PlayerManager.shared.loadPlayer.currentVideo.song.coverUrls!.first!)
|
||||
// item.reviewImage = URL(string: MP_PlayerManager.shared.loadPlayer.currentVideo.song.reviewUrls!.first!)
|
||||
// item.title = MP_PlayerManager.shared.loadPlayer.currentVideo.song.title
|
||||
// item.longBylineText = MP_PlayerManager.shared.loadPlayer.currentVideo.song.longBylineText
|
||||
// item.lengthText = MP_PlayerManager.shared.loadPlayer.currentVideo.song.lengthText
|
||||
// item.shortBylineText = MP_PlayerManager.shared.loadPlayer.currentVideo.song.shortBylineText
|
||||
// item.lyrics = MP_PlayerManager.shared.loadPlayer.currentVideo.lyrics
|
||||
// item.videoId = MP_PlayerManager.shared.loadPlayer.currentVideo.song.videoId
|
||||
// item.relatedID = MP_PlayerManager.shared.loadPlayer.currentVideo.song.relatedID
|
||||
//
|
||||
// MPPositive_DownloadItemModel.save()
|
||||
// DispatchQueue.main.async {
|
||||
// MP_PlayerManager.shared.loadPlayer.currentVideo.reloadCollectionAndDownLoad()
|
||||
// self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal)
|
||||
// self?.loadBtn.setImage(UIImage(named: ""), for: .normal)
|
||||
// }
|
||||
//
|
||||
// self?.loadView.removeFromSuperview()
|
||||
// case .failure(let error):
|
||||
// print("Download failed with error: \(error)")
|
||||
// self?.loadView.removeFromSuperview()
|
||||
// DispatchQueue.main.async {
|
||||
// self?.loadBtn.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
|
||||
// self?.loadBtn.setImage(UIImage(named: ""), for: .normal)
|
||||
// }
|
||||
// MP_HUD.text("Download timed out, please download again", delay: 1.5, completion: nil)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// // 添加圆形进度条
|
||||
|
||||
private func addCircularProgressBar(over button: UIButton) {
|
||||
|
||||
loadView.removeFromSuperview() // 移除先前的进度视图(如果有)
|
||||
|
||||
loadView = CircularProgressView(frame: button.bounds)
|
||||
addSubview(loadView)
|
||||
loadView.snp.makeConstraints { make in
|
||||
make.center.equalTo(button)
|
||||
make.width.height.equalTo(button)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ class MPPositive_RecommendShowTypeView: UIView, JXSegmentedListContainerViewList
|
||||
}
|
||||
//选中内容
|
||||
var chooseItemBlock:((MPPositive_BrowseItemViewModel) -> Void)?
|
||||
|
||||
var moreBlock:((MPPositive_BrowseItemViewModel) -> Void)?
|
||||
init(_ frame:CGRect, list:MPPositive_RecommendListViewModel) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .clear
|
||||
@ -82,6 +82,15 @@ extension MPPositive_RecommendShowTypeView:UITableViewDataSource, UITableViewDel
|
||||
case .single:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowSongTableViewCellID) as! MPPositive_ArtistShowSongTableViewCell
|
||||
cell.itemView = sectionList.items[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
if moreBlock != nil {
|
||||
moreBlock!(sectionList.items[indexPath.row])
|
||||
}
|
||||
}
|
||||
return cell
|
||||
case .list:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_ArtistShowListableViewCellID, for: indexPath) as! MPPositive_ArtistShowListableViewCell
|
||||
|
||||
@ -38,6 +38,7 @@ class MPPositive_SearchResultPreviewShowView: UIView, JXSegmentedListContainerVi
|
||||
}
|
||||
var scrollBlock:(() -> Void)?
|
||||
var chooseMoreIndexBlock:((Int) -> Void)?
|
||||
var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
|
||||
//选中内容
|
||||
var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
|
||||
init(frame: CGRect, sectionLists:[MPPositive_SearchResultListViewModel]) {
|
||||
@ -85,6 +86,13 @@ extension MPPositive_SearchResultPreviewShowView:UITableViewDataSource, UITableV
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
|
||||
cell.itemView = sectionLists[indexPath.section].previewItemViews[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if moreBlock != nil {
|
||||
moreBlock!(sectionLists[indexPath.section].previewItemViews[indexPath.row])
|
||||
}
|
||||
}
|
||||
return cell
|
||||
}
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
|
||||
@ -61,7 +61,7 @@ class MPPositive_SearchResultShowTableViewCell: UITableViewCell {
|
||||
subtitleLabel.text = loadViewModel.subtitle
|
||||
}
|
||||
}
|
||||
|
||||
var moreBlock:(() -> Void)?
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
@ -118,7 +118,9 @@ class MPPositive_SearchResultShowTableViewCell: UITableViewCell {
|
||||
|
||||
//点击更多
|
||||
@objc private func moreActionClick(_ sender:UIButton) {
|
||||
|
||||
if moreBlock != nil {
|
||||
moreBlock!()
|
||||
}
|
||||
}
|
||||
//点击下载
|
||||
@objc private func loadActionClick(_ sender:UIButton) {
|
||||
|
||||
@ -55,7 +55,7 @@ class MPPositive_SearchResultTypeShowView: UIView, JXSegmentedListContainerViewL
|
||||
}
|
||||
}
|
||||
var scrollBlock:(() -> Void)?
|
||||
|
||||
var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
|
||||
init(frame: CGRect, list:MPPositive_SearchResultListViewModel) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .init(hex: "1A1A1A")
|
||||
@ -106,6 +106,13 @@ extension MPPositive_SearchResultTypeShowView:UITableViewDataSource, UITableView
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_SearchResultShowTableViewCellID, for: indexPath) as! MPPositive_SearchResultShowTableViewCell
|
||||
cell.itemView = sectionList.allItemViews[indexPath.row]
|
||||
cell.moreBlock = {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if moreBlock != nil {
|
||||
moreBlock!(sectionList.allItemViews[indexPath.row])
|
||||
}
|
||||
}
|
||||
return cell
|
||||
}
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
|
||||
@ -15,17 +15,21 @@ class MPPositive_SearchResultsShowView: UIView {
|
||||
[weak self] in
|
||||
guard let self = self else {return}
|
||||
if loadModel == nil {
|
||||
isHidden = true
|
||||
emptyImageView.isHidden = false
|
||||
}else {
|
||||
MP_AnalyticsManager.shared.search_result_pvAction()
|
||||
loadModel.resultReloadBlock = {
|
||||
[weak self] in
|
||||
//刷新分页控制器
|
||||
guard let self = self else {return}
|
||||
MP_HUD.hideNow()
|
||||
isHidden = false
|
||||
MP_AnalyticsManager.shared.search_resultsuccess_actionAction()
|
||||
dataSource.titles = loadModel?.sectionLists.compactMap({$0.title}) ?? []
|
||||
dataSource.reloadData(selectedIndex: 0)
|
||||
segmentView.reloadData()
|
||||
emptyImageView.isHidden = !(dataSource.titles.count == 0)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,7 +74,7 @@ class MPPositive_SearchResultsShowView: UIView {
|
||||
return listContainerView
|
||||
}()
|
||||
//数据空图片
|
||||
private lazy var emptyImageView:UIImageView = {
|
||||
lazy var emptyImageView:UIImageView = {
|
||||
let imageView = UIImageView(image: UIImage(named: "empty"))
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
return imageView
|
||||
@ -78,6 +82,7 @@ class MPPositive_SearchResultsShowView: UIView {
|
||||
var scrollBlock:(() -> Void)?
|
||||
//选中内容
|
||||
var chooseItemBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
|
||||
var moreBlock:((MPPositive_SearchResultItemViewModel) -> Void)?
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .init(hex: "1A1A1A")
|
||||
@ -144,6 +149,13 @@ extension MPPositive_SearchResultsShowView: JXSegmentedListContainerViewDataSour
|
||||
chooseItemBlock!(item)
|
||||
}
|
||||
}
|
||||
showView.moreBlock = {
|
||||
[weak self] (itemView) in
|
||||
guard let self = self else {return}
|
||||
if moreBlock != nil {
|
||||
moreBlock!(itemView)
|
||||
}
|
||||
}
|
||||
return showView
|
||||
}else {
|
||||
//展示分类结果
|
||||
@ -154,6 +166,13 @@ extension MPPositive_SearchResultsShowView: JXSegmentedListContainerViewDataSour
|
||||
self?.scrollBlock!()
|
||||
}
|
||||
}
|
||||
showView.moreBlock = {
|
||||
[weak self] (itemView) in
|
||||
guard let self = self else {return}
|
||||
if moreBlock != nil {
|
||||
moreBlock!(itemView)
|
||||
}
|
||||
}
|
||||
return showView
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,34 +10,34 @@
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MPSideA_AboutViewController" customModule="MusicPlayer" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="versionView" destination="4fU-O9-pas" id="cz8-Dk-J8d"/>
|
||||
<outlet property="view" destination="Cvd-ZP-KOW" id="sSX-FH-ZL7"/>
|
||||
<outlet property="versionView" destination="Wrk-iL-cGv" id="sjM-ZU-PyB"/>
|
||||
<outlet property="view" destination="VwI-HU-Cye" id="KxG-DI-JCd"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<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"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<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"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="691" id="d8K-qP-7hT">
|
||||
<constraint firstAttribute="height" constant="691" id="ES1-v2-JTn">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
</constraints>
|
||||
</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"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="42" id="Kld-pl-2rN">
|
||||
<constraint firstAttribute="height" constant="42" id="6ds-ro-ddB">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
<constraint firstAttribute="height" constant="42" id="bc8-oI-D4b">
|
||||
<constraint firstAttribute="width" constant="42" id="KQK-bd-ttH">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
@ -46,24 +46,24 @@
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" backgroundImage="Pop‘logo"/>
|
||||
<connections>
|
||||
<action selector="popClick:" destination="-1" eventType="touchUpInside" id="iM4-K0-ZQH"/>
|
||||
<action selector="popClick:" destination="-1" eventType="touchUpInside" id="Unh-Z8-VGv"/>
|
||||
</connections>
|
||||
</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"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</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"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="80" id="L2T-XG-rgG">
|
||||
<constraint firstAttribute="width" constant="80" id="6aP-Fe-KRS">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
<constraint firstAttribute="width" constant="80" id="zrl-r2-Cfr">
|
||||
<constraint firstAttribute="height" constant="80" id="h6c-M8-B3C">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
@ -71,7 +71,7 @@
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
|
||||
<real key="value" value="15"/>
|
||||
<real key="value" value="12"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
|
||||
<color key="value" red="0.50196078430000002" green="0.97647058819999999" blue="0.53333333329999999" alpha="0.75449892240000005" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
@ -81,31 +81,16 @@
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="MUSICOO'logo" translatesAutoresizingMaskIntoConstraints="NO" id="WxR-ti-5D3">
|
||||
<rect key="frame" x="125.5" y="244" width="124" height="19"/>
|
||||
<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"/>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wrk-iL-cGv">
|
||||
<rect key="frame" x="0.0" y="296" width="375" height="60"/>
|
||||
<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"/>
|
||||
<fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="13"/>
|
||||
<color key="textColor" white="1" alpha="0.40000000000000002" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</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"/>
|
||||
<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"/>
|
||||
@ -114,66 +99,75 @@
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="zjg-cK-5H2" firstAttribute="centerY" secondItem="4fU-O9-pas" secondAttribute="centerY" id="Tls-lR-3ux"/>
|
||||
<constraint firstItem="zjg-cK-5H2" firstAttribute="leading" secondItem="4fU-O9-pas" secondAttribute="leading" constant="15" id="Tn4-xz-DYE">
|
||||
<constraint firstAttribute="trailing" secondItem="miJ-lx-eHK" secondAttribute="trailing" constant="15" id="844-fM-WCc">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</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>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
<constraint firstItem="Aeb-L1-sB0" firstAttribute="centerY" secondItem="4fU-O9-pas" secondAttribute="centerY" id="mhe-B3-Ieg"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Aeb-L1-sB0" secondAttribute="trailing" constant="15" id="oBb-b6-a4B">
|
||||
<constraint firstItem="miJ-lx-eHK" firstAttribute="centerY" secondItem="Wrk-iL-cGv" secondAttribute="centerY" id="Dr2-lQ-0pR"/>
|
||||
<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>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
</constraints>
|
||||
</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>
|
||||
<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"/>
|
||||
<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>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
<constraint firstItem="4fU-O9-pas" firstAttribute="top" secondItem="WxR-ti-5D3" secondAttribute="bottom" constant="40" id="3Hk-O3-plC"/>
|
||||
<constraint firstItem="be2-1M-VPB" firstAttribute="leading" secondItem="Cvd-ZP-KOW" secondAttribute="leading" id="9tf-ol-TR5"/>
|
||||
<constraint firstItem="YYU-ac-9gn" firstAttribute="centerX" secondItem="Cvd-ZP-KOW" secondAttribute="centerX" id="H5x-yw-4iD"/>
|
||||
<constraint firstItem="0xx-Pi-pKv" firstAttribute="centerX" secondItem="Cvd-ZP-KOW" secondAttribute="centerX" id="K2R-cQ-Zag"/>
|
||||
<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">
|
||||
<constraint firstItem="Wrk-iL-cGv" firstAttribute="trailing" secondItem="UZJ-Fu-b9f" secondAttribute="trailing" id="Vu6-Fm-dFV"/>
|
||||
<constraint firstItem="Wrk-iL-cGv" firstAttribute="leading" secondItem="UZJ-Fu-b9f" secondAttribute="leading" id="Zo7-eO-o9U"/>
|
||||
<constraint firstItem="Zfn-fz-UtF" firstAttribute="centerX" secondItem="bdA-Qf-Wki" secondAttribute="centerX" id="cTy-pH-dNH"/>
|
||||
<constraint firstItem="Zfn-fz-UtF" firstAttribute="top" secondItem="bdA-Qf-Wki" secondAttribute="bottom" constant="12" id="dJs-I4-rFf">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</constraint>
|
||||
<constraint firstAttribute="trailing" secondItem="be2-1M-VPB" secondAttribute="trailing" id="jLP-Wn-udz"/>
|
||||
<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">
|
||||
<constraint firstItem="5gw-lg-ffB" firstAttribute="leading" secondItem="UZJ-Fu-b9f" secondAttribute="leading" constant="16" id="h5u-9Q-G99">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</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>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="adapterScreen" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</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>
|
||||
<point key="canvasLocation" x="130.40000000000001" y="-12.143928035982009"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ICON" width="1024" height="1024"/>
|
||||
<image name="MUSICOO'logo" width="124" height="19"/>
|
||||
<image name="ICON" width="256" height="256"/>
|
||||
<image name="Pop‘logo" width="42" height="42"/>
|
||||
<image name="Set'mask" width="375" height="691"/>
|
||||
</resources>
|
||||
|
||||
@ -47,9 +47,9 @@ extension MPSideA_SettingViewController: UITableViewDataSource, UITableViewDeleg
|
||||
navigationController?.pushViewController(aboutVC, animated: true)
|
||||
case 1://Feedback
|
||||
let alert = UIAlertController(title: "Feedback", message: "If you have any comments or suggestions, please contact us at the following e-mail address", preferredStyle: .actionSheet)
|
||||
let email = UIAlertAction(title: "support@musicoo.app", style: .default) { (_) in
|
||||
let email = UIAlertAction(title: "marketing@lux-ad.com", style: .default) { (_) in
|
||||
//将邮箱复制到剪切板中
|
||||
UIPasteboard.general.string = "support@musicoo.app"
|
||||
UIPasteboard.general.string = "marketing@lux-ad.com"
|
||||
MP_HUD.text("Successfully copied the e-mail address to the clipboard", delay: 1.0, completion: nil)
|
||||
}
|
||||
alert.addAction(email)
|
||||
@ -62,7 +62,7 @@ extension MPSideA_SettingViewController: UITableViewDataSource, UITableViewDeleg
|
||||
//分享图片icon
|
||||
let image = UIImage(named: "ICON")
|
||||
//设置分享路径
|
||||
let url = URL(string: "https://musicoo.app/")
|
||||
let url = URL(string: "https://musiclax.mystrikingly.com/")
|
||||
let activityItems = [text,image as Any,url as Any]
|
||||
//弹出分享框
|
||||
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities:nil)
|
||||
|
||||
@ -53,7 +53,7 @@ class MPSideA_HomeViewController: MPSideA_BaseViewController {
|
||||
super.viewWillDisappear(animated)
|
||||
//移除所有通知
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
MP_AnalyticsManager.shared.home_a_pvAction()
|
||||
}
|
||||
deinit {
|
||||
|
||||
|
||||
27
MusicPlayer/PrivacyInfo.xcprivacy
Normal 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>
|
||||