Initial attempt at updating for 10.9

This commit is contained in:
Jade Macho 2024-05-12 23:21:17 +02:00
parent 0cce72b24b
commit e8c3154a79
Signed by: 0x0ade
GPG Key ID: E1960710FE4FBEEF
8 changed files with 134 additions and 46 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.htaccess
/priv/
/.vscode/
update.sh

View File

@ -41,33 +41,6 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
}
initEarly() {
// Required for mods to be able to replace some things.
// Might or might not horribly wreck everything.
const defineProperty = this._Object_defineProperty = Object.defineProperty.bind(Object);
const defineProperties = this._Object_defineProperties = Object.defineProperties.bind(Object);
Object.defineProperty = (obj, prop, descriptor) => {
if (descriptor && prop != "prototype") {
descriptor.configurable = true;
}
return defineProperty(obj, prop, descriptor);
};
Object.defineProperties = (obj, props) => {
if (props) {
for (const prop of Object.keys(props)) {
const descriptor = props[prop];
if (descriptor && prop != "prototype") {
descriptor.configurable = true;
}
}
}
return defineProperties(obj, props);
};
// Required for mods to be able to perform certain worker shenanigans.
window.Worker = (class Worker extends window.Worker {
constructor(scriptURL, options) {
@ -92,6 +65,8 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
}
async init(base) {
const self = this;
await super.init("Jadefin", this.modUrl);
// Wait until webpackChunk and Emby exist.
@ -108,10 +83,71 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
return;
}
// Wait until webpackChunk and Emby exist.
// It might be delayed due to the main jellyfin bundle being replaced by an inject script on Android.
/** @type {(id: number | string) => any} */
this.webpackTryLoad = id => {
try {
return this.webpackLoad?.(id);
} catch (e) {
this.log.w(`Failed to load webpack module ${id}`);
this.log.dir(e);
return null;
}
};
/** @type {(id: any) => string} */
this.webpackIdToChunkJS = Object.values(this.webpackLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`+".chunk.js"`) != -1);
/** @type {(id: any) => string} */
this.webpackIdToChunkCSS = Object.values(this.webpackLoad).find(v => typeof(v) == "function" && v.toString().indexOf(`+".css"`) != -1);
const webpackIdToChunkJSKey = Object.keys(this.webpackLoad).find(k => this.webpackLoad?.[k] == this.webpackIdToChunkJS);
if (!this.webpackIdToChunkJS || !webpackIdToChunkJSKey) {
this.log.e("Couldn't obtain webpackIdToChunkJS");
return;
}
this.webpackLoad[webpackIdToChunkJSKey] = function(id) {
const rv = self.webpackIdToChunkJS?.(id);
self.log.v(`Webpack converted chunk ID to JS name: ${id} -> ${rv}`);
return rv;
};
/** @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");
return;
}
this.webpackLoad[webpackLoadChunkLowLevelKey] = function(name, cb, sid, id) {
const _cb = cb;
self.log.v(`Webpack loading chunk ${id} (${sid}) from ${name}`);
/*
cb = (event) => {
self.log.v(`Webpack loaded chunk ${id} (${sid}) from ${name}: ${event.type}`);
return _cb(event);
};
*/
const rv = self.webpackLoadChunkLowLevel?.(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);
// HACKFIX: Load all chunks when Jadefin initializes!
// webpackIdToChunkJS contains all IDs either ahead of === or :
// FIXME: This smells like race condition hell! We might be too late for this kind of hooking.
// FIXME: Ideally, don't. This makes startup take ages.
const webpackChunks = [...this.webpackIdToChunkJS.toString().matchAll(/\d+(?=:|=)/g)].map(v => parseInt(v[0]));
await Promise.all(webpackChunks.map(id => this.webpackLoadChunk?.(id)));
// Wait until everything else is ready.
await JadefinUtils.waitUntil(() => this.webpackModuleFuncs);
// this._webpackUnsafeModuleIDs = this.findUnsafeWebpackModules();
const initing = [
this.initHookHtmlVideoPlayer(),
];
@ -174,21 +210,41 @@ export default JadefinIntegrity("Jadefin", import.meta.url, () => window["Jadefi
* @param {(e: any) => any} cb
*/
findWebpackRawLoad(cb) {
return Object.keys(this.webpackModuleFuncs).map(id => this.webpackLoad?.(id)).filter(e => e && cb(e));
return Object.keys(this.webpackModuleFuncs).map(id => this.webpackTryLoad?.(id)).filter(e => e && cb(e));
}
/**
* @param {(e: any) => any} cb
*/
findWebpackModules(cb) {
return Object.keys(this.webpackModuleFuncs).map(id => this.webpackLoad?.(id)?.default).filter(e => e && cb(e));
return Object.keys(this.webpackModuleFuncs).map(id => this.webpackTryLoad?.(id)?.default).filter(e => e && cb(e));
}
/**
* @param {(e: any) => any} cb
*/
findWebpackFunctions(cb) {
return Object.keys(this.webpackModuleFuncs).map(id => this.webpackLoad?.(id)).filter(e => e && e instanceof Function && cb(e));
return Object.keys(this.webpackModuleFuncs).map(id => this.webpackTryLoad?.(id)).filter(e => e && e instanceof Function && cb(e));
}
/**
* @param {any} [modules]
*/
findUnsafeWebpackModules(modules) {
const unsafe = [];
for (const id of Object.keys(modules || this.webpackModuleFuncs)) {
try {
this.webpackLoad?.(id);
} catch (e) {
this.log.w(`Failed to load webpack module ${id}`);
this.log.w(this.webpackModuleFuncs[id].toString());
this.log.dir(e);
unsafe.push(id);
}
}
return unsafe;
}
/**

View File

@ -89,13 +89,13 @@ export default JadefinIntegrity("JadefinModules", import.meta.url, () => window[
// escape-html
/** @return {(text: string) => string} */
get escapeHtml() {
return this._.escapeHtml ??= this.Jadefin.findWebpackFunctions(e => e.toString().indexOf(`{switch(r.charCodeAt(a)){case 34`) != -1)[0];
return this._.escapeHtml ??= this.Jadefin.findWebpackFunctions(e => e.toString().indexOf(`charCodeAt(a)){case 34`) != -1)[0];
}
// toast
/** @return {(text: string) => void} */
get toast() {
return this._.toast ??= this.Jadefin.findWebpackRawLoad(e => (e.Z?.toString().indexOf(`toast"),t.textContent=`) || -1) != -1)[0]?.Z;
return this._.toast ??= this.Jadefin.findWebpackRawLoad(e => (e.A?.toString().indexOf(`toast"),t.textContent=`) || -1) != -1)[0]?.A;
}
// plugins/syncPlay/core/index
@ -108,7 +108,7 @@ export default JadefinIntegrity("JadefinModules", import.meta.url, () => window[
}}
*/
get syncPlay() {
return this._.syncPlay ??= this.Jadefin.findWebpackRawLoad(e => e.Z?.Manager?.isSyncPlayEnabled)[0]?.Z;
return this._.syncPlay ??= this.Jadefin.findWebpackModules(e => e.Manager?.isSyncPlayEnabled)[0];
}
// inputManager
@ -131,7 +131,7 @@ export default JadefinIntegrity("JadefinModules", import.meta.url, () => window[
@return {any}
*/
get playbackManager() {
return this._.playbackManager ??= this.Jadefin.findWebpackRawLoad(e => e.O?.canHandleOffsetOnCurrentSubtitle)[0]?.O;
return this._.playbackManager ??= this.Jadefin.findWebpackRawLoad(e => e.f?.canHandleOffsetOnCurrentSubtitle)[0]?.f;
}
// plugins/htmlVideoPlayer/plugin.js
@ -156,7 +156,7 @@ export default JadefinIntegrity("JadefinModules", import.meta.url, () => window[
}
*/
get taskButton() {
return this._.taskButton ??= this.Jadefin.findWebpackRawLoad(e => (e.Z?.toString().indexOf(`sendMessage("ScheduledTasksInfoStart","1000,1000")`) || -1) != -1)[0]?.Z;
return this._.taskButton ??= this.Jadefin.findWebpackRawLoad(e => (e.A?.toString().indexOf(`sendMessage("ScheduledTasksInfoStart","1000,1000")`) || -1) != -1)[0]?.A;
}
// browser
@ -202,7 +202,7 @@ export default JadefinIntegrity("JadefinModules", import.meta.url, () => window[
}}
*/
get browser() {
return this._.browser ??= this.Jadefin.findWebpackRawLoad(e => e.Z && "supportsCssAnimation" in e.Z && "version" in e.Z)[0]?.Z;
return this._.browser ??= this.Jadefin.findWebpackRawLoad(e => typeof(e.A) == "object" && "supportsCssAnimation" in e.A && "version" in e.A)[0]?.A;
}
// datetime
@ -220,7 +220,7 @@ export default JadefinIntegrity("JadefinModules", import.meta.url, () => window[
}}
*/
get datetime() {
return this._.datetime ??= this.Jadefin.findWebpackRawLoad(e => e.ZP?.getDisplayRunningTime)[0]?.ZP;
return this._.datetime ??= this.Jadefin.findWebpackRawLoad(e => e.Ay?.getDisplayRunningTime)[0]?.Ay;
}
// events

View File

@ -29,11 +29,11 @@ export default JadefinIntegrity("JadefinUtils", import.meta.url, () => window["J
}
get routePath() {
return JadefinModules.Emby.Page.currentRouteInfo.route.path;
return JadefinModules.Emby.Page.currentRouteInfo.path;
}
get routePathIsVideo() {
return this.routePath == "playback/video/index.html";
return this.routePath == "/video";
}
get currentPlayer() {

31
init.js
View File

@ -2,6 +2,37 @@
let jadefinInit = /** @type {HTMLScriptElement} */ (document.currentScript);
function jadefinInitExtremelyEarly() {
// Required for mods to be able to replace some things.
// Might or might not horribly wreck everything.
const defineProperty = this._Object_defineProperty = Object.defineProperty.bind(Object);
const defineProperties = this._Object_defineProperties = Object.defineProperties.bind(Object);
Object.defineProperty = (obj, prop, descriptor) => {
if (descriptor && prop != "prototype") {
descriptor.configurable = true;
}
return defineProperty(obj, prop, descriptor);
};
Object.defineProperties = (obj, props) => {
if (props) {
for (const prop of Object.keys(props)) {
const descriptor = props[prop];
if (descriptor && prop != "prototype") {
descriptor.configurable = true;
}
}
}
return defineProperties(obj, props);
};
}
jadefinInitExtremelyEarly();
(() => {
let loadedJellyfin = false;
let initedJadefin = false;

View File

@ -212,7 +212,7 @@ export default JadefinIntegrity("InputEater", import.meta.url, () => new (class
}
initHookMediaSessionHandlers() {
const owner = Jadefin.findWebpackRawLoad(e => e?.O?.nextTrack && e?.O?.fastForward)[0].O;
const owner = Jadefin.findWebpackRawLoad(e => e?.f?.nextTrack && e?.f?.fastForward)[0].f;
if (!owner || !("mediaSession" in navigator)) {
this.log.e("Couldn't hook media session handlers");

View File

@ -35,7 +35,7 @@ div#videoOsdPage .osdTranscript .line {
scroll-snap-align: end;
--bg: #000000;
--bg-opacity: 0.25;
--bar: rgb(var(--accent, 0x00, 0xad, 0xee));
--bar: var(--transcript-accent, #00adee);
--bar-opacity: 0;
}
body:not([input-eaten=true]) div#videoOsdPage .osdTranscript .line {

View File

@ -25,10 +25,10 @@ export default JadefinIntegrity("PatchAndroidHLSJS", import.meta.url, () => new
async init(name, url) {
await super.init(name, url);
const htmlMediaHelper = this.htmlMediaPlayer = Jadefin.findWebpackRawLoad(e => (e.rR?.toString().indexOf("x-mpegURL") || -1) != -1)[0];
this._enableHlsJsPlayer = htmlMediaHelper.rR.bind(htmlMediaHelper);
const htmlMediaHelper = this.htmlMediaPlayer = Jadefin.findWebpackRawLoad(e => (e.JQ?.toString().indexOf("x-mpegURL") || -1) != -1)[0];
this._enableHlsJsPlayer = htmlMediaHelper.JQ.bind(htmlMediaHelper);
Object.defineProperty(htmlMediaHelper, "rR", { get: () => this.enableHlsJsPlayer });
Object.defineProperty(htmlMediaHelper, "JQ", { get: () => this.enableHlsJsPlayer });
const ExtrasMenu = /** @type {import("../ExtrasMenu.js").default} */ (Jadefin.getMod("ExtrasMenu"));