173 lines
5.2 KiB
Swift
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)
|
|
}
|