Several sources, one rectangle
The player walks its hosts in priority order and plays the first that answers. When they all miss it says so — MEDIA_UNAVAILABLE, not a spinner forever.
Point an iframe at a TMDB or IMDb id and it plays. We find the sources, pick the best one, load the subtitles and handle the fallbacks — your page just holds the rectangle.
No SDK, no script tag on your page, no account. Give it a 16:9 box and keep the allow attributes — fullscreen and picture-in-picture are granted by the parent, not by us.
<iframe src="https://player.example.com/movie/550" style="width:100%;aspect-ratio:16/9;border:0" allow="autoplay; fullscreen; encrypted-media; picture-in-picture" allowfullscreen referrerpolicy="origin"></iframe>
The player walks its hosts in priority order and plays the first that answers. When they all miss it says so — MEDIA_UNAVAILABLE, not a spinner forever.
Until someone presses the poster the frame is artwork and about 80 KB of JavaScript. Put ten on a listing page; it stays cheap.
Tracks are collected from every host that answered, not just the one playing. Add your own with ?sub= and it leads the list.
postMessage both ways: progress, source changes and episode moves out; play, pause, seek, volume and LOAD in.
The player is ad-supported and every ad stays inside the frame — nothing is written into your page or your layout. The frame is served Referrer-Policy: no-referrer, so the hosts we resolve never learn where it was embedded. We count views and which sites embed us — the domain only, never a URL; that is what referrerpolicy="origin" in the snippet is for. Don't add sandbox to the iframe: it blocks the ads that fund it.
Accent colour, poster, start position, auto-next and the next-episode button are all query parameters. No stylesheet to fight.
The frame reports READY, PLAY,SOURCE_CHANGE, TIME_UPDATE,NEXT_EPISODE, ENDED andMEDIA_UNAVAILABLE. Send it LOAD to swap episodes without a reload — the warm cache and the resolved source survive, so the next one starts noticeably faster.
window.addEventListener("message", (event) => {
if (event.origin !== PLAYER) return;
if (event.data?.source !== "embed-player") return;
if (event.data.event === "TIME_UPDATE") {
save(event.data.data.currentTime);
}
});The test page builds the URL, shows you exactly what to paste, and plays it in the same frame your visitors would get.