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

173 lines
5.2 KiB
Swift

//
// PlayerView.swift
// SwiftAudio
//
// Created by Brandon Sneed on 3/30/24.
//
import SwiftUI
import SwiftAudioEx
struct PlayerView: View {
@ObservedObject var viewModel: ViewModel
@State private var showingQueue = false
let controller = AudioController.shared
init(viewModel: PlayerView.ViewModel = ViewModel()) {
self.viewModel = viewModel
}
var body: some View {
VStack(spacing: 0) {
HStack(alignment: .center) {
Spacer()
Button(action: { showingQueue.toggle() }, label: {
Text("Queue")
.fontWeight(.bold)
})
}
if let image = viewModel.artwork {
#if os(macOS)
Image(nsImage: image)
.resizable()
.scaledToFit()
.frame(width: 240, height: 240)
.padding(.top, 30)
#elseif os(iOS)
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 240, height: 240)
.padding(.top, 30)
#endif
} else {
AsyncImage(url: nil)
.frame(width: 240, height: 240)
.padding(.top, 30)
}
VStack(spacing: 4) {
Text(viewModel.title)
.fontWeight(.semibold)
.font(.system(size: 18))
Text(viewModel.artist)
.fontWeight(.thin)
}
.padding(.top, 30)
if viewModel.maxTime > 0 {
VStack {
Slider(value: $viewModel.position, in: 0...viewModel.maxTime) { editing in
viewModel.isScrubbing = editing
print("scrubbing = \(viewModel.isScrubbing)")
if viewModel.isScrubbing == false {
controller.player.seek(to: viewModel.position)
}
}
HStack {
Text(viewModel.elapsedTime)
.font(.system(size: 14))
Spacer()
Text(viewModel.remainingTime)
.font(.system(size: 14))
}
}
.padding(.top, 25)
} else {
Text("Live Stream")
.padding(.top, 35)
}
HStack {
Button(action: controller.player.previous, label: {
Text("Prev")
.font(.system(size: 14))
})
.frame(maxWidth: .infinity)
Button(action: {
if viewModel.playing {
controller.player.pause()
} else {
controller.player.play()
}
}, label: {
Text(!viewModel.playWhenReady || viewModel.playbackState == .failed ? "Play" : "Pause")
.font(.system(size: 18))
.fontWeight(.semibold)
})
.frame(maxWidth: .infinity)
Button(action: controller.player.next, label: {
Text("Next")
.font(.system(size: 14))
})
.frame(maxWidth: .infinity)
}
.padding(.top, 80)
VStack {
if viewModel.playbackState == .failed {
Text("Playback failed.")
.font(.system(size: 14))
.foregroundStyle(.red)
.padding(.top, 20)
} else if (viewModel.playbackState == .loading || viewModel.playbackState == .buffering) && viewModel.playWhenReady {
ProgressView()
.progressViewStyle(.circular)
.controlSize(.small)
.padding(.top, 20)
}
}
Spacer()
}
.sheet(isPresented: $showingQueue) {
QueueView()
#if os(macOS)
.frame(width: 300, height: 400)
#endif
}
.padding(.horizontal, 16)
.padding(.top)
}
}
#Preview("Standard") {
let viewModel = PlayerView.ViewModel()
viewModel.title = "Longing"
viewModel.artist = "David Chavez"
return PlayerView(viewModel: viewModel)
}
#Preview("Error") {
let viewModel = PlayerView.ViewModel()
viewModel.title = "Longing"
viewModel.artist = "David Chavez"
viewModel.playbackState = .failed
return PlayerView(viewModel: viewModel)
}
#Preview("Buffering") {
let viewModel = PlayerView.ViewModel()
viewModel.title = "Longing"
viewModel.artist = "David Chavez"
viewModel.playbackState = .buffering
viewModel.playWhenReady = true
return PlayerView(viewModel: viewModel)
}
#Preview("Live Stream") {
let viewModel = PlayerView.ViewModel()
viewModel.title = "Longing"
viewModel.artist = "David Chavez"
viewModel.maxTime = 0
return PlayerView(viewModel: viewModel)
}