11 Commits

Author SHA1 Message Date
20336ea716 feat(audio): rebuild playback + visualization on resident daemon and PCM cache
Two fragility points, rebuilt at the root:

Playback: one resident mpv daemon (--idle --keep-open) with a persistent
IPC connection and observe_property state instead of spawn-per-episode and
connect-per-poll. Play/pause/seek are sub-ms commands; time-pos pushes at
~20Hz; external pauses arrive as events. Boot session restore preloads the
episode paused (loadfile + paused time-pos seek, since mpv defers --start
stream work until playback) so first Play is a ~400ms unpause instead of a
cold 4.3s open+seek. Load ops are mutex-serialized so a raced preload
cannot clobber an in-flight play.

Data throttling: mpv demuxer cache capped (cache-secs=90, max-bytes=40MiB)
so a paused preload no longer races to its 150MiB default (measured
45.7MB/12s); decoder paced at 4x realtime instead of 84x so playback start
isn't starved by the visualizer ripping the whole episode.

Visualization: replaced the paced-ring reader (AudioStreamReader) with a
position-indexed PCM cache (audio-pcm-cache). ffmpeg fills a cache indexed
by absolute playback time; reads at the player position are always exact.
Pause freezes the render loop, resume re-arms it — no coverage guessing,
no clamped-buffer freeze (the pause->broken-waveform->freeze bug). Seeks
and speed changes need no pipeline restarts; uncovered reads return empty
and the last frame holds.

Cover art: persistent per-URL disk cache under XDG cache dir; play() no
longer awaits a curl subprocess (up to 8s). Cache hit = one stat; misses
apply late via mpv video-add.

Test suite: 161 pass. New tests pin the position-index contract (sample-
exact window reads, hold-on-uncovered, pause-keeps-cache, seek segments),
the daemon contract (play/pause/resume/seek/stop, preload fast path,
EOF->replay), and cover cache/single-flight/404.
2026-08-11 19:54:05 -04:00
3388757185 fix(macos): hard-fail bundle without mpv, PATH fallback, CI mpv install
- build.ts: darwin compile now exits 1 when mpv is absent — CI can no
  longer ship a PodTui.app without its bundled player (0.4.0 did, killing
  Now Playing attribution).
- audio-player.ts: the bundled mpv is probed (--version) at first resolve;
  it links against brew's dylibs, and a Homebrew ffmpeg major upgrade can
  break it — fall back to PATH mpv so audio survives (icon degrades to
  blank instead of playback dying). Probed once per process.
- release.yml: brew install mpv on darwin runners; smoke test now asserts
  the tarball's PodTui.app has a launchable mpv signed with the
  com.mikefreno.podtui identifier.
2026-08-11 13:37:43 -04:00
a9589e7686 feat(macos): ship PodTui.app bundle so Now Playing shows our icon
mpv owns the macOS Now Playing session (it plays the audio), and an
unbundled binary renders as a blank placeholder for the source-app icon.
macOS has no public API for a third party to claim session ownership
(MPNowPlayingSession is iOS-only; the private MRMediaRemoteSetNowPlayingApplication
was removed from the shared cache), so instead we make the OWNING process
carry our bundle: the darwin tarball now includes PodTui.app with mpv copied
into Contents/MacOS. AudioPlayer resolves the sibling mpv first (falling
back to PATH), LaunchServices attributes the process to com.mikefreno.podtui,
and Control Center shows the PodTui icon + name with podcast cover art.
AppIcon.icns generated from the Xcode icon-composer exports.
2026-08-10 17:56:54 -04:00
5dce21c038 feat(media): expose podcast name + cover art to system Now Playing
- mpv: --force-media-title '<podcast> — <episode>' so macOS Now Playing shows
  the podcast name instead of the download-hash filename (mediaTitle PlayOption)
- media registry: artist is now the human podcast title (customName ||
  podcast.title), falling back to podcastId
- downloads: write the podcast cover as a <base>.jpg sibling so mpv's
  cover-art-auto=exact picks it up for artwork; delete it with the download
2026-08-10 16:50:08 -04:00
19eae4fd5a feat: sync waveform to live player position (mpv time-pos, position-window reads, smooth clock) 2026-08-10 15:51:02 -04:00
91a831c5f9 audio playback fixes 2026-08-07 19:28:57 -04:00
85cb9fba26 ui cleanup 2026-08-07 13:38:32 -04:00
d8f11040bc start revive 2026-07-30 21:25:03 -04:00
2e323d283f needs testing 2026-02-07 00:45:14 -05:00
0e4f47323f mulitmedia pass, downloads 2026-02-06 00:00:15 -05:00
f3344fbed2 start player 2026-02-05 18:42:56 -05:00