mHub is an open protocol for media catalogs, a browser API for media websites, and apps that bring both to phones, TVs and desktops. Publish a folder of JSON files and your archive is browsable in every app that speaks the protocol. Load one script and your site gets powers a browser does not hand out.
Everything lives under one namespace: window.mhub,
and every host serves the full core. Feature-detect the namespace once, then just call it.
The API is built and documented, and it arrives with the next release of
the apps. Write against it today and your page keeps working either way:
where no host is present, window.mhub
is simply absent and your site stays an ordinary website.
Fetch anything. No CORS, and request headers the browser normally reserves for itself.
// cross-origin, readable const r = await mhub.fetch(url);
Some media needs its own request headers to play. One call, and the host picks the right route on every platform.
const s = await mhub.openStream( { url, headers }); video.src = s.url;
Key/value storage that follows your site, not the domain it happens to run on. No prompt, no setup.
await mhub.storage.set(k, v);
One domain down, your site still loads. Serve one small file on every domain; the browser finds it, verifies your mirrors and fails over by itself. No code.
{ "id": "mysite", "endpoints": [ "https://a.example", "https://b.example" ] }
A tile on the browser home screen. The way back to your site, surviving between visits.
mhub.setLinks([ { id, name, icon, url }]);
The address bar becomes your site's search. Queries and suggestions go straight to your page.
mhub.setSearch({ placeholder, onQuery, onSuggest });
Know where you run and what the host can do, from phone to TV, before first paint.
if (mhub.device.isTV) tvUi();
First script in your <head>. Self-host it if you prefer; it has no server side.
<script src="https://mhub.mx/mhub.js"> </script>
One promise tells you whether an mHub host is present.
const hosted = await mhub.ready;
Call the API directly. The user grants web access once per site.
const r = await mhub.fetch(url); render(await r.json());
The same API on every platform, arriving with the next release of each
app. What varies between hosts is announced in
mhub.capabilities, never silently missing.
Version 2 of the addon protocol needs no server: every endpoint can be a static file on any CDN. The whole spec fits in a single prompt, so you can hand it to an AI and get a working addon back.
All endpoints are GET, all cacheable, all hostable as plain files. One optional POST for the task system.
/mhub-addon.json /catalog/{type}/{catalogId}.json /item/{type}/{id}.json /source/{type}/{id}.json
Catalogs, item details, sources, resolvers and subtitles are separate resources. One addon can provide all of them or just one.
"resources": ["catalog", "item", "source", "resolve", "subtitle"]
An endpoint can answer with a task instead of data. The app fetches with the user's own IP, so a static addon works with geo-blocked APIs.
// addon stays static, // the client does the trip
A minimal addon is one manifest and one catalog file. Upload both to any static host and it works. The spec is OpenAPI 3.1 with generated TypeScript types and a zero-dependency client library, MIT licensed.
{ "id": "my-movies", "name": "My Movies", "specVersion": 2, "resources": ["catalog"], "types": ["video"], "catalogs": [{ "id": "all", "name": "All", "type": "video" }] }
The full API reference covers every member, the permission model, and the mirror system. The Addon Protocol v2 spec follows when its repo goes public.
API documentation