Browse Source

4.3a改造:移除没用的demo ,另外混淆了字体扩展

kln 1 week ago
parent
commit
dec5d48d51

+ 3 - 31
AIPlayRingtones.xcodeproj/project.pbxproj

@@ -57,9 +57,6 @@
 		3DBEA1842DE71703000C6859 /* ASMediaPlayerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBEA1832DE71700000C6859 /* ASMediaPlayerController.swift */; };
 		3DCD56F32DDAE3E3004AAB5B /* ASRingToneCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F22DDAE3DF004AAB5B /* ASRingToneCellView.swift */; };
 		3DCD56F52DDAE42A004AAB5B /* ASViewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F42DDAE421004AAB5B /* ASViewTool.swift */; };
-		3DCD56F92DDAE481004AAB5B /* TSBusinessAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56F62DDAE481004AAB5B /* TSBusinessAudioPlayer.swift */; };
-		3DCD56FD2DDAFBE5004AAB5B /* ASDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56FC2DDAFBE4004AAB5B /* ASDownloadManager.swift */; };
-		3DCD56FF2DDAFC1B004AAB5B /* ASBusinessFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD56FE2DDAFC17004AAB5B /* ASBusinessFileManager.swift */; };
 		3DCD57182DDB1158004AAB5B /* libmp3lame.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DCD57092DDB1158004AAB5B /* libmp3lame.a */; };
 		3DCD571A2DDB1158004AAB5B /* ExtAudioConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD570A2DDB1158004AAB5B /* ExtAudioConverter.m */; };
 		3DCD571B2DDB1158004AAB5B /* AudioConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DCD57072DDB1158004AAB5B /* AudioConverter.m */; };
@@ -187,9 +184,6 @@
 		3DBEA1832DE71700000C6859 /* ASMediaPlayerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASMediaPlayerController.swift; sourceTree = "<group>"; };
 		3DCD56F22DDAE3DF004AAB5B /* ASRingToneCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASRingToneCellView.swift; sourceTree = "<group>"; };
 		3DCD56F42DDAE421004AAB5B /* ASViewTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASViewTool.swift; sourceTree = "<group>"; };
-		3DCD56F62DDAE481004AAB5B /* TSBusinessAudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBusinessAudioPlayer.swift; sourceTree = "<group>"; };
-		3DCD56FC2DDAFBE4004AAB5B /* ASDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASDownloadManager.swift; sourceTree = "<group>"; };
-		3DCD56FE2DDAFC17004AAB5B /* ASBusinessFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASBusinessFileManager.swift; sourceTree = "<group>"; };
 		3DCD57062DDB1158004AAB5B /* AudioConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioConverter.h; sourceTree = "<group>"; };
 		3DCD57072DDB1158004AAB5B /* AudioConverter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioConverter.m; sourceTree = "<group>"; };
 		3DCD57082DDB1158004AAB5B /* ExtAudioConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExtAudioConverter.h; sourceTree = "<group>"; };
@@ -356,24 +350,6 @@
 			path = BaseClass;
 			sourceTree = "<group>";
 		};
-		3DCD56F82DDAE481004AAB5B /* TSAudioPlayer */ = {
-			isa = PBXGroup;
-			children = (
-				3DCD56F62DDAE481004AAB5B /* TSBusinessAudioPlayer.swift */,
-				3DBEA1832DE71700000C6859 /* ASMediaPlayerController.swift */,
-			);
-			path = TSAudioPlayer;
-			sourceTree = "<group>";
-		};
-		3DCD56FB2DDAFBD8004AAB5B /* ASDownloadManager */ = {
-			isa = PBXGroup;
-			children = (
-				3DCD56FC2DDAFBE4004AAB5B /* ASDownloadManager.swift */,
-				3DBEA1812DE71529000C6859 /* ASMediaDownloader.swift */,
-			);
-			path = ASDownloadManager;
-			sourceTree = "<group>";
-		};
 		3DCD570C2DDB1158004AAB5B /* libmp3 */ = {
 			isa = PBXGroup;
 			children = (
@@ -515,11 +491,10 @@
 				3DBEA1392DE6DBE6000C6859 /* BaseClass */,
 				3DB4D4AD2DDDCED00082596A /* Ex */,
 				3DCD57172DDB1158004AAB5B /* TSBandRingTool */,
-				3DCD56FE2DDAFC17004AAB5B /* ASBusinessFileManager.swift */,
-				3DBEA17F2DE7149E000C6859 /* ASResourceCacheHandler.swift */,
-				3DCD56FB2DDAFBD8004AAB5B /* ASDownloadManager */,
-				3DCD56F82DDAE481004AAB5B /* TSAudioPlayer */,
 				A848F8F92DD7536300B746EC /*  Notification+Ex.swift */,
+				3DBEA17F2DE7149E000C6859 /* ASResourceCacheHandler.swift */,
+				3DBEA1812DE71529000C6859 /* ASMediaDownloader.swift */,
+				3DBEA1832DE71700000C6859 /* ASMediaPlayerController.swift */,
 				A848F89D2DD6D59C00B746EC /* TSNetWork */,
 				A848F8902DD6D4FD00B746EC /* Common.swift */,
 				3DBEA1632DE6F390000C6859 /* ASRandomTextTool.h */,
@@ -909,7 +884,6 @@
 				3DBEA14D2DE6E8AB000C6859 /* UILabel+AS.swift in Sources */,
 				3DB4D4932DDC25C10082596A /* APAudioToRingVM.swift in Sources */,
 				3DCD571F2DDB1158004AAB5B /* TSBandRingTool.swift in Sources */,
-				3DCD56F92DDAE481004AAB5B /* TSBusinessAudioPlayer.swift in Sources */,
 				A800FEBB2DDACAF3009DABDC /* ASRingGeneratorVC.swift in Sources */,
 				A848F8F32DD7528D00B746EC /* ASBaseOperation.swift in Sources */,
 				A848F8BB2DD6E30600B746EC /* APRingTonesVM.swift in Sources */,
@@ -920,7 +894,6 @@
 				3DB4D4B22DDF0B960082596A /* FakeBlurView.swift in Sources */,
 				3DCD56F32DDAE3E3004AAB5B /* ASRingToneCellView.swift in Sources */,
 				3DBEA15E2DE6F008000C6859 /* UIFont+AS.swift in Sources */,
-				3DCD56FF2DDAFC1B004AAB5B /* ASBusinessFileManager.swift in Sources */,
 				3DBEA1762DE6FB18000C6859 /* ASFileManager.swift in Sources */,
 				A848F8C42DD6E70500B746EC /* APRingTonesVC+Style.swift in Sources */,
 				A848F8F82DD752E700B746EC /* ASGenerateTextToRingOperation.swift in Sources */,
@@ -935,7 +908,6 @@
 				3DBEA14F2DE6E926000C6859 /* ASTouchBtn.swift in Sources */,
 				A848F8B92DD6DF7900B746EC /* ASAppBtnView.swift in Sources */,
 				3DBEA1342DE6D862000C6859 /* ASBaseCollectionReusableView.m in Sources */,
-				3DCD56FD2DDAFBE5004AAB5B /* ASDownloadManager.swift in Sources */,
 				3DCD573B2DDB4E61004AAB5B /* ASPageNullView.swift in Sources */,
 				A848F8F12DD7528800B746EC /* ASBaseOperationQueue.swift in Sources */,
 				3DBEA12E2DE6BF19000C6859 /* ASBaseView.m in Sources */,

+ 0 - 126
AIPlayRingtones/Common/ASBusinessFileManager.swift

@@ -1,126 +0,0 @@
-//
-//  ASBusinessFileManager.swift
-//  AIPlayRingtones
-//
-//  Created by mini on 2025/5/18.
-//
-
-//
-//class ASBusinessFileManager {
-//    
-//    /// 获取 Video 下载后保存的的文件件路径
-//    static var saveRingPathURL:URL = {
-//        let saveRingPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("ring")
-//        return saveRingPathURL
-//    }()
-//    
-//    static var saveCacheAllPathURL:URL = {
-//        let saveRingPathURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("cacheAll")
-//        return saveRingPathURL
-//    }()
-//    
-//    public static func generateFileName(
-//        urlString: String,
-//        fileEx:String? = nil,
-//    missingEx:String? = nil
-//    )->String?{
-//        guard let url = URL(string: urlString) else{
-//            return nil
-//        }
-//        
-//        var fileName = url.path.md5
-//
-//        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
-//        var fileExtension = ""
-//        if let fileEx = fileEx {
-//            fileExtension = fileEx
-//        }else{
-//            var missingExStr = ""
-//            if let missingEx = missingEx {
-//                missingExStr = missingEx
-//            }
-//            fileExtension = url.pathExtension.isEmpty ? missingExStr : url.pathExtension
-//        }
-//
-//        if fileExtension.count > 0 {
-//            fileName = url.path.md5 + ".\(fileExtension)"
-//        }
-//         
-//        return fileName
-//    }
-//    
-//    //获取 urlstring 本地的缓存 url path
-//    public static func getLocalURL(
-//        urlString: String,
-//        fileEx:String? = nil,
-//    missingEx:String? = nil)->URL?{
-//        
-//        guard let fileName = generateFileName(urlString: urlString,fileEx:fileEx,missingEx: missingEx) else{
-//            return nil
-//        }
-//        
-//        //检查文件是否已存在于缓存中
-//        let ringFileURL = saveRingPathURL.appendingPathComponent(fileName)
-//        if FileManager.default.fileExists(atPath: ringFileURL.path) {
-//            print("文件已存在于缓存中: \(ringFileURL)")
-//            return ringFileURL
-//        }
-//        
-//        let cachedFileURL = saveCacheAllPathURL.appendingPathComponent(fileName)
-//        if FileManager.default.fileExists(atPath: cachedFileURL.path) {
-//            print("文件已存在于缓存中: \(cachedFileURL)")
-//            return cachedFileURL
-//        }
-//        
-//        return nil
-//    }
-//    
-//
-//}
-//
-////缓存路径
-//extension ASBusinessFileManager {
-//    
-//    //检查 url 对不对
-//    public static func generateLocalURL(
-//        from urlString: String,
-//        fileEx:String? = nil,
-//        missingEx:String? = nil,
-//        frontPathURL:URL,
-//        completion:((String?, Error?) -> Void)? = nil
-//    )->URL?
-//    {
-//        guard let url = URL(string: urlString) else{
-//            completion?(nil, NSError(domain: "url null", code: 0))
-//            return nil
-//        }
-//        
-//        if !urlString.contains("http") && urlString.contains("/"){
-//            completion?(urlString.fillCachePath, nil)
-//            return nil
-//        }
-//        
-//        let fileManager = FileManager.default
-//        let cacheAllDirectory = frontPathURL
-//        
-//        // 创建 `cacheAll` 文件夹(如果不存在)
-//        if !fileManager.fileExists(atPath: cacheAllDirectory.path) {
-//            do {
-//                try fileManager.createDirectory(at: cacheAllDirectory, withIntermediateDirectories: true, attributes: nil)
-//            } catch {
-//                completion?(nil, error)
-//                return nil
-//            }
-//        }
-//        
-//        
-//        guard let fileName = generateFileName(urlString: urlString,fileEx:fileEx,missingEx: missingEx) else{
-//            completion?(nil, NSError(domain: "url error", code: 0))
-//            return nil
-//        }
-//        
-//        let cachedFileURL = cacheAllDirectory.appendingPathComponent(fileName)
-//        return cachedFileURL
-//    }
-//    
-//}

+ 0 - 45
AIPlayRingtones/Common/ASDownloadManager/ASDownloadManager.swift

@@ -1,45 +0,0 @@
-//
-//  ASDownloadManager.swift
-//  AIPlayRingtones
-//
-//  Created by mini on 2025/5/18.
-//
-
-//import AVFoundation
-//import Alamofire
-//
-//class ASDownloadManager {
-//    
-//    static func getDownLoadRing(urlString:String,progressHandler: ((Double) -> Void)? = nil,complete:@escaping (URL?,Bool)->Void){
-//        if let path = ASBusinessFileManager.getLocalURL(urlString: urlString,fileEx:nil,missingEx: "mp3") {
-//            complete(path,false)
-//        }else{
-//            _ = ASDownloadManager.downloadRing(urlString:urlString,missingEx: "mp3",progressHandler: progressHandler) { url, error in
-//                if let path = url {
-//                    complete(path,true)
-//                }else{
-//                    complete(nil,true)
-//                }
-//            }
-//        }
-//    }
-//    
-//    static func downloadRing(
-//        urlString: String,
-//        fileEx:String? = nil,
-//    missingEx:String? = nil,
-//        progressHandler: ((Double) -> Void)? = nil,
-//        completion: @escaping (URL?, Error?) -> Void
-//    ) -> DownloadRequest? {
-//        
-//        if let fileName = ASBusinessFileManager.getLocalURL(urlString: urlString,fileEx:fileEx,missingEx:missingEx){
-//            completion(fileName,nil)
-//            return nil
-//        }
-//        guard let savePath = ASBusinessFileManager.generateLocalURL(from: urlString, fileEx: fileEx, missingEx: missingEx,frontPathURL: ASBusinessFileManager.saveRingPathURL,completion: { string, error in
-//            completion(nil,error)
-//        })else { return nil }
-//        
-//        return TSNetworkShared.downloadFile(urlString: urlString,to: savePath, progressHandler:progressHandler,completion: completion)
-//    }
-//}

+ 0 - 0
AIPlayRingtones/Common/ASDownloadManager/ASMediaDownloader.swift → AIPlayRingtones/Common/ASMediaDownloader.swift


+ 0 - 0
AIPlayRingtones/Common/TSAudioPlayer/ASMediaPlayerController.swift → AIPlayRingtones/Common/ASMediaPlayerController.swift


+ 26 - 36
AIPlayRingtones/Common/Ex/UIFont+AS.swift

@@ -12,41 +12,31 @@ public extension String {
 
 public extension UIFont {
     static func font(name: String = .PingFangSC, size: CGFloat, weight: UIFont.Weight = .regular) -> UIFont {
-        guard !name.isEmpty,let _ = UIFont(name: name, size: size) else {
-            return UIFont.systemFont(ofSize: size, weight: weight)
-        }
-        var finalName = name
-        
-        let fontNames = UIFont.fontNames(forFamilyName: name)
-        switch weight {
-        case .light://细体 300
-            if let fontName = fontNames.first(where: { $0.lowercased().hasSuffix("-light") || $0.lowercased().hasSuffix("_light") }) {
-                finalName = fontName
-            }
-        case .medium://中黑体 500
-            if let fontName = fontNames.first(where: { $0.lowercased().hasSuffix("-medium") || $0.lowercased().hasSuffix("_medium") }) {
-                finalName = fontName
-            }
-        case .bold:// 粗体 700
-            if let fontName = fontNames.first(where: { $0.lowercased().hasSuffix("-bold") || $0.lowercased().hasSuffix("_bold") }) {
-                finalName = fontName
-            }
-        case .semibold://半粗体 600
-            if let fontName = fontNames.first(where: { $0.lowercased().hasSuffix("-semibold") || $0.lowercased().hasSuffix("_semibold") }) {
-                finalName = fontName
-            }
-        case .heavy: //粗黑体 800
-            if let fontName = fontNames.first(where: { $0.lowercased().hasSuffix("-heavy") || $0.lowercased().hasSuffix("_heavy") }) {
-                finalName = fontName
-            }
-        case .black: // 黑体 900
-            if let fontName = fontNames.first(where: { $0.lowercased().hasSuffix("-black") || $0.lowercased().hasSuffix("_black") }) {
-                finalName = fontName
-            }
-        default:
-            break
-        }
-        
-        return UIFont(name: finalName, size: size)!
+        guard !name.isEmpty, UIFont(name: name, size: size) != nil else {
+           return systemFont(ofSize: size, weight: weight)
+       }
+       
+       var selectedFontName = name
+       
+       let availableFonts = UIFont.fontNames(forFamilyName: name)
+       let weightMapping: [UIFont.Weight: [String]] = [
+           .light: ["-light", "_light"],//细体 300
+           .medium: ["-medium", "_medium"],////中黑体 500
+           .bold: ["-bold", "_bold"],//// 粗体 700
+           .semibold: ["-semibold", "_semibold"],//半粗体 600
+           .heavy: ["-heavy", "_heavy"],//粗黑体 800
+           .black: ["-black", "_black"] // 黑体 900
+       ]
+       
+       if let suffixes = weightMapping[weight] {
+           for suffix in suffixes {
+               if let matchedFont = availableFonts.first(where: { $0.lowercased().hasSuffix(suffix) }) {
+                   selectedFontName = matchedFont
+                   break
+               }
+           }
+       }
+       
+       return UIFont(name: selectedFontName, size: size) ?? systemFont(ofSize: size, weight: weight)
     }
 }

+ 0 - 493
AIPlayRingtones/Common/TSAudioPlayer/TSBusinessAudioPlayer.swift

@@ -1,493 +0,0 @@
-//
-//  TSBusinessAudioPlayer.swift
-//  AIRingtone
-//
-//  Created by 100Years on 2025/3/7.
-//
-
-//import AVFoundation
-//class TSBusinessAudioPlayer {
-//    
-//    static let shared = TSBusinessAudioPlayer()
-//    
-//    enum PlayerState:Equatable {
-//        case play
-//        case pause
-//        case stop
-//        case loading(Float)
-//        case volume(Float)
-//        case currentTime(Double)
-//    }
-//    
-//    private var audioPlayer: TSAudioPlayer?
-//    
-//    var stateChangedHandle:((PlayerState) -> Void)?
-//    var currentTimeChangedHandle:((Double,Double) -> Void)?
-//    
-//    var currentPlayerState:PlayerState = .stop
-//    var duration:Double{
-//        if let audioPlayer = audioPlayer {
-//            return audioPlayer.duration
-//        }
-//        return 0.0
-//    }
-//
-//    var isPlaying:Bool{
-//        if let audioPlayer = audioPlayer {
-//            return audioPlayer.isPlaying
-//        }
-//        return false
-//    }
-//    
-//    var isLoading:Bool{
-//        switch currentPlayerState {
-//        case .loading(let float):
-//            return float < 1.0 ? true : false
-//        default:
-//            return false
-//        }
-//    }
-//    
-//    var currentTime:Double{
-//        if let audioPlayer = audioPlayer {
-//            return audioPlayer.currentTime
-//        }
-//        return 0.0
-//    }
-//    
-//    /// 跳转到指定时间
-//    /// - Parameter time: 目标时间(秒)
-//    func seek(to time: Double) {
-//        audioPlayer?.seek(to: time)
-//    }
-//    
-//    var playProgress:Double{
-//        let playProgress = currentTime / duration
-////        logPrint("TSAudioPlayer playProgress = \(playProgress)")
-//        return playProgress
-//    }
-//    
-//    //播放器是否可用
-//    var playerUsable:Bool {
-//        if let audioPlayer = audioPlayer {
-//            return audioPlayer.playerUsable
-//        }
-//        return false
-//    }
-//    var currentURLString:String = ""
-//    var currentLocalURL:URL? = nil
-//    var currentIndexPath:IndexPath? = nil
-//    
-//    //加载音乐可能 2-3 秒有结果,停止加载后播放.
-//    private var isStopPlayingAfterLoading:Bool = false
-//
-//    func isPlayURLString(string:String,localURL:URL? = nil,indexPath:IndexPath? = nil) -> Bool {
-//
-//        if currentURLString == string {
-//            
-//            if let currentIndexPath = currentIndexPath,
-//               let indexPath = indexPath,
-//               indexPath != currentIndexPath
-//            {
-//                return false
-//            }else if let currentLocalURL = currentLocalURL,
-//                let localURL = localURL,
-//                currentLocalURL != localURL
-//             {
-//                 return false
-//             }else{
-//                return true
-//            }
-//        }
-//        return false
-//    }
-//    
-//    func loadLoactionURL(url:URL){
-//        self.audioPlayer = TSAudioPlayer(url: url)
-//    }
-//
-//    func playUrlString(_ urlString:String?,localURL:URL? = nil,loop:Bool = false,indexPath:IndexPath? = nil) {
-//        self.stop()
-//        if let urlString = urlString {
-//            
-//            self.currentURLString = urlString
-//            self.currentLocalURL = localURL
-//            self.currentIndexPath = indexPath
-//
-//            let palyFile:(URL)->Void = { [weak self] url in
-//                guard let self = self else { return }
-//                logPrint("TSAudioPlayer 正在播放url:\(currentURLString)")
-//                logPrint("TSAudioPlayer 正在播放path:\(url)")
-//                self.audioPlayer = TSAudioPlayer(url: url)
-//                self.audioPlayer?.setLoop(loop)
-//                
-//                if self.audioPlayer?.volume == 0 {
-//                    setVolume(volume: 1.0)
-//                }
-//                
-//                self.audioPlayer?.currentTimeChanged = { [weak self] currentTime,duration in
-//                    guard let self = self else { return }
-//                    currentTimeChangedHandle?(currentTime,duration)
-//                    changePlayerState(.currentTime(currentTime))
-//                }
-//                
-//                self.play()
-//                logPrint(self.audioPlayer?.duration)
-//                
-//                self.audioPlayer?.audioPlayerDidFinishHandle = { [weak self] flag in
-//                    guard let self = self else { return }
-//                    if flag == true, self.audioPlayer?.isLooping == false{
-//                        stop()
-//                    }
-//                }
-//            }
-//            
-//            isStopPlayingAfterLoading = false
-//            
-//            if let path = self.currentLocalURL,ASFileManager.fileExists(at: path){
-//                palyFile(path) //播放
-//                
-//            }else{
-//                self.changePlayerState(.loading(0.0))
-//                
-//                _ = ASDownloadManager.getDownLoadRing(urlString: urlString, progressHandler: { progress in
-//                    logPrint("ASDownloadManager.etDownLoadRing progress = \(progress)")
-//                }, complete: {[weak self] url, success in
-//                    
-//                    guard let self = self else { return }
-//                    
-//                    self.changePlayerState(.loading(1.0))
-//                
-//                    if isStopPlayingAfterLoading == true || currentURLString != urlString{
-//                        isStopPlayingAfterLoading = false
-//                        return
-//                    }
-//                    
-//                    if let url = url {
-//                        palyFile(url) //播放
-//                    }else{
-//                        //暂停
-//                        self.stop()
-//                    }
-//                })
-//            }
-//        }
-//    }
-//    
-//
-//    func play() {
-//        self.audioPlayer?.play()
-//        changePlayerState(.play)
-//    }
-//    
-//    func stop() {
-//        self.audioPlayer?.currentTimeChanged = nil
-//        isStopPlayingAfterLoading = true
-//        currentURLString = ""
-//        self.audioPlayer?.stop()
-//        changePlayerState(.stop)
-//    }
-//    
-//    func pause() {
-//        isStopPlayingAfterLoading = true
-//        self.audioPlayer?.pause()
-//        changePlayerState(.pause)
-//    }
-//    
-//    func setVolume(volume:Float){
-//        self.audioPlayer?.volume = volume
-//        changePlayerState(.volume(volume))
-//    }
-//    
-//
-//    func changeAudioSwitch()->Float {
-//        let volume:Float = self.audioPlayer?.volume == 0.0 ? 1.0 : 0.0
-//        setVolume(volume: volume)
-//        return volume
-//    }
-//    
-//    func changePlayerState(_ state:PlayerState){
-//
-//        if case .currentTime(let time) = state {} else {
-//            logPrint("TSAudioPlayer changePlayerState=\(state)")
-//        }
-//        currentPlayerState = state
-//        kExecuteOnMainThread{
-//            self.stateChangedHandle?(state)
-////            NotificationCenter.default.post(name: .kBusinessAudioStateChange, object: nil, userInfo: ["PlayerState": state])
-//        }
-//    }
-//     
-//    deinit {
-//        logPrint("TSAudioPlayer TSBusinessAudioPlayer deinit")
-//    }
-//}
-//
-//
-//extension TSBusinessAudioPlayer{
-//    struct AudioFileInfo {
-//        let sizeInBytes: UInt64?    // 文件大小(字节)
-//        let durationInSeconds: Double?  // 音频时长(秒)
-//        let songName: String?  // 音频时长(秒)
-//    }
-//
-//    static func getAudioFileInfo(path: String) -> AudioFileInfo? {
-//        // 1. 检查URL有效性
-//        guard let url = URL(string: path) else {
-//            print("getAudioFileInfo 无效的URL字符串")
-//            return nil
-//        }
-//        
-//        // 2. 检查文件是否存在(仅限本地文件)
-//        guard FileManager.default.fileExists(atPath: url.path) else {
-//            print("getAudioFileInfo 文件不存在或不是本地路径")
-//            return nil
-//        }
-//        
-//        // 3. 获取文件大小
-//        let fileSize: UInt64? = {
-//            do {
-//                let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
-//                return attributes[.size] as? UInt64
-//            } catch {
-//                print("获取文件大小失败: \(error.localizedDescription)")
-//                return nil
-//            }
-//        }()
-//        
-//        // 4. 获取音频时长
-//        let duration: Double? = {
-//            return getAudioDurationWithAudioFile(url: url)
-//        }()
-//        
-//        // 5. 获取音频名称
-//        let songName: String? = {
-//            return getSongNameFromLocalFile(fileURL: url)
-//        }()
-//        
-//        return AudioFileInfo(sizeInBytes: fileSize, durationInSeconds: duration,songName: songName)
-//    }
-//    
-//    /// 同步获取音频时长(可能阻塞线程!)
-//    /// 使用 AudioFile 同步获取音频时长
-//    static func getAudioDurationWithAudioFile(url: URL) -> TimeInterval? {
-//        var audioFile: AudioFileID?
-//        let status = AudioFileOpenURL(url as CFURL, .readPermission, 0, &audioFile)
-//        
-//        guard status == noErr, let file = audioFile else {
-//            print("⚠️ 打开音频文件失败: \(status)")
-//            return nil
-//        }
-//        
-//        // 获取音频时长(单位:秒)
-//        var duration: Float64 = 0
-//        var propertySize = UInt32(MemoryLayout.size(ofValue: duration))
-//        let durationStatus = AudioFileGetProperty(
-//            file,
-//            kAudioFilePropertyEstimatedDuration,
-//            &propertySize,
-//            &duration
-//        )
-//        
-//        AudioFileClose(file)
-//        
-//        return durationStatus == noErr ? duration : nil
-//    }
-//    
-//    //获取音频的名称
-//    static func getSongNameFromLocalFile(fileURL: URL) -> String? {
-//        // 1. 检查文件是否存在
-//        guard FileManager.default.fileExists(atPath: fileURL.path) else {
-//            print("文件不存在")
-//            return nil
-//        }
-//        
-//        // 2. 创建 AVAsset 对象(代表音频文件)
-//        let asset = AVAsset(url: fileURL)
-//        
-//        // 3. 同步读取元数据(适用于本地文件)
-//        var songName: String? = nil
-//        let metadataFormats = asset.availableMetadataFormats
-//        
-//        for format in metadataFormats {
-//            let metadata = asset.metadata(forFormat: format)
-//            
-//            // 4. 遍历元数据项,查找歌曲标题
-//            for item in metadata {
-//                if item.commonKey == .commonKeyTitle, // 标准键:标题
-//                   let value = item.value as? String { // 确保值是字符串
-//                    songName = value
-//                    break
-//                }
-//                
-//                // 额外检查 ID3 标签(某些 MP3 文件可能用非标准键)
-//                if item.key as? String == "TIT2", // ID3v2 标题键
-//                   let value = item.value as? String {
-//                    songName = value
-//                    break
-//                }
-//            }
-//            
-//            if songName != nil { break }
-//        }
-//        
-//        // 5. 返回结果(若未找到,则尝试从文件名推断)
-//        return songName ?? fileURL.deletingPathExtension().lastPathComponent
-//    }
-//}
-//
-//class TSAudioPlayer: NSObject {
-//    private var player: AVPlayer?
-//    private var timeObserverToken: Any?
-//    var isLooping: Bool = false
-//    
-//    override init() {
-//        super.init()
-//    }
-//    
-//    var isPlaying: Bool {
-////        return player?.rate != 0 && player?.error == nil
-//        
-//        if let player = player {
-//            if #available(iOS 10.0, *) {
-//                return player.timeControlStatus == .playing
-//            } else {
-//                return player.rate > 0
-//            }
-//        }
-//        return false
-//    }
-//    
-//    var duration: Double {
-//        if let currentItem = player?.currentItem {
-//            return CMTimeGetSeconds(currentItem.asset.duration)
-//        }
-//        return 0.0
-//    }
-//    
-//    var volume: Float {
-//        get {
-//            return player?.volume ?? 0.0
-//        }
-//        set {
-//            player?.volume = max(0.0, min(newValue, 1.0))
-//        }
-//    }
-//    
-//    var currentTime: Double {
-//        if let currentItem = player?.currentItem {
-//            return CMTimeGetSeconds(currentItem.currentTime())
-//        }
-//        return 0.0
-//    }
-//    
-//    var playerUsable: Bool {
-//        return player != nil
-//    }
-//    
-//    var currentTimeChanged: ((Double,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 {
-//            let audioSession = AVAudioSession.sharedInstance()
-//            try audioSession.setCategory(.playback)
-//            try audioSession.setActive(true)
-//        } catch {
-//            print("TSAudioPlayer 音频会话设置失败: \(error.localizedDescription)")
-//            return nil
-//        }
-//    }
-//    
-//    deinit {
-//        stop()
-//    }
-//    
-//    /// 开始播放音频
-//    func play() {
-//        guard let player = player else { return }
-//        
-//        let outputVolume = AVAudioSession.sharedInstance().outputVolume
-//        print("TSAudioPlayer outputVolume: \(outputVolume)")
-//        
-//        if outputVolume == 0.0 {
-//            print("Please turn up the volume".localized)
-//        }
-//        
-//        player.play()
-//        
-//        // 监听播放进度
-//        addTimeObserver()
-//    }
-//    
-//    /// 暂停播放音频
-//    func pause() {
-//        player?.pause()
-//    }
-//    
-//    /// 停止并销毁播放器
-//    func stop() {
-//        player?.pause()
-//        player?.replaceCurrentItem(with: nil)
-//        player = nil
-//        removeTimeObserver()
-//    }
-//    
-//    /// 设置是否重复播放
-//    /// - Parameter loop: 是否循环播放
-//    func setLoop(_ loop: Bool) {
-//        isLooping = loop
-//    }
-//    
-//    /// 跳转到指定时间
-//    /// - Parameter time: 目标时间(秒)
-//    func seek(to time: Double) {
-//        if let player = player {
-//            let targetTime = CMTimeMakeWithSeconds(time, preferredTimescale: 600)
-//            player.seek(to: targetTime)
-//        }
-//    }
-//    
-//    /// 监听播放完成
-//    @objc private func playerDidFinishPlaying() {
-//        if isLooping {
-//            seek(to: 0.0)
-//            play()
-//        }
-//        audioPlayerDidFinishHandle?(true)
-//    }
-//    
-//    /// 添加时间观察者
-//    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,duration)
-//        }
-//    }
-//    
-//    /// 移除时间观察者
-//    private func removeTimeObserver() {
-//        if let token = timeObserverToken {
-//            player?.removeTimeObserver(token)
-//            timeObserverToken = nil
-//        }
-//    }
-//}