mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Update KefinTweaks-plugin to properly support switching to Experimental branch based on new changes
This commit is contained in:
1 parent
47f4b4a7bd
commit
0c5d5f603a
1 file changed
+279
-20
+279
-20
@@ -186,7 +186,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Save configuration to JavaScript Injector
|
||||
// Save configuration to JavaScript Injector (Config + KefinTweaks-injector preload)
|
||||
async function saveConfigToJavaScriptInjector(config) {
|
||||
try {
|
||||
const pluginId = await findJavaScriptInjectorPlugin();
|
||||
@@ -202,19 +202,57 @@
|
||||
|
||||
window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
|
||||
const existingScriptIndex = injectorConfig.CustomJavaScripts.findIndex(
|
||||
script => script.Name === 'KefinTweaks-Config'
|
||||
);
|
||||
let injectorScriptContent = null;
|
||||
try {
|
||||
const rootRaw = config.kefinTweaksRoot || '';
|
||||
const resolvedRaw = config.kefinTweaksRootResolved || rootRaw;
|
||||
const root = resolvedRaw.endsWith('/') ? resolvedRaw : resolvedRaw + '/';
|
||||
if (root && root !== '/') {
|
||||
await loadKefinTweaksLoaderFromRoot(root);
|
||||
const Loader = window.KefinTweaksLoader;
|
||||
if (Loader) {
|
||||
Loader.ensureKefinTweaksApi(Loader.SCRIPT_DEFINITIONS);
|
||||
let major = null;
|
||||
try {
|
||||
major = await window.KefinTweaks.getJellyfinMajorVersion();
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Could not resolve Jellyfin major for injector bake:', e);
|
||||
}
|
||||
const planRoot = Loader.getResolvedKefinRoot
|
||||
? Loader.getResolvedKefinRoot(config)
|
||||
: root;
|
||||
const plan = Loader.buildLoadPlan(config, major, {
|
||||
root: planRoot,
|
||||
configOnly: config.enabled === false
|
||||
});
|
||||
injectorScriptContent = Loader.buildInjectorScript(plan);
|
||||
console.log('[KefinTweaks Installer] Built KefinTweaks-injector with', plan.assets.length, 'assets');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Failed to build KefinTweaks-injector:', e);
|
||||
}
|
||||
|
||||
if (existingScriptIndex !== -1) {
|
||||
injectorConfig.CustomJavaScripts[existingScriptIndex].Script = scriptContent;
|
||||
if (window.KefinTweaksLoader) {
|
||||
injectorConfig.CustomJavaScripts = window.KefinTweaksLoader.upsertInjectorEntries(
|
||||
injectorConfig.CustomJavaScripts,
|
||||
{ configScriptContent: scriptContent, injectorScriptContent }
|
||||
);
|
||||
} else {
|
||||
injectorConfig.CustomJavaScripts.push({
|
||||
Name: 'KefinTweaks-Config',
|
||||
Script: scriptContent,
|
||||
Enabled: true,
|
||||
RequiresAuthentication: false
|
||||
});
|
||||
const existingScriptIndex = injectorConfig.CustomJavaScripts.findIndex(
|
||||
script => script.Name === 'KefinTweaks-Config'
|
||||
);
|
||||
if (existingScriptIndex !== -1) {
|
||||
injectorConfig.CustomJavaScripts[existingScriptIndex].Script = scriptContent;
|
||||
injectorConfig.CustomJavaScripts[existingScriptIndex].Enabled = true;
|
||||
} else {
|
||||
injectorConfig.CustomJavaScripts.push({
|
||||
Name: 'KefinTweaks-Config',
|
||||
Script: scriptContent,
|
||||
Enabled: true,
|
||||
RequiresAuthentication: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const server = ApiClient._serverAddress;
|
||||
@@ -239,6 +277,157 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
}
|
||||
}
|
||||
|
||||
function loadKefinTweaksLoaderFromRoot(root) {
|
||||
const normalized = root.endsWith('/') ? root : root + '/';
|
||||
const url = `${normalized}scripts/kefinTweaks-loader.js`;
|
||||
if (window.KefinTweaksLoader) return Promise.resolve(window.KefinTweaksLoader);
|
||||
return new Promise((resolve, reject) => {
|
||||
const existing = document.querySelector(`script[src="${url}"]`);
|
||||
if (existing) {
|
||||
const start = Date.now();
|
||||
const wait = () => {
|
||||
if (window.KefinTweaksLoader) return resolve(window.KefinTweaksLoader);
|
||||
if (Date.now() - start > 15000) return reject(new Error('Timed out waiting for KefinTweaksLoader'));
|
||||
setTimeout(wait, 50);
|
||||
};
|
||||
return wait();
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.async = false;
|
||||
script.onload = () => resolve(window.KefinTweaksLoader);
|
||||
script.onerror = () => reject(new Error('Failed to load ' + url));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
function isLoggedInForInjectorWrite() {
|
||||
try {
|
||||
return !!(window.ApiClient
|
||||
&& window.ApiClient._loggedIn
|
||||
&& typeof window.ApiClient.accessToken === 'function'
|
||||
&& window.ApiClient.accessToken()
|
||||
&& window.ApiClient._serverAddress);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForLoginForInjectorWrite(maxWaitMs = 10000, checkInterval = 250) {
|
||||
if (isLoggedInForInjectorWrite()) return true;
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < maxWaitMs) {
|
||||
await new Promise((r) => setTimeout(r, checkInterval));
|
||||
if (isLoggedInForInjectorWrite()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create/update KefinTweaks-injector (and Config) in JS Injector when missing.
|
||||
* @returns {Promise<{ ok: boolean, plan?: object, reason?: string }>}
|
||||
*/
|
||||
async function ensureKefinTweaksInjectorEntry(config) {
|
||||
const rootRaw = config?.kefinTweaksRoot || '';
|
||||
if (!rootRaw) {
|
||||
return { ok: false, reason: 'kefinTweaksRoot not configured' };
|
||||
}
|
||||
|
||||
if (!isLoggedInForInjectorWrite()) {
|
||||
return { ok: false, reason: 'not logged in' };
|
||||
}
|
||||
|
||||
try {
|
||||
const normalize = (r) => {
|
||||
if (!r || typeof r !== 'string') return '';
|
||||
return r.endsWith('/') ? r : r + '/';
|
||||
};
|
||||
|
||||
const expected = normalize(await resolveRootVersion(rootRaw));
|
||||
const current = normalize(config.kefinTweaksRootResolved || '');
|
||||
if (!current || current !== expected) {
|
||||
config.kefinTweaksRootResolved = expected;
|
||||
console.log('[KefinTweaks Installer] Updated kefinTweaksRootResolved:', current || '(none)', '->', expected);
|
||||
}
|
||||
|
||||
const loadRoot = normalize(config.kefinTweaksRootResolved || rootRaw);
|
||||
await loadKefinTweaksLoaderFromRoot(loadRoot);
|
||||
const Loader = window.KefinTweaksLoader;
|
||||
if (!Loader) {
|
||||
return { ok: false, reason: 'KefinTweaksLoader unavailable' };
|
||||
}
|
||||
|
||||
Loader.ensureKefinTweaksApi(Loader.SCRIPT_DEFINITIONS);
|
||||
let major = null;
|
||||
try {
|
||||
major = await window.KefinTweaks.getJellyfinMajorVersion();
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Could not resolve Jellyfin major for auto-create:', e);
|
||||
}
|
||||
|
||||
const resolvedRoot = Loader.getResolvedKefinRoot(config);
|
||||
const plan = Loader.buildLoadPlan(config, major, {
|
||||
root: resolvedRoot,
|
||||
configOnly: config.enabled === false
|
||||
});
|
||||
const injectorScriptContent = Loader.buildInjectorScript(plan);
|
||||
const configScriptContent = `// KefinTweaks Configuration
|
||||
// This file is automatically generated by KefinTweaks Installer
|
||||
// Do not edit manually unless you know what you're doing
|
||||
|
||||
window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
|
||||
const pluginId = await findJavaScriptInjectorPlugin();
|
||||
if (!pluginId) {
|
||||
return { ok: false, reason: 'JavaScript Injector plugin not found' };
|
||||
}
|
||||
const injectorConfig = await getJavaScriptInjectorConfig(pluginId);
|
||||
if (!injectorConfig) {
|
||||
return { ok: false, reason: 'Could not read JS Injector configuration' };
|
||||
}
|
||||
if (!injectorConfig.CustomJavaScripts) {
|
||||
injectorConfig.CustomJavaScripts = [];
|
||||
}
|
||||
|
||||
injectorConfig.CustomJavaScripts = Loader.upsertInjectorEntries(
|
||||
injectorConfig.CustomJavaScripts,
|
||||
{ configScriptContent, injectorScriptContent }
|
||||
);
|
||||
|
||||
const server = ApiClient._serverAddress;
|
||||
const response = await fetch(`${server}/Plugins/${pluginId}/Configuration`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': getAuthHeader(),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(injectorConfig)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: `POST failed HTTP ${response.status}: ${response.statusText}`
|
||||
};
|
||||
}
|
||||
|
||||
console.log('[KefinTweaks Installer] Auto-created KefinTweaks-injector with', plan.assets.length, 'assets');
|
||||
return { ok: true, plan };
|
||||
} catch (err) {
|
||||
console.warn('[KefinTweaks Installer] ensureKefinTweaksInjectorEntry failed:', err);
|
||||
return { ok: false, reason: err?.message || String(err) };
|
||||
}
|
||||
}
|
||||
|
||||
function hasEnabledKefinInjectorEntry(customJavaScripts) {
|
||||
if (window.KefinTweaksLoader?.hasEnabledInjectorEntry) {
|
||||
return window.KefinTweaksLoader.hasEnabledInjectorEntry(customJavaScripts);
|
||||
}
|
||||
return (customJavaScripts || []).some(
|
||||
(s) => s.Name === 'KefinTweaks-injector' && s.Enabled !== false
|
||||
);
|
||||
}
|
||||
|
||||
const LATEST_RELEASE_NAME = 'Latest';
|
||||
const DEVELOPMENT_NAME = 'Development';
|
||||
const EXPERIMENTAL_NAME = 'Experimental';
|
||||
@@ -726,6 +915,10 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
}
|
||||
|
||||
const kefinTweaksRoot = buildKefinTweaksRootUrl(sourceType, source);
|
||||
const kefinTweaksRootResolvedRaw = await resolveRootVersion(kefinTweaksRoot);
|
||||
const kefinTweaksRootResolved = kefinTweaksRootResolvedRaw.endsWith('/')
|
||||
? kefinTweaksRootResolvedRaw
|
||||
: kefinTweaksRootResolvedRaw + '/';
|
||||
|
||||
// Load default config from the selected root
|
||||
let defaultConfig;
|
||||
@@ -736,6 +929,7 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
// Create minimal config if loading fails
|
||||
defaultConfig = {
|
||||
kefinTweaksRoot: kefinTweaksRoot,
|
||||
kefinTweaksRootResolved: kefinTweaksRootResolved,
|
||||
enabled: true,
|
||||
scripts: {},
|
||||
exclusiveElsewhere: {},
|
||||
@@ -757,6 +951,7 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
kefinTweaksRoot: kefinTweaksRoot,
|
||||
...(currentConfig || {}),
|
||||
kefinTweaksRoot: kefinTweaksRoot, // Ensure root is updated
|
||||
kefinTweaksRootResolved: kefinTweaksRootResolved,
|
||||
enabled: enabledState // Update enabled state
|
||||
};
|
||||
|
||||
@@ -805,7 +1000,20 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
versionDisplay = sourceInfo.version;
|
||||
}
|
||||
|
||||
// Load injector.js from the configured root
|
||||
// Always load live injector.js for plan apply + stamp sync (baked preload may be stale)
|
||||
let hasPreload = false;
|
||||
try {
|
||||
const pluginId = await findJavaScriptInjectorPlugin();
|
||||
const injCfg = await getJavaScriptInjectorConfig(pluginId);
|
||||
hasPreload = (injCfg.CustomJavaScripts || []).some(
|
||||
(s) => s.Name === 'KefinTweaks-injector' && s.Enabled !== false
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Could not verify KefinTweaks-injector after save:', e);
|
||||
}
|
||||
if (hasPreload) {
|
||||
console.log('[KefinTweaks Installer] KefinTweaks-injector saved; still loading injector.js for live plan + sync');
|
||||
}
|
||||
await loadInjectorFromRoot(kefinTweaksRoot);
|
||||
|
||||
// Show success modal
|
||||
@@ -885,6 +1093,17 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
console.log('[KefinTweaks Installer] Renamed KefinTweaks-Config to backup and disabled it');
|
||||
}
|
||||
|
||||
// Also disable/rename KefinTweaks-injector preload entry
|
||||
const injectorScriptIndex = injectorConfig.CustomJavaScripts.findIndex(
|
||||
script => script.Name === 'KefinTweaks-injector'
|
||||
);
|
||||
if (injectorScriptIndex !== -1) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
|
||||
injectorConfig.CustomJavaScripts[injectorScriptIndex].Name = `KefinTweaks-injector-Backup-${timestamp}`;
|
||||
injectorConfig.CustomJavaScripts[injectorScriptIndex].Enabled = false;
|
||||
console.log('[KefinTweaks Installer] Renamed KefinTweaks-injector to backup and disabled it');
|
||||
}
|
||||
|
||||
// Step 2: Find and disable installer script
|
||||
// Look for script containing kefinTweaks-plugin.js (flexible pattern to match script.src*kefinTweaks-plugin.js)
|
||||
const installerScriptIndex = injectorConfig.CustomJavaScripts.findIndex(
|
||||
@@ -1625,7 +1844,7 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
});
|
||||
}
|
||||
|
||||
// Check if config exists and load injector.js if ready
|
||||
// Check if config exists; auto-create KefinTweaks-injector when missing; always load live injector.js
|
||||
async function checkAndLoadInjector() {
|
||||
try {
|
||||
const config = await getKefinTweaksConfig();
|
||||
@@ -1633,12 +1852,53 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
|
||||
// Before injector/utils: hide fallback page chrome for custom routes
|
||||
injectCustomPageStyles(config);
|
||||
|
||||
if (root && root !== '') {
|
||||
await loadInjectorFromRoot(root);
|
||||
} else {
|
||||
|
||||
if (!root || root === '') {
|
||||
console.log('[KefinTweaks Installer] kefinTweaksRoot not configured. Please configure via the Plugins page.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Plugin config GET/POST needs a session; wait briefly so admin first-load can auto-create
|
||||
await waitForLoginForInjectorWrite();
|
||||
|
||||
let injectorConfig = null;
|
||||
try {
|
||||
const pluginId = await findJavaScriptInjectorPlugin();
|
||||
if (pluginId) {
|
||||
injectorConfig = await getJavaScriptInjectorConfig(pluginId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Could not read JS Injector config:', e);
|
||||
}
|
||||
|
||||
const hasPreload = !!(injectorConfig && hasEnabledKefinInjectorEntry(injectorConfig.CustomJavaScripts));
|
||||
if (hasPreload) {
|
||||
console.log('[KefinTweaks Installer] KefinTweaks-injector present; still loading injector.js for live plan + sync');
|
||||
} else {
|
||||
// Entry missing: try to create + apply assets now (admin/logged-in)
|
||||
const ensured = await ensureKefinTweaksInjectorEntry(config);
|
||||
if (ensured.ok && ensured.plan) {
|
||||
window.KefinTweaksScriptsPreloaded = true;
|
||||
if (window.KefinTweaksLoader) {
|
||||
window.KefinTweaksLoader.ensureKefinTweaksApi(window.KefinTweaksLoader.SCRIPT_DEFINITIONS);
|
||||
window.KefinTweaksLoader.applyAssetsToDocument(ensured.plan.assets);
|
||||
}
|
||||
try {
|
||||
document.dispatchEvent(new CustomEvent('kefinTweaksLoaded', {
|
||||
detail: {
|
||||
stamp: ensured.plan.stamp,
|
||||
timestamp: new Date().toISOString(),
|
||||
autoCreated: true
|
||||
}
|
||||
}));
|
||||
} catch (e) { /* ignore */ }
|
||||
console.log('[KefinTweaks Installer] Applied KefinTweaks-injector assets after auto-create');
|
||||
} else {
|
||||
console.log('[KefinTweaks Installer] KefinTweaks-injector not available (' + (ensured.reason || 'unknown') + '); loading injector.js');
|
||||
}
|
||||
}
|
||||
|
||||
await loadInjectorFromRoot(root);
|
||||
} catch (error) {
|
||||
console.error('[KefinTweaks Installer] Error checking config:', error);
|
||||
}
|
||||
@@ -1654,5 +1914,4 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
}
|
||||
|
||||
console.log('[KefinTweaks Installer] Installer script loaded');
|
||||
})();
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user