Redo webpack blacklist, patch runtime.bundle.js, fix for 10.10
This commit is contained in:
parent
034cf94c31
commit
dd71df83a1
242
Jadefin.js
242
Jadefin.js
@ -24,6 +24,33 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
/** @type {any} */
|
||||
_webpackModuleFuncs;
|
||||
|
||||
_webpackBlacklistClient = JSON.parse(localStorage.getItem("jadefin-blacklist") || "[]");
|
||||
/** @type {typeof this._webpackBlacklistClient} */
|
||||
webpackBlacklistClient = new Proxy(this._webpackBlacklistClient, {
|
||||
deleteProperty: (target, property) => {
|
||||
delete target[property];
|
||||
|
||||
localStorage.setItem("jadefin-blacklist", JSON.stringify(this._webpackBlacklistClient));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
set: (target, property, value, receiver) => {
|
||||
target[property] = value;
|
||||
|
||||
localStorage.setItem("jadefin-blacklist", JSON.stringify(this._webpackBlacklistClient));
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// To update, Jadefin.webpackBlacklistClient.push(0); and update this after a few reloads: JSON.stringify(Jadefin.webpackBlacklistClient)
|
||||
// Once done: Jadefin.webpackBlacklistClient.splice(0, Jadefin.webpackBlacklistClient.length)
|
||||
webpackBlacklistDefault = JSON.parse(`[0,"activity","node_modules.@mui.x-date-pickers","15088","node_modules.@mui.utils","node_modules.@mui.material","node_modules.react-transition-group","node_modules.date-fns.esm","23416","livetvtuner","syncPlay-core-players-GenericPlayer","47472","node_modules.@mui.system","55802"]`);
|
||||
|
||||
/** @type {{[id: string]: any}} */
|
||||
_webpackCache = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -42,6 +69,12 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
}
|
||||
|
||||
initEarly() {
|
||||
if (this.webpackBlacklistClient.length) {
|
||||
this.webpackBlacklist = this.webpackBlacklistClient;
|
||||
} else {
|
||||
this.webpackBlacklist = this.webpackBlacklistDefault;
|
||||
}
|
||||
|
||||
// Required for mods to be able to perform certain worker shenanigans.
|
||||
window.Worker = (class Worker extends window.Worker {
|
||||
constructor(scriptURL, options) {
|
||||
@ -63,6 +96,41 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// Required with newer versions of Jellyfin and Jadefin: Fetch and patch runtime.bundle.js ourselves.
|
||||
// Thankfully this can happen async, Jellyfin will load whenever this is done.
|
||||
const runtimeBundleScript = Array.from(document.getElementsByTagName("script")).find(s => s.src.indexOf("runtime.jadefin.bundle.js") != -1);
|
||||
if (!runtimeBundleScript) {
|
||||
this._runtimeBundlePatched = false;
|
||||
this.log.e("Using vanilla runtime.bundle.js - some mods might not work properly!");
|
||||
} else {
|
||||
this._runtimeBundlePatched = true;
|
||||
const url = runtimeBundleScript.src.replace("runtime.jadefin.bundle.js", "runtime.bundle.js");
|
||||
this.log.i(`Fetching runtime.bundle.js from ${url}`);
|
||||
fetch(url).then(r => r.text()).then(async src => {
|
||||
this.log.i("Patching runtime.bundle.js");
|
||||
this._runtimeBundleOrig = src;
|
||||
|
||||
try {
|
||||
// __webpack_require__ in Compilation.js as reference
|
||||
// var d=f[e]={id:e,loaded:!1,exports:{}}; in minified version
|
||||
const matchRequire = src.match(/var [^=]+=([^\[]+)\[[^\[]+\]=\{[^}]*(?:id:[^;]+|loaded:[^;]+|exports:{}[^;]+){3};/);
|
||||
if (!matchRequire || matchRequire.index === undefined) {
|
||||
throw new Error("Couldn't find __webpack_require__");
|
||||
}
|
||||
|
||||
src = src.substring(0, matchRequire.index) + `window["_JadefinWebpackCache"]=${matchRequire[1]};` + src.substring(matchRequire.index);
|
||||
|
||||
this.log.i("Running patched runtime.bundle.js");
|
||||
eval(src);
|
||||
} catch (e) {
|
||||
this._runtimeBundlePatched = false;
|
||||
this.log.e("Patched runtime.bundle.js errored, running vanilla runtime.bundle.js as fallback");
|
||||
this.log.dir(e);
|
||||
eval(this._runtimeBundleOrig);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async init(base) {
|
||||
@ -72,79 +140,100 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
|
||||
// Wait until webpackChunk and Emby exist.
|
||||
// It might be delayed due to the main jellyfin bundle being replaced by an inject script on Android.
|
||||
await JadefinUtils.waitUntil(() => window["webpackChunk"] && window["Emby"]);
|
||||
await JadefinUtils.waitUntil(() => window["webpackChunk"] && window["Emby"] && (!this._runtimeBundlePatched || window["_JadefinWebpackCache"]));
|
||||
|
||||
this._webpackRawCache = window["_JadefinWebpackCache"];
|
||||
if (!this._webpackRawCache) {
|
||||
this.log.w("Couldn't obtain _webpackRawCache");
|
||||
}
|
||||
|
||||
window["webpackChunk"].push([["custom_init"], {}, a => {
|
||||
/** @type {((id: number | string) => any & {[key: string]: any})} */
|
||||
this.webpackLoad = a;
|
||||
this.webpackRawLoad = a;
|
||||
}]);
|
||||
|
||||
if (!this.webpackLoad) {
|
||||
this.log.e("Couldn't obtain webpackLoad");
|
||||
if (!this.webpackRawLoad) {
|
||||
this.log.e("Couldn't obtain webpackRawLoad");
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {(id: number | string) => any} */
|
||||
this.webpackTryLoad = id => {
|
||||
this.webpackLoad = id => {
|
||||
const sid = `${id}`;
|
||||
|
||||
if (!document.querySelector(".mainDrawer")) {
|
||||
const chunk = window["webpackChunk"].find(c => c[1][sid]);
|
||||
const chunkNames = chunk ? chunk[0].map(this.webpackChunkIdToJS) : null;
|
||||
if (chunkNames && (chunkNames.find(c =>
|
||||
c.startsWith("session-login.") ||
|
||||
c.startsWith("syncPlay-ui-") ||
|
||||
c.startsWith("35463.") ||
|
||||
c.startsWith("1998.") ||
|
||||
c.startsWith("73233.")
|
||||
))) {
|
||||
const cached = this._webpackCache[sid];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const name = this.webpackIdToName(id);
|
||||
|
||||
if (this.webpackBlacklist !== this.webpackBlacklistClient) {
|
||||
const cachedRaw = this._webpackRawCache?.[sid];
|
||||
if (cachedRaw && cachedRaw.loaded) {
|
||||
return this._webpackCache[sid] = cachedRaw.exports;
|
||||
}
|
||||
|
||||
// Some modules are known to break things when loaded too early.
|
||||
if (name.startsWith("session-login") ||
|
||||
name.startsWith("syncPlay-ui-") ||
|
||||
name.startsWith("user-display") ||
|
||||
name.startsWith("activity") ||
|
||||
name.startsWith("node_modules.@juggle") ||
|
||||
name.startsWith("node_modules.@mui") ||
|
||||
false
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Some modules shall only be loaded via their intended codepaths.
|
||||
if (this.webpackBlacklist.indexOf(name) != -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return this.webpackLoad?.(id);
|
||||
return this._webpackCache[sid] = this.webpackRawLoad?.(id);
|
||||
} catch (e) {
|
||||
this.log.e(`Failed to load webpack module ${id}`);
|
||||
this.log.e(`Failed to load webpack module ${id} (${name})`);
|
||||
this.log.dir(e);
|
||||
this.webpackBlacklist.push(name);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {(id: any) => string} */
|
||||
this.webpackChunkIdToJS = Object.values(this.webpackLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`+".chunk.js"`) != -1);
|
||||
this.webpackChunkIdToJS = Object.values(this.webpackRawLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`+".chunk.js"`) != -1);
|
||||
/** @type {(id: any) => string} */
|
||||
this.webpackIdToCSS = Object.values(this.webpackLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`+".css"`) != -1);
|
||||
this.webpackIdToCSS = Object.values(this.webpackRawLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`+".css"`) != -1);
|
||||
if (!this.webpackChunkIdToJS || !this.webpackIdToCSS) {
|
||||
this.log.e("Couldn't obtain webpackChunkIdToJS or webpackIdToCSS");
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {(name: any, cb: any, sid: any, id: any) => any} */
|
||||
this.webpackLoadChunkLowLevel = Object.values(this.webpackLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`document.head.appendChild`) != -1);
|
||||
const webpackLoadChunkLowLevelKey = Object.keys(this.webpackLoad).find(k => this.webpackLoad?.[k] == this.webpackLoadChunkLowLevel);
|
||||
if (!this.webpackLoadChunkLowLevel || !webpackLoadChunkLowLevelKey) {
|
||||
this.log.e("Couldn't obtain webpackLoadChunkLowLevel");
|
||||
this.webpackPreloadChunkLowLevel = Object.values(this.webpackRawLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`document.head.appendChild`) != -1);
|
||||
const webpackPreloadChunkLowLevelKey = Object.keys(this.webpackRawLoad).find(k => this.webpackRawLoad?.[k] == this.webpackPreloadChunkLowLevel);
|
||||
if (!this.webpackPreloadChunkLowLevel || !webpackPreloadChunkLowLevelKey) {
|
||||
this.log.e("Couldn't obtain webpackPreloadChunkLowLevel");
|
||||
return;
|
||||
}
|
||||
|
||||
this.webpackLoad[webpackLoadChunkLowLevelKey] = function(name, cb, sid, id) {
|
||||
this.webpackRawLoad[webpackPreloadChunkLowLevelKey] = function(name, cb, sid, id) {
|
||||
const _cb = cb;
|
||||
// self.log.v(`Webpack loading chunk ${id} (${sid}) from ${name}`);
|
||||
// self.log.v(`Webpack preloading chunk ${id} (${sid}) from ${name}`);
|
||||
cb = (event) => {
|
||||
self.log.v(`Webpack loaded chunk ${id} (${sid}) from ${name}: ${event.type}`);
|
||||
self.log.v(`Webpack preloaded chunk ${id} (${sid}) from ${name}: ${event.type}`);
|
||||
return _cb(event);
|
||||
};
|
||||
const rv = self.webpackLoadChunkLowLevel?.(name, cb, sid, id);
|
||||
const rv = self.webpackPreloadChunkLowLevel?.(name, cb, sid, id);
|
||||
return rv;
|
||||
};
|
||||
|
||||
/** @type {(id: any) => Promise<any>} */
|
||||
this.webpackLoadChunk = Object.values(this.webpackLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`Object.keys(`) != -1 && v.toString().indexOf(`}),[])`) != -1);
|
||||
this.webpackPreloadChunk = Object.values(this.webpackRawLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`Object.keys(`) != -1 && v.toString().indexOf(`}),[])`) != -1);
|
||||
|
||||
// HACK: Load all chunks we could need when Jadefin initializes!
|
||||
// webpackChunkIdToJS contains all IDs either ahead of === or :
|
||||
// FIXME: Ideally, don't. This makes startup take ages and is unreliable!
|
||||
this.webpackChunkIds = [...this.webpackChunkIdToJS.toString().matchAll(/\d+(?=:|=)/g)].map(v => parseInt(v[0]));
|
||||
/** @type {any} */
|
||||
this.webpackChunkJSs = {};
|
||||
@ -152,29 +241,6 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
this.webpackChunkJSs[this.webpackChunkIdToJS(id)] = id;
|
||||
}
|
||||
|
||||
// Grabbed by running this on various screens:
|
||||
// (()=>{let a = webpackChunk.flatMap(c => c[0]); return JSON.stringify(a.filter((v,i)=>a.indexOf(v)==i))})()
|
||||
let webpackChunkIdsWanted = [
|
||||
// Home
|
||||
JSON.parse("[45642,1451,23247,60815,62155,16304,1270,81771,9886,55931,12036,59928,33067,44965,22940,56401,7495,86897,17060,67224,67622,94048,36933,82363,36546,85500,7011,80183,60232,82798,14577,78283,37658,94160,7184,83518,15277,71779,9911,82420,49398,32292,37821,74776,99994,87074,67942,60039,27017,65149,27182,4636,87530,4801,82255,99435,77077,71944,59874,21816,79754,89409,69285,43091,64380,90186,40810,87903,70118,64633,10905,68672,19842,71318,70555,41542,86040,4836,49087,64706,13151,65849,80835,14510,96307,30357,6270,19907,55125,83354,49755,60138,927,10672,2,56577,29593,1680,39573,40394,59258,39232,40465,18395,48979,68413,78938,55802,39435,28567,79617,1998,15605,21857,44184,22424,91737,40367,96084,73233,52011,5617,56422,45568,27962,32762,8372,28349,29808,58782,24468,18119,50777,84158,49275,35308,38965,11136,14044,22943,27018,30010,39548,63365,94115,70594,98574,59015,58122,66935,28312,84430,88788,86355,57949,18084,35463]"),
|
||||
].flatMap(l => l);
|
||||
webpackChunkIdsWanted = webpackChunkIdsWanted.filter((id, i) => webpackChunkIdsWanted.indexOf(id) == i);
|
||||
|
||||
// Grabbed through searching for module loads which still errored out.
|
||||
webpackChunkIdsWanted.push(
|
||||
...Object.keys(this.webpackChunkJSs).filter(js => {
|
||||
return false ||
|
||||
js.startsWith("user-display.") ||
|
||||
js.startsWith("user-display-") ||
|
||||
js.startsWith("activity.") ||
|
||||
js.startsWith("node_modules.@juggle.") ||
|
||||
js.startsWith("node_modules.@mui.") ||
|
||||
false;
|
||||
}).map(js => this.webpackChunkJSs[js])
|
||||
);
|
||||
|
||||
await Promise.all(webpackChunkIdsWanted.map(id => this.webpackLoadChunk?.(id)));
|
||||
|
||||
// Wait until everything else is ready.
|
||||
await JadefinUtils.waitUntil(() => this.webpackModuleFuncs);
|
||||
|
||||
@ -235,28 +301,28 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
}
|
||||
|
||||
get webpackModuleFuncs() {
|
||||
return this._webpackModuleFuncs ??= this.webpackLoad ? Object.values(this.webpackLoad).find(v => typeof(v) == "object" && Object.keys(v).length > 10) : null;
|
||||
return this._webpackModuleFuncs ??= this.webpackRawLoad ? Object.values(this.webpackRawLoad).find(v => typeof(v) == "object" && Object.keys(v).length > 10) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(e: any) => any} cb
|
||||
*/
|
||||
findWebpackRawLoad(cb) {
|
||||
return JadefinUtils.filterMap(Object.keys(this.webpackModuleFuncs).map(id => this.webpackTryLoad?.(id)), e => e && cb(e));
|
||||
return JadefinUtils.filterMap(Object.keys(this.webpackModuleFuncs).map(id => this.webpackLoad?.(id)), e => e && cb(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(e: any) => any} cb
|
||||
*/
|
||||
findWebpackModules(cb) {
|
||||
return JadefinUtils.filterMap(Object.keys(this.webpackModuleFuncs).map(id => this.webpackTryLoad?.(id)?.default), e => e && cb(e));
|
||||
return JadefinUtils.filterMap(Object.keys(this.webpackModuleFuncs).map(id => this.webpackLoad?.(id)?.default), e => e && cb(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(e: any) => any} cb
|
||||
*/
|
||||
findWebpackFunctions(cb) {
|
||||
return JadefinUtils.filterMap(Object.keys(this.webpackModuleFuncs).map(id => this.webpackTryLoad?.(id)), e => e && e instanceof Function && cb(e));
|
||||
return JadefinUtils.filterMap(Object.keys(this.webpackModuleFuncs).map(id => this.webpackLoad?.(id)), e => e && e instanceof Function && cb(e));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,10 +333,10 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
|
||||
for (const id of Object.keys(modules || this.webpackModuleFuncs)) {
|
||||
try {
|
||||
this.webpackLoad?.(id);
|
||||
this.webpackRawLoad?.(id);
|
||||
} catch (e) {
|
||||
this.log.w(`Failed to load webpack module ${id}`);
|
||||
this.log.w(this.webpackModuleFuncs[id].toString());
|
||||
this.log.dir(this.webpackModuleFuncs[id]);
|
||||
this.log.dir(e);
|
||||
unsafe.push(id);
|
||||
}
|
||||
@ -279,6 +345,64 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
|
||||
return unsafe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} id
|
||||
* @return {string[]}
|
||||
*/
|
||||
webpackIdToJSs(id) {
|
||||
if (!this.webpackChunkIdToJS) {
|
||||
return [ `${id}.unknown.chunk.js` ];
|
||||
}
|
||||
|
||||
const sid = `${id}`;
|
||||
|
||||
const meta = window["webpackChunk"].find(c => c[1][sid]);
|
||||
if (!meta) {
|
||||
return [ this.webpackChunkIdToJS(parseInt(sid)) ];
|
||||
}
|
||||
|
||||
const jss = [];
|
||||
|
||||
for (const pid of meta[0]) {
|
||||
jss.push(this.webpackChunkIdToJS(pid));
|
||||
}
|
||||
|
||||
for (const cid of Object.keys(meta[1])) {
|
||||
jss.push(this.webpackChunkIdToJS(cid));
|
||||
}
|
||||
|
||||
return jss;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} id
|
||||
* @return {string}
|
||||
*/
|
||||
webpackIdToJS(id) {
|
||||
const jss = this.webpackIdToJSs(id);
|
||||
|
||||
return jss.find(c => !/^\d/.test(c)) || jss.find(c => c.startsWith(`${id}.`)) || `${id}.unknown.chunk.js`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} id
|
||||
* @return {string}
|
||||
*/
|
||||
webpackIdToName(id) {
|
||||
const name = this.webpackIdToJS(id);
|
||||
|
||||
if (name.endsWith(".bundle.js")) {
|
||||
return name.substring(0, name.length - ".bundle.js".length);
|
||||
}
|
||||
|
||||
if (name.endsWith(".chunk.js")) {
|
||||
const split = name.lastIndexOf(".", name.length - ".chunk.js".length - 1);
|
||||
return name.substring(0, split);
|
||||
}
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
|
@ -65,7 +65,11 @@ export default JadefinIntegrity("JadefinLog", import.meta.url, () => class Jadef
|
||||
}
|
||||
|
||||
if (args.length == 1) {
|
||||
if (args[0] instanceof Error) {
|
||||
console.error(args[0]);
|
||||
} else {
|
||||
console.dir(args[0]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export default JadefinIntegrity("JadefinUtils", import.meta.url, () => window["J
|
||||
}
|
||||
|
||||
get routePath() {
|
||||
return JadefinModules.Emby.Page.currentRouteInfo.path;
|
||||
return JadefinModules.Emby.Page.currentRouteInfo?.path;
|
||||
}
|
||||
|
||||
get routePathIsVideo() {
|
||||
|
Loading…
Reference in New Issue
Block a user