콘텐츠 추가
This commit is contained in:
282
SodaLive/Sources/Content/ContentPlayManager.swift
Normal file
282
SodaLive/Sources/Content/ContentPlayManager.swift
Normal file
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// 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: .default)
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user