add initial support for more integrations in the now playing panel

This commit is contained in:
Boof2015
2026-04-11 22:05:00 -04:00
parent 85eddbbad5
commit 71fd59c710
30 changed files with 2479 additions and 404 deletions
+102
View File
@@ -0,0 +1,102 @@
import type { AstraIntegrationConfig } from './astra'
export type NowPlayingProviderId = 'astra' | 'spotify' | 'tidal'
export type NowPlayingPlaybackState = 'stopped' | 'playing' | 'paused' | 'loading'
export type NowPlayingProviderConnectionState = 'disabled' | 'connecting' | 'connected' | 'error' | 'unavailable'
export type NowPlayingControlCommand = 'play' | 'pause' | 'next' | 'previous'
export type NowPlayingProviderAuthMode = 'token' | 'oauth' | 'none'
export interface NowPlayingTrackSnapshot {
id: string
title: string
artist: string
album: string
isFavorite: boolean
artworkDataUrl: string | null
}
export interface NowPlayingSnapshot {
playbackState: NowPlayingPlaybackState
currentTime: number
duration: number
queueLength: number
outputDeviceLabel: string | null
visualizerLineColor: string
currentTrack: NowPlayingTrackSnapshot | null
updatedAt: number
}
export interface NowPlayingProviderDefinition {
id: NowPlayingProviderId
label: string
description: string
authMode: NowPlayingProviderAuthMode
available: boolean
comingSoon: boolean
supportsTransportControls: boolean
}
export interface UnsupportedNowPlayingProviderConfig {
}
export interface NowPlayingProviderConfigMap {
astra: AstraIntegrationConfig
spotify: UnsupportedNowPlayingProviderConfig
tidal: UnsupportedNowPlayingProviderConfig
}
export interface NowPlayingProviderState {
providerId: NowPlayingProviderId
connectionState: NowPlayingProviderConnectionState
lastError: string | null
lastControlError: string | null
snapshot: NowPlayingSnapshot | null
isConfigured: boolean
available: boolean
supportsTransportControls: boolean
}
export type NowPlayingProviderStateMap = Record<NowPlayingProviderId, NowPlayingProviderState>
export type NowPlayingProviderDefinitionMap = Record<NowPlayingProviderId, NowPlayingProviderDefinition>
export interface NowPlayingState {
definitions: NowPlayingProviderDefinitionMap
configs: NowPlayingProviderConfigMap
providers: NowPlayingProviderStateMap
providerPriority: NowPlayingProviderId[]
activeProviderId: NowPlayingProviderId | null
hasConfiguredProvider: boolean
onboardingRequired: boolean
}
export const NOW_PLAYING_PROVIDER_IDS: NowPlayingProviderId[] = ['astra', 'spotify', 'tidal']
export const NOW_PLAYING_PROVIDER_DEFINITIONS: NowPlayingProviderDefinitionMap = {
astra: {
id: 'astra',
label: 'Astra',
description: 'Connect Prism to the Astra local API for live track data and transport controls.',
authMode: 'token',
available: true,
comingSoon: false,
supportsTransportControls: true,
},
spotify: {
id: 'spotify',
label: 'Spotify',
description: 'Spotify integration is planned but not implemented yet.',
authMode: 'oauth',
available: false,
comingSoon: true,
supportsTransportControls: false,
},
tidal: {
id: 'tidal',
label: 'TIDAL',
description: 'TIDAL integration is planned but not implemented yet.',
authMode: 'oauth',
available: false,
comingSoon: true,
supportsTransportControls: false,
},
}
+14 -4
View File
@@ -6,9 +6,9 @@ export type ScopeKind =
| 'vumeter'
| 'lufsmeter'
| 'waveform'
| 'astra'
| 'nowPlaying'
export type AudioScopeKind = Exclude<ScopeKind, 'astra'>
export type AudioScopeKind = Exclude<ScopeKind, 'nowPlaying'>
export const SCOPE_KINDS: ScopeKind[] = [
'spectrum',
@@ -18,7 +18,7 @@ export const SCOPE_KINDS: ScopeKind[] = [
'vumeter',
'lufsmeter',
'waveform',
'astra',
'nowPlaying',
]
export const AUDIO_SCOPE_KINDS: AudioScopeKind[] = [
@@ -35,6 +35,16 @@ export function isAudioScopeKind(value: unknown): value is AudioScopeKind {
return typeof value === 'string' && AUDIO_SCOPE_KINDS.includes(value as AudioScopeKind)
}
export function normalizeScopeKind(value: unknown): ScopeKind | null {
if (value === 'astra') {
return 'nowPlaying'
}
return typeof value === 'string' && SCOPE_KINDS.includes(value as ScopeKind)
? value as ScopeKind
: null
}
export const SCOPE_LABELS: Record<ScopeKind, string> = {
spectrum: 'Spectrum',
oscilloscope: 'Oscilloscope',
@@ -43,5 +53,5 @@ export const SCOPE_LABELS: Record<ScopeKind, string> = {
vumeter: 'VU Meter',
lufsmeter: 'LUFS Meter',
waveform: 'Waveform',
astra: 'Astra',
nowPlaying: 'Now Playing',
}
+2 -2
View File
@@ -51,7 +51,7 @@ export interface ScopeSettings {
gainDb: number
multiband: boolean
}
astra: {
nowPlaying: {
showCoverArt: boolean
showTitle: boolean
showArtist: boolean
@@ -69,7 +69,7 @@ export const DEFAULT_SCOPE_SETTINGS: ScopeSettings = {
vumeter: { mode: 'bar', orientation: 'horizontal' },
lufsmeter: { mode: 'bar' },
waveform: { mode: DEFAULT_WAVEFORM_MODE, scrollSpeed: 1, gainDb: 0, multiband: false },
astra: {
nowPlaying: {
showCoverArt: true,
showTitle: true,
showArtist: true,
+8 -4
View File
@@ -120,7 +120,7 @@ export interface ThemeWaveformTokens {
guides?: string
}
export interface ThemeAstraTokens {
export interface ThemeNowPlayingTokens {
accent?: string
background?: string
surface?: string
@@ -134,6 +134,8 @@ export interface ThemeAstraTokens {
statusError?: string
}
export type ThemeAstraTokens = ThemeNowPlayingTokens
export interface PrismTheme {
name: string
credit?: string
@@ -149,7 +151,7 @@ export interface PrismTheme {
vumeter: ThemeVUMeterTokens
lufsmeter: ThemeLUFSMeterTokens
waveform: ThemeWaveformTokens
astra: ThemeAstraTokens
nowPlaying: ThemeNowPlayingTokens
}
export interface PrismThemeLocalStateV1 {
@@ -305,7 +307,7 @@ export interface ResolvedWaveformTheme {
bandHigh: string
}
export interface ResolvedAstraTheme {
export interface ResolvedNowPlayingTheme {
accent: string
text: string
subtext: string
@@ -323,6 +325,8 @@ export interface ResolvedAstraTheme {
statusError: string
}
export type ResolvedAstraTheme = ResolvedNowPlayingTheme
export interface PrismResolvedTheme {
name: string
credit?: string
@@ -336,5 +340,5 @@ export interface PrismResolvedTheme {
vumeter: ResolvedVUMeterTheme
lufsmeter: ResolvedLUFSMeterTheme
waveform: ResolvedWaveformTheme
astra: ResolvedAstraTheme
nowPlaying: ResolvedNowPlayingTheme
}
+2
View File
@@ -1,4 +1,5 @@
import type { ScopeKind } from './scope'
import type { WindowBounds } from './popout'
export const WINDOW_LOCAL_STATE_FORMAT = 'prism-window-local'
export const WINDOW_LOCAL_STATE_VERSION = 1
@@ -8,4 +9,5 @@ export interface PrismWindowLocalStateV1 {
version: typeof WINDOW_LOCAL_STATE_VERSION
mainAlwaysOnTop: boolean
popoutAlwaysOnTop: Partial<Record<ScopeKind, boolean>>
nowPlayingConfigWindowBounds?: WindowBounds
}