Compare commits

..

37 Commits

Author SHA1 Message Date
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
CodeDevMLH
cfebccc289 Update manifest.json for release v1.2.3.5 [skip ci] 2026-01-28 20:21:21 +00:00
CodeDevMLH
2613fd7acf Update version to 1.2.3.5 in project file and manifest.json
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m20s
2026-01-28 21:20:06 +01:00
CodeDevMLH
bb10b9c15e Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/jellyfin-plugin-media-bar-enhanced 2026-01-28 21:16:52 +01:00
CodeDevMLH
a9ad8d65e4 Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/jellyfin-plugin-media-bar-enhanced [skip-ci] 2026-01-28 02:15:28 +01:00
CodeDevMLH
e7232da38b Update manifest.json for release v1.2.3.4 [skip ci] 2026-01-28 01:09:39 +00:00
CodeDevMLH
53bd1d23c7 Update manifest.json for release v1.2.3.4 with changelog and source URL adjustments
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 57s
2026-01-28 02:07:25 +01:00
CodeDevMLH
5643ebfd28 Update manifest.json for release v1.2.3.3 [skip ci] 2026-01-28 01:05:46 +00:00
CodeDevMLH
bed2fbaa76 Bump version to 1.2.3.4 and update release automation to generate commit log
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 58s
2026-01-28 02:04:45 +01:00
CodeDevMLH
05f64bc3d7 Update manifest.json for release v1.2.3.3 [skip ci] 2026-01-28 00:45:20 +00:00
CodeDevMLH
277433581c Bump version to 1.2.3.3 and update manifest with changelog for button positioning adjustments
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 58s
2026-01-28 01:44:23 +01:00
CodeDevMLH
44f0b1cb38 Update manifest.json for release v1.2.3.2 [skip ci] 2026-01-28 00:30:37 +00:00
CodeDevMLH
d31dcbabdc Bump version to 1.2.3.2 and update manifest with changelog for button adjustments on small screens
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m1s
2026-01-28 01:29:42 +01:00
CodeDevMLH
4ed6509505 Update manifest.json for release v1.2.3.1 [skip ci] 2026-01-28 00:17:29 +00:00
CodeDevMLH
6427b8422a Bump version to 1.2.3.1 and update manifest with changelog for button layout adjustments on small screens
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-01-28 01:16:38 +01:00
CodeDevMLH
ebfbe1d563 Update manifest.json for release v1.2.3.0 [skip ci] 2026-01-27 23:54:43 +00:00
CodeDevMLH
67e5d8e4d2 Bump version to 1.2.3.0 and update manifest with changelog for button display fixes on small screens
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-01-28 00:53:50 +01:00
CodeDevMLH
d68ba7e846 Update manifest.json for release v1.2.2.0 [skip ci] 2026-01-24 22:53:55 +00:00
CodeDevMLH
0444cf333d Bump version to 1.2.2.0 and update manifest with changelog for visibility fixes
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m0s
2026-01-24 23:52:59 +01:00
CodeDevMLH
10279e97d9 Comment out CHANGELOG body in release automation workflow 2026-01-23 01:18:22 +01:00
CodeDevMLH
f6ccd0ea5f Update manifest.json for release v1.2.1.0 [skip ci] 2026-01-22 23:50:56 +00:00
CodeDevMLH
47827df047 Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 3.0.8 from original repo
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m0s
2026-01-23 00:50:01 +01:00
CodeDevMLH
7fd781c9d0 Update manifest.json for release v1.2.0.0 [skip ci] 2026-01-08 23:30:58 +00:00
CodeDevMLH
6faa8f1a4c Update plugin description in manifest.json for clarity and completeness
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 1m4s
2026-01-09 00:30:00 +01:00
CodeDevMLH
3e05ff1dc9 Update manifest.json for release v1.2.0.0 [skip ci] 2026-01-08 23:15:07 +00:00
CodeDevMLH
fa06179cd3 Update manifest.json to enhance plugin description and overview
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 55s
2026-01-09 00:14:16 +01:00
CodeDevMLH
d8327fc5c9 Update manifest.json for release v1.2.0.0 [skip ci] 2026-01-08 22:16:05 +00:00
CodeDevMLH
9ffe03f0df Update version to 1.2.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 57s
2026-01-08 23:15:13 +01:00
CodeDevMLH
37e99d7fed Add Preferred Video Quality setting and update playback quality logic 2026-01-08 23:14:59 +01:00
9 changed files with 313 additions and 51 deletions

View File

@@ -24,6 +24,8 @@ jobs:
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v5
@@ -94,16 +96,52 @@ jobs:
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]" commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
file_pattern: manifest.json file_pattern: manifest.json
- name: Generate Commit Log
if: success()
shell: bash
run: |
echo "Generating commit log since last tag..."
# Get the previous tag
PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
echo "No previous tag found. Getting all commits."
start_range=""
else
echo "Previous tag: $PREV_TAG"
start_range="$PREV_TAG.."
fi
echo "### What's Changed" > commit_log.md
echo "" >> commit_log.md
git log --pretty=format:"- %s (%h) by @%an" $start_range >> commit_log.md
# Combine Changelog from manifest (if exists) and commit log
if [ -n "${{ env.CHANGELOG }}" ]; then
echo "${{ env.CHANGELOG }}" > release_body.txt
echo "" >> release_body.txt
echo "" >> release_body.txt
cat commit_log.md >> release_body.txt
else
cat commit_log.md > release_body.txt
fi
# Debug output
cat release_body.txt
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
tag_name: "v${{ env.VERSION }}" tag_name: "v${{ env.VERSION }}"
name: "v${{ env.VERSION }}" name: "v${{ env.VERSION }}"
body: ${{ env.CHANGELOG }} # body: ${{ env.CHANGELOG }}
files: ${{ env.ZIP_PATH }} files: ${{ env.ZIP_PATH }}
draft: false draft: false
prerelease: false prerelease: false
generate_release_notes: true generate_release_notes: false
body_path: release_body.txt
# Update Message in Remote Repository # Update Message in Remote Repository
- name: Checkout Central Manifest Repo - name: Checkout Central Manifest Repo

View File

@@ -31,6 +31,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
public bool AlwaysShowArrows { get; set; } = false; public bool AlwaysShowArrows { get; set; } = false;
public string CustomMediaIds { get; set; } = ""; public string CustomMediaIds { get; set; } = "";
public bool EnableCustomMediaIds { get; set; } = true; public bool EnableCustomMediaIds { get; set; } = true;
public string PreferredVideoQuality { get; set; } = "Auto";
public bool EnableSeasonalContent { get; set; } = false; public bool EnableSeasonalContent { get; set; } = false;
public bool IsEnabled { get; set; } = true; public bool IsEnabled { get; set; } = true;
} }

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,25 @@
<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>
<b>Manual Trailer Override:</b> You can specify a YouTube URL for an item by adding it in
brackets: <code>ID [https://youtu.be/...]</code>
<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 (-) (e.g. <code>ID | Description</code>).
Note: The separator MUST NOT be a hex character (0-9, a-f).</div> <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.
<b>Note:</b> The separator MUST NOT 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>
@@ -156,6 +161,18 @@
</label> </label>
<div class="fieldDescription">Skip intro/outro segments in YouTube trailers.</div> <div class="fieldDescription">Skip intro/outro segments in YouTube trailers.</div>
</div> </div>
<div class="selectContainer">
<label class="selectLabel" for="PreferredVideoQuality">Preferred YouTube Quality</label>
<select is="emby-select" id="PreferredVideoQuality" name="PreferredVideoQuality"
class="emby-select-withcolor emby-select">
<option value="Auto">Auto (Smart)</option>
<option value="Maximum">Maximum (4K+)</option>
<option value="1080p">1080p</option>
<option value="720p">720p</option>
</select>
<div class="fieldDescription">"Auto" selects Maximum if screen width > 1920px, otherwise
1080p.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="StartMuted" name="StartMuted" /> <input is="emby-checkbox" type="checkbox" id="StartMuted" name="StartMuted" />

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.1.2.0</Version> <Version>1.3.0.1</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.6 * 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:
@@ -41,11 +41,11 @@
@keyframes kenBurnsZoomIn { @keyframes kenBurnsZoomIn {
from { from {
transform: scale(1); transform: scale3d(1, 1, 0);
} }
to { to {
transform: scale(1.1); transform: scale3d(1.1, 1.1, 0.1);
} }
} }
@@ -128,6 +128,10 @@
transition: width 0.2s ease-in-out; transition: width 0.2s ease-in-out;
} }
.layout-mobile .splashLogo {
height: 12%;
}
.backdrop.low-quality { .backdrop.low-quality {
filter: blur(0.5px); filter: blur(0.5px);
transform: scale(1.01); transform: scale(1.01);
@@ -166,6 +170,12 @@
height: 90%; height: 90%;
overflow: hidden; overflow: hidden;
margin: 0 auto; margin: 0 auto;
pointer-events: auto;
}
#slides-container[style*="display: none"],
#slides-container[style*="visibility: hidden"] {
pointer-events: none !important;
} }
.arrow { .arrow {
@@ -780,6 +790,41 @@
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
@media (max-width: 400px) {
.button-container {
gap: 10px;
}
.play-button,
.trailer-button {
padding: 8px 14px;
}
.misc-info {
flex-wrap: wrap;
justify-content: center;
gap: 0px 10px;
}
.runTime {
width: 100%;
justify-content: center;
margin-top: 0.5vh;
}
.misc-info .separator-icon:last-of-type {
display: none;
}
.genre {
top: calc(50% + 16vh);
}
.button-container {
top: calc(50% + 27vh);
}
}
} }
/*Landscape Mode Phones*/ /*Landscape Mode Phones*/

View File

@@ -1,5 +1,5 @@
/* /*
* Jellyfin Slideshow by M0RPH3US v3.0.6 * 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:
@@ -42,6 +42,7 @@ const CONFIG = {
fullWidthVideo: true, fullWidthVideo: true,
enableMobileVideo: false, enableMobileVideo: false,
showTrailerButton: true, showTrailerButton: true,
preferredVideoQuality: "Auto",
enableKeyboardControls: true, enableKeyboardControls: true,
alwaysShowArrows: false, alwaysShowArrows: false,
enableCustomMediaIds: true, enableCustomMediaIds: true,
@@ -77,6 +78,7 @@ const STATE = {
videoPlayers: {}, videoPlayers: {},
sponsorBlockInterval: null, sponsorBlockInterval: null,
isMuted: CONFIG.startMuted, isMuted: CONFIG.startMuted,
customTrailerUrls: {},
}, },
}; };
@@ -252,31 +254,53 @@ const initLoadingScreen = () => {
const checkInterval = setInterval(() => { const checkInterval = setInterval(() => {
const loginFormLoaded = document.querySelector(".manualLoginForm"); const loginFormLoaded = document.querySelector(".manualLoginForm");
const homePageLoaded = const activeTab = document.querySelector(".pageTabContent.is-active");
document.querySelector(".homeSectionsContainer") &&
document.querySelector("#slides-container");
if (loginFormLoaded || homePageLoaded) { if (loginFormLoaded) {
clearInterval(progressInterval); finishLoading();
clearInterval(checkInterval); return;
}
progressBar.style.transition = "width 300ms ease-in-out"; if (activeTab) {
progressBar.style.width = "100%"; const tabIndex = activeTab.getAttribute("data-index");
unfilledBar.style.width = "0%";
progressBar.addEventListener('transitionend', () => { if (tabIndex === "0") {
requestAnimationFrame(() => { const homeSections = document.querySelector(".homeSectionsContainer");
const loader = document.querySelector(".bar-loading"); const slidesContainer = document.querySelector("#slides-container");
if (loader) {
loader.style.opacity = '0'; if (homeSections && slidesContainer) {
setTimeout(() => { finishLoading();
loader.remove(); }
}, 300); } else {
} if (
}); activeTab.children.length > 0 ||
}) activeTab.innerText.trim().length > 0
) {
finishLoading();
}
}
} }
}, CONFIG.loadingCheckInterval); }, CONFIG.loadingCheckInterval);
const finishLoading = () => {
clearInterval(progressInterval);
clearInterval(checkInterval);
progressBar.style.transition = "width 300ms ease-in-out";
progressBar.style.width = "100%";
unfilledBar.style.width = "0%";
progressBar.addEventListener("transitionend", () => {
requestAnimationFrame(() => {
const loader = document.querySelector(".bar-loading");
if (loader) {
loader.style.opacity = "0";
setTimeout(() => {
loader.remove();
}, 300);
}
});
});
};
}; };
/** /**
@@ -321,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;
}; };
@@ -820,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();
const replaceEscaped = (text) =>
text.replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\\\/g, '\\').replace(/\\'/g, "'");
// 1. Try to remove start and end wrappers first
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);
return;
} 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\(['"](.*?)['"]\)/); let jsonMatch = chunkText.match(/JSON\.parse\(['"](.*?)['"]\)/);
if (jsonMatch) { if (jsonMatch) {
let jsonString = jsonMatch[1]
.replace(/\\"/g, '"')
.replace(/\\n/g, '\n')
.replace(/\\\\/g, '\\')
.replace(/\\'/g, "'");
try { try {
const jsonString = replaceEscaped(jsonMatch[1]);
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 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);
} }
@@ -1252,9 +1312,12 @@ const VisibilityObserver = {
const isVisible = const isVisible =
(window.location.hash === "#/home.html" || (window.location.hash === "#/home.html" ||
window.location.hash === "#/home") && window.location.hash === "#/home") &&
activeTab &&
activeTab.getAttribute("data-index") === "0"; activeTab.getAttribute("data-index") === "0";
container.style.display = isVisible ? "block" : "none"; container.style.display = isVisible ? "block" : "none";
container.style.visibility = isVisible ? "visible" : "hidden";
container.style.pointerEvents = isVisible ? "auto" : "none";
if (isVisible) { if (isVisible) {
if (STATE.slideshow.slideInterval && !STATE.slideshow.isPaused) { if (STATE.slideshow.slideInterval && !STATE.slideshow.isPaused) {
@@ -1273,11 +1336,11 @@ const VisibilityObserver = {
* Initializes visibility observer * Initializes visibility observer
*/ */
init() { init() {
const observer = new MutationObserver(this.updateVisibility); const observer = new MutationObserver(() => this.updateVisibility());
observer.observe(document.body, { childList: true, subtree: true }); observer.observe(document.body, { childList: true, subtree: true });
document.body.addEventListener("click", this.updateVisibility); document.body.addEventListener("click", () => this.updateVisibility());
window.addEventListener("hashchange", this.updateVisibility); window.addEventListener("hashchange", () => this.updateVisibility());
this.updateVisibility(); this.updateVisibility();
}, },
@@ -1368,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;
} }
@@ -1417,6 +1484,21 @@ const SlideCreator = {
loop: 0 loop: 0
}; };
// Determine video quality
let quality = 'hd1080';
if (CONFIG.preferredVideoQuality === 'Maximum') {
quality = 'highres';
} else if (CONFIG.preferredVideoQuality === '720p') {
quality = 'hd720';
} else if (CONFIG.preferredVideoQuality === '1080p') {
quality = 'hd1080';
} else { // Auto or fallback
// If screen is wider than 1920, prefer highres, otherwise 1080p
quality = window.screen.width > 1920 ? 'highres' : 'hd1080';
}
playerVars.suggestedQuality = quality;
// Apply SponsorBlock start/end times // Apply SponsorBlock start/end times
if (segments.intro) { if (segments.intro) {
playerVars.start = Math.ceil(segments.intro[1]); playerVars.start = Math.ceil(segments.intro[1]);
@@ -1446,6 +1528,10 @@ const SlideCreator = {
event.target.setVolume(40); event.target.setVolume(40);
} }
if (typeof event.target.setPlaybackQuality === 'function') {
event.target.setPlaybackQuality(quality);
}
// Only play if this is the active slide // Only play if this is the active slide
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`); const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
if (slide && slide.classList.contains('active')) { if (slide && slide.classList.contains('active')) {
@@ -1494,6 +1580,7 @@ const SlideCreator = {
className: "backdrop video-backdrop", className: "backdrop video-backdrop",
src: trailerUrl, src: trailerUrl,
autoplay: false, autoplay: false,
preload: "auto",
loop: false, loop: false,
style: "object-fit: cover; width: 100%; height: 100%; pointer-events: none;" style: "object-fit: cover; width: 100%; height: 100%; pointer-events: none;"
}; };
@@ -2494,10 +2581,25 @@ 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();
} }
@@ -2554,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

@@ -27,7 +27,8 @@ Setzt deine Commits neu auf die Spitze eines anderen Branches. Die Commit-IDs we
```bash ```bash
git checkout dev git checkout dev
git rebase main git fetch origin
git rebase origin/main
``` ```
| Details | | | Details | |

View File

@@ -2,12 +2,52 @@
{ {
"guid": "d7e11d57-819b-4bdd-a88d-53c5f5560225", "guid": "d7e11d57-819b-4bdd-a88d-53c5f5560225",
"name": "Media Bar Enhanced", "name": "Media Bar Enhanced",
"description": "A jellyfin plugin to display a media bar (featured content) for jellyfin web.", "description": "A feature-rich fork of the original Media Bar script by MakD that brings your home screen to life.\n\n-> 100% Configurable via Web UI: Manage all features, lists, and settings effortlessly through the plugin configuration page.\n\nKey Highlights:\n- Cinematic Video Backdrops: Supports local & YouTube trailers (incl. SponsorBlock)\n- Custom Content: Curate your slideshow with specific Collections, Playlists, or seasonal events\n\nAdditional Features:\n- Full-width immersive mode\n- Smart resolution handling (up to 4K)\n- Full keyboard navigation & playback control\n- Wait-for-trailer options\n- Customizable pagination & animations\n\nIf you do not have write permissions to the web folder, please also install the file-transformation plugin.",
"overview": "Media Bar for Jellyfin", "overview": "Transforms your Jellyfin home screen with an immersive, fully configurable media slideshow featuring video backdrops.",
"owner": "CodeDevMLH", "owner": "CodeDevMLH",
"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.3.0.1",
"changelog": "- feat: Enhance custom media ID functionality with manual trailer override support",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.3.0.1/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "5a4f555e29c733dabd51169f6ace56eb",
"timestamp": "2026-02-04T01:14:19Z"
},
{
"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",
"changelog": "- Fixes issues with persistent slides-container visibility",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.2.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "3362f93815845c4e85b66b31bcd0f52c",
"timestamp": "2026-01-24T22:53:55Z"
},
{
"version": "1.2.1.0",
"changelog": "- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 3.0.8 from original repo",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.1.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "70defc1fb29a17ff4c9362bf7bdc53b5",
"timestamp": "2026-01-22T23:50:56Z"
},
{
"version": "1.2.0.0",
"changelog": "- Add video quality preference setting (Auto / 1080p / Highres)\n- Set preferred video quality on YouTube player based on setting",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "0b6379f68990026240d97fe8f77fbef1",
"timestamp": "2026-01-08T23:30:58Z"
},
{ {
"version": "1.1.2.0", "version": "1.1.2.0",
"changelog": "- Add method to resume video playback when slideshow is active", "changelog": "- Add method to resume video playback when slideshow is active",