// // RecordPlayerService.swift // AutoCatCore // // Created by Selim Mustafaev on 31.03.2025. // Copyright © 2025 Selim Mustafaev. All rights reserved. // import AVFoundation public final class RecordPlayerService: NSObject { var player: AVAudioPlayer? var record: AudioRecordDto? var onStop: ((AudioRecordDto, Error?) -> Void)? var onProgress: ((Double) -> Void)? var progressTimer: Timer? func playNewRecord( record: AudioRecordDto, onStop: ((AudioRecordDto, Error?) -> Void)?, onProgress: ((Double) -> Void)? ) throws { let url = try FileManager.default.url(for: record.path, in: Constants.audioRecordsFolder) player = try AVAudioPlayer(contentsOf: url) player?.delegate = self self.record = record self.onStop = onStop activateProgressTimer() self.onProgress = onProgress try activateSession() player?.play() } func activateSession() throws { try AVAudioSession.sharedInstance().setCategory( .playback, mode: .default, options: [.duckOthers] ) try AVAudioSession.sharedInstance().setActive(true) } func deactivateSession() throws { try AVAudioSession.sharedInstance().setActive( false, options: .notifyOthersOnDeactivation ) } func activateProgressTimer() { progressTimer?.invalidate() progressTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in if let player = self?.player { let progress = player.currentTime/player.duration self?.onProgress?(progress) } } } func deactivateProgressTimer() { progressTimer?.invalidate() progressTimer = nil } } extension RecordPlayerService: RecordPlayerServiceProtocol { public func play( record: AudioRecordDto, onStop: ((AudioRecordDto, Error?) -> Void)?, onProgress: ((Double) -> Void)? ) throws { guard let player, self.record?.id == record.id else { try playNewRecord(record: record, onStop: onStop, onProgress: onProgress) return } if player.isPlaying { player.pause() deactivateProgressTimer() try deactivateSession() } else { try activateSession() activateProgressTimer() player.play() } } public var currentPlayingId: String? { guard player?.isPlaying == true else { return nil } return record?.id } } extension RecordPlayerService: AVAudioPlayerDelegate { public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { guard let record else { return } onStop?(record, nil) reset() } public func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: (any Error)?) { guard let record else { return } onStop?(record, error) reset() } func reset() { onStop = nil player = nil record = nil deactivateProgressTimer() try? deactivateSession() } }