stremio/apps/tizen-tv/www/js/api.js
Jos Vooges | STH a0311c031e Add Samsung Tizen TV web client.
Focus-friendly VMC shell for Tizen with live TV and playback against the master API.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 19:16:30 +02:00

163 lines
5.8 KiB
JavaScript

(function (global) {
var BASE = "https://master.vonas.nl";
var TOKEN_KEY = "mc_refresh_token";
var PROFILE_KEY = "mc_profile";
var FAV_KEY = "mc_iptv_favs";
function store() {
try {
return localStorage;
} catch (e) {
return {
_d: {},
getItem: function (k) { return this._d[k] || null; },
setItem: function (k, v) { this._d[k] = String(v); },
removeItem: function (k) { delete this._d[k]; },
};
}
}
var ls = store();
function getToken() { return ls.getItem(TOKEN_KEY); }
function setToken(token) {
if (token) ls.setItem(TOKEN_KEY, token);
else ls.removeItem(TOKEN_KEY);
}
function getProfileCache() {
try { return JSON.parse(ls.getItem(PROFILE_KEY) || "null"); } catch (e) { return null; }
}
function setProfileCache(profile) {
if (profile) ls.setItem(PROFILE_KEY, JSON.stringify(profile));
else ls.removeItem(PROFILE_KEY);
}
function getFavs() {
try { return JSON.parse(ls.getItem(FAV_KEY) || "[]"); } catch (e) { return []; }
}
function setFavs(ids) { ls.setItem(FAV_KEY, JSON.stringify(ids || [])); }
function toggleFav(streamId) {
var id = String(streamId);
var favs = getFavs();
var i = favs.indexOf(id);
if (i >= 0) favs.splice(i, 1);
else favs.push(id);
setFavs(favs);
return favs.indexOf(id) >= 0;
}
function isFav(streamId) { return getFavs().indexOf(String(streamId)) >= 0; }
function errMessage(body, status) {
if (body && body.error && body.error.message) return body.error.message;
if (body && body.error && body.error.code) return body.error.code;
return "HTTP " + status;
}
function request(path, opts) {
opts = opts || {};
var headers = opts.headers || {};
headers["Content-Type"] = "application/json";
var token = getToken();
if (token && !opts.noAuth) headers.Authorization = "Bearer " + token;
return fetch(BASE + path, {
method: opts.method || "GET",
headers: headers,
body: opts.body ? JSON.stringify(opts.body) : undefined,
}).then(function (res) {
return res.text().then(function (text) {
var body = null;
try { body = text ? JSON.parse(text) : null; } catch (e) { body = { raw: text }; }
if (!res.ok) {
var err = new Error(errMessage(body, res.status));
err.status = res.status;
err.code = body && body.error && body.error.code;
err.body = body;
throw err;
}
return body;
});
});
}
global.McApi = {
BASE: BASE,
getToken: getToken,
setToken: setToken,
getProfileCache: getProfileCache,
setProfileCache: setProfileCache,
getFavs: getFavs,
toggleFav: toggleFav,
isFav: isFav,
clearSession: function () { setToken(null); setProfileCache(null); },
deviceCode: function () {
return request("/api/v1/client/device/code", {
method: "POST", noAuth: true,
body: { deviceName: "Samsung Tizen", platform: "tizen" },
});
},
devicePoll: function (code) {
return request("/api/v1/client/device/poll?code=" + encodeURIComponent(code), { noAuth: true });
},
me: function () { return request("/api/v1/client/me"); },
listProfiles: function () { return request("/api/v1/client/profiles"); },
selectProfile: function (id, pin) {
var body = {};
if (pin) body.pin = pin;
return request("/api/v1/client/profiles/" + encodeURIComponent(id) + "/select", { method: "POST", body: body });
},
clearProfile: function () {
return request("/api/v1/client/profiles/clear", { method: "POST", body: {} });
},
home: function () { return request("/api/v1/client/home"); },
search: function (q) {
return request("/api/v1/client/search?q=" + encodeURIComponent(q) + "&take=60");
},
catalog: function (id, skip, take) {
skip = skip || 0; take = take || 60;
return request(
"/api/v1/client/catalog/" + encodeURIComponent(id) +
"?skip=" + skip + "&take=" + take
);
},
movie: function (id) { return request("/api/v1/client/movies/" + encodeURIComponent(id)); },
series: function (id) { return request("/api/v1/client/series/" + encodeURIComponent(id)); },
play: function (mediaFileId) {
return request("/api/v1/client/play", { method: "POST", body: { mediaFileId: mediaFileId } });
},
progress: function (payload) {
return request("/api/v1/client/progress", { method: "POST", body: payload });
},
dismissContinue: function (progressId) {
return request("/api/v1/client/progress/" + encodeURIComponent(progressId) + "/dismiss", {
method: "POST", body: {},
});
},
iptvCategories: function () { return request("/api/v1/client/iptv/categories"); },
iptvChannels: function (categoryId, includeEpg) {
var params = [];
if (categoryId) params.push("categoryId=" + encodeURIComponent(categoryId));
if (includeEpg) params.push("epg=1");
var q = params.length ? "?" + params.join("&") : "";
return request("/api/v1/client/iptv/channels" + q);
},
iptvPlay: function (streamId) {
return request("/api/v1/client/iptv/channels/" + streamId + "/play");
},
iptvEpg: function (streamId) {
return request("/api/v1/client/iptv/channels/" + streamId + "/epg");
},
searchSubtitles: function (mediaFileId, languages) {
languages = languages || "nl,en";
return request(
"/api/v1/client/subtitles/search?mediaFileId=" +
encodeURIComponent(mediaFileId) +
"&languages=" +
encodeURIComponent(languages)
);
},
fetchSubtitle: function (fileId, source) {
return request("/api/v1/client/subtitles/fetch", {
method: "POST",
body: { fileId: fileId, source: source || "org" },
});
},
};
})(window);