Compare commits
428 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81b28952cc | ||
|
|
911c96216a | ||
|
|
b3b6296798 | ||
|
|
bca2d577b9 | ||
|
|
11b6316338 | ||
|
|
bbc6da9d41 | ||
|
|
c313cbf37d | ||
|
|
79405c6e95 | ||
|
|
a542e38dc3 | ||
|
|
a27566632c | ||
|
|
779e20bfb2 | ||
|
|
0624624190 | ||
|
|
dd07c452ca | ||
|
|
0cd64d4eea | ||
|
|
3480131e3c | ||
|
|
f33c5be75f | ||
|
|
c1d06f3bb8 | ||
|
|
c493212f65 | ||
|
|
d497f22416 | ||
|
|
f65475673d | ||
|
|
0a8ba042db | ||
|
|
f9b8722259 | ||
|
|
66f6f7b434 | ||
|
|
22c873d686 | ||
|
|
c3a73cc28b | ||
|
|
fc3a5f1e66 | ||
|
|
cd490cf0f3 | ||
|
|
bb6310381a | ||
|
|
518fd5640e | ||
|
|
a57f3db009 | ||
|
|
8ff4f081f3 | ||
|
|
4a07c22091 | ||
|
|
4d1d442746 | ||
|
|
1df2b341e5 | ||
|
|
b2dbd6df45 | ||
|
|
60c72a01b1 | ||
|
|
9f7ef3c96b | ||
|
|
7ffcfa68c1 | ||
|
|
aaf21d3c33 | ||
|
|
9758ecd417 | ||
|
|
a4547d80b1 | ||
|
|
671e38ff32 | ||
|
|
0e9d0f9d09 | ||
|
|
5f296f3c88 | ||
|
|
a14b3ca8b5 | ||
|
|
4d12e34d01 | ||
|
|
59fe6f7083 | ||
|
|
dcb2164ea1 | ||
|
|
2f71f7b46b | ||
|
|
70b0a2a192 | ||
|
|
300c76890b | ||
|
|
64e5441aff | ||
|
|
f47c9dde88 | ||
|
|
9d42b5af8d | ||
|
|
8c5f66716f | ||
|
|
41e6c1032d | ||
|
|
fe07fe9f5e | ||
|
|
22a7eb8dcb | ||
|
|
07658f4fbc | ||
|
|
25ee5b73b4 | ||
|
|
8f8e251054 | ||
|
|
05529e5627 | ||
|
|
b3e00e8ad6 | ||
|
|
39649a6dd3 | ||
|
|
11aca32158 | ||
|
|
9bcb2f984a | ||
|
|
c23a614f9f | ||
|
|
3a367cb2be | ||
|
|
2993bfe3f2 | ||
|
|
3ffa2c262a | ||
|
|
dc88110e9c | ||
|
|
f9ae62a459 | ||
|
|
9e2f861213 | ||
|
|
4781618a52 | ||
|
|
2bed81c1f8 | ||
|
|
292fcfc389 | ||
|
|
da718a0e57 | ||
|
|
95a8907496 | ||
|
|
0498756529 | ||
|
|
f1d92080b2 | ||
|
|
586b57d23e | ||
|
|
47b05a82ba | ||
|
|
cb45e0cb43 | ||
|
|
19246c3a19 | ||
|
|
99b4b09306 | ||
|
|
563a1e5484 | ||
|
|
788708370d | ||
|
|
4bb0de11da | ||
|
|
d2abfc6b46 | ||
|
|
8ba14d4df0 | ||
|
|
538138fcf3 | ||
|
|
08efb11d95 | ||
|
|
de62c794f9 | ||
|
|
fc2491a4ef | ||
|
|
03276d7722 | ||
|
|
8676160e7b | ||
|
|
c562560bc0 | ||
|
|
98474d4ff6 | ||
|
|
14c0eb43ed | ||
|
|
c4cbeda2b8 | ||
|
|
53ad568be4 | ||
|
|
fba64bd0f6 | ||
|
|
3da16c4c5c | ||
|
|
c7cd7be3ee | ||
|
|
6d90523eef | ||
|
|
2a3e8057a1 | ||
|
|
42026b0ee8 | ||
|
|
64dbc3cfd3 | ||
|
|
c998266dd7 | ||
|
|
9b941e5a77 | ||
|
|
1d70d7166d | ||
|
|
5331f0faf1 | ||
|
|
0508188705 | ||
|
|
cc861f4263 | ||
|
|
10e6cdc4a2 | ||
|
|
a8c7faab6b | ||
|
|
6df390fa18 | ||
|
|
d0c3d7ee4d | ||
|
|
bc621aacdf | ||
|
|
73eb30d671 | ||
|
|
2cfbec95c9 | ||
|
|
08fc29cba3 | ||
|
|
0d6b835486 | ||
|
|
bf620e447f | ||
|
|
3117d627dd | ||
|
|
71402f7e86 | ||
|
|
cce202b88d | ||
|
|
1d334e4d95 | ||
|
|
142063ce63 | ||
|
|
1a0050ae1a | ||
|
|
46ebfdbafc | ||
|
|
14d2bb957b | ||
|
|
7a0c1e4488 | ||
|
|
ec0e686e00 | ||
|
|
54395896b3 | ||
|
|
8b2fe59f5a | ||
|
|
a44bf7ebf4 | ||
|
|
1f273906bf | ||
|
|
0534d0458e | ||
|
|
8b0d6f137d | ||
|
|
2208b86a47 | ||
|
|
5a1048687c | ||
|
|
d3f6641158 | ||
|
|
c214a620e4 | ||
|
|
f0c9462878 | ||
|
|
e12a5b56a2 | ||
|
|
51ff0f2623 | ||
|
|
2c907debc8 | ||
|
|
7b30f8c9e9 | ||
|
|
3a90605112 | ||
|
|
5772d670ff | ||
|
|
e558594c52 | ||
|
|
343436ac56 | ||
|
|
6075e20a11 | ||
|
|
8b7def809b | ||
|
|
43950eac60 | ||
|
|
c09f265b26 | ||
|
|
379c370b4a | ||
|
|
b567307003 | ||
|
|
ff9ea9eff0 | ||
|
|
9427e3e535 | ||
|
|
19318a916d | ||
|
|
5d85284df8 | ||
|
|
2382f850b6 | ||
|
|
22041293f6 | ||
|
|
5595158f9d | ||
|
|
39f85e0c9b | ||
|
|
18a9980a0a | ||
|
|
deb426833d | ||
|
|
bf4b6da0f0 | ||
|
|
2bc7d90254 | ||
|
|
3f302d4c64 | ||
|
|
13a1cc7885 | ||
|
|
a62900f96e | ||
|
|
9d90a29a40 | ||
|
|
cd3973088e | ||
|
|
4112cfad4a | ||
|
|
2618b18df1 | ||
|
|
ef378c5e87 | ||
|
|
b8d0dd9f1a | ||
|
|
d7c44061cb | ||
|
|
9acb05d19e | ||
|
|
619d8533d2 | ||
|
|
a4b39ae3bf | ||
|
|
74a367584b | ||
|
|
06407f9121 | ||
|
|
0926493391 | ||
|
|
6dbb33be96 | ||
|
|
211d9d316a | ||
|
|
329733246d | ||
|
|
6110e1cdd8 | ||
|
|
31c8a209a5 | ||
|
|
ad2e761bbd | ||
|
|
85f90e8fbb | ||
|
|
9f5f607168 | ||
|
|
108a644983 | ||
|
|
ab778f774f | ||
|
|
5dcb60487e | ||
|
|
9a6997f1da | ||
|
|
31d315ed8f | ||
|
|
2b1301ea0b | ||
|
|
ee8c0b8888 | ||
|
|
64ef4915b8 | ||
|
|
1f655ed7b6 | ||
|
|
0682967591 | ||
|
|
7938728f8e | ||
|
|
a0773c66eb | ||
|
|
10f2a38add | ||
|
|
9bfa3ba5ea | ||
|
|
5c00c07b8a | ||
|
|
773c49a228 | ||
|
|
41a309e0d1 | ||
|
|
43797fbb98 | ||
|
|
f13a1ba1af | ||
|
|
d489c22f28 | ||
|
|
7816c87543 | ||
|
|
720567bafc | ||
|
|
2289a1f83e | ||
|
|
a269318f58 | ||
|
|
fdb409fd3b | ||
|
|
9bb4b9d355 | ||
|
|
1e18c22937 | ||
|
|
a83913d15c | ||
|
|
2f50931beb | ||
|
|
5b14bdba35 | ||
|
|
9ba3b1e49f | ||
|
|
bf7c7fb8e8 | ||
|
|
39e29046de | ||
|
|
18260f8eac | ||
|
|
59c07f3c45 | ||
|
|
b06d1e9375 | ||
|
|
e5bf23a7bc | ||
|
|
0d7113969b | ||
|
|
f69f676a68 | ||
|
|
f448c89ef2 | ||
|
|
daf26fe53a | ||
|
|
26ef307838 | ||
|
|
c296483583 | ||
|
|
7992e20715 | ||
|
|
1ae59f5da5 | ||
|
|
92eaf91173 | ||
|
|
e7410ec22a | ||
|
|
bb43d1e679 | ||
|
|
b6609d23a2 | ||
|
|
9d4cbf37d3 | ||
|
|
b5e63ef3b7 | ||
|
|
22f9906188 | ||
|
|
ae54ab41a8 | ||
|
|
9663ab78d2 | ||
|
|
f633e4273f | ||
|
|
c0895fd8d7 | ||
|
|
002ccdb08b | ||
|
|
7cb03410ee | ||
|
|
17c8681e93 | ||
|
|
3a4c663c0e | ||
|
|
3385196611 | ||
|
|
2538556f7c | ||
|
|
550ebed942 | ||
|
|
21d55711d4 | ||
|
|
81a0d375be | ||
|
|
23cbc0a85a | ||
|
|
2de066cca8 | ||
|
|
138e50ff15 | ||
|
|
bf72dc08a3 | ||
|
|
65a63b4aa0 | ||
|
|
a1df756c56 | ||
|
|
f2d383ec61 | ||
|
|
b85f52d8d3 | ||
|
|
ad18acb011 | ||
|
|
2ae147ac01 | ||
|
|
9896044988 | ||
|
|
93e91e2e60 | ||
|
|
b613b028d0 | ||
|
|
9906784845 | ||
|
|
009a3c4720 | ||
|
|
595056230a | ||
|
|
b18060dfd7 | ||
|
|
ebb2af9d24 | ||
|
|
743af20b8e | ||
|
|
9844b186d7 | ||
|
|
104b76aa41 | ||
|
|
7493c8fa93 | ||
|
|
77c03157a1 | ||
|
|
a7929e1ff6 | ||
|
|
c78e07de62 | ||
|
|
a90f805ea8 | ||
|
|
ccba1857e1 | ||
|
|
ff56c9370b | ||
|
|
162600c43f | ||
|
|
a21549af47 | ||
|
|
1b319ade40 | ||
|
|
75757d67e7 | ||
|
|
92fc8d72f7 | ||
|
|
cfe9dec550 | ||
|
|
1bef573aaf | ||
|
|
29a365b690 | ||
|
|
0ee0a65309 | ||
|
|
152d22b709 | ||
|
|
216dddad94 | ||
|
|
a6de148ca1 | ||
|
|
1f9378d74d | ||
|
|
cc025779dc | ||
|
|
3d10fd59b5 | ||
|
|
bf261eba96 | ||
|
|
e3116c30cf | ||
|
|
bb39c91d32 | ||
|
|
4e0c74614a | ||
|
|
b61bf92437 | ||
|
|
dfbd6ce964 | ||
|
|
f1cbcad177 | ||
|
|
feedd5d95f | ||
|
|
87d82cca15 | ||
|
|
a70746e095 | ||
|
|
f32283e0bf | ||
|
|
8f3985f307 | ||
|
|
0b2817ecff | ||
|
|
84faee2db4 | ||
|
|
3efa07ec51 | ||
|
|
af603e8803 | ||
|
|
fe63414e4b | ||
|
|
614c86083f | ||
|
|
17b9e8921e | ||
|
|
5075226ba8 | ||
|
|
443fb1008b | ||
|
|
8f12140aad | ||
|
|
1469712bb5 | ||
|
|
76ff03ac17 | ||
|
|
25b1ba5f2b | ||
|
|
eed3ca1860 | ||
|
|
0f14577f5d | ||
|
|
9999d6a633 | ||
|
|
a935fd7d5d | ||
|
|
60293e81e1 | ||
|
|
f54e55fe04 | ||
|
|
b3c42e2100 | ||
|
|
20ddbb32c7 | ||
|
|
14a5075e22 | ||
|
|
accb316a81 | ||
|
|
51c5cdf5bf | ||
|
|
306eff757b | ||
|
|
b8f28a5735 | ||
|
|
0932d9611d | ||
|
|
5e616db0ae | ||
|
|
538b0f2110 | ||
|
|
e717c07c54 | ||
|
|
0fa2d01ca3 | ||
|
|
5299b2a9d5 | ||
|
|
4f244988b9 | ||
|
|
5635a8f05e | ||
|
|
c901da4b0c | ||
|
|
c45cd0281f | ||
|
|
0c3e74829a | ||
|
|
e6b769f099 | ||
|
|
77371f7b98 | ||
|
|
988b800b6d | ||
|
|
4c6514ba9f | ||
|
|
6910eba7d6 | ||
|
|
3585b47b6c | ||
|
|
8170abdc94 | ||
|
|
535c0e17bf | ||
|
|
df1cee0eeb | ||
|
|
16cc56030f | ||
|
|
13ecbac96e | ||
|
|
8bca6f9052 | ||
|
|
217db2a66d | ||
|
|
5fd7bcb8b6 | ||
|
|
0b0e41a9f9 | ||
|
|
370db55714 | ||
|
|
e5d4800ef1 | ||
|
|
b910e92364 | ||
|
|
82283b1faf | ||
|
|
1588b1a6b2 | ||
|
|
de19466341 | ||
|
|
15054e314c | ||
|
|
3877f96b09 | ||
|
|
abca7cb3b6 | ||
|
|
998a0cfc68 | ||
|
|
63be8214d0 | ||
|
|
99411afffd | ||
|
|
9510ae6ba7 | ||
|
|
2030538acc | ||
|
|
463e9ef424 | ||
|
|
dbe9ce97d2 | ||
|
|
45c780c018 | ||
|
|
9de268caa7 | ||
|
|
a8298461f9 | ||
|
|
aa369e5c7b | ||
|
|
f62dc853be | ||
|
|
d0a7f5da8c | ||
|
|
1f140fd3c1 | ||
|
|
18474d4537 | ||
|
|
839424a960 | ||
|
|
8050aa8e76 | ||
|
|
0c173b9685 | ||
|
|
90ecc7c0ef | ||
|
|
c80d3726a6 | ||
|
|
a8e63c935f | ||
|
|
d931a5d589 | ||
|
|
629f13ab58 | ||
|
|
6a91784401 | ||
|
|
3f8777db9f | ||
|
|
4ec337a893 | ||
|
|
7366f2ca30 | ||
|
|
faabf13870 | ||
|
|
3e584fa419 | ||
|
|
13889c3c68 | ||
|
|
7af9eb5004 | ||
|
|
1e57d8acef | ||
|
|
5964d2cb34 | ||
|
|
6f3fd23879 | ||
|
|
47949e9820 | ||
|
|
5fe8da8347 | ||
|
|
a8842f8415 | ||
|
|
3ecaf5fae6 | ||
|
|
9d13d61e77 | ||
|
|
0e441b751b | ||
|
|
4f14c00636 | ||
|
|
2dbb4dcfea | ||
|
|
1982511374 | ||
|
|
3d7c1ec2cb | ||
|
|
bf7762f4d8 | ||
|
|
9ba4bae6a8 | ||
|
|
f7388d8c25 | ||
|
|
7097c49773 | ||
|
|
0dce0c1551 | ||
|
|
386c5f9d7d | ||
|
|
2bd1a7a8b4 | ||
|
|
61eeed78b8 |
@@ -51,7 +51,31 @@ jobs:
|
|||||||
echo "$CHANGELOG" >> $GITHUB_ENV
|
echo "$CHANGELOG" >> $GITHUB_ENV
|
||||||
echo "EOF" >> $GITHUB_ENV
|
echo "EOF" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Check if Release Already Exists
|
||||||
|
id: check_release
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
VERSION="${{ env.VERSION }}"
|
||||||
|
TAG="v$VERSION"
|
||||||
|
SERVER_URL="https://git.mahom03-spacecloud.de"
|
||||||
|
|
||||||
|
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$SERVER_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$TAG")
|
||||||
|
|
||||||
|
if [ "$HTTP_STATUS" -eq 200 ]; then
|
||||||
|
echo "Release $TAG already exists. Skipping release-related steps."
|
||||||
|
echo "release_exists=true" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$HTTP_STATUS" -eq 404 ]; then
|
||||||
|
echo "No existing release for $TAG. Continuing."
|
||||||
|
echo "release_exists=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "Unexpected response when checking release: $HTTP_STATUS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Build and Zip
|
- name: Build and Zip
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Inject version from manifest into the build
|
# Inject version from manifest into the build
|
||||||
@@ -71,6 +95,7 @@ jobs:
|
|||||||
echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip" >> $GITHUB_ENV
|
echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Update manifest.json
|
- name: Update manifest.json
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
REPO_OWNER="${{ github.repository_owner }}"
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
@@ -90,12 +115,14 @@ jobs:
|
|||||||
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
||||||
|
|
||||||
- name: Commit manifest.json
|
- name: Commit manifest.json
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
uses: stefanzweifel/git-auto-commit-action@v7
|
uses: stefanzweifel/git-auto-commit-action@v7
|
||||||
with:
|
with:
|
||||||
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
|
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
|
||||||
file_pattern: manifest.json
|
file_pattern: manifest.json
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
uses: akkuman/gitea-release-action@v1
|
uses: akkuman/gitea-release-action@v1
|
||||||
with:
|
with:
|
||||||
server_url: "https://git.mahom03-spacecloud.de"
|
server_url: "https://git.mahom03-spacecloud.de"
|
||||||
@@ -109,6 +136,7 @@ jobs:
|
|||||||
|
|
||||||
# Update Message in Remote Repository
|
# Update Message in Remote Repository
|
||||||
- name: Checkout Central Manifest Repo
|
- name: Checkout Central Manifest Repo
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
||||||
@@ -116,6 +144,7 @@ jobs:
|
|||||||
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
||||||
|
|
||||||
- name: Update Central Manifest
|
- name: Update Central Manifest
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cd central-manifest
|
cd central-manifest
|
||||||
@@ -171,6 +200,7 @@ jobs:
|
|||||||
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 Central Manifest
|
- name: Commit and Push Central Manifest
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
run: |
|
run: |
|
||||||
cd central-manifest
|
cd central-manifest
|
||||||
git config user.name "CodeDevMLH"
|
git config user.name "CodeDevMLH"
|
||||||
@@ -186,79 +216,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
|
||||||
167
.github/workflows/release_automation.yml
vendored
167
.github/workflows/release_automation.yml
vendored
@@ -52,7 +52,32 @@ jobs:
|
|||||||
echo "$CHANGELOG" >> $GITHUB_ENV
|
echo "$CHANGELOG" >> $GITHUB_ENV
|
||||||
echo "EOF" >> $GITHUB_ENV
|
echo "EOF" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Check if Release Already Exists
|
||||||
|
id: check_release
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
VERSION="${{ env.VERSION }}"
|
||||||
|
TAG="v$VERSION"
|
||||||
|
API_URL="https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$TAG"
|
||||||
|
|
||||||
|
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$API_URL")
|
||||||
|
|
||||||
|
if [ "$HTTP_STATUS" -eq 200 ]; then
|
||||||
|
echo "Release $TAG already exists. Skipping release-related steps."
|
||||||
|
echo "release_exists=true" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$HTTP_STATUS" -eq 404 ]; then
|
||||||
|
echo "No existing release for $TAG. Continuing."
|
||||||
|
echo "release_exists=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "Unexpected response when checking release: $HTTP_STATUS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
- name: Build and Zip
|
- name: Build and Zip
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Inject version from manifest into the build
|
# Inject version from manifest into the build
|
||||||
@@ -72,6 +97,7 @@ jobs:
|
|||||||
echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip" >> $GITHUB_ENV
|
echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Update manifest.json
|
- name: Update manifest.json
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
REPO_OWNER="${{ github.repository_owner }}"
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
@@ -91,13 +117,14 @@ jobs:
|
|||||||
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
||||||
|
|
||||||
- name: Commit manifest.json
|
- name: Commit manifest.json
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
uses: stefanzweifel/git-auto-commit-action@v7
|
uses: stefanzweifel/git-auto-commit-action@v7
|
||||||
with:
|
with:
|
||||||
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
|
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
|
||||||
file_pattern: manifest.json
|
file_pattern: manifest.json
|
||||||
|
|
||||||
- name: Generate Commit Log
|
- name: Generate Commit Log
|
||||||
if: success()
|
if: success() && steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "Generating commit log since last tag..."
|
echo "Generating commit log since last tag..."
|
||||||
@@ -131,6 +158,7 @@ jobs:
|
|||||||
cat release_body.txt
|
cat release_body.txt
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: "v${{ env.VERSION }}"
|
tag_name: "v${{ env.VERSION }}"
|
||||||
@@ -145,6 +173,7 @@ jobs:
|
|||||||
|
|
||||||
# Update Message in Remote Repository
|
# Update Message in Remote Repository
|
||||||
- name: Checkout Central Manifest Repo
|
- name: Checkout Central Manifest Repo
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
||||||
@@ -152,6 +181,7 @@ jobs:
|
|||||||
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
||||||
|
|
||||||
- name: Update Central Manifest
|
- name: Update Central Manifest
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cd central-manifest
|
cd central-manifest
|
||||||
@@ -207,6 +237,7 @@ jobs:
|
|||||||
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 Central Manifest
|
- name: Commit and Push Central Manifest
|
||||||
|
if: steps.check_release.outputs.release_exists == 'false'
|
||||||
run: |
|
run: |
|
||||||
cd central-manifest
|
cd central-manifest
|
||||||
git config user.name "CodeDevMLH"
|
git config user.name "CodeDevMLH"
|
||||||
@@ -222,79 +253,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
|
||||||
246
Injector_new.cs
Normal file
246
Injector_new.cs
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using Jellyfin.Plugin.MediaBarEnhanced.Helpers;
|
||||||
|
|
||||||
|
namespace Jellyfin.Plugin.MediaBarEnhanced
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the injection of the MediaBarEnhanced script into the Jellyfin web interface.
|
||||||
|
/// </summary>
|
||||||
|
public class ScriptInjector
|
||||||
|
{
|
||||||
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly ILogger<ScriptInjector> _logger;
|
||||||
|
public const string ScriptTag = "<script src=\"../MediaBarEnhanced/Resources/mediaBarEnhanced.js\" defer></script>";
|
||||||
|
public const string CssTag = "<link rel=\"stylesheet\" href=\"../MediaBarEnhanced/Resources/mediaBarEnhanced.css\" />";
|
||||||
|
public const string ScriptMarker = "</body>";
|
||||||
|
public const string CssMarker = "</head>";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ScriptInjector"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appPaths">The application paths.</param>
|
||||||
|
/// <param name="logger">The logger.</param>
|
||||||
|
public ScriptInjector(IApplicationPaths appPaths, ILogger<ScriptInjector> logger)
|
||||||
|
{
|
||||||
|
_appPaths = appPaths;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Injects the script tag into index.html if it's not already present.
|
||||||
|
/// </summary>
|
||||||
|
public void Inject()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var webPath = GetWebPath();
|
||||||
|
if (string.IsNullOrEmpty(webPath))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Could not find Jellyfin web path. Script injection skipped. Attempting fallback.");
|
||||||
|
RegisterFileTransformation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexPath = Path.Combine(webPath, "index.html");
|
||||||
|
if (!File.Exists(indexPath))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("index.html not found at {Path}. Script injection skipped. Attempting fallback.", indexPath);
|
||||||
|
RegisterFileTransformation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = File.ReadAllText(indexPath);
|
||||||
|
var injectedJS = false;
|
||||||
|
var injectedCSS = false;
|
||||||
|
|
||||||
|
if (!content.Contains(ScriptTag))
|
||||||
|
{
|
||||||
|
var index = content.IndexOf(ScriptMarker, StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
content = content.Insert(index, ScriptTag + Environment.NewLine);
|
||||||
|
injectedJS = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content.Contains(CssTag))
|
||||||
|
{
|
||||||
|
var index = content.IndexOf(CssMarker, StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
content = content.Insert(index, CssTag + Environment.NewLine);
|
||||||
|
injectedCSS = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (injectedJS && injectedCSS)
|
||||||
|
{
|
||||||
|
File.WriteAllText(indexPath, content);
|
||||||
|
_logger.LogInformation("MediaBarEnhanced script injected into index.html.");
|
||||||
|
} else if (injectedJS)
|
||||||
|
{
|
||||||
|
File.WriteAllText(indexPath, content);
|
||||||
|
_logger.LogInformation("MediaBarEnhanced JS script injected into index.html. But CSS was already present or could not be injected.");
|
||||||
|
}
|
||||||
|
else if (injectedCSS)
|
||||||
|
{
|
||||||
|
File.WriteAllText(indexPath, content);
|
||||||
|
_logger.LogInformation("MediaBarEnhanced CSS injected into index.html. But JS script was already present or could not be injected.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("MediaBarEnhanced script and CSS already present in index.html. Or could not be injected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Unauthorized access when attempting to inject script into index.html. Automatic injection failed. Attempting fallback now...");
|
||||||
|
RegisterFileTransformation();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error injecting MediaBarEnhanced resources. Attempting fallback.");
|
||||||
|
RegisterFileTransformation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the script tag from index.html.
|
||||||
|
/// </summary>
|
||||||
|
public void Remove()
|
||||||
|
{
|
||||||
|
UnregisterFileTransformation();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var webPath = GetWebPath();
|
||||||
|
if (string.IsNullOrEmpty(webPath))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexPath = Path.Combine(webPath, "index.html");
|
||||||
|
if (!File.Exists(indexPath))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = File.ReadAllText(indexPath);
|
||||||
|
var modified = false;
|
||||||
|
|
||||||
|
if (content.Contains(ScriptTag))
|
||||||
|
{
|
||||||
|
content = content.Replace(ScriptTag + Environment.NewLine, "").Replace(ScriptTag, "");
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.Contains(CssTag))
|
||||||
|
{
|
||||||
|
content = content.Replace(CssTag + Environment.NewLine, "").Replace(CssTag, "");
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified)
|
||||||
|
{
|
||||||
|
File.WriteAllText(indexPath, content);
|
||||||
|
_logger.LogInformation("MediaBarEnhanced script removed from index.html.");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("MediaBarEnhanced script not found in index.html. No removal necessary.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException uaEx)
|
||||||
|
{
|
||||||
|
_logger.LogError(uaEx, "Unauthorized access when trying to remove MediaBarEnhanced script. Check file permissions.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error removing MediaBarEnhanced script.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetWebPath()
|
||||||
|
{
|
||||||
|
var prop = _appPaths.GetType().GetProperty("WebPath", BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
return prop?.GetValue(_appPaths) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterFileTransformation()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("MediaBarEnhanced Fallback. Registering file transformations.");
|
||||||
|
|
||||||
|
List<JObject> payloads = new List<JObject>();
|
||||||
|
|
||||||
|
{
|
||||||
|
JObject payload = new JObject();
|
||||||
|
payload.Add("id", "0dfac9d7-d898-4944-900b-1c1837707279");
|
||||||
|
payload.Add("fileNamePattern", "index.html");
|
||||||
|
payload.Add("callbackAssembly", GetType().Assembly.FullName);
|
||||||
|
payload.Add("callbackClass", typeof(TransformationPatches).FullName);
|
||||||
|
payload.Add("callbackMethod", nameof(TransformationPatches.IndexHtml));
|
||||||
|
|
||||||
|
payloads.Add(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assembly? fileTransformationAssembly =
|
||||||
|
AssemblyLoadContext.All.SelectMany(x => x.Assemblies).FirstOrDefault(x =>
|
||||||
|
x.FullName?.Contains(".FileTransformation") ?? false);
|
||||||
|
|
||||||
|
if (fileTransformationAssembly != null)
|
||||||
|
{
|
||||||
|
Type? pluginInterfaceType = fileTransformationAssembly.GetType("Jellyfin.Plugin.FileTransformation.PluginInterface");
|
||||||
|
|
||||||
|
if (pluginInterfaceType != null)
|
||||||
|
{
|
||||||
|
foreach (JObject payload in payloads)
|
||||||
|
{
|
||||||
|
pluginInterfaceType.GetMethod("RegisterTransformation")?.Invoke(null, new object?[] { payload });
|
||||||
|
}
|
||||||
|
_logger.LogInformation("File transformations registered successfully.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FileTransformation plugin found but PluginInterface type missing.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FileTransformation plugin assembly not found. Fallback failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterFileTransformation()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assembly? fileTransformationAssembly =
|
||||||
|
AssemblyLoadContext.All.SelectMany(x => x.Assemblies).FirstOrDefault(x =>
|
||||||
|
x.FullName?.Contains(".FileTransformation") ?? false);
|
||||||
|
|
||||||
|
if (fileTransformationAssembly != null)
|
||||||
|
{
|
||||||
|
Type? pluginInterfaceType = fileTransformationAssembly.GetType("Jellyfin.Plugin.FileTransformation.PluginInterface");
|
||||||
|
|
||||||
|
if (pluginInterfaceType != null)
|
||||||
|
{
|
||||||
|
Guid id = Guid.Parse("0dfac9d7-d898-4944-900b-1c1837707279");
|
||||||
|
pluginInterfaceType.GetMethod("RemoveTransformation")?.Invoke(null, new object?[] { id });
|
||||||
|
_logger.LogInformation("File transformation unregistered successfully.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Error attempting to unregister file transformation. It might not have been registered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
187
Jellyfin.Plugin.MediaBarEnhanced/Api/OverlayImageController.cs
Normal file
187
Jellyfin.Plugin.MediaBarEnhanced/Api/OverlayImageController.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
|
||||||
|
namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Controller for handling custom overlay image uploads and retrieval.
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("MediaBarEnhanced")]
|
||||||
|
public class OverlayImageController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IApplicationPaths _applicationPaths;
|
||||||
|
private readonly string _imageDirectory;
|
||||||
|
|
||||||
|
public OverlayImageController(IApplicationPaths applicationPaths)
|
||||||
|
{
|
||||||
|
_applicationPaths = applicationPaths;
|
||||||
|
_imageDirectory = Path.Combine(applicationPaths.PluginConfigurationsPath, "Jellyfin.Plugin.MediaBarEnhanced", "Assets");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads a new custom overlay image.
|
||||||
|
/// </summary>
|
||||||
|
// [Microsoft.AspNetCore.Authorization.Authorize]
|
||||||
|
[HttpPost("OverlayImage")]
|
||||||
|
[Consumes("multipart/form-data")]
|
||||||
|
public async Task<IActionResult> UploadImage([FromForm] IFormFile file, [FromQuery] string? filename = null)
|
||||||
|
{
|
||||||
|
if (file == null || file.Length == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("No file uploaded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract original extension or fallback to .jpg
|
||||||
|
string extension = Path.GetExtension(file.FileName);
|
||||||
|
if (string.IsNullOrWhiteSpace(extension)) extension = ".jpg";
|
||||||
|
|
||||||
|
// Delete any existing file with this prefix before saving the new one (as extensions might differ)
|
||||||
|
string prefix = string.IsNullOrWhiteSpace(filename) ? "custom_overlay_image_global" : $"custom_overlay_image_{filename}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(_imageDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_imageDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove existing
|
||||||
|
var existingFiles = Directory.GetFiles(_imageDirectory, $"{prefix}.*");
|
||||||
|
foreach(var extFile in existingFiles)
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(extFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
string targetFileName = $"{prefix}{extension}";
|
||||||
|
string targetPath = Path.Combine(_imageDirectory, targetFileName);
|
||||||
|
|
||||||
|
// Delete is not strictly necessary and can cause locking issues if someone is currently reading it.
|
||||||
|
// FileMode.Create will truncate the file if it exists, effectively overwriting it.
|
||||||
|
// We use FileShare.None to ensure we have exclusive write access, but handle potential IOExceptions gracefully.
|
||||||
|
using (var stream = new FileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
await file.CopyToAsync(stream).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the GET URL that the frontend can use
|
||||||
|
var qs = string.IsNullOrWhiteSpace(filename) ? "" : $"?filename={Uri.EscapeDataString(filename)}&";
|
||||||
|
var getUrl = $"/MediaBarEnhanced/OverlayImage{qs}{(qs == "" ? "?" : "")}t={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
|
||||||
|
return Ok(new { url = getUrl });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, $"Internal server error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the custom overlay image.
|
||||||
|
/// </summary>
|
||||||
|
// [Microsoft.AspNetCore.Authorization.Authorize]
|
||||||
|
[HttpGet("OverlayImage")]
|
||||||
|
public IActionResult GetImage([FromQuery] string? filename = null)
|
||||||
|
{
|
||||||
|
string prefix = string.IsNullOrWhiteSpace(filename) ? "custom_overlay_image_global" : $"custom_overlay_image_{filename}";
|
||||||
|
|
||||||
|
if (!Directory.Exists(_imageDirectory))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var existingFiles = Directory.GetFiles(_imageDirectory, $"{prefix}.*");
|
||||||
|
if (existingFiles.Length == 0)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
string targetPath = existingFiles[0];
|
||||||
|
|
||||||
|
// Read the file and return with appropriate MIME type
|
||||||
|
// We use FileShare.ReadWrite | FileShare.Delete so that if someone is currently overwriting the file (uploading), we don't block them.
|
||||||
|
var stream = new FileStream(targetPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
|
||||||
|
|
||||||
|
string mimeType = "application/octet-stream";
|
||||||
|
string ext = Path.GetExtension(targetPath).ToLowerInvariant();
|
||||||
|
switch (ext) {
|
||||||
|
case ".jpg": case ".jpeg": mimeType = "image/jpeg"; break;
|
||||||
|
case ".png": mimeType = "image/png"; break;
|
||||||
|
case ".gif": mimeType = "image/gif"; break;
|
||||||
|
case ".webp": mimeType = "image/webp"; break;
|
||||||
|
}
|
||||||
|
return File(stream, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes a custom overlay image.
|
||||||
|
/// </summary>
|
||||||
|
// [Microsoft.AspNetCore.Authorization.Authorize]
|
||||||
|
[HttpDelete("OverlayImage")]
|
||||||
|
public IActionResult DeleteImage([FromQuery] string? filename = null)
|
||||||
|
{
|
||||||
|
string prefix = string.IsNullOrWhiteSpace(filename) ? "custom_overlay_image_global" : $"custom_overlay_image_{filename}";
|
||||||
|
|
||||||
|
if (Directory.Exists(_imageDirectory))
|
||||||
|
{
|
||||||
|
var existingFiles = Directory.GetFiles(_imageDirectory, $"{prefix}.*");
|
||||||
|
foreach(var file in existingFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(file);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, $"Error deleting file: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renames a custom overlay image (used when a seasonal section is renamed).
|
||||||
|
/// </summary>
|
||||||
|
// [Microsoft.AspNetCore.Authorization.Authorize]
|
||||||
|
[HttpPut("OverlayImage/Rename")]
|
||||||
|
public IActionResult RenameImage([FromQuery] string oldName, [FromQuery] string newName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(oldName) || string.IsNullOrWhiteSpace(newName))
|
||||||
|
{
|
||||||
|
return BadRequest("Both oldName and newName must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Directory.Exists(_imageDirectory))
|
||||||
|
return Ok();
|
||||||
|
|
||||||
|
var oldFiles = Directory.GetFiles(_imageDirectory, $"custom_overlay_image_{oldName}.*");
|
||||||
|
if (oldFiles.Length == 0)
|
||||||
|
return Ok();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string oldPath = oldFiles[0];
|
||||||
|
string extension = Path.GetExtension(oldPath);
|
||||||
|
string newPath = Path.Combine(_imageDirectory, $"custom_overlay_image_{newName}{extension}");
|
||||||
|
|
||||||
|
// If a file with the new name already exists, delete it first to avoid conflicts
|
||||||
|
var existingNewFiles = Directory.GetFiles(_imageDirectory, $"custom_overlay_image_{newName}.*");
|
||||||
|
foreach(var existing in existingNewFiles) {
|
||||||
|
System.IO.File.Delete(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.IO.File.Move(oldPath, newPath);
|
||||||
|
|
||||||
|
var qs = $"?filename={Uri.EscapeDataString(newName)}&";
|
||||||
|
var getUrl = $"/MediaBarEnhanced/OverlayImage{qs}t={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
|
||||||
|
return Ok(new { url = getUrl });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, $"Error renaming file: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,12 +15,19 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
|
|||||||
public int MaxMovies { get; set; } = 15;
|
public int MaxMovies { get; set; } = 15;
|
||||||
public int MaxTvShows { get; set; } = 15;
|
public int MaxTvShows { get; set; } = 15;
|
||||||
public int MaxItems { get; set; } = 500;
|
public int MaxItems { get; set; } = 500;
|
||||||
|
public int MaxParentalRating { get; set; } = 0;
|
||||||
|
public int MaxDaysRecent { get; set; } = 0;
|
||||||
public int PreloadCount { get; set; } = 3;
|
public int PreloadCount { get; set; } = 3;
|
||||||
public int FadeTransitionDuration { get; set; } = 500;
|
public int FadeTransitionDuration { get; set; } = 500;
|
||||||
public int MaxPaginationDots { get; set; } = 15;
|
public int MaxPaginationDots { get; set; } = 15;
|
||||||
|
public bool ShowPaginationDots { get; set; } = true;
|
||||||
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 RandomizeLocalTrailers { get; set; } = false;
|
||||||
|
public bool PreferLocalBackdrops { get; set; } = false;
|
||||||
|
public bool RandomizeThemeVideos { get; set; } = false;
|
||||||
public bool WaitForTrailerToEnd { get; set; } = true;
|
public bool WaitForTrailerToEnd { get; set; } = true;
|
||||||
public bool StartMuted { get; set; } = true;
|
public bool StartMuted { get; set; } = true;
|
||||||
public bool FullWidthVideo { get; set; } = true;
|
public bool FullWidthVideo { get; set; } = true;
|
||||||
@@ -29,11 +36,29 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
|
|||||||
public bool EnableLoadingScreen { get; set; } = true;
|
public bool EnableLoadingScreen { get; set; } = true;
|
||||||
public bool EnableKeyboardControls { get; set; } = true;
|
public bool EnableKeyboardControls { get; set; } = true;
|
||||||
public bool AlwaysShowArrows { get; set; } = false;
|
public bool AlwaysShowArrows { get; set; } = false;
|
||||||
|
public bool HideArrowsOnMobile { get; set; } = true;
|
||||||
public string CustomMediaIds { get; set; } = "";
|
public string CustomMediaIds { get; set; } = "";
|
||||||
public bool EnableCustomMediaIds { get; set; } = true;
|
public bool EnableCustomMediaIds { get; set; } = true;
|
||||||
public string PreferredVideoQuality { get; set; } = "Auto";
|
public string PreferredVideoQuality { get; set; } = "Auto";
|
||||||
public bool EnableSeasonalContent { get; set; } = false;
|
public bool EnableSeasonalContent { get; set; } = false;
|
||||||
|
public bool ExcludeSeasonalContent { get; set; } = true;
|
||||||
|
public string SeasonalSections { get; set; } = "[]";
|
||||||
public bool IsEnabled { get; set; } = true;
|
public bool IsEnabled { get; set; } = true;
|
||||||
public bool EnableClientSideSettings { get; set; } = false;
|
public bool EnableClientSideSettings { get; set; } = false;
|
||||||
|
public bool ApplyLimitsToCustomIds { get; set; } = false;
|
||||||
|
public bool IncludeWatchedContent { get; set; } = false;
|
||||||
|
public string SortBy { get; set; } = "Random";
|
||||||
|
public string SortOrder { get; set; } = "Ascending";
|
||||||
|
|
||||||
|
public bool EnableCustomOverlay { get; set; } = false;
|
||||||
|
public string CustomOverlayText { get; set; } = "";
|
||||||
|
public string CustomOverlayImageUrl { get; set; } = "";
|
||||||
|
public string CustomOverlayStyle { get; set; } = "Shadowed";
|
||||||
|
public string CustomOverlayImageStyle { get; set; } = "None";
|
||||||
|
public string CustomOverlayPriority { get; set; } = "Image";
|
||||||
|
|
||||||
|
public int CustomOverlayPositionX { get; set; } = 0;
|
||||||
|
public int CustomOverlayPositionY { get; set; } = 0;
|
||||||
|
public int CustomOverlayScale { get; set; } = 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Helpers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Safety Check: If plugin is disabled, do nothing
|
// Safety Check: If plugin is disabled, do nothing
|
||||||
if (!MediaBarEnhancedPlugin.Instance.Configuration.IsEnabled)
|
if (MediaBarEnhancedPlugin.Instance?.Configuration?.IsEnabled != true)
|
||||||
{
|
{
|
||||||
return originalContents;
|
return originalContents;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
|
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
|
||||||
<Title>Jellyfin Media Bar Enhanced Plugin</Title>
|
<Title>Jellyfin Media Bar Enhanced Plugin</Title>
|
||||||
<Authors>CodeDevMLH</Authors>
|
<Authors>CodeDevMLH</Authors>
|
||||||
<Version>1.4.0.3</Version>
|
<Version>1.7.2.7</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>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
{
|
{
|
||||||
private readonly ScriptInjector _scriptInjector;
|
private readonly ScriptInjector _scriptInjector;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
public IServiceProvider ServiceProvider { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MediaBarEnhancedPlugin"/> class.
|
/// Initializes a new instance of the <see cref="MediaBarEnhancedPlugin"/> class.
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
{
|
{
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
private readonly ILogger<ScriptInjector> _logger;
|
private readonly ILogger<ScriptInjector> _logger;
|
||||||
public const string ScriptTag = "<script src=\"/MediaBarEnhanced/Resources/mediaBarEnhanced.js\" defer></script>";
|
public const string ScriptTag = "<script src=\"../MediaBarEnhanced/Resources/mediaBarEnhanced.js\" defer></script>";
|
||||||
public const string CssTag = "<link rel=\"stylesheet\" href=\"/MediaBarEnhanced/Resources/mediaBarEnhanced.css\" />";
|
public const string CssTag = "<link rel=\"stylesheet\" href=\"../MediaBarEnhanced/Resources/mediaBarEnhanced.css\" />";
|
||||||
public const string ScriptMarker = "</body>";
|
public const string ScriptMarker = "</body>";
|
||||||
public const string CssMarker = "</head>";
|
public const string CssMarker = "</head>";
|
||||||
|
|
||||||
@@ -60,6 +60,11 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
var content = File.ReadAllText(indexPath);
|
var content = File.ReadAllText(indexPath);
|
||||||
var injectedJS = false;
|
var injectedJS = false;
|
||||||
var injectedCSS = false;
|
var injectedCSS = false;
|
||||||
|
var modified = false;
|
||||||
|
|
||||||
|
// Cleanup legacy tags first to avoid duplicates or conflicts
|
||||||
|
content = RemoveLegacyTags(content, ref modified);
|
||||||
|
|
||||||
|
|
||||||
if (!content.Contains(ScriptTag))
|
if (!content.Contains(ScriptTag))
|
||||||
{
|
{
|
||||||
@@ -81,21 +86,28 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (injectedJS || injectedCSS || modified)
|
||||||
|
{
|
||||||
|
File.WriteAllText(indexPath, content);
|
||||||
|
|
||||||
if (injectedJS && injectedCSS)
|
if (injectedJS && injectedCSS)
|
||||||
{
|
{
|
||||||
File.WriteAllText(indexPath, content);
|
|
||||||
_logger.LogInformation("MediaBarEnhanced script injected into index.html.");
|
_logger.LogInformation("MediaBarEnhanced script injected into index.html.");
|
||||||
} else if (injectedJS)
|
}
|
||||||
|
else if (injectedJS)
|
||||||
{
|
{
|
||||||
File.WriteAllText(indexPath, content);
|
|
||||||
_logger.LogInformation("MediaBarEnhanced JS script injected into index.html. But CSS was already present or could not be injected.");
|
_logger.LogInformation("MediaBarEnhanced JS script injected into index.html. But CSS was already present or could not be injected.");
|
||||||
}
|
}
|
||||||
else if (injectedCSS)
|
else if (injectedCSS)
|
||||||
{
|
{
|
||||||
File.WriteAllText(indexPath, content);
|
|
||||||
_logger.LogInformation("MediaBarEnhanced CSS injected into index.html. But JS script was already present or could not be injected.");
|
_logger.LogInformation("MediaBarEnhanced CSS injected into index.html. But JS script was already present or could not be injected.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("MediaBarEnhanced script and CSS already present. Legacy tags removed if found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_logger.LogInformation("MediaBarEnhanced script and CSS already present in index.html. Or could not be injected.");
|
_logger.LogInformation("MediaBarEnhanced script and CSS already present in index.html. Or could not be injected.");
|
||||||
}
|
}
|
||||||
@@ -148,6 +160,9 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove legacy tags
|
||||||
|
content = RemoveLegacyTags(content, ref modified);
|
||||||
|
|
||||||
if (modified)
|
if (modified)
|
||||||
{
|
{
|
||||||
File.WriteAllText(indexPath, content);
|
File.WriteAllText(indexPath, content);
|
||||||
@@ -242,5 +257,33 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
_logger.LogWarning(ex, "Error attempting to unregister file transformation. It might not have been registered.");
|
_logger.LogWarning(ex, "Error attempting to unregister file transformation. It might not have been registered.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Removes legacy script and css tags from the content.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">The file content.</param>
|
||||||
|
/// <param name="modified">Ref bool to track if changes were made.</param>
|
||||||
|
/// <returns>The modified content.</returns>
|
||||||
|
private string RemoveLegacyTags(string content, ref bool modified)
|
||||||
|
{
|
||||||
|
// Legacy tags (used in versions prior to 1.6.3.0 where paths started with / instead of ../)
|
||||||
|
const string LegacyScriptTag = "<script src=\"/MediaBarEnhanced/Resources/mediaBarEnhanced.js\" defer></script>";
|
||||||
|
const string LegacyCssTag = "<link rel=\"stylesheet\" href=\"/MediaBarEnhanced/Resources/mediaBarEnhanced.css\" />";
|
||||||
|
|
||||||
|
if (content.Contains(LegacyScriptTag))
|
||||||
|
{
|
||||||
|
content = content.Replace(LegacyScriptTag + Environment.NewLine, "").Replace(LegacyScriptTag, "");
|
||||||
|
modified = true;
|
||||||
|
_logger.LogInformation("Legacy MediaBarEnhanced script tag removed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.Contains(LegacyCssTag))
|
||||||
|
{
|
||||||
|
content = content.Replace(LegacyCssTag + Environment.NewLine, "").Replace(LegacyCssTag, "");
|
||||||
|
modified = true;
|
||||||
|
_logger.LogInformation("Legacy MediaBarEnhanced CSS tag removed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 |
@@ -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 |
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Jellyfin Slideshow by M0RPH3US v3.0.9
|
* Jellyfin Slideshow by M0RPH3US v4.0.1
|
||||||
* Modified by CodeDevMLH v1.1.0.0
|
* Modified by CodeDevMLH
|
||||||
*
|
*
|
||||||
* New features:
|
* New features:
|
||||||
* - optional Trailer background video support
|
* - optional Trailer background video support
|
||||||
@@ -14,6 +14,9 @@
|
|||||||
* - 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)
|
* - option to enable client-side settings (allow users to override settings locally on their device)
|
||||||
|
* - option to enable seasonal content (only show items that are relevant to the current season/holiday)
|
||||||
|
* - option to prefer local trailers (from the media item) over online sources
|
||||||
|
* - options to sort the content by various criteria (PremiereDate, ProductionYear, Random, Original order, etc.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@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);
|
||||||
@@ -172,6 +175,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#slides-container[style*="display: none"],
|
#slides-container[style*="display: none"],
|
||||||
@@ -350,12 +354,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
mask-image: linear-gradient(to top,
|
mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
-webkit-mask-image: linear-gradient(to top,
|
-webkit-mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,12 +384,12 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
mask-image: linear-gradient(to top,
|
mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
-webkit-mask-image: linear-gradient(to top,
|
-webkit-mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,12 +403,12 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
mask-image: linear-gradient(to top,
|
mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 4%,
|
rgba(0, 0, 0, 0.5) 4%,
|
||||||
#000000 6%);
|
#000000 6%);
|
||||||
-webkit-mask-image: linear-gradient(to top,
|
-webkit-mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 4%,
|
rgba(0, 0, 0, 0.5) 4%,
|
||||||
#000000 6%);
|
#000000 6%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,12 +424,12 @@
|
|||||||
rgba(29, 29, 29, 0) 100%);
|
rgba(29, 29, 29, 0) 100%);
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
mask-image: linear-gradient(to top,
|
mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 4%,
|
rgba(0, 0, 0, 0.5) 4%,
|
||||||
#000000 6%);
|
#000000 6%);
|
||||||
-webkit-mask-image: linear-gradient(to top,
|
-webkit-mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 4%,
|
rgba(0, 0, 0, 0.5) 4%,
|
||||||
#000000 6%);
|
#000000 6%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,6 +525,8 @@
|
|||||||
font-family: "Archivo Narrow", sans-serif;
|
font-family: "Archivo Narrow", sans-serif;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
color: rgb(0, 0, 0);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -531,6 +537,7 @@
|
|||||||
|
|
||||||
.detail-button {
|
.detail-button {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
color: rgb(0, 0, 0);
|
color: rgb(0, 0, 0);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@@ -543,6 +550,7 @@
|
|||||||
|
|
||||||
.favorite-button {
|
.favorite-button {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
color: red;
|
color: red;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@@ -658,7 +666,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: rgb(255 255 255 / 0.8);
|
background: rgba(255, 255, 255, 0.8);
|
||||||
color: #000;
|
color: #000;
|
||||||
border: none;
|
border: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -708,12 +716,12 @@
|
|||||||
object-position: center 20%;
|
object-position: center 20%;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
mask-image: linear-gradient(to top,
|
mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
-webkit-mask-image: linear-gradient(to top,
|
-webkit-mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,16 +731,16 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgb(0 0 0 / 0.25);
|
background: rgba(0, 0, 0, 0.25);
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
mask-image: linear-gradient(to top,
|
mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
-webkit-mask-image: linear-gradient(to top,
|
-webkit-mask-image: linear-gradient(to top,
|
||||||
#fff0 2%,
|
rgba(255, 255, 255, 0) 2%,
|
||||||
rgb(0 0 0 / 0.5) 6%,
|
rgba(0, 0, 0, 0.5) 6%,
|
||||||
#000000 8%);
|
#000000 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,7 +772,22 @@
|
|||||||
.button-container {
|
.button-container {
|
||||||
top: calc(50% + 25vh);
|
top: calc(50% + 25vh);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%) scale(0.95);
|
transform: translateX(-50%);
|
||||||
|
width: max-content;
|
||||||
|
max-width: 98vw;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container button {
|
||||||
|
margin: 0 !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container .detail-button,
|
||||||
|
.button-container .favorite-button {
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@@ -821,10 +844,6 @@
|
|||||||
.genre {
|
.genre {
|
||||||
top: calc(50% + 16vh);
|
top: calc(50% + 16vh);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-container {
|
|
||||||
top: calc(50% + 27vh);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -992,3 +1011,446 @@
|
|||||||
.dots-container .slide-counter {
|
.dots-container .slide-counter {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-tv .backdrop-container {
|
||||||
|
top: -5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 960px) and (-webkit-device-pixel-ratio: 1) {
|
||||||
|
.layout-tv .backdrop.animate {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-tv .logo.animate {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-tv .slide-counter,
|
||||||
|
.layout-tv .dots-container {
|
||||||
|
backdrop-filter: none;
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Overlay Styling */
|
||||||
|
.custom-overlay-container {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(8vh + var(--overlay-y, 0vh));
|
||||||
|
left: calc(4vw + var(--overlay-x, 0vw));
|
||||||
|
transform: scale(var(--overlay-scale, 1));
|
||||||
|
z-index: 15;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: overlayFadeInGlobal 1.5s ease-in-out forwards;
|
||||||
|
/* animation: fadeInOverlay 1.5s ease-in-out forwards; */
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes overlayFadeInGlobal {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @keyframes fadeInOverlay {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
.custom-overlay-text {
|
||||||
|
font-family: "Archivo Narrow", sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.8), -1px -1px 4px rgba(0, 0, 0, 0.5);
|
||||||
|
margin: 0;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-image {
|
||||||
|
max-width: 300px;
|
||||||
|
max-height: 120px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(2px 4px 6px rgba(0,0,0,0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 767px) and (orientation: portrait) {
|
||||||
|
.custom-overlay-container {
|
||||||
|
top: calc(15vh + var(--overlay-y, 0vh));
|
||||||
|
left: calc(50% + var(--overlay-x, 0vw));
|
||||||
|
transform: translateX(-50%) scale(var(--overlay-scale, 1));
|
||||||
|
width: 90%;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-text {
|
||||||
|
font-size: 1.5rem; /* 1.5 - 1.8 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @keyframes fadeInOverlay {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
.custom-overlay-image {
|
||||||
|
max-width: 150px; /* oder 200px? */
|
||||||
|
max-height: 60px; /* oder 80px? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Overlay Text Styles */
|
||||||
|
.custom-overlay-style-None {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Shadowed {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.9), -1px -1px 4px rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Frosted {
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(135deg, rgba(255,255,255,0.15), rgba(255,255,255,0));
|
||||||
|
backdrop-filter: blur(20px) saturate(1.5);
|
||||||
|
-webkit-backdrop-filter: blur(20px) saturate(1.5);
|
||||||
|
padding: 10px 30px;
|
||||||
|
border-radius: 50px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Cinematic {
|
||||||
|
background: linear-gradient(to right, #bf953f, #fcf6ba, #b38728, #fbf5b7, #aa771c, #fcf6ba, #bf953f);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: none;
|
||||||
|
filter: drop-shadow(0px 2px 8px rgba(255, 215, 0, 0.4)) drop-shadow(2px 2px 4px rgba(0,0,0,0.8));
|
||||||
|
animation: shineCinematic 6s linear infinite;
|
||||||
|
background-size: 200% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shineCinematic {
|
||||||
|
0% { background-position: 0% center; }
|
||||||
|
100% { background-position: 200% center; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Pulse {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.9), -1px -1px 4px rgba(0, 0, 0, 0.8);
|
||||||
|
animation: pulseOverlayText 3s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulseOverlayText {
|
||||||
|
from {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Neon {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px #fff,
|
||||||
|
0 0 10px #fff,
|
||||||
|
0 0 20px #ff00de,
|
||||||
|
0 0 40px #ff00de,
|
||||||
|
0 0 80px #ff00de,
|
||||||
|
0 0 90px #ff00de,
|
||||||
|
0 0 100px #ff00de,
|
||||||
|
0 0 150px #ff00de;
|
||||||
|
animation: flickerNeon 1.5s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flickerNeon {
|
||||||
|
0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% {
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px #fff,
|
||||||
|
0 0 10px #fff,
|
||||||
|
0 0 20px #ff00de,
|
||||||
|
0 0 40px #ff00de,
|
||||||
|
0 0 80px #ff00de,
|
||||||
|
0 0 90px #ff00de,
|
||||||
|
0 0 100px #ff00de,
|
||||||
|
0 0 150px #ff00de;
|
||||||
|
}
|
||||||
|
20%, 24%, 55% {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Typewriter {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
background-color: #222;
|
||||||
|
color: #00ff00;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: 2px solid #00ff00;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 4px 4px 0px #00ff00;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Bubble {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
-webkit-backdrop-filter: blur(5px);
|
||||||
|
padding: 12px 30px;
|
||||||
|
border-radius: 100px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), inset 0 0 20px rgba(255,255,255,0.2);
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||||
|
animation: floatBubble 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes floatBubble {
|
||||||
|
0% { transform: translateY(0px); }
|
||||||
|
50% { transform: translateY(-15px); }
|
||||||
|
100% { transform: translateY(0px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-SlideIn {
|
||||||
|
color: #fff;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 5px;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
|
||||||
|
position: relative;
|
||||||
|
animation: slideInCinematic 1.2s cubic-bezier(0.25, 1, 0.5, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-SlideIn::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
bottom: -10px;
|
||||||
|
left: -50vw;
|
||||||
|
right: -50px;
|
||||||
|
background: linear-gradient(to right, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.6) 40%, transparent 100%);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInCinematic {
|
||||||
|
from {
|
||||||
|
transform: translateX(-100vw);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-SteadyNeon {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px #fff,
|
||||||
|
0 0 10px #fff,
|
||||||
|
0 0 20px #ff00de,
|
||||||
|
0 0 40px #ff00de,
|
||||||
|
0 0 80px #ff00de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Glitch {
|
||||||
|
color: #fff;
|
||||||
|
position: relative;
|
||||||
|
text-shadow: 2px 2px 8px rgba(0,0,0,0.8);
|
||||||
|
animation: glitchText 3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glitchText {
|
||||||
|
0% { text-shadow: 2px 2px 8px rgba(0,0,0,0.8); transform: translate(0); }
|
||||||
|
20% { text-shadow: 2px 2px 8px rgba(0,0,0,0.8); transform: translate(0); }
|
||||||
|
21% { text-shadow: -2px 0 0 #ff00c1, 2px 0 0 #00fff9; transform: translate(-2px, 1px); }
|
||||||
|
23% { text-shadow: 2px 0 0 #ff00c1, -2px 0 0 #00fff9; transform: translate(2px, -1px); }
|
||||||
|
25% { text-shadow: 2px 2px 8px rgba(0,0,0,0.8); transform: translate(0); }
|
||||||
|
100% { text-shadow: 2px 2px 8px rgba(0,0,0,0.8); transform: translate(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-RetroPop {
|
||||||
|
color: #f7f7f7;
|
||||||
|
text-shadow:
|
||||||
|
2px 2px 0px #ff0055,
|
||||||
|
4px 4px 0px #00a4dc,
|
||||||
|
6px 6px 0px #ffcc00,
|
||||||
|
8px 8px 10px rgba(0,0,0,0.6);
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Shimmer {
|
||||||
|
color: rgba(255,255,255,0.7);
|
||||||
|
background: linear-gradient(to right, #222 20%, #fff 40%, #fff 60%, #222 80%);
|
||||||
|
background-size: 200% auto;
|
||||||
|
color: #000;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
animation: shimmerText 3s linear infinite;
|
||||||
|
text-shadow: 2px 2px 8px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmerText {
|
||||||
|
to {
|
||||||
|
background-position: 200% center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Wave {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 2px 2px 8px rgba(0,0,0,0.8);
|
||||||
|
display: inline-block;
|
||||||
|
animation: liquidWave 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes liquidWave {
|
||||||
|
0%, 100% { transform: translateY(0) skewY(0); }
|
||||||
|
25% { transform: translateY(-3px) skewY(1deg); }
|
||||||
|
50% { transform: translateY(0) skewY(0); }
|
||||||
|
75% { transform: translateY(3px) skewY(-1deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-VHS {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 3px 0 0 #f00, -3px 0 0 #0ff;
|
||||||
|
animation: vhsTracking 2s steps(2, start) infinite;
|
||||||
|
}
|
||||||
|
@keyframes vhsTracking {
|
||||||
|
0%, 100% { text-shadow: 3px 0 0 #f00, -3px 0 0 #0ff; }
|
||||||
|
50% { text-shadow: 2px 1px 0 #f00, -2px -1px 0 #0ff; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Matrix {
|
||||||
|
color: #0f0;
|
||||||
|
font-family: monospace;
|
||||||
|
text-shadow: 0 0 5px #0f0, 0 0 10px #0f0;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
animation: matrixGlow 2s infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes matrixGlow {
|
||||||
|
to { text-shadow: 0 0 10px #0f0, 0 0 20px #0f0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-Ghost {
|
||||||
|
color: rgba(255,255,255,0.8);
|
||||||
|
filter: blur(0px);
|
||||||
|
animation: ghostApparition 6s infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes ghostApparition {
|
||||||
|
0% { filter: blur(0px); opacity: 1; transform: scale(1); }
|
||||||
|
50% { filter: blur(3px); opacity: 0.6; transform: scale(1.02); }
|
||||||
|
100% { filter: blur(8px); opacity: 0; transform: scale(1.05); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-style-PulseGlow {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 10px rgba(255,255,255,0.8);
|
||||||
|
animation: pulseGlowAura 3s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes pulseGlowAura {
|
||||||
|
0% { text-shadow: 0 0 5px rgba(255,255,255,0.5), 0 0 10px #00a4dc; transform: scale(1); }
|
||||||
|
100% { text-shadow: 0 0 15px rgba(255,255,255,1), 0 0 30px #00a4dc, 0 0 40px #00a4dc; transform: scale(1.05); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Overlay Image Styles */
|
||||||
|
.custom-overlay-img-RoundedShadow {
|
||||||
|
border-radius: 12px;
|
||||||
|
filter: drop-shadow(0px 10px 15px rgba(0, 0, 0, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-GlowingBorder {
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px #00a4dc, 0 0 20px #00a4dc, 0 0 30px #00a4dc, inset 0 0 10px #00a4dc;
|
||||||
|
animation: pulseGlowImg 2s infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes pulseGlowImg {
|
||||||
|
from { box-shadow: 0 0 10px #00a4dc, 0 0 20px #00a4dc, 0 0 30px #00a4dc; }
|
||||||
|
to { box-shadow: 0 0 15px #00a4dc, 0 0 30px #00a4dc, 0 0 45px #00a4dc; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-Polaroid {
|
||||||
|
background: white;
|
||||||
|
padding: 10px 10px 25px 10px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.4), 0 12px 24px rgba(0,0,0,0.3);
|
||||||
|
transform: rotate(-3deg);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-Vintage {
|
||||||
|
filter: sepia(0.6) contrast(1.2) brightness(0.9) saturate(1.2) drop-shadow(2px 4px 8px rgba(0,0,0,0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-Grayscale {
|
||||||
|
filter: grayscale(100%) contrast(1.1) drop-shadow(2px 4px 8px rgba(0,0,0,0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-Hologram {
|
||||||
|
filter: drop-shadow(0 0 10px #0ff) sepia(0.8) hue-rotate(180deg) saturate(3);
|
||||||
|
opacity: 0.8;
|
||||||
|
animation: hologramFlicker 3s infinite;
|
||||||
|
}
|
||||||
|
@keyframes hologramFlicker {
|
||||||
|
0% { opacity: 0.8; transform: skewX(0); }
|
||||||
|
5% { opacity: 0.5; transform: skewX(2deg); }
|
||||||
|
10% { opacity: 0.9; transform: skewX(-2deg); }
|
||||||
|
15% { opacity: 0.8; transform: skewX(0); }
|
||||||
|
100% { opacity: 0.8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-CRT {
|
||||||
|
filter: contrast(1.5) brightness(1.2) drop-shadow(3px 0 0 rgba(255,0,0,0.5)) drop-shadow(-3px 0 0 rgba(0,0,255,0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-Floating {
|
||||||
|
filter: drop-shadow(0 15px 20px rgba(0,0,0,0.6));
|
||||||
|
animation: floatImg 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes floatImg {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-15px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-VHSTracking {
|
||||||
|
filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5));
|
||||||
|
animation: imgVhsTracking 2s infinite;
|
||||||
|
}
|
||||||
|
@keyframes imgVhsTracking {
|
||||||
|
0% { transform: translateX(0); clip-path: inset(0 0 0 0); filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)) hue-rotate(0deg); }
|
||||||
|
5% { transform: translateX(-5px); clip-path: inset(10% 0 80% 0); filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)) hue-rotate(90deg); }
|
||||||
|
10% { transform: translateX(5px); clip-path: inset(40% 0 40% 0); filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)) hue-rotate(-90deg); }
|
||||||
|
15% { transform: translateX(0); clip-path: inset(0 0 0 0); filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)) hue-rotate(0deg); }
|
||||||
|
100% { transform: translateX(0); clip-path: inset(0 0 0 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-ColorCycle {
|
||||||
|
filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5));
|
||||||
|
animation: imgColorCycle 10s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes imgColorCycle {
|
||||||
|
from { filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)) hue-rotate(0deg); }
|
||||||
|
to { filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)) hue-rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-overlay-img-Mirror {
|
||||||
|
filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5));
|
||||||
|
-webkit-box-reflect: below 2px linear-gradient(transparent, rgba(255,255,255,0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
81
README.md
81
README.md
@@ -20,11 +20,14 @@ This plugin is a fork and enhancement of the original [Media Bar by MakD](https:
|
|||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [General Settings](#general-settings)
|
- [General Settings](#general-settings)
|
||||||
- [Custom Content](#custom-content)
|
- [Custom Content](#custom-content)
|
||||||
|
- [Content Sorting](#content-sorting)
|
||||||
|
- [Content Limits](#content-limits)
|
||||||
- [Advanced Settings](#advanced-settings)
|
- [Advanced Settings](#advanced-settings)
|
||||||
- [Build The Plugin By Yourself](#build-the-plugin-by-yourself)
|
- [Build The Plugin By Yourself](#build-the-plugin-by-yourself)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Effects Not Showing](#effects-not-showing)
|
- [Effects Not Showing](#effects-not-showing)
|
||||||
- [Docker Permission Issues](#docker-permission-issues)
|
- [Docker Permission Issues](#docker-permission-issues)
|
||||||
|
- [Credits](#credits)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -82,11 +85,27 @@ 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>
|
||||||
|
* **Local Trailers Preference**: Option to prefer local trailers (from the media item) over online sources.
|
||||||
|
* **Theme Video Support**: Option to prefer local theme videos (backdrops) over trailers.
|
||||||
|
* **Randomization**: Options to randomize theme videos and local trailers if multiple versions exist.
|
||||||
|
* **Include Watched Content**: Option to include watched items in the random slideshow.
|
||||||
|
* **Content Sorting Options**: Sort content by various criteria such as PremiereDate, ProductionYear, Random, or Original order.
|
||||||
|
* **Client-Side Settings**: Allow users to override settings locally on their device.
|
||||||
|
|
||||||
### Core Features
|
### Core Features
|
||||||
* **Immersive Slideshow**: Rotates through your media library
|
* **Immersive Slideshow**: Rotates through your media library
|
||||||
@@ -94,6 +113,7 @@ This plugin builds upon the original Media Bar with new capabilities and improve
|
|||||||
* **Direct Play**: Click "Play" to start watching immediately
|
* **Direct Play**: Click "Play" to start watching immediately
|
||||||
* **Details View**: Click "Info" to jump to the item's detail page
|
* **Details View**: Click "Info" to jump to the item's detail page
|
||||||
* **Add To Favorites**: Click the heart to add the item to your favorites
|
* **Add To Favorites**: Click the heart to add the item to your favorites
|
||||||
|
* **Customize**: Change the plugins behavior through the Jellyfin admin panel
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -118,13 +138,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
|
||||||
|
|
||||||
@@ -139,30 +160,62 @@ Configure the plugin via **Dashboard** > **Plugins** > **Media Bar Enhanced**.
|
|||||||
* **Wait For Trailer To End**: Prevents slide transition until the video finishes.
|
* **Wait For Trailer To End**: Prevents slide transition until the video finishes.
|
||||||
* **Enable Mobile Video**: specific setting to allow video playback on mobile devices (disabled by default to save data/battery).
|
* **Enable Mobile Video**: specific setting to allow video playback on mobile devices (disabled by default to save data/battery).
|
||||||
* **Show Trailer Button**: Adds a button to open the trailer in a popup modal if video backdrops are disabled (e.g. on mobile if trailers are disabled there)
|
* **Show Trailer Button**: Adds a button to open the trailer in a popup modal if video backdrops are disabled (e.g. on mobile if trailers are disabled there)
|
||||||
|
* **Prefer Local Trailers**: If enabled, local trailers will be preferred over remote (YouTube) trailers.
|
||||||
|
* **Prefer Local Backdrops / Theme Videos**: If enabled, local backdrop videos (Theme Videos) will be preferred over trailers.
|
||||||
|
|
||||||
### Custom Content
|
### Custom Content
|
||||||
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.
|
* **Manual Trailer Override**: Add `[YouTube_URL]` or `[Jellyfin_ID]` after an ID to force a specific trailer/video.
|
||||||
* Example ID: `a1b2c3d4e5... [https://www.youtube.com/watch?v=VIDEO_ID]`
|
* Example ID: `a1b2c3d4e5... [https://www.youtube.com/watch?v=VIDEO_ID]`
|
||||||
|
* Example ID: `z1b2c3d4e5... [Jellyfin_ID]`
|
||||||
|
* **Example Mixed List**:
|
||||||
|
```
|
||||||
|
a1b2c3d4e5f6... <-- Plays local item video
|
||||||
|
6bdu812812hd... [https://youtu.be/...] <-- Item metadata + Custom YouTube Trailer
|
||||||
|
12h44h124sf7... [hdc78127z4ff...] <-- Item metadata + Custom Jellyfin Trailer/Video etc.
|
||||||
|
```
|
||||||
* Example Collection Name: `Halloween Collection [https://...] | My Description` (Note: Use `|` to separate description from name if using a name instead of an ID)
|
* Example Collection Name: `Halloween Collection [https://...] | My Description` (Note: Use `|` to separate description from name if using a name instead of an ID)
|
||||||
|
* **Apply Limits to Custom IDs**: If enabled, the "Content Limits" (see below) will also apply to your Custom Media IDs list. By default, custom lists show all listed items regardless of limits.
|
||||||
* **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`
|
* **GUI Configuration**: You can easily add "Seasons" via the **Add Season** button.
|
||||||
* Example: `20.10-31.10 | Halloween | <ID_OF_HALLOWEEN_COLLECTION>`
|
* **Active Period**: Select the Start and End Day/Month for each season.
|
||||||
* If the current date matches a range, those IDs are used. Otherwise, it defaults to standard behavior or the Custom Media IDs list.
|
* **Media IDs**: Enter the Comma-separated list of IDs (Movies, Series, Collections) for that season.
|
||||||
|
* **Priority**: If the current date matches a defined season, those IDs are used. If multiple seasons overlap, the first matching one is used. If no season matches, it falls back to the Default Custom Media IDs.
|
||||||
|
|
||||||
**How to get IDs:**
|
**How to get IDs:**
|
||||||
Check the URL of an item in the web interface:
|
Check the URL of an item in the web interface:
|
||||||
`.../web/#/details?id=YOUR_ITEM_ID_HERE&...`
|
`.../web/#/details?id=YOUR_ITEM_ID_HERE&...`
|
||||||
|
|
||||||
|
### Content Sorting
|
||||||
|
Customize the order of slides in the Media Bar.
|
||||||
|
|
||||||
|
* **Sort By**: Choose criteria like *Random*, *Premiere Date*, *Production Year*, *Critic Rating*, *Community Rating*, *Name*, or *Runtime*.
|
||||||
|
* **Sort Order**: Ascending or Descending.
|
||||||
|
* **Note**: Sorting applies to both server-fetched content AND Custom Media IDs. Select **Original** to preserve the exact order of your Custom Media IDs list.
|
||||||
|
|
||||||
|
### Content Limits
|
||||||
|
Fine-tune performance by limiting the number of items fetched from the server.
|
||||||
|
|
||||||
|
* **Total Max Items**: Maximum total items to fetch (combined).
|
||||||
|
* **Include Watched Content**: If enabled, the random slideshow will also include items that you have already watched.
|
||||||
|
* **Max Movies**: Maximum movies to include (for random selection).
|
||||||
|
* **Max Tv Shows**: Maximum TV shows to include (for random selection).
|
||||||
|
* **Preload Count**: Number of slides to preload for smooth transitions.
|
||||||
|
* *Intelligent Preloading*: The plugin uses a safe preloading strategy that respects this count but handles small lists gracefully to avoid playback issues.
|
||||||
|
* **Max Pagination Dots**: Maximum number of dots to show. If exceeded, it switches to a counter (e.g., 1/20).
|
||||||
|
|
||||||
### Advanced Settings
|
### Advanced Settings
|
||||||
* **Slide Animations**: Enable/disable the "Zoom In" effect.
|
* **Slide Animations**: Enable/disable the "Zoom In" effect.
|
||||||
* **Use SponsorBlock**: Skips non-content segments in YouTube trailers (if the data exists).
|
* **Use SponsorBlock**: Skips non-content segments in YouTube trailers (if the data exists).
|
||||||
|
* **Preferred YouTube Quality**: Select your preferred resolution (*Auto*, *Maximum*, *1080p*, *720p*).
|
||||||
* **Start Muted**: Videos start without sound (user can unmute).
|
* **Start Muted**: Videos start without sound (user can unmute).
|
||||||
* **Full Width Video**: Stretches video to cover the entire width (good for desktop, crop on mobile).
|
* **Full Width Video**: Stretches video to cover the entire width (good for desktop, crop on mobile).
|
||||||
* **Enable Loading Screen**: Enable/disable the loading indicator while the bar initializes.
|
* **Enable Loading Screen**: Enable/disable the loading indicator while the bar initializes.
|
||||||
* **Always Show Arrows**: Keeps navigation arrows visible instead of hiding them on mouse leave.
|
* **Always Show Arrows**: Keeps navigation arrows visible instead of hiding them on mouse leave.
|
||||||
|
* **Randomize Backdrop Video**: If enabled, a random video from the backdrops/theme videos will be selected instead of the first one.
|
||||||
|
* **Randomize Local Trailer**: If enabled, a random local trailer will be selected instead of the first one.
|
||||||
* **Enable Keyboard Controls**:
|
* **Enable Keyboard Controls**:
|
||||||
* `Left`/`Right`: Change slide
|
* `Left`/`Right`: Change slide
|
||||||
* `Space`: Pause/Play slideshow
|
* `Space`: Pause/Play slideshow
|
||||||
@@ -233,6 +286,12 @@ volumes:
|
|||||||
- /path/to/jellyfin/config/index.html:/jellyfin/jellyfin-web/index.html
|
- /path/to/jellyfin/config/index.html:/jellyfin/jellyfin-web/index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
This project is based on the original [Jellyfin Media Bar by MakD](https://github.com/MakD/Jellyfin-Media-Bar) and incorporates concepts from [IAmParadox27's plugin fork](https://github.com/IAmParadox27/jellyfin-plugin-media-bar). Thanks for their work!
|
||||||
|
|
||||||
|
Also, special thanks to IAmParadox27 for the [File Transformation plugin](https://github.com/IAmParadox27/jellyfin-plugin-file-transformation) which this plugin can optionally use for improved Docker compatibility.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Feel free to contribute to this project by creating pull requests or reporting issues.
|
Feel free to contribute to this project by creating pull requests or reporting issues.
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Bevor du baust, musst du die Versionsnummer in den folgenden Dateien aktualisier
|
|||||||
Führe den folgenden Befehl im Terminal (PowerShell) im Hauptverzeichnis aus. Wir nutzen hier `dotnet build` statt `publish`, um unnötige Dateien zu vermeiden.
|
Führe den folgenden Befehl im Terminal (PowerShell) im Hauptverzeichnis aus. Wir nutzen hier `dotnet build` statt `publish`, um unnötige Dateien zu vermeiden.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet build Jellyfin.Plugin.MediaBar/Jellyfin.Plugin.MediaBar.csproj --configuration Release --output bin/Publish; Compress-Archive -Path bin/Publish/* -DestinationPath bin/Publish/Jellyfin.Plugin.MediaBar.zip -Force; $hash = (Get-FileHash -Algorithm MD5 bin/Publish/Jellyfin.Plugin.MediaBar.zip).Hash.ToLower(); $time = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); Write-Output "`n----------------------------------------"; Write-Output "NEUE CHECKSUMME (MD5): $hash"; Write-Output "ZEITSTEMPEL: $time"; Write-Output "----------------------------------------`n"
|
dotnet build Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj --configuration Release --output bin/Publish; Compress-Archive -Path bin/Publish/* -DestinationPath bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip -Force; $hash = (Get-FileHash -Algorithm MD5 bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip).Hash.ToLower(); $time = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); Write-Output "`n----------------------------------------"; Write-Output "NEUE CHECKSUMME (MD5): $hash"; Write-Output "ZEITSTEMPEL: $time"; Write-Output "----------------------------------------`n"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. Manifest aktualisieren
|
## 3. Manifest aktualisieren
|
||||||
|
|||||||
2911
configPageExample.html
Normal file
2911
configPageExample.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,12 +9,92 @@
|
|||||||
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
|
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
|
||||||
"versions": [
|
"versions": [
|
||||||
{
|
{
|
||||||
"version": "1.4.0.3",
|
"version": "1.7.2.7",
|
||||||
"changelog": "- feat: Add client-side settings feature for selected media bar settings",
|
"changelog": "feat: add custom text/image overlay option\n- feat: add option to disable pagination dots/counter\n- feat: add exclude seasonal content from random fetching option\n- Add hide arrows on mobile option \n- fix button issue on mobile when using ElegantFin Theme",
|
||||||
"targetAbi": "10.11.0.0",
|
"targetAbi": "10.11.0.0",
|
||||||
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.4.0.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.7.2.7/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
"checksum": "9abf21c095e1ae99cdbeb51edb08f370",
|
"checksum": "8d9939d696b3528ddfc4378b91881007",
|
||||||
"timestamp": "2026-02-04T15:52:11Z"
|
"timestamp": "2026-03-10T22:49:35Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.7.0.14",
|
||||||
|
"changelog": "- Switched to YouTube no-cookie host and referrer policy for iframe security\n- fix playback issues on iOS/MacOS \n- Disable animations and backdrop filters for TV layout\n- removed list.txt functionality to clean up, use the web ui instead\n- Enhance logging with contextual messages, in order to be able to clearly assign logs to this plugin",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.7.0.14/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "07875c74aab766657be3b8033be6d53f",
|
||||||
|
"timestamp": "2026-03-06T03:13:48Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.6.4",
|
||||||
|
"changelog": "- feat: add static backdrop also for video backdrops\n- fix: renaming issue of settings (avoiding conflict with other plugins)",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.6.4/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "2c55cf9687e44b04a0824997e2980dc9",
|
||||||
|
"timestamp": "2026-02-19T17:21:40Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.5.2",
|
||||||
|
"changelog": "- refactored seasonal UI settings",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.5.2/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "552cb3376c77ede5a0664ced56bf7d1e",
|
||||||
|
"timestamp": "2026-02-16T23:57:57Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.4.1",
|
||||||
|
"changelog": "- fix slide transition when using local/backdrop videos",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.4.1/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "a9c5a863427de84639eca082483936da",
|
||||||
|
"timestamp": "2026-02-15T22:56:17Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.3.1",
|
||||||
|
"changelog": "- fix path issue on subpath installations",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.3.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "6a952445bfb80ba4603017358e48da91",
|
||||||
|
"timestamp": "2026-02-15T22:38:19Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.2.3",
|
||||||
|
"changelog": "- feat: add options for local backdrops (theme videos) support and randomization features for local trailer/backdrops if more than one is available\n- feat: add option to include watched content in the random selection",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.2.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "c7ff2d783889c25b5a53783bfbe30b11",
|
||||||
|
"timestamp": "2026-02-15T00:38:07Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.1.32",
|
||||||
|
"changelog": "- feat: add seasonal UI logic\n- add option to also set the limits for custom ids\n- fix tv mode scroll issue\n- smaller fixes and improvements",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.1.32/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "e196fd393ef0bcf51f8ce535103f1811",
|
||||||
|
"timestamp": "2026-02-14T16:34:32Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.0.2",
|
||||||
|
"changelog": "- add local trailer support on trailer button\nfix: iOS/MacOS playback issue?",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.0.2/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "cdd0208f8cc4f4b04f50e7138e508370",
|
||||||
|
"timestamp": "2026-02-10T22:07:33Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.5.1.3",
|
||||||
|
"changelog": "- fix: iOS/MacOS playback issue?",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.5.1.3/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "9d9dbed453673d4b78acf2adaaaee126",
|
||||||
|
"timestamp": "2026-02-10T20:12:59Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.5.0.28",
|
||||||
|
"changelog": "- fix: Keyboard controls in TV mode\n- Add sorting options for content\n- Add local trailer support\n- fix performance issue\n- Update mediaBarEnhanced.js and mediaBarEnhanced.css with version 4.0.1 from upstream repo",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.5.0.28/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "0261ff27be18d48cefa5078706954240",
|
||||||
|
"timestamp": "2026-02-10T00:35:41Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"version": "1.3.0.3",
|
"version": "1.3.0.3",
|
||||||
|
|||||||
116
test_scripts/check_backdrop_fields.js
Normal file
116
test_scripts/check_backdrop_fields.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
(async () => {
|
||||||
|
// 1. Initialisierung
|
||||||
|
const apiClient = window.ApiClient;
|
||||||
|
if (!apiClient) {
|
||||||
|
console.error("❌ ApiClient nicht gefunden. Bitte in der Browser-Konsole einer Jellyfin-Seite ausführen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Item ID abfragen oder festlegen
|
||||||
|
let itemId = prompt("Bitte die Item ID eingeben (von einem Film/Serie mit Theme Video/Backdrop Video):");
|
||||||
|
if (!itemId) {
|
||||||
|
console.warn("Keine Item ID eingegeben. Abbruch.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemId = itemId.trim();
|
||||||
|
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
console.log(`%c🔍 Untersuche Item: ${itemId}`, "color: #00a4dc; font-size: 1.2em; font-weight: bold;");
|
||||||
|
|
||||||
|
// 3. Helper Funktion für Fetch requests
|
||||||
|
const fetchJson = async (url) => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `MediaBrowser Client="Jellyfin Web", Device="Browser", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (res.ok) return await res.json();
|
||||||
|
console.warn(`⚠️ Request fehlgeschlagen: ${url} (${res.status})`);
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`❌ Fehler bei Request: ${url}`, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// TEST 1: Standard Item Details mit erweiterten Feldern
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
console.group("1. Standard Item Details (mit Fields)");
|
||||||
|
const fields = "Overview,RemoteTrailers,MediaSources,LocalTrailerCount,ThemeVideoIds,ThemeSongIds";
|
||||||
|
const itemDetailsUrl = `${apiClient.serverAddress()}/Users/${userId}/Items/${itemId}?Fields=${fields}`;
|
||||||
|
|
||||||
|
const item = await fetchJson(itemDetailsUrl);
|
||||||
|
if (item) {
|
||||||
|
console.log("Name:", item.Name);
|
||||||
|
console.log("ThemeVideoIds:", item.ThemeVideoIds);
|
||||||
|
console.log("ThemeSongIds:", item.ThemeSongIds);
|
||||||
|
console.log("LocalTrailerCount:", item.LocalTrailerCount);
|
||||||
|
console.log("RemoteTrailers:", item.RemoteTrailers);
|
||||||
|
|
||||||
|
if (item.ThemeVideoIds && item.ThemeVideoIds.length > 0) {
|
||||||
|
console.log(`%c✅ ThemeVideoIds gefunden: ${item.ThemeVideoIds.length}`, "color: green; font-weight: bold;");
|
||||||
|
} else {
|
||||||
|
console.log(`%c❌ Keine ThemeVideoIds im Item-Objekt.`, "color: orange;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// TEST 2: ThemeMedia Endpoint
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
console.group("2. Endpoint: /Items/{Id}/ThemeMedia");
|
||||||
|
const themeMediaUrl = `${apiClient.serverAddress()}/Items/${itemId}/ThemeMedia?userId=${userId}`;
|
||||||
|
const themeMedia = await fetchJson(themeMediaUrl);
|
||||||
|
|
||||||
|
if (themeMedia) {
|
||||||
|
console.dir(themeMedia);
|
||||||
|
if (themeMedia.ThemeVideos && themeMedia.ThemeVideos.length > 0) {
|
||||||
|
console.log(`%c✅ ThemeVideos gefunden: ${themeMedia.ThemeVideos.length}`, "color: green; font-weight: bold;");
|
||||||
|
themeMedia.ThemeVideos.forEach(v => console.log(` - ID: ${v.Id}, Name: ${v.Name}, Path: ${v.Path}`));
|
||||||
|
} else {
|
||||||
|
console.log("❌ Keine ThemeVideos in ThemeMedia gefunden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// TEST 3: ThemeVideos Endpoint (Spezifisch)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
console.group("3. Endpoint: /Items/{Id}/ThemeVideos");
|
||||||
|
// Manchmal auch unter Users/{UserId}/Items/{Id}/ThemeVideos
|
||||||
|
const themeVideosUrl = `${apiClient.serverAddress()}/Items/${itemId}/ThemeVideos?userId=${userId}`;
|
||||||
|
const themeVideos = await fetchJson(themeVideosUrl);
|
||||||
|
|
||||||
|
if (themeVideos) {
|
||||||
|
// Kann Array oder Objekt mit Items sein
|
||||||
|
const videos = Array.isArray(themeVideos) ? themeVideos : (themeVideos.Items || []);
|
||||||
|
console.dir(videos);
|
||||||
|
|
||||||
|
if (videos.length > 0) {
|
||||||
|
console.log(`%c✅ ThemeVideos Endpoint lieferte Ergebnisse: ${videos.length}`, "color: green; font-weight: bold;");
|
||||||
|
} else {
|
||||||
|
console.log("❌ ThemeVideos Endpoint lieferte leeres Array.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// TEST 4: LocalTrailers Endpoint
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
console.group("4. Endpoint: /Items/{Id}/LocalTrailers");
|
||||||
|
const localTrailersUrl = `${apiClient.serverAddress()}/Users/${userId}/Items/${itemId}/LocalTrailers`;
|
||||||
|
const localTrailers = await fetchJson(localTrailersUrl);
|
||||||
|
|
||||||
|
if (localTrailers && localTrailers.length > 0) {
|
||||||
|
console.log(`%cℹ️ LocalTrailers gefunden: ${localTrailers.length}`, "color: blue;");
|
||||||
|
console.dir(localTrailers);
|
||||||
|
} else {
|
||||||
|
console.log("❌ Keine LocalTrailers gefunden.");
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
|
||||||
|
console.log("%cFertig.", "font-weight: bold;");
|
||||||
|
|
||||||
|
})();
|
||||||
32
test_scripts/fetch_random_item_browser_console.js
Normal file
32
test_scripts/fetch_random_item_browser_console.js
Normal 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); }
|
||||||
|
})();
|
||||||
32
test_scripts/fetch_random_item_browser_console_2.js
Normal file
32
test_scripts/fetch_random_item_browser_console_2.js
Normal 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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
28
test_scripts/fetch_specific_items.js
Normal file
28
test_scripts/fetch_specific_items.js
Normal 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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
37
test_scripts/fetch_specific_items_with_fields.js
Normal file
37
test_scripts/fetch_specific_items_with_fields.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
(async () => {
|
||||||
|
const apiClient = window.ApiClient;
|
||||||
|
if (!apiClient) {
|
||||||
|
console.error("ApiClient nicht gefunden.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemId = "DEINE_ITEM_ID_HIER";
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
|
||||||
|
const fields = "Overview,RemoteTrailers,Genres,CommunityRating,CriticRating,OfficialRating,PremiereDate,ProductionYear,MediaSources,RunTimeTicks,LocalTrailerCount,ThemeVideoIds";
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Rufe erweiterte Details für Item ${itemId} ab...`);
|
||||||
|
|
||||||
|
const url = apiClient.getUrl(`Users/${userId}/Items/${itemId}`, {
|
||||||
|
Fields: fields
|
||||||
|
});
|
||||||
|
|
||||||
|
const item = await apiClient.getJSON(url);
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
console.log(`%cErgebnis für: ${item.Name}`, "color: #00a4dc; font-weight: bold;");
|
||||||
|
|
||||||
|
console.log("Remote Trailer:", item.RemoteTrailers);
|
||||||
|
console.log("Local Trailer Count:", item.LocalTrailerCount);
|
||||||
|
console.log("Media Sources:", item.MediaSources);
|
||||||
|
console.log("ThemeVideos:", item.ThemeVideoIds);
|
||||||
|
|
||||||
|
console.dir(item);
|
||||||
|
} else {
|
||||||
|
console.warn("Item konnte nicht gefunden werden.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Abrufen des Items:", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
73
test_scripts/test_direct_date_created.js
Normal file
73
test_scripts/test_direct_date_created.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
(async () => {
|
||||||
|
const apiClient = window.ApiClient;
|
||||||
|
if (!apiClient) {
|
||||||
|
console.error("ApiClient not found. Are you logged in?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
const serverAddress = apiClient.serverAddress();
|
||||||
|
|
||||||
|
const maxDaysRecent = 30; // 30 Tage Limit
|
||||||
|
const pastDate = new Date();
|
||||||
|
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
|
||||||
|
const dateStr = pastDate.toISOString();
|
||||||
|
|
||||||
|
console.log(`\n%c=== TEST: DateCreated Direkt-Abfrage ===`, "color: #00a4dc; font-weight: bold; font-size: 14px;");
|
||||||
|
console.log(`Wir suchen Filme, die nach dem ${dateStr} hinzugefügt wurden.\n`);
|
||||||
|
|
||||||
|
// Wir probieren alle denkbaren Parameter-Schreibweisen aus,
|
||||||
|
// die Jellyfin historisch oder in Forks für "DateCreated" akzeptieren könnte.
|
||||||
|
const testCases = [
|
||||||
|
{ name: "MinDateCreated (PascalCase)", param: `MinDateCreated=${dateStr}` },
|
||||||
|
{ name: "minDateCreated (camelCase)", param: `minDateCreated=${dateStr}` },
|
||||||
|
{ name: "DateCreatedMin", param: `DateCreatedMin=${dateStr}` },
|
||||||
|
{ name: "dateCreatedMin", param: `dateCreatedMin=${dateStr}` },
|
||||||
|
{ name: "StartDate", param: `StartDate=${dateStr}` },
|
||||||
|
{ name: "startDate", param: `startDate=${dateStr}` }
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < testCases.length; i++) {
|
||||||
|
const test = testCases[i];
|
||||||
|
const apiUrl = `${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=5&fields=Id,DateCreated&userId=${userId}&${test.param}`;
|
||||||
|
|
||||||
|
console.log(`%cTest ${i+1}: ${test.name}`, "color: yellow;");
|
||||||
|
console.log(`URL: ${apiUrl}`);
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`-> ❌ HTTP Fehler: ${response.status}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const items = data.Items || [];
|
||||||
|
|
||||||
|
// Zur Auswertung: Wenn die Abfrage ignoriert wird, liefert er oft ALLE (hier max 5) zurück.
|
||||||
|
// Filtert er wirklich, sollten es WENIGER als 5, und zwar im besten Fall genau die RICHTIGEN sein.
|
||||||
|
console.log(`-> Ergebnis: ${items.length} Items zurückgeliefert.`);
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
// Wir checken, ob die zurückgelieferten Items WIRKLICH neu sind
|
||||||
|
const oldItems = items.filter(item => new Date(item.DateCreated) < pastDate);
|
||||||
|
if (oldItems.length > 0) {
|
||||||
|
console.log(` ❌ Filter FEHLGESCHLAGEN: Es wurden ${oldItems.length} "alte" Filme zurückgegeben (z.B. ${oldItems[0].Name}). Er ignoriert also den Parameter.`);
|
||||||
|
} else {
|
||||||
|
console.log(` ✅ Filter KÖNNTE funktionieren: Alle zurückgegebenen Filme sind neuer als unser Datum! (Erster: ${items[0].Name})`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(` ❓ Keine Items gefunden (entweder strenger Filter oder gar keine neuen Filme vorhanden).`);
|
||||||
|
}
|
||||||
|
console.log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Abruf:", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
56
test_scripts/test_filters_dates.js
Normal file
56
test_scripts/test_filters_dates.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
(async () => {
|
||||||
|
const apiClient = window.ApiClient;
|
||||||
|
if (!apiClient) {
|
||||||
|
console.error("ApiClient not found. Are you logged in?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
const serverAddress = apiClient.serverAddress();
|
||||||
|
|
||||||
|
const maxDaysRecent = 30; // Test: Added in last 30 days
|
||||||
|
const pastDate = new Date();
|
||||||
|
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
|
||||||
|
const dateStr = pastDate.toISOString();
|
||||||
|
|
||||||
|
console.log(`Searching for items added after: ${dateStr}`);
|
||||||
|
|
||||||
|
const testUrls = [
|
||||||
|
// Test 1: minDateCreated (CamelCase)
|
||||||
|
`${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=5&fields=Id,DateCreated&userId=${userId}&minDateCreated=${dateStr}`,
|
||||||
|
// Test 2: minDateLastSaved
|
||||||
|
`${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=5&fields=Id,DateCreated&userId=${userId}&minDateLastSaved=${dateStr}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < testUrls.length; i++) {
|
||||||
|
const apiUrl = testUrls[i];
|
||||||
|
console.log(`\n%cTest ${i+1}: Testing URL:\n${apiUrl}`, "color: yellow;");
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Failed to fetch items: ${response.status} ${response.statusText}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const items = data.Items || [];
|
||||||
|
|
||||||
|
console.log(`%cErgebnis: ${items.length} Items gefunden!`, "color: #00a4dc; font-weight: bold;");
|
||||||
|
|
||||||
|
if(items.length > 0) {
|
||||||
|
console.log("Gefundene Items:");
|
||||||
|
items.forEach(item => {
|
||||||
|
console.log(`- Name: ${item.Name}, DateCreated: ${item.DateCreated}, Art: ${item.Type}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Abrufen der URL:", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
72
test_scripts/test_filters_dates_final.js
Normal file
72
test_scripts/test_filters_dates_final.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
(async () => {
|
||||||
|
const apiClient = window.ApiClient;
|
||||||
|
if (!apiClient) {
|
||||||
|
console.error("ApiClient not found. Are you logged in?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
const serverAddress = apiClient.serverAddress();
|
||||||
|
|
||||||
|
const maxDaysRecent = 30; // Test: Added in last 30 days
|
||||||
|
|
||||||
|
// 1. Calculate the cutoff date
|
||||||
|
const pastDate = new Date();
|
||||||
|
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
|
||||||
|
const dateStr = pastDate.toISOString();
|
||||||
|
|
||||||
|
console.log(`\n%c=== TEST: 2-Stufen "Zuletzt Hinzugefügt" Filter ===`, "color: #00a4dc; font-weight: bold; font-size: 14px;");
|
||||||
|
console.log(`Suche Items neuer als: ${dateStr} (${maxDaysRecent} Tage alt)\n`);
|
||||||
|
|
||||||
|
const apiUrl = `${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&enableUserData=true&Limit=50&fields=Id,DateCreated&userId=${userId}&minDateLastSaved=${dateStr}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`%cSchritt 1: API Call mit minDateLastSaved...`, "color: yellow;");
|
||||||
|
console.log(`URL: ${apiUrl}`);
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch items: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
let items = data.Items || [];
|
||||||
|
|
||||||
|
console.log(`-> API lieferte ${items.length} potenziell neue/geänderte Items zurück.\n`);
|
||||||
|
if (items.length > 0) {
|
||||||
|
console.log("Die API hielt diese Items für neu/aktuell:");
|
||||||
|
items.forEach(item => console.log(` - ${item.Name} (DateCreated: ${item.DateCreated})`));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n%cSchritt 2: Lokaler DateCreated Filter (Wie das Plugin ihn jetzt nutzt)...`, "color: yellow;");
|
||||||
|
|
||||||
|
// Exakt dieser Filter-Block ist jetzt auch so in mediaBarEnhanced.js
|
||||||
|
const finalItems = items.filter(item => {
|
||||||
|
if (!item.DateCreated) return true; // Fallback falls Jellyfin kein Datum schickt
|
||||||
|
const dCreated = new Date(item.DateCreated);
|
||||||
|
return dCreated >= pastDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`%c-> FINALES ERGEBNIS: ${finalItems.length} echte Neuzugänge bleiben übrig!`, "color: #00ff00; font-weight: bold; font-size: 14px;");
|
||||||
|
|
||||||
|
if(finalItems.length > 0) {
|
||||||
|
console.log("Diese Items schaffen es in die Slideshow:");
|
||||||
|
finalItems.forEach(item => {
|
||||||
|
console.log(` 🎬 Name: ${item.Name}, DateCreated: ${item.DateCreated}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teste den Fallback
|
||||||
|
if(finalItems.length === 0 && items.length > 0) {
|
||||||
|
console.log("\n%c💡 HINWEIS: Da nach Filterung 0 Items übrig bleiben, greift in der Slideshow jetzt automatisch unser Fallback und zeigt zufällige Filme aller Jahre!", "color: orange;");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Abruf:", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
62
test_scripts/test_filters_url.js
Normal file
62
test_scripts/test_filters_url.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
(async () => {
|
||||||
|
const apiClient = window.ApiClient;
|
||||||
|
if (!apiClient) {
|
||||||
|
console.error("ApiClient not found. Are you logged in?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
const serverAddress = apiClient.serverAddress();
|
||||||
|
|
||||||
|
// Example test configuration flags
|
||||||
|
const maxItems = 50;
|
||||||
|
const includeWatchedContent = false; // set to false to ONLY show unplayed (newly watched)
|
||||||
|
const maxParentalRating = 12; // Test age limit
|
||||||
|
const maxDaysRecent = 30; // Test recency limit
|
||||||
|
|
||||||
|
// Build the query parameters just like in mediaBarEnhanced.js
|
||||||
|
const sortParams = "sortBy=Random";
|
||||||
|
const playedFilter = includeWatchedContent ? '' : '&isPlayed=False';
|
||||||
|
|
||||||
|
let parentalFilter = '';
|
||||||
|
if (maxParentalRating) {
|
||||||
|
parentalFilter = `&MaxOfficialRating=${maxParentalRating}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dateFilter = '';
|
||||||
|
if (maxDaysRecent) {
|
||||||
|
const pastDate = new Date();
|
||||||
|
pastDate.setDate(pastDate.getDate() - maxDaysRecent);
|
||||||
|
dateFilter = `&MinDateCreated=${pastDate.toISOString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl = `${serverAddress}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&${sortParams}${playedFilter}${parentalFilter}${dateFilter}&enableUserData=true&Limit=${maxItems}&fields=Id&userId=${userId}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Testing generated URL with filters:\n%c${apiUrl}`, "color: yellow;");
|
||||||
|
|
||||||
|
// Execute the fetch
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `MediaBrowser Client="${apiClient.appName()}", Device="${apiClient.deviceName()}", DeviceId="${apiClient.deviceId()}", Version="${apiClient.appVersion()}", Token="${apiClient.accessToken()}"`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch items: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const items = data.Items || [];
|
||||||
|
|
||||||
|
console.log(`%cErgebnis: ${items.length} Items gefunden!`, "color: #00a4dc; font-weight: bold;");
|
||||||
|
|
||||||
|
if(items.length > 0) {
|
||||||
|
console.log("Erstes Item als Beispiel:");
|
||||||
|
console.dir(items[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Abrufen der URL:", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user