Files
2024-04-12 18:56:05 +02:00

121 lines
4.2 KiB
Swift

//
// PlayerViewModel.swift
// SwiftAudio
//
// Created by David Chavez on 4/12/24.
//
import SwiftAudioEx
#if os(macOS)
import AppKit
public typealias NativeImage = NSImage
#elseif os(iOS)
import UIKit
public typealias NativeImage = UIImage
#endif
extension PlayerView {
final class ViewModel: ObservableObject {
// MARK: - Observables
@Published var playing: Bool = false
@Published var position: Double = 0
@Published var artwork: NativeImage? = nil
@Published var title: String = ""
@Published var artist: String = ""
@Published var maxTime: TimeInterval = 100
@Published var isScrubbing: Bool = false
@Published var elapsedTime: String = "00:00"
@Published var remainingTime: String = "00:00"
@Published var playWhenReady: Bool = false
@Published var playbackState: AudioPlayerState = .idle
// MARK: - Properties
let controller = AudioController.shared
// MARK: - Initializer
init() {
controller.player.event.playWhenReadyChange.addListener(self, handlePlayWhenReadyChange)
controller.player.event.stateChange.addListener(self, handleAudioPlayerStateChange)
controller.player.event.playbackEnd.addListener(self, handleAudioPlayerPlaybackEnd(data:))
controller.player.event.secondElapse.addListener(self, handleAudioPlayerSecondElapsed)
controller.player.event.seek.addListener(self, handleAudioPlayerDidSeek)
controller.player.event.updateDuration.addListener(self, handleAudioPlayerUpdateDuration)
controller.player.event.didRecreateAVPlayer.addListener(self, handleAVPlayerRecreated)
}
// MARK: - Updates
private func render() {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
playing = (controller.player.playerState == .playing)
playbackState = controller.player.playerState
playWhenReady = controller.player.playWhenReady
position = controller.player.currentTime
maxTime = controller.player.duration
artist = controller.player.currentItem?.getArtist() ?? ""
title = controller.player.currentItem?.getTitle() ?? ""
elapsedTime = controller.player.currentTime.secondsToString()
remainingTime = (controller.player.duration - controller.player.currentTime).secondsToString()
if let item = controller.player.currentItem as? DefaultAudioItem {
artwork = item.artwork
} else {
artwork = nil
}
}
}
private func renderTimes() {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
position = controller.player.currentTime
maxTime = controller.player.duration
elapsedTime = controller.player.currentTime.secondsToString()
remainingTime = (controller.player.duration - controller.player.currentTime).secondsToString()
print(elapsedTime)
}
}
// MARK: - AudioPlayer Event Handlers
func handleAudioPlayerStateChange(data: AudioPlayer.StateChangeEventData) {
print("state=\(data)")
render()
}
func handlePlayWhenReadyChange(data: AudioPlayer.PlayWhenReadyChangeData) {
print("playWhenReady=\(data)")
render()
}
func handleAudioPlayerPlaybackEnd(data: AudioPlayer.PlaybackEndEventData) {
print("playEndReason=\(data)")
}
func handleAudioPlayerSecondElapsed(data: AudioPlayer.SecondElapseEventData) {
if !isScrubbing {
renderTimes()
}
}
func handleAudioPlayerDidSeek(data: AudioPlayer.SeekEventData) {
// .. don't need this
}
func handleAudioPlayerUpdateDuration(data: AudioPlayer.UpdateDurationEventData) {
if !isScrubbing {
renderTimes()
}
}
func handleAVPlayerRecreated() {
// .. don't need this
}
}
}