Three of the apps meant to adopt this already have a player, and they are not
thin ones: p0dcasters runs a 511-line queue-aware dock, rssamplifier has a
dock plus a playlist player, and media-streamer has a modal per source with
its own retry and favourites. Handing them `createPlayer` would mean deleting
queues, next/previous and video.js to gain a nicer-looking bar. That is a
regression dressed as an adoption.
What all three DO need is the half below the UI: which engine plays this
source. Each answers it separately today, which is how a podcast shipping an
HLS enclosure plays on one of our sites and not another.
So `attachSource(media, { src })` picks the engine, attaches it, and hands
back something that lets go. No DOM, no styles, no opinions about the host's
UI. `createPlayer` now calls it too rather than keeping a second copy of the
ladder -- a second copy is the one that stops matching.
One behaviour change falls out and is an improvement: an unplayable source no
longer attaches a native engine as a consolation. Pointing a <video> at an
.m3u8 it cannot parse replaced a precise reason ("this browser cannot play HLS
streams") with a generic media error a few seconds later.
8 new tests; 79 pass, tsc and prettier clean.
Claude-Session: https://claude.ai/code/session_01WFLwZtXE6iBJyJSgrsWqRM
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>