Stemify iOS 1.1 and WebUI updates, repo restructure into desktop/ios/server/web
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
894dd5a695
commit
df5d847169
157 files changed
+3935
-742
No files matched your search
@@ -8,13 +8,17 @@ import UIKit
|
||||
final class NowPlaying: @unchecked Sendable {
|
||||
private let pm: PlaybackManager
|
||||
private let plex: PlexClient
|
||||
private let radio: WebRadio
|
||||
private let settings: Settings
|
||||
private let center = MPNowPlayingInfoCenter.default()
|
||||
private var artKey: String?
|
||||
private var artwork: MPMediaItemArtwork?
|
||||
|
||||
init(pm: PlaybackManager, plex: PlexClient) {
|
||||
init(pm: PlaybackManager, plex: PlexClient, radio: WebRadio, settings: Settings) {
|
||||
self.pm = pm
|
||||
self.plex = plex
|
||||
self.radio = radio
|
||||
self.settings = settings
|
||||
pm.addListener { [weak self] in self?.publish() }
|
||||
wireCommands()
|
||||
publish()
|
||||
@@ -25,13 +29,14 @@ final class NowPlaying: @unchecked Sendable {
|
||||
/// The mix shown next to the artist: nil unless stems are playing or the mix is Full.
|
||||
var mixLabel: String? {
|
||||
guard pm.source == .Stems else { return nil }
|
||||
guard let p = MixPresets.current(pm.stemNames, pm.mix) else { return "Custom mix" }
|
||||
guard let p = MixPresets.current(settings, pm.stemNames, pm.mix) else { return "Custom mix" }
|
||||
return p == MixPresets.full ? nil : p.label
|
||||
}
|
||||
|
||||
var vocalsAvailable: Bool { pm.source == .Stems && pm.stemNames.contains("vocals") }
|
||||
var vocalsSilent: Bool { MixPresets.vocalsSilent(pm.stemNames, pm.mix) }
|
||||
var nextMix: MixPreset { MixPresets.next(pm.stemNames, pm.mix) }
|
||||
/// Follows the Stem Editor: deleted mixes are skipped and the user's own are in the rotation.
|
||||
var nextMix: MixPreset { MixPresets.next(settings, pm.stemNames, pm.mix) }
|
||||
|
||||
/// One tap: hear the vocals again, or take them out.
|
||||
func toggleVocals() {
|
||||
@@ -50,7 +55,11 @@ final class NowPlaying: @unchecked Sendable {
|
||||
|
||||
// ---- info --------------------------------------------------------------------------
|
||||
|
||||
/// Puts the queue back on the lock screen (after the web radio stops).
|
||||
func refresh() { publish() }
|
||||
|
||||
private func publish() {
|
||||
if radio.active { return }
|
||||
guard let t = pm.current?.track else {
|
||||
center.nowPlayingInfo = nil
|
||||
center.playbackState = .stopped
|
||||
@@ -92,16 +101,21 @@ final class NowPlaying: @unchecked Sendable {
|
||||
|
||||
private func wireCommands() {
|
||||
let c = MPRemoteCommandCenter.shared()
|
||||
func on(_ cmd: MPRemoteCommand, _ f: @escaping (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus) {
|
||||
func on(_ cmd: MPRemoteCommand, _ f: @escaping @MainActor (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus) {
|
||||
cmd.isEnabled = true
|
||||
cmd.addTarget { e in MainActor.assumeIsolated { f(e) } }
|
||||
}
|
||||
on(c.playCommand) { [pm] _ in pm.play(); return .success }
|
||||
on(c.pauseCommand) { [pm] _ in pm.pause(); return .success }
|
||||
on(c.stopCommand) { [pm] _ in pm.pause(); return .success }
|
||||
on(c.togglePlayPauseCommand) { [pm] _ in pm.togglePlay(); return .success }
|
||||
on(c.nextTrackCommand) { [pm] _ in pm.next(); return .success }
|
||||
on(c.previousTrackCommand) { [pm] _ in pm.previous(); return .success }
|
||||
// While a web radio station is loaded, play/pause drive it and skipping does nothing.
|
||||
on(c.playCommand) { [pm, radio] _ in if radio.active { radio.resume() } else { pm.play() }; return .success }
|
||||
on(c.pauseCommand) { [pm, radio] _ in if radio.active { radio.pause() } else { pm.pause() }; return .success }
|
||||
on(c.stopCommand) { [pm, radio] _ in if radio.active { radio.stop() } else { pm.pause() }; return .success }
|
||||
on(c.togglePlayPauseCommand) { [pm, radio] _ in if radio.active { radio.toggle() } else { pm.togglePlay() }; return .success }
|
||||
on(c.nextTrackCommand) { [pm, radio] _ in if !radio.active { pm.next() }; return .success }
|
||||
on(c.previousTrackCommand) { [weak self, pm, radio] _ in
|
||||
guard !radio.active else { return .success }
|
||||
if self?.settings.lockScreenMix == true && pm.source == .Stems { self?.cycleMix() } else { pm.previous() }
|
||||
return .success
|
||||
}
|
||||
on(c.changePlaybackPositionCommand) { [pm] e in
|
||||
guard let e = e as? MPChangePlaybackPositionCommandEvent else { return .commandFailed }
|
||||
pm.seekTo(Int64(e.positionTime * 1000))
|
||||
|
||||
Reference in new issue
Block a user