Compare commits

...

15 Commits

Author SHA1 Message Date
CodeDevMLH
623e1d55b2 Update manifest.json for release v1.3.0.3 [skip ci] 2026-02-04 01:41:36 +00:00
CodeDevMLH
54b522101d Update configPage.html to emphasize note on item ID resolution feedback
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 02:40:47 +01:00
CodeDevMLH
dc92e8adba Bump version to 1.3.0.3
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-02-04 02:39:31 +01:00
CodeDevMLH
9de0d60bb8 Update configPage.html to clarify manual trailer override instructions 2026-02-04 02:39:07 +01:00
CodeDevMLH
5cd1f020fd Update manifest.json for release v1.3.0.2 [skip ci] 2026-02-04 01:27:56 +00:00
CodeDevMLH
1285d79538 Bump version to 1.3.0.2
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-02-04 02:27:04 +01:00
CodeDevMLH
05128ebc7e Update manifest.json for release v1.3.0.1 [skip ci] 2026-02-04 01:14:20 +00:00
CodeDevMLH
3bad812e96 Bump version to 1.3.0.1
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 02:13:29 +01:00
CodeDevMLH
1872a744a7 Update manifest.json for release v1.3.0.0 [skip ci] 2026-02-04 00:07:16 +00:00
CodeDevMLH
83441a3c4f Bump version to 1.3.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m0s
2026-02-04 01:06:22 +01:00
CodeDevMLH
49ad41c3f6 Enhance custom media ID functionality with manual trailer override 2026-02-04 01:06:12 +01:00
CodeDevMLH
984a41b180 Update manifest.json for release v1.2.3.7 [skip ci] 2026-01-28 22:39:54 +00:00
CodeDevMLH
c8421d12b0 Bump version to 1.2.3.7 and update changelog for release
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 57s
2026-01-28 23:39:03 +01:00
CodeDevMLH
b5d38629d2 Update manifest.json for release v1.2.3.6 [skip ci] 2026-01-28 21:31:54 +00:00
CodeDevMLH
8f4adece49 Bump version to 1.2.3.6 and update changelog for release
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 58s
2026-01-28 22:31:01 +01:00
6 changed files with 118 additions and 29 deletions

View File

@@ -16,7 +16,7 @@
<a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;" <a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;"
target="_blank" href="https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced"> target="_blank" href="https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced">
<i class="md-icon button-icon button-icon-left secondaryText"></i> <i class="md-icon button-icon button-icon-left secondaryText"></i>
<span>Help</span> <span>${Help}</span>
</a> </a>
</div> </div>
<hr style="max-width: 800px; margin: 1em 0;"> <hr style="max-width: 800px; margin: 1em 0;">
@@ -109,20 +109,27 @@
<div class="inputContainer"> <div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="CustomMediaIds">Media/Collection/Playlist <label class="inputLabel inputLabelUnfocused" for="CustomMediaIds">Media/Collection/Playlist
IDs IDs
(Comma or Newline separated)</label> (Newline or Comma separated)</label>
<textarea is="emby-textarea" id="CustomMediaIds" name="CustomMediaIds" <textarea is="emby-textarea" id="CustomMediaIds" name="CustomMediaIds"
style="width: 100%; height: 150px; font-family: monospace;"></textarea> style="width: 100%; height: 150px; font-family: monospace;"></textarea>
<div class="fieldDescription" id="customMediaIdsDesc">Enter the IDs of the items you want to <div class="fieldDescription" id="customMediaIdsDesc">Enter the IDs of the items you want to show in the slideshow.
show in the slideshow. You can separate them by new line or comma.
You can separate them by comma or new line. <br><br>
<b>Manual Trailer Override:</b> You can specify a YouTube URL for an item by adding it in
brackets: <br> <code>e.g. ID DESCRIPTION [https://youtu.be/...]</code> or <code>ID [https://youtu.be/...] DESCRIPTION</code>
<br><br>
You can also add a description after the ID using any separator like space, pipe You can also add a description after the ID using any separator like space, pipe
(|) or dash (-) (e.g. <code>ID | Description</code>). (|) or dash (-): <br>e.g. <code>ID DESCRIPTION</code> or <code>ID | DESCRIPTION</code>
Note: The separator MUST NOT be a hex character (0-9, a-f).</div> <br><br>
<b>Note:</b> If using a <b>Collection Name</b> (instead of an ID) combined with a description, you <b>MUST</b> use
the pipe (|) separator.
<br>
<b>Note:</b> The separator <b>MUST NOT</b> be a hex character (0-9, a-f).</div>
<div class="fieldDescription" id="seasonalMediaIdsDesc" style="display: none;"> <div class="fieldDescription" id="seasonalMediaIdsDesc" style="display: none;">
<b>Seasonal Mode Enabled:</b> Define lines with date ranges (Format: DD.MM-DD.MM | <b>Seasonal Mode Enabled:</b> Define lines with date ranges (Format: DD.MM-DD.MM |
<i>name</i> | <i>IDs</i>).<br> <i>name</i> | <i>IDs</i>).<br>
Example:<br> Example:<br>
<code>20.10-31.10 | Halloween | ID1, ID2</code><br> <code>20.10-31.10 | Halloween | ID1, ID2 [https://youtu.be/...]</code><br>
<code>01.12-26.12 | Christmas | ID3, ID4</code><br> <code>01.12-26.12 | Christmas | ID3, ID4</code><br>
<i>Only lines matching the current date will be used. If no line matches, it will try to <i>Only lines matching the current date will be used. If no line matches, it will try to
fetch the list.txt or use random items.</i> fetch the list.txt or use random items.</i>
@@ -131,7 +138,7 @@
Example: Example:
<code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br><br> <code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br><br>
You can also insert a name of a collection or playlist to fetch the IDs of all items in You can also insert a name of a collection or playlist to fetch the IDs of all items in
it (will take the first hit.<br>Note: there is currently no feedback if the name it (will take the first hit.<br><b>Note:</b> there is currently no feedback if the name
resolution succeeded, you will have to look if the bar displays the correct items.). resolution succeeded, you will have to look if the bar displays the correct items.).
</p> </p>
</div> </div>

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.2.3.5</Version> <Version>1.3.0.3</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

@@ -1,5 +1,5 @@
/* /*
* Jellyfin Slideshow by M0RPH3US v3.0.8 * Jellyfin Slideshow by M0RPH3US v3.0.9
* Modified by CodeDevMLH v1.1.0.0 * Modified by CodeDevMLH v1.1.0.0
* *
* New features: * New features:
@@ -810,7 +810,7 @@
.runTime { .runTime {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
margin-top: 0; margin-top: 0.5vh;
} }
.misc-info .separator-icon:last-of-type { .misc-info .separator-icon:last-of-type {

View File

@@ -1,5 +1,5 @@
/* /*
* Jellyfin Slideshow by M0RPH3US v3.0.8 * Jellyfin Slideshow by M0RPH3US v3.0.9
* Modified by CodeDevMLH v1.1.0.0 * Modified by CodeDevMLH v1.1.0.0
* *
* New features: * New features:
@@ -78,6 +78,7 @@ const STATE = {
videoPlayers: {}, videoPlayers: {},
sponsorBlockInterval: null, sponsorBlockInterval: null,
isMuted: CONFIG.startMuted, isMuted: CONFIG.startMuted,
customTrailerUrls: {},
}, },
}; };
@@ -344,6 +345,7 @@ const resetSlideshowState = () => {
STATE.slideshow.itemIds = []; STATE.slideshow.itemIds = [];
STATE.slideshow.loadedItems = {}; STATE.slideshow.loadedItems = {};
STATE.slideshow.createdSlides = {}; STATE.slideshow.createdSlides = {};
STATE.slideshow.customTrailerUrls = {};
STATE.slideshow.totalItems = 0; STATE.slideshow.totalItems = 0;
STATE.slideshow.isLoading = false; STATE.slideshow.isLoading = false;
}; };
@@ -843,29 +845,64 @@ const LocalizationUtils = {
throw new Error(`Failed to fetch translations: ${response.statusText}`); throw new Error(`Failed to fetch translations: ${response.statusText}`);
} }
/**
* @example
* Standard version
* ```js
* "use strict";
* (self.webpackChunk = self.webpackChunk || []).push([[62634], {
* 30985: function(e) {
* e.exports = JSON.parse('{"Absolute":"..."}')
* }
* }]);
* ```
*
* Minified version
* ```js
* "use strict";(self.webpackChunk=self.webpackChunk||[]).push([[24072],{60715:function(e){e.exports=JSON.parse('{"Absolute":"..."}')}}]);
* ```
*/
const chunkText = await response.text(); const chunkText = await response.text();
let jsonMatch = chunkText.match(/JSON\.parse\(['"](.*?)['"]\)/); const replaceEscaped = (text) =>
if (jsonMatch) { text.replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\\\/g, '\\').replace(/\\'/g, "'");
let jsonString = jsonMatch[1]
.replace(/\\"/g, '"') // 1. Try to remove start and end wrappers first
.replace(/\\n/g, '\n')
.replace(/\\\\/g, '\\')
.replace(/\\'/g, "'");
try { try {
// Matches from start of file to the beginning of JSON.parse('
const START = /^(.*)JSON\.parse\(['"]/gms;
// Matches from the end of the JSON string to the end of the file
const END = /['"]?\)?\s*}?(\r\n|\r|\n)?}?]?\)?;(\r\n|\r|\n)?$/gms;
const jsonString = replaceEscaped(chunkText.replace(START, '').replace(END, ''));
this.translations[locale] = JSON.parse(jsonString); this.translations[locale] = JSON.parse(jsonString);
return; return;
} catch (e) { } catch (e) {
console.error('Failed to parse JSON from standard extraction.');
// Try alternative extraction below
}
// 2. Try to extract only the JSON string directly
let jsonMatch = chunkText.match(/JSON\.parse\(['"](.*?)['"]\)/);
if (jsonMatch) {
try {
const jsonString = replaceEscaped(jsonMatch[1]);
this.translations[locale] = JSON.parse(jsonString);
return;
} catch (e) {
console.error('Failed to parse JSON from direct extraction.');
// Try direct extraction // Try direct extraction
} }
} }
// 3. Fallback: extract everything between the first { and the last }
const jsonStart = chunkText.indexOf('{'); const jsonStart = chunkText.indexOf('{');
const jsonEnd = chunkText.lastIndexOf('}') + 1; const jsonEnd = chunkText.lastIndexOf('}') + 1;
if (jsonStart !== -1 && jsonEnd > jsonStart) { if (jsonStart !== -1 && jsonEnd > jsonStart) {
const jsonString = chunkText.substring(jsonStart, jsonEnd); const jsonString = chunkText.substring(jsonStart, jsonEnd);
try { try {
this.translations[locale] = JSON.parse(jsonString); this.translations[locale] = JSON.parse(jsonString);
return;
} catch (e) { } catch (e) {
console.error("Failed to parse JSON from chunk:", e); console.error("Failed to parse JSON from chunk:", e);
} }
@@ -1394,7 +1431,11 @@ const SlideCreator = {
let trailerUrl = null; let trailerUrl = null;
// 1. Check for Remote Trailers (YouTube) // 1. Check for Remote Trailers (YouTube)
if (item.RemoteTrailers && item.RemoteTrailers.length > 0) { // Priority: Custom Config URL > Metadata RemoteTrailer
if (STATE.slideshow.customTrailerUrls && STATE.slideshow.customTrailerUrls[itemId]) {
trailerUrl = STATE.slideshow.customTrailerUrls[itemId];
console.log(`Using custom trailer URL for ${itemId}: ${trailerUrl}`);
} else if (item.RemoteTrailers && item.RemoteTrailers.length > 0) {
trailerUrl = item.RemoteTrailers[0].Url; trailerUrl = item.RemoteTrailers[0].Url;
} }
@@ -2541,9 +2582,24 @@ const SlideshowManager = {
parseCustomIds() { parseCustomIds() {
if (!CONFIG.enableSeasonalContent) { if (!CONFIG.enableSeasonalContent) {
return CONFIG.customMediaIds return CONFIG.customMediaIds
.split(/[\n,]/) // Split by comma or newline .split(/[\n,]/).map((line) => {
.map((id) => id.trim()) // Remove whitespace const urlMatch = line.match(/\[(.*?)\]/);
.filter((id) => id); // Remove empty strings let id = line;
if (urlMatch) {
const url = urlMatch[1];
id = line.replace(/\[.*?\]/, '').trim();
const guidMatch = id.match(/([0-9a-f]{32})/i);
if (guidMatch) {
id = guidMatch[1];
} else {
id = id.split('|')[0].trim();
}
STATE.slideshow.customTrailerUrls[id] = url;
}
return id.trim();
})
.map((id) => id.trim())
.filter((id) => id);
} else { } else {
return this.parseSeasonalIds(); return this.parseSeasonalIds();
} }
@@ -2600,7 +2656,22 @@ const SlideshowManager = {
if (isInRange) { if (isInRange) {
console.log(`Seasonal match found: ${line}`); console.log(`Seasonal match found: ${line}`);
const ids = idsPart.split(/[,]/).map(id => id.trim()).filter(id => id); const ids = idsPart.split(/[,]/).map(line => {
const urlMatch = line.match(/\[(.*?)\]/);
let id = line;
if (urlMatch) {
const url = urlMatch[1];
id = line.replace(/\[.*?\]/, '').trim();
const guidMatch = id.match(/([0-9a-f]{32})/i);
if (guidMatch) {
id = guidMatch[1];
} else {
id = id.split('|')[0].trim();
}
STATE.slideshow.customTrailerUrls[id] = url;
}
return id.trim();
}).filter(id => id);
rawIds.push(...ids); rawIds.push(...ids);
} }
} }

View File

@@ -144,6 +144,9 @@ Configure the plugin via **Dashboard** > **Plugins** > **Media Bar Enhanced**.
Define exactly what shows up in your bar. Define exactly what shows up in your bar.
* **Enable Custom Media IDs**: Restrict the slideshow to a specific list of IDs. * **Enable Custom Media IDs**: Restrict the slideshow to a specific list of IDs.
* **Manual Trailer Override**: Add `[YouTube_URL]` after an ID to force a specific trailer.
* Example ID: `a1b2c3d4e5... [https://www.youtube.com/watch?v=VIDEO_ID]`
* Example Collection Name: `Halloween Collection [https://...] | My Description` (Note: Use `|` to separate description from name if using a name instead of an ID)
* **Enable Seasonal Content Mode**: Advanced date-based scheduling. * **Enable Seasonal Content Mode**: Advanced date-based scheduling.
* Format: `DD.MM-DD.MM | Name | ID1, ID2, ID3` * Format: `DD.MM-DD.MM | Name | ID1, ID2, ID3`
* Example: `20.10-31.10 | Halloween | <ID_OF_HALLOWEEN_COLLECTION>` * Example: `20.10-31.10 | Halloween | <ID_OF_HALLOWEEN_COLLECTION>`

View File

@@ -9,12 +9,20 @@
"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.2.3.5", "version": "1.3.0.3",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.", "changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",
"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.2.3.5/Jellyfin.Plugin.MediaBarEnhanced.zip", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.3.0.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "b5efea79ec465522dad31e4ee5f1710c", "checksum": "1d9e0a8342d46f84aed3f7bd1bee32d3",
"timestamp": "2026-01-28T20:21:20Z" "timestamp": "2026-02-04T01:41:35Z"
},
{
"version": "1.2.3.7",
"changelog": "- Fixes the issue where buttons were cut off on smaller screens such as on S24/S25.\n- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 3.0.9 from original repo",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.3.7/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "fa1bf48cff159cc7dbf0aab48511a37c",
"timestamp": "2026-01-28T22:39:54Z"
}, },
{ {
"version": "1.2.2.0", "version": "1.2.2.0",