283 lines
8.8 KiB
Swift
283 lines
8.8 KiB
Swift
//
|
|
// ContentPlayManager.swift
|
|
// SodaLive
|
|
//
|
|
// Created by klaus on 2023/08/11.
|
|
//
|
|
|
|
import Foundation
|
|
import AVKit
|
|
import MediaPlayer
|
|
import ObjectBox
|
|
|
|
final class ContentPlayManager: NSObject, ObservableObject {
|
|
static let shared = ContentPlayManager()
|
|
|
|
var creatorId = 0
|
|
@Published var contentId: Int = 0
|
|
|
|
@Published private (set) var duration: TimeInterval = 0
|
|
|
|
@Published var title = ""
|
|
@Published var nickname = ""
|
|
@Published var coverImage = ""
|
|
|
|
@Published var isFree: Bool? = nil
|
|
@Published var isPreview: Bool? = nil
|
|
@Published private (set) var isShowingMiniPlayer = false
|
|
@Published private (set) var isPlaying = false
|
|
|
|
@Published var isLoading = false
|
|
@Published var errorMessage = ""
|
|
@Published var isShowPopup = false
|
|
|
|
var player: AVAudioPlayer!
|
|
|
|
var startTimer: (() -> Void)?
|
|
var stopTimer: (() -> Void)?
|
|
|
|
private var playbackTrackingId: Id = 0
|
|
private let repository = PlaybackTrackingRepository()
|
|
}
|
|
|
|
extension ContentPlayManager {
|
|
func playAudio(
|
|
creatorId: Int = 0,
|
|
contentId: Int = 0,
|
|
title: String = "",
|
|
nickname: String = "",
|
|
coverImage: String = "",
|
|
contentUrl: String = "",
|
|
isFree: Bool? = nil,
|
|
isPreview: Bool? = nil
|
|
) {
|
|
if contentId <= 0 {
|
|
return
|
|
}
|
|
|
|
if let startTimer = startTimer {
|
|
startTimer()
|
|
}
|
|
|
|
if self.contentId > 0 && self.contentId == contentId {
|
|
player?.play()
|
|
isPlaying = player.isPlaying
|
|
} else {
|
|
isLoading = true
|
|
stopAudio()
|
|
|
|
self.creatorId = creatorId
|
|
self.contentId = contentId
|
|
self.title = title
|
|
self.nickname = nickname
|
|
self.coverImage = coverImage
|
|
self.isFree = isFree
|
|
self.isPreview = isPreview
|
|
|
|
guard let url = URL(string: contentUrl) else {
|
|
showError()
|
|
return
|
|
}
|
|
|
|
URLSession.shared.dataTask(with: url) { [unowned self] data, response, error in
|
|
guard let audioData = data else {
|
|
self.isLoading = false
|
|
return
|
|
}
|
|
|
|
do {
|
|
let audioSession = AVAudioSession.sharedInstance()
|
|
try audioSession.setCategory(.playback, mode: .moviePlayback)
|
|
try audioSession.setActive(true)
|
|
|
|
self.player = try AVAudioPlayer(data: audioData)
|
|
saveNewPlaybackTracking(totalDuration: Int(player.duration), progress: 0)
|
|
|
|
DispatchQueue.main.async {
|
|
self.player?.volume = 1
|
|
self.player?.delegate = self
|
|
self.player?.prepareToPlay()
|
|
|
|
self.duration = self.player.duration
|
|
self.player?.play()
|
|
self.isPlaying = self.player.isPlaying
|
|
self.isShowingMiniPlayer = true
|
|
UIApplication.shared.beginReceivingRemoteControlEvents()
|
|
}
|
|
|
|
self.registerNowPlayingInfoCenter()
|
|
self.registerRemoteControlEvents()
|
|
} catch {
|
|
DispatchQueue.main.async {
|
|
self.showError()
|
|
}
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
self.isLoading = false
|
|
}
|
|
}.resume()
|
|
}
|
|
}
|
|
|
|
func stopAudio() {
|
|
if let player = player {
|
|
player.stop()
|
|
setEndPositionPlaybackTracking(progress: Int(player.currentTime))
|
|
|
|
player.currentTime = 0
|
|
isPlaying = player.isPlaying
|
|
}
|
|
|
|
resetAudioData()
|
|
unRegisterRemoteControlEvents()
|
|
}
|
|
|
|
func conditionalStopAudio(contentId: Int) {
|
|
if self.contentId == contentId {
|
|
stopAudio()
|
|
}
|
|
}
|
|
|
|
func pauseAudio() {
|
|
if let player = player {
|
|
player.pause()
|
|
isPlaying = player.isPlaying
|
|
if let stopTimer = stopTimer {
|
|
stopTimer()
|
|
}
|
|
}
|
|
}
|
|
|
|
func resetAudioData() {
|
|
title = ""
|
|
nickname = ""
|
|
coverImage = ""
|
|
contentId = 0
|
|
duration = 0
|
|
|
|
isPreview = false
|
|
isShowingMiniPlayer = false
|
|
player = nil
|
|
startTimer = nil
|
|
stopTimer = nil
|
|
}
|
|
|
|
func setCurrentTime(_ progress: TimeInterval) {
|
|
if let player = player, contentId > 0 {
|
|
player.currentTime = progress
|
|
saveNewPlaybackTracking(totalDuration: Int(player.duration), progress: Int(progress))
|
|
}
|
|
}
|
|
|
|
private func repeatAudio() {
|
|
if let stopTimer = stopTimer {
|
|
stopTimer()
|
|
}
|
|
|
|
player.stop()
|
|
setEndPositionPlaybackTracking(progress: Int(player.currentTime))
|
|
player.currentTime = 0
|
|
|
|
saveNewPlaybackTracking(totalDuration: Int(player.duration), progress: 0)
|
|
player.play()
|
|
|
|
if let startTimer = startTimer {
|
|
startTimer()
|
|
}
|
|
}
|
|
|
|
private func showError() {
|
|
self.errorMessage = "오류가 발생했습니다. 다시 시도해 주세요."
|
|
self.isShowPopup = true
|
|
self.resetAudioData()
|
|
}
|
|
|
|
private func registerNowPlayingInfoCenter() {
|
|
let center = MPNowPlayingInfoCenter.default()
|
|
var nowPlayingInfo = center.nowPlayingInfo ?? [String: Any]()
|
|
|
|
nowPlayingInfo[MPMediaItemPropertyTitle] = title
|
|
nowPlayingInfo[MPMediaItemPropertyArtist] = nickname
|
|
if let artworkURL = URL(string: coverImage), let imageData = try? Data(contentsOf: artworkURL), let artworkImage = UIImage(data: imageData) {
|
|
let artwork = MPMediaItemArtwork(boundsSize: artworkImage.size) { size in
|
|
return artworkImage
|
|
}
|
|
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
|
|
}
|
|
|
|
// 콘텐츠 총 길이
|
|
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = player.duration
|
|
// 콘텐츠 재생 시간에 따른 progressBar 초기화
|
|
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
|
|
// 콘텐츠 현재 재생시간
|
|
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player.currentTime
|
|
|
|
center.nowPlayingInfo = nowPlayingInfo
|
|
}
|
|
|
|
private func registerRemoteControlEvents() {
|
|
let center = MPRemoteCommandCenter.shared()
|
|
|
|
center.playCommand.addTarget { [unowned self] (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
if let player = player {
|
|
player.play()
|
|
self.isPlaying = player.isPlaying
|
|
if let startTimer = self.startTimer {
|
|
startTimer()
|
|
}
|
|
}
|
|
|
|
return .success
|
|
}
|
|
|
|
center.pauseCommand.addTarget { [unowned self] (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
self.pauseAudio()
|
|
return .success
|
|
}
|
|
}
|
|
|
|
private func unRegisterRemoteControlEvents() {
|
|
let center = MPRemoteCommandCenter.shared()
|
|
center.playCommand.removeTarget(nil)
|
|
center.pauseCommand.removeTarget(nil)
|
|
UIApplication.shared.endReceivingRemoteControlEvents()
|
|
}
|
|
}
|
|
|
|
extension ContentPlayManager {
|
|
private func saveNewPlaybackTracking(totalDuration: Int, progress: Int) {
|
|
if creatorId != UserDefaults.int(forKey: .userId) {
|
|
playbackTrackingId = repository
|
|
.savePlaybackTracking(data: PlaybackTracking(
|
|
audioContentId: contentId,
|
|
totalDuration: totalDuration,
|
|
startPosition: progress,
|
|
isFree: isFree ?? true,
|
|
isPreview: isPreview ?? true)
|
|
)
|
|
}
|
|
}
|
|
|
|
private func setEndPositionPlaybackTracking(progress: Int) {
|
|
if creatorId != UserDefaults.int(forKey: .userId) && playbackTrackingId > 0 {
|
|
if let playbackTracking = repository.getPlaybackTracking(id: playbackTrackingId) {
|
|
playbackTracking.endPosition = progress
|
|
_ = repository.savePlaybackTracking(data: playbackTracking)
|
|
}
|
|
|
|
playbackTrackingId = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
extension ContentPlayManager: AVAudioPlayerDelegate {
|
|
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
|
if UserDefaults.bool(forKey: .isContentPlayLoop) {
|
|
repeatAudio()
|
|
} else {
|
|
stopAudio()
|
|
}
|
|
}
|
|
}
|