Compare commits

..

35 Commits

Author SHA1 Message Date
CodeDevMLH
2030538acc Update manifest.json for release v1.5.0.5 [skip ci] 2026-02-08 02:20:04 +00:00
CodeDevMLH
463e9ef424 Bump version to 1.5.0.5; update keyboard controls and changelog
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-08 03:19:12 +01:00
CodeDevMLH
dbe9ce97d2 Update manifest.json for release v1.5.0.4 [skip ci] 2026-02-08 01:32:40 +00:00
CodeDevMLH
45c780c018 Bump version to 1.5.0.4 and update changelog for mediaBarEnhanced.js and manifest.json; enhance keyboard navigation controls
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-08 02:31:49 +01:00
CodeDevMLH
9de268caa7 , 2026-02-08 02:28:06 +01:00
CodeDevMLH
a8298461f9 Update manifest.json for release v1.5.0.3 [skip ci] 2026-02-08 00:41:45 +00:00
CodeDevMLH
aa369e5c7b Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/jellyfin-plugin-media-bar-enhanced
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-08 01:40:53 +01:00
CodeDevMLH
f62dc853be Bump version to 1.5.0.3 and update changelog for mediaBarEnhanced.js and manifest.json; improve keyboard controls in TV mode 2026-02-08 01:40:50 +01:00
CodeDevMLH
d0a7f5da8c Update manifest.json for release v1.5.0.2 [skip ci] 2026-02-08 00:10:29 +00:00
CodeDevMLH
1f140fd3c1 Bump version to 1.5.0.2 and update changelog for mediaBarEnhanced.js and manifest.json; enhance keyboard controls in TV mode
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-08 01:09:38 +01:00
CodeDevMLH
18474d4537 Update manifest.json for release v1.5.0.1 [skip ci] 2026-02-08 00:00:01 +00:00
CodeDevMLH
839424a960 Bump version to 1.5.0.1 and update changelog for mediaBarEnhanced.js and manifest.json; fix keyboard controls in TV mode
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 57s
2026-02-08 00:59:04 +01:00
CodeDevMLH
8050aa8e76 Update manifest.json for release v1.5.0.0 [skip ci] 2026-02-07 23:50:07 +00:00
CodeDevMLH
0c173b9685 Bump version to 1.5.0.0 and update changelog for mediaBarEnhanced.js and mediaBarEnhanced.css
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-02-08 00:49:12 +01:00
CodeDevMLH
90ecc7c0ef add without list.txt 2026-02-08 00:48:48 +01:00
CodeDevMLH
c80d3726a6 Enhance README.md with new features: added custom trailer URL option, client settings, and updated client platform support details. 2026-02-04 21:11:52 +01:00
CodeDevMLH
a8e63c935f Comment out seasonal manifest update steps in release automation workflow [skip ci] 2026-02-04 19:47:03 +01:00
CodeDevMLH
d931a5d589 Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/jellyfin-plugin-media-bar-enhanced 2026-02-04 19:08:14 +01:00
CodeDevMLH
629f13ab58 Comment out seasonal manifest update steps in release automation workflow 2026-02-04 19:08:13 +01:00
CodeDevMLH
6a91784401 Update manifest.json for release v1.4.0.12 [skip ci] 2026-02-04 18:07:40 +00:00
CodeDevMLH
3f8777db9f Bump version to 1.4.0.12
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 19:06:47 +01:00
CodeDevMLH
4ec337a893 Update button layout and text for improved clarity in settings UI 2026-02-04 19:05:27 +01:00
CodeDevMLH
7366f2ca30 Update button text for clarity in settings UI 2026-02-04 19:04:19 +01:00
CodeDevMLH
faabf13870 Update manifest.json for release v1.4.0.11 [skip ci] 2026-02-04 17:58:14 +00:00
CodeDevMLH
3e584fa419 Bump version to 1.4.0.11
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 18:57:23 +01:00
CodeDevMLH
13889c3c68 Enhance button layout in settings UI for better alignment and clarity 2026-02-04 18:57:09 +01:00
CodeDevMLH
7af9eb5004 Update manifest.json for release v1.4.0.10 [skip ci] 2026-02-04 17:51:26 +00:00
CodeDevMLH
1e57d8acef Bump version to 1.4.0.10
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-04 18:50:36 +01:00
CodeDevMLH
5964d2cb34 Enhance settings UI with reset functionality and improved button layout 2026-02-04 18:50:20 +01:00
CodeDevMLH
6f3fd23879 Update manifest.json for release v1.4.0.9 [skip ci] 2026-02-04 17:39:12 +00:00
CodeDevMLH
47949e9820 Bump version to 1.4.0.9
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 18:38:21 +01:00
CodeDevMLH
5fe8da8347 Update configuration descriptions for clarity and accuracy 2026-02-04 18:38:05 +01:00
CodeDevMLH
a8842f8415 Update manifest.json for release v1.4.0.8 [skip ci] 2026-02-04 17:27:23 +00:00
CodeDevMLH
3ecaf5fae6 Bump version to 1.4.0.8
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 18:26:30 +01:00
CodeDevMLH
9d13d61e77 Update configuration labels and descriptions for clarity 2026-02-04 18:26:16 +01:00
9 changed files with 4005 additions and 198 deletions

View File

@@ -186,79 +186,79 @@ jobs:
fi fi
# Update Message in Seasonals Repository # Update Message in Seasonals Repository
- name: Checkout Seasonal Manifest Repo # - name: Checkout Seasonal Manifest Repo
uses: actions/checkout@v6 # uses: actions/checkout@v6
with: # with:
repository: ${{ github.repository_owner }}/Jellyfin-Seasonals-Plugin # repository: ${{ github.repository_owner }}/Jellyfin-Seasonals-Plugin
path: seasonal-manifest # path: seasonal-manifest
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }} # token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
- name: Update Seasonal Manifest # - name: Update Seasonal Manifest
shell: bash # shell: bash
run: | # run: |
cd seasonal-manifest # cd seasonal-manifest
REPO_OWNER="${{ github.repository_owner }}" # REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}" # REPO_NAME="${{ github.event.repository.name }}"
# 1. Get info from previous steps # # 1. Get info from previous steps
VERSION="${{ env.VERSION }}" # VERSION="${{ env.VERSION }}"
HASH="${{ env.ZIP_HASH }}" # HASH="${{ env.ZIP_HASH }}"
TIME="${{ env.BUILD_TIME }}" # TIME="${{ env.BUILD_TIME }}"
DOWNLOAD_URL="https://git.mahom03-spacecloud.de/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip" # DOWNLOAD_URL="https://git.mahom03-spacecloud.de/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
# 2. Get info from env # # 2. Get info from env
PLUGIN_GUID="${{ env.PLUGIN_GUID }}" # PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
CHANGELOG="${{ env.CHANGELOG }}" # CHANGELOG="${{ env.CHANGELOG }}"
TARGET_ABI="${{ env.TARGET_ABI }}" # TARGET_ABI="${{ env.TARGET_ABI }}"
echo "Updating Seasonal Manifest for Plugin GUID: $PLUGIN_GUID" # echo "Updating Seasonal Manifest for Plugin GUID: $PLUGIN_GUID"
# 3. Update/Prepend entry in seasonal manifest.json # # 3. Update/Prepend entry in seasonal manifest.json
# Logic: # # Logic:
# - If array is empty or new version != old version: PREPEND new entry # # - If array is empty or new version != old version: PREPEND new entry
# - If new version == old version: OVERWRITE (update) existing entry (re-release) # # - If new version == old version: OVERWRITE (update) existing entry (re-release)
jq --arg guid "$PLUGIN_GUID" \ # jq --arg guid "$PLUGIN_GUID" \
--arg hash "$HASH" \ # --arg hash "$HASH" \
--arg time "$TIME" \ # --arg time "$TIME" \
--arg url "$DOWNLOAD_URL" \ # --arg url "$DOWNLOAD_URL" \
--arg ver "$VERSION" \ # --arg ver "$VERSION" \
--arg changelog "$CHANGELOG" \ # --arg changelog "$CHANGELOG" \
--arg abi "$TARGET_ABI" \ # --arg abi "$TARGET_ABI" \
'map(if .guid == $guid then # 'map(if .guid == $guid then
if .versions[0].version != $ver then # if .versions[0].version != $ver then
# New Version -> Prepend # # New Version -> Prepend
.versions = [{ # .versions = [{
"version": $ver, # "version": $ver,
"changelog": $changelog, # "changelog": $changelog,
"targetAbi": $abi, # "targetAbi": $abi,
"sourceUrl": $url, # "sourceUrl": $url,
"checksum": $hash, # "checksum": $hash,
"timestamp": $time # "timestamp": $time
}] + .versions # }] + .versions
else # else
# Same Version -> Update existing (overwrite top) # # Same Version -> Update existing (overwrite top)
.versions[0].changelog = $changelog | # .versions[0].changelog = $changelog |
.versions[0].targetAbi = $abi | # .versions[0].targetAbi = $abi |
.versions[0].sourceUrl = $url | # .versions[0].sourceUrl = $url |
.versions[0].checksum = $hash | # .versions[0].checksum = $hash |
.versions[0].timestamp = $time # .versions[0].timestamp = $time
end # end
else . end)' \ # else . end)' \
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json # manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
- name: Commit and Push Seasonal Manifest # - name: Commit and Push Seasonal Manifest
run: | # run: |
cd seasonal-manifest # cd seasonal-manifest
git config user.name "CodeDevMLH" # git config user.name "CodeDevMLH"
git config user.email "145071728+CodeDevMLH@users.noreply.github.com" # git config user.email "145071728+CodeDevMLH@users.noreply.github.com"
# Check if there are changes # # Check if there are changes
if [[ -n $(git status -s) ]]; then # if [[ -n $(git status -s) ]]; then
git add manifest.json # git add manifest.json
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}" # git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}"
git push # git push
else # else
echo "No changes to seasonal manifest." # echo "No changes to seasonal manifest."
fi # fi

View File

@@ -222,79 +222,79 @@ jobs:
fi fi
# Update Message in Seasonals Repository # Update Message in Seasonals Repository
- name: Checkout Seasonal Manifest Repo # - name: Checkout Seasonal Manifest Repo
uses: actions/checkout@v6 # uses: actions/checkout@v6
with: # with:
repository: ${{ github.repository_owner }}/Jellyfin-Seasonals # repository: ${{ github.repository_owner }}/Jellyfin-Seasonals
path: seasonal-manifest # path: seasonal-manifest
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }} # token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
- name: Update Seasonal Manifest # - name: Update Seasonal Manifest
shell: bash # shell: bash
run: | # run: |
cd seasonal-manifest # cd seasonal-manifest
REPO_OWNER="${{ github.repository_owner }}" # REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}" # REPO_NAME="${{ github.event.repository.name }}"
# 1. Get info from previous steps # # 1. Get info from previous steps
VERSION="${{ env.VERSION }}" # VERSION="${{ env.VERSION }}"
HASH="${{ env.ZIP_HASH }}" # HASH="${{ env.ZIP_HASH }}"
TIME="${{ env.BUILD_TIME }}" # TIME="${{ env.BUILD_TIME }}"
DOWNLOAD_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip" # DOWNLOAD_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
# 2. Get info from env # # 2. Get info from env
PLUGIN_GUID="${{ env.PLUGIN_GUID }}" # PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
CHANGELOG="${{ env.CHANGELOG }}" # CHANGELOG="${{ env.CHANGELOG }}"
TARGET_ABI="${{ env.TARGET_ABI }}" # TARGET_ABI="${{ env.TARGET_ABI }}"
echo "Updating Seasonal Manifest for Plugin GUID: $PLUGIN_GUID" # echo "Updating Seasonal Manifest for Plugin GUID: $PLUGIN_GUID"
# 3. Update/Prepend entry in seasonal manifest.json # # 3. Update/Prepend entry in seasonal manifest.json
# Logic: # # Logic:
# - If array is empty or new version != old version: PREPEND new entry # # - If array is empty or new version != old version: PREPEND new entry
# - If new version == old version: OVERWRITE (update) existing entry (re-release) # # - If new version == old version: OVERWRITE (update) existing entry (re-release)
jq --arg guid "$PLUGIN_GUID" \ # jq --arg guid "$PLUGIN_GUID" \
--arg hash "$HASH" \ # --arg hash "$HASH" \
--arg time "$TIME" \ # --arg time "$TIME" \
--arg url "$DOWNLOAD_URL" \ # --arg url "$DOWNLOAD_URL" \
--arg ver "$VERSION" \ # --arg ver "$VERSION" \
--arg changelog "$CHANGELOG" \ # --arg changelog "$CHANGELOG" \
--arg abi "$TARGET_ABI" \ # --arg abi "$TARGET_ABI" \
'map(if .guid == $guid then # 'map(if .guid == $guid then
if .versions[0].version != $ver then # if .versions[0].version != $ver then
# New Version -> Prepend # # New Version -> Prepend
.versions = [{ # .versions = [{
"version": $ver, # "version": $ver,
"changelog": $changelog, # "changelog": $changelog,
"targetAbi": $abi, # "targetAbi": $abi,
"sourceUrl": $url, # "sourceUrl": $url,
"checksum": $hash, # "checksum": $hash,
"timestamp": $time # "timestamp": $time
}] + .versions # }] + .versions
else # else
# Same Version -> Update existing (overwrite top) # # Same Version -> Update existing (overwrite top)
.versions[0].changelog = $changelog | # .versions[0].changelog = $changelog |
.versions[0].targetAbi = $abi | # .versions[0].targetAbi = $abi |
.versions[0].sourceUrl = $url | # .versions[0].sourceUrl = $url |
.versions[0].checksum = $hash | # .versions[0].checksum = $hash |
.versions[0].timestamp = $time # .versions[0].timestamp = $time
end # end
else . end)' \ # else . end)' \
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json # manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
- name: Commit and Push Seasonal Manifest # - name: Commit and Push Seasonal Manifest
run: | # run: |
cd seasonal-manifest # cd seasonal-manifest
git config user.name "CodeDevMLH" # git config user.name "CodeDevMLH"
git config user.email "145071728+CodeDevMLH@users.noreply.github.com" # git config user.email "145071728+CodeDevMLH@users.noreply.github.com"
# Check if there are changes # # Check if there are changes
if [[ -n $(git status -s) ]]; then # if [[ -n $(git status -s) ]]; then
git add manifest.json # git add manifest.json
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}" # git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}"
git push # git push
else # else
echo "No changes to seasonal manifest." # echo "No changes to seasonal manifest."
fi # fi

View File

@@ -52,9 +52,9 @@
<label> <label>
<input is="emby-checkbox" type="checkbox" id="EnableVideoBackdrop" <input is="emby-checkbox" type="checkbox" id="EnableVideoBackdrop"
name="EnableVideoBackdrop" /> name="EnableVideoBackdrop" />
<span>Enable Video Backdrops</span> <span>Enable Trailer Backdrops</span>
</label> </label>
<div class="fieldDescription">Show video trailers as background if available.<br>Adds a <div class="fieldDescription">Show trailers as background if available.<br>Adds a
mute/unmute and pause/play button to control the video in the right top corner.</div> mute/unmute and pause/play button to control the video in the right top corner.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
@@ -69,7 +69,7 @@
<label> <label>
<input is="emby-checkbox" type="checkbox" id="EnableMobileVideo" <input is="emby-checkbox" type="checkbox" id="EnableMobileVideo"
name="EnableMobileVideo" /> name="EnableMobileVideo" />
<span>Enable Mobile Video</span> <span>Enable Trailer On Mobile</span>
</label> </label>
<div class="fieldDescription">Allow video playback on mobile devices.</div> <div class="fieldDescription">Allow video playback on mobile devices.</div>
</div> </div>
@@ -80,7 +80,7 @@
<span>Show Trailer Button</span> <span>Show Trailer Button</span>
</label> </label>
<div class="fieldDescription">Display a button to open trailer in modal. Only visible if <div class="fieldDescription">Display a button to open trailer in modal. Only visible if
trailer is not set as backdrop.</div> trailer is not set as backdrop or if no trailer is available.</div>
</div> </div>
</div> </div>
@@ -153,8 +153,17 @@
name="SlideAnimationEnabled" /> name="SlideAnimationEnabled" />
<span>Enable Slide Animations</span> <span>Enable Slide Animations</span>
</label> </label>
<div class="fieldDescription">Enable the zooming-in effect when a new slide is <div class="fieldDescription">Enable the zooming-in effect on background images when a new slide is
shown. Attention: This may cause performance issues on weaker client hardware.</div> shown (does not affect trailer backdrops). Attention: This may cause performance issues on weaker client hardware.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableClientSideSettings"
name="EnableClientSideSettings" />
<span>Enable Client-Side Settings</span>
</label>
<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>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
@@ -221,15 +230,6 @@
Space (pause), M (mute/unmute)) for Space (pause), M (mute/unmute)) for
the slideshow.</div> the slideshow.</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableClientSideSettings"
name="EnableClientSideSettings" />
<span>Enable Client-Side Settings</span>
</label>
<div class="fieldDescription">If enabled, users will see a media bar icon in the header to
override settings (like disabling the bar or video backdrops) locally on their device.</div>
</div>
<h2 class="sectionTitle">Time Settings</h2> <h2 class="sectionTitle">Time Settings</h2>
<p>Leave a setting blank to use the default value.</p> <p>Leave a setting blank to use the default value.</p>

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.4.0.7</Version> <Version>1.5.0.5</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.9 * Jellyfin Slideshow by M0RPH3US v4.0.1
* Modified by CodeDevMLH v1.1.0.0 * Modified by CodeDevMLH v1.1.0.0
* *
* New features: * New features:
@@ -81,6 +81,7 @@ const STATE = {
sponsorBlockInterval: null, sponsorBlockInterval: null,
isMuted: CONFIG.startMuted, isMuted: CONFIG.startMuted,
customTrailerUrls: {}, customTrailerUrls: {},
ytPromise: null,
}, },
}; };
@@ -582,23 +583,25 @@ const SlideUtils = {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
loadYouTubeIframeAPI() { loadYouTubeIframeAPI() {
return new Promise((resolve) => { if (STATE.slideshow.ytPromise) return STATE.slideshow.ytPromise;
STATE.slideshow.ytPromise = new Promise((resolve) => {
if (window.YT && window.YT.Player) { if (window.YT && window.YT.Player) {
resolve(); resolve(window.YT);
return; return;
} }
const tag = document.createElement('script'); window.onYouTubeIframeAPIReady = () => resolve(window.YT);
tag.src = "https://www.youtube.com/iframe_api";
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
const previousOnYouTubeIframeAPIReady = window.onYouTubeIframeAPIReady; if (!document.querySelector('script[src*="youtube.com/iframe_api"]')) {
window.onYouTubeIframeAPIReady = () => { const tag = document.createElement('script');
if (previousOnYouTubeIframeAPIReady) previousOnYouTubeIframeAPIReady(); tag.src = "https://www.youtube.com/iframe_api";
resolve(); const firstScriptTag = document.getElementsByTagName('script')[0];
}; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
}); });
return STATE.slideshow.ytPromise;
}, },
/** /**
@@ -2530,29 +2533,32 @@ const SlideshowManager = {
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (e) => {
const container = document.getElementById("slides-container"); const container = document.getElementById("slides-container");
// Allow interaction if container is visible, even if not strictly focused
if (!container || container.style.display === "none") { if (!container || container.style.display === "none") {
return; return;
} }
// Only trap keys if focus is on body (neutral) or inside our container.
// To allow standard TV navigation to work for other elements (e.g. library cards).
const activeEl = document.activeElement;
const isBody = activeEl === document.body || !activeEl;
const isInContainer = container.contains(activeEl) || activeEl === container;
if (!isBody && !isInContainer) {
return;
}
const focusElement = document.activeElement; const focusElement = document.activeElement;
switch (e.key) { switch (e.key) {
case "ArrowRight": case "d":
if (focusElement && focusElement.classList.contains("detail-button")) { case "D":
focusElement.previousElementSibling.focus(); SlideshowManager.nextSlide();
} else {
SlideshowManager.nextSlide();
}
e.preventDefault(); e.preventDefault();
break; break;
case "ArrowLeft": case "a":
if (focusElement && focusElement.classList.contains("play-button")) { case "A":
focusElement.nextElementSibling.focus(); SlideshowManager.prevSlide();
} else {
SlideshowManager.prevSlide();
}
e.preventDefault(); e.preventDefault();
break; break;
@@ -2568,8 +2574,15 @@ const SlideshowManager = {
break; break;
case "Enter": case "Enter":
if (focusElement) { const currentItemId = STATE.slideshow.itemIds[STATE.slideshow.currentSlideIndex];
focusElement.click(); if (currentItemId) {
if (window.Emby && window.Emby.Page) {
Emby.Page.show(
`/details?id=${currentItemId}&serverId=${STATE.jellyfinData.serverId}`
);
} else {
window.location.href = `#/details?id=${currentItemId}&serverId=${STATE.jellyfinData.serverId}`;
}
} }
e.preventDefault(); e.preventDefault();
break; break;
@@ -3026,12 +3039,12 @@ const MediaBarEnhancedSettingsManager = {
popup.style.top = `${rect.bottom + 10}px`; popup.style.top = `${rect.bottom + 10}px`;
const settings = [ const settings = [
{ key: 'enabled', label: 'Enable Media Bar', description: 'Toggle the entire media bar visibility.', default: true }, { key: 'enabled', label: 'Enable Media Bar Enhanced', description: 'Toggle the entire media bar visibility.', default: true },
{ key: 'videoBackdrops', label: 'Enable Video Backdrops', description: 'Play trailers as background videos.', default: CONFIG.enableVideoBackdrop }, { key: 'videoBackdrops', label: 'Enable Trailer Backdrops', description: 'Play trailers as background videos.', default: CONFIG.enableVideoBackdrop },
{ key: 'trailerButton', label: 'Show Trailer Button', description: 'Show button to play trailers in popup on non Video backdrops.', default: CONFIG.showTrailerButton }, { key: 'trailerButton', label: 'Show Trailer Button', description: 'Show button to play trailer in popup (only backdrops without trailer)', default: CONFIG.showTrailerButton },
{ key: 'mobileVideo', label: 'Enable Mobile Video', description: 'Allow video backdrops on mobile devices.', default: CONFIG.enableMobileVideo }, { key: 'mobileVideo', label: 'Enable Trailer On Mobile', description: 'Allow trailer backdrops on mobile devices.', default: CONFIG.enableMobileVideo },
{ key: 'waitForTrailer', label: 'Wait For Trailer To End', description: 'Wait for the trailer to finish before changing slides.', default: CONFIG.waitForTrailerToEnd }, { key: 'waitForTrailer', label: 'Wait For Trailer To End', description: 'Wait for the trailer to finish before changing slides.', default: CONFIG.waitForTrailerToEnd },
{ key: 'slideAnimations', label: 'Enable Animations', description: 'Enable transition animations between slides.', default: CONFIG.slideAnimationEnabled }, { key: 'slideAnimations', label: 'Enable Animations', description: 'Enable zooming-in effect (only on background images)', default: CONFIG.slideAnimationEnabled },
]; ];
let html = '<h3 style="margin-top:0; margin-bottom:1em; border-bottom:1px solid #444; padding-bottom:0.5em;">Media Bar Settings</h3>'; let html = '<h3 style="margin-top:0; margin-bottom:1em; border-bottom:1px solid #444; padding-bottom:0.5em;">Media Bar Settings</h3>';
@@ -3049,11 +3062,14 @@ const MediaBarEnhancedSettingsManager = {
`; `;
}); });
// Reload button // Buttons Container
html += ` html += `
<div style="margin-top:1em; text-align:right;"> <div style="margin-top:1em; display:flex; justify-content:flex-end; align-items:center; gap:1.5em;">
<button is="emby-button" type="button" class="raised button-cancel emby-button" id="mb-settings-reset" title="Reset to Server Defaults">
<span>Load Server Defaults</span>
</button>
<button is="emby-button" type="button" class="raised button-submit emby-button" id="mb-settings-save"> <button is="emby-button" type="button" class="raised button-submit emby-button" id="mb-settings-save">
<span>Reload</span> <span>Save & Reload</span>
</button> </button>
</div> </div>
`; `;
@@ -3068,10 +3084,23 @@ const MediaBarEnhancedSettingsManager = {
}); });
}); });
// Reload Handler
popup.querySelector('#mb-settings-save').addEventListener('click', () => { popup.querySelector('#mb-settings-save').addEventListener('click', () => {
location.reload(); location.reload();
}); });
// Reset Handler
popup.querySelector('#mb-settings-reset').addEventListener('click', () => {
if (confirm("Reset all local Media Bar settings to server defaults?")) {
Object.keys(localStorage).forEach(key => {
if (key.startsWith('mediaBarEnhanced-')) {
localStorage.removeItem(key);
}
});
location.reload();
}
});
const closeHandler = (e) => { const closeHandler = (e) => {
if (!popup.contains(e.target) && e.target !== anchorElement && !anchorElement.contains(e.target)) { if (!popup.contains(e.target) && e.target !== anchorElement && !anchorElement.contains(e.target)) {
popup.remove(); popup.remove();
@@ -3093,6 +3122,84 @@ const MediaBarEnhancedSettingsManager = {
} }
}; };
/**
* Initialize page visibility handling to pause when tab is inactive
*/
const initPageVisibilityHandler = () => {
let wasVideoPlayingBeforeHide = false;
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
console.log("Tab inactive - pausing slideshow and videos");
wasVideoPlayingBeforeHide = STATE.slideshow.isVideoPlaying;
if (STATE.slideshow.slideInterval) {
STATE.slideshow.slideInterval.stop();
}
// Pause active video if playing
const currentItemId = STATE.slideshow.itemIds[STATE.slideshow.currentSlideIndex];
if (currentItemId) {
// YouTube
if (STATE.slideshow.videoPlayers && STATE.slideshow.videoPlayers[currentItemId]) {
const player = STATE.slideshow.videoPlayers[currentItemId];
if (typeof player.pauseVideo === "function") {
try {
player.pauseVideo();
STATE.slideshow.isVideoPlaying = false;
} catch (e) {
console.warn("Error pausing video on tab hide:", e);
}
} else if (player.tagName === 'VIDEO') { // HTML5 Video
player.pause();
STATE.slideshow.isVideoPlaying = false;
}
}
}
} else {
console.log("Tab active - resuming slideshow");
if (!STATE.slideshow.isPaused) {
const currentItemId = STATE.slideshow.itemIds[STATE.slideshow.currentSlideIndex];
if (wasVideoPlayingBeforeHide && currentItemId && STATE.slideshow.videoPlayers && STATE.slideshow.videoPlayers[currentItemId]) {
const player = STATE.slideshow.videoPlayers[currentItemId];
// YouTube
if (typeof player.playVideo === "function") {
try {
player.playVideo();
STATE.slideshow.isVideoPlaying = true;
} catch (e) {
console.warn("Error resuming video on tab show:", e);
if (STATE.slideshow.slideInterval) {
STATE.slideshow.slideInterval.start();
}
}
} else if (player.tagName === 'VIDEO') { // HTML5 Video
try {
player.play().catch(e => console.warn("Error resuming HTML5 video:", e));
STATE.slideshow.isVideoPlaying = true;
} catch(e) { console.warn(e); }
}
} else {
// No video was playing, just restart interval
const activeSlide = document.querySelector('.slide.active');
const hasVideo = activeSlide && activeSlide.querySelector('.video-backdrop');
if (CONFIG.waitForTrailerToEnd && hasVideo) {
// Don't restart interval if waiting for trailer
} else {
if (STATE.slideshow.slideInterval) {
STATE.slideshow.slideInterval.start();
}
}
}
wasVideoPlayingBeforeHide = false;
}
}
});
};
/** /**
* Initialize the slideshow * Initialize the slideshow
*/ */
@@ -3206,6 +3313,8 @@ const slidesInit = async () => {
SlideshowManager.initKeyboardEvents(); SlideshowManager.initKeyboardEvents();
initPageVisibilityHandler();
VisibilityObserver.init(); VisibilityObserver.init();
console.log("✅ Enhanced Jellyfin Slideshow initialized successfully"); console.log("✅ Enhanced Jellyfin Slideshow initialized successfully");

View File

@@ -82,11 +82,21 @@ This plugin builds upon the original Media Bar with new capabilities and improve
* **Smarter Playback**: * **Smarter Playback**:
* Option to wait for the trailer to end before advancing the slide. * Option to wait for the trailer to end before advancing the slide.
* Mute/Unmute controls * Mute/Unmute controls
* **Override Trailers**: Manually specify a custom trailer URL for any item via the Custom Media IDs list
* **Customization**: * **Customization**:
* **Custom Media IDs**: Manually specify which items (Movies, Series, Collections/Boxsets) to display. Easily configurable via the plugin settings * **Custom Media IDs**: Manually specify which items (Movies, Series, Collections/Boxsets) to display. Easily configurable via the plugin settings
* **Seasonal Content Mode**: Define date-based lists for holidays and seasons (e.g., Halloween, Christmas) * **Seasonal Content Mode**: Define date-based lists for holidays and seasons (e.g., Halloween, Christmas)
* Pagination dots turn into a counter (e.g., 1/20) if the limit is exceeded * Pagination dots turn into a counter (e.g., 1/20) if the limit is exceeded
<details>
<summary>Have a look:</summary>
<img width="167" height="142" alt="PagDots_Number" src="https://github.com/user-attachments/assets/6a0a5040-cf13-4d9c-ae96-f50ec249c3f1" />
</details>
* Option to disable the loading screen * Option to disable the loading screen
* Client Settings: Optionally allow users to set selected media bar settings from their client.
<details>
<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" />
</details>
### Core Features ### Core Features
* **Immersive Slideshow**: Rotates through your media library * **Immersive Slideshow**: Rotates through your media library
@@ -118,13 +128,14 @@ Because this plugin relies on injecting JavaScript and CSS into the web interfac
| Client Platform | Status | Notes | | Client Platform | Status | Notes |
| :--- | :---: | :--- | | :--- | :---: | :--- |
| **Web Browsers** (Chrome, Firefox, Edge, etc.) | ✅ | Fully supported. | | **Web Browsers** (Firefox, Chrome etc.) | ✅ | Direct JS injection |
| **Jellyfin Media Player** (Windows/Linux/macOS) | ✅ | Fully supported. | | **Jellyfin Media Player** (Windows/Linux/macOS) | ✅ | Uses jellyfin web |
| **Android App** | ✅ | Works (Web wrapper). | | **Android App** | ✅ | Uses a web wrapper |
| **iOS App** | ✅ | Works (Web wrapper). | | **iOS App** | ✅ | Uses a web wrapper |
| **Android TV / Fire TV** | ❌ | **Not supported** (Native UI). | | **Android TV / Fire TV** | ❌ | **Not supported.** Uses a native Java/Kotlin UI. |
| **Roku** | ❌ | **Not supported** (Native UI). | | **Roku** | ❌ | **Not supported.** Uses a native UI. |
| **Swiftfin** | ❌ | **Not supported** (Native UI). | | **Swiftfin** (iOS/tvOS) | ❌ | **Not supported.** Uses a native Swift UI. |
| **Kodi** (via Jellyfin Addon) | ❌ | **Not supported.** Uses Kodi's native skinning engine. |
## Configuration ## Configuration

View File

@@ -9,12 +9,12 @@
"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.4.0.7", "version": "1.5.0.5",
"changelog": "- feat: Add client-side settings feature for selected media bar settings", "changelog": "- fix: keyboard controls in TV mode \n- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 4.0.1 from original repo",
"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.4.0.7/Jellyfin.Plugin.MediaBarEnhanced.zip", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.5.0.5/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "450e5977228d08b8451b6047e4a6be94", "checksum": "6fb6de0a4020fc5e905ef35ebff80672",
"timestamp": "2026-02-04T17:09:28Z" "timestamp": "2026-02-08T02:20:03Z"
}, },
{ {
"version": "1.3.0.3", "version": "1.3.0.3",

View File

@@ -0,0 +1,462 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Media Bar Enhanced Configuration</title>
</head>
<body>
<div id="MediaBarEnhancedConfigurationPage" data-role="page" class="page type-interior pluginConfigurationPage"
data-require="emby-input,emby-button,emby-select,emby-checkbox,emby-textarea">
<div data-role="content">
<div class="content-primary">
<div class="sectionTitleContainer">
<h2 class="sectionTitle">Media Bar Enhanced</h2>
<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">
<i class="md-icon button-icon button-icon-left secondaryText"></i>
<span>${Help}</span>
</a>
</div>
<hr style="max-width: 800px; margin: 1em 0;">
<div style="margin-bottom: 1.5em;">
<button class="jellyfin-tab-button active" onclick="showTab('basic', this)"
style="background: none; border: none; color: #fff; cursor: pointer; transition: color 0.3s, border-bottom 0.3s; padding: 0.5em 1em; border-bottom: 2px solid #00a4dc;">
<h3>General Settings</h3>
</button>
<button class="jellyfin-tab-button" onclick="showTab('custom', this)"
style="background: none; border: none; color: #ccc; cursor: pointer; transition: color 0.3s, border-bottom 0.3s; padding: 0.5em 1em; border-bottom: 2px solid transparent;">
<h3>Custom Content</h3>
</button>
<button class="jellyfin-tab-button" onclick="showTab('advanced', this)"
style="background: none; border: none; color: #ccc; cursor: pointer; transition: color 0.3s, border-bottom 0.3s; padding: 0.5em 1em; border-bottom: 2px solid transparent;">
<h3>Advanced Settings</h3>
</button>
</div>
<form id="mediaBarEnhancedConfigForm">
<!-- BASIC TAB -->
<div id="basic" class="tab-content">
<h2 class="sectionTitle">Main Plugin Settings</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="IsEnabled" name="IsEnabled" />
<span>Enable Media Bar Enhanced Plugin</span>
</label>
<div class="fieldDescription">Enable or disable the entire plugin functionality.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableVideoBackdrop"
name="EnableVideoBackdrop" />
<span>Enable Trailer Backdrops</span>
</label>
<div class="fieldDescription">Show trailers as background if available.<br>Adds a
mute/unmute and pause/play button to control the video in the right top corner.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd"
name="WaitForTrailerToEnd" />
<span>Wait For Trailer To End</span>
</label>
<div class="fieldDescription">Delay slide transition until trailer finishes.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableMobileVideo"
name="EnableMobileVideo" />
<span>Enable Trailer On Mobile</span>
</label>
<div class="fieldDescription">Allow video playback on mobile devices.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="ShowTrailerButton"
name="ShowTrailerButton" />
<span>Show Trailer Button</span>
</label>
<div class="fieldDescription">Display a button to open trailer in modal. Only visible if
trailer is not set as backdrop or if no trailer is available.</div>
</div>
</div>
<!-- CUSTOM CONTENT TAB -->
<div id="custom" class="tab-content" style="display:none;">
<h2 class="sectionTitle">Custom Media IDs</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableCustomMediaIds"
name="EnableCustomMediaIds" />
<span>Enable Custom Media IDs</span>
</label>
<div class="fieldDescription">If enabled, the slideshow will try to show the items listed
below. If the list is empty, default behavior (random items) is used. Disable this
to temporarily ignore your custom list without deleting it.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableSeasonalContent"
name="EnableSeasonalContent" />
<span>Enable Seasonal Content Mode</span>
</label>
<div class="fieldDescription">Enable this to define time-based lists in the field below.
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="CustomMediaIds">Media/Collection/Playlist
IDs
(Newline or Comma separated)</label>
<textarea is="emby-textarea" id="CustomMediaIds" name="CustomMediaIds"
style="width: 100%; height: 150px; font-family: monospace;"></textarea>
<div class="fieldDescription" id="customMediaIdsDesc">Enter the IDs of the items you want to show in the slideshow.
You can separate them by new line or comma.
<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
(|) or dash (-): <br>e.g. <code>ID DESCRIPTION</code> or <code>ID | DESCRIPTION</code>
<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;">
<b>Seasonal Mode Enabled:</b> Define lines with date ranges (Format: DD.MM-DD.MM |
<i>name</i> | <i>IDs</i>).<br>
Example:<br>
<code>20.10-31.10 | Halloween | ID1, ID2 [https://youtu.be/...]</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
use random items.</i>
</div>
<p>You can find the IDs of your items in the URL of the item page in the web interface.<br>
Example:
<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
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.).
</p>
</div>
</div>
<!-- ADVANCED TAB -->
<div id="advanced" class="tab-content" style="display:none;">
<h2 class="sectionTitle">Features</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="SlideAnimationEnabled"
name="SlideAnimationEnabled" />
<span>Enable Slide Animations</span>
</label>
<div class="fieldDescription">Enable the zooming-in effect on background images when a new slide is
shown (does not affect trailer backdrops). Attention: This may cause performance issues on weaker client hardware.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableClientSideSettings"
name="EnableClientSideSettings" />
<span>Enable Client-Side Settings</span>
</label>
<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>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="UseSponsorBlock" name="UseSponsorBlock" />
<span>Use SponsorBlock</span>
</label>
<div class="fieldDescription">Skip intro/outro segments in YouTube trailers.</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">
<label>
<input is="emby-checkbox" type="checkbox" id="StartMuted" name="StartMuted" />
<span>Start Muted</span>
</label>
<div class="fieldDescription">Start trailer video playback muted. (Known issue: In the
Android/IOS app, backdrop trailers are always muted.)</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="FullWidthVideo" name="FullWidthVideo" />
<span>Full Width Video</span>
</label>
<div class="fieldDescription">Stretch video to full width. Very nice on desktops, on mobile
devices only the middle of the video is visible.<br>Disable to get the full aspect ratio
on
mobile devices. (looks bad on desktops)</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableLoadingScreen"
name="EnableLoadingScreen" />
<span>Enable Loading Screen</span>
</label>
<div class="fieldDescription">Show a loading screen while the slideshow initializes. (You
may have to reload the page twice)</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="AlwaysShowArrows"
name="AlwaysShowArrows" />
<span>Always Show Arrows</span>
</label>
<div class="fieldDescription">If enabled, navigation arrows will always be visible instead
of only on hover.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableKeyboardControls"
name="EnableKeyboardControls" />
<span>Enable Keyboard Controls</span>
</label>
<div class="fieldDescription">Enable keyboard shortcuts (Arrows left/right (change slide),
Space (pause), M (mute/unmute)) for
the slideshow.</div>
</div>
<h2 class="sectionTitle">Time Settings</h2>
<p>Leave a setting blank to use the default value.</p>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="ShuffleInterval">Shuffle Interval
(ms)</label>
<input is="emby-input" type="number" id="ShuffleInterval" name="ShuffleInterval" />
<div class="fieldDescription">Time in milliseconds between changing slides.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="RetryInterval">Retry Interval
(ms)</label>
<input is="emby-input" type="number" id="RetryInterval" name="RetryInterval" />
<div class="fieldDescription">Time in milliseconds to wait before retrying failed
operations.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="LoadingCheckInterval">Loading Check
Interval (ms)</label>
<input is="emby-input" type="number" id="LoadingCheckInterval"
name="LoadingCheckInterval" />
<div class="fieldDescription">Frequency of checking wether the login screen or home screen
has loaded (in milliseconds).</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="FadeTransitionDuration">Fade
Transition Duration (ms)</label>
<input is="emby-input" type="number" id="FadeTransitionDuration"
name="FadeTransitionDuration" />
<div class="fieldDescription">Duration in milliseconds of the transition between slides.
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MinSwipeDistance">Min Swipe Distance
(px)</label>
<input is="emby-input" type="number" id="MinSwipeDistance" name="MinSwipeDistance" />
<div class="fieldDescription">Minimum distance in pixels for a swipe to be registered (for
mobile).</div>
</div>
<h2 class="sectionTitle">Content Limits</h2>
<p>Leave a setting blank to use the default value.</p>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxItems">Total Max Items</label>
<input is="emby-input" type="number" id="MaxItems" name="MaxItems" />
<div class="fieldDescription">Maximum total items to fetch (for all content types combined).
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxMovies">Max Movies</label>
<input is="emby-input" type="number" id="MaxMovies" name="MaxMovies" />
<div class="fieldDescription">Maximum movies to include in slideshow (for random selection).
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxTvShows">Max TV Shows</label>
<input is="emby-input" type="number" id="MaxTvShows" name="MaxTvShows" />
<div class="fieldDescription">Maximum TV shows to include in slideshow (for random
selection).</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="PreloadCount">Preload Count</label>
<input is="emby-input" type="number" id="PreloadCount" name="PreloadCount" />
<div class="fieldDescription">Number of images to preload.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxPaginationDots">Max Pagination
Dots</label>
<input is="emby-input" type="number" id="MaxPaginationDots" name="MaxPaginationDots" />
<div class="fieldDescription">Maximum number of dots to show in navigation. If the number
will be exceeded, the dots will "turn" into a counter style (e.g. 1/100). Set to 0 to
enable counter style directly.</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxPlotLength">Max Plot
Length</label>
<input is="emby-input" type="number" id="MaxPlotLength" name="MaxPlotLength" />
<div class="fieldDescription">Maximum characters for the plot summary.</div>
</div>
</div>
<div
style="background-color: rgba(255, 255, 255, 0.05); border-left: 4px solid #00a4dc; border-radius: 4px; padding: 1em 1.5em; margin: 1.5em 0; display: flex; align-items: center; gap: 1em;">
<i class="material-icons" style="color: #00a4dc; font-size: 24px;">info</i>
<div>
All changes require a page refresh (ctrl + r or F5) after saving for changes to take effect.
<br />
If old settings persist, please force clear browser cache.
</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block emby-button">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block btnCancel"
onclick="history.back();">
<span>${ButtonCancel}</span>
</button>
</div>
</form>
</div>
</div>
<script>
function showTab(tabId, btn) {
document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');
document.getElementById(tabId).style.display = 'block';
document.querySelectorAll('.jellyfin-tab-button').forEach(b => {
b.classList.remove('active');
b.style.color = '#ccc';
b.style.borderBottom = '2px solid transparent';
});
if (btn) {
btn.classList.add('active');
btn.style.color = '#fff';
btn.style.borderBottom = '2px solid #00a4dc';
}
}
var MediaBarEnhancedConfigurationPage = {
pluginId: 'd7e11d57-819b-4bdd-a88d-53c5f5560225',
loadConfiguration: function (page) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MediaBarEnhancedConfigurationPage.pluginId).then(function (config) {
var keys = [
'IsEnabled', 'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo',
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent', 'EnableClientSideSettings'
];
keys.forEach(function (key) {
var el = page.querySelector('#' + key);
if (el) {
if (el.type === 'checkbox') {
el.checked = config[key];
} else {
el.value = config[key];
}
}
});
// Handle Seasonal UI logic
var seasonalCheckbox = page.querySelector('#EnableSeasonalContent');
var normalDesc = page.querySelector('#customMediaIdsDesc');
var seasonalDesc = page.querySelector('#seasonalMediaIdsDesc');
function updateDesc() {
if (seasonalCheckbox && seasonalCheckbox.checked) {
if (normalDesc) normalDesc.style.display = 'none';
if (seasonalDesc) seasonalDesc.style.display = 'block';
} else {
if (normalDesc) normalDesc.style.display = 'block';
if (seasonalDesc) seasonalDesc.style.display = 'none';
}
}
if (seasonalCheckbox) {
seasonalCheckbox.addEventListener('change', updateDesc);
updateDesc();
}
Dashboard.hideLoadingMsg();
});
},
saveConfiguration: function (page) {
Dashboard.showLoadingMsg();
var config = {};
var keys = [
'IsEnabled', 'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo',
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent', 'EnableClientSideSettings'
];
keys.forEach(function (key) {
var el = page.querySelector('#' + key);
if (el) {
if (el.type === 'checkbox') {
config[key] = el.checked;
} else {
config[key] = (el.type === 'number') ? parseInt(el.value, 10) : el.value;
}
}
});
ApiClient.updatePluginConfiguration(MediaBarEnhancedConfigurationPage.pluginId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
}
};
document.querySelector('#MediaBarEnhancedConfigurationPage').addEventListener('pageshow', function () {
MediaBarEnhancedConfigurationPage.loadConfiguration(this);
});
document.querySelector('#mediaBarEnhancedConfigForm').addEventListener('submit', function (e) {
e.preventDefault();
MediaBarEnhancedConfigurationPage.saveConfiguration(document.querySelector('#MediaBarEnhancedConfigurationPage'));
return false;
});
</script>
<style>
.jellyfin-tab-button.active {
color: #fff !important;
border-bottom: 2px solid #00a4dc !important;
}
</style>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff