Update authentication headers to support v12 requirements, also add v12 skin support to skins from v11

This commit is contained in:
HighImKevin committed 2026-09-09 05:54:42 -04:00
1 parent 290b36f7bf
commit 6f2617f942
15 files changed
+135 -153

No files matched your search

+27 -15
View File
@@ -7,6 +7,22 @@
console.log('[KefinTweaks Injector] Initializing...');
function getAuthHeader() {
const token = ApiClient.accessToken();
const client = typeof ApiClient.applicationName === 'function' ? ApiClient.applicationName() : 'Jellyfin Web';
const device = typeof ApiClient.deviceName === 'function' ? ApiClient.deviceName() : (navigator.userAgent.includes('Chrome') ? 'Chrome' : 'Browser');
const deviceId = typeof ApiClient.deviceId === 'function' ? ApiClient.deviceId() : '';
const version = ApiClient._appVersion || ApiClient._serverVersion || '';
const parts = [
`Client="${encodeURIComponent(client)}"`,
`Device="${encodeURIComponent(device)}"`,
`DeviceId="${encodeURIComponent(deviceId)}"`,
`Version="${encodeURIComponent(version)}"`,
`Token="${encodeURIComponent(token)}"`
];
return `MediaBrowser ${parts.join(', ')}`;
}
// Check for global configuration reset
try {
const config = window.KefinTweaksConfig || {};
@@ -110,7 +126,7 @@
name: 'utils',
script: 'utils.js',
css: null,
dependencies: [],
dependencies: ['apiHelper'],
description: 'Common utilities for page view management and MutationObserver conversion'
},
{
@@ -131,7 +147,7 @@
name: 'skinManager',
script: 'skinManager.js',
css: 'defaultSkin.css',
dependencies: ['utils', 'skinConfigLegacyDefaults', 'skinConfig', 'modal'],
dependencies: ['utils', 'skinConfigLegacyDefaults', 'skinConfig', 'modal', 'apiHelper'],
priority: true, // Load immediately after dependencies to reduce UI disruption
description: 'Adds skin selection dropdown to display preferences page and manages skin CSS loading'
},
@@ -202,7 +218,7 @@
name: 'headerTabs',
script: 'headerTabs.js',
css: null,
dependencies: [],
dependencies: ['apiHelper'],
description: 'Header tab improvements'
},
{
@@ -230,7 +246,7 @@
name: 'updoot',
script: 'updoot.js',
css: null,
dependencies: [],
dependencies: ['apiHelper'],
description: 'Upvote functionality provided by https://github.com/BobHasNoSoul/jellyfin-updoot'
},
{
@@ -251,14 +267,14 @@
name: 'removeContinue',
script: 'removeContinue.js',
css: null,
dependencies: [],
dependencies: ['apiHelper'],
description: 'Adds remove from continue watching functionality to cards with data-position-ticks'
},
{
name: 'subtitleSearch',
script: 'subtitleSearch.js',
css: 'subtitleSearch.css',
dependencies: ['toaster'],
dependencies: ['toaster', 'apiHelper'],
description: 'Adds subtitle search functionality to the video OSD, allowing users to search and download subtitles from remote sources'
},
{
@@ -624,7 +640,7 @@
}
async function loadConfigurationJS() {
const configDependencyNames = ['modal', 'toaster', 'utils', 'apiHelper', 'indexedDBCache'];
const configDependencyNames = ['modal', 'toaster', 'apiHelper', 'utils', 'indexedDBCache'];
for (const depName of configDependencyNames) {
const depScript = SCRIPT_DEFINITIONS.find(script => script.name === depName);
if (depScript) {
@@ -839,11 +855,10 @@
}
const server = ApiClient._serverAddress;
const token = ApiClient.accessToken();
const response = await fetch(`${server}/Plugins`, {
headers: {
'X-Emby-Token': token
'Authorization': getAuthHeader()
}
});
@@ -870,11 +885,10 @@
}
const server = ApiClient._serverAddress;
const token = ApiClient.accessToken();
const response = await fetch(`${server}/Plugins/${pluginId}/Configuration`, {
headers: {
'X-Emby-Token': token
'Authorization': getAuthHeader()
}
});
@@ -897,12 +911,11 @@
}
const server = ApiClient._serverAddress;
const token = ApiClient.accessToken();
const response = await fetch(`${server}/Plugins/${pluginId}/Configuration`, {
method: 'POST',
headers: {
'X-Emby-Token': token,
'Authorization': getAuthHeader(),
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
@@ -956,11 +969,10 @@
// Get current CustomTabs config
const server = ApiClient._serverAddress;
const token = ApiClient.accessToken();
const response = await fetch(`${server}/CustomTabs/Config`, {
headers: {
'X-Emby-Token': token
'Authorization': getAuthHeader()
}
});
+8 -11
View File
@@ -21,7 +21,6 @@
async function getActiveSession() {
ensureApiClient();
const token = ApiClient.accessToken();
const serverUrl = ApiClient.serverAddress();
const deviceId = ApiClient.deviceId();
@@ -39,7 +38,7 @@
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': getAuthHeader()
}
});
@@ -71,7 +70,6 @@
const session = await getActiveSession();
const sessionId = session.Id;
const token = ApiClient.accessToken();
const serverUrl = ApiClient.serverAddress();
const limitedIds = ids.slice(0, MAX_PLAY_NOW_IDS);
@@ -102,7 +100,7 @@
const response = await fetch(`${serverUrl}/Sessions/${sessionId}/Playing?${params.toString()}`, {
method: 'POST',
headers: {
'X-Emby-Token': token,
'Authorization': getAuthHeader(),
}
});
@@ -115,7 +113,6 @@
const session = await getActiveSession();
const sessionId = session.Id;
const token = ApiClient.accessToken();
const serverUrl = ApiClient.serverAddress();
const params = new URLSearchParams({
@@ -131,7 +128,7 @@
const response = await fetch(`${serverUrl}/Sessions/${sessionId}/Playing?${params.toString()}`, {
method: 'POST',
headers: {
'X-Emby-Token': token
'Authorization': getAuthHeader()
}
});
@@ -276,10 +273,9 @@
// Fetch from API
try {
const token = ApiClient.accessToken();
const response = await fetch(url, {
headers: {
'X-Emby-Token': token,
'Authorization': getAuthHeader(),
'Accept': 'application/json'
}
});
@@ -426,7 +422,7 @@
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Emby-Token': ApiClient.accessToken()
'Authorization': getAuthHeader()
},
body: JSON.stringify({
Rating: rating,
@@ -502,7 +498,6 @@
throw new Error('No active session found');
}
const token = ApiClient.accessToken();
const serverUrl = ApiClient.serverAddress();
// Build NowPlayingQueue array - each item needs at least an Id
@@ -522,7 +517,7 @@
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': getAuthHeader()
},
body: JSON.stringify(payload)
});
@@ -555,6 +550,8 @@
return await response.json();
},
getAuthHeader,
};
// Expose apiHelper to global window object
+5 -18
View File
@@ -3782,7 +3782,7 @@
const token = window.ApiClient.accessToken();
const response = await fetch(`${serverAddress}/Users/${userId}/Items?IncludeItemTypes=BoxSet,CollectionFolder&Recursive=true&Fields=ItemCounts`, {
headers: { 'X-Emby-Token': token }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!response.ok) {
@@ -3902,7 +3902,7 @@
const token = window.ApiClient.accessToken();
const response = await fetch(`${serverAddress}/Users/${userId}/Items?IncludeItemTypes=Playlist&Recursive=true&Fields=ItemCounts`, {
headers: { 'X-Emby-Token': token }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!response.ok) {
@@ -4022,7 +4022,7 @@
const token = window.ApiClient.accessToken();
const response = await fetch(`${serverAddress}/Users/${userId}/Items?IncludeItemTypes=BoxSet,CollectionFolder&Recursive=true&Fields=ItemCounts`, {
headers: { 'X-Emby-Token': token }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!response.ok) {
@@ -4160,7 +4160,7 @@
const token = window.ApiClient.accessToken();
const response = await fetch(`${serverAddress}/Users/${userId}/Items?IncludeItemTypes=Playlist&Recursive=true&Fields=ItemCounts`, {
headers: { 'X-Emby-Token': token }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!response.ok) {
@@ -4738,23 +4738,10 @@
}
const server = ApiClient._serverAddress;
const token = ApiClient.accessToken();
const client = typeof ApiClient.applicationName === 'function' ? ApiClient.applicationName() : 'Jellyfin Web';
const device = typeof ApiClient.deviceName === 'function' ? ApiClient.deviceName() : (navigator.userAgent.includes('Chrome') ? 'Chrome' : 'Browser');
const deviceId = typeof ApiClient.deviceId === 'function' ? ApiClient.deviceId() : '';
const version = ApiClient._appVersion || ApiClient._serverVersion || '';
const parts = [
`Client="${encodeURIComponent(client)}"`,
`Device="${encodeURIComponent(device)}"`,
`DeviceId="${encodeURIComponent(deviceId)}"`,
`Version="${encodeURIComponent(version)}"`,
`Token="${encodeURIComponent(token)}"`
];
const auth = `MediaBrowser ${parts.join(', ')}`;
const response = await fetch(`${server}/Plugins/${pluginId}/Configuration`, {
headers: {
'Authorization': auth
'Authorization': apiHelper.getAuthHeader()
}
});
+1 -1
View File
@@ -132,7 +132,7 @@
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
+2 -2
View File
@@ -24,7 +24,7 @@
LOG(`Fetching Next Up episode for series: ${seriesId}`);
const nextUpUrl = `${serverUrl}/Shows/NextUp?SeriesId=${seriesId}&UserId=${userId}&Fields=MediaSourceCount`;
const nextUpRes = await fetch(nextUpUrl, {
headers: { "Authorization": `MediaBrowser Token="${token}"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
});
if (!nextUpRes.ok) {
@@ -69,7 +69,7 @@
LOG(`Fetching episodes for series: ${seriesId}`);
const episodesUrl = `${serverUrl}/Shows/${seriesId}/Episodes?UserId=${userId}&Fields=UserData`;
const episodesRes = await fetch(episodesUrl, {
headers: { "Authorization": `MediaBrowser Token="${token}"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
});
if (!episodesRes.ok) {
+2 -2
View File
@@ -93,7 +93,7 @@
`${serverAddress}/DisplayPreferences/usersettings?userId=${userId}&client=emby`,
{
headers: {
'X-Emby-Token': accessToken
'Authorization': apiHelper.getAuthHeader()
}
}
);
@@ -345,7 +345,7 @@
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Emby-Token": ApiClient._serverInfo.AccessToken || ApiClient.accessToken(),
"Authorization": apiHelper.getAuthHeader(),
},
});
const customTabs = await response.json();
+15 -15
View File
@@ -1112,7 +1112,7 @@
try {
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -1552,7 +1552,7 @@
try {
const response = await fetch(url, {
headers: {
'Authorization': `MediaBrowser Token="${ApiClient.accessToken()}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -1649,7 +1649,7 @@
try {
const response = await fetch(`${serverUrl}/Genres?IncludeItemTypes=Movie`, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -1745,7 +1745,7 @@
queries.map(async (query) => {
const response = await fetch(query.url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -1854,7 +1854,7 @@
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -2214,7 +2214,7 @@
try {
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -2251,7 +2251,7 @@
try {
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -2565,7 +2565,7 @@
const studiosUrl = `${serverUrl}/Studios?SortBy=SortName&SortOrder=Ascending&IncludeItemTypes=Series&Recursive=true&Fields=DateCreated%2CPrimaryImageAspectRatio&StartIndex=0&ParentId=${library.Id}&userId=${userId}${filterParam}`;
const response = await fetch(studiosUrl, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -2646,7 +2646,7 @@
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -2680,7 +2680,7 @@
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -3828,7 +3828,7 @@
const movieUrl = `${serverUrl}/Users/${userId}/Items/${recentMovie.Id}?Fields=People`;
const movieResponse = await fetch(movieUrl, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -3890,7 +3890,7 @@
const movieUrl = `${serverUrl}/Users/${userId}/Items/${recentMovie.Id}?Fields=People`;
const movieResponse = await fetch(movieUrl, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -3958,7 +3958,7 @@
const movieUrl = `${serverUrl}/Users/${userId}/Items/${recentMovie.Id}?Fields=People`;
const movieResponse = await fetch(movieUrl, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -4708,7 +4708,7 @@
const response = await fetch(url, {
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -5391,7 +5391,7 @@
const serverAddress = ApiClient.serverAddress();
const token = ApiClient.accessToken();
const genresResponse = await fetch(`${serverAddress}/Genres?IncludeItemTypes=Movie`, {
headers: { "Authorization": `MediaBrowser Token="${token}"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
});
if (!genresResponse.ok) {
+2 -2
View File
@@ -162,7 +162,7 @@
const response = await fetch(userDataUrl, {
method: 'GET',
headers: {
"Authorization": `MediaBrowser Token="${token}"`,
"Authorization": apiHelper.getAuthHeader(),
"Content-Type": "application/json"
}
});
@@ -196,7 +196,7 @@
const response = await fetch(userDataUrl, {
method: 'POST',
headers: {
"Authorization": `MediaBrowser Token="${token}"`,
"Authorization": apiHelper.getAuthHeader(),
"Content-Type": "application/json"
},
body: JSON.stringify(updatedUserData)
+17 -16
View File
@@ -24,7 +24,7 @@
LOG(`Fetching Next Up episode for series: ${seriesId}`);
const nextUpUrl = `${serverUrl}/Shows/NextUp?SeriesId=${seriesId}&UserId=${userId}&Fields=MediaSourceCount`;
const nextUpRes = await fetch(nextUpUrl, {
headers: { "Authorization": `MediaBrowser Token="${token}"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
});
if (!nextUpRes.ok) {
@@ -63,7 +63,7 @@
LOG(`Fetching season data for season ID: ${seasonId}`);
const seasonUrl = `${serverUrl}/Items/${seasonId}?UserId=${userId}`;
const seasonRes = await fetch(seasonUrl, {
headers: { "Authorization": `MediaBrowser Token="${token}"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
});
if (!seasonRes.ok) {
@@ -95,7 +95,7 @@
LOG(`Fetching episodes for series: ${seriesId}${seasonId ? ` (Season ID: ${seasonId})` : ''}`);
const episodesUrl = `${serverUrl}/Shows/${seriesId}/Episodes?UserId=${userId}&Fields=UserData`;
const episodesRes = await fetch(episodesUrl, {
headers: { "Authorization": `MediaBrowser Token="${token}"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
});
if (!episodesRes.ok) {
@@ -559,18 +559,17 @@
// Insert episodes section right after NextUp section (or at the beginning if no NextUp)
const detailPageContent = activePage.querySelector('.detailPageContent');
if (detailPageContent) {
if (nextUpSection && nextUpSection.parentNode) {
nextUpSection.parentNode.insertBefore(seasonSection, nextUpSection.nextSibling);
} else {
// Find a good insertion point - after NextUp or before childrenCollapsible
const childrenCollapsible = activePage.querySelector('.detailSection #listChildrenCollapsible') ||
activePage.querySelector('#childrenCollapsible');
if (childrenCollapsible && childrenCollapsible.parentNode) {
childrenCollapsible.parentNode.insertBefore(seasonSection, childrenCollapsible);
} else {
detailPageContent.insertBefore(seasonSection, detailPageContent.firstChild);
}
if (nextUpSection && nextUpSection.parentNode) {
nextUpSection.parentNode.insertBefore(seasonSection, nextUpSection.nextSibling);
} else {
// Find a good insertion point - after NextUp or before childrenCollapsible
const childrenCollapsible = activePage.querySelector('.detailSection #listChildrenCollapsible') ||
activePage.querySelector('#childrenCollapsible');
if (childrenCollapsible && childrenCollapsible.parentNode) {
childrenCollapsible.parentNode.insertBefore(seasonSection, childrenCollapsible);
}
else if (detailPageContent){
detailPageContent.insertBefore(seasonSection, detailPageContent.firstChild);
}
}
@@ -817,7 +816,9 @@
}
// Mark as processed
activePage.dataset.seriesEpisodesProcessed = 'true';
if (activePage.querySelector('.series-episodes-section')) {
activePage.dataset.seriesEpisodesProcessed = 'true';
}
}
/**
+12 -12
View File
@@ -107,7 +107,7 @@
]
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/lscambo13/ElegantFin@main/Theme/ElegantFin-jellyfin-theme-build-latest-minified.css',
`${getKefinTweaksRoot()}skins/elegant-kefin.css`,
@@ -159,7 +159,7 @@
author: 'KartoffelChipss',
url: [
{
majorServerVersions: [10, 11],
majorServerVersions: [10, 11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/KartoffelChipss/NeutralFin@latest/theme/neutralfin-minified.css',
'https://cdn.jsdelivr.net/gh/KartoffelChipss/Jellyfin-Lucide@main/theme/jellyfin-lucide.css',
@@ -211,7 +211,7 @@
author: 'KBH-Reeper',
url: [
{
majorServerVersions: [10, 11],
majorServerVersions: [10, 11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/KBH-Reeper/GlassFin@latest/Theme/GlassFin-Theme-latest-stable.css',
'https://cdn.jsdelivr.net/gh/KBH-Reeper/GlassFin@latest/Theme/PluginStyling/MediaBar-Plugin-latest.css',
@@ -296,7 +296,7 @@
urls: ['https://cdn.jsdelivr.net/gh/ranaldsgift/Jellypane@main/jellypane.css']
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: ['https://cdn.jsdelivr.net/gh/tedhinklater/Jellypane@main/jellypane.css']
},
],
@@ -311,7 +311,7 @@
urls: ['https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@main/finimalism7.css']
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: ['https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@main/finimalism10.11.css']
}
],
@@ -338,7 +338,7 @@
urls: ['https://cdn.jsdelivr.net/gh/n00bcodr/jellyfish@main/theme.css']
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/n00bcodr/jellyfish@main/theme.css',
'https://cdn.jsdelivr.net/gh/n00bcodr/jellyfish@main/10.11_fixes.css'
@@ -428,7 +428,7 @@
author: 'alexyle',
url: [
{
majorServerVersions: [10, 11],
majorServerVersions: [10, 11, 12],
urls: ['https://cdn.jsdelivr.net/gh/alexyle/jellyfin-theme@main/glassmorphism/theme.css']
}
],
@@ -491,7 +491,7 @@
]
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/JamsRepos/Jamfin@latest/theme/complete.css',
`${getKefinTweaksRoot()}skins/jamfin-kefin.css`
@@ -539,7 +539,7 @@
]
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/loof2736/scyfin@latest/CSS/scyfin-theme.css',
`${getKefinTweaksRoot()}skins/scyfin-kefin.css`
@@ -581,7 +581,7 @@
author: 'Catppuccin',
url: [
{
majorServerVersions: [10, 11],
majorServerVersions: [10, 11, 12],
urls: ['https://jellyfin.catppuccin.com/theme.css']
}
],
@@ -616,7 +616,7 @@
]
},
{
majorServerVersions: [11],
majorServerVersions: [11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/LitCastVlog/Flow@main/CSS/ScyFlow-oneliner.css',
'https://cdn.jsdelivr.net/gh/LitCastVlog/Flow@main/CSS/ScyFlow-Compatibility.css',
@@ -719,7 +719,7 @@
author: 'CTalvio',
url: [
{
majorServerVersions: [10, 11],
majorServerVersions: [10, 11, 12],
urls: [
'https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/base.css',
'https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/accentlist.css',
+8 -3
View File
@@ -26,6 +26,11 @@
function getMajorServerVersion(version) {
const versionParts = version.split('.');
const firstPartInt = parseInt(versionParts[0], 10);
if (firstPartInt >= 12) {
return 12;
}
if (versionParts.length >= 2) {
const majorVersion = parseInt(versionParts[1], 10);
if (!isNaN(majorVersion)) {
@@ -360,7 +365,7 @@
// Find JavaScript Injector plugin
const pluginsResponse = await fetch(`${server}/Plugins`, {
headers: { 'X-Emby-Token': token }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!pluginsResponse.ok) {
@@ -382,7 +387,7 @@
// Get current injector config
const configUrl = `${server}/Plugins/${pluginId}/Configuration`;
const configResponse = await fetch(configUrl, {
headers: { 'X-Emby-Token': token }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!configResponse.ok) {
@@ -421,7 +426,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
const saveResponse = await fetch(configUrl, {
method: 'POST',
headers: {
'X-Emby-Token': token,
'Authorization': apiHelper.getAuthHeader(),
'Content-Type': 'application/json'
},
body: JSON.stringify(injectorConfig)
+7 -7
View File
@@ -338,7 +338,7 @@
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -425,7 +425,7 @@
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -512,7 +512,7 @@
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -588,7 +588,7 @@
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -638,7 +638,7 @@
const response = await fetch(`${serverUrl}/Sessions/${sessionId}/Command`, {
method: 'POST',
headers: {
'X-Emby-Token': token,
'Authorization': apiHelper.getAuthHeader(),
'Content-Type': 'application/json'
},
body: JSON.stringify({
@@ -788,7 +788,7 @@
const commandResponse = await fetch(`${serverUrl}/Sessions/${sessionId}/Playing?${params.toString()}`, {
method: 'POST',
headers: {
'X-Emby-Token': token
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -903,7 +903,7 @@
const response = await fetch(`${serverUrl}/Items/${itemId}/RemoteSearch/Subtitles/${subId}`, {
method: 'POST',
headers: {
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
+2 -2
View File
@@ -29,11 +29,11 @@
async function fetchItemDetails(itemId) {
console.log('Fetching item details for itemId:', itemId);
try {
const url = `${serverUrl}/Items/${itemId}?api_key=${apiKey}`; // Fixed typo from original: 'melalui' to 'apiKey'
const url = `${serverUrl}/Items/${itemId}`;
console.log('Requesting:', url);
const response = await fetch(url, {
method: 'GET',
headers: { 'X-Emby-Token': apiKey }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!response.ok) {
console.error('Fetch item details failed:', `HTTP ${response.status}`);
+3 -23
View File
@@ -51,26 +51,6 @@
}
}
/**
* Build MediaBrowser Authorization header
* @returns {string} - Authorization header
*/
function getAuthHeader() {
const token = ApiClient.accessToken();
const client = typeof ApiClient.applicationName === 'function' ? ApiClient.applicationName() : 'Jellyfin Web';
const device = typeof ApiClient.deviceName === 'function' ? ApiClient.deviceName() : (navigator.userAgent.includes('Chrome') ? 'Chrome' : 'Browser');
const deviceId = typeof ApiClient.deviceId === 'function' ? ApiClient.deviceId() : '';
const version = ApiClient._appVersion || ApiClient._serverVersion || '';
const parts = [
`Client="${encodeURIComponent(client)}"`,
`Device="${encodeURIComponent(device)}"`,
`DeviceId="${encodeURIComponent(deviceId)}"`,
`Version="${encodeURIComponent(version)}"`,
`Token="${encodeURIComponent(token)}"`
];
return `MediaBrowser ${parts.join(', ')}`;
}
// Array to store registered handlers
const handlers = [];
@@ -452,7 +432,7 @@
// Get current injector config
const configUrl = `${server}/Plugins/${pluginId}/Configuration`;
const configResponse = await fetch(configUrl, {
headers: { 'Authorization': getAuthHeader() }
headers: { 'Authorization': apiHelper.getAuthHeader() }
});
if (!configResponse.ok) {
@@ -495,7 +475,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToSave, null, 2)};`;
const saveResponse = await fetch(configUrl, {
method: 'POST',
headers: {
'Authorization': getAuthHeader(),
'Authorization': apiHelper.getAuthHeader(),
'Content-Type': 'application/json'
},
body: JSON.stringify(injectorConfig)
@@ -579,7 +559,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToSave, null, 2)};`;
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Emby-Token": ApiClient._serverInfo.AccessToken || ApiClient.accessToken(),
"Authorization": apiHelper.getAuthHeader(),
},
});
const data = await response.json();
+24 -24
View File
@@ -1263,13 +1263,13 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
try {
const episodeUrl = `${serverUrl}/Items/${episodeId}?UserId=${userId}&Fields=ParentId`;
const episodeRes = await fetch(episodeUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const episodeRes = await fetch(episodeUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const episodeData = await episodeRes.json();
// Get the series ID from the episode's parent (season) parent
if (episodeData.ParentId) {
const seasonUrl = `${serverUrl}/Items/${episodeData.ParentId}?UserId=${userId}&Fields=ParentId`;
const seasonRes = await fetch(seasonUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const seasonRes = await fetch(seasonUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const seasonData = await seasonRes.json();
return seasonData.ParentId || null;
@@ -1351,7 +1351,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
try {
// Fetch the series item
const seriesUrl = `${serverUrl}/Items/${seriesId}?UserId=${userId}&Fields=UserData,RecursiveItemCount&EnableImageTypes=Primary,Banner`;
const seriesRes = await fetch(seriesUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const seriesRes = await fetch(seriesUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const series = await seriesRes.json();
if (!series || !series.Id) {
@@ -2505,7 +2505,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const seriesUrl = `${serverUrl}/Items?IncludeItemTypes=Series&UserId=${userId}&Recursive=true&Fields=UserData,RecursiveItemCount&EnableImageTypes=Primary,Banner`;
try {
const seriesRes = await fetch(seriesUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const seriesRes = await fetch(seriesUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const seriesData = await seriesRes.json();
const series = seriesData.Items || [];
@@ -2596,7 +2596,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
// Get all episodes including missing ones to get accurate TotalRecordCount
const episodesUrl = `${serverUrl}/Shows/${series.Id}/Episodes?UserId=${userId}&Fields=UserData&EnableImageTypes=Primary`;
const episodesRes = await fetch(episodesUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const episodesRes = await fetch(episodesUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const episodesData = await episodesRes.json();
let episodes = episodesData.Items || [];
@@ -2736,7 +2736,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const toggleRes = await fetch(toggleUrl, {
method: method,
headers: {
"Authorization": `MediaBrowser Token="${token}"`
"Authorization": apiHelper.getAuthHeader()
}
});
@@ -3803,7 +3803,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const url = `${serverUrl}/Users/${userId}/Items?IncludeItemTypes=Playlist&Recursive=true&Fields=ChildCount`;
const response = await fetch(url, {
headers: {
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -4039,7 +4039,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const response = await fetch(`${serverUrl}/Playlists?${params.toString()}`, {
method: 'POST',
headers: {
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -4062,7 +4062,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
// Remove existing entries
const existingItemsResponse = await fetch(`${serverUrl}/Playlists/${playlistId}/Items`, {
headers: {
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
@@ -4082,7 +4082,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const deleteResponse = await fetch(`${serverUrl}/Playlists/${playlistId}/Items?${params.toString()}`, {
method: 'DELETE',
headers: {
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
if (!deleteResponse.ok) {
@@ -4102,7 +4102,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const response = await fetch(`${serverUrl}/Playlists/${playlistId}/Items?${params.toString()}`, {
method: 'POST',
headers: {
'Authorization': `MediaBrowser Token="${token}"`
'Authorization': apiHelper.getAuthHeader()
}
});
if (!response.ok) {
@@ -4269,7 +4269,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
if (item.ParentId) {
try {
const seasonUrl = `${serverUrl}/Items/${item.ParentId}?UserId=${userId}&Fields=Name`;
const seasonRes = await fetch(seasonUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const seasonRes = await fetch(seasonUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const seasonData = await seasonRes.json();
if (seasonData.Name) {
exportItem.SeasonName = seasonData.Name;
@@ -4405,7 +4405,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
let allLibraryItems = [];
try {
const url = `${serverUrl}/Items?${queryParams.toString()}`;
const res = await fetch(url, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const res = await fetch(url, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const libraryData = await res.json();
allLibraryItems = libraryData.Items || [];
LOG(`Fetched ${allLibraryItems.length} items from library matching import criteria`);
@@ -4439,7 +4439,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
for (const type of watchlistTypes) {
try {
const url = `${serverUrl}/Items?Filters=Likes&IncludeItemTypes=${type}&UserId=${userId}&Recursive=true&Fields=ProviderIds`;
const res = await fetch(url, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const res = await fetch(url, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const watchlistData = await res.json();
const items = watchlistData.Items || [];
for (const item of items) {
@@ -4547,7 +4547,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
for (const type of types) {
try {
const url = `${serverUrl}/Items?Filters=Likes&IncludeItemTypes=${type}&UserId=${userId}&Recursive=true&Fields=Id`;
const res = await fetch(url, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const res = await fetch(url, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const data = await res.json();
const items = data.Items || [];
@@ -4869,7 +4869,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
try {
// Fetch the series item
const seriesUrl = `${serverUrl}/Items/${seriesId}?UserId=${userId}&Fields=UserData,RecursiveItemCount&EnableImageTypes=Primary,Banner`;
const seriesRes = await fetch(seriesUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const seriesRes = await fetch(seriesUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const series = await seriesRes.json();
if (!series || !series.Id) {
@@ -5251,7 +5251,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const url = `${serverUrl}/Items?IncludeItemTypes=Movie&UserId=${userId}&Recursive=true&Filters=IsPlayed&Fields=UserData,ProviderIds&EnableImageTypes=Primary,Backdrop,Thumb&ImageTypeLimit=1&SortBy=DatePlayed&SortOrder=Descending`;
try {
const res = await fetch(url, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const res = await fetch(url, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const data = await res.json();
const movies = data.Items || [];
@@ -6033,7 +6033,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
try {
LOG(`Fetching episodes from API for series: ${seriesId}`);
const episodesUrl = `${serverUrl}/Shows/${seriesId}/Episodes?UserId=${userId}&Fields=UserData&EnableImageTypes=Primary`;
const episodesRes = await fetch(episodesUrl, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const episodesRes = await fetch(episodesUrl, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const episodesData = await episodesRes.json();
let episodes = episodesData.Items || [];
@@ -6203,7 +6203,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const response = await fetch(markPlayedUrl, {
method: isWatched ? 'POST' : 'DELETE',
headers: {
"Authorization": `MediaBrowser Token="${token}"`,
"Authorization": apiHelper.getAuthHeader(),
"Content-Type": "application/json"
}
});
@@ -6328,7 +6328,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const response = await fetch(markPlayedUrl, {
method: 'POST',
headers: {
"Authorization": `MediaBrowser Token="${token}"`,
"Authorization": apiHelper.getAuthHeader(),
"Content-Type": "application/json"
}
});
@@ -7124,7 +7124,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Emby-Token": ApiClient._serverInfo.AccessToken || ApiClient.accessToken(),
"Authorization": apiHelper.getAuthHeader(),
},
});
const data = await response.json();
@@ -7229,7 +7229,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Emby-Token": ApiClient._serverInfo.AccessToken || ApiClient.accessToken(),
"Authorization": apiHelper.getAuthHeader(),
},
})
.then((r) => r.json())
@@ -7795,7 +7795,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const token = apiClient.accessToken();
const url = `${serverUrl}/Items?IncludeItemTypes=Series&UserId=${userId}&Recursive=true&Fields=UserData,RecursiveItemCount`;
const res = await fetch(url, { headers: { "Authorization": `MediaBrowser Token=\"${token}\"` } });
const res = await fetch(url, { headers: { "Authorization": apiHelper.getAuthHeader() } });
const data = await res.json();
const series = data.Items || [];
@@ -7823,7 +7823,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
const token = apiClient.accessToken();
fetch(`${serverUrl}/Items?IncludeItemTypes=Series&UserId=${userId}&Recursive=true&Fields=UserData,ChildCount,RecursiveItemCount`, {
headers: { "Authorization": `MediaBrowser Token=\"${token}\"` }
headers: { "Authorization": apiHelper.getAuthHeader() }
})
.then(res => res.json())
.then(data => {