Explorar el Código

音乐播放进度开发完毕

100Years hace 1 mes
padre
commit
51a4aa88f0
Se han modificado 39 ficheros con 932 adiciones y 537 borrados
  1. 35 11
      AIRingtone.xcodeproj/project.pbxproj
  2. 22 0
      AIRingtone/Assets.xcassets/Common/play_right_shade.imageset/Contents.json
  3. BIN
      AIRingtone/Assets.xcassets/Common/play_right_shade.imageset/play_right_shade@2x.png
  4. BIN
      AIRingtone/Assets.xcassets/Common/play_right_shade.imageset/play_right_shade@3x.png
  5. 22 0
      AIRingtone/Assets.xcassets/Common/refresh_white.imageset/Contents.json
  6. BIN
      AIRingtone/Assets.xcassets/Common/refresh_white.imageset/refresh_white@2x.png
  7. BIN
      AIRingtone/Assets.xcassets/Common/refresh_white.imageset/refresh_white@3x.png
  8. 22 0
      AIRingtone/Assets.xcassets/Common/ringGenerateProgress.imageset/Contents.json
  9. BIN
      AIRingtone/Assets.xcassets/Common/ringGenerateProgress.imageset/generateProgress@2x.png
  10. BIN
      AIRingtone/Assets.xcassets/Common/ringGenerateProgress.imageset/generateProgress@3x.png
  11. 22 0
      AIRingtone/Assets.xcassets/Common/ring_icon.imageset/Contents.json
  12. BIN
      AIRingtone/Assets.xcassets/Common/ring_icon.imageset/ring_icon@2x.png
  13. BIN
      AIRingtone/Assets.xcassets/Common/ring_icon.imageset/ring_icon@3x.png
  14. BIN
      AIRingtone/Assets.xcassets/Common/ringplaceholderImage.imageset/ringplaceholderImage@2x.png
  15. BIN
      AIRingtone/Assets.xcassets/Common/ringplaceholderImage.imageset/ringplaceholderImage@3x.png
  16. BIN
      AIRingtone/Assets.xcassets/Common/ringtone_loading.imageset/ringtone_loading@2x.png
  17. BIN
      AIRingtone/Assets.xcassets/Common/ringtone_loading.imageset/ringtone_loading@3x.png
  18. BIN
      AIRingtone/Assets.xcassets/Common/ringtone_pause.imageset/ringtone_pause@2x.png
  19. BIN
      AIRingtone/Assets.xcassets/Common/ringtone_pause.imageset/ringtone_pause@3x.png
  20. BIN
      AIRingtone/Assets.xcassets/Common/ringtone_play.imageset/ringtone_play@2x.png
  21. BIN
      AIRingtone/Assets.xcassets/Common/ringtone_play.imageset/ringtone_play@3x.png
  22. 36 36
      AIRingtone/Business/TSAIPhotoVC/TSGeneralPicVC/TSGenneralPicVM.swift
  23. 7 26
      AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/View/TSAIRintoneHistoryCell.swift
  24. 2 25
      AIRingtone/Business/TSAIRintoneVC/TSGeneralRintoneVC/TSGeneralRintoneVC.swift
  25. 36 36
      AIRingtone/Business/TSAIRintoneVC/TSGeneralRintoneVC/TSGeneralRintoneVM.swift
  26. 1 1
      AIRingtone/Business/TSCollectionViewVM/TSCollectionViewVM+Config.swift
  27. 0 158
      AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/View/TSDiscoverListCell.swift
  28. 7 8
      AIRingtone/Business/TSThemeVC/TSThemeBrowseVC/TSThemeBrowseVC.swift
  29. 1 1
      AIRingtone/Business/TSThemeVC/TSThemeSetVC/TSThemeSetRingToneView.swift
  30. 2 26
      AIRingtone/Business/TSThemeVC/TSThemeSetVC/TSThemeSetVC.swift
  31. 129 9
      AIRingtone/Business/VIewTool/TSRingToneCellView.swift
  32. 227 0
      AIRingtone/Common/Tool/OperationQueue/TSBaseOperation.swift
  33. 61 0
      AIRingtone/Common/Tool/OperationQueue/TSBaseOperationQueue.swift
  34. 7 0
      AIRingtone/Common/Tool/OperationQueue/TSGenerateRintoneOperation/TSGenerateRintoneOperation.swift
  35. 8 0
      AIRingtone/Common/Tool/OperationQueue/TSGenerateRintoneOperation/TSGenerateRintoneOperationQueue.swift
  36. 220 53
      AIRingtone/Common/Tool/TSAudioPlayer/TSAudioPlayer.swift
  37. 17 34
      AIRingtone/Common/Tool/TSBandRingTool/TSBandRingTool.swift
  38. 42 113
      AIRingtone/Common/Tool/TSBusinessAudioPlayer.swift
  39. 6 0
      AIRingtone/Common/Tool/TSPublicContent.swift

+ 35 - 11
AIRingtone.xcodeproj/project.pbxproj

@@ -50,6 +50,8 @@
 		A83F87202D794FF000D29B1B /* TSAIPhotoImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A83F871F2D794FE600D29B1B /* TSAIPhotoImageCell.swift */; };
 		A83F87222D7953C000D29B1B /* Notification+TSEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = A83F87212D7953BA00D29B1B /* Notification+TSEx.swift */; };
 		A83F87242D7954BB00D29B1B /* TSAIPhotoChildVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A83F87232D7954B800D29B1B /* TSAIPhotoChildVM.swift */; };
+		A840A7EB2D8D81CD0044B8B9 /* TSGenerateRintoneOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840A7EA2D8D81C90044B8B9 /* TSGenerateRintoneOperationQueue.swift */; };
+		A840A7ED2D8D81DB0044B8B9 /* TSGenerateRintoneOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840A7EC2D8D81DA0044B8B9 /* TSGenerateRintoneOperation.swift */; };
 		A868A89A2D75505E00F6D884 /* TSThemeBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868A8992D75505800F6D884 /* TSThemeBannerCell.swift */; };
 		A868A89C2D75506C00F6D884 /* TSThemeContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868A89B2D75506500F6D884 /* TSThemeContentCell.swift */; };
 		A868A8A22D7560B900F6D884 /* TSPageNullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868A89D2D7560B900F6D884 /* TSPageNullView.swift */; };
@@ -110,7 +112,6 @@
 		A899D38D2D891C5600AB9C1C /* TSDiscoverCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D38C2D891C4D00AB9C1C /* TSDiscoverCell.swift */; };
 		A899D3932D8924A300AB9C1C /* TSDiscoverListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3922D8924A200AB9C1C /* TSDiscoverListVC.swift */; };
 		A899D3962D89258B00AB9C1C /* TSDiscoverListVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3952D89258600AB9C1C /* TSDiscoverListVM.swift */; };
-		A899D3992D8947D800AB9C1C /* TSDiscoverListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3982D8947D200AB9C1C /* TSDiscoverListCell.swift */; };
 		A899D39C2D894F9800AB9C1C /* TSRingDownVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D39B2D894F9600AB9C1C /* TSRingDownVC.swift */; };
 		A899D3A32D896A7B00AB9C1C /* TSRingDownNullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D39F2D895F3C00AB9C1C /* TSRingDownNullView.swift */; };
 		A899D3A52D89786200AB9C1C /* TSAudioAVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3A42D89785E00AB9C1C /* TSAudioAVPlayer.swift */; };
@@ -127,6 +128,9 @@
 		A8CC55892D798E5D002E0CAA /* TSTextGeneralRintoneVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8CC55882D798E5A002E0CAA /* TSTextGeneralRintoneVM.swift */; };
 		A8CC558B2D79905A002E0CAA /* TSCreatBtnView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8CC558A2D799051002E0CAA /* TSCreatBtnView.swift */; };
 		A8CC55902D799F9F002E0CAA /* TSAIRintoneVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8CC558F2D799F9C002E0CAA /* TSAIRintoneVM.swift */; };
+		A8D776F82D8D3448007EAB35 /* TSBaseOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D776F72D8D343F007EAB35 /* TSBaseOperationQueue.swift */; };
+		A8D776FA2D8D345C007EAB35 /* TSBaseOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D776F92D8D345B007EAB35 /* TSBaseOperation.swift */; };
+		A8D776FE2D8D3E5E007EAB35 /* TSPublicContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D776FD2D8D3E4E007EAB35 /* TSPublicContent.swift */; };
 		A8DEC2702D7C395C002EB948 /* TSThemeCopyrightVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8DEC26F2D7C395B002EB948 /* TSThemeCopyrightVC.swift */; };
 		F5FF0EC10B0056B65FDB9C78 /* Pods_AIRingtone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39C823AB7F3D49E8B2356E07 /* Pods_AIRingtone.framework */; };
 /* End PBXBuildFile section */
@@ -180,6 +184,8 @@
 		A83F871F2D794FE600D29B1B /* TSAIPhotoImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIPhotoImageCell.swift; sourceTree = "<group>"; };
 		A83F87212D7953BA00D29B1B /* Notification+TSEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+TSEx.swift"; sourceTree = "<group>"; };
 		A83F87232D7954B800D29B1B /* TSAIPhotoChildVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIPhotoChildVM.swift; sourceTree = "<group>"; };
+		A840A7EA2D8D81C90044B8B9 /* TSGenerateRintoneOperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGenerateRintoneOperationQueue.swift; sourceTree = "<group>"; };
+		A840A7EC2D8D81DA0044B8B9 /* TSGenerateRintoneOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGenerateRintoneOperation.swift; sourceTree = "<group>"; };
 		A868A8992D75505800F6D884 /* TSThemeBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThemeBannerCell.swift; sourceTree = "<group>"; };
 		A868A89B2D75506500F6D884 /* TSThemeContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThemeContentCell.swift; sourceTree = "<group>"; };
 		A868A89D2D7560B900F6D884 /* TSPageNullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPageNullView.swift; sourceTree = "<group>"; };
@@ -242,7 +248,6 @@
 		A899D38C2D891C4D00AB9C1C /* TSDiscoverCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverCell.swift; sourceTree = "<group>"; };
 		A899D3922D8924A200AB9C1C /* TSDiscoverListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverListVC.swift; sourceTree = "<group>"; };
 		A899D3952D89258600AB9C1C /* TSDiscoverListVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverListVM.swift; sourceTree = "<group>"; };
-		A899D3982D8947D200AB9C1C /* TSDiscoverListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverListCell.swift; sourceTree = "<group>"; };
 		A899D39B2D894F9600AB9C1C /* TSRingDownVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRingDownVC.swift; sourceTree = "<group>"; };
 		A899D39F2D895F3C00AB9C1C /* TSRingDownNullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRingDownNullView.swift; sourceTree = "<group>"; };
 		A899D3A42D89785E00AB9C1C /* TSAudioAVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAudioAVPlayer.swift; sourceTree = "<group>"; };
@@ -259,6 +264,9 @@
 		A8CC55882D798E5A002E0CAA /* TSTextGeneralRintoneVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextGeneralRintoneVM.swift; sourceTree = "<group>"; };
 		A8CC558A2D799051002E0CAA /* TSCreatBtnView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCreatBtnView.swift; sourceTree = "<group>"; };
 		A8CC558F2D799F9C002E0CAA /* TSAIRintoneVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIRintoneVM.swift; sourceTree = "<group>"; };
+		A8D776F72D8D343F007EAB35 /* TSBaseOperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseOperationQueue.swift; sourceTree = "<group>"; };
+		A8D776F92D8D345B007EAB35 /* TSBaseOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseOperation.swift; sourceTree = "<group>"; };
+		A8D776FD2D8D3E4E007EAB35 /* TSPublicContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPublicContent.swift; sourceTree = "<group>"; };
 		A8DEC26F2D7C395B002EB948 /* TSThemeCopyrightVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThemeCopyrightVC.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -387,6 +395,8 @@
 		A80EDEBA2D718CEA003CD332 /* Tool */ = {
 			isa = PBXGroup;
 			children = (
+				A8D776FD2D8D3E4E007EAB35 /* TSPublicContent.swift */,
+				A8D776F62D8D342D007EAB35 /* OperationQueue */,
 				A899D3CA2D89A53C00AB9C1C /* TSDownloadManager.swift */,
 				A899D3A42D89785E00AB9C1C /* TSAudioAVPlayer.swift */,
 				A899D3CE2D89AD4800AB9C1C /* TSAudioPlayerFileTool.swift */,
@@ -618,20 +628,22 @@
 			path = View;
 			sourceTree = "<group>";
 		};
-		A868A8982D75505100F6D884 /* View */ = {
+		A840A7E92D8D81350044B8B9 /* TSGenerateRintoneOperation */ = {
 			isa = PBXGroup;
 			children = (
-				A868A89B2D75506500F6D884 /* TSThemeContentCell.swift */,
-				A868A8992D75505800F6D884 /* TSThemeBannerCell.swift */,
+				A840A7EA2D8D81C90044B8B9 /* TSGenerateRintoneOperationQueue.swift */,
+				A840A7EC2D8D81DA0044B8B9 /* TSGenerateRintoneOperation.swift */,
 			);
-			path = View;
+			path = TSGenerateRintoneOperation;
 			sourceTree = "<group>";
 		};
-		A868A89F2D7560B900F6D884 /* UILabel */ = {
+		A868A8982D75505100F6D884 /* View */ = {
 			isa = PBXGroup;
 			children = (
+				A868A89B2D75506500F6D884 /* TSThemeContentCell.swift */,
+				A868A8992D75505800F6D884 /* TSThemeBannerCell.swift */,
 			);
-			path = UILabel;
+			path = View;
 			sourceTree = "<group>";
 		};
 		A868A8A12D7560B900F6D884 /* VIewTool */ = {
@@ -643,7 +655,6 @@
 				A868A8CF2D76D02300F6D884 /* TSRingToneCellView.swift */,
 				A868A89D2D7560B900F6D884 /* TSPageNullView.swift */,
 				A868A89E2D7560B900F6D884 /* TSCommonloadingView.swift */,
-				A868A89F2D7560B900F6D884 /* UILabel */,
 				A868A8A02D7560B900F6D884 /* TSViewTool.swift */,
 			);
 			path = VIewTool;
@@ -910,7 +921,6 @@
 			isa = PBXGroup;
 			children = (
 				A899D3D02D89B14400AB9C1C /* TSDiscoverListCellVM.swift */,
-				A899D3982D8947D200AB9C1C /* TSDiscoverListCell.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -1004,6 +1014,16 @@
 			path = ViewModel;
 			sourceTree = "<group>";
 		};
+		A8D776F62D8D342D007EAB35 /* OperationQueue */ = {
+			isa = PBXGroup;
+			children = (
+				A840A7E92D8D81350044B8B9 /* TSGenerateRintoneOperation */,
+				A8D776F72D8D343F007EAB35 /* TSBaseOperationQueue.swift */,
+				A8D776F92D8D345B007EAB35 /* TSBaseOperation.swift */,
+			);
+			path = OperationQueue;
+			sourceTree = "<group>";
+		};
 		A8DEC26E2D7C3948002EB948 /* TSThemeCopyrightVC */ = {
 			isa = PBXGroup;
 			children = (
@@ -1139,6 +1159,7 @@
 			files = (
 				A899D3742D87B15F00AB9C1C /* TSPurchaseTool.swift in Sources */,
 				A868A9072D77EED800F6D884 /* TSTGPTitleView.swift in Sources */,
+				A840A7EB2D8D81CD0044B8B9 /* TSGenerateRintoneOperationQueue.swift in Sources */,
 				A868A8C22D76A2A700F6D884 /* UIFont+TSEx.swift in Sources */,
 				A868A91A2D78559800F6D884 /* TSProgressState.swift in Sources */,
 				A899D3842D88303E00AB9C1C /* TSDiscoverVC.swift in Sources */,
@@ -1147,15 +1168,18 @@
 				A83F87202D794FF000D29B1B /* TSAIPhotoImageCell.swift in Sources */,
 				A899D3932D8924A300AB9C1C /* TSDiscoverListVC.swift in Sources */,
 				A868A8DB2D76F00C00F6D884 /* TSBandRingTool.swift in Sources */,
+				A8D776FA2D8D345C007EAB35 /* TSBaseOperation.swift in Sources */,
 				A899D3602D82C8D800AB9C1C /* TSPurchaseManager.swift in Sources */,
 				A868A8F12D77081C00F6D884 /* TSContactsTool.swift in Sources */,
 				A80EDF2D2D71C2CA003CD332 /* TSThemeModel.swift in Sources */,
 				A80EDE582D718623003CD332 /* AppDelegate.swift in Sources */,
 				A868A8EF2D77041400F6D884 /* TSLoadingAnimation.swift in Sources */,
 				A80EDE652D718CAF003CD332 /* GlobalImports.swift in Sources */,
+				A840A7ED2D8D81DB0044B8B9 /* TSGenerateRintoneOperation.swift in Sources */,
 				A868A9092D7828EC00F6D884 /* TSTGPPhotoStyleView.swift in Sources */,
+				A8D776FE2D8D3E5E007EAB35 /* TSPublicContent.swift in Sources */,
 				A8DEC2702D7C395C002EB948 /* TSThemeCopyrightVC.swift in Sources */,
-				A899D3992D8947D800AB9C1C /* TSDiscoverListCell.swift in Sources */,
+				A8D776F82D8D3448007EAB35 /* TSBaseOperationQueue.swift in Sources */,
 				A80EDF112D718ED1003CD332 /* TSAIRintoneVC.swift in Sources */,
 				A80EDF1B2D71AC90003CD332 /* TSCollectionViewVM.swift in Sources */,
 				A80EDE612D718AF4003CD332 /* TSConfig.swift in Sources */,

+ 22 - 0
AIRingtone/Assets.xcassets/Common/play_right_shade.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "play_right_shade@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "play_right_shade@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/play_right_shade.imageset/play_right_shade@2x.png


BIN
AIRingtone/Assets.xcassets/Common/play_right_shade.imageset/play_right_shade@3x.png


+ 22 - 0
AIRingtone/Assets.xcassets/Common/refresh_white.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "refresh_white@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "refresh_white@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/refresh_white.imageset/refresh_white@2x.png


BIN
AIRingtone/Assets.xcassets/Common/refresh_white.imageset/refresh_white@3x.png


+ 22 - 0
AIRingtone/Assets.xcassets/Common/ringGenerateProgress.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "generateProgress@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "generateProgress@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/ringGenerateProgress.imageset/generateProgress@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ringGenerateProgress.imageset/generateProgress@3x.png


+ 22 - 0
AIRingtone/Assets.xcassets/Common/ring_icon.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ring_icon@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ring_icon@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/ring_icon.imageset/ring_icon@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ring_icon.imageset/ring_icon@3x.png


BIN
AIRingtone/Assets.xcassets/Common/ringplaceholderImage.imageset/ringplaceholderImage@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ringplaceholderImage.imageset/ringplaceholderImage@3x.png


BIN
AIRingtone/Assets.xcassets/Common/ringtone_loading.imageset/ringtone_loading@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ringtone_loading.imageset/ringtone_loading@3x.png


BIN
AIRingtone/Assets.xcassets/Common/ringtone_pause.imageset/ringtone_pause@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ringtone_pause.imageset/ringtone_pause@3x.png


BIN
AIRingtone/Assets.xcassets/Common/ringtone_play.imageset/ringtone_play@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ringtone_play.imageset/ringtone_play@3x.png


+ 36 - 36
AIRingtone/Business/TSAIPhotoVC/TSGeneralPicVC/TSGenneralPicVM.swift

@@ -48,50 +48,50 @@ class TSGenneralPicVM {
     var gennerateType:TSGennerateType = .poster
     var generatingProgress = 0
     
-//    //模拟数据
-//    func creatImageEmoji(text:String) {
-//        stateDatauPblished = (.start,nil)
-//        stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
-//
-//        kDelayOnMainThread(0.2) {
-//            self.stateDatauPblished = (.progressString(self.generating(progress: 0.5)),nil)
-//        }
-//
-//        kDelayOnMainThread(1.0) {
-//            if Bool.random() {
-//                let infoModel = TSActionInfoModel(JSON: self.gennerateType == .poster ? actionInfoDictPoster : actionInfoDictPhoto)
-//                self.stateDatauPblished = (.success(nil),infoModel)
-//            }else{
-//                self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
-//            }
-//        }
-//    }
-    
-    //width 和 height 必须是 32 的倍数
+    //模拟数据
     func creatImageEmoji(text:String) {
-        generatingProgress = 0
-        aiText = text
-        let postDict:[String : Any] = [
-            "prompt":text,
-            "width":textPicW,
-            "height":textPicH
-        ]
         stateDatauPblished = (.start,nil)
         stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
-        creatRequest = TSNetworkShared.post(urlType: .textPicCreate,parameters: postDict) { [weak self] data,error in
-            guard let self = self else { return }
-            if let dataDict = data as? [String:Any] ,
-               dataDict.safeInt(forKey: "code") == 200,
-               let actionId = dataDict["actionId"] as? Int{
-                if stopNetwork == false {
-                    self.getActionInfo(action_id:actionId)
-                }
+
+        kDelayOnMainThread(0.2) {
+            self.stateDatauPblished = (.progressString(self.generating(progress: 0.5)),nil)
+        }
+
+        kDelayOnMainThread(1.0) {
+            if Bool.random() {
+                let infoModel = TSActionInfoModel(JSON: self.gennerateType == .poster ? actionInfoDictPoster : actionInfoDictPhoto)
+                self.stateDatauPblished = (.success(nil),infoModel)
             }else{
-                self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
+                self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
             }
         }
     }
     
+//    //width 和 height 必须是 32 的倍数
+//    func creatImageEmoji(text:String) {
+//        generatingProgress = 0
+//        aiText = text
+//        let postDict:[String : Any] = [
+//            "prompt":text,
+//            "width":textPicW,
+//            "height":textPicH
+//        ]
+//        stateDatauPblished = (.start,nil)
+//        stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
+//        creatRequest = TSNetworkShared.post(urlType: .textPicCreate,parameters: postDict) { [weak self] data,error in
+//            guard let self = self else { return }
+//            if let dataDict = data as? [String:Any] ,
+//               dataDict.safeInt(forKey: "code") == 200,
+//               let actionId = dataDict["actionId"] as? Int{
+//                if stopNetwork == false {
+//                    self.getActionInfo(action_id:actionId)
+//                }
+//            }else{
+//                self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
+//            }
+//        }
+//    }
+    
     func getActionInfo(action_id:Int){
         queryRequest = TSNetworkShared.get(urlType: .actionInfo,parameters: ["action_id":action_id]) { [weak self] data,error in
             guard let self = self else { return }

+ 7 - 26
AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/View/TSAIRintoneHistoryCell.swift

@@ -35,7 +35,7 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
     
     lazy var ringView: TSRingToneCellView = {
         let ringToneView = TSRingToneCellView()
-        ringToneView.backgroundColor = .clear
+//        ringToneView.backgroundColor = .clear
         ringToneView.clickPlayHandel = { [weak self] isplay in
             guard let self = self else { return }
             
@@ -115,9 +115,9 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
     }
     
     
-    override var isSelected: Bool{
-        didSet{
-            debugPrint("isSelected = \(isSelected),indexPath = \(indexPath)")
+//    override var isSelected: Bool{
+//        didSet{
+//            debugPrint("isSelected = \(isSelected),indexPath = \(indexPath)")
 //            ringView.isloading = isSelected
 //            if isSelected, self.ringView.isPlay == false{
 //                
@@ -131,8 +131,8 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
 //                TSBusinessAudioPlayer.shared.stop()
 //                backgroundColor = .cardColor
 //            }
-        }
-    }
+//        }
+//    }
 
         
     var playSelf:Bool{
@@ -193,31 +193,12 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
     
     
     func changePlayerState(state:TSBusinessAudioPlayer.PlayerState){
-//        if self.isSelected == false {
-//        if self.modelIsSelected == false {
             if playSelf == false {
             self.ringView.isPlay = false
             backgroundColor = .cardColor
             return
         }
-        
-        switch state {
-        case .loading(let progress):
-            if progress == 0.0 {
-                self.ringView.isloading = true
-            }else if progress == 1.0 {
-                self.ringView.isloading = false
-            }
-        case .play:
-            self.ringView.isPlay = true
-            backgroundColor = "#3C213F".uiColor
-        case .stop:
-            self.ringView.isloading = false
-            self.ringView.isPlay = false
-            backgroundColor = .cardColor
-        default:
-            break
-        }
+        ringView.handelAudioPlayerStateChange(state: state)
     }
 
     deinit {

+ 2 - 25
AIRingtone/Business/TSAIRintoneVC/TSGeneralRintoneVC/TSGeneralRintoneVC.swift

@@ -118,14 +118,8 @@ class TSGeneralRintoneVC: TSBottomAlertVC {
             guard let self = self else { return }
             self.upDateView(state: state, model: model)
         }.store(in: &cancellable)
-        
-        NotificationCenter.default.addObserver(forName: .kBusinessAudioStateChange, object: nil, queue: nil) { notification in
-            if let userInfo = notification.userInfo as? [String: TSBusinessAudioPlayer.PlayerState], let state = userInfo["PlayerState"] {
-                kExecuteOnMainThread {
-                    self.audioPlayerStateChange(state: state)
-                }
-            }
-        }
+
+        ringView.monitorPlayStateDefaultHandle()
     }
 }
 
@@ -135,22 +129,5 @@ extension TSGeneralRintoneVC{
         ringView.nameLab.text = model.response.title
         ringView.setCoverImageView(urlString: model.response.coverUrl)
     }
-    
-    func audioPlayerStateChange(state:TSBusinessAudioPlayer.PlayerState){
-        switch state {
-        case .loading(let progress):
-            if progress == 0.0 {
-                ringView.isloading = true
-            }else if progress == 1.0 {
-                ringView.isloading = false
-            }
-        case .play:
-            ringView.isPlay = true
-        case .stop:
-            ringView.isPlay = false
-        default:
-            break
-        }
-    }
 }
 

+ 36 - 36
AIRingtone/Business/TSAIRintoneVC/TSGeneralRintoneVC/TSGeneralRintoneVM.swift

@@ -29,50 +29,50 @@ class TSGeneralRintoneVM {
     @Published var stateDatauPblished:(TSProgressState,TSActionInfoModel?) = (TSProgressState.none,nil)
     var aiText:String = ""
     var generatingProgress = 0
-//    //模拟数据
-//    func creatRintone(text:String) {
-//
-//        stateDatauPblished = (.start,nil)
-//        stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
-//
-//        kDelayOnMainThread(0.2) {
-//            self.stateDatauPblished = (.progressString(self.generating(progress: 0.5)),nil)
-//        }
-//
-//        kDelayOnMainThread(1.0) {
-//            if Bool.random() {
-//                let infoModel = TSActionInfoModel(JSON: actionInfoDict)
-//                self.stateDatauPblished = (.success(nil),infoModel)
-//            }else{
-//                self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
-//            }
-//        }
-//    }
-    
- 
+    //模拟数据
     func creatRintone(text:String) {
-        generatingProgress = 0
-        aiText = text
-        let postDict:[String : Any] = [
-            "prompt":text,
-            "duration":20
-        ]
+
         stateDatauPblished = (.start,nil)
         stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
-        creatRequest = TSNetworkShared.post(urlType: .musicCreate,parameters: postDict) { [weak self] data,error in
-            guard let self = self else { return }
-            if let dataDict = data as? [String:Any] ,
-               dataDict.safeInt(forKey: "code") == 200,
-               let actionId = dataDict["actionId"] as? Int{
-                if stopNetwork == false {
-                    self.getActionInfo(action_id:actionId)
-                }
+
+        kDelayOnMainThread(0.2) {
+            self.stateDatauPblished = (.progressString(self.generating(progress: 0.5)),nil)
+        }
+
+        kDelayOnMainThread(1.0) {
+            if Bool.random() {
+                let infoModel = TSActionInfoModel(JSON: actionInfoDict)
+                self.stateDatauPblished = (.success(nil),infoModel)
             }else{
-                self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
+                self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
             }
         }
     }
     
+ 
+//    func creatRintone(text:String) {
+//        generatingProgress = 0
+//        aiText = text
+//        let postDict:[String : Any] = [
+//            "prompt":text,
+//            "duration":20
+//        ]
+//        stateDatauPblished = (.start,nil)
+//        stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
+//        creatRequest = TSNetworkShared.post(urlType: .musicCreate,parameters: postDict) { [weak self] data,error in
+//            guard let self = self else { return }
+//            if let dataDict = data as? [String:Any] ,
+//               dataDict.safeInt(forKey: "code") == 200,
+//               let actionId = dataDict["actionId"] as? Int{
+//                if stopNetwork == false {
+//                    self.getActionInfo(action_id:actionId)
+//                }
+//            }else{
+//                self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
+//            }
+//        }
+//    }
+    
     func getActionInfo(action_id:Int){
         queryRequest = TSNetworkShared.get(urlType: .actionInfo,parameters: ["action_id":action_id]) { [weak self] data,error in
             guard let self = self else { return }

+ 1 - 1
AIRingtone/Business/TSCollectionViewVM/TSCollectionViewVM+Config.swift

@@ -264,7 +264,7 @@ let ringListConfig:TSColVVMSizeConfig = {
     let h = w/originalScale
     
     let cellSize = CGSizeMake(w, h)
-    let cellClass = TSAIRintoneHistoryCell.self // TSDiscoverListCell.self
+    let cellClass = TSAIRintoneHistoryCell.self
     
     let headerViewSize = CGSizeMake(k_ScreenWidth, 0)
     let headerView = TSColVVMSectionHeaderView.self

+ 0 - 158
AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/View/TSDiscoverListCell.swift

@@ -1,158 +0,0 @@
-//
-//  TSDiscoverListCell.swift
-//  AIRingtone
-//
-//  Created by 100Years on 2025/3/17.
-//
-
-class TSDiscoverListCell: TSBaseCollectionCell {
-    
-    static let cellID = "TSDiscoverListCell"
-    lazy var setRingBtn: TSUIExpandedTouchButton = {
-        let setRingBtn = TSUIExpandedTouchButton()
-        setRingBtn.setImage(UIImage(named: "ai_setRing_icon"), for: .normal)
-        return setRingBtn
-    }()
-    
-    lazy var ringView: TSRingToneCellView = {
-        let ringToneView = TSRingToneCellView()
-        ringToneView.backgroundColor = .clear
-        ringToneView.playBtn.addTarget(self, action: #selector(clickPlay), for: .touchUpInside)
-        return ringToneView
-    }()
-    
-    
-    lazy var exampleView: UIView = {
-        let exampleView = UIView()
-        exampleView.backgroundColor = "#7E57F4".uiColor
-        
-        let textLabel = UILabel.createLabel(
-            text: "Example".localized,
-            font: .font(size: 12),
-            textColor: .white
-        )
-        
-        exampleView.addSubview(textLabel)
-        textLabel.snp.makeConstraints { make in
-            make.top.edges.equalTo(UIEdgeInsets(top: 4, left: 6, bottom: 4, right: 6))
-        }
-        
-        kDelayMainShort {
-            exampleView.makeCorner([.topLeft,.bottomLeft], radius: 10)
-        }
-//        exampleView.isHidden = true
-        return exampleView
-    }()
-    
-    var ringModel:TSRingModel?{
-        didSet{
-            guard let ringModel = ringModel else { return }
-            ringView.timeLab.text = Float(ringModel.duration).floatToMinuteSecond()
-            ringView.nameLab.text = ringModel.title
-            ringView.setCoverImageView(urlString: "")
-            exampleView.isHidden = true
-        }
-    }
-    
-    override var isSelected: Bool{
-        didSet{
-            ringView.isloading = isSelected
-            if isSelected, self.ringView.isPlay == false{
-                TSBusinessAudioPlayer.shared.playUrlString( ringModel?.audioUrl)
-            }else{
-                TSBusinessAudioPlayer.shared.stop()
-                backgroundColor = .cardColor
-            }
-        }
-    }
-    
-    override func creatUI() {
-        cornerRadius = 16.0
-        backgroundColor = .cardColor
-  
-        contentView.addSubview(exampleView)
-        exampleView.snp.makeConstraints { make in
-            make.trailing.equalToSuperview()
-            make.top.equalTo(0)
-            make.height.equalTo(20)
-        }
-  
-        contentView.addSubview(ringView)
-        ringView.snp.makeConstraints { make in
-            make.edges.equalToSuperview()
-        }
-    
-        contentView.addSubview(setRingBtn)
-        setRingBtn.snp.makeConstraints { make in
-            make.centerY.equalToSuperview()
-            make.trailing.equalTo(-16)
-            make.width.height.equalTo(20)
-        }
-        
-        NotificationCenter.default.addObserver(forName: .kBusinessAudioStateChange, object: nil, queue: nil) { notification in
-            if let userInfo = notification.userInfo as? [String: TSBusinessAudioPlayer.PlayerState], let state = userInfo["PlayerState"] {
-                kExecuteOnMainThread {
-                    self.changePlayerState(state: state)
-                }
-                
-            }
-        }
-    }
-
-    override func dealThings() {
-        
-    }
-    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
-        super.renderView(with: object, component: component, attributes: attributes)
-        if let itemModel = object as? TSColVVMItemModel{
-            if let dataModel = itemModel.dataModel as? TSRingModel{
-                ringModel = dataModel
-                
-                
-//                if let ringModel = ringModel {
-//                    if ringModel.audioPlayerFileTool == nil {
-//                        ringModel.audioPlayerFileTool = TSAudioPlayerFileTool(urlString: ringModel.audioUrl)
-//                    }
-//                }
-                
-                
-            }
-        }
-    }
-
-    deinit {
-        NotificationCenter.default.removeObserver(self)
-    }
-}
-extension TSDiscoverListCell{
-    
-    @objc func clickPlay(){
-        
-        
-    }
-    
-    
-    func changePlayerState(state:TSBusinessAudioPlayer.PlayerState){
-        if self.isSelected == false {
-            self.ringView.isPlay = false
-            return
-        }
-        
-        switch state {
-        case .loading(let progress):
-            if progress == 0.0 {
-                self.ringView.isloading = true
-            }else if progress == 1.0 {
-                self.ringView.isloading = false
-            }
-        case .play:
-            self.ringView.isPlay = true
-            backgroundColor = "#3C213F".uiColor
-        case .stop:
-            self.ringView.isPlay = false
-            backgroundColor = .cardColor
-        default:
-            break
-        }
-    }
-}

+ 7 - 8
AIRingtone/Business/TSThemeVC/TSThemeBrowseVC/TSThemeBrowseVC.swift

@@ -150,14 +150,7 @@ class TSThemeBrowseVC: TSBaseVC {
         }
         
         setVipUI()
-        
-        NotificationCenter.default.addObserver(forName: .kBusinessAudioStateChange, object: nil, queue: nil) { notification in
-            if let userInfo = notification.userInfo as? [String: TSBusinessAudioPlayer.PlayerState], let state = userInfo["PlayerState"] {
-                kExecuteOnMainThread {
-                    self.audioPlayerStateChange(state: state)
-                }
-            }
-        }
+    
     }
     
     func setVipUI(){
@@ -216,6 +209,12 @@ extension TSThemeBrowseVC {
 
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
+        
+        audioPlayer.stateChangedHandle = { [weak self] state  in
+            guard let self = self else { return }
+            self.audioPlayerStateChange(state: state)
+        }
+        
         if browseViewModel.audioPlayer?.playerUsable == true {
             browseViewModel.audioPlayer?.setVolume(volume: 1.0)
             browseViewModel.audioPlayer?.play()

+ 1 - 1
AIRingtone/Business/TSThemeVC/TSThemeSetVC/TSThemeSetRingToneView.swift

@@ -39,7 +39,7 @@ class TSThemeSetPosterView: TSThemeSetItemView {
         titleLabel.text = "Contact Poster".localized
         numImageView.image = UIImage(named: "num_2")
         iconImageView.image = UIImage(named: "theme_icon_poster")
-        setBtn.setTitle("Download".localized, for: .normal)
+//        setBtn.setTitle("Download".localized, for: .normal)
         netWorkImageView.cornerRadius = 12.0
         diyView.addSubview(netWorkImageView)
         netWorkImageView.snp.makeConstraints { make in

+ 2 - 26
AIRingtone/Business/TSThemeVC/TSThemeSetVC/TSThemeSetVC.swift

@@ -93,6 +93,7 @@ class TSThemeSetVC: TSBaseVC {
     }
     
     override func dealThings() {
+        ringView.cellView.monitorPlayStateDefaultHandle()
         ringView.cellView.nameLab.text = model.ringtoneName
         ringView.cellView.setCoverImageView(urlString: model.ringtoneCover)
         ringView.cellView.timeLab.text = duration.floatToMinuteSecond()
@@ -103,13 +104,6 @@ class TSThemeSetVC: TSBaseVC {
         NotificationCenter.default.addObserver(self, selector: #selector(vipInfoChanged), name: .kPurchaseDidChanged, object: nil)
         setVipUI()
         
-        NotificationCenter.default.addObserver(forName: .kBusinessAudioStateChange, object: nil, queue: nil) { notification in
-            if let userInfo = notification.userInfo as? [String: TSBusinessAudioPlayer.PlayerState], let state = userInfo["PlayerState"] {
-                kExecuteOnMainThread {
-                    self.audioPlayerStateChange(state: state)
-                }
-            }
-        }
     }
     
     @objc func vipInfoChanged() {
@@ -123,25 +117,7 @@ class TSThemeSetVC: TSBaseVC {
         self.posterView.setBtn.setImage(getNeedVip ? UIImage(named: "vip_icon_white"): nil, for: .normal)
         self.photoView.setBtn.setImage(getNeedVip ? UIImage(named: "vip_icon_white"): nil, for: .normal)
     }
-    
 
-    func audioPlayerStateChange(state:TSBusinessAudioPlayer.PlayerState){
-        switch state {
-        case .loading(let progress):
-            if progress == 0.0 {
-                ringView.cellView.isloading = true
-            }else if progress == 1.0 {
-                ringView.cellView.isloading = false
-            }
-        case .play:
-            ringView.cellView.isPlay = true
-        case .stop:
-            ringView.cellView.isPlay = false
-        default:
-            break
-        }
-    }
-    
     @objc func clickRight(){
         let browseVC = TSThemeTutorialsVC()
         kPushVC(target: self, modelVC: browseVC)
@@ -160,7 +136,7 @@ extension TSThemeSetVC {
         if audioPlayer.isPlaying{
             audioPlayer.stop()
         }else{
-            audioPlayer.playUrlString( model.ringtone)
+            audioPlayer.playUrlString(model.ringtone)
         }
     }
     

+ 129 - 9
AIRingtone/Business/VIewTool/TSRingToneCellView.swift

@@ -11,6 +11,17 @@ class TSRingToneCellView: TSBaseView {
     var clickPlayHandel:((Bool)->Void)?
     
     let coverImageView:UIImageView = UIImageView()
+    
+    lazy var progressView: TSCircularProgressView = {
+        let progressView = TSCircularProgressView(frame: CGRect(x: 0, y: 0, width: 49, height: 49))
+        progressView.trackColor = .clear
+        progressView.progressColor = .white
+        progressView.lineWidth = 2.0 // 设置线条粗细
+        progressView.isClockwise = true // 设置为逆时针
+        progressView.progress = 0.0
+        return progressView
+    }()
+    
     let playBtn = UIButton.createButton(image: UIImage(named: "ringtone_play"))
     let nameLab = UILabel.createLabel(text: "--",font: .font(size: 14),textColor: .white)
     let timeLab = UILabel.createLabel(text:"--:--",font: .font(size: 12),textColor: .white.withAlphaComponent(0.6))
@@ -26,6 +37,7 @@ class TSRingToneCellView: TSBaseView {
         didSet{
             playBtn.stopRotating()
             playBtn.setImage(UIImage(named: isPlay ? "ringtone_pause" : "ringtone_play"), for: .normal)
+            backgroundColor = isPlay ? "#3C213F".uiColor : .cardColor
         }
     }
     
@@ -45,26 +57,32 @@ class TSRingToneCellView: TSBaseView {
     override func creatUI() {
         backgroundColor = .cardColor
         cornerRadius = 16
-        coverImageView.cornerRadius = 8
+        coverImageView.cornerRadius = 24
         coverImageView.backgroundColor = .black
         
         contentView.addSubview(coverImageView)
+        contentView.addSubview(progressView)
         coverImageView.addSubview(playBtn)
         contentView.addSubview(nameLab)
         contentView.addSubview(timeLab)
         
-        
-        playBtn.snp.makeConstraints { make in
-            make.center.equalToSuperview()
-            make.width.height.equalTo(24)
-        }
-    
         coverImageView.snp.makeConstraints { make in
             make.leading.equalTo(12)
             make.centerY.equalToSuperview()
             make.width.height.equalTo(48)
         }
+
+        progressView.snp.makeConstraints { make in
+            make.leading.equalTo(11)
+            make.centerY.equalToSuperview()
+            make.width.height.equalTo(progressView.width)
+        }
         
+        playBtn.snp.makeConstraints { make in
+            make.center.equalToSuperview()
+            make.width.height.equalTo(24)
+        }
+
         nameLab.snp.makeConstraints { make in
             make.leading.equalTo(76)
             make.trailing.equalTo(-60)
@@ -77,12 +95,17 @@ class TSRingToneCellView: TSBaseView {
             make.height.equalTo(12)
         }
         
+        let rightShade = UIImageView.createImageView(imageName: "play_right_shade",contentMode: .scaleAspectFill)
+        contentView.addSubview(rightShade)
+        rightShade.snp.makeConstraints { make in
+            make.trailing.top.bottom.equalToSuperview()
+            make.width.equalTo(103*kDesignScale)
+        }
+        
         
-//        clearBtn.backgroundColor = .yellow.withAlphaComponent(0.2)
         contentView.addSubview(clearBtn)
         clearBtn.snp.makeConstraints { make in
             make.leading.top.bottom.equalToSuperview()
-//            make.width.equalTo(72)
             make.trailing.equalTo(0)
         }
     }
@@ -91,5 +114,102 @@ class TSRingToneCellView: TSBaseView {
     func setCoverImageView(urlString:String){
         coverImageView.setAsyncImage(urlString: urlString,placeholder:kRingPlaceholderImage,contentMode: .scaleAspectFill,showLoading: false)
     }
+    
+}
+
+extension TSRingToneCellView {
+    
+    func monitorPlayStateDefaultHandle(){
+        TSBusinessAudioPlayer.shared.stateChangedHandle = { [weak self] state  in
+            guard let self = self else { return }
+            self.handelAudioPlayerStateChange(state: state)
+        }
+    }
+    
+    func handelAudioPlayerStateChange(state:TSBusinessAudioPlayer.PlayerState){
+        switch state {
+        case .loading(let progress):
+            if progress == 0.0 {
+                self.isloading = true
+            }else if progress == 1.0 {
+                self.isloading = false
+            }
+        case .play:
+            self.isPlay = true
+//            backgroundColor = "#3C213F".uiColor
+        case .stop:
+            self.isloading = false
+            self.isPlay = false
+            self.progressView.progress = 0.0
+//            backgroundColor = .cardColor
+        case .currentTime(_):
+            self.progressView.progress =  CGFloat(TSBusinessAudioPlayer.shared.playProgress)
+        default:
+            break
+        }
+    }
+    
+}
+
+
+class TSRingToneGenerateView:TSBaseView {
+    
+    lazy var generateProgressView: UIImageView = {
+        let generateProgressView = UIImageView.createImageView(imageName: "ringGenerateProgress",contentMode: .scaleToFill)
+        return generateProgressView
+    }()
+    
+    lazy var iconImageView: UIImageView = {
+        let iconImageView = UIImageView.createImageView(imageName: "ring_icon",contentMode: .scaleToFill)
+        return iconImageView
+    }()
+    
 
+    lazy var infoLabel: UILabel = {
+        let infoLabel = UILabel.createLabel(font: .font(size: 14),textColor: .white)
+        return infoLabel
+    }()
+    
+    lazy var refreshBtn: TSUIExpandedTouchButton = {
+        let refreshBtn = TSUIExpandedTouchButton()
+        refreshBtn.setUpButton(image: UIImage(named: "refresh_white")){
+            [weak self]  in
+            guard let self = self else { return }
+        }
+        refreshBtn.isHidden = true
+        return refreshBtn
+    }()
+    
+    
+    override func creatUI() {
+        backgroundColor = .cardColor
+        
+        contentView.addSubview(generateProgressView)
+        generateProgressView.snp.makeConstraints { make in
+            make.leading.top.bottom.equalTo(0)
+            make.width.equalToSuperview().multipliedBy(0.0)
+        }
+        
+        contentView.addSubview(iconImageView)
+        iconImageView.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.width.height.equalTo(48.0)
+            make.leading.equalTo(12.0)
+        }
+        
+        contentView.addSubview(infoLabel)
+        infoLabel.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.leading.equalTo(iconImageView.snp.trailing).offset(16)
+            make.trailing.equalTo(-50)
+        }
+        
+        contentView.addSubview(refreshBtn)
+        refreshBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.centerY.equalToSuperview()
+            make.width.height.equalTo(20.0)
+        }
+        
+    }
 }

+ 227 - 0
AIRingtone/Common/Tool/OperationQueue/TSBaseOperation.swift

@@ -0,0 +1,227 @@
+//
+//  TSBaseOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/20.
+//
+
+import Foundation
+
+class TSBaseOperation: Operation , @unchecked Sendable{
+    typealias TaskCompletionHandler = (Error?) -> Void
+    typealias TaskBlock = (@escaping TaskCompletionHandler) -> Void
+    
+    private let task: TaskBlock
+    private var _executing = false
+    private var _finished = false
+    private var _cancelled = false
+    private var _error: Error?
+    
+    init(task: @escaping TaskBlock) {
+        self.task = task
+        super.init()
+    }
+    
+    // MARK: - State Management
+    
+    override var isExecuting: Bool {
+        return _executing
+    }
+    
+    override var isFinished: Bool {
+        return _finished
+    }
+    
+    override var isCancelled: Bool {
+        return _cancelled
+    }
+    
+    override var isAsynchronous: Bool {
+        return true
+    }
+    
+    override func start() {
+        if isCancelled {
+            completeOperation()
+            return
+        }
+        
+        willChangeValue(forKey: "isExecuting")
+        _executing = true
+        didChangeValue(forKey: "isExecuting")
+        
+        task { [weak self] error in
+            guard let self = self else { return }
+            self._error = error
+            self.completeOperation()
+        }
+    }
+    
+    override func cancel() {
+        willChangeValue(forKey: "isCancelled")
+        _cancelled = true
+        didChangeValue(forKey: "isCancelled")
+        
+        if isExecuting {
+            completeOperation()
+        }
+    }
+    
+    private func completeOperation() {
+        if _executing {
+            willChangeValue(forKey: "isExecuting")
+            _executing = false
+            didChangeValue(forKey: "isExecuting")
+        }
+        
+        if !_finished {
+            willChangeValue(forKey: "isFinished")
+            _finished = true
+            didChangeValue(forKey: "isFinished")
+        }
+    }
+    
+    // MARK: - Error Handling
+    
+    var error: Error? {
+        return _error
+    }
+    
+    // MARK: - Convenience Methods
+    
+    override func waitUntilFinished() {
+        while !isFinished {
+            RunLoop.current.run(mode: .default, before: .distantFuture)
+        }
+    }
+    
+    func addDependency(_ operation: TSBaseOperation) {
+        super.addDependency(operation)
+    }
+    
+    func removeDependency(_ operation: TSBaseOperation) {
+        super.removeDependency(operation)
+    }
+}
+
+
+
+
+
+//import Foundation
+//
+//final class TaskOperation: Operation, @unchecked Sendable {
+//    typealias TaskCompletionHandler = (Error?) -> Void
+//    typealias TaskBlock = (@escaping TaskCompletionHandler) -> Void
+//    
+//    private let task: TaskBlock
+//    private var _executing = false
+//    private var _finished = false
+//    private var _cancelled = false
+//    private var _error: Error?
+//    
+//    private let stateLock = NSLock() // 用于线程安全的状态管理
+//    
+//    init(task: @escaping TaskBlock) {
+//        self.task = task
+//        super.init()
+//    }
+//    
+//    // MARK: - State Management
+//    
+//    override var isExecuting: Bool {
+//        stateLock.lock()
+//        defer { stateLock.unlock() }
+//        return _executing
+//    }
+//    
+//    override var isFinished: Bool {
+//        stateLock.lock()
+//        defer { stateLock.unlock() }
+//        return _finished
+//    }
+//    
+//    override var isCancelled: Bool {
+//        stateLock.lock()
+//        defer { stateLock.unlock() }
+//        return _cancelled
+//    }
+//    
+//    override var isAsynchronous: Bool {
+//        return true
+//    }
+//    
+//    override func start() {
+//        stateLock.lock()
+//        if isCancelled {
+//            stateLock.unlock()
+//            completeOperation()
+//            return
+//        }
+//        
+//        willChangeValue(forKey: "isExecuting")
+//        _executing = true
+//        didChangeValue(forKey: "isExecuting")
+//        stateLock.unlock()
+//        
+//        task { [weak self] error in
+//            guard let self = self else { return }
+//            self.stateLock.lock()
+//            self._error = error
+//            self.stateLock.unlock()
+//            self.completeOperation()
+//        }
+//    }
+//    
+//    override func cancel() {
+//        stateLock.lock()
+//        willChangeValue(forKey: "isCancelled")
+//        _cancelled = true
+//        didChangeValue(forKey: "isCancelled")
+//        stateLock.unlock()
+//        
+//        if isExecuting {
+//            completeOperation()
+//        }
+//    }
+//    
+//    private func completeOperation() {
+//        stateLock.lock()
+//        if _executing {
+//            willChangeValue(forKey: "isExecuting")
+//            _executing = false
+//            didChangeValue(forKey: "isExecuting")
+//        }
+//        
+//        if !_finished {
+//            willChangeValue(forKey: "isFinished")
+//            _finished = true
+//            didChangeValue(forKey: "isFinished")
+//        }
+//        stateLock.unlock()
+//    }
+//    
+//    // MARK: - Error Handling
+//    
+//    var error: Error? {
+//        stateLock.lock()
+//        defer { stateLock.unlock() }
+//        return _error
+//    }
+//    
+//    // MARK: - Convenience Methods
+//    
+//    func waitUntilFinished() {
+//        while !isFinished {
+//            RunLoop.current.run(mode: .default, before: .distantFuture)
+//        }
+//    }
+//    
+//    func addDependency(_ operation: TaskOperation) {
+//        super.addDependency(operation)
+//    }
+//    
+//    func removeDependency(_ operation: TaskOperation) {
+//        super.removeDependency(operation)
+//    }
+//}

+ 61 - 0
AIRingtone/Common/Tool/OperationQueue/TSBaseOperationQueue.swift

@@ -0,0 +1,61 @@
+//
+//  TSBaseOperationQueue.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/20.
+//
+
+import Foundation
+
+class TaskQueueManager {
+    private let queue: OperationQueue
+    
+    
+    
+    var isAvailability:Bool {
+        get {
+            if queue.operationCount <= queue.maxConcurrentOperationCount {
+                return true
+            }
+            return false
+        }
+    }
+    
+    init(maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount) {
+        queue = OperationQueue()
+        queue.maxConcurrentOperationCount = maxConcurrentOperationCount
+    }
+    
+    func addTask(_ task: @escaping TSBaseOperation.TaskBlock) -> TSBaseOperation {
+        let operation = TSBaseOperation(task: task)
+        queue.addOperation(operation)
+        return operation
+    }
+    
+    func cancelAllOperations() {
+        queue.cancelAllOperations()
+    }
+    
+    func waitUntilAllOperationsAreFinished() {
+        queue.waitUntilAllOperationsAreFinished()
+    }
+    
+    
+    
+    
+    //// 监听 operationCount 属性
+    //queue.addObserver(self, forKeyPath: "operationCount", options: [.new], context: nil)
+    //override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+    //    if keyPath == "operationCount" {
+    //        if queue.operationCount == 0 {
+    //            print("所有任务已完成")
+    //        } else {
+    //            print("队列中还有 \(queue.operationCount) 个任务在执行")
+    //        }
+    //    }
+    //}
+    //
+    //deinit {
+    //  queue.removeObserver(self, forKeyPath: "operationCount")
+    //}
+}

+ 7 - 0
AIRingtone/Common/Tool/OperationQueue/TSGenerateRintoneOperation/TSGenerateRintoneOperation.swift

@@ -0,0 +1,7 @@
+//
+//  TSGenerateRintoneOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/21.
+//
+

+ 8 - 0
AIRingtone/Common/Tool/OperationQueue/TSGenerateRintoneOperation/TSGenerateRintoneOperationQueue.swift

@@ -0,0 +1,8 @@
+//
+//  TSGenerateRintoneOperationQueue.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/21.
+//
+
+TSGenerateRintoneOperation

+ 220 - 53
AIRingtone/Common/Tool/TSAudioPlayer/TSAudioPlayer.swift

@@ -7,112 +7,279 @@
 
 import AVFoundation
 
-class TSAudioPlayer : NSObject, AVAudioPlayerDelegate{
-    private var audioPlayer: AVAudioPlayer?
+class TSAudioPlayer: NSObject {
+    private var player: AVPlayer?
+    private var timeObserverToken: Any?
     var isLooping: Bool = false
     
     override init() {
         super.init()
     }
     
-    var isPlaying:Bool{
-        if let audioPlayer = audioPlayer {
-            return audioPlayer.isPlaying
-        }
-        return false
+    var isPlaying: Bool {
+        return player?.rate != 0 && player?.error == nil
     }
     
-    var duration:Double{
-        if let audioPlayer = audioPlayer {
-            return audioPlayer.duration
+    var duration: Double {
+        if let currentItem = player?.currentItem {
+            return CMTimeGetSeconds(currentItem.asset.duration)
         }
         return 0.0
     }
     
-    var volume:Float{
-        if let audioPlayer = audioPlayer {
-            return audioPlayer.volume
+    var volume: Float {
+        get {
+            return player?.volume ?? 0.0
+        }
+        set {
+            player?.volume = max(0.0, min(newValue, 1.0))
         }
-        return 0
     }
     
-    //播放器是否可用
-    var playerUsable:Bool {
-        if audioPlayer != nil  {
-            return true
+    var currentTime: Double {
+        if let currentItem = player?.currentItem {
+            return CMTimeGetSeconds(currentItem.currentTime())
         }
-        return false
+        return 0.0
     }
-
+    
+    var playerUsable: Bool {
+        return player != nil
+    }
+    
+    var currentTimeChanged: ((Double) -> Void)?
+    var audioPlayerDidFinishHandle: ((Bool) -> Void)?
+    
     /// 初始化播放器
     /// - Parameter url: 音频文件的 URL
     init?(url: URL) {
         super.init()
+        
+        let playerItem = AVPlayerItem(url: url)
+        player = AVPlayer(playerItem: playerItem)
+        
+        // 监听播放完成事件
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(playerDidFinishPlaying),
+            name: .AVPlayerItemDidPlayToEndTime,
+            object: playerItem
+        )
+        
+        // 设置音频会话
         do {
-            audioPlayer = try AVAudioPlayer(contentsOf: url)
-            audioPlayer?.prepareToPlay()
-            audioPlayer?.delegate = self
             let audioSession = AVAudioSession.sharedInstance()
-            try audioSession.setCategory(.playback) // 设置类别为 playback
-            try audioSession.setActive(true) // 激活音频会话
-
+            try audioSession.setCategory(.playback)
+            try audioSession.setActive(true)
         } catch {
-            print("音频文件加载失败: \(error.localizedDescription)")
+            print("TSAudioPlayer 音频会话设置失败: \(error.localizedDescription)")
             return nil
         }
     }
     
+    deinit {
+        stop()
+    }
+    
     /// 开始播放音频
     func play() {
-        guard let audioPlayer = audioPlayer else { return }
+        guard let player = player else { return }
+        
         let outputVolume = AVAudioSession.sharedInstance().outputVolume
-        dePrint("outputVolume\(outputVolume)")
+        print("TSAudioPlayer outputVolume: \(outputVolume)")
         
         if outputVolume == 0.0 {
-            TSToastShared.showToast(text: "Please turn up the volume".localized)
+            print("Please turn up the volume".localized)
         }
-    
-        audioPlayer.numberOfLoops = isLooping ? -1 : 0
-        audioPlayer.play()
+        
+        player.play()
+        
+        // 监听播放进度
+        addTimeObserver()
     }
     
     /// 暂停播放音频
     func pause() {
-        audioPlayer?.pause()
+        player?.pause()
     }
     
     /// 停止并销毁播放器
     func stop() {
-        audioPlayer?.stop()
-        audioPlayer = nil
+        player?.pause()
+        player?.replaceCurrentItem(with: nil)
+        player = nil
+        removeTimeObserver()
     }
     
     /// 设置是否重复播放
     /// - Parameter loop: 是否循环播放
     func setLoop(_ loop: Bool) {
         isLooping = loop
-        audioPlayer?.numberOfLoops = loop ? -1 : 0
     }
     
-    /// 设置音量
-    /// - Parameter volume: 音量大小 (0.0 静音, 1.0 最大)
-    func setVolume(_ volume: Float) {
-        audioPlayer?.volume = max(0.0, min(volume, 1.0))
+    /// 跳转到指定时间
+    /// - Parameter time: 目标时间(秒)
+    func seek(to time: Double) {
+        if let player = player {
+            let targetTime = CMTimeMakeWithSeconds(time, preferredTimescale: 600)
+            player.seek(to: targetTime)
+        }
     }
     
-    deinit {
-        stop()
+    /// 监听播放完成
+    @objc private func playerDidFinishPlaying() {
+        if isLooping {
+            seek(to: 0.0)
+            play()
+        }
+        audioPlayerDidFinishHandle?(true)
     }
-
-    var audioPlayerDidFinishHandle:((Bool)->Void)?
-    // 实现 AVAudioPlayerDelegate 方法
-    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
-        if flag {
-            print("音频播放完成")
-            
-        } else {
-            print("音频播放中断")
+    
+    /// 添加时间观察者
+    private func addTimeObserver() {
+        let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
+        timeObserverToken = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
+            guard let self = self else { return }
+            let currentTime = CMTimeGetSeconds(time)
+            self.currentTimeChanged?(currentTime)
+        }
+    }
+    
+    /// 移除时间观察者
+    private func removeTimeObserver() {
+        if let token = timeObserverToken {
+            player?.removeTimeObserver(token)
+            timeObserverToken = nil
         }
-        audioPlayerDidFinishHandle?(flag)
     }
 }
+
+//class TSAudioPlayer : NSObject, AVAudioPlayerDelegate{
+//    private var audioPlayer: AVAudioPlayer?
+//    var isLooping: Bool = false
+//    
+//    override init() {
+//        super.init()
+//    }
+//    
+//    var isPlaying:Bool{
+//        if let audioPlayer = audioPlayer {
+//            return audioPlayer.isPlaying
+//        }
+//        return false
+//    }
+//    
+//    var duration:Double{
+//        if let audioPlayer = audioPlayer {
+//            return audioPlayer.duration
+//        }
+//        return 0.0
+//    }
+//    
+//    var volume:Float{
+//        if let audioPlayer = audioPlayer {
+//            return audioPlayer.volume
+//        }
+//        return 0
+//    }
+//    
+//    var currentTime:Double{
+//        if let audioPlayer = audioPlayer {
+//            return audioPlayer.currentTime
+//        }
+//        return 0.0
+//    }
+//    
+//    //播放器是否可用
+//    var playerUsable:Bool {
+//        if audioPlayer != nil  {
+//            return true
+//        }
+//        return false
+//    }
+//
+//    var timer:GCDTimer = GCDTimer()
+//    
+//    var currentTimeChanged:((Double)->Void)?
+//    
+//    /// 初始化播放器
+//    /// - Parameter url: 音频文件的 URL
+//    init?(url: URL) {
+//        super.init()
+//        do {
+//            audioPlayer = try AVAudioPlayer(contentsOf: url)
+//            audioPlayer?.prepareToPlay()
+//            audioPlayer?.delegate = self
+//            let audioSession = AVAudioSession.sharedInstance()
+//            try audioSession.setCategory(.playback) // 设置类别为 playback
+//            try audioSession.setActive(true) // 激活音频会话
+//
+//        } catch {
+//            dePrint("TSAudioPlayer 音频文件加载失败: \(error.localizedDescription)")
+//            return nil
+//        }
+//    }
+//    
+//    /// 开始播放音频
+//    func play() {
+//        guard let audioPlayer = audioPlayer else { return }
+//        let outputVolume = AVAudioSession.sharedInstance().outputVolume
+//        dePrint("TSAudioPlayer outputVolume\(outputVolume)")
+//        
+//        if outputVolume == 0.0 {
+//            TSToastShared.showToast(text: "Please turn up the volume".localized)
+//        }
+//    
+//        audioPlayer.numberOfLoops = isLooping ? -1 : 0
+//        audioPlayer.play()
+//        
+//        timer.start(interval: 0.5, repeats: true) { [weak self]  in
+//            guard let self = self else { return }
+//            
+//            let currentTime = audioPlayer.currentTime
+//            dePrint("TSAudioPlayer Current playback time: \(currentTime) seconds")
+//            currentTimeChanged?(currentTime)
+//          }
+//    }
+//    
+//    /// 暂停播放音频
+//    func pause() {
+//        audioPlayer?.pause()
+//    }
+//    
+//    /// 停止并销毁播放器
+//    func stop() {
+//        audioPlayer?.stop()
+//        audioPlayer = nil
+//        timer.stop()
+//    }
+//    
+//    /// 设置是否重复播放
+//    /// - Parameter loop: 是否循环播放
+//    func setLoop(_ loop: Bool) {
+//        isLooping = loop
+//        audioPlayer?.numberOfLoops = loop ? -1 : 0
+//    }
+//    
+//    /// 设置音量
+//    /// - Parameter volume: 音量大小 (0.0 静音, 1.0 最大)
+//    func setVolume(_ volume: Float) {
+//        audioPlayer?.volume = max(0.0, min(volume, 1.0))
+//    }
+//    
+//    deinit {
+//        stop()
+//    }
+//
+//    var audioPlayerDidFinishHandle:((Bool)->Void)?
+//    // 实现 AVAudioPlayerDelegate 方法
+//    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+//        if flag {
+//            print("TSAudioPlayer 音频播放完成")
+//            
+//        } else {
+//            print("TSAudioPlayer 音频播放中断")
+//        }
+//        audioPlayerDidFinishHandle?(flag)
+//    }
+//}

+ 17 - 34
AIRingtone/Common/Tool/TSBandRingTool/TSBandRingTool.swift

@@ -146,44 +146,27 @@ class TSBandRingTool:NSObject {
             
             let vc = UIActivityViewController(activityItems: [fileUrl], applicationActivities: nil)
             // 排除不需要的活动类型
-            vc.excludedActivityTypes = [
-                .postToFacebook,
-                .postToTwitter,
-                .postToWeibo,
-                .message,
-                .mail,
-                .print,
-                .copyToPasteboard,
-                .assignToContact,
-                .saveToCameraRoll,
-                .addToReadingList,
-                .postToFlickr,
-                .postToVimeo,
-                .postToTencentWeibo,
-                .airDrop,
-                .openInIBooks
-            ]
+//            vc.excludedActivityTypes = [
+//                .postToFacebook,
+//                .postToTwitter,
+//                .postToWeibo,
+//                .message,
+//                .mail,
+//                .print,
+//                .copyToPasteboard,
+//                .assignToContact,
+//                .saveToCameraRoll,
+//                .addToReadingList,
+//                .postToFlickr,
+//                .postToVimeo,
+//                .postToTencentWeibo,
+//                .airDrop,
+//                .openInIBooks
+//            ]
             self.targetVC?.present(vc, animated: true, completion: {
                 self.tryStartPictureInPicture()
             })
             
-//            self.shareFileToGarageBand(fileURL: fileUrl)
-        }
-    }
-    
-    
-    func shareFileToGarageBand(fileURL: URL) {
-        // 假设你有一个文件的 URL
-        // 创建 GarageBand 的 URL Scheme
-        let garageBandURL = URL(string: "garageband://mobilegarageband203.audiobus://import?url=\(fileURL.absoluteString)")!
-        
-        // 检查是否可以打开 GarageBand
-        if UIApplication.shared.canOpenURL(garageBandURL) {
-            // 打开 GarageBand
-            UIApplication.shared.open(garageBandURL, options: [:], completionHandler: nil)
-        } else {
-            // 如果 GarageBand 未安装,提示用户
-            print("GarageBand 未安装")
         }
     }
     

+ 42 - 113
AIRingtone/Common/Tool/TSBusinessAudioPlayer.swift

@@ -8,11 +8,7 @@
 
 class TSBusinessAudioPlayer {
     
-    static let shared = TSBusinessAudioPlayer { state in
-        kExecuteOnMainThread{
-            NotificationCenter.default.post(name: .kBusinessAudioStateChange, object: nil, userInfo: ["PlayerState": state])
-        }
-    }
+    static let shared = TSBusinessAudioPlayer()
     
     enum PlayerState:Equatable {
         case play
@@ -20,11 +16,12 @@ class TSBusinessAudioPlayer {
         case stop
         case loading(Float)
         case volume(Float)
+        case currentTime(Double)
     }
     
     private var audioPlayer: TSAudioPlayer?
     
-    var stateChangeBlock:(PlayerState) -> Void
+    var stateChangedHandle:((PlayerState) -> Void)?
     var currentPlayerState:PlayerState = .stop
     var duration:Double{
         if let audioPlayer = audioPlayer {
@@ -49,6 +46,20 @@ class TSBusinessAudioPlayer {
         }
     }
     
+    var currentTime:Double{
+        if let audioPlayer = audioPlayer {
+            return audioPlayer.currentTime
+        }
+        return 0.0
+    }
+    
+    
+    var playProgress:Double{
+        let playProgress = currentTime / duration
+        dePrint("TSAudioPlayer playProgress = \(playProgress)")
+        return playProgress
+    }
+    
     //播放器是否可用
     var playerUsable:Bool {
         if let audioPlayer = audioPlayer {
@@ -58,13 +69,10 @@ class TSBusinessAudioPlayer {
     }
     var currentURLString:String = ""
     var currentIndexPath:IndexPath? = nil
+    
     //加载音乐可能 2-3 秒有结果,停止加载后播放.
     private var isStopPlayingAfterLoading:Bool = false
-//    private var loadingLogic:(show: () -> Void, hide: () -> Void)?
-    init(stateChangeBlock:@escaping (PlayerState) -> Void) {
-        self.stateChangeBlock = stateChangeBlock
-    }
-    
+
     func isPlayURLString(string:String,indexPath:IndexPath? = nil) -> Bool {
 
         if currentURLString == string {
@@ -80,8 +88,7 @@ class TSBusinessAudioPlayer {
         return false
     }
     
-    
-    
+
     func playUrlString(_ urlString:String?,loop:Bool = false,indexPath:IndexPath? = nil) {
         self.stop()
         if let urlString = urlString {
@@ -95,8 +102,8 @@ class TSBusinessAudioPlayer {
 
             let palyFile:(URL)->Void = { [weak self] url in
                 guard let self = self else { return }
-                debugPrint("正在播放url:\(currentURLString)")
-                debugPrint("正在播放path:\(url)")
+                debugPrint("TSAudioPlayer 正在播放url:\(currentURLString)")
+                debugPrint("TSAudioPlayer 正在播放path:\(url)")
                 self.audioPlayer = TSAudioPlayer(url: url)
                 self.audioPlayer?.setLoop(loop)
                 
@@ -104,12 +111,16 @@ class TSBusinessAudioPlayer {
                     setVolume(volume: 1.0)
                 }
                 
+                self.audioPlayer?.currentTimeChanged = { [weak self] currentTime in
+                    guard let self = self else { return }
+                    changePlayerState(.currentTime(currentTime))
+                }
+                
                 self.play()
                 dePrint(self.audioPlayer?.duration)
                 
                 self.audioPlayer?.audioPlayerDidFinishHandle = { [weak self] flag in
                     guard let self = self else { return }
-                    
                     if flag == true, self.audioPlayer?.isLooping == false{
                         stop()
                     }
@@ -117,11 +128,11 @@ class TSBusinessAudioPlayer {
             }
             
             isStopPlayingAfterLoading = false
-            if let path = TSCommonTool.getCachedURLString(from: urlString) {
+            if let path = TSCommonTool.getCachedURLString(from: urlString,missingEx: "mp3") {
                 palyFile(path) //播放
             }else{
                 self.changePlayerState(.loading(0.0))
-                TSCommonTool.downloadAndCacheFile(from: urlString) { [weak self] path, error in
+                TSCommonTool.downloadAndCacheFile(from: urlString,missingEx: "mp3") { [weak self] path, error in
                     guard let self = self else { return }
                     self.changePlayerState(.loading(1.0))
                 
@@ -130,8 +141,8 @@ class TSBusinessAudioPlayer {
                         return
                     }
                     
-                    if let path = path, let url = URL(string: path) {
-                        palyFile(url) //播放
+                    if let path = path {
+                        palyFile(URL(fileURLWithPath: path)) //播放
                     }else{
                         //暂停
                         self.stop()
@@ -141,99 +152,14 @@ class TSBusinessAudioPlayer {
         }
     }
     
-    
-//
-//    func playUrlString(_ urlString:String?,loop:Bool = false,indexPath:IndexPath? = nil) {
-//        self.stop()
-//        if let urlString = urlString {
-//            
-////            if self.currentURLString == urlStrin {
-////                self.play()
-////                return
-////            }
-//            self.currentURLString = urlString
-//            self.currentIndexPath = indexPath
-//            loadingLogic = kCreateLoadingLogic { [weak self]  in
-//                guard let self = self else { return }
-//                self.changePlayerState(.loading(0.0))
-//            } hideBlock: {[weak self]  in
-//                guard let self = self else { return }
-//                self.changePlayerState(.loading(1.0))
-//            }
-//
-//            loadingLogic?.show()
-//            
-//            
-////            if let path = getCachedURLString(from: urlString) {
-////                
-////            }else{
-////                
-////                
-////            }
-//
-//            
-//            let palyFile:(String)->Void = { [weak self] path in
-//                guard let self = self else { return }
-//            }
-//            palyFile("111")
-//            
-//            isStopPlayingAfterLoading = false
-//            TSCommonTool.downloadAndCacheFile(from: urlString) { [weak self] path, error in
-//                guard let self = self else { return }
-//                loadingLogic?.hide()
-//                
-//                if isStopPlayingAfterLoading == true || currentURLString != urlString{
-//                    self.stop()
-//                    isStopPlayingAfterLoading = false
-//                    return
-//                }
-//                
-//                if let path = path {
-//                    //播放
-//                    if let url = URL(string: path) {
-//                        debugPrint("正在播放url:\(currentURLString)")
-//                        debugPrint("正在播放path:\(url)")
-//                        self.audioPlayer = TSAudioPlayer(url: url)
-//                        self.audioPlayer?.setLoop(loop)
-//                        setVolume(volume: 1.0)
-//                        self.play()
-//                        dePrint(self.audioPlayer?.duration)
-//                        
-//                        self.audioPlayer?.audioPlayerDidFinishHandle = { [weak self] flag in
-//                            guard let self = self else { return }
-//                            
-//                            if flag == true, self.audioPlayer?.isLooping == false{
-//                                stop()
-//                            }
-//                        }
-//                    }
-//                    
-//                }else{
-//                    //暂停
-//                    self.stop()
-//                }
-//            }
-//        }
-//        
-//
-//    }
-//    
-    
-    
+
     func play() {
         self.audioPlayer?.play()
         changePlayerState(.play)
     }
     
-    func stop(/*_ urlString:String? = nil*/) {
-        
-//        if let urlStr = urlString , currentURLString != urlStr {
-//            return
-//        }
-
+    func stop() {
         isStopPlayingAfterLoading = true
-//        loadingLogic?.hide()
-//        changePlayerState(.loading(1.0))
         currentURLString = ""
         self.audioPlayer?.stop()
         changePlayerState(.stop)
@@ -241,14 +167,13 @@ class TSBusinessAudioPlayer {
     
     func pause() {
         isStopPlayingAfterLoading = true
-//        loadingLogic?.hide()
-//        changePlayerState(.loading(1.0))
         self.audioPlayer?.pause()
         changePlayerState(.pause)
     }
     
     func setVolume(volume:Float){
-        self.audioPlayer?.setVolume(volume)
+        self.audioPlayer?.volume = volume 
+//        self.audioPlayer?.setVolume(volume)
         changePlayerState(.volume(volume))
     }
     
@@ -260,12 +185,16 @@ class TSBusinessAudioPlayer {
     }
     
     func changePlayerState(_ state:PlayerState){
-        debugPrint("changePlayerState=\(state)")
+        debugPrint("TSAudioPlayer changePlayerState=\(state)")
         currentPlayerState = state
-        stateChangeBlock(state)
+        
+        kExecuteOnMainThread{
+            self.stateChangedHandle?(state)
+            NotificationCenter.default.post(name: .kBusinessAudioStateChange, object: nil, userInfo: ["PlayerState": state])
+        }
     }
      
     deinit {
-        dePrint("TSBusinessAudioPlayer deinit")
+        dePrint("TSAudioPlayer TSBusinessAudioPlayer deinit")
     }
 }

+ 6 - 0
AIRingtone/Common/Tool/TSPublicContent.swift

@@ -0,0 +1,6 @@
+//
+//  TSPublicContent.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/20.
+//