137 lines
3.4 KiB
Swift
137 lines
3.4 KiB
Swift
//
|
|
// 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()
|
|
}
|
|
}
|