Compare commits

..

96 Commits

Author SHA1 Message Date
CodeDevMLH
6910eba7d6 Update manifest.json for release v1.5.0.12 [skip ci] 2026-02-09 15:10:07 +00:00
CodeDevMLH
3585b47b6c Bump version to 1.5.0.12 and update changelog in manifest.json
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-02-09 16:09:10 +01:00
CodeDevMLH
8170abdc94 Update manifest.json for release v1.5.0.11 [skip ci] 2026-02-09 14:56:05 +00:00
CodeDevMLH
535c0e17bf Add upstream trailer layout feature and update version to 1.5.0.11
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 52s
2026-02-09 15:55:14 +01:00
CodeDevMLH
df1cee0eeb Update manifest.json for release v1.5.0.10 [skip ci] 2026-02-09 13:31:00 +00:00
CodeDevMLH
16cc56030f Bump version to 1.5.0.10 in project file and manifest
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 55s
2026-02-09 14:30:05 +01:00
CodeDevMLH
13ecbac96e Update manifest.json for release v1.5.0.9 [skip ci] 2026-02-09 13:18:30 +00:00
CodeDevMLH
8bca6f9052 Bump version to 1.5.0.9 in project file and manifest
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-09 14:17:38 +01:00
CodeDevMLH
217db2a66d Fix video backdrop styling for full coverage and pointer events 2026-02-09 14:17:22 +01:00
CodeDevMLH
5fd7bcb8b6 Update manifest.json for release v1.5.0.8 [skip ci] 2026-02-09 13:11:30 +00:00
CodeDevMLH
0b0e41a9f9 Bump version to 1.5.0.8 in project file and manifest
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-02-09 14:10:35 +01:00
CodeDevMLH
370db55714 Enhance slideshow video styling for better positioning and responsiveness 2026-02-09 14:06:18 +01:00
CodeDevMLH
e5d4800ef1 Update manifest.json for release v1.5.0.7 [skip ci] 2026-02-09 12:28:45 +00:00
CodeDevMLH
b910e92364 Add spotlight.html: Initial implementation of Seth's Spotlight UI enhancement for Jellyfin with movie slideshow and trailer functionality
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 55s
2026-02-09 13:27:51 +01:00
CodeDevMLH
82283b1faf Merge branch 'main' of ssh://git.mahom03-spacecloud.de:44322/CodeDevMLH/jellyfin-plugin-media-bar-enhanced
Some checks failed
Auto Release Plugin / build-and-release (push) Has been cancelled
2026-02-09 13:27:36 +01:00
CodeDevMLH
1588b1a6b2 Add option to prefer local trailers over remote ones; update configuration and UI 2026-02-09 03:31:05 +01:00
CodeDevMLH
de19466341 Add script to fetch specific item details from Jellyfin API 2026-02-09 02:56:53 +01:00
CodeDevMLH
15054e314c refine sorting options description 2026-02-09 02:42:23 +01:00
CodeDevMLH
3877f96b09 Add sorting options for Production Year and Critic Rating; remove Date Created sorting 2026-02-09 02:39:42 +01:00
CodeDevMLH
abca7cb3b6 Refactor random item fetching logic and enhance error handling; add JSON download feature 2026-02-09 02:39:32 +01:00
CodeDevMLH
998a0cfc68 Add scripts to fetch random items from Jellyfin API with detailed logging [skip ci] 2026-02-09 02:21:12 +01:00
CodeDevMLH
63be8214d0 Update manifest.json for release v1.5.0.6 [skip ci] 2026-02-09 00:31:35 +00:00
CodeDevMLH
99411afffd Bump version to 1.5.0.6; update changelog and fix keyboard controls in TV mode
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 58s
2026-02-09 01:30:36 +01:00
CodeDevMLH
9510ae6ba7 Add sorting options for content in configuration and update sorting logic 2026-02-09 01:30:28 +01:00
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
CodeDevMLH
0e441b751b Update manifest.json for release v1.4.0.7 [skip ci] 2026-02-04 17:09:29 +00:00
CodeDevMLH
4f14c00636 Bump version to 1.4.0.7
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 55s
2026-02-04 18:08:37 +01:00
CodeDevMLH
2dbb4dcfea Add minimal logo SVG and update mediaBarEnhanced.js to use it 2026-02-04 18:08:24 +01:00
CodeDevMLH
1982511374 Update manifest.json for release v1.4.0.6 [skip ci] 2026-02-04 16:40:22 +00:00
CodeDevMLH
3d7c1ec2cb Bump version to 1.4.0.6
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 17:39:30 +01:00
CodeDevMLH
bf7762f4d8 Add short logo SVG and update mediaBarEnhanced.js to use it 2026-02-04 17:39:17 +01:00
CodeDevMLH
9ba4bae6a8 Update manifest.json for release v1.4.0.5 [skip ci] 2026-02-04 16:23:15 +00:00
CodeDevMLH
f7388d8c25 Bump version to 1.4.0.5
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-04 17:22:25 +01:00
CodeDevMLH
7097c49773 Fix logo image source in MediaBar settings button for correct display 2026-02-04 17:21:18 +01:00
CodeDevMLH
0dce0c1551 Update manifest.json for release v1.4.0.4 [skip ci] 2026-02-04 16:17:16 +00:00
CodeDevMLH
386c5f9d7d Bump version to 1.4.0.4
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 56s
2026-02-04 17:16:23 +01:00
CodeDevMLH
2bd1a7a8b4 Update logo image source in MediaBar settings button for improved display and drag handling 2026-02-04 17:16:07 +01:00
CodeDevMLH
61eeed78b8 Update logo image source in MediaBar settings button for improved display 2026-02-04 17:13:12 +01:00
CodeDevMLH
5a844f4d57 Update manifest.json for release v1.4.0.3 [skip ci] 2026-02-04 15:52:12 +00:00
CodeDevMLH
4127a2e20c Bump version to 1.4.0.3
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
2026-02-04 16:51:22 +01:00
CodeDevMLH
f6b56a6f65 Refactor settings management to use MediaBarEnhancedSettingsManager for consistency 2026-02-04 16:51:03 +01:00
CodeDevMLH
4e8af02ab4 Update manifest.json for release v1.4.0.2 [skip ci] 2026-02-04 15:45:39 +00:00
CodeDevMLH
22d15748d9 Bump version to 1.4.0.2 and enhance client-side settings with descriptions for media bar options
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 55s
2026-02-04 16:44:47 +01:00
CodeDevMLH
2db3056511 Update manifest.json for release v1.4.0.1 [skip ci] 2026-02-04 15:01:50 +00:00
CodeDevMLH
2cc038e164 Bump version to 1.4.0.1 and update plugin configuration loading method
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 53s
2026-02-04 16:01:01 +01:00
CodeDevMLH
aa2df6e035 Update manifest.json for release v1.4.0.0 [skip ci] 2026-02-04 12:49:45 +00:00
CodeDevMLH
ff41618879 Fix localStorage key for loading screen preference (v1.4.0.0)
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 48s
2026-02-04 13:48:54 +01:00
CodeDevMLH
fbc03a28ea Update manifest.json for release v1.4.0.0 [skip ci] 2026-02-04 12:46:16 +00:00
CodeDevMLH
d2db678855 Bump version to 1.4.0.0
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 51s
2026-02-04 13:45:27 +01:00
CodeDevMLH
9259a0f487 Add client-side settings feature and support for SVG file type 2026-02-04 13:45:11 +01:00
CodeDevMLH
c391649884 add svg logo 2026-02-04 13:42:55 +01:00
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
18 changed files with 4942 additions and 217 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

@@ -70,6 +70,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase)) return "application/javascript"; if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase)) return "application/javascript";
if (path.EndsWith(".css", StringComparison.OrdinalIgnoreCase)) return "text/css"; if (path.EndsWith(".css", StringComparison.OrdinalIgnoreCase)) return "text/css";
if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) return "text/html"; if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) return "text/html";
if (path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) return "image/svg+xml";
return "application/octet-stream"; return "application/octet-stream";
} }
} }

View File

@@ -21,6 +21,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
public bool SlideAnimationEnabled { get; set; } = true; public bool SlideAnimationEnabled { get; set; } = true;
public bool EnableVideoBackdrop { get; set; } = true; public bool EnableVideoBackdrop { get; set; } = true;
public bool UseSponsorBlock { get; set; } = true; public bool UseSponsorBlock { get; set; } = true;
public bool PreferLocalTrailers { get; set; } = false;
public bool WaitForTrailerToEnd { get; set; } = true; public bool WaitForTrailerToEnd { get; set; } = true;
public bool StartMuted { get; set; } = true; public bool StartMuted { get; set; } = true;
public bool FullWidthVideo { get; set; } = true; public bool FullWidthVideo { get; set; } = true;
@@ -34,5 +35,9 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
public string PreferredVideoQuality { get; set; } = "Auto"; 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;
public bool EnableClientSideSettings { get; set; } = false;
public bool EnableUpstreamTrailerLayout { get; set; } = false;
public string SortBy { get; set; } = "Random";
public string SortOrder { get; set; } = "Ascending";
} }
} }

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;">
@@ -52,11 +52,19 @@
<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" id="PreferLocalTrailersContainer">
<label>
<input is="emby-checkbox" type="checkbox" id="PreferLocalTrailers"
name="PreferLocalTrailers" />
<span>Prefer Local Trailers</span>
</label>
<div class="fieldDescription">If enabled, local trailers will be preferred over remote (YouTube) trailers.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label> <label>
<input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd" <input is="emby-checkbox" type="checkbox" id="WaitForTrailerToEnd"
@@ -65,11 +73,19 @@
</label> </label>
<div class="fieldDescription">Delay slide transition until trailer finishes.</div> <div class="fieldDescription">Delay slide transition until trailer finishes.</div>
</div> </div>
<div id="UpstreamTrailerLayoutContainer" class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="EnableUpstreamTrailerLayout"
name="EnableUpstreamTrailerLayout" />
<span>Enable Upstream Trailer Layout</span>
</label>
<div class="fieldDescription">Use the upstream (original) layout for trailers. This renders the video inside a container overlaying the backdrop, instead of replacing it to support full-width video.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<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 +96,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>
@@ -109,20 +125,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 +154,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>
@@ -146,8 +169,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>
@@ -258,6 +290,33 @@
mobile).</div> mobile).</div>
</div> </div>
<h2 class="sectionTitle">Content Sorting</h2>
<div class="selectContainer">
<label class="selectLabel" for="SortBy">Sort By</label>
<select is="emby-select" id="SortBy" name="SortBy" class="emby-select-withcolor emby-select">
<option value="Random">Random</option>
<option value="Original">Original (Custom List Order)</option>
<option value="PremiereDate">Premiere Date</option>
<option value="ProductionYear">Production Year</option>
<option value="CriticRating">Critic Rating</option>
<option value="CommunityRating">Community Rating</option>
<option value="Name">Name</option>
<option value="Runtime">Runtime</option>
</select>
<div class="fieldDescription">Sort items by the selected criteria.</div>
</div>
<div class="selectContainer">
<label class="selectLabel" for="SortOrder">Sort Order</label>
<select is="emby-select" id="SortOrder" name="SortOrder" class="emby-select-withcolor emby-select">
<option value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
</select>
<div class="fieldDescription">Sort items in Ascending or Descending order.</div>
</div>
<div class="fieldDescription" style="margin-bottom: 2em; color: #ffcc00;">
<b>Note:</b> Sorting settings apply to both Server content and Custom IDs. 'Original' preserves Custom List order.
</div>
<h2 class="sectionTitle">Content Limits</h2> <h2 class="sectionTitle">Content Limits</h2>
<p>Leave a setting blank to use the default value.</p> <p>Leave a setting blank to use the default value.</p>
<div class="inputContainer"> <div class="inputContainer">
@@ -354,7 +413,8 @@
'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo', 'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo',
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls', 'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen', 'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent' 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'EnableUpstreamTrailerLayout'
]; ];
keys.forEach(function (key) { keys.forEach(function (key) {
@@ -388,6 +448,26 @@
updateDesc(); updateDesc();
} }
// Handle Prefer Local Trailers visibility
var enableVideoBackdropCheckbox = page.querySelector('#EnableVideoBackdrop');
var preferLocalContainer = page.querySelector('#PreferLocalTrailersContainer');
var upstreamLayoutContainer = page.querySelector('#UpstreamTrailerLayoutContainer');
function updatePreferLocalVisibility() {
if (enableVideoBackdropCheckbox && enableVideoBackdropCheckbox.checked) {
if (preferLocalContainer) preferLocalContainer.style.display = 'block';
if (upstreamLayoutContainer) upstreamLayoutContainer.style.display = 'block';
} else {
if (preferLocalContainer) preferLocalContainer.style.display = 'none';
if (upstreamLayoutContainer) upstreamLayoutContainer.style.display = 'none';
}
}
if (enableVideoBackdropCheckbox) {
enableVideoBackdropCheckbox.addEventListener('change', updatePreferLocalVisibility);
updatePreferLocalVisibility();
}
Dashboard.hideLoadingMsg(); Dashboard.hideLoadingMsg();
}); });
}, },
@@ -403,7 +483,8 @@
'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo', 'WaitForTrailerToEnd', 'StartMuted', 'FullWidthVideo', 'EnableMobileVideo',
'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls', 'ShowTrailerButton', 'AlwaysShowArrows', 'EnableKeyboardControls',
'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen', 'EnableCustomMediaIds', 'CustomMediaIds', 'EnableLoadingScreen',
'EnableSeasonalContent' 'EnableSeasonalContent', 'EnableClientSideSettings', 'SortBy', 'SortOrder',
'PreferLocalTrailers', 'EnableUpstreamTrailerLayout'
]; ];
keys.forEach(function (key) { keys.forEach(function (key) {

View File

@@ -12,7 +12,7 @@
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> --> <!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
<Title>Jellyfin Media Bar Enhanced Plugin</Title> <Title>Jellyfin Media Bar Enhanced Plugin</Title>
<Authors>CodeDevMLH</Authors> <Authors>CodeDevMLH</Authors>
<Version>1.2.3.7</Version> <Version>1.5.0.12</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

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="100%"
viewBox="0 0 1350 614"
enable-background="new 0 0 1350 614"
xml:space="preserve"
sodipodi:docname="file.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs12" /><sodipodi:namedview
id="namedview12"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="0.6577778"
inkscape:cx="949.40875"
inkscape:cy="353.46283"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M498.548157,519.821655 C508.235687,521.444031 517.517883,522.883118 526.789673,524.386841 C536.062744,525.890869 545.290039,527.733582 554.600769,528.937012 C569.316772,530.839050 584.077454,532.410095 598.836426,533.957336 C604.395325,534.539978 609.999268,534.894470 615.587463,534.962463 C649.167603,535.371338 682.751465,536.051270 716.329163,535.842651 C733.921753,535.733337 751.570740,534.638367 769.072815,532.819946 C786.970642,530.960388 804.773499,527.980896 822.517700,524.912109 C837.109558,522.388550 851.563538,519.058533 866.055603,515.977478 C868.387451,515.481689 870.928345,514.879272 872.806641,513.535217 C879.726013,508.583801 887.504639,508.919464 895.403381,508.953278 C923.388123,509.073120 951.373596,509.000000 979.719482,509.000000 C982.161377,520.378662 984.893127,531.637695 981.961182,543.227234 C979.976440,551.072632 973.778442,555.828491 967.460266,559.698853 C953.314880,568.364014 937.162048,571.950806 921.308411,575.872864 C904.270447,580.087952 887.066101,583.750244 869.788757,586.839539 C856.508240,589.214233 842.995605,590.277039 829.598083,592.014893 C817.345276,593.604248 805.122803,595.434509 792.859192,596.931213 C785.386536,597.843262 777.873047,598.539368 770.356934,598.946899 C760.284119,599.493164 750.191467,599.679504 740.106567,599.996582 C729.022766,600.345154 717.940002,600.757629 706.853455,600.980286 C686.771912,601.383606 666.685425,602.025940 646.605713,601.887451 C630.265808,601.774658 613.922607,600.848145 597.599915,599.941284 C584.830078,599.231812 572.043884,598.388062 559.347107,596.902161 C542.894592,594.976624 526.524048,592.351746 510.117493,590.032715 C498.219177,588.351013 486.247437,587.069946 474.439728,584.896851 C460.666870,582.362061 446.959656,579.361694 433.368530,575.981812 C421.710663,573.082764 410.121399,569.759766 398.757050,565.877136 C390.349060,563.004578 382.063538,559.402710 375.940796,552.427185 C368.723480,544.204651 369.426941,534.276978 370.326233,524.483704 C370.763611,519.720825 372.250519,515.054382 373.336273,510.000000 C379.597778,510.000000 385.905701,510.000000 392.213623,510.000000 C416.043518,510.000000 439.873474,509.974213 463.703278,510.020355 C468.857361,510.030334 473.744324,509.818298 478.814545,512.700806 C484.680084,516.035461 491.648621,517.429932 498.548157,519.821655 z"
id="path2" />
<path
fill="#c8c8c8"
opacity="1"
stroke="none"
d="m 330.93526,188.95459 c 0.0532,1.16148 0.0581,2.32297 0.0581,3.48445 0.002,83.55873 -0.0386,167.75573 -0.57327,251.76418 -44.6889,0.44479 -88.81469,-0.19785 -132.97407,-0.2035 -13.11818,-0.002 -20.44468,-7.24185 -20.44509,-20.21618 -0.002,-70.32166 0.28149,-140.64545 -0.24711,-210.96311 -0.10208,-13.57908 11.89072,-24.0623 24.10976,-23.96327 43.32297,0.35117 86.74576,0.11257 130.07169,0.0974 z"
id="path3"
sodipodi:nodetypes="cccsssscc" />
<path
fill="#cacaca"
opacity="1"
stroke="none"
d="m 1022.16,444.04364 c -0.053,-1.16144 -0.1533,-2.32288 -0.1533,-3.48431 0,-83.22391 0,-167.06543 0.5397,-250.74024 44.4984,-0.44619 88.4667,0.17002 132.435,0.20091 1.9751,10e-4 4.0287,2e-5 5.9117,0.50249 9.4167,2.51288 15.1069,10.15306 15.1069,20.00441 0,70.78233 -0.01,141.56468 0.01,212.34703 0,11.71546 -8.3125,21.49304 -20.9484,21.33988 -44.2946,-0.53681 -88.6475,0.5279 -132.9487,0.54246 z"
id="path4"
sodipodi:nodetypes="cccssssscc" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M395.009064,492.000000 C388.373993,491.343384 382.134369,491.180939 376.119720,489.933716 C360.581818,486.711700 348.845917,469.578186 348.888336,452.941223 C349.121979,361.276459 348.856903,269.610443 349.117371,177.945831 C349.171234,158.979095 361.452057,145.815338 378.579987,141.080048 C381.670135,140.225739 385.026764,140.042633 388.260437,140.041855 C580.424316,139.995056 772.588196,140.032410 964.752075,139.947540 C983.056519,139.939468 999.484863,152.050247 1002.960144,170.649078 C1003.555847,173.837250 1003.955872,177.116028 1003.957764,180.353241 C1004.010315,271.518555 1003.995422,362.683899 1004.003967,453.849243 C1004.005615,471.728058 992.793335,486.960602 974.694824,490.930511 C971.377319,491.658203 967.901123,491.956940 964.498047,491.957794 C774.834351,492.005280 585.170715,492.000000 395.009064,492.000000 M366.000000,444.473267 C366.000000,445.473083 366.077789,446.479889 365.988403,447.471649 C364.525879,463.702362 376.379547,474.760803 392.373016,474.218567 C416.907867,473.386719 441.491730,474.000000 466.054779,474.000000 C631.708801,474.000000 797.362976,473.895935 963.016418,474.208832 C975.500122,474.232391 987.143127,463.686707 987.110474,450.109283 C986.896118,360.949951 986.935608,271.789764 987.060913,182.630081 C987.082764,167.068771 976.391968,156.885590 961.346191,156.898560 C779.693542,157.055176 598.040710,157.000000 416.387909,157.000015 C407.555267,157.000015 398.721680,156.919998 389.890198,157.021133 C379.513062,157.139969 369.799591,164.361786 367.516266,174.235321 C366.373566,179.176605 366.064880,184.403137 366.059814,189.501343 C365.975830,274.161438 366.000000,358.821686 366.000000,444.473267 z"
id="path5" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M1022.085632,444.503815 C1066.461182,444.029083 1110.766602,443.677002 1155.061157,444.213806 C1167.697144,444.366974 1176.012939,434.589386 1176.009644,422.873932 C1175.990234,352.091583 1176.000000,281.309235 1176.000000,210.526901 C1176.000000,200.675552 1170.309814,193.035370 1160.893066,190.522491 C1159.010132,190.020020 1156.956543,190.021393 1154.981445,190.020004 C1111.013062,189.989136 1067.044800,189.990509 1022.618164,189.970871 C1022.159912,185.153610 1022.159912,180.351242 1022.159912,175.223175 C1023.488647,175.146652 1024.761597,175.009567 1026.034424,175.009247 C1069.847046,174.998154 1113.660400,174.850555 1157.471680,175.086029 C1171.636108,175.162170 1181.810181,182.166565 1188.159180,194.986008 C1188.953613,196.590164 1192.106445,197.862457 1194.174561,197.874329 C1225.658569,198.055084 1257.144043,198.000000 1288.629150,198.000000 C1296.625366,198.000000 1304.621582,198.006012 1312.617798,197.998688 C1327.143555,197.985397 1341.007446,211.825241 1341.004761,226.425781 C1340.993286,287.063751 1341.126831,347.702118 1340.917603,408.339386 C1340.859741,425.102753 1334.462280,432.430908 1319.805664,438.607025 C1318.373657,439.210449 1316.552856,438.985443 1314.912476,438.986176 C1274.764771,439.003845 1234.617065,439.049438 1194.469727,438.913055 C1190.980713,438.901215 1189.458252,440.058899 1187.874390,443.116028 C1182.058105,454.342072 1172.220093,460.703491 1159.901367,460.820709 C1114.260986,461.255157 1068.613892,460.968658 1022.969238,460.944519 C1022.828308,460.944427 1022.687500,460.819489 1022.011292,460.499634 C1022.011292,455.500580 1022.011292,450.232269 1022.085632,444.503815 M1247.500000,211.000000 C1231.341675,211.000000 1215.183350,210.992218 1199.025024,211.003860 C1192.604980,211.008499 1191.005493,212.565262 1191.004761,218.862900 C1190.997070,284.995239 1190.996216,351.127563 1191.005737,417.259918 C1191.006714,423.998322 1192.925781,425.988373 1199.485962,425.991455 C1236.799927,426.009003 1274.113892,425.976929 1311.427856,426.022675 C1317.619751,426.030273 1322.117432,423.150269 1324.465454,417.754608 C1326.001587,414.224365 1326.887695,410.114258 1326.897217,406.260986 C1327.044556,346.958801 1327.000488,287.656097 1326.999512,228.353546 C1326.999390,217.144135 1320.810547,211.008224 1309.468384,211.002762 C1289.145630,210.992966 1268.822754,211.000000 1247.500000,211.000000 z"
id="path6" />
<path
fill="#ffffff"
opacity="1"
stroke="none"
d="m 330.91425,188.95955 c -43.39994,0.47338 -86.72771,0.24878 -130.05068,-0.10239 -12.21904,-0.099 -24.21184,10.38419 -24.10976,23.96327 0.5286,70.31766 0.24494,140.64145 0.24711,210.96311 4.1e-4,12.97433 7.32691,20.21451 20.44509,20.21618 44.15938,0.006 88.31876,0.01 132.93637,0.0304 0.45826,5.48407 0.45826,10.95264 0.45826,16.96985 h -5.38221 c -43.31478,0 -86.6306,0.16705 -129.94372,-0.10172 -13.00781,-0.0807 -23.54056,-5.59921 -29.7357,-17.6466 -1.61699,-3.14444 -3.63599,-4.35541 -7.30498,-4.34201 -39.64915,0.1448 -79.29908,0.0573 -118.948685,0.1159 -8.759545,0.0129 -15.254898,-4.23486 -20.405224,-10.70178 -5.312873,-6.67105 -7.246366,-14.40582 -7.218926,-23.05365 0.188187,-59.30734 0.09876,-118.61557 0.09882,-177.92351 1.1e-5,-11.41679 8.002948,-23.41223 18.672981,-27.12843 3.893362,-1.35602 8.201628,-2.10867 12.324739,-2.12553 38.316288,-0.15668 76.63382,-0.16838 114.94993,0.008 4.54648,0.0209 6.93851,-1.56689 9.02278,-5.48947 5.92766,-11.15582 15.14014,-17.58424 28.12126,-17.59602 C 238.40646,174.97557 281.72125,175 325.03604,175 h 5.95221 c 0,4.49806 -0.0475,6.53519 -0.12151,11.26344 M 75.5,426 c 25.9955,0 51.991,0.002 77.9865,-0.002 7.14871,-0.001 8.51109,-1.33438 8.51145,-8.35559 0.003,-66.15506 0.004,-132.31012 -7.3e-4,-198.46518 -4.9e-4,-6.96535 -1.18309,-8.17265 -8.02932,-8.17404 -37.32679,-0.008 -74.653577,-0.0119 -111.980363,0.002 -9.938388,0.004 -15.978248,6.1517 -15.984113,16.22824 -0.01154,19.82986 -0.0034,39.65971 -0.0034,59.48958 0,39.6597 0.195755,79.32095 -0.116185,118.97821 -0.09684,12.31097 8.311491,21.3251 20.131846,20.44104 C 55.46117,425.43628 65.001663,426 75.5,426 Z"
id="path7"
sodipodi:nodetypes="cssssccssssssssssssssccsssssssssss" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M752.998535,122.000000 C697.384888,122.000000 642.270752,122.000031 587.156677,121.999809 C585.991150,121.999802 584.755127,122.248329 583.674011,121.945503 C581.040527,121.207886 579.301270,119.989822 581.538269,116.943802 C595.221191,98.312027 608.645996,79.486267 622.579712,61.044662 C631.013855,49.881832 639.965149,39.081825 649.147034,28.521265 C655.735779,20.943233 663.853149,14.640454 674.239014,14.266470 C682.228760,13.978765 690.355103,15.915215 696.482056,22.029982 C710.278809,35.799351 722.732300,50.695175 734.200928,66.471100 C745.246033,81.664513 756.752502,96.522255 768.020935,111.553917 C769.170105,113.086906 770.086121,114.795883 771.095093,116.432625 C773.244812,119.919868 772.167419,121.913857 767.983948,121.981750 C763.156494,122.060104 758.326782,121.999985 752.998535,122.000000 z"
id="path8" />
<path
fill="#B9B9B9"
opacity="1.000000"
stroke="none"
d=" M366.000000,443.977570 C366.000000,358.821686 365.975830,274.161438 366.059814,189.501343 C366.064880,184.403137 366.373566,179.176605 367.516266,174.235321 C369.799591,164.361786 379.513062,157.139969 389.890198,157.021133 C398.721680,156.919998 407.555267,157.000015 416.387909,157.000015 C598.040710,157.000000 779.693542,157.055176 961.346191,156.898560 C976.391968,156.885590 987.082764,167.068771 987.060913,182.630081 C986.935608,271.789764 986.896118,360.949951 987.110474,450.109283 C987.143127,463.686707 975.500122,474.232391 963.016418,474.208832 C797.362976,473.895935 631.708801,474.000000 466.054779,474.000000 C441.491730,474.000000 416.907867,473.386719 392.373016,474.218567 C376.379547,474.760803 364.525879,463.702362 365.988403,447.471649 C366.077789,446.479889 366.000000,445.473083 366.000000,443.977570 M612.844971,237.388443 C611.896790,239.785782 610.129822,242.180267 610.121521,244.580856 C609.958679,291.555145 609.984619,338.530182 610.022644,385.505005 C610.026978,390.825134 613.298340,395.021606 618.073486,396.969757 C622.378906,398.726318 626.389282,397.628296 629.872437,395.474457 C644.906433,386.178070 659.816589,376.677917 674.674377,367.100769 C694.658508,354.219269 714.593994,341.261017 734.470093,328.213562 C745.839661,320.750214 745.924316,311.287506 734.653076,303.900543 C710.193420,287.870148 685.640808,271.981476 661.117188,256.048767 C651.003967,249.478363 640.900208,242.892380 630.730652,236.409973 C623.383179,231.726349 619.020142,231.866287 612.844971,237.388443 z"
id="path9" />
<path
fill="#CFCFCF"
opacity="1.000000"
stroke="none"
d=" M1248.000000,211.000000 C1268.822754,211.000000 1289.145630,210.992966 1309.468384,211.002762 C1320.810547,211.008224 1326.999390,217.144135 1326.999512,228.353546 C1327.000488,287.656097 1327.044556,346.958801 1326.897217,406.260986 C1326.887695,410.114258 1326.001587,414.224365 1324.465454,417.754608 C1322.117432,423.150269 1317.619751,426.030273 1311.427856,426.022675 C1274.113892,425.976929 1236.799927,426.009003 1199.485962,425.991455 C1192.925781,425.988373 1191.006714,423.998322 1191.005737,417.259918 C1190.996216,351.127563 1190.997070,284.995239 1191.004761,218.862900 C1191.005493,212.565262 1192.604980,211.008499 1199.025024,211.003860 C1215.183350,210.992218 1231.341675,211.000000 1248.000000,211.000000 z"
id="path10" />
<path
fill="#C0C0C0"
opacity="1.000000"
stroke="none"
d=" M75.000000,426.000000 C65.001663,426.000000 55.461170,425.436279 46.015667,426.142670 C34.195312,427.026733 25.786983,418.012604 25.883821,405.701630 C26.195761,366.044373 26.000006,326.383118 26.000006,286.723419 C26.000004,266.893555 25.991882,247.063705 26.003424,227.233841 C26.009289,217.157303 32.049149,211.009308 41.987537,211.005600 C79.314323,210.991669 116.641106,210.996048 153.967896,211.003571 C160.814133,211.004959 161.996735,212.212265 161.997223,219.177612 C162.001892,285.332672 162.001419,351.487732 161.997955,417.642792 C161.997589,424.664001 160.635208,425.997284 153.486496,425.998383 C127.490997,426.002441 101.495499,426.000000 75.000000,426.000000 z"
id="path11" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M612.987183,237.031128 C619.020142,231.866287 623.383179,231.726349 630.730652,236.409973 C640.900208,242.892380 651.003967,249.478363 661.117188,256.048767 C685.640808,271.981476 710.193420,287.870148 734.653076,303.900543 C745.924316,311.287506 745.839661,320.750214 734.470093,328.213562 C714.593994,341.261017 694.658508,354.219269 674.674377,367.100769 C659.816589,376.677917 644.906433,386.178070 629.872437,395.474457 C626.389282,397.628296 622.378906,398.726318 618.073486,396.969757 C613.298340,395.021606 610.026978,390.825134 610.022644,385.505005 C609.984619,338.530182 609.958679,291.555145 610.121521,244.580856 C610.129822,242.180267 611.896790,239.785782 612.987183,237.031128 z"
id="path12" />
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="100%"
viewBox="0 0 661 614"
enable-background="new 0 0 661 614"
xml:space="preserve"
sodipodi:docname="logo.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs6" /><sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="1.3925081"
inkscape:cx="330.33918"
inkscape:cy="307"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M327.000000,536.000000 C342.496460,536.000000 357.498688,536.258484 372.487610,535.922241 C385.664062,535.626770 398.866150,535.102905 411.983582,533.888245 C428.681671,532.341858 445.408508,530.610413 461.935608,527.840759 C481.796478,524.512451 501.467499,520.042786 521.199707,515.965698 C523.529053,515.484375 526.041382,514.803894 527.927551,513.457397 C534.700745,508.622009 542.319885,508.932159 550.045410,508.959991 C578.189880,509.061340 606.334900,509.000000 634.740234,509.000000 C637.186890,520.472595 639.957642,531.779480 636.933289,543.351501 C634.903503,551.117737 628.718933,555.934631 622.355957,559.773132 C608.164856,568.333984 592.050842,571.987366 576.187866,575.886841 C559.302490,580.037537 542.257751,583.610168 525.170837,586.844788 C514.799194,588.808228 504.204498,589.571716 493.731018,591.025574 C485.218262,592.207336 476.759888,593.799255 468.234467,594.866150 C460.604767,595.821045 452.927185,596.476501 445.251709,596.965637 C427.840851,598.075317 410.418152,598.998474 393.000580,600.003540 C387.415436,600.325806 381.835144,600.855774 376.246216,600.966370 C355.166992,601.383362 334.085815,601.853882 313.004028,601.930115 C300.255890,601.976196 287.500092,601.463989 274.757141,600.963013 C262.081879,600.464600 249.388809,600.007324 236.757446,598.910950 C221.974625,597.627930 207.229050,595.838806 192.505737,593.967224 C175.589020,591.816711 158.624741,589.840515 141.845245,586.863647 C122.976410,583.516113 104.227318,579.411011 85.549820,575.099731 C74.786339,572.615234 64.115028,569.507751 53.686581,565.866333 C45.305603,562.939758 36.992039,559.384155 30.888775,552.377869 C23.703732,544.129883 24.449305,534.207397 25.353891,524.421143 C25.793415,519.666260 27.276276,515.007690 28.352476,510.000000 C31.826927,510.000000 35.304031,510.000000 38.781136,510.000000 C64.774979,510.000000 90.768852,509.977386 116.762634,510.017090 C122.732361,510.026184 128.391159,510.172974 134.351212,512.686401 C143.412094,516.507568 153.202728,518.885010 162.889816,520.866455 C176.838547,523.719727 190.930084,525.975830 205.039062,527.917175 C219.893982,529.961182 234.816391,531.620972 249.755615,532.913391 C262.386749,534.006165 275.078796,534.468628 287.753540,534.962585 C300.662903,535.465698 313.584015,535.666687 327.000000,536.000000 z"
id="path2" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M606.999634,140.000000 C616.128296,140.706787 625.146667,140.066467 633.298218,142.423615 C646.862671,146.345963 654.950256,156.523895 657.932922,170.401443 C658.647095,173.724014 658.955444,177.196487 658.957458,180.600327 C659.010925,271.765411 659.009338,362.930511 658.991943,454.095612 C658.988525,471.780304 647.686096,487.160645 629.451294,490.947906 C626.281982,491.606171 622.988159,491.957306 619.751709,491.958099 C427.255005,492.004913 234.758301,491.987091 42.261608,492.021698 C24.730577,492.024841 8.072522,479.153046 5.027515,461.896179 C4.492155,458.862152 4.043763,455.761963 4.042027,452.692047 C3.990298,361.193634 3.871316,269.694855 4.104546,178.197021 C4.153368,159.043365 16.296051,146.063629 33.331520,141.092468 C36.402164,140.196411 39.775433,140.042984 43.009106,140.042160 C230.839310,139.994934 418.669525,140.000000 606.999634,140.000000 M359.500000,157.000000 C289.517120,157.000000 219.534225,157.000000 149.551346,157.000000 C114.893280,157.000000 80.235199,156.980255 45.577160,157.011627 C34.607761,157.021545 24.557083,164.368423 22.403088,174.518036 C21.314177,179.648972 21.059866,185.035080 21.055122,190.306381 C20.977442,276.618225 21.132351,362.930573 20.868862,449.241699 C20.825279,463.518036 31.508949,474.175995 45.628529,474.158508 C236.414246,473.922333 427.200470,473.908447 617.986023,474.208954 C630.511597,474.228729 642.140198,463.677826 642.108398,450.146484 C641.898560,360.835785 642.000916,271.524384 641.999146,182.213196 C641.998840,166.797821 632.262817,157.001709 616.936340,157.001236 C531.457581,156.998657 445.978790,157.000000 359.500000,157.000000 z"
id="path3" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M410.281616,93.782341 C415.291992,100.787689 420.140594,107.470741 424.713318,114.337517 C425.884766,116.096649 426.184479,118.436256 426.884674,120.509186 C424.913452,121.006126 422.942780,121.934616 420.970886,121.937248 C363.000671,122.014679 305.030334,122.011337 247.060028,121.969559 C243.859818,121.967247 240.659912,121.524078 236.054047,121.181580 C238.077652,116.962379 239.199081,113.266785 241.360825,110.339432 C254.448532,92.616531 267.699677,75.012611 281.049347,57.485538 C288.331970,47.924042 295.607147,38.325375 303.457062,29.236992 C310.346344,21.260773 318.726776,14.498528 329.750397,14.243196 C337.574341,14.061975 345.864899,15.756383 351.433380,22.105671 C364.247803,36.716980 376.755737,51.604027 389.124115,66.596909 C396.351105,75.357414 403.069855,84.537186 410.281616,93.782341 z"
id="path4" />
<path
fill="#B9B9B9"
opacity="1.000000"
stroke="none"
d=" M360.000000,157.000000 C445.978790,157.000000 531.457581,156.998657 616.936340,157.001236 C632.262817,157.001709 641.998840,166.797821 641.999146,182.213196 C642.000916,271.524384 641.898560,360.835785 642.108398,450.146484 C642.140198,463.677826 630.511597,474.228729 617.986023,474.208954 C427.200470,473.908447 236.414246,473.922333 45.628529,474.158508 C31.508949,474.175995 20.825279,463.518036 20.868862,449.241699 C21.132351,362.930573 20.977442,276.618225 21.055122,190.306381 C21.059866,185.035080 21.314177,179.648972 22.403088,174.518036 C24.557083,164.368423 34.607761,157.021545 45.577160,157.011627 C80.235199,156.980255 114.893280,157.000000 149.551346,157.000000 C219.534225,157.000000 289.517120,157.000000 360.000000,157.000000 M265.000000,302.500000 C265.000000,329.638916 264.986816,356.777832 265.030060,383.916656 C265.032898,385.700653 265.134369,387.610443 265.756378,389.246338 C268.897522,397.507599 276.467865,400.490845 284.746582,395.464508 C301.954895,385.016602 318.697327,373.801392 335.631592,362.902100 C353.537262,351.377533 371.500580,339.941193 389.328918,328.298248 C400.978180,320.690613 400.993195,311.320953 389.382721,303.731628 C365.885529,288.372528 342.291901,273.160889 318.730255,257.900482 C307.365387,250.539673 296.161346,242.908707 284.556580,235.946579 C273.636658,229.395309 265.002625,234.531113 265.000702,247.055817 C264.997925,265.203888 265.000000,283.351929 265.000000,302.500000 z"
id="path5" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M265.000000,302.000000 C265.000000,283.351929 264.997925,265.203888 265.000702,247.055817 C265.002625,234.531113 273.636658,229.395309 284.556580,235.946579 C296.161346,242.908707 307.365387,250.539673 318.730255,257.900482 C342.291901,273.160889 365.885529,288.372528 389.382721,303.731628 C400.993195,311.320953 400.978180,320.690613 389.328918,328.298248 C371.500580,339.941193 353.537262,351.377533 335.631592,362.902100 C318.697327,373.801392 301.954895,385.016602 284.746582,395.464508 C276.467865,400.490845 268.897522,397.507599 265.756378,389.246338 C265.134369,387.610443 265.032898,385.700653 265.030060,383.916656 C264.986816,356.777832 265.000000,329.638916 265.000000,302.000000 z"
id="path6" />
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="100%"
viewBox="0 0 1039 614"
enable-background="new 0 0 1039 614"
xml:space="preserve"
sodipodi:docname="MediaBar_logo_SW_mono_SHORT.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs10" /><sodipodi:namedview
id="namedview10"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="0.98465195"
inkscape:cx="383.3842"
inkscape:cy="268.62284"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M215.230072,518.448547 C216.097900,515.443542 216.766510,512.833374 217.492325,510.000000 C245.648331,510.000000 273.793915,510.000000 301.939484,510.000000 C302.939331,510.000000 303.955444,510.117462 304.936493,509.981628 C313.343964,508.817383 320.674316,511.954315 328.378204,514.797546 C337.342743,518.106018 346.869507,520.088257 356.294983,521.906006 C371.867737,524.909302 387.508484,527.647339 403.209564,529.868103 C414.376190,531.447510 425.675507,532.195068 436.940094,532.947632 C450.260864,533.837524 463.601135,534.604797 476.945160,534.939026 C499.188751,535.496155 521.441528,535.867615 543.691467,535.929138 C558.440735,535.969788 573.226501,535.846863 587.932678,534.857544 C603.060181,533.839905 618.131775,531.848083 633.189392,529.973938 C647.529541,528.189148 661.894775,526.424377 676.112915,523.888184 C692.549683,520.956238 709.140747,518.246887 724.096924,510.011292 C725.304810,509.346222 726.865112,509.043365 728.262390,509.041016 C759.918213,508.987732 791.574036,509.000031 823.689392,509.000031 C826.219116,520.269714 828.853699,531.504944 825.976807,543.085388 C823.978455,551.129578 817.664246,555.932556 811.155334,559.864441 C798.097412,567.752258 783.349731,571.421509 768.683411,574.911438 C753.177429,578.601196 737.558228,581.845886 721.920593,584.941956 C710.332214,587.236267 698.673035,589.250488 686.976013,590.896667 C675.032043,592.577637 663.003235,593.655762 651.011902,595.000488 C639.175720,596.327820 627.352295,597.784241 615.499207,598.937012 C609.108643,599.558533 602.671326,599.774353 596.248901,599.976440 C583.167419,600.388245 570.083069,600.730896 556.997620,600.985046 C536.915833,601.375183 516.833008,601.840393 496.749054,601.931274 C485.666565,601.981384 474.576111,601.445251 463.497986,600.969482 C450.989288,600.432373 438.468353,599.937683 425.993683,598.921692 C414.128784,597.955383 402.311890,596.381531 390.482666,594.999573 C376.571899,593.374573 362.627686,591.959412 348.777679,589.913574 C335.662842,587.976318 322.697510,585.027222 309.582977,583.087280 C290.888214,580.321899 272.677460,575.598938 254.744934,569.879639 C245.490875,566.928101 236.261154,563.377563 227.757507,558.744751 C219.453522,554.220642 213.865524,546.819397 214.043716,536.577576 C214.146606,530.662292 214.687256,524.754578 215.230072,518.448547 z"
id="path2" />
<path
fill="#C8C8C8"
opacity="1.000000"
stroke="none"
d=" M174.078720,187.882538 C174.385803,189.408966 174.960938,190.935257 174.961624,192.461823 C174.999268,276.011963 174.988754,359.562103 174.453644,443.563446 C129.277191,444.009735 84.630196,444.013519 39.983231,443.993744 C29.198257,443.988983 21.325663,436.491302 21.028820,425.828705 C20.864725,419.934387 20.999969,414.031769 20.999973,408.132874 C21.000015,346.326721 21.001759,284.520538 20.990532,222.714386 C20.990231,221.055740 20.676064,219.383453 20.805677,217.740997 C21.238888,212.251480 20.492048,206.188721 22.610405,201.416809 C26.479988,192.699936 34.791191,189.113480 44.038651,189.075058 C85.519699,188.902679 127.001884,189.014496 168.483551,188.952637 C170.349152,188.949860 172.213684,188.255020 174.078720,187.882538 z"
id="path3" />
<path
fill="#CACACA"
opacity="1.000000"
stroke="none"
d=" M866.159912,444.043640 C866.106628,442.882202 866.006592,441.720764 866.006592,440.559326 C866.004822,357.335419 866.010986,274.111511 866.546448,190.436707 C911.044739,189.990524 955.013000,189.989151 998.981323,190.020020 C1000.956421,190.021408 1003.010071,190.019989 1004.893005,190.522461 C1014.309570,193.035324 1019.999939,200.675659 1019.999939,210.526749 C1020.000000,281.309113 1019.990295,352.091492 1020.009766,422.873871 C1020.012939,434.589478 1011.696716,444.366974 999.061157,444.213806 C954.766541,443.676971 910.461243,444.029114 866.159912,444.043640 z"
id="path4" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M642.000000,492.000000 C504.668976,492.000000 367.837982,492.007538 231.006973,491.990753 C213.329834,491.988586 197.742874,479.343262 194.068604,462.633972 C193.339264,459.317200 193.044693,455.840637 193.042694,452.437439 C192.989120,360.939026 192.856583,269.440186 193.117493,177.942459 C193.171539,158.983154 205.450027,145.812973 222.583267,141.079895 C225.673691,140.226166 229.030045,140.042664 232.263763,140.041870 C424.427155,139.995071 616.590576,140.032379 808.753967,139.947617 C827.057373,139.939545 843.486572,152.051819 846.960083,170.650864 C847.555481,173.839096 847.955994,177.117783 847.957886,180.354980 C848.010376,271.520081 847.995483,362.685181 848.003906,453.850311 C848.005554,471.728851 836.792358,486.961578 818.693909,490.930542 C815.376343,491.658081 811.900452,491.952789 808.497070,491.956573 C753.164734,492.017883 697.832397,492.000000 642.000000,492.000000 M408.500000,474.000000 C541.155823,474.000000 673.812073,473.849609 806.466797,474.234650 C819.614380,474.272797 831.175842,463.575836 831.134583,449.658783 C830.870544,360.666779 830.931946,271.673523 831.064148,182.680923 C831.087341,167.063034 820.378662,156.888992 805.397217,156.900894 C615.746033,157.051605 426.094727,157.000000 236.443451,157.000046 C235.443527,157.000046 234.429825,156.892563 233.445816,157.017914 C222.831177,158.369949 214.189850,163.378632 211.540894,174.189514 C210.374725,178.948914 210.065567,184.018188 210.060806,188.947449 C209.976807,275.940308 209.998886,362.933258 210.001083,449.926208 C210.001450,464.527679 219.366562,473.994415 233.819275,473.998169 C259.048828,474.004730 284.278412,474.000000 309.507965,474.000000 C342.171967,474.000000 374.835999,474.000000 408.500000,474.000000 z"
id="path5" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M470.999268,56.008606 C478.080780,46.904430 484.917328,37.975853 492.243896,29.469269 C499.274078,21.306780 507.730499,14.547179 518.938110,14.236814 C526.245911,14.034445 534.043579,15.664103 539.490784,21.206964 C547.066956,28.916189 554.111450,37.174259 561.055176,45.472176 C569.831726,55.960323 578.543762,66.523552 586.798096,77.422142 C596.652222,90.432991 605.991638,103.833656 615.555847,117.064087 C617.796204,120.163223 615.770142,121.274780 613.182678,121.957214 C612.247803,122.203773 611.193848,121.999855 610.194275,121.999855 C550.384399,122.000015 490.574524,122.000038 430.764648,121.999641 C429.598450,121.999634 428.321289,122.302048 427.288483,121.929474 C424.932068,121.079414 423.416840,119.791931 425.460999,117.011757 C439.162445,98.377037 452.780548,79.681015 466.488373,61.051033 C467.757965,59.325569 469.464752,57.921833 470.999268,56.008606 z"
id="path6" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M866.085632,444.503815 C910.461243,444.029114 954.766541,443.676971 999.061157,444.213806 C1011.696716,444.366974 1020.012939,434.589478 1020.009766,422.873871 C1019.990295,352.091492 1020.000000,281.309113 1019.999939,210.526749 C1019.999939,200.675659 1014.309570,193.035324 1004.893005,190.522461 C1003.010071,190.019989 1000.956421,190.021408 998.981323,190.020020 C955.013000,189.989151 911.044739,189.990524 866.618164,189.970871 C866.159912,185.154236 866.159912,180.352509 866.159912,175.219986 C867.653748,175.144806 869.092346,175.009506 870.530884,175.009140 C914.169250,174.997925 957.808472,174.851959 1001.445618,175.084991 C1015.605042,175.160599 1025.345459,182.384720 1032.258301,194.884445 C1035.300171,200.384384 1034.944214,205.512314 1034.949707,210.926346 C1035.024414,285.044891 1034.976929,359.163574 1035.034058,433.282135 C1035.040161,441.160797 1030.435303,446.713684 1025.517456,451.668091 C1017.754089,459.489075 1007.615479,461.114532 996.880737,461.070099 C955.241760,460.897766 913.601685,461.000000 871.961975,461.000000 C870.155457,461.000000 868.348877,461.000000 866.011292,461.000000 C866.011292,455.499420 866.011292,450.231689 866.085632,444.503815 z"
id="path7" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M174.224472,187.493225 C172.213684,188.255020 170.349152,188.949860 168.483551,188.952637 C127.001884,189.014496 85.519699,188.902679 44.038651,189.075058 C34.791191,189.113480 26.479988,192.699936 22.610405,201.416809 C20.492048,206.188721 21.238888,212.251480 20.805677,217.740997 C20.676064,219.383453 20.990231,221.055740 20.990532,222.714386 C21.001759,284.520538 21.000015,346.326721 20.999973,408.132874 C20.999969,414.031769 20.864725,419.934387 21.028820,425.828705 C21.325663,436.491302 29.198257,443.988983 39.983231,443.993744 C84.630196,444.013519 129.277191,444.009735 174.382401,444.030182 C174.737961,448.729980 174.817245,453.430756 174.393692,458.085815 C174.298065,459.136627 172.588226,460.310577 171.366852,460.901947 C170.420822,461.359955 169.077637,460.999695 167.911011,460.999695 C125.078690,461.000061 82.245537,461.148987 39.414570,460.908203 C26.748209,460.837006 16.930914,455.100189 9.852262,444.182495 C6.592582,439.154968 5.933938,434.201477 5.940778,428.565857 C6.029986,355.067688 6.164409,281.568512 5.764170,208.072418 C5.720665,200.083664 9.208368,194.251083 13.163262,188.681961 C19.964035,179.105347 29.633146,174.772690 41.836056,174.866425 C85.991035,175.205673 130.150192,175.000000 175.168060,175.000000 C174.895874,179.129318 174.633041,183.116623 174.224472,187.493225 z"
id="path8" />
<path
fill="#B9B9B9"
opacity="1.000000"
stroke="none"
d=" M408.000000,474.000000 C374.835999,474.000000 342.171967,474.000000 309.507965,474.000000 C284.278412,474.000000 259.048828,474.004730 233.819275,473.998169 C219.366562,473.994415 210.001450,464.527679 210.001083,449.926208 C209.998886,362.933258 209.976807,275.940308 210.060806,188.947449 C210.065567,184.018188 210.374725,178.948914 211.540894,174.189514 C214.189850,163.378632 222.831177,158.369949 233.445816,157.017914 C234.429825,156.892563 235.443527,157.000046 236.443451,157.000046 C426.094727,157.000000 615.746033,157.051605 805.397217,156.900894 C820.378662,156.888992 831.087341,167.063034 831.064148,182.680923 C830.931946,271.673523 830.870544,360.666779 831.134583,449.658783 C831.175842,463.575836 819.614380,474.272797 806.466797,474.234650 C673.812073,473.849609 541.155823,474.000000 408.000000,474.000000 M586.229492,319.395325 C587.844971,313.210358 585.613464,308.445679 578.616882,303.871887 C551.096436,285.881165 523.586060,267.874084 495.963287,250.041458 C487.238708,244.409042 478.472534,238.692429 469.170746,234.150177 C461.849823,230.575211 454.054626,236.239990 454.040649,244.358078 C453.959473,291.498627 453.959900,338.639465 454.058899,385.779907 C454.069946,391.030212 456.924652,394.877625 461.877869,396.864655 C468.895386,399.679810 474.590881,395.717529 479.854248,392.373016 C498.671570,380.415802 517.141296,367.913239 535.831177,355.753143 C548.286072,347.649658 561.049011,340.010132 573.349304,331.683136 C578.020752,328.520630 581.782837,324.014771 586.229492,319.395325 z"
id="path9" />
<path
fill="#FFFFFF"
opacity="1.000000"
stroke="none"
d=" M586.094482,319.758270 C581.782837,324.014771 578.020752,328.520630 573.349304,331.683136 C561.049011,340.010132 548.286072,347.649658 535.831177,355.753143 C517.141296,367.913239 498.671570,380.415802 479.854248,392.373016 C474.590881,395.717529 468.895386,399.679810 461.877869,396.864655 C456.924652,394.877625 454.069946,391.030212 454.058899,385.779907 C453.959900,338.639465 453.959473,291.498627 454.040649,244.358078 C454.054626,236.239990 461.849823,230.575211 469.170746,234.150177 C478.472534,238.692429 487.238708,244.409042 495.963287,250.041458 C523.586060,267.874084 551.096436,285.881165 578.616882,303.871887 C585.613464,308.445679 587.844971,313.210358 586.094482,319.758270 z"
id="path10" />
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -13,6 +13,7 @@
* - option to set a maximum for the pagination dots (will turn into a counter style if exceeded) * - option to set a maximum for the pagination dots (will turn into a counter style if exceeded)
* - option to disable loading screen * - option to disable loading screen
* - option to put collection (boxsets) IDs into the slideshow to display their items * - option to put collection (boxsets) IDs into the slideshow to display their items
* - option to enable client-side settings (allow users to override settings locally on their device)
*/ */
@import url(https://fonts.googleapis.com/css2?family=Archivo+Narrow:ital,wght@0,400..700;1,400..700&display=swap); @import url(https://fonts.googleapis.com/css2?family=Archivo+Narrow:ital,wght@0,400..700;1,400..700&display=swap);
@@ -342,6 +343,49 @@
z-index: 1; z-index: 1;
} }
.video-container {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 0;
z-index: 2;
overflow: hidden;
transition:
width 0.5s ease-in-out,
opacity 0.5s ease-in-out;
opacity: 0;
pointer-events: none;
mask-image:
linear-gradient(to right, transparent 10%, black 30%),
linear-gradient(to top, transparent 2%, rgba(0, 0, 0, 0.5) 6%, black 8%);
-webkit-mask-image:
linear-gradient(to right, transparent 10%, black 30%),
linear-gradient(to top, transparent 2%, rgba(0, 0, 0, 0.5) 6%, black 8%);
mask-composite: intersect;
-webkit-mask-composite: source-in;
}
.video-container.active {
opacity: 1;
pointer-events: auto;
}
.video-player {
width: 100%;
height: 100%;
}
/* Ensure video inside container fills it */
.video-container iframe,
.video-container video {
width: 100%;
height: 100%;
border: none;
}
.backdrop-container { .backdrop-container {
position: absolute; position: absolute;
top: 0%; top: 0%;
@@ -378,6 +422,10 @@
object-position: center 20%; object-position: center 20%;
border-radius: 5px; border-radius: 5px;
z-index: 3; z-index: 3;
transition:
width 0.5s ease-in-out,
mask-image 0.5s ease-in-out,
-webkit-mask-image 0.5s ease-in-out;
mask-image: linear-gradient(to top, mask-image: linear-gradient(to top,
#fff0 2%, #fff0 2%,
rgb(0 0 0 / 0.5) 6%, rgb(0 0 0 / 0.5) 6%,
@@ -388,6 +436,20 @@
#000000 8%); #000000 8%);
} }
.backdrop.with-video {
width: 100%;
mask-image:
linear-gradient(to top, #fff0 2%, rgb(0 0 0 / 0.5) 6%, #000000 8%),
linear-gradient(to right, black 30%, transparent 85%);
-webkit-mask-image:
linear-gradient(to top, #fff0 2%, rgb(0 0 0 / 0.5) 6%, #000000 8%),
linear-gradient(to right, black 30%, transparent 85%);
mask-composite: source-in;
-webkit-mask-composite: source-in;
}
.backdrop-overlay { .backdrop-overlay {
position: absolute; position: absolute;
top: 0; top: 0;

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:
@@ -13,6 +13,7 @@
* - option to set a maximum for the pagination dots (will turn into a counter style if exceeded) * - option to set a maximum for the pagination dots (will turn into a counter style if exceeded)
* - option to disable loading screen * - option to disable loading screen
* - option to put collection (boxsets) IDs into the slideshow to display their items * - option to put collection (boxsets) IDs into the slideshow to display their items
* - option to enable client-side settings (allow users to override settings locally on their device)
*/ */
//Core Module Configuration //Core Module Configuration
@@ -37,6 +38,7 @@ const CONFIG = {
slideAnimationEnabled: true, slideAnimationEnabled: true,
enableVideoBackdrop: true, enableVideoBackdrop: true,
useSponsorBlock: true, useSponsorBlock: true,
preferLocalTrailers: false,
waitForTrailerToEnd: true, waitForTrailerToEnd: true,
startMuted: true, startMuted: true,
fullWidthVideo: true, fullWidthVideo: true,
@@ -49,6 +51,10 @@ const CONFIG = {
enableSeasonalContent: false, enableSeasonalContent: false,
customMediaIds: "", customMediaIds: "",
enableLoadingScreen: true, enableLoadingScreen: true,
enableClientSideSettings: false,
enableUpstreamTrailerLayout: false,
sortBy: "Random",
sortOrder: "Ascending",
}; };
// State management // State management
@@ -78,6 +84,8 @@ const STATE = {
videoPlayers: {}, videoPlayers: {},
sponsorBlockInterval: null, sponsorBlockInterval: null,
isMuted: CONFIG.startMuted, isMuted: CONFIG.startMuted,
customTrailerUrls: {},
ytPromise: null,
}, },
}; };
@@ -206,7 +214,7 @@ const initLoadingScreen = () => {
if (!isHomePage) return; if (!isHomePage) return;
// Check LocalStorage for cached preference to avoid flash // Check LocalStorage for cached preference to avoid flash
const cachedSetting = localStorage.getItem('mediaBarEnhanced_enableLoadingScreen'); const cachedSetting = localStorage.getItem('mediaBarEnhanced-enableLoadingScreen');
if (cachedSetting === 'false') { if (cachedSetting === 'false') {
return; return;
} }
@@ -344,6 +352,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;
}; };
@@ -439,7 +448,7 @@ const fetchPluginConfig = async () => {
} }
// Sync to LocalStorage for next load // Sync to LocalStorage for next load
localStorage.setItem('mediaBarEnhanced_enableLoadingScreen', CONFIG.enableLoadingScreen); localStorage.setItem('mediaBarEnhanced-enableLoadingScreen', CONFIG.enableLoadingScreen);
console.log("✅ MediaBarEnhanced config loaded", CONFIG); console.log("✅ MediaBarEnhanced config loaded", CONFIG);
} }
@@ -455,6 +464,66 @@ waitForApiClientAndInitialize();
* Utility functions for slide creation and management * Utility functions for slide creation and management
*/ */
const SlideUtils = { const SlideUtils = {
/**
* Sorts items based on configuration
* @param {Array<Object>} items - Array of item objects
* @param {string} sortBy - Sort criteria
* @param {string} sortOrder - Sort order 'Ascending' or 'Descending'
* @returns {Array<Object>} Sorted array of items
*/
sortItems(items, sortBy, sortOrder) {
if (sortBy === 'Random' || sortBy === 'Original') {
return items;
}
const simpleCompare = (a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
};
const sorted = [...items].sort((a, b) => {
let valA, valB;
switch (sortBy) {
case 'PremiereDate':
valA = new Date(a.PremiereDate).getTime();
valB = new Date(b.PremiereDate).getTime();
break;
case 'ProductionYear':
valA = a.ProductionYear || 0;
valB = b.ProductionYear || 0;
break;
case 'CriticRating':
valA = a.CriticRating || 0;
valB = b.CriticRating || 0;
break;
case 'CommunityRating':
valA = a.CommunityRating || 0;
valB = b.CommunityRating || 0;
break;
case 'Runtime':
valA = a.RunTimeTicks || 0;
valB = b.RunTimeTicks || 0;
break;
case 'Name':
valA = (a.Name || '').toLowerCase();
valB = (b.Name || '').toLowerCase();
break;
default:
return 0;
}
return simpleCompare(valA, valB);
});
if (sortOrder === 'Descending') {
sorted.reverse();
}
return sorted;
},
/** /**
* Shuffles array elements randomly * Shuffles array elements randomly
* @param {Array} array - Array to shuffle * @param {Array} array - Array to shuffle
@@ -578,23 +647,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;
}, },
/** /**
@@ -953,8 +1024,8 @@ const ApiUtils = {
} }
const response = await fetch( const response = await fetch(
`${STATE.jellyfinData.serverAddress}/Items/${itemId}`, // `${STATE.jellyfinData.serverAddress}/Items/${itemId}`,
// `${STATE.jellyfinData.serverAddress}/Users/${STATE.jellyfinData.userId}/Items/${itemId}?Fields=Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,RunTimeTicks,ProductionYear,MediaSources`, `${STATE.jellyfinData.serverAddress}/Items/${itemId}?Fields=Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,ProductionYear,MediaSources,RunTimeTicks,LocalTrailerCount`,
{ {
headers: this.getAuthHeaders(), headers: this.getAuthHeaders(),
} }
@@ -1026,8 +1097,16 @@ const ApiUtils = {
console.log("Fetching random items from server..."); console.log("Fetching random items from server...");
let sortParams = `sortBy=${CONFIG.sortBy}`;
if (CONFIG.sortBy === 'Random' || CONFIG.sortBy === 'Original') {
sortParams = 'sortBy=Random';
} else {
sortParams += `&sortOrder=${CONFIG.sortOrder}`;
}
const response = await fetch( const response = await fetch(
`${STATE.jellyfinData.serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&sortBy=Random&isPlayed=False&enableUserData=true&Limit=${CONFIG.maxItems}&fields=Id`, `${STATE.jellyfinData.serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&${sortParams}&isPlayed=False&enableUserData=true&Limit=${CONFIG.maxItems}&fields=Id`,
{ {
headers: this.getAuthHeaders(), headers: this.getAuthHeaders(),
} }
@@ -1246,6 +1325,39 @@ const ApiUtils = {
console.error(`Error fetching collection items for ${collectionId}:`, error); console.error(`Error fetching collection items for ${collectionId}:`, error);
return []; return [];
} }
},
/**
* Fetches the first local trailer for an item
* @param {string} itemId - Item ID
* @returns {Promise<string|null>} Stream URL or null
*/
async fetchLocalTrailer(itemId) {
try {
const response = await fetch(
`${STATE.jellyfinData.serverAddress}/Users/${STATE.jellyfinData.userId}/Items/${itemId}/LocalTrailers`,
{
headers: this.getAuthHeaders(),
}
);
if (!response.ok) {
return null;
}
const trailers = await response.json();
if (trailers && trailers.length > 0) {
const trailer = trailers[0];
const mediaSourceId = trailer.MediaSources && trailer.MediaSources[0] ? trailer.MediaSources[0].Id : trailer.Id;
// Construct stream URL
return `${STATE.jellyfinData.serverAddress}/Videos/${trailer.Id}/stream.mp4?Static=true&mediaSourceId=${mediaSourceId}&api_key=${STATE.jellyfinData.accessToken}`;
}
return null;
} catch (error) {
console.error(`Error fetching local trailer for ${itemId}:`, error);
return null;
}
} }
}; };
@@ -1426,15 +1538,34 @@ const SlideCreator = {
let backdrop; let backdrop;
let isVideo = false; let isVideo = false;
let hasUpstreamVideo = false;
let trailerUrl = null; let trailerUrl = null;
// 1. Check for Remote Trailers (YouTube) // 1. Check for Remote/Local Trailers
if (item.RemoteTrailers && item.RemoteTrailers.length > 0) { // Priority: Custom Config URL > (PreferLocal -> Local) > Metadata RemoteTrailer
// 1a. Custom URL override
if (STATE.slideshow.customTrailerUrls && STATE.slideshow.customTrailerUrls[itemId]) {
trailerUrl = STATE.slideshow.customTrailerUrls[itemId];
console.log(`Using custom trailer URL for ${itemId}: ${trailerUrl}`);
}
// 1b. Check Local Trailer if preferred
else if (CONFIG.preferLocalTrailers && item.LocalTrailerCount > 0 && item.localTrailerUrl) {
trailerUrl = item.localTrailerUrl;
console.log(`Using local trailer for ${itemId}: ${trailerUrl}`);
}
// 1c. Fallback to Remote Trailer
else if (item.RemoteTrailers && item.RemoteTrailers.length > 0) {
trailerUrl = item.RemoteTrailers[0].Url; trailerUrl = item.RemoteTrailers[0].Url;
} }
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const shouldPlayVideo = CONFIG.enableVideoBackdrop && (!isMobile || CONFIG.enableMobileVideo);
// Client Setting Overrides
const enableVideo = MediaBarEnhancedSettingsManager.getSetting('videoBackdrops', CONFIG.enableVideoBackdrop);
const enableMobileVideo = MediaBarEnhancedSettingsManager.getSetting('mobileVideo', CONFIG.enableMobileVideo);
const shouldPlayVideo = enableVideo && (!isMobile || enableMobileVideo);
if (trailerUrl && shouldPlayVideo) { if (trailerUrl && shouldPlayVideo) {
let isYoutube = false; let isYoutube = false;
@@ -1455,13 +1586,31 @@ const SlideCreator = {
if (isYoutube && videoId) { if (isYoutube && videoId) {
isVideo = true; isVideo = true;
// Create container for YouTube API
const videoClass = CONFIG.fullWidthVideo ? "video-backdrop-full" : "video-backdrop-default"; const videoClass = CONFIG.fullWidthVideo ? "video-backdrop-full" : "video-backdrop-default";
backdrop = SlideUtils.createElement("div", { if (CONFIG.enableUpstreamTrailerLayout) {
className: `backdrop video-backdrop ${videoClass}`, // UPSTREAM LAYOUT: Wrapper Container
id: `youtube-player-${itemId}` const videoContainer = SlideUtils.createElement("div", {
}); className: "video-container",
id: `video-container-${itemId}`
});
const playerDiv = SlideUtils.createElement("div", {
id: `youtube-player-${itemId}`
});
videoContainer.appendChild(playerDiv);
slide.appendChild(videoContainer); // Append container first
isVideo = false;
hasUpstreamVideo = true;
} else {
// ENHANCED LAYOUT: Direct Backdrop Replacement
backdrop = SlideUtils.createElement("div", {
className: `backdrop video-backdrop ${videoClass}`,
id: `youtube-player-${itemId}`
});
}
// Initialize YouTube Player // Initialize YouTube Player
SlideUtils.loadYouTubeIframeAPI().then(() => { SlideUtils.loadYouTubeIframeAPI().then(() => {
@@ -1547,6 +1696,15 @@ const SlideCreator = {
} }
}, },
'onStateChange': (event) => { 'onStateChange': (event) => {
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
const videoContainer = slide ? slide.querySelector('.video-container') : null;
if (event.data === YT.PlayerState.PLAYING) {
if (videoContainer) videoContainer.classList.add('active');
} else {
if (videoContainer) videoContainer.classList.remove('active');
}
if (event.data === YT.PlayerState.ENDED) { if (event.data === YT.PlayerState.ENDED) {
if (CONFIG.waitForTrailerToEnd) { if (CONFIG.waitForTrailerToEnd) {
SlideshowManager.nextSlide(); SlideshowManager.nextSlide();
@@ -1576,7 +1734,7 @@ const SlideCreator = {
autoplay: false, autoplay: false,
preload: "auto", preload: "auto",
loop: false, loop: false,
style: "object-fit: cover; width: 100%; height: 100%; pointer-events: none;" style: "object-fit: cover; object-position: center center; width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none;"
}; };
if (STATE.slideshow.isMuted) { if (STATE.slideshow.isMuted) {
@@ -1608,12 +1766,30 @@ const SlideCreator = {
SlideshowManager.nextSlide(); SlideshowManager.nextSlide();
} }
}); });
if (CONFIG.enableUpstreamTrailerLayout) {
// Wrap in container
const videoContainer = SlideUtils.createElement("div", {
className: "video-container",
id: `video-container-${itemId}`
});
backdrop.style.position = "";
videoContainer.appendChild(backdrop);
slide.appendChild(videoContainer);
isVideo = false;
hasUpstreamVideo = true;
backdrop.addEventListener('play', () => videoContainer.classList.add('active'));
backdrop.addEventListener('pause', () => videoContainer.classList.remove('active'));
backdrop.addEventListener('ended', () => videoContainer.classList.remove('active'));
}
} }
} }
if (!isVideo) { if (!isVideo) {
backdrop = SlideUtils.createElement("img", { backdrop = SlideUtils.createElement("img", {
className: "backdrop high-quality", className: hasUpstreamVideo ? "backdrop high-quality with-video" : "backdrop high-quality",
src: this.buildImageUrl(item, "Backdrop", 0, serverAddress, 60), src: this.buildImageUrl(item, "Backdrop", 0, serverAddress, 60),
alt: LocalizationUtils.getLocalizedString('Backdrop', 'Backdrop'), alt: LocalizationUtils.getLocalizedString('Backdrop', 'Backdrop'),
loading: "eager", loading: "eager",
@@ -1918,6 +2094,11 @@ const SlideCreator = {
const item = await ApiUtils.fetchItemDetails(itemId); const item = await ApiUtils.fetchItemDetails(itemId);
// Pre-fetch local trailer URL if needed
if (CONFIG.preferLocalTrailers && item.LocalTrailerCount > 0) {
item.localTrailerUrl = await ApiUtils.fetchLocalTrailer(itemId);
}
const slideElement = this.createSlideElement( const slideElement = this.createSlideElement(
item, item,
item.Type === "Movie" ? "Movie" : "TV Show" item.Type === "Movie" ? "Movie" : "TV Show"
@@ -2129,7 +2310,9 @@ const SlideshowManager = {
} }
} }
if (CONFIG.slideAnimationEnabled) { const enableAnimations = MediaBarEnhancedSettingsManager.getSetting('slideAnimations', CONFIG.slideAnimationEnabled);
if (enableAnimations) {
const backdrop = currentSlide.querySelector(".backdrop"); const backdrop = currentSlide.querySelector(".backdrop");
if (backdrop && !backdrop.classList.contains("video-backdrop")) { if (backdrop && !backdrop.classList.contains("video-backdrop")) {
backdrop.classList.add("animate"); backdrop.classList.add("animate");
@@ -2175,11 +2358,14 @@ const SlideshowManager = {
setTimeout(() => { setTimeout(() => {
STATE.slideshow.isTransitioning = false; STATE.slideshow.isTransitioning = false;
if (previousVisibleSlide && CONFIG.slideAnimationEnabled) { if (previousVisibleSlide) {
const prevBackdrop = previousVisibleSlide.querySelector(".backdrop"); const enableAnimations = MediaBarEnhancedSettingsManager.getSetting('slideAnimations', CONFIG.slideAnimationEnabled);
const prevLogo = previousVisibleSlide.querySelector(".logo"); if (enableAnimations) {
if (prevBackdrop) prevBackdrop.classList.remove("animate"); const prevBackdrop = previousVisibleSlide.querySelector(".backdrop");
if (prevLogo) prevLogo.classList.remove("animate"); const prevLogo = previousVisibleSlide.querySelector(".logo");
if (prevBackdrop) prevBackdrop.classList.remove("animate");
if (prevLogo) prevLogo.classList.remove("animate");
}
} }
}, CONFIG.fadeTransitionDuration); }, CONFIG.fadeTransitionDuration);
} }
@@ -2512,29 +2698,18 @@ 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;
} }
const focusElement = document.activeElement;
switch (e.key) { switch (e.key) {
case "ArrowRight": case "ArrowRight":
if (focusElement && focusElement.classList.contains("detail-button")) { SlideshowManager.nextSlide();
focusElement.previousElementSibling.focus();
} else {
SlideshowManager.nextSlide();
}
e.preventDefault(); e.preventDefault();
break; break;
case "ArrowLeft": case "ArrowLeft":
if (focusElement && focusElement.classList.contains("play-button")) { SlideshowManager.prevSlide();
focusElement.nextElementSibling.focus();
} else {
SlideshowManager.prevSlide();
}
e.preventDefault(); e.preventDefault();
break; break;
@@ -2550,8 +2725,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;
@@ -2575,10 +2757,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();
} }
@@ -2635,7 +2832,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);
} }
} }
@@ -2715,9 +2927,27 @@ const SlideshowManager = {
if (itemIds.length === 0) { if (itemIds.length === 0) {
console.log("No custom list found, fetching random items from server..."); console.log("No custom list found, fetching random items from server...");
itemIds = await ApiUtils.fetchItemIdsFromServer(); itemIds = await ApiUtils.fetchItemIdsFromServer();
}
itemIds = SlideUtils.shuffleArray(itemIds); if (CONFIG.sortBy === 'Random') {
itemIds = SlideUtils.shuffleArray(itemIds);
}
} else {
// Custom IDs
if (CONFIG.sortBy === 'Random') {
itemIds = SlideUtils.shuffleArray(itemIds);
} else if (CONFIG.sortBy !== 'Original') {
// Client-side sort required...
console.log(`Sorting ${itemIds.length} custom items by ${CONFIG.sortBy} ${CONFIG.sortOrder}`);
const itemsWithDetails = [];
for (const id of itemIds) {
const item = await ApiUtils.fetchItemDetails(id);
if (item) itemsWithDetails.push(item);
}
const sortedItems = SlideUtils.sortItems(itemsWithDetails, CONFIG.sortBy, CONFIG.sortOrder);
itemIds = sortedItems.map(i => i.Id);
}
}
STATE.slideshow.itemIds = itemIds; STATE.slideshow.itemIds = itemIds;
STATE.slideshow.totalItems = itemIds.length; STATE.slideshow.totalItems = itemIds.length;
@@ -2738,7 +2968,10 @@ const SlideshowManager = {
this.nextSlide(); this.nextSlide();
}, CONFIG.shuffleInterval); }, CONFIG.shuffleInterval);
if (CONFIG.waitForTrailerToEnd && STATE.slideshow.slideInterval) { // Check if we should wait for trailer
const waitForTrailer = MediaBarEnhancedSettingsManager.getSetting('waitForTrailer', CONFIG.waitForTrailerToEnd);
if (waitForTrailer && STATE.slideshow.slideInterval) {
const activeSlide = document.querySelector('.slide.active'); const activeSlide = document.querySelector('.slide.active');
const hasActiveVideo = !!(activeSlide && activeSlide.querySelector('.video-backdrop')); const hasActiveVideo = !!(activeSlide && activeSlide.querySelector('.video-backdrop'));
if (hasActiveVideo) { if (hasActiveVideo) {
@@ -2883,6 +3116,259 @@ const initArrowNavigation = () => {
); );
}; };
const MediaBarEnhancedSettingsManager = {
initialized: false,
init() {
if (this.initialized) return;
if (!CONFIG.enableClientSideSettings) return;
this.initialized = true;
this.injectSettingsIcon();
console.log("MediaBarEnhanced: Client-Side Settings Manager initialized.");
},
getSetting(key, defaultValue) {
if (!CONFIG.enableClientSideSettings) return defaultValue;
const value = localStorage.getItem(`mediaBarEnhanced-${key}`);
return value !== null ? value === 'true' : defaultValue;
},
setSetting(key, value) {
localStorage.setItem(`mediaBarEnhanced-${key}`, value);
},
createIcon() {
const button = document.createElement('button');
button.type = 'button';
button.className = 'paper-icon-button-light headerButton media-bar-settings-button';
button.title = 'Media Bar Settings';
// button.innerHTML = '<span class="material-icons">tune</span>';
// button.innerHTML = '<img src="/MediaBarEnhanced/Resources/assets/logo_SW.svg" style="width: 24px; height: 24px; vertical-align: middle;">';
// currently not optimal, as it's egg-shaped due to the svg format... but if it's square, it's very small...
// button.innerHTML = '<img src="/MediaBarEnhanced/Resources/assets/logo_SW.svg" draggable="false" style="width: 52px; height: 24px; vertical-align: middle; pointer-events: none;">';
// button.innerHTML = '<img src="/MediaBarEnhanced/Resources/assets/logo_SW_SHORT.svg" draggable="false" style="width: 41px; height: 24px; vertical-align: middle; pointer-events: none;">';
button.innerHTML = '<img src="/MediaBarEnhanced/Resources/assets/logo_SW_MINIMAL.svg" draggable="false" style="width: 24px; height: 24px; vertical-align: middle; pointer-events: none;">';
button.style.verticalAlign = 'middle';
button.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleSettingsPopup(button);
});
return button;
},
injectSettingsIcon() {
const observer = new MutationObserver((mutations, obs) => {
const headerRight = document.querySelector('.headerRight');
if (headerRight && !document.querySelector('.media-bar-settings-button')) {
const icon = this.createIcon();
headerRight.prepend(icon);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
},
createPopup(anchorElement) {
const existing = document.querySelector('.media-bar-settings-popup');
if (existing) existing.remove();
const popup = document.createElement('div');
popup.className = 'media-bar-settings-popup dialog';
Object.assign(popup.style, {
position: 'fixed',
zIndex: '10000',
backgroundColor: '#202020',
padding: '1em',
borderRadius: '0.3em',
boxShadow: '0 0 20px rgba(0,0,0,0.5)',
minWidth: '250px',
color: '#fff',
});
const rect = anchorElement.getBoundingClientRect();
let rightPos = window.innerWidth - rect.right;
if (window.innerWidth < 450 || (window.innerWidth - rightPos) < 260) {
popup.style.right = '1rem';
popup.style.left = 'auto';
} else {
popup.style.right = `${rightPos}px`;
popup.style.left = 'auto';
}
popup.style.top = `${rect.bottom + 10}px`;
const settings = [
{ key: 'enabled', label: 'Enable Media Bar Enhanced', description: 'Toggle the entire media bar visibility.', default: true },
{ 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 trailer in popup (only backdrops without trailer)', default: CONFIG.showTrailerButton },
{ 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: '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>';
settings.forEach(setting => {
const isChecked = this.getSetting(setting.key, setting.default);
html += `
<div class="checkboxContainer checkboxContainer-withDescription" style="margin-bottom: 0.5em;">
<label class="emby-checkbox-label">
<input id="mb-setting-${setting.key}" type="checkbox" is="emby-checkbox" class="emby-checkbox" ${isChecked ? 'checked' : ''} />
<span class="checkboxLabel">${setting.label}</span>
</label>
<div class="fieldDescription">${setting.description}</div>
</div>
`;
});
// Buttons Container
html += `
<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">
<span>Save & Reload</span>
</button>
</div>
`;
popup.innerHTML = html;
// Add Listeners
settings.forEach(setting => {
const checkbox = popup.querySelector(`#mb-setting-${setting.key}`);
checkbox.addEventListener('change', (e) => {
this.setSetting(setting.key, e.target.checked);
});
});
// Reload Handler
popup.querySelector('#mb-settings-save').addEventListener('click', () => {
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) => {
if (!popup.contains(e.target) && e.target !== anchorElement && !anchorElement.contains(e.target)) {
popup.remove();
document.removeEventListener('click', closeHandler);
}
};
setTimeout(() => document.addEventListener('click', closeHandler), 0);
document.body.appendChild(popup);
},
toggleSettingsPopup(anchorElement) {
const existing = document.querySelector('.media-bar-settings-popup');
if (existing) {
existing.remove();
} else {
this.createPopup(anchorElement);
}
}
};
/**
* 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
*/ */
@@ -2891,6 +3377,24 @@ const slidesInit = async () => {
console.log("⚠️ Slideshow already initialized, skipping"); console.log("⚠️ Slideshow already initialized, skipping");
return; return;
} }
if (CONFIG.enableClientSideSettings) {
MediaBarEnhancedSettingsManager.init();
const isEnabled = MediaBarEnhancedSettingsManager.getSetting('enabled', true);
if (!isEnabled) {
console.log("MediaBarEnhanced: Disabled by client-side setting.");
const homeSections = document.querySelector('.homeSectionsContainer');
if (homeSections) {
homeSections.style.top = '0';
homeSections.style.marginTop = '0';
}
const container = document.getElementById('slides-container');
if (container) container.style.display = 'none';
return;
}
}
STATE.slideshow.hasInitialized = true; STATE.slideshow.hasInitialized = true;
/** /**
@@ -2978,6 +3482,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");
@@ -3008,6 +3514,6 @@ window.mediaBarEnhanced = {
initLoadingScreen(); initLoadingScreen();
loadPluginConfig().then(() => { fetchPluginConfig().then(() => {
startLoginStatusWatcher(); startLoginStatusWatcher();
}); });

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
@@ -144,6 +155,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

@@ -8,6 +8,22 @@
"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.5.0.12",
"changelog": "- fix: keyboard controls in TV mode\n- Add sorting options for content\n- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 4.0.1 from original repo",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.5.0.12/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "f43d2193b1da2bd319668c73ab7a19ee",
"timestamp": "2026-02-09T15:10:06Z"
},
{
"version": "1.3.0.3",
"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.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
"checksum": "1d9e0a8342d46f84aed3f7bd1bee32d3",
"timestamp": "2026-02-04T01:41:35Z"
},
{ {
"version": "1.2.3.7", "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", "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",

View File

@@ -0,0 +1,32 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) { console.error("Logged in?"); return; }
try {
// Fetch 1 random item ID
const rnd = await apiClient.getItems(apiClient.getCurrentUserId(), { SortBy: "Random", Limit: 1, Recursive: true, IncludeItemTypes: "Movie,Series" });
if (rnd.Items.length > 0) {
const id = rnd.Items[0].Id;
console.log("Random Item ID:", id);
// Fetch Default Details
const defd = await apiClient.getItem(apiClient.getCurrentUserId(), id);
console.log("Default Fields:", defd);
// Fetch ALL Known Fields manually
const allFields = "Chapters,People,MediaStreams,UserData,RecursiveItemCount,DateCreated,MediaSources,ProductionYear,Studios,Genres,Tags,RemoteTrailers,ProviderIds,Overview,CommunityRating,CriticRating,OfficialRating,PremiereDate,RunTimeTicks";
const full = await res.json();
console.log("Full Details:", full);
// Helper to download JSON
const blob = new Blob([JSON.stringify(full, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `jellyfin-item-${id}.json`;
a.click();
URL.revokeObjectURL(url);
console.log("Downloaded JSON file.");
} else { console.warn("No items."); }
} catch (e) { console.error(e); }
})();

View File

@@ -0,0 +1,32 @@
(async () => {
// 1. Get Auth Data from the active client
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient not found. Are you logged in?");
return;
}
try {
console.log("Fetching random item...");
// 2. Fetch 1 random item
// const result = await apiClient.getItems(apiClient.getCurrentUserId(), { SortBy: "Random", Limit: 1, Recursive: true, IncludeItemTypes: "Movie,Series" });
const result = await apiClient.getItems(apiClient.getCurrentUserId(), {
SortBy: "Random",
Limit: 1,
Recursive: true,
IncludeItemTypes: "Movie,Series", // Optional: filter types
Fields: "Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,RunTimeTicks,ProductionYear,MediaSources" // Request ALL fields
});
if (result.Items.length > 0) {
const item = result.Items[0];
console.log("Random Item Found:", item.Name);
console.dir(item); // Prints the full interactive object
} else {
console.warn("No items found.");
}
} catch (error) {
console.error("Error fetching item:", error);
}
})();

View File

@@ -0,0 +1,28 @@
(async () => {
const apiClient = window.ApiClient;
if (!apiClient) {
console.error("ApiClient nicht gefunden.");
return;
}
// Die ID des Items, das du abrufen möchtest
const itemId = "DEINE_ITEM_ID_HIER";
const userId = apiClient.getCurrentUserId();
try {
console.log(`Rufe Details für Item ${itemId} ab...`);
// Nutze getItem() statt getItems()
// Parameter: userId, itemId
const item = await apiClient.getItem(userId, itemId);
if (item) {
console.log("Item Details gefunden:", item.Name);
console.dir(item); // Zeigt alle Metadaten (Genres, Pfade, ProviderIds, etc.)
} else {
console.warn("Item konnte nicht gefunden werden.");
}
} catch (error) {
console.error("Fehler beim Abrufen des Items:", error);
}
})();

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