Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8327fc5c9 | ||
|
|
9ffe03f0df | ||
|
|
37e99d7fed | ||
|
|
9b0e3762ac | ||
|
|
48f93e3480 | ||
|
|
2e9c093cdc | ||
|
|
60593dc855 | ||
|
|
0edde43720 | ||
|
|
0f6938a91d | ||
|
|
fe661925e0 | ||
|
|
5fbe60c27a | ||
|
|
89bde9233d | ||
|
|
24ac119e01 | ||
|
|
1ee4aaefb5 | ||
|
|
d737bc9422 | ||
|
|
2a154aaf92 | ||
|
|
561a4254b2 | ||
|
|
b8a0c7f589 | ||
|
|
10e02eeb3c | ||
|
|
f39200544d | ||
|
|
5173f66449 | ||
|
|
965942f63b | ||
|
|
c251cf7e70 | ||
|
|
8699e0b3e2 | ||
|
|
5db2157232 | ||
|
|
822f572006 | ||
|
|
e6637b34f7 | ||
|
|
1acaff6552 | ||
|
|
58188ca094 | ||
|
|
37b30aef1a | ||
|
|
f0370ac57f | ||
|
|
0663b7d9e4 | ||
|
|
e3213f72cc | ||
|
|
6c131aef58 | ||
|
|
0747f63d11 | ||
|
|
12f6f23314 | ||
|
|
a27488366b | ||
|
|
dd4c71c3bf | ||
|
|
715cd1a663 | ||
|
|
9371ed9a33 | ||
|
|
c21b2d3ede | ||
|
|
8ac0b9c003 | ||
|
|
0a0080c889 | ||
|
|
b2dd3e4d21 | ||
|
|
e4dac0d85c | ||
|
|
52bc8022fc | ||
|
|
fb4416f4b3 | ||
|
|
a67c4ecbe7 | ||
|
|
972c914dab | ||
|
|
60825b7e2f | ||
|
|
5d23594e08 | ||
|
|
2cccb139bf | ||
|
|
f37b946c0c | ||
|
|
51c3eec58a | ||
|
|
236def9fed | ||
|
|
f864b1105e | ||
|
|
353db1eab1 |
@@ -40,6 +40,11 @@ jobs:
|
|||||||
|
|
||||||
echo "Detected Version: $VERSION"
|
echo "Detected Version: $VERSION"
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "TARGET_ABI=$TARGET_ABI" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Also export GUID for later use
|
||||||
|
PLUGIN_GUID=$(jq -r '.[0].guid' manifest.json)
|
||||||
|
echo "PLUGIN_GUID=$PLUGIN_GUID" >> $GITHUB_ENV
|
||||||
|
|
||||||
# Escape newlines in changelog for GITHUB_ENV
|
# Escape newlines in changelog for GITHUB_ENV
|
||||||
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
|
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
|
||||||
@@ -71,7 +76,7 @@ jobs:
|
|||||||
REPO_OWNER="${{ github.repository_owner }}"
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
REPO_NAME="${{ github.event.repository.name }}"
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
VERSION="${{ env.VERSION }}"
|
VERSION="${{ env.VERSION }}"
|
||||||
DOWNLOAD_URL="https://git.mahom03-spacecloud.de/${{ github.repository }}/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"
|
||||||
|
|
||||||
echo "Updating manifest.json with:"
|
echo "Updating manifest.json with:"
|
||||||
echo "Hash: ${{ env.ZIP_HASH }}"
|
echo "Hash: ${{ env.ZIP_HASH }}"
|
||||||
@@ -101,3 +106,159 @@ jobs:
|
|||||||
tag_name: "v${{ env.VERSION }}"
|
tag_name: "v${{ env.VERSION }}"
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
|
# Update Message in Remote Repository
|
||||||
|
- name: Checkout Central Manifest Repo
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
||||||
|
path: central-manifest
|
||||||
|
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
||||||
|
|
||||||
|
- name: Update Central Manifest
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd central-manifest
|
||||||
|
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
|
||||||
|
# 1. Get info from previous steps
|
||||||
|
VERSION="${{ env.VERSION }}"
|
||||||
|
HASH="${{ env.ZIP_HASH }}"
|
||||||
|
TIME="${{ env.BUILD_TIME }}"
|
||||||
|
DOWNLOAD_URL="https://git.mahom03-spacecloud.de/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
|
||||||
|
|
||||||
|
# 2. Get info from env
|
||||||
|
PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
|
||||||
|
CHANGELOG="${{ env.CHANGELOG }}"
|
||||||
|
TARGET_ABI="${{ env.TARGET_ABI }}"
|
||||||
|
|
||||||
|
echo "Updating Central Manifest for Plugin GUID: $PLUGIN_GUID"
|
||||||
|
|
||||||
|
# 3. Update/Prepend entry in central manifest.json
|
||||||
|
# Logic:
|
||||||
|
# - If array is empty or new version != old version: PREPEND new entry
|
||||||
|
# - If new version == old version: OVERWRITE (update) existing entry (re-release)
|
||||||
|
|
||||||
|
jq --arg guid "$PLUGIN_GUID" \
|
||||||
|
--arg hash "$HASH" \
|
||||||
|
--arg time "$TIME" \
|
||||||
|
--arg url "$DOWNLOAD_URL" \
|
||||||
|
--arg ver "$VERSION" \
|
||||||
|
--arg changelog "$CHANGELOG" \
|
||||||
|
--arg abi "$TARGET_ABI" \
|
||||||
|
'map(if .guid == $guid then
|
||||||
|
if .versions[0].version != $ver then
|
||||||
|
# New Version -> Prepend
|
||||||
|
.versions = [{
|
||||||
|
"version": $ver,
|
||||||
|
"changelog": $changelog,
|
||||||
|
"targetAbi": $abi,
|
||||||
|
"sourceUrl": $url,
|
||||||
|
"checksum": $hash,
|
||||||
|
"timestamp": $time
|
||||||
|
}] + .versions
|
||||||
|
else
|
||||||
|
# Same Version -> Update existing (overwrite top)
|
||||||
|
.versions[0].changelog = $changelog |
|
||||||
|
.versions[0].targetAbi = $abi |
|
||||||
|
.versions[0].sourceUrl = $url |
|
||||||
|
.versions[0].checksum = $hash |
|
||||||
|
.versions[0].timestamp = $time
|
||||||
|
end
|
||||||
|
else . end)' \
|
||||||
|
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
||||||
|
|
||||||
|
- name: Commit and Push Central Manifest
|
||||||
|
run: |
|
||||||
|
cd central-manifest
|
||||||
|
git config user.name "CodeDevMLH"
|
||||||
|
git config user.email "145071728+CodeDevMLH@users.noreply.github.com"
|
||||||
|
|
||||||
|
# Check if there are changes
|
||||||
|
if [[ -n $(git status -s) ]]; then
|
||||||
|
git add manifest.json
|
||||||
|
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}"
|
||||||
|
git push
|
||||||
|
else
|
||||||
|
echo "No changes to central manifest."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update Message in Seasonals Repository
|
||||||
|
- name: Checkout Seasonal Manifest Repo
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository_owner }}/Jellyfin-Seasonals-Plugin
|
||||||
|
path: seasonal-manifest
|
||||||
|
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
||||||
|
|
||||||
|
- name: Update Seasonal Manifest
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd seasonal-manifest
|
||||||
|
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
|
||||||
|
# 1. Get info from previous steps
|
||||||
|
VERSION="${{ env.VERSION }}"
|
||||||
|
HASH="${{ env.ZIP_HASH }}"
|
||||||
|
TIME="${{ env.BUILD_TIME }}"
|
||||||
|
DOWNLOAD_URL="https://git.mahom03-spacecloud.de/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
|
||||||
|
|
||||||
|
# 2. Get info from env
|
||||||
|
PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
|
||||||
|
CHANGELOG="${{ env.CHANGELOG }}"
|
||||||
|
TARGET_ABI="${{ env.TARGET_ABI }}"
|
||||||
|
|
||||||
|
echo "Updating Seasonal Manifest for Plugin GUID: $PLUGIN_GUID"
|
||||||
|
|
||||||
|
# 3. Update/Prepend entry in seasonal manifest.json
|
||||||
|
# Logic:
|
||||||
|
# - If array is empty or new version != old version: PREPEND new entry
|
||||||
|
# - If new version == old version: OVERWRITE (update) existing entry (re-release)
|
||||||
|
|
||||||
|
jq --arg guid "$PLUGIN_GUID" \
|
||||||
|
--arg hash "$HASH" \
|
||||||
|
--arg time "$TIME" \
|
||||||
|
--arg url "$DOWNLOAD_URL" \
|
||||||
|
--arg ver "$VERSION" \
|
||||||
|
--arg changelog "$CHANGELOG" \
|
||||||
|
--arg abi "$TARGET_ABI" \
|
||||||
|
'map(if .guid == $guid then
|
||||||
|
if .versions[0].version != $ver then
|
||||||
|
# New Version -> Prepend
|
||||||
|
.versions = [{
|
||||||
|
"version": $ver,
|
||||||
|
"changelog": $changelog,
|
||||||
|
"targetAbi": $abi,
|
||||||
|
"sourceUrl": $url,
|
||||||
|
"checksum": $hash,
|
||||||
|
"timestamp": $time
|
||||||
|
}] + .versions
|
||||||
|
else
|
||||||
|
# Same Version -> Update existing (overwrite top)
|
||||||
|
.versions[0].changelog = $changelog |
|
||||||
|
.versions[0].targetAbi = $abi |
|
||||||
|
.versions[0].sourceUrl = $url |
|
||||||
|
.versions[0].checksum = $hash |
|
||||||
|
.versions[0].timestamp = $time
|
||||||
|
end
|
||||||
|
else . end)' \
|
||||||
|
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
||||||
|
|
||||||
|
- name: Commit and Push Seasonal Manifest
|
||||||
|
run: |
|
||||||
|
cd seasonal-manifest
|
||||||
|
git config user.name "CodeDevMLH"
|
||||||
|
git config user.email "145071728+CodeDevMLH@users.noreply.github.com"
|
||||||
|
|
||||||
|
# Check if there are changes
|
||||||
|
if [[ -n $(git status -s) ]]; then
|
||||||
|
git add manifest.json
|
||||||
|
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}"
|
||||||
|
git push
|
||||||
|
else
|
||||||
|
echo "No changes to seasonal manifest."
|
||||||
|
fi
|
||||||
163
.github/workflows/release_automation.yml
vendored
163
.github/workflows/release_automation.yml
vendored
@@ -39,6 +39,11 @@ jobs:
|
|||||||
|
|
||||||
echo "Detected Version: $VERSION"
|
echo "Detected Version: $VERSION"
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "TARGET_ABI=$TARGET_ABI" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Also export GUID for later use
|
||||||
|
PLUGIN_GUID=$(jq -r '.[0].guid' manifest.json)
|
||||||
|
echo "PLUGIN_GUID=$PLUGIN_GUID" >> $GITHUB_ENV
|
||||||
|
|
||||||
# Escape newlines in changelog for GITHUB_ENV
|
# Escape newlines in changelog for GITHUB_ENV
|
||||||
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
|
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
|
||||||
@@ -94,8 +99,164 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tag_name: "v${{ env.VERSION }}"
|
tag_name: "v${{ env.VERSION }}"
|
||||||
name: "v${{ env.VERSION }}"
|
name: "v${{ env.VERSION }}"
|
||||||
# body: ${{ env.CHANGELOG }}
|
body: ${{ env.CHANGELOG }}
|
||||||
files: ${{ env.ZIP_PATH }}
|
files: ${{ env.ZIP_PATH }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|
||||||
|
# Update Message in Remote Repository
|
||||||
|
- name: Checkout Central Manifest Repo
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
||||||
|
path: central-manifest
|
||||||
|
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
||||||
|
|
||||||
|
- name: Update Central Manifest
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd central-manifest
|
||||||
|
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
|
||||||
|
# 1. Get info from previous steps
|
||||||
|
VERSION="${{ env.VERSION }}"
|
||||||
|
HASH="${{ env.ZIP_HASH }}"
|
||||||
|
TIME="${{ env.BUILD_TIME }}"
|
||||||
|
DOWNLOAD_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
|
||||||
|
|
||||||
|
# 2. Get info from env
|
||||||
|
PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
|
||||||
|
CHANGELOG="${{ env.CHANGELOG }}"
|
||||||
|
TARGET_ABI="${{ env.TARGET_ABI }}"
|
||||||
|
|
||||||
|
echo "Updating Central Manifest for Plugin GUID: $PLUGIN_GUID"
|
||||||
|
|
||||||
|
# 3. Update/Prepend entry in central manifest.json
|
||||||
|
# Logic:
|
||||||
|
# - If array is empty or new version != old version: PREPEND new entry
|
||||||
|
# - If new version == old version: OVERWRITE (update) existing entry (re-release)
|
||||||
|
|
||||||
|
jq --arg guid "$PLUGIN_GUID" \
|
||||||
|
--arg hash "$HASH" \
|
||||||
|
--arg time "$TIME" \
|
||||||
|
--arg url "$DOWNLOAD_URL" \
|
||||||
|
--arg ver "$VERSION" \
|
||||||
|
--arg changelog "$CHANGELOG" \
|
||||||
|
--arg abi "$TARGET_ABI" \
|
||||||
|
'map(if .guid == $guid then
|
||||||
|
if .versions[0].version != $ver then
|
||||||
|
# New Version -> Prepend
|
||||||
|
.versions = [{
|
||||||
|
"version": $ver,
|
||||||
|
"changelog": $changelog,
|
||||||
|
"targetAbi": $abi,
|
||||||
|
"sourceUrl": $url,
|
||||||
|
"checksum": $hash,
|
||||||
|
"timestamp": $time
|
||||||
|
}] + .versions
|
||||||
|
else
|
||||||
|
# Same Version -> Update existing (overwrite top)
|
||||||
|
.versions[0].changelog = $changelog |
|
||||||
|
.versions[0].targetAbi = $abi |
|
||||||
|
.versions[0].sourceUrl = $url |
|
||||||
|
.versions[0].checksum = $hash |
|
||||||
|
.versions[0].timestamp = $time
|
||||||
|
end
|
||||||
|
else . end)' \
|
||||||
|
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
||||||
|
|
||||||
|
- name: Commit and Push Central Manifest
|
||||||
|
run: |
|
||||||
|
cd central-manifest
|
||||||
|
git config user.name "CodeDevMLH"
|
||||||
|
git config user.email "145071728+CodeDevMLH@users.noreply.github.com"
|
||||||
|
|
||||||
|
# Check if there are changes
|
||||||
|
if [[ -n $(git status -s) ]]; then
|
||||||
|
git add manifest.json
|
||||||
|
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}"
|
||||||
|
git push
|
||||||
|
else
|
||||||
|
echo "No changes to central manifest."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update Message in Seasonals Repository
|
||||||
|
- name: Checkout Seasonal Manifest Repo
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository_owner }}/Jellyfin-Seasonals
|
||||||
|
path: seasonal-manifest
|
||||||
|
token: ${{ secrets.JELLYFIN_PLUGIN_MANIFEST_UPDATER_PAT }}
|
||||||
|
|
||||||
|
- name: Update Seasonal Manifest
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd seasonal-manifest
|
||||||
|
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
|
||||||
|
# 1. Get info from previous steps
|
||||||
|
VERSION="${{ env.VERSION }}"
|
||||||
|
HASH="${{ env.ZIP_HASH }}"
|
||||||
|
TIME="${{ env.BUILD_TIME }}"
|
||||||
|
DOWNLOAD_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
|
||||||
|
|
||||||
|
# 2. Get info from env
|
||||||
|
PLUGIN_GUID="${{ env.PLUGIN_GUID }}"
|
||||||
|
CHANGELOG="${{ env.CHANGELOG }}"
|
||||||
|
TARGET_ABI="${{ env.TARGET_ABI }}"
|
||||||
|
|
||||||
|
echo "Updating Seasonal Manifest for Plugin GUID: $PLUGIN_GUID"
|
||||||
|
|
||||||
|
# 3. Update/Prepend entry in seasonal manifest.json
|
||||||
|
# Logic:
|
||||||
|
# - If array is empty or new version != old version: PREPEND new entry
|
||||||
|
# - If new version == old version: OVERWRITE (update) existing entry (re-release)
|
||||||
|
|
||||||
|
jq --arg guid "$PLUGIN_GUID" \
|
||||||
|
--arg hash "$HASH" \
|
||||||
|
--arg time "$TIME" \
|
||||||
|
--arg url "$DOWNLOAD_URL" \
|
||||||
|
--arg ver "$VERSION" \
|
||||||
|
--arg changelog "$CHANGELOG" \
|
||||||
|
--arg abi "$TARGET_ABI" \
|
||||||
|
'map(if .guid == $guid then
|
||||||
|
if .versions[0].version != $ver then
|
||||||
|
# New Version -> Prepend
|
||||||
|
.versions = [{
|
||||||
|
"version": $ver,
|
||||||
|
"changelog": $changelog,
|
||||||
|
"targetAbi": $abi,
|
||||||
|
"sourceUrl": $url,
|
||||||
|
"checksum": $hash,
|
||||||
|
"timestamp": $time
|
||||||
|
}] + .versions
|
||||||
|
else
|
||||||
|
# Same Version -> Update existing (overwrite top)
|
||||||
|
.versions[0].changelog = $changelog |
|
||||||
|
.versions[0].targetAbi = $abi |
|
||||||
|
.versions[0].sourceUrl = $url |
|
||||||
|
.versions[0].checksum = $hash |
|
||||||
|
.versions[0].timestamp = $time
|
||||||
|
end
|
||||||
|
else . end)' \
|
||||||
|
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
||||||
|
|
||||||
|
- name: Commit and Push Seasonal Manifest
|
||||||
|
run: |
|
||||||
|
cd seasonal-manifest
|
||||||
|
git config user.name "CodeDevMLH"
|
||||||
|
git config user.email "145071728+CodeDevMLH@users.noreply.github.com"
|
||||||
|
|
||||||
|
# Check if there are changes
|
||||||
|
if [[ -n $(git status -s) ]]; then
|
||||||
|
git add manifest.json
|
||||||
|
git commit -m "Auto-Update MediaBar Enhanced to v${{ env.VERSION }}"
|
||||||
|
git push
|
||||||
|
else
|
||||||
|
echo "No changes to seasonal manifest."
|
||||||
|
fi
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,7 +4,5 @@ obj/
|
|||||||
.idea/
|
.idea/
|
||||||
artifacts
|
artifacts
|
||||||
|
|
||||||
example-plugins/
|
|
||||||
|
|
||||||
*.md
|
*.md
|
||||||
!README.md
|
!README.md
|
||||||
@@ -30,7 +30,8 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Configuration
|
|||||||
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 string CustomMediaIds { get; set; } = "";
|
public string CustomMediaIds { get; set; } = "";
|
||||||
public bool EnableCustomMediaIds { get; set; } = false;
|
public bool EnableCustomMediaIds { get; set; } = true;
|
||||||
|
public string PreferredVideoQuality { get; set; } = "Auto";
|
||||||
public bool EnableSeasonalContent { get; set; } = false;
|
public bool EnableSeasonalContent { get; set; } = false;
|
||||||
public bool IsEnabled { get; set; } = true;
|
public bool IsEnabled { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="sectionTitleContainer">
|
<div class="sectionTitleContainer">
|
||||||
<h2 class="sectionTitle">Media Bar Enhanced</h2>
|
<h2 class="sectionTitle">Media Bar Enhanced</h2>
|
||||||
<a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;"
|
<a is="emby-linkbutton" class="raised raised-mini emby-button" style="margin-left: 2em;"
|
||||||
target="_blank" href="https://github.com/CodeDevMLH/Jellyfin-Seasonals">
|
target="_blank" href="https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced">
|
||||||
<i class="md-icon button-icon button-icon-left secondaryText"></i>
|
<i class="md-icon button-icon button-icon-left secondaryText"></i>
|
||||||
<span>Help</span>
|
<span>Help</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -93,8 +93,9 @@
|
|||||||
name="EnableCustomMediaIds" />
|
name="EnableCustomMediaIds" />
|
||||||
<span>Enable Custom Media IDs</span>
|
<span>Enable Custom Media IDs</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="fieldDescription">If enabled, the slideshow will ONLY show the items listed
|
<div class="fieldDescription">If enabled, the slideshow will try to show the items listed
|
||||||
below.</div>
|
below. If the list is empty, default behavior (random items) is used. Disable this
|
||||||
|
to temporarily ignore your custom list without deleting it.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
@@ -113,7 +114,10 @@
|
|||||||
style="width: 100%; height: 150px; font-family: monospace;"></textarea>
|
style="width: 100%; height: 150px; font-family: monospace;"></textarea>
|
||||||
<div class="fieldDescription" id="customMediaIdsDesc">Enter the IDs of the items you want to
|
<div class="fieldDescription" id="customMediaIdsDesc">Enter the IDs of the items you want to
|
||||||
show in the slideshow.
|
show in the slideshow.
|
||||||
You can separate them by comma or new line.</div>
|
You can separate them by comma or new line.
|
||||||
|
You can also add a description after the ID using any separator like space, pipe
|
||||||
|
(|) or dash (-) (e.g. <code>ID | Description</code>).
|
||||||
|
Note: The separator MUST NOT be a hex character (0-9, a-f).</div>
|
||||||
<div class="fieldDescription" id="seasonalMediaIdsDesc" style="display: none;">
|
<div class="fieldDescription" id="seasonalMediaIdsDesc" style="display: none;">
|
||||||
<b>Seasonal Mode Enabled:</b> Define lines with date ranges (Format: DD.MM-DD.MM |
|
<b>Seasonal Mode Enabled:</b> Define lines with date ranges (Format: DD.MM-DD.MM |
|
||||||
<i>name</i> | <i>IDs</i>).<br>
|
<i>name</i> | <i>IDs</i>).<br>
|
||||||
@@ -125,11 +129,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<p>You can find the IDs of your items in the URL of the item page in the web interface.<br>
|
<p>You can find the IDs of your items in the URL of the item page in the web interface.<br>
|
||||||
Example:
|
Example:
|
||||||
<code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br>
|
<code>https://your-jellyfin-url/web/#/details?id=<b style="color:red;">your-item-id</b>&serverId=your-server-id</code><br><br>
|
||||||
You can also insert a name of a collection or playlist to fetch the IDs of all items in
|
You can also insert a name of a collection or playlist to fetch the IDs of all items in
|
||||||
it (will take the first hit. Note: there is currently no feedback if the name resolution
|
it (will take the first hit.<br>Note: there is currently no feedback if the name
|
||||||
succeeded, you
|
resolution succeeded, you will have to look if the bar displays the correct items.).
|
||||||
will have to look if the bar displays the correct items.).
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,6 +156,18 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="fieldDescription">Skip intro/outro segments in YouTube trailers.</div>
|
<div class="fieldDescription">Skip intro/outro segments in YouTube trailers.</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="selectContainer">
|
||||||
|
<label class="selectLabel" for="PreferredVideoQuality">Preferred YouTube Quality</label>
|
||||||
|
<select is="emby-select" id="PreferredVideoQuality" name="PreferredVideoQuality"
|
||||||
|
class="emby-select-withcolor emby-select">
|
||||||
|
<option value="Auto">Auto (Smart)</option>
|
||||||
|
<option value="Maximum">Maximum (4K+)</option>
|
||||||
|
<option value="1080p">1080p</option>
|
||||||
|
<option value="720p">720p</option>
|
||||||
|
</select>
|
||||||
|
<div class="fieldDescription">"Auto" selects Maximum if screen width > 1920px, otherwise
|
||||||
|
1080p.</div>
|
||||||
|
</div>
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" id="StartMuted" name="StartMuted" />
|
<input is="emby-checkbox" type="checkbox" id="StartMuted" name="StartMuted" />
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
<!-- <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.0.0.0</Version>
|
<Version>1.2.0.0</Version>
|
||||||
<RepositoryUrl>https://git.mahom03-spacecloud.de/CodeDevMLH/Media-Bar-Plugin</RepositoryUrl>
|
<RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string Name => "Media Bar";
|
public override string Name => "Media Bar Enhanced";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Guid Id => Guid.Parse("d7e11d57-819b-4bdd-a88d-53c5f5560225");
|
public override Guid Id => Guid.Parse("d7e11d57-819b-4bdd-a88d-53c5f5560225");
|
||||||
|
|||||||
@@ -18,10 +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/slideshowpure.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/slideshowpure.css\" />";
|
public const string CssTag = "<link rel=\"stylesheet\" href=\"/MediaBarEnhanced/Resources/mediaBarEnhanced.css\" />";
|
||||||
// private const string ScriptTag = "<script src=\"/MediaBarEnhanced/Resources/media-bar.js\" defer></script>";
|
|
||||||
// private const string CssTag = "<link rel=\"stylesheet\" href=\"/MediaBarEnhanced/Resources/media-bar.css\">";
|
|
||||||
public const string ScriptMarker = "</body>";
|
public const string ScriptMarker = "</body>";
|
||||||
public const string CssMarker = "</head>";
|
public const string CssMarker = "</head>";
|
||||||
|
|
||||||
@@ -39,7 +37,6 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Injects the script tag into index.html if it's not already present.
|
/// Injects the script tag into index.html if it's not already present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if injection was successful or already present, false otherwise.</returns>
|
|
||||||
public void Inject()
|
public void Inject()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -103,6 +100,11 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
_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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Unauthorized access when attempting to inject script into index.html. Automatic injection failed. Attempting fallback now...");
|
||||||
|
RegisterFileTransformation();
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error injecting MediaBarEnhanced resources. Attempting fallback.");
|
_logger.LogError(ex, "Error injecting MediaBarEnhanced resources. Attempting fallback.");
|
||||||
@@ -150,8 +152,15 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
{
|
{
|
||||||
File.WriteAllText(indexPath, content);
|
File.WriteAllText(indexPath, content);
|
||||||
_logger.LogInformation("MediaBarEnhanced script removed from index.html.");
|
_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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error removing MediaBarEnhanced script.");
|
_logger.LogError(ex, "Error removing MediaBarEnhanced script.");
|
||||||
@@ -172,7 +181,6 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
|
|
||||||
{
|
{
|
||||||
JObject payload = new JObject();
|
JObject payload = new JObject();
|
||||||
// Random GUID for ID
|
|
||||||
payload.Add("id", "0dfac9d7-d898-4944-900b-1c1837707279");
|
payload.Add("id", "0dfac9d7-d898-4944-900b-1c1837707279");
|
||||||
payload.Add("fileNamePattern", "index.html");
|
payload.Add("fileNamePattern", "index.html");
|
||||||
payload.Add("callbackAssembly", GetType().Assembly.FullName);
|
payload.Add("callbackAssembly", GetType().Assembly.FullName);
|
||||||
@@ -223,7 +231,6 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
|
|
||||||
if (pluginInterfaceType != null)
|
if (pluginInterfaceType != null)
|
||||||
{
|
{
|
||||||
// The ID must match the one used in RegisterFileTransformation
|
|
||||||
Guid id = Guid.Parse("0dfac9d7-d898-4944-900b-1c1837707279");
|
Guid id = Guid.Parse("0dfac9d7-d898-4944-900b-1c1837707279");
|
||||||
pluginInterfaceType.GetMethod("RemoveTransformation")?.Invoke(null, new object?[] { id });
|
pluginInterfaceType.GetMethod("RemoveTransformation")?.Invoke(null, new object?[] { id });
|
||||||
_logger.LogInformation("File transformation unregistered successfully.");
|
_logger.LogInformation("File transformation unregistered successfully.");
|
||||||
@@ -232,7 +239,6 @@ namespace Jellyfin.Plugin.MediaBarEnhanced
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Log but don't throw, as we want to continue with normal removal
|
|
||||||
_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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,10 @@ const CONFIG = {
|
|||||||
fullWidthVideo: true,
|
fullWidthVideo: true,
|
||||||
enableMobileVideo: false,
|
enableMobileVideo: false,
|
||||||
showTrailerButton: true,
|
showTrailerButton: true,
|
||||||
|
preferredVideoQuality: "Auto",
|
||||||
enableKeyboardControls: true,
|
enableKeyboardControls: true,
|
||||||
alwaysShowArrows: false,
|
alwaysShowArrows: false,
|
||||||
enableCustomMediaIds: false,
|
enableCustomMediaIds: true,
|
||||||
enableSeasonalContent: false,
|
enableSeasonalContent: false,
|
||||||
customMediaIds: "",
|
customMediaIds: "",
|
||||||
enableLoadingScreen: true,
|
enableLoadingScreen: true,
|
||||||
@@ -1259,11 +1260,13 @@ const VisibilityObserver = {
|
|||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
if (STATE.slideshow.slideInterval && !STATE.slideshow.isPaused) {
|
if (STATE.slideshow.slideInterval && !STATE.slideshow.isPaused) {
|
||||||
STATE.slideshow.slideInterval.start();
|
STATE.slideshow.slideInterval.start();
|
||||||
|
SlideshowManager.resumeActivePlayback();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (STATE.slideshow.slideInterval) {
|
if (STATE.slideshow.slideInterval) {
|
||||||
STATE.slideshow.slideInterval.stop();
|
STATE.slideshow.slideInterval.stop();
|
||||||
}
|
}
|
||||||
|
SlideshowManager.stopAllPlayback();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1415,6 +1418,21 @@ const SlideCreator = {
|
|||||||
loop: 0
|
loop: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Determine video quality
|
||||||
|
let quality = 'hd1080';
|
||||||
|
if (CONFIG.preferredVideoQuality === 'Maximum') {
|
||||||
|
quality = 'highres';
|
||||||
|
} else if (CONFIG.preferredVideoQuality === '720p') {
|
||||||
|
quality = 'hd720';
|
||||||
|
} else if (CONFIG.preferredVideoQuality === '1080p') {
|
||||||
|
quality = 'hd1080';
|
||||||
|
} else { // Auto or fallback
|
||||||
|
// If screen is wider than 1920, prefer highres, otherwise 1080p
|
||||||
|
quality = window.screen.width > 1920 ? 'highres' : 'hd1080';
|
||||||
|
}
|
||||||
|
|
||||||
|
playerVars.suggestedQuality = quality;
|
||||||
|
|
||||||
// Apply SponsorBlock start/end times
|
// Apply SponsorBlock start/end times
|
||||||
if (segments.intro) {
|
if (segments.intro) {
|
||||||
playerVars.start = Math.ceil(segments.intro[1]);
|
playerVars.start = Math.ceil(segments.intro[1]);
|
||||||
@@ -1444,6 +1462,10 @@ const SlideCreator = {
|
|||||||
event.target.setVolume(40);
|
event.target.setVolume(40);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof event.target.setPlaybackQuality === 'function') {
|
||||||
|
event.target.setPlaybackQuality(quality);
|
||||||
|
}
|
||||||
|
|
||||||
// Only play if this is the active slide
|
// Only play if this is the active slide
|
||||||
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
|
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
|
||||||
if (slide && slide.classList.contains('active')) {
|
if (slide && slide.classList.contains('active')) {
|
||||||
@@ -1492,6 +1514,7 @@ const SlideCreator = {
|
|||||||
className: "backdrop video-backdrop",
|
className: "backdrop video-backdrop",
|
||||||
src: trailerUrl,
|
src: trailerUrl,
|
||||||
autoplay: false,
|
autoplay: false,
|
||||||
|
preload: "auto",
|
||||||
loop: false,
|
loop: false,
|
||||||
style: "object-fit: cover; width: 100%; height: 100%; pointer-events: none;"
|
style: "object-fit: cover; width: 100%; height: 100%; pointer-events: none;"
|
||||||
};
|
};
|
||||||
@@ -2317,6 +2340,65 @@ const SlideshowManager = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all video playback (YouTube and HTML5)
|
||||||
|
* Used when navigating away from the home screen
|
||||||
|
*/
|
||||||
|
stopAllPlayback() {
|
||||||
|
// 1. Pause all YouTube players
|
||||||
|
if (STATE.slideshow.videoPlayers) {
|
||||||
|
Object.values(STATE.slideshow.videoPlayers).forEach(player => {
|
||||||
|
try {
|
||||||
|
if (player && typeof player.pauseVideo === 'function') {
|
||||||
|
player.pauseVideo();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Error pausing YouTube player:", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Pause all HTML5 videos
|
||||||
|
const container = document.getElementById("slides-container");
|
||||||
|
if (container) {
|
||||||
|
container.querySelectorAll('video').forEach(video => {
|
||||||
|
try {
|
||||||
|
video.pause();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Error pausing HTML5 video:", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes playback for the active slide if not globally paused
|
||||||
|
*/
|
||||||
|
resumeActivePlayback() {
|
||||||
|
if (STATE.slideshow.isPaused) return;
|
||||||
|
|
||||||
|
const currentItemId = STATE.slideshow.itemIds[STATE.slideshow.currentSlideIndex];
|
||||||
|
if (!currentItemId) return;
|
||||||
|
|
||||||
|
const currentSlide = document.querySelector(`.slide[data-item-id="${currentItemId}"]`);
|
||||||
|
if (!currentSlide) return;
|
||||||
|
|
||||||
|
// 1. Try YouTube Player
|
||||||
|
const ytPlayer = STATE.slideshow.videoPlayers[currentItemId];
|
||||||
|
if (ytPlayer && typeof ytPlayer.playVideo === 'function') {
|
||||||
|
ytPlayer.playVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Try HTML5 Video
|
||||||
|
const html5Video = currentSlide.querySelector('video');
|
||||||
|
if (html5Video) {
|
||||||
|
if (STATE.slideshow.isMuted) {
|
||||||
|
html5Video.muted = true;
|
||||||
|
}
|
||||||
|
html5Video.play().catch(e => console.warn("Error resuming HTML5 video:", e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes touch events for swiping
|
* Initializes touch events for swiping
|
||||||
*/
|
*/
|
||||||
@@ -2514,8 +2596,13 @@ const SlideshowManager = {
|
|||||||
try {
|
try {
|
||||||
let id = rawId;
|
let id = rawId;
|
||||||
|
|
||||||
// If not a valid GUID, treat as a name and search
|
// If not a valid GUID, check if it starts with one (comments) or treat as a name
|
||||||
if (!guidRegex.test(rawId)) {
|
if (!guidRegex.test(rawId)) {
|
||||||
|
const guidMatch = rawId.match(/^([0-9a-f]{32})(?:[^0-9a-f]|$)/i);
|
||||||
|
|
||||||
|
if (guidMatch) {
|
||||||
|
id = guidMatch[1];
|
||||||
|
} else {
|
||||||
console.log(`Input '${rawId}' is not a GUID, searching for Collection/Playlist by name...`);
|
console.log(`Input '${rawId}' is not a GUID, searching for Collection/Playlist by name...`);
|
||||||
const resolvedId = await ApiUtils.findCollectionOrPlaylistByName(rawId);
|
const resolvedId = await ApiUtils.findCollectionOrPlaylistByName(rawId);
|
||||||
|
|
||||||
@@ -2527,6 +2614,7 @@ const SlideshowManager = {
|
|||||||
continue; // Skip if resolution failed
|
continue; // Skip if resolution failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const item = await ApiUtils.fetchItemDetails(id);
|
const item = await ApiUtils.fetchItemDetails(id);
|
||||||
if (item && (item.Type === 'BoxSet' || item.Type === 'Playlist')) {
|
if (item && (item.Type === 'BoxSet' || item.Type === 'Playlist')) {
|
||||||
@@ -2839,7 +2927,7 @@ const slidesInit = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.slideshowPure = {
|
window.mediaBarEnhanced = {
|
||||||
CONFIG,
|
CONFIG,
|
||||||
STATE,
|
STATE,
|
||||||
SlideUtils,
|
SlideUtils,
|
||||||
236
README.md
236
README.md
@@ -1 +1,235 @@
|
|||||||
# jellyfin-plugin-media-bar-enhanced
|
# Jellyfin Media Bar Enhanced Plugin
|
||||||
|
|
||||||
|
Media Bar Enhanced is a plugin for Jellyfin that introduces a customizable and interactive media bar to your dashboard view on Jellyfin web.
|
||||||
|
|
||||||
|
This plugin is a fork and enhancement of the original [Media Bar by MakD](https://github.com/MakD/Jellyfin-Media-Bar), but can be installed as plugin for easier installation and management/configuration.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Jellyfin Media Bar Enhanced Plugin](#jellyfin-media-bar-enhanced-plugin)
|
||||||
|
- [Table of Contents](#table-of-contents)
|
||||||
|
- [Overview](#overview)
|
||||||
|
- [Features](#features)
|
||||||
|
- [New Features \& Enhancements](#new-features--enhancements)
|
||||||
|
- [Core Features](#core-features)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Client Compatibility](#client-compatibility)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [General Settings](#general-settings)
|
||||||
|
- [Custom Content](#custom-content)
|
||||||
|
- [Advanced Settings](#advanced-settings)
|
||||||
|
- [Build The Plugin By Yourself](#build-the-plugin-by-yourself)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
- [Effects Not Showing](#effects-not-showing)
|
||||||
|
- [Docker Permission Issues](#docker-permission-issues)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|

|
||||||
|
|
||||||
|
Expand to get more impressions:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Desktop Layout</summary>
|
||||||
|
|
||||||
|
<img width="1920" height="1080" alt="trailer button" src="https://github.com/user-attachments/assets/5dce8eb1-8f2f-4583-a6d5-16f27ced8608" />
|
||||||
|
Normal mode like the original with additional trailer button
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<img width="1920" height="993" alt="modal_desktop" src="https://github.com/user-attachments/assets/9087f43d-cd9d-4581-a7e0-404b75bc8e02" />
|
||||||
|
Trailer modal
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<img width="1920" height="994" alt="config" src="https://github.com/user-attachments/assets/5492c384-a5c4-47ee-9428-3d9de2748e63" />
|
||||||
|
Excerpt from the config: E.g. here you can simply add your items that should be displayed
|
||||||
|
<br><br>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Mobile Layout</summary>
|
||||||
|
|
||||||
|

|
||||||
|
<br>If trailer on mobile is eenabled...
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<img width="1080" height="2199" alt="mobile" src="https://github.com/user-attachments/assets/f0a0cc0d-f019-45f5-96c8-a5de14bf92ba" />
|
||||||
|
Normal mode like the original with additional trailer button
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<img width="1080" height="2199" alt="trailer_modal_mobile" src="https://github.com/user-attachments/assets/944f9b82-9c9b-411f-883b-877b65ed933f" />
|
||||||
|
Trailer modal in portrait mode
|
||||||
|
<br><br>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
This plugin builds upon the original Media Bar with new capabilities and improvements:
|
||||||
|
|
||||||
|
### New Features & Enhancements
|
||||||
|
* **Video Backdrop Support**: Play trailer as background video directly in the slideshow
|
||||||
|
* **SponsorBlock Integration**: Automatically skip intro/outro segments in YouTube trailers
|
||||||
|
* **Enhanced Controls**:
|
||||||
|
* Keyboard shortcuts (Arrow keys to navigate, Space to pause, M to mute)
|
||||||
|
* Option to always show navigation arrows
|
||||||
|
* Standalone "Trailer" button (opens in a modal) if video backdrops are disabled
|
||||||
|
* **Smarter Playback**:
|
||||||
|
* Option to wait for the trailer to end before advancing the slide.
|
||||||
|
* Mute/Unmute controls
|
||||||
|
* **Customization**:
|
||||||
|
* **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)
|
||||||
|
* Pagination dots turn into a counter (e.g., 1/20) if the limit is exceeded
|
||||||
|
* Option to disable the loading screen
|
||||||
|
|
||||||
|
### Core Features
|
||||||
|
* **Immersive Slideshow**: Rotates through your media library
|
||||||
|
* **Metadata Display**: Shows title, rating, year, and plot summary
|
||||||
|
* **Direct Play**: Click "Play" to start watching immediately
|
||||||
|
* **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
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This plugin is based on Jellyfin Version `10.11.x`
|
||||||
|
|
||||||
|
1. Open your **Jellyfin Dashboard**.
|
||||||
|
2. Navigate to **Plugins** > **Repositories**.
|
||||||
|
3. Click the **+** button to add a new repository.
|
||||||
|
4. Enter a name for the repo and paste the following URL:
|
||||||
|
```
|
||||||
|
https://raw.githubusercontent.com/CodeDevMLH/jellyfin-plugin-manifest/refs/heads/main/manifest.json
|
||||||
|
```
|
||||||
|
5. Click **Save**.
|
||||||
|
6. Go to the **Catalog** tab.
|
||||||
|
7. Find **Media Bar Enhanced** (Under **General**) and install it.
|
||||||
|
8. **Restart your Jellyfin server.**
|
||||||
|
9. **Refresh your browser** (Ctrl+F5) to load the new interface elements.
|
||||||
|
|
||||||
|
## Client Compatibility
|
||||||
|
|
||||||
|
Because this plugin relies on injecting JavaScript and CSS into the web interface, it works best on clients that use the web wrapper.
|
||||||
|
|
||||||
|
| Client Platform | Status | Notes |
|
||||||
|
| :--- | :---: | :--- |
|
||||||
|
| **Web Browsers** (Chrome, Firefox, Edge, etc.) | ✅ | Fully supported. |
|
||||||
|
| **Jellyfin Media Player** (Windows/Linux/macOS) | ✅ | Fully supported. |
|
||||||
|
| **Android App** | ✅ | Works (Web wrapper). |
|
||||||
|
| **iOS App** | ✅ | Works (Web wrapper). |
|
||||||
|
| **Android TV / Fire TV** | ❌ | **Not supported** (Native UI). |
|
||||||
|
| **Roku** | ❌ | **Not supported** (Native UI). |
|
||||||
|
| **Swiftfin** | ❌ | **Not supported** (Native UI). |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Configure the plugin via **Dashboard** > **Plugins** > **Media Bar Enhanced**.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> You must refresh your browser window (F5 or Ctrl+R) after saving changes for them to take effect.
|
||||||
|
|
||||||
|
### General Settings
|
||||||
|
* **Enable Media Bar Enhanced Plugin**: Master switch to toggle the plugin.
|
||||||
|
* **Enable Video Backdrops**: Dynamically plays trailers in the background.
|
||||||
|
* **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).
|
||||||
|
* **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)
|
||||||
|
|
||||||
|
### Custom Content
|
||||||
|
Define exactly what shows up in your bar.
|
||||||
|
|
||||||
|
* **Enable Custom Media IDs**: Restrict the slideshow to a specific list of IDs.
|
||||||
|
* **Enable Seasonal Content Mode**: Advanced date-based scheduling.
|
||||||
|
* Format: `DD.MM-DD.MM | Name | ID1, ID2, ID3`
|
||||||
|
* Example: `20.10-31.10 | Halloween | <ID_OF_HALLOWEEN_COLLECTION>`
|
||||||
|
* If the current date matches a range, those IDs are used. Otherwise, it defaults to standard behavior or the Custom Media IDs list.
|
||||||
|
|
||||||
|
**How to get IDs:**
|
||||||
|
Check the URL of an item in the web interface:
|
||||||
|
`.../web/#/details?id=YOUR_ITEM_ID_HERE&...`
|
||||||
|
|
||||||
|
### Advanced Settings
|
||||||
|
* **Slide Animations**: Enable/disable the "Zoom In" effect.
|
||||||
|
* **Use SponsorBlock**: Skips non-content segments in YouTube trailers (if the data exists).
|
||||||
|
* **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).
|
||||||
|
* **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.
|
||||||
|
* **Enable Keyboard Controls**:
|
||||||
|
* `Left`/`Right`: Change slide
|
||||||
|
* `Space`: Pause/Play slideshow
|
||||||
|
* `M`: Mute/Unmute video
|
||||||
|
* **Content Limits**: Fine-tune performance by limiting the number of items (Movies, TV Shows) fetched.
|
||||||
|
|
||||||
|
## Build The Plugin By Yourself
|
||||||
|
|
||||||
|
If you want to build the plugin yourself:
|
||||||
|
|
||||||
|
1. Clone the repository.
|
||||||
|
2. Ensure you have the .NET SDK installed (NET 8 or 9 depending on your Jellyfin version).
|
||||||
|
3. Run the build command:
|
||||||
|
```powershell
|
||||||
|
dotnet build Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj --configuration Release --output bin/Publish
|
||||||
|
```
|
||||||
|
4. The compiled DLL and resources will be in bin/Publish.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Effects Not Showing
|
||||||
|
1. **Verify plugin installation**:
|
||||||
|
- Check that the plugin appears in the jellyfin admin panel
|
||||||
|
- Ensure that the plugin is enabled and active
|
||||||
|
|
||||||
|
2. **Clear browser cache**:
|
||||||
|
- Force refresh browser (Ctrl+F5)
|
||||||
|
- Clear jellyfin web client cache (--> mostly you have to clear the whole browser cache)
|
||||||
|
|
||||||
|
### Docker Permission Issues
|
||||||
|
If you encounter the message `Access was denied when attempting to inject script into index.html. Automatic direct injection failed. Automatic direct insertion failed. The system will now attempt to use the File Transformation plugin.` in the log or similar permission errors in Docker:
|
||||||
|
|
||||||
|
**Option 1: Use File Transformation Plugin (Recommended)**
|
||||||
|
|
||||||
|
Media Bar Enhanced now automatically detects and uses the [File Transformation](https://github.com/IAmParadox27/jellyfin-plugin-file-transformation) plugin (v2.5.0.0+) if it's installed. This eliminates permission issues by transforming content at runtime without modifying files on disk.
|
||||||
|
|
||||||
|
**Installation Steps:**
|
||||||
|
1. Install the File Transformation plugin from the Jellyfin plugin catalog
|
||||||
|
2. Restart Jellyfin
|
||||||
|
3. Media Bar Enhanced will automatically detect and use it (no configuration needed)
|
||||||
|
4. Check logs to confirm: Look for "Successfully registered transformation with File Transformation plugin"
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- No file permission issues in Docker environments
|
||||||
|
- Works with read-only web directories
|
||||||
|
- Survives Jellyfin updates without re-injection
|
||||||
|
- No manual file modifications required
|
||||||
|
|
||||||
|
**Option 2: Fix File Permissions**
|
||||||
|
```bash
|
||||||
|
# Find the actual index.html location
|
||||||
|
docker exec -it jellyfin find / -name index.html
|
||||||
|
|
||||||
|
# Fix ownership (replace 'jellyfin' with your container name and adjust user:group if needed)
|
||||||
|
docker exec -it --user root jellyfin chown jellyfin:jellyfin /jellyfin/jellyfin-web/index.html
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
docker restart jellyfin
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 3: Manual Volume Mapping**
|
||||||
|
```bash
|
||||||
|
# Extract index.html from container
|
||||||
|
docker cp jellyfin:/jellyfin/jellyfin-web/index.html /path/to/jellyfin/config/index.html
|
||||||
|
|
||||||
|
# Add to docker-compose.yml volumes section:
|
||||||
|
volumes:
|
||||||
|
- /path/to/jellyfin/config/index.html:/jellyfin/jellyfin-web/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Feel free to contribute to this project by creating pull requests or reporting issues.
|
||||||
|
|||||||
104
jprm.md
104
jprm.md
@@ -1,104 +0,0 @@
|
|||||||
# Using JPRM (Jellyfin Plugin Repository Manager)
|
|
||||||
|
|
||||||
Wenn du mehrere Plugins hast, ist es oft einfacher, den offiziellen **JPRM** (Jellyfin Plugin Repository Manager) zu nutzen. Anstatt dass jedes Plugin sich selbst in die Manifest-Datei "pusht" (wie in deinem aktuellen Script), "pullt" das zentrale Repo automatisch die neuesten Releases deiner Plugins.
|
|
||||||
|
|
||||||
## Wie es funktioniert
|
|
||||||
1. Du hast ein zentrales Repo (z.B. `jellyfin-plugin-manifest`).
|
|
||||||
2. Dort läuft ein Script (JPRM), das eine Liste von deinen Plugin-Repos durchgeht.
|
|
||||||
3. Es prüft, ob es neue Releases gibt.
|
|
||||||
4. Es baut die `manifest.json` komplett neu.
|
|
||||||
|
|
||||||
## Schritt 1: Das zentrale Repo vorbereiten
|
|
||||||
|
|
||||||
Erstelle ein neues Repo (oder nimm dein vorhandenes `jellyfin-plugin-manifest`) und erstelle eine Datei namens `config.json` (oder ähnlich), die deine Plugins auflistet.
|
|
||||||
|
|
||||||
Beispiel `manifest-config.json` für JPRM:
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Url": "https://github.com/CodeDevMLH/Media-Bar-Plugin",
|
|
||||||
"Branch": "main",
|
|
||||||
"Package": "Jellyfin.Plugin.MediaBarEnhanced"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Url": "https://github.com/CodeDevMLH/Anderes-Plugin",
|
|
||||||
"Branch": "main",
|
|
||||||
"Package": "Jellyfin.Plugin.Anderes"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Schritt 2: Der GitHub Workflow (im zentralen Repo)
|
|
||||||
|
|
||||||
In deinem **zentralen Repo** erstellst du einen Workflow `.github/workflows/update-manifest.yaml`. Dieser läuft z.B. jede Nacht oder wenn du ihn manuell startest.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: Generate Manifest
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * *' # Täglich um Mitternacht
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
generate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- name: Install JPRM
|
|
||||||
run: pip install jprm
|
|
||||||
|
|
||||||
- name: Generate Manifest
|
|
||||||
run: |
|
|
||||||
# jprm repo init --help für mehr infos
|
|
||||||
# Hier ein einfaches Beispielkommando (die genauen Flags hängen von deiner Struktur ab)
|
|
||||||
jprm repo build --url https://github.com/CodeDevMLH/Media-Bar-Plugin .
|
|
||||||
|
|
||||||
# Alternativ: Nutze ein fertiges Action-Script oder schreibe ein kleines Python Script,
|
|
||||||
# das die config.json liest und jprm aufruft.
|
|
||||||
|
|
||||||
- name: Commit & Push
|
|
||||||
uses: stefanzweifel/git-auto-commit-action@v4
|
|
||||||
with:
|
|
||||||
commit_message: Update repository manifest
|
|
||||||
file_pattern: manifest.json
|
|
||||||
```
|
|
||||||
|
|
||||||
*Hinweis: Der offizielle JPRM ist sehr mächtig, aber manchmal etwas komplex in der Einrichtung für einfache Setups. Viele User nutzen stattdessen ein einfacheres Python-Script.*
|
|
||||||
|
|
||||||
## Alternative: Einfaches Python Script (Empfohlen für den Start)
|
|
||||||
Ein simples Script, das du in deinem zentralen Repo ablegst und im Action-Workflow ausführst:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import json
|
|
||||||
import requests
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
# Konfiguration
|
|
||||||
PLUGINS = [
|
|
||||||
{"user": "CodeDevMLH", "repo": "Media-Bar-Plugin", "guid": "..."}
|
|
||||||
]
|
|
||||||
|
|
||||||
FINAL_MANIFEST = []
|
|
||||||
|
|
||||||
for p in PLUGINS:
|
|
||||||
# 1. Hole Latest Release vpm GitHub API
|
|
||||||
resp = requests.get(f"https://api.github.com/repos/{p['user']}/{p['repo']}/releases/latest")
|
|
||||||
data = resp.json()
|
|
||||||
|
|
||||||
# 2. Lade assets herunter, berechne Hash
|
|
||||||
# 3. Baue JSON Objekt
|
|
||||||
# ...
|
|
||||||
|
|
||||||
# Speichere final_manifest.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fazit
|
|
||||||
- **Push-Methode (Deine aktuelle Lösung):** Gut für den Anfang. Du hast sofort Kontrolle. Jedes Plugin "kümmert sich selbst".
|
|
||||||
- **Pull-Methode (JPRM):** Besser wenn du 5+ Plugins hast. Das zentrale Repo hat die Hoheit.
|
|
||||||
BIN
logo.png
BIN
logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 MiB After Width: | Height: | Size: 312 KiB |
BIN
logos/MediaBar_logo_mod.png
Normal file
BIN
logos/MediaBar_logo_mod.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 KiB |
@@ -6,15 +6,47 @@
|
|||||||
"overview": "Media Bar for Jellyfin",
|
"overview": "Media Bar for Jellyfin",
|
||||||
"owner": "CodeDevMLH",
|
"owner": "CodeDevMLH",
|
||||||
"category": "General",
|
"category": "General",
|
||||||
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Media-Bar-Plugin/raw/branch/main/logo.png",
|
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
|
||||||
"versions": [
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "1.2.0.0",
|
||||||
|
"changelog": "- Add video quality preference setting (Auto / 1080p / Highres)\n- Set preferred video quality on YouTube player based on setting",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.2.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "2f1628d864b35cb710431974a8b85bee",
|
||||||
|
"timestamp": "2026-01-08T22:16:05Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.2.0",
|
||||||
|
"changelog": "- Add method to resume video playback when slideshow is active",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.1.2.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "a0e8ff5e59b22a1bdedc916cd5e1c16a",
|
||||||
|
"timestamp": "2026-01-08T15:26:55Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.1.0",
|
||||||
|
"changelog": "- Add method to pause all video playback when navigating away from home screen",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.1.1.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "09da95fc561b11191d23a5cfa30ea731",
|
||||||
|
"timestamp": "2026-01-08T14:54:57Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.0.0",
|
||||||
|
"changelog": "- 'custom media IDs' setting is now enabled by default (no input --> random selection)\n- improve GUID handling in slideshow manager to handle seperator and description",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.1.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "32305d72b8d704acf8eef0c22277fee9",
|
||||||
|
"timestamp": "2026-01-08T02:15:50Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.0.0.0",
|
"version": "1.0.0.0",
|
||||||
"changelog": "Initial release",
|
"changelog": "Initial release",
|
||||||
"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.0.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.0.0.0/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
"checksum": "caf7dc027bb55b915e09613a534c6e74",
|
"checksum": "2ba7cc7f238f6aa7097628797935b903",
|
||||||
"timestamp": "2026-01-06T01:43:51Z"
|
"timestamp": "2026-01-06T18:56:30Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
name: Auto Release Plugin
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/**'
|
|
||||||
- 'README.md'
|
|
||||||
- 'jellyfin.ruleset'
|
|
||||||
- '.gitignore'
|
|
||||||
- '.editorconfig'
|
|
||||||
- 'LICENSE'
|
|
||||||
- 'logo.png'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v5
|
|
||||||
with:
|
|
||||||
dotnet-version: '9.x'
|
|
||||||
|
|
||||||
- name: Read Version from Manifest
|
|
||||||
id: read_version
|
|
||||||
run: |
|
|
||||||
VERSION=$(jq -r '.[0].versions[0].version' manifest.json)
|
|
||||||
CHANGELOG=$(jq -r '.[0].versions[0].changelog' manifest.json)
|
|
||||||
TARGET_ABI=$(jq -r '.[0].versions[0].targetAbi' manifest.json)
|
|
||||||
|
|
||||||
echo "Detected Version: $VERSION"
|
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
# Escape newlines in changelog for GITHUB_ENV
|
|
||||||
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
|
|
||||||
echo "$CHANGELOG" >> $GITHUB_ENV
|
|
||||||
echo "EOF" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build and Zip
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Inject version from manifest into the build
|
|
||||||
dotnet build Jellyfin.Plugin.MediaBarEnhanced/Jellyfin.Plugin.MediaBarEnhanced.csproj --configuration Release --output bin/Publish /p:Version=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }}
|
|
||||||
|
|
||||||
cd bin/Publish
|
|
||||||
zip -r Jellyfin.Plugin.MediaBarEnhanced.zip *
|
|
||||||
cd ../..
|
|
||||||
|
|
||||||
# Calculate hash
|
|
||||||
HASH=$(md5sum bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip | awk '{ print $1 }')
|
|
||||||
TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
|
|
||||||
# Export variables for next steps
|
|
||||||
echo "ZIP_HASH=$HASH" >> $GITHUB_ENV
|
|
||||||
echo "BUILD_TIME=$TIME" >> $GITHUB_ENV
|
|
||||||
echo "ZIP_PATH=bin/Publish/Jellyfin.Plugin.MediaBarEnhanced.zip" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Update Local manifest.json (Optional)
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
REPO_OWNER="${{ github.repository_owner }}"
|
|
||||||
REPO_NAME="${{ github.event.repository.name }}"
|
|
||||||
VERSION="${{ env.VERSION }}"
|
|
||||||
DOWNLOAD_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
|
|
||||||
|
|
||||||
echo "Updating local manifest.json with:"
|
|
||||||
echo "Hash: ${{ env.ZIP_HASH }}"
|
|
||||||
echo "Time: ${{ env.BUILD_TIME }}"
|
|
||||||
echo "Url: $DOWNLOAD_URL"
|
|
||||||
|
|
||||||
jq --arg hash "${{ env.ZIP_HASH }}" \
|
|
||||||
--arg time "${{ env.BUILD_TIME }}" \
|
|
||||||
--arg url "$DOWNLOAD_URL" \
|
|
||||||
'.[0].versions[0].checksum = $hash | .[0].versions[0].timestamp = $time | .[0].versions[0].sourceUrl = $url' \
|
|
||||||
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
|
||||||
|
|
||||||
- name: Commit Local manifest.json
|
|
||||||
uses: stefanzweifel/git-auto-commit-action@v7
|
|
||||||
with:
|
|
||||||
commit_message: "Update manifest.json for release v${{ env.VERSION }} [skip ci]"
|
|
||||||
file_pattern: manifest.json
|
|
||||||
|
|
||||||
- name: Create Release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
tag_name: "v${{ env.VERSION }}"
|
|
||||||
name: "v${{ env.VERSION }}"
|
|
||||||
# body: ${{ env.CHANGELOG }}
|
|
||||||
files: ${{ env.ZIP_PATH }}
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
generate_release_notes: true
|
|
||||||
|
|
||||||
# Update Message in Remote Repository
|
|
||||||
- name: Checkout Central Manifest Repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: ${{ github.repository_owner }}/jellyfin-plugin-manifest
|
|
||||||
path: central-manifest
|
|
||||||
token: ${{ secrets.CENTRAL_REPO_PAT }}
|
|
||||||
|
|
||||||
- name: Update Central Manifest
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd central-manifest
|
|
||||||
|
|
||||||
# 1. Get info from previous steps
|
|
||||||
VERSION="${{ env.VERSION }}"
|
|
||||||
HASH="${{ env.ZIP_HASH }}"
|
|
||||||
TIME="${{ env.BUILD_TIME }}"
|
|
||||||
# URL points to the RELEASE we just created in the CURRENT repo
|
|
||||||
DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/v$VERSION/Jellyfin.Plugin.MediaBarEnhanced.zip"
|
|
||||||
|
|
||||||
# 2. Extract GUID from the *built* artifact or the source manifest (in parent dir)
|
|
||||||
# We use the source manifest from the checkout in '..'
|
|
||||||
PLUGIN_GUID=$(jq -r '.[0].guid' ../manifest.json)
|
|
||||||
|
|
||||||
echo "Updating Central Manifest for Plugin GUID: $PLUGIN_GUID"
|
|
||||||
|
|
||||||
# 3. Update the specific plugin entry in the central manifest.json
|
|
||||||
# This logic finds the object with matching guid, and updates its first version entry.
|
|
||||||
jq --arg guid "$PLUGIN_GUID" \
|
|
||||||
--arg hash "$HASH" \
|
|
||||||
--arg time "$TIME" \
|
|
||||||
--arg url "$DOWNLOAD_URL" \
|
|
||||||
--arg ver "$VERSION" \
|
|
||||||
'map(if .guid == $guid then
|
|
||||||
.versions[0].version = $ver |
|
|
||||||
.versions[0].checksum = $hash |
|
|
||||||
.versions[0].timestamp = $time |
|
|
||||||
.versions[0].sourceUrl = $url
|
|
||||||
else . end)' \
|
|
||||||
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
|
||||||
|
|
||||||
- name: Commit and Push Central Manifest
|
|
||||||
run: |
|
|
||||||
cd central-manifest
|
|
||||||
git config user.name "GitHub Action"
|
|
||||||
git config user.email "action@github.com"
|
|
||||||
|
|
||||||
# Check if there are changes
|
|
||||||
if [[ -n $(git status -s) ]]; then
|
|
||||||
git add manifest.json
|
|
||||||
git commit -m "Update MediaBar Enhanced to v${{ env.VERSION }}"
|
|
||||||
git push
|
|
||||||
else
|
|
||||||
echo "No changes to central manifest."
|
|
||||||
fi
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
# Anleitung: Automatische Updates für "Plugin B" im Media-Bar Manifest
|
|
||||||
|
|
||||||
Damit dein Plugin B (das andere Repo) automatisch seine Version im `manifest.json` des **Media-Bar-Plugin** Repos aktualisiert, musst du die `release_automation.yml` **in Repo B** anpassen.
|
|
||||||
|
|
||||||
Das Prinzip ist dasselbe wie beim zentralen Repo: Repo B checkt Repo A aus und updatet sich selbst.
|
|
||||||
|
|
||||||
## Schritt 1: Secret anlegen
|
|
||||||
Du brauchst ein **PAT (Personal Access Token)**, das Zugriff auf das **Media-Bar-Plugin** Repo hat.
|
|
||||||
- Erstelle das Secret `MEDIA_BAR_REPO_PAT` im **Plugin B** Repo.
|
|
||||||
|
|
||||||
## Schritt 2: Workflow in Plugin B anpassen
|
|
||||||
|
|
||||||
Füge diesen Job-Step am Ende der `release_automation.yml` von **Plugin B** hinzu:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
# UPDATE MEDIA BAR MANIFEST (Cross-Promotion)
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
- name: Checkout Media Bar Repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: CodeDevMLH/Media-Bar-Plugin # <--- Ziel-Repo
|
|
||||||
path: media-bar-repo
|
|
||||||
token: ${{ secrets.MEDIA_BAR_REPO_PAT }}
|
|
||||||
|
|
||||||
- name: Update Entry in Media Bar Manifest
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd media-bar-repo
|
|
||||||
|
|
||||||
# Info vom aktuellen Release (Repo B)
|
|
||||||
VERSION="${{ env.VERSION }}"
|
|
||||||
HASH="${{ env.ZIP_HASH }}"
|
|
||||||
TIME="${{ env.BUILD_TIME }}"
|
|
||||||
# URL zum Release von Repo B
|
|
||||||
DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/v$VERSION/Jellyfin.Plugin.MeinTollesPlugin.zip" # <--- ANPASSEN
|
|
||||||
|
|
||||||
# GUID von Plugin B (Muss fest hinterlegt sein oder aus dem lokalen manifest kommen)
|
|
||||||
PLUGIN_GUID="a1b2c3d4-..." # <--- GUID von Plugin B HIER EINTRAGEN
|
|
||||||
|
|
||||||
echo "Updating Media Bar Manifest for GUID: $PLUGIN_GUID"
|
|
||||||
|
|
||||||
# Update Logic (findet Eintrag anhand GUID und updatet ihn)
|
|
||||||
jq --arg guid "$PLUGIN_GUID" \
|
|
||||||
--arg hash "$HASH" \
|
|
||||||
--arg time "$TIME" \
|
|
||||||
--arg url "$DOWNLOAD_URL" \
|
|
||||||
--arg ver "$VERSION" \
|
|
||||||
'map(if .guid == $guid then
|
|
||||||
.versions[0].version = $ver |
|
|
||||||
.versions[0].checksum = $hash |
|
|
||||||
.versions[0].timestamp = $time |
|
|
||||||
.versions[0].sourceUrl = $url
|
|
||||||
else . end)' \
|
|
||||||
manifest.json > manifest.json.tmp && mv manifest.json.tmp manifest.json
|
|
||||||
|
|
||||||
- name: Commit and Push to Media Bar Repo
|
|
||||||
run: |
|
|
||||||
cd media-bar-repo
|
|
||||||
git config user.name "GitHub Action"
|
|
||||||
git config user.email "action@github.com"
|
|
||||||
|
|
||||||
if [[ -n $(git status -s) ]]; then
|
|
||||||
git add manifest.json
|
|
||||||
git commit -m "Auto-Update Plugin B to v${{ env.VERSION }}"
|
|
||||||
git push
|
|
||||||
else
|
|
||||||
echo "No changes needed."
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user