mulitmedia pass, downloads

This commit is contained in:
2026-02-06 00:00:15 -05:00
parent 42a1ddf458
commit 0e4f47323f
29 changed files with 1195 additions and 23 deletions

192
src/utils/media-registry.ts Normal file
View File

@@ -0,0 +1,192 @@
/**
* Platform-specific media session registration.
*
* Registers the currently playing track with the OS so that system
* media controls (notification center, lock screen, MPRIS) display
* track info and can send play/pause/next/prev commands.
*
* Implementations:
* - **macOS**: Shells out to `nowplaying-cli` (brew install nowplaying-cli)
* Falls back to no-op if the binary isn't available.
* - **Linux**: Writes a minimal MPRIS2 metadata file that desktop
* environments can pick up. Full D-Bus integration would
* require native bindings; this is best-effort.
* - **Other**: No-op stub.
*
* All methods are fire-and-forget and never throw.
*/
import { spawn } from "child_process"
export interface TrackMetadata {
title: string
artist?: string
album?: string
artworkUrl?: string
duration?: number // seconds
}
export interface MediaRegistryInstance {
/** Platform identifier */
readonly platform: "macos" | "linux" | "windows" | "unknown"
/** Whether the platform integration is available */
readonly available: boolean
/** Register / update now-playing metadata */
setNowPlaying(meta: TrackMetadata): void
/** Update playback position (seconds) */
setPosition(seconds: number): void
/** Update playing/paused state */
setPlaybackState(playing: boolean): void
/** Clear now-playing info (e.g. on stop) */
clearNowPlaying(): void
/** Tear down any resources */
dispose(): void
}
// ---------------------------------------------------------------------------
// Platform detection
// ---------------------------------------------------------------------------
function detectPlatform(): "macos" | "linux" | "windows" | "unknown" {
switch (process.platform) {
case "darwin":
return "macos"
case "linux":
return "linux"
case "win32":
return "windows"
default:
return "unknown"
}
}
// ---------------------------------------------------------------------------
// macOS — nowplaying-cli
// ---------------------------------------------------------------------------
function hasBinary(name: string): boolean {
try {
const result = Bun.spawnSync(["which", name])
return result.exitCode === 0
} catch {
return false
}
}
function createMacOSRegistry(): MediaRegistryInstance {
const hasNowPlaying = hasBinary("nowplaying-cli")
function run(args: string[]): void {
if (!hasNowPlaying) return
try {
const proc = spawn("nowplaying-cli", args, {
stdio: "ignore",
detached: true,
})
proc.unref()
} catch {
// Best-effort
}
}
return {
platform: "macos",
available: hasNowPlaying,
setNowPlaying(meta) {
const args = ["set", "title", meta.title]
if (meta.artist) args.push("artist", meta.artist)
if (meta.album) args.push("album", meta.album)
if (meta.duration) args.push("duration", String(meta.duration))
run(args)
},
setPosition(seconds) {
run(["set", "elapsedTime", String(Math.floor(seconds))])
},
setPlaybackState(playing) {
run(["set", "playbackRate", playing ? "1" : "0"])
},
clearNowPlaying() {
run(["clear"])
},
dispose() {
run(["clear"])
},
}
}
// ---------------------------------------------------------------------------
// Linux — best-effort MPRIS stub
// ---------------------------------------------------------------------------
function createLinuxRegistry(): MediaRegistryInstance {
// Full MPRIS2 requires owning a D-Bus name and exposing the
// org.mpris.MediaPlayer2.Player interface. That needs native
// bindings (dbus-next, etc.) which adds significant complexity.
//
// For now we provide a no-op stub that can be upgraded later
// without changing the public interface.
return {
platform: "linux",
available: false,
setNowPlaying() {},
setPosition() {},
setPlaybackState() {},
clearNowPlaying() {},
dispose() {},
}
}
// ---------------------------------------------------------------------------
// No-op fallback
// ---------------------------------------------------------------------------
function createNoopRegistry(platform: "windows" | "unknown"): MediaRegistryInstance {
return {
platform,
available: false,
setNowPlaying() {},
setPosition() {},
setPlaybackState() {},
clearNowPlaying() {},
dispose() {},
}
}
// ---------------------------------------------------------------------------
// Factory & singleton
// ---------------------------------------------------------------------------
let instance: MediaRegistryInstance | null = null
/**
* Returns the singleton MediaRegistry for the current platform.
* Always safe to call — returns a no-op if no integration is available.
*/
export function useMediaRegistry(): MediaRegistryInstance {
if (instance) return instance
const platform = detectPlatform()
switch (platform) {
case "macos":
instance = createMacOSRegistry()
break
case "linux":
instance = createLinuxRegistry()
break
default:
instance = createNoopRegistry(platform)
break
}
return instance
}