//
|
// VoicePlayer.swift
|
// DolphinEnglishLearnStudent
|
//
|
// Created by 无故事王国 on 2024/6/4.
|
//
|
|
import Foundation
|
import AVFAudio
|
|
|
protocol VoicePlayerDelegate{
|
func playComplete()
|
func playing()
|
}
|
|
/// 音频播放器
|
class VoicePlayer:NSObject{
|
/// 缓存地址
|
private let voiceCacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("voices")
|
private static var _sharedInstance: VoicePlayer?
|
private var player:AVAudioPlayer?
|
private var tempPlayer:AVAudioPlayer?
|
|
var delegate:VoicePlayerDelegate?
|
|
//是否正在播放
|
var isPlaying:Bool{return player?.isPlaying ?? false}
|
//播放完成回调
|
private var playComplete:(()->Void)?
|
|
|
/// 单例
|
class func share() -> VoicePlayer {
|
guard let instance = _sharedInstance else {
|
_sharedInstance = VoicePlayer()
|
try? FileManager.default.createDirectory(at: _sharedInstance!.voiceCacheDirectory, withIntermediateDirectories: true)
|
return _sharedInstance!
|
}
|
return instance
|
}
|
|
private override init() {} // 私有化init方法
|
|
///销毁单例对象
|
class func destroy() {
|
_sharedInstance = nil
|
}
|
|
/// 播放语音
|
/// - Parameter url: Https的语音地址
|
func playerAt(url:String?){
|
guard let u = url else {return}
|
if player?.isPlaying ?? false{
|
// player?.stop()
|
return
|
}
|
//文件存在:直接播放缓存路径的语音
|
let fileURL = self.voiceCacheDirectory.appendingPathComponent(URL(fileURLWithPath: u).lastPathComponent).droppedScheme()
|
if FileManager.default.fileExists(atPath: fileURL!.absoluteString){
|
self.player = try? AVAudioPlayer(contentsOf: fileURL!)
|
self.player?.delegate = self
|
self.player?.play()
|
DispatchQueue.main.async {
|
self.delegate?.playing()
|
}
|
}else{
|
//文件不存在:执行下载
|
let downloadTask = URLSession.shared.downloadTask(with: URL(string: u)!) { tempLocalUrl, response, error in
|
if let tempLocalUrl = tempLocalUrl, error == nil {
|
do {
|
let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent(URL(fileURLWithPath: u).lastPathComponent)
|
try FileManager.default.moveItem(at: tempLocalUrl, to: finalCacheUrl)
|
self.player = try? AVAudioPlayer(contentsOf: finalCacheUrl.droppedScheme()!)
|
self.player?.delegate = self
|
self.player?.play()
|
DispatchQueue.main.async {
|
self.delegate?.playing()
|
}
|
} catch {
|
print("视频缓存失败:catch")
|
}
|
} else {
|
print("视频缓存失败:\(error?.localizedDescription ?? "")")
|
}
|
}
|
downloadTask.resume()
|
}
|
}
|
|
func playerEnd(){
|
player?.stop()
|
playComplete?()
|
}
|
|
//打断播放未完全播放
|
func playerInterrupt(){
|
player?.stop()
|
}
|
|
func playEnd(course:@escaping ()->Void){
|
self.playComplete = course
|
}
|
|
func playSuccessVoice(){
|
let list = try? FileManager.default.contentsOfDirectory(atPath: voiceCacheDirectory.droppedScheme()!.absoluteString)
|
var promoteName:String?
|
for v in list ?? []{
|
if v.contains("SuccessPromote"){promoteName = v;break}
|
}
|
guard let url = promoteName else { return }
|
let promote = self.voiceCacheDirectory.appendingPathComponent(url)
|
tempPlayer = try? AVAudioPlayer(contentsOf: promote)
|
tempPlayer?.play()
|
}
|
|
func playFailVoice(){
|
let list = try? FileManager.default.contentsOfDirectory(atPath: voiceCacheDirectory.droppedScheme()!.absoluteString)
|
var promoteName:String?
|
for v in list ?? []{
|
if v.contains("FailPromote"){promoteName = v;break}
|
}
|
guard let url = promoteName else { return }
|
let promote = self.voiceCacheDirectory.appendingPathComponent(url)
|
tempPlayer = try? AVAudioPlayer(contentsOf: promote)
|
tempPlayer?.play()
|
}
|
|
static func hasPromoteVoice()->Bool{
|
let list = try? FileManager.default.contentsOfDirectory(atPath: VoicePlayer.share().voiceCacheDirectory.droppedScheme()!.absoluteString)
|
return list?.contains(["SuccessPromote","FailPromote"]) ?? false
|
}
|
|
func donwloadPromoteVoice(successVoice:String,failVoice:String,updateTime:String){
|
print("-->\(VoicePlayer.share().voiceCacheDirectory)")
|
let group = DispatchGroup()
|
let promoteQueue = DispatchQueue(label: "promoteVoice")
|
promoteQueue.async(group: group){
|
//文件不存在:执行下载
|
let downloadTask = URLSession.shared.downloadTask(with: URL(string: successVoice)!) { tempLocalUrl, response, error in
|
if let tempLocalUrl = tempLocalUrl, error == nil {
|
do {
|
let fileType = URL(fileURLWithPath: successVoice).lastPathComponent.components(separatedBy: ".").last ?? "mp3"
|
let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent("SuccessPromote.\(fileType)")
|
if FileManager.default.fileExists(atPath: finalCacheUrl.droppedScheme()!.absoluteString){
|
try? FileManager.default.removeItem(at: finalCacheUrl)
|
}
|
try FileManager.default.moveItem(at: tempLocalUrl, to: finalCacheUrl)
|
} catch {
|
print("视频缓存失败:catch")
|
}
|
} else {
|
print("视频缓存失败:\(error?.localizedDescription ?? "")")
|
}
|
}
|
downloadTask.resume()
|
}
|
|
promoteQueue.async(group: group){
|
//文件不存在:执行下载
|
let downloadTask = URLSession.shared.downloadTask(with: URL(string: failVoice)!) { tempLocalUrl, response, error in
|
if let tempLocalUrl = tempLocalUrl, error == nil {
|
do {
|
let fileType = URL(fileURLWithPath: successVoice).lastPathComponent.components(separatedBy: ".").last ?? "mp3"
|
let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent("FailPromote.\(fileType)")
|
if FileManager.default.fileExists(atPath: finalCacheUrl.droppedScheme()!.absoluteString){
|
try? FileManager.default.removeItem(at: finalCacheUrl)
|
}
|
try FileManager.default.moveItem(at: tempLocalUrl, to: finalCacheUrl)
|
} catch {
|
print("视频缓存失败:catch")
|
}
|
} else {
|
print("视频缓存失败:\(error?.localizedDescription ?? "")")
|
}
|
}
|
downloadTask.resume()
|
}
|
|
group.notify(queue: .main){
|
UserDefaults.standard.setValue(updateTime, forKey: "promptVoiceDate")
|
UserDefaults.standard.synchronize()
|
}
|
}
|
}
|
|
extension VoicePlayer:AVAudioPlayerDelegate{
|
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
DispatchQueue.main.async {
|
VoicePlayer.share().playComplete?()
|
self.delegate?.playComplete()
|
}
|
}
|
|
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: (any Error)?) {
|
print("播放错误")
|
}
|
}
|