Skip to content

Instantly share code, notes, and snippets.

@n0bodysec
Last active October 3, 2023 18:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save n0bodysec/2896438d8578f13c4a9e30373f7200c1 to your computer and use it in GitHub Desktop.
Save n0bodysec/2896438d8578f13c4a9e30373f7200c1 to your computer and use it in GitHub Desktop.
Stremio Web client patches
/**
*
* @name Stremio JavaScript Patcher
* @author n0bodysec
* @description A simple JS that is executed on document load. You can apply custom client patches with it.
* @example https://gist.github.com/n0bodysec/2896438d8578f13c4a9e30373f7200c1
* This script can be loaded using nginx sub_filter
* sub_filter '</body>' '<script src="https://your.stremio.url/patches.js"></script></body>';
* sub_filter_once on;
*
*/
// see https://github.com/n0bodysec/docker-images/issues/5
// prototypes
Storage.prototype.setObject = function (key, value) { this.setItem(key, JSON.stringify(value)); };
Storage.prototype.getObject = function (key)
{
const value = this.getItem(key);
return value && JSON.parse(value);
};
const oConsoleLog = console.log;
console.log = function ()
{
Array.prototype.unshift.call(arguments, '[STREMIO PATCHER]');
oConsoleLog.apply(this, arguments);
};
const oConsoleError = console.error;
console.error = function ()
{
Array.prototype.unshift.call(arguments, '[STREMIO PATCHER]');
oConsoleError.apply(this, arguments);
};
// global vars
// references: https://github.com/Stremio/stremio-web/blob/HEAD/src/types/models/Ctx.d.ts
const defaultProfile = {
auth: null,
addons: [],
settings: {
interfaceLanguage: 'eng',
streamingServerUrl: 'http://127.0.0.1:11470/',
playerType: null,
bingeWatching: true,
playInBackground: true,
hardwareDecoding: true,
frameRateMatchingStrategy: 'FrameRateOnly',
nextVideoNotificationDuration: 35000,
audioPassthrough: false,
audioLanguage: 'eng',
secondaryAudioLanguage: null,
subtitlesLanguage: 'eng',
secondarySubtitlesLanguage: null,
subtitlesSize: 100,
subtitlesFont: 'Roboto',
subtitlesBold: false,
subtitlesOffset: 5,
subtitlesTextColor: '#FFFFFFFF',
subtitlesBackgroundColor: '#00000000',
subtitlesOutlineColor: '#000000',
seekTimeDuration: 10000,
streamingServerWarningDismissed: null,
},
};
let globalProfile = defaultProfile;
const OFFICIAL_ADDONS = [
'https://v3-cinemeta.strem.io/manifest.json',
'https://v3-channels.strem.io/manifest.json',
'https://watchhub.strem.io/manifest.json',
'https://caching.stremio.net/publicdomainmovies.now.sh/manifest.json',
// TODO: fix below
// 'https://opensubtitles.strem.io/stremio/v1',
// 'http://127.0.0.1:11470/local-addon/manifest.json',
];
const CUSTOM_ADDONS = [
'https://torrentio.strem.fun/manifest.json',
];
const isLegacy = () => localStorage.getItem('streamingServerUrl') != null;
async function fetchResponse(url)
{
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
return { res, json: await res.json() };
}
function setStreamingServer(url)
{
if (isLegacy()) localStorage.setItem('streamingServerUrl', url);
else
{
globalProfile.settings.streamingServerUrl = url;
localStorage.setObject('profile', globalProfile);
}
}
async function installAddon(transportUrl, options)
{
options = options ?? { flags: { official: false, protected: false } };
if (globalProfile.addons.findIndex((addon) => addon.transportUrl === transportUrl) !== -1) return null;
const manifest = (await fetchResponse(transportUrl)).json;
const obj = { manifest, transportUrl, flags: options.flags };
globalProfile.addons.push(obj);
console.log(`Addon '${manifest.name}' has been installed!`);
return obj;
}
window.addEventListener('DOMContentLoaded', async (event) =>
{
if (localStorage === undefined) return console.error('localStorage is not defined. Script execution has been paused.');
if (localStorage.getItem('patchOnce')) return console.log('Skipping the patches because they are already applied.');
const legacy = isLegacy();
console.log(`Detected ${legacy ? 'Legacy' : 'Web'} UI`);
// localStorage safeguard
const lsProfile = localStorage.getObject('profile');
if ((legacy && !localStorage.getObject('addons')) || (!legacy && !lsProfile))
{
console.log('Installing default addons...');
for (const addon of OFFICIAL_ADDONS) await installAddon(addon, { flags: { official: true, protected: false } });
}
else
{
globalProfile = lsProfile;
console.log('Profile loaded!');
}
// install addons
console.log('Installing custom addons...');
for (const addon of CUSTOM_ADDONS) await installAddon(addon);
// save configs
console.log('Saving profile...');
localStorage.setItem('patchOnce', true);
if (legacy) localStorage.setObject('addons', globalProfile.addons);
else localStorage.setObject('profile', globalProfile);
// set custom streaming server url (https://stremio.domain.tld -> https://stremio-server.domain.tld)
const serverUrl = location.protocol + '//' + location.host.replace('stremio', 'stremio-server');
console.log(`Setting streaming server url to ${serverUrl}`);
setStreamingServer(serverUrl);
// reload after apply all patches
location.reload();
return 1;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment