diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index cd97e56..7e65ca7 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -32,21 +32,51 @@ jobs: - name: Run tests run: dotnet test Jellyfin.Plugin.Jellypod.sln --no-build --configuration Release --verbosity normal - - name: Build Jellyfin Plugin - id: jprm + - name: Build plugin for Jellyfin 10.9 (net8.0) + id: jprm_jf109 run: | - mkdir -p artifacts - jprm --verbosity=debug plugin build . + mkdir -p ./artifacts/jf10.9 + jprm --verbosity=debug plugin build . --output ./artifacts/jf10.9 - # Find the generated zip file - ARTIFACT=$(find . -name "*.zip" -type f -print -quit) - echo "artifact=${ARTIFACT}" >> $GITHUB_OUTPUT - echo "Found artifact: ${ARTIFACT}" + ARTIFACT=$(find ./artifacts/jf10.9 -name "*.zip" -type f -print -quit) + RENAMED="./artifacts/jf10.9/$(basename "${ARTIFACT}" .zip)_jf10.9.zip" + mv "${ARTIFACT}" "${RENAMED}" + echo "artifact=${RENAMED}" >> $GITHUB_OUTPUT + echo "Found artifact: ${RENAMED}" - - name: Upload build artifact + - name: Retarget build config at Jellyfin 12.0 (net10.0) + run: | + sed -i \ + -e 's/^targetAbi:.*/targetAbi: "12.0.0.0"/' \ + -e 's/^framework:.*/framework: "net10.0"/' \ + -e 's/^dotnet_framework:.*/dotnet_framework: "net10.0"/' \ + build.yaml + cat build.yaml + + - name: Build plugin for Jellyfin 12.0 (net10.0) + id: jprm_jf12 + run: | + mkdir -p ./artifacts/jf12 + jprm --verbosity=debug plugin build . --output ./artifacts/jf12 + + ARTIFACT=$(find ./artifacts/jf12 -name "*.zip" -type f -print -quit) + RENAMED="./artifacts/jf12/$(basename "${ARTIFACT}" .zip)_jf12.zip" + mv "${ARTIFACT}" "${RENAMED}" + echo "artifact=${RENAMED}" >> $GITHUB_OUTPUT + echo "Found artifact: ${RENAMED}" + + - name: Upload build artifact (Jellyfin 10.9) uses: actions/upload-artifact@v3 with: - name: jellypod-plugin - path: ${{ steps.jprm.outputs.artifact }} + name: jellypod-plugin-jf10.9 + path: ${{ steps.jprm_jf109.outputs.artifact }} + retention-days: 30 + if-no-files-found: error + + - name: Upload build artifact (Jellyfin 12.0) + uses: actions/upload-artifact@v3 + with: + name: jellypod-plugin-jf12 + path: ${{ steps.jprm_jf12.outputs.artifact }} retention-days: 30 if-no-files-found: error diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index fabbf16..f755665 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -39,6 +39,17 @@ jobs: sed -i "s/^version:.*/version: \"${VERSION}\"/" build.yaml cat build.yaml + - name: Generate Jellyfin 12.0 build config + run: | + # build.yaml is the Jellyfin 10.9 (net8.0) config; derive the + # Jellyfin 12.0 (net10.0) one from it so the two cannot drift apart. + sed \ + -e 's/^targetAbi:.*/targetAbi: "12.0.0.0"/' \ + -e 's/^framework:.*/framework: "net10.0"/' \ + -e 's/^dotnet_framework:.*/dotnet_framework: "net10.0"/' \ + build.yaml > build.jf12.yaml + cat build.jf12.yaml + - name: Restore dependencies run: dotnet restore Jellyfin.Plugin.Jellypod.sln @@ -48,25 +59,39 @@ jobs: - name: Run tests run: dotnet test Jellyfin.Plugin.Jellypod.sln --no-build --configuration Release --verbosity normal - - name: Build Jellyfin Plugin - id: jprm + - name: Build plugin for Jellyfin 10.9 (net8.0) + id: jprm_jf109 run: | - mkdir -p artifacts - jprm --verbosity=debug plugin build ./ + mkdir -p ./artifacts/jf10.9 + jprm --verbosity=debug plugin build . --output ./artifacts/jf10.9 - # Find the generated zip file - ARTIFACT=$(find . -name "*.zip" -type f -print -quit) - ARTIFACT_NAME=$(basename "${ARTIFACT}") - echo "artifact=${ARTIFACT}" >> $GITHUB_OUTPUT - echo "artifact_name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT - echo "Found artifact: ${ARTIFACT}" + ARTIFACT=$(find ./artifacts/jf10.9 -name "*.zip" -type f -print -quit) + RENAMED="./artifacts/jf10.9/$(basename "${ARTIFACT}" .zip)_jf10.9.zip" + mv "${ARTIFACT}" "${RENAMED}" + echo "artifact=${RENAMED}" >> $GITHUB_OUTPUT + echo "artifact_name=$(basename "${RENAMED}")" >> $GITHUB_OUTPUT + echo "checksum=$(md5sum "${RENAMED}" | awk '{print $1}')" >> $GITHUB_OUTPUT + echo "Found artifact: ${RENAMED}" - - name: Calculate checksum - id: checksum + - name: Build plugin for Jellyfin 12.0 (net10.0) + id: jprm_jf12 run: | - CHECKSUM=$(md5sum "${{ steps.jprm.outputs.artifact }}" | awk '{print $1}') - echo "checksum=${CHECKSUM}" >> $GITHUB_OUTPUT - echo "MD5 Checksum: ${CHECKSUM}" + # jprm always reads build.yaml, so swap the 12.0 config in for this build. + cp build.yaml build.jf10.9.yaml + cp build.jf12.yaml build.yaml + + mkdir -p ./artifacts/jf12 + jprm --verbosity=debug plugin build . --output ./artifacts/jf12 + + mv build.jf10.9.yaml build.yaml + + ARTIFACT=$(find ./artifacts/jf12 -name "*.zip" -type f -print -quit) + RENAMED="./artifacts/jf12/$(basename "${ARTIFACT}" .zip)_jf12.zip" + mv "${ARTIFACT}" "${RENAMED}" + echo "artifact=${RENAMED}" >> $GITHUB_OUTPUT + echo "artifact_name=$(basename "${RENAMED}")" >> $GITHUB_OUTPUT + echo "checksum=$(md5sum "${RENAMED}" | awk '{print $1}')" >> $GITHUB_OUTPUT + echo "Found artifact: ${RENAMED}" - name: Create Release id: create_release @@ -84,7 +109,7 @@ jobs: -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" \ "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases" \ - -d "$(jq -n --arg tag "$VERSION" --arg name "Release $VERSION" --arg body "Jellypod Jellyfin Plugin. See attached files for plugin installation." '{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')") + -d "$(jq -n --arg tag "$VERSION" --arg name "Release $VERSION" --arg body "Jellypod Jellyfin Plugin. Install the _jf10.9 build on Jellyfin 10.9.x and the _jf12 build on Jellyfin 12.0 or newer." '{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')") HTTP_CODE=$(echo "$RESPONSE" | tail -n1) BODY=$(echo "$RESPONSE" | sed '$d') @@ -99,21 +124,19 @@ jobs: exit 1 fi - # Upload plugin artifact - echo "Uploading plugin artifact..." - curl -f -X POST \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -H "Content-Type: application/zip" \ - --data-binary "@${{ steps.jprm.outputs.artifact }}" \ - "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets?name=${{ steps.jprm.outputs.artifact_name }}" + upload_asset() { + echo "Uploading $2 ..." + curl -f -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: $3" \ + --data-binary "@$1" \ + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets?name=$2" + } - # Upload build.yaml - echo "Uploading build.yaml..." - curl -f -X POST \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -H "Content-Type: application/x-yaml" \ - --data-binary "@build.yaml" \ - "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets?name=build.yaml" + upload_asset "${{ steps.jprm_jf109.outputs.artifact }}" "${{ steps.jprm_jf109.outputs.artifact_name }}" "application/zip" + upload_asset "${{ steps.jprm_jf12.outputs.artifact }}" "${{ steps.jprm_jf12.outputs.artifact_name }}" "application/zip" + upload_asset build.yaml build.yaml "application/x-yaml" + upload_asset build.jf12.yaml build.jf12.yaml "application/x-yaml" echo "Release created successfully!" echo "View at: ${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/tag/${{ steps.get_version.outputs.version }}" @@ -126,10 +149,8 @@ jobs: REPO_NAME="${{ github.event.repository.name }}" GITEA_URL="${{ github.server_url }}" VERSION="${{ steps.get_version.outputs.version_number }}" - CHECKSUM="${{ steps.checksum.outputs.checksum }}" - ARTIFACT_NAME="${{ steps.jprm.outputs.artifact_name }}" TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - DOWNLOAD_URL="${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/download/${{ steps.get_version.outputs.version }}/${ARTIFACT_NAME}" + RELEASE_URL="${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/download/${{ steps.get_version.outputs.version }}" # Configure git git config user.name "Gitea Actions" @@ -139,14 +160,23 @@ jobs: git fetch origin master git checkout master - # Update manifest.json - prepend new version to versions array + # Prepend both ABI entries for this version. Jellyfin keeps only the + # entries whose targetAbi is <= the server version and then takes the + # first of the highest version, so the 12.0 entry must come first: a + # 12.0 server sees both and picks it, a 10.9 server never sees it. jq --arg ver "${VERSION}" \ --arg changelog "Release ${VERSION}" \ - --arg abi "10.9.0.0" \ - --arg url "${DOWNLOAD_URL}" \ - --arg checksum "${CHECKSUM}" \ + --arg abi12 "12.0.0.0" \ + --arg url12 "${RELEASE_URL}/${{ steps.jprm_jf12.outputs.artifact_name }}" \ + --arg checksum12 "${{ steps.jprm_jf12.outputs.checksum }}" \ + --arg abi109 "10.9.0.0" \ + --arg url109 "${RELEASE_URL}/${{ steps.jprm_jf109.outputs.artifact_name }}" \ + --arg checksum109 "${{ steps.jprm_jf109.outputs.checksum }}" \ --arg ts "${TIMESTAMP}" \ - '.[0].versions = [{version: $ver, changelog: $changelog, targetAbi: $abi, sourceUrl: $url, checksum: $checksum, timestamp: $ts}] + .[0].versions' \ + '.[0].versions = [ + {version: $ver, changelog: $changelog, targetAbi: $abi12, sourceUrl: $url12, checksum: $checksum12, timestamp: $ts}, + {version: $ver, changelog: $changelog, targetAbi: $abi109, sourceUrl: $url109, checksum: $checksum109, timestamp: $ts} + ] + .[0].versions' \ manifest.json > manifest.tmp && mv manifest.tmp manifest.json # Commit and push diff --git a/.gitignore b/.gitignore index 0b72c24..9a31ffc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ obj/ .vs/ .idea/ artifacts +build.jf12.yaml diff --git a/Dockerfile.build b/Dockerfile.build index e2da9d7..30e1c05 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 +FROM mcr.microsoft.com/dotnet/sdk:10.0 # Install Python, Node.js, and tools RUN apt-get update && apt-get install -y \ @@ -10,6 +10,12 @@ RUN apt-get update && apt-get install -y \ npm \ && rm -rf /var/lib/apt/lists/* +# The .NET 10 SDK builds the net10.0 (Jellyfin 12.0) target; the .NET 8 SDK is +# needed alongside it for the net8.0 (Jellyfin 10.9) target. +RUN curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \ + && bash /tmp/dotnet-install.sh --channel 8.0 --install-dir /usr/share/dotnet --no-path \ + && rm /tmp/dotnet-install.sh + # Install JPRM RUN pip install --break-system-packages jprm diff --git a/Jellyfin.Plugin.Jellypod/Jellyfin.Plugin.Jellypod.csproj b/Jellyfin.Plugin.Jellypod/Jellyfin.Plugin.Jellypod.csproj index 8bcfc41..2fc230a 100644 --- a/Jellyfin.Plugin.Jellypod/Jellyfin.Plugin.Jellypod.csproj +++ b/Jellyfin.Plugin.Jellypod/Jellyfin.Plugin.Jellypod.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net10.0 Jellyfin.Plugin.Jellypod true true @@ -11,7 +11,8 @@ true - + + runtime @@ -22,6 +23,18 @@ + + + + runtime + + + runtime + + + + + diff --git a/Jellyfin.Plugin.Jellypod/ScheduledTasks/PodcastUpdateTask.cs b/Jellyfin.Plugin.Jellypod/ScheduledTasks/PodcastUpdateTask.cs index 948dcc6..8392c0e 100644 --- a/Jellyfin.Plugin.Jellypod/ScheduledTasks/PodcastUpdateTask.cs +++ b/Jellyfin.Plugin.Jellypod/ScheduledTasks/PodcastUpdateTask.cs @@ -150,7 +150,11 @@ public class PodcastUpdateTask : IScheduledTask { new TaskTriggerInfo { +#if NET10_0_OR_GREATER + Type = TaskTriggerInfoType.IntervalTrigger, +#else Type = TaskTriggerInfo.TriggerInterval, +#endif IntervalTicks = TimeSpan.FromHours(intervalHours).Ticks } }; diff --git a/README.md b/README.md index 77a264d..a1b2d2d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,19 @@ Add this repository URL in Jellyfin (Dashboard → Plugins → Repositories): https://gitea.tourolle.paris/dtourolle/jellypod/raw/branch/master/manifest.json ``` +## Supported Jellyfin Versions + +Jellypod is released as two builds from the same source. The repository manifest +lists both, and Jellyfin picks the right one for your server automatically: + +| Jellyfin server | Build | Target framework | +| --------------- | -------------- | ---------------- | +| 10.9.x | `*_jf10.9.zip` | .NET 8 | +| 12.0 and newer | `*_jf12.zip` | .NET 10 | + +If you install manually, download the build matching your server — a build +installed on the wrong server will fail to load. + ## Features - Browse and subscribe to podcasts @@ -92,7 +105,9 @@ ffmpeg -i "$INPUT" -c:a aac -b:a 128k "$OUTPUT" ### Manual Installation -1. Download the latest release from [Releases](https://gitea.tourolle.paris/dtourolle/jellypod/releases) +1. Download the latest release from [Releases](https://gitea.tourolle.paris/dtourolle/jellypod/releases), + picking `jellypod__jf10.9.zip` for Jellyfin 10.9.x or + `jellypod__jf12.zip` for Jellyfin 12.0 and newer 2. Extract the contents to your Jellyfin plugins directory: - **Linux**: `~/.local/share/jellyfin/plugins/Jellypod/` - **Windows**: `%LOCALAPPDATA%\jellyfin\plugins\Jellypod\` @@ -103,15 +118,32 @@ ffmpeg -i "$INPUT" -c:a aac -b:a 128k "$OUTPUT" #### Requirements -- [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) +- [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) for the Jellyfin 10.9 build +- [.NET SDK 10.0](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) for the Jellyfin 12.0 build #### Build ```bash -dotnet build +dotnet build # both targets +dotnet build -f net8.0 # Jellyfin 10.9 only +dotnet build -f net10.0 # Jellyfin 12.0 only ``` -The plugin DLL will be in `Jellyfin.Plugin.Jellypod/bin/Debug/net8.0/`. +The plugin DLL will be in `Jellyfin.Plugin.Jellypod/bin/Debug/net8.0/` or +`Jellyfin.Plugin.Jellypod/bin/Debug/net10.0/`. + +#### Packaging + +`build.yaml` is the [jprm](https://github.com/oddstr13/jellyfin-plugin-repository-manager) +config for the Jellyfin 10.9 build. The Jellyfin 12.0 config is derived from it +at release time, so the two cannot drift apart: + +```bash +sed -e 's/^targetAbi:.*/targetAbi: "12.0.0.0"/' \ + -e 's/^framework:.*/framework: "net10.0"/' \ + -e 's/^dotnet_framework:.*/dotnet_framework: "net10.0"/' \ + build.yaml > build.jf12.yaml +``` ## Development diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 8af791c..5e055d8 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -100,6 +100,8 @@ + +