Compare commits

...

12 Commits

Author SHA1 Message Date
CodeDevMLH
d7c44061cb Update manifest.json for release v1.6.2.0 [skip ci] 2026-02-14 23:34:47 +00:00
CodeDevMLH
9acb05d19e Update version to 1.6.2.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 49s
2026-02-15 00:33:58 +01:00
CodeDevMLH
619d8533d2 Add configuration options for local backdrops and randomization features 2026-02-15 00:31:01 +01:00
CodeDevMLH
a4b39ae3bf Add check_backdrop_fields script for testing item details and theme media 2026-02-15 00:30:44 +01:00
CodeDevMLH
74a367584b Add fetch_specific_items_with_fields script to retrieve extended item details 2026-02-15 00:30:36 +01:00
CodeDevMLH
06407f9121 Update manifest.json for release v1.6.1.32 [skip ci] 2026-02-14 16:34:33 +00:00
CodeDevMLH
0926493391 Update manifest.json with new seasonal UI logic and custom ID limits
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-14 17:33:39 +01:00
CodeDevMLH
6dbb33be96 remove tests
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-02-14 17:33:04 +01:00
CodeDevMLH
211d9d316a Update README.md to include new sections for content sorting, limits, and credits 2026-02-14 17:32:46 +01:00
CodeDevMLH
329733246d Update field description for seasonal content configuration to clarify fallback behavior 2026-02-14 17:32:42 +01:00
CodeDevMLH
6110e1cdd8 Remove commented-out code for previous slide handling and play/pause button state management 2026-02-14 17:32:38 +01:00
CodeDevMLH
31c8a209a5 add test
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-02-14 17:32:25 +01:00
9 changed files with 290 additions and 3644 deletions

View File

@@ -22,6 +22,9 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
public bool EnableVideoBackdrop { get; set; } = true; public bool EnableVideoBackdrop { get; set; } = true;
public bool UseSponsorBlock { get; set; } = true; public bool UseSponsorBlock { get; set; } = true;
public bool PreferLocalTrailers { get; set; } = false; public bool PreferLocalTrailers { get; set; } = false;
public bool RandomizeLocalTrailers { get; set; } = false;
public bool PreferLocalBackdrops { get; set; } = false;
public bool RandomizeThemeVideos { get; set; } = false;
public bool WaitForTrailerToEnd { get; set; } = true; public bool WaitForTrailerToEnd { get; set; } = true;
public bool StartMuted { get; set; } = true; public bool StartMuted { get; set; } = true;
public bool FullWidthVideo { get; set; } = true; public bool FullWidthVideo { get; set; } = true;

View File

@@ -65,6 +65,14 @@
</label> </label>
<div class="fieldDescription">If enabled, local trailers will be preferred over remote (YouTube) trailers.</div> <div class="fieldDescription">If enabled, local trailers will be preferred over remote (YouTube) trailers.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription" id="PreferLocalBackdropsContainer">
<label>
<input is="emby-checkbox" type="checkbox" id="PreferLocalBackdrops"
name="PreferLocalBackdrops" />
<span>Prefer Local Backdrops / Theme Videos</span>
</label>
<div class="fieldDescription">If enabled, local backdrop videos (Theme Videos) will be preferred over trailers.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd" <input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd"
@@ -152,8 +160,8 @@
name="EnableSeasonalContent" /> name="EnableSeasonalContent" />
<span>Enable Seasonal Content</span> <span>Enable Seasonal Content</span>
</label> </label>
<div class="fieldDescription">When enabled, seasonal sections below will override the default list <div class="fieldDescription">When enabled, seasonal sections below will override the default list or random selection
during their active date ranges. If no season matches the current date, the default Custom Media IDs above are used as fallback.</div> during their active date ranges. If no season matches the current date, the default Custom Media IDs above or random selection are used as fallback.</div>
</div> </div>
<div id="seasonalContentContainer" style="display: none;"> <div id="seasonalContentContainer" style="display: none;">
<div id="seasonalSectionsList"></div> <div id="seasonalSectionsList"></div>
@@ -187,6 +195,22 @@
<div class="fieldDescription">If enabled, users will see a media bar icon in the header to <div class="fieldDescription">If enabled, users will see a media bar icon in the header to
override settings (like disabling the bar or trailer backdrops) locally on their device.</div> override settings (like disabling the bar or trailer backdrops) locally on their device.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="RandomizeThemeVideos"
name="RandomizeThemeVideos" />
<span>Randomize Backdrop Video</span>
</label>
<div class="fieldDescription">If enabled, a random video from the backdrops/theme videos will be selected instead of the first one.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="RandomizeLocalTrailers"
name="RandomizeLocalTrailers" />
<span>Randomize Local Trailer</span>
</label>
<div class="fieldDescription">If enabled, a random local trailer will be selected instead of the first one (if multiple exist).</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="UseSponsorBlock" name="UseSponsorBlock" /> <input is="emby-checkbox" type="checkbox" id="UseSponsorBlock" name="UseSponsorBlock" />
@@ -419,7 +443,8 @@
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls', 'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen', 'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections' 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections',
'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers'
]; ];
keys.forEach(function (key) { keys.forEach(function (key) {
@@ -470,12 +495,15 @@
// Handle Prefer Local Trailers visibility // Handle Prefer Local Trailers visibility
var enableVideoBackdropCheckbox = page.querySelector('#EnableVideoBackdrop'); var enableVideoBackdropCheckbox = page.querySelector('#EnableVideoBackdrop');
var preferLocalContainer = page.querySelector('#PreferLocalTrailersContainer'); var preferLocalContainer = page.querySelector('#PreferLocalTrailersContainer');
var preferLocalBackdropsContainer = page.querySelector('#PreferLocalBackdropsContainer');
function updatePreferLocalVisibility() { function updatePreferLocalVisibility() {
if (enableVideoBackdropCheckbox && enableVideoBackdropCheckbox.checked) { if (enableVideoBackdropCheckbox && enableVideoBackdropCheckbox.checked) {
if (preferLocalContainer) preferLocalContainer.style.display = 'block'; if (preferLocalContainer) preferLocalContainer.style.display = 'block';
if (preferLocalBackdropsContainer) preferLocalBackdropsContainer.style.display = 'block';
} else { } else {
if (preferLocalContainer) preferLocalContainer.style.display = 'none'; if (preferLocalContainer) preferLocalContainer.style.display = 'none';
if (preferLocalBackdropsContainer) preferLocalBackdropsContainer.style.display = 'none';
} }
} }
@@ -505,7 +533,8 @@
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls', 'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen', 'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder', 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections' 'PreferLocalTrailers', 'ApplyLimitsToCustomIds', 'SeasonalSections',
'PreferLocalBackdrops', 'RandomizeThemeVideos', 'RandomizeLocalTrailers'
]; ];
keys.forEach(function (key) { keys.forEach(function (key) {

View File

@@ -12,7 +12,7 @@
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> --> <!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
<Title>Jellyfin Media Bar Enhanced Plugin</Title> <Title>Jellyfin Media Bar Enhanced Plugin</Title>
<Authors>CodeDevMLH</Authors> <Authors>CodeDevMLH</Authors>
<Version>1.6.1.32</Version> <Version>1.6.2.0</Version>
<RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl> <RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl>
</PropertyGroup> </PropertyGroup>

View File

@@ -42,6 +42,9 @@ const CONFIG = {
enableVideoBackdrop: true, enableVideoBackdrop: true,
useSponsorBlock: true, useSponsorBlock: true,
preferLocalTrailers: false, preferLocalTrailers: false,
randomizeLocalTrailers: false,
preferLocalBackdrops: false,
randomizeThemeVideos: false,
waitForTrailerToEnd: true, waitForTrailerToEnd: true,
startMuted: true, startMuted: true,
fullWidthVideo: true, fullWidthVideo: true,
@@ -1371,20 +1374,68 @@ const ApiUtils = {
return null; return null;
} }
const trailers = await response.json(); const trailers = await response.json();
if (trailers && trailers.length > 0) { if (trailers && trailers.length > 0) {
const trailer = trailers[0];
const mediaSourceId = trailer.MediaSources && trailer.MediaSources[0] ? trailer.MediaSources[0].Id : trailer.Id; let trailer;
if (CONFIG.randomizeLocalTrailers && trailers.length > 1) {
// Return object with ID and URL const randomIndex = Math.floor(Math.random() * trailers.length);
return { trailer = trailers[randomIndex];
id: trailer.Id, console.log(`Using random local trailer (${randomIndex + 1}/${trailers.length}) for ${itemId}: ${trailer.Name}`);
url: `${STATE.jellyfinData.serverAddress}/Videos/${trailer.Id}/stream.mp4?Static=true&mediaSourceId=${mediaSourceId}&api_key=${STATE.jellyfinData.accessToken}` } else {
}; trailer = trailers[0];
}
const mediaSourceId = trailer.MediaSources && trailer.MediaSources[0] ? trailer.MediaSources[0].Id : trailer.Id;
return {
id: trailer.Id,
url: `${STATE.jellyfinData.serverAddress}/Videos/${trailer.Id}/stream.mp4?mediaSourceId=${mediaSourceId}&api_key=${STATE.jellyfinData.accessToken}`
};
}
return null;
} catch (error) {
console.error(`Error fetching local trailer for ${itemId}:`, error);
return null;
}
},
/**
* Fetches theme videos for an item
* @param {string} itemId - Item ID
* @returns {Promise<Object|null>} Theme video data object {id, url} or null
*/
async fetchThemeVideos(itemId) {
try {
const response = await fetch(
`${STATE.jellyfinData.serverAddress}/Items/${itemId}/ThemeVideos?userId=${STATE.jellyfinData.userId}`,
{ headers: this.getAuthHeaders() }
);
if (response.ok) {
const data = await response.json();
const items = Array.isArray(data) ? data : (data.Items || []);
if (items.length > 0) {
let video;
if (CONFIG.randomizeThemeVideos && items.length > 1) {
const randomIndex = Math.floor(Math.random() * items.length);
video = items[randomIndex];
console.log(`Found Theme Video (Random ${randomIndex + 1}/${items.length}) via ThemeVideos endpoint: ${video.Name} (${video.Id})`);
} else {
video = items[0];
console.log(`Found Theme Video (First) via ThemeVideos endpoint: ${video.Name} (${video.Id})`);
}
return {
id: video.Id,
url: `${STATE.jellyfinData.serverAddress}/Videos/${video.Id}/stream.mp4?api_key=${STATE.jellyfinData.accessToken}`
};
}
} }
return null; return null;
} catch (error) { } catch (error) {
console.error(`Error fetching local trailer for ${itemId}:`, error); console.error(`Error fetching theme videos for ${itemId}:`, error);
return null; return null;
} }
} }
@@ -1603,24 +1654,29 @@ const SlideCreator = {
trailerUrl = { trailerUrl = {
id: videoId, id: videoId,
url: `${STATE.jellyfinData.serverAddress}/Videos/${videoId}/stream.mp4?Static=true&api_key=${STATE.jellyfinData.accessToken}` url: `${STATE.jellyfinData.serverAddress}/Videos/${videoId}/stream.mp4?api_key=${STATE.jellyfinData.accessToken}`
}; };
} else { } else {
// Assume it's a standard URL (YouTube, etc.) // Assume it's a standard URL (YouTube, etc.)
trailerUrl = customValue; trailerUrl = customValue;
console.log(`Using custom trailer URL for ${itemId}: ${trailerUrl}`); console.log(`Using custom trailer URL for ${itemId}: ${trailerUrl}`);
} }
}
// 1b. Check Theme Video if preferred (Local Backdrop)
else if (CONFIG.preferLocalBackdrops && item.themeVideoUrl) {
trailerUrl = item.themeVideoUrl;
console.log(`Using theme video (local backdrop) for ${itemId}: ${trailerUrl.url || trailerUrl}`);
} }
// 1b. Check Local Trailer if preferred // 1c. Check Local Trailer if preferred
else if (CONFIG.preferLocalTrailers && item.LocalTrailerCount > 0 && item.localTrailerUrl) { else if (CONFIG.preferLocalTrailers && item.LocalTrailerCount > 0 && item.localTrailerUrl) {
trailerUrl = item.localTrailerUrl; trailerUrl = item.localTrailerUrl;
console.log(`Using local trailer for ${itemId}: ${trailerUrl}`); console.log(`Using local trailer for ${itemId}: ${trailerUrl}`);
} }
// 1c. Fallback to Remote Trailer // 1d. Fallback to Remote Trailer
else if (item.RemoteTrailers && item.RemoteTrailers.length > 0) { else if (item.RemoteTrailers && item.RemoteTrailers.length > 0) {
trailerUrl = item.RemoteTrailers[0].Url; trailerUrl = item.RemoteTrailers[0].Url;
} }
// 1d. Final Fallback to Local Trailer (even if not preferred) // 1e. Final Fallback to Local Trailer (even if not preferred)
else if (item.LocalTrailerCount > 0 && item.localTrailerUrl) { else if (item.LocalTrailerCount > 0 && item.localTrailerUrl) {
trailerUrl = item.localTrailerUrl; trailerUrl = item.localTrailerUrl;
console.log(`Using local trailer fallback for ${itemId}: ${trailerUrl}`); console.log(`Using local trailer fallback for ${itemId}: ${trailerUrl}`);
@@ -2170,6 +2226,11 @@ const SlideCreator = {
item.localTrailerUrl = await ApiUtils.fetchLocalTrailer(itemId); item.localTrailerUrl = await ApiUtils.fetchLocalTrailer(itemId);
} }
// Pre-fetch theme video URL if needed
if (CONFIG.preferLocalBackdrops) {
item.themeVideoUrl = await ApiUtils.fetchThemeVideos(itemId);
}
const slideElement = this.createSlideElement( const slideElement = this.createSlideElement(
item, item,
item.Type === "Movie" ? "Movie" : "TV Show" item.Type === "Movie" ? "Movie" : "TV Show"
@@ -2291,25 +2352,11 @@ const SlideshowManager = {
if (previousVisibleSlide) { if (previousVisibleSlide) {
previousVisibleSlide.classList.remove("active"); previousVisibleSlide.classList.remove("active");
// previousVisibleSlide.setAttribute("inert", "");
// previousVisibleSlide.setAttribute("tabindex", "-1");
} }
currentSlide.classList.add("active"); currentSlide.classList.add("active");
// // Update Play/Pause Button State if it was paused
// if (STATE.slideshow.isPaused) {
// STATE.slideshow.isPaused = false;
// const pauseButton = document.querySelector('.pause-button');
// if (pauseButton) {
// pauseButton.innerHTML = '<i class="material-icons">pause</i>';
// const pauseLabel = LocalizationUtils.getLocalizedString('ButtonPause', 'Pause');
// pauseButton.setAttribute("aria-label", pauseLabel);
// pauseButton.setAttribute("title", pauseLabel);
// }
// }
// Manage Video Playback: Stop others, Play current // Manage Video Playback: Stop others, Play current
// 1. Stop all other YouTube players and local video elements // 1. Stop all other YouTube players and local video elements
if (STATE.slideshow.videoPlayers) { if (STATE.slideshow.videoPlayers) {
Object.keys(STATE.slideshow.videoPlayers).forEach(id => { Object.keys(STATE.slideshow.videoPlayers).forEach(id => {

View File

@@ -20,11 +20,14 @@ This plugin is a fork and enhancement of the original [Media Bar by MakD](https:
- [Configuration](#configuration) - [Configuration](#configuration)
- [General Settings](#general-settings) - [General Settings](#general-settings)
- [Custom Content](#custom-content) - [Custom Content](#custom-content)
- [Content Sorting](#content-sorting)
- [Content Limits](#content-limits)
- [Advanced Settings](#advanced-settings) - [Advanced Settings](#advanced-settings)
- [Build The Plugin By Yourself](#build-the-plugin-by-yourself) - [Build The Plugin By Yourself](#build-the-plugin-by-yourself)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
- [Effects Not Showing](#effects-not-showing) - [Effects Not Showing](#effects-not-showing)
- [Docker Permission Issues](#docker-permission-issues) - [Docker Permission Issues](#docker-permission-issues)
- [Credits](#credits)
- [Contributing](#contributing) - [Contributing](#contributing)
--- ---
@@ -97,6 +100,9 @@ This plugin builds upon the original Media Bar with new capabilities and improve
<summary>Have a look:</summary> <summary>Have a look:</summary>
<img width="513" height="575" alt="Client-Settings" src="https://github.com/user-attachments/assets/3e29a84f-f8ea-4b7b-b561-80493cb1535b" /> <img width="513" height="575" alt="Client-Settings" src="https://github.com/user-attachments/assets/3e29a84f-f8ea-4b7b-b561-80493cb1535b" />
</details> </details>
* **Local Trailers Preference**: Option to prefer local trailers (from the media item) over online sources.
* **Content Sorting Options**: Sort content by various criteria such as PremiereDate, ProductionYear, Random, or Original order.
* **Client-Side Settings**: Allow users to override settings locally on their device.
### Core Features ### Core Features
* **Immersive Slideshow**: Rotates through your media library * **Immersive Slideshow**: Rotates through your media library
@@ -104,6 +110,7 @@ This plugin builds upon the original Media Bar with new capabilities and improve
* **Direct Play**: Click "Play" to start watching immediately * **Direct Play**: Click "Play" to start watching immediately
* **Details View**: Click "Info" to jump to the item's detail page * **Details View**: Click "Info" to jump to the item's detail page
* **Add To Favorites**: Click the heart to add the item to your favorites * **Add To Favorites**: Click the heart to add the item to your favorites
* **Customize**: Change the plugins behavior through the Jellyfin admin panel
## Installation ## Installation
@@ -271,6 +278,12 @@ volumes:
- /path/to/jellyfin/config/index.html:/jellyfin/jellyfin-web/index.html - /path/to/jellyfin/config/index.html:/jellyfin/jellyfin-web/index.html
``` ```
## Credits
This project is based on the original [Jellyfin Media Bar by MakD](https://github.com/MakD/Jellyfin-Media-Bar) and incorporates concepts from [IAmParadox27's plugin fork](https://github.com/IAmParadox27/jellyfin-plugin-media-bar). Thanks for their work!
Also, special thanks to IAmParadox27 for the [File Transformation plugin](https://github.com/IAmParadox27/jellyfin-plugin-file-transformation) which this plugin can optionally use for improved Docker compatibility.
## Contributing ## Contributing
Feel free to contribute to this project by creating pull requests or reporting issues. Feel free to contribute to this project by creating pull requests or reporting issues.

View File

@@ -8,13 +8,21 @@
"category": "General", "category": "General",
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png", "imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
"versions": [ "versions": [
{
"version": "1.6.2.0",
"changelog": "- feat: add options for local backdrops (theme videos) support and randomization features for local trailer/backdrops if more than 1 is available",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.2.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "ccc738317994a37d75db528ef03f9e25",
"timestamp": "2026-02-14T23:34:46Z"
},
{ {
"version": "1.6.1.32", "version": "1.6.1.32",
"changelog": "- fix tv mode issue\n- refactor video playback management", "changelog": "- feat: add seasonal UI logic\n- add option to also set the limits for custom ids\n- fix tv mode scroll issue\n- smaller fixes and improvements",
"targetAbi": "10.11.0.0", "targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.1.32/Jellyfin.Plugin.MediaBarEnhanced.zip", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.1.32/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "8d12099d8b1972412b6c300eeddc0c1b", "checksum": "e196fd393ef0bcf51f8ce535103f1811",
"timestamp": "2026-02-14T15:57:28Z" "timestamp": "2026-02-14T16:34:32Z"
}, },
{ {
"version": "1.6.0.2", "version": "1.6.0.2",

View File

@@ -0,0 +1,116 @@
(async () => {
// 1. Initialisierung
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("❌ ApiClient nicht gefunden. Bitte in der Browser-Konsole einer Jellyfin-Seite ausführen.");
return;
}
// 2. Item ID abfragen oder festlegen
let itemId = prompt("Bitte die Item ID eingeben (von einem Film/Serie mit Theme Video/Backdrop Video):");
if (!itemId) {
console.warn("Keine Item ID eingegeben. Abbruch.");
return;
}
itemId = itemId.trim();
const userId = apiClient.getCurrentUserId();
console.log(`%c🔍 Untersuche Item: ${itemId}`, "color: #00a4dc; font-size: 1.2em; font-weight: bold;");
// 3. Helper Funktion für Fetch requests
const fetchJson = async (url) => {
try {
const res = await fetch(url, {
headers: {
'Authorization': `MediaBrowser Client="Jellyfin Web", Device="Browser", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
}
});
if (res.ok) return await res.json();
console.warn(`⚠️ Request fehlgeschlagen: ${url} (${res.status})`);
return null;
} catch (e) {
console.error(`❌ Fehler bei Request: ${url}`, e);
return null;
}
};
// ---------------------------------------------------------
// TEST 1: Standard Item Details mit erweiterten Feldern
// ---------------------------------------------------------
console.group("1. Standard Item Details (mit Fields)");
const fields = "Overview,RemoteTrailers,MediaSources,LocalTrailerCount,ThemeVideoIds,ThemeSongIds";
const itemDetailsUrl = `${apiClient.serverAddress()}/Users/${userId}/Items/${itemId}?Fields=${fields}`;
const item = await fetchJson(itemDetailsUrl);
if (item) {
console.log("Name:", item.Name);
console.log("ThemeVideoIds:", item.ThemeVideoIds);
console.log("ThemeSongIds:", item.ThemeSongIds);
console.log("LocalTrailerCount:", item.LocalTrailerCount);
console.log("RemoteTrailers:", item.RemoteTrailers);
if (item.ThemeVideoIds && item.ThemeVideoIds.length > 0) {
console.log(`%c✅ ThemeVideoIds gefunden: ${item.ThemeVideoIds.length}`, "color: green; font-weight: bold;");
} else {
console.log(`%c❌ Keine ThemeVideoIds im Item-Objekt.`, "color: orange;");
}
}
console.groupEnd();
// ---------------------------------------------------------
// TEST 2: ThemeMedia Endpoint
// ---------------------------------------------------------
console.group("2. Endpoint: /Items/{Id}/ThemeMedia");
const themeMediaUrl = `${apiClient.serverAddress()}/Items/${itemId}/ThemeMedia?userId=${userId}`;
const themeMedia = await fetchJson(themeMediaUrl);
if (themeMedia) {
console.dir(themeMedia);
if (themeMedia.ThemeVideos && themeMedia.ThemeVideos.length > 0) {
console.log(`%c✅ ThemeVideos gefunden: ${themeMedia.ThemeVideos.length}`, "color: green; font-weight: bold;");
themeMedia.ThemeVideos.forEach(v => console.log(` - ID: ${v.Id}, Name: ${v.Name}, Path: ${v.Path}`));
} else {
console.log("❌ Keine ThemeVideos in ThemeMedia gefunden.");
}
}
console.groupEnd();
// ---------------------------------------------------------
// TEST 3: ThemeVideos Endpoint (Spezifisch)
// ---------------------------------------------------------
console.group("3. Endpoint: /Items/{Id}/ThemeVideos");
// Manchmal auch unter Users/{UserId}/Items/{Id}/ThemeVideos
const themeVideosUrl = `${apiClient.serverAddress()}/Items/${itemId}/ThemeVideos?userId=${userId}`;
const themeVideos = await fetchJson(themeVideosUrl);
if (themeVideos) {
// Kann Array oder Objekt mit Items sein
const videos = Array.isArray(themeVideos) ? themeVideos : (themeVideos.Items || []);
console.dir(videos);
if (videos.length > 0) {
console.log(`%c✅ ThemeVideos Endpoint lieferte Ergebnisse: ${videos.length}`, "color: green; font-weight: bold;");
} else {
console.log("❌ ThemeVideos Endpoint lieferte leeres Array.");
}
}
console.groupEnd();
// ---------------------------------------------------------
// TEST 4: LocalTrailers Endpoint
// ---------------------------------------------------------
console.group("4. Endpoint: /Items/{Id}/LocalTrailers");
const localTrailersUrl = `${apiClient.serverAddress()}/Users/${userId}/Items/${itemId}/LocalTrailers`;
const localTrailers = await fetchJson(localTrailersUrl);
if (localTrailers && localTrailers.length > 0) {
console.log(`%c LocalTrailers gefunden: ${localTrailers.length}`, "color: blue;");
console.dir(localTrailers);
} else {
console.log("❌ Keine LocalTrailers gefunden.");
}
console.groupEnd();
console.log("%cFertig.", "font-weight: bold;");
})();

View File

@@ -0,0 +1,37 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient nicht gefunden.");
return;
}
const itemId = "DEINE_ITEM_ID_HIER";
const userId = apiClient.getCurrentUserId();
const fields = "Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,ProductionYear,MediaSources,RunTimeTicks,LocalTrailerCount,ThemeVideoIds";
try {
console.log(`Rufe erweiterte Details für Item ${itemId} ab...`);
const url = apiClient.getUrl(`Users/${userId}/Items/${itemId}`, {
Fields: fields
});
const item = await apiClient.getJSON(url);
if (item) {
console.log(`%cErgebnis für: ${item.Name}`, "color: #00a4dc; font-weight: bold;");
console.log("Remote Trailer:", item.RemoteTrailers);
console.log("Local Trailer Count:", item.LocalTrailerCount);
console.log("Media Sources:", item.MediaSources);
console.log("ThemeVideos:", item.ThemeVideoIds);
console.dir(item);
} else {
console.warn("Item konnte nicht gefunden werden.");
}
} catch (error) {
console.error("Fehler beim Abrufen des Items:", error);
}
})();

3607
tmp.js

File diff suppressed because it is too large Load Diff