Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
Some checks failed
release / build-release (push) Has been cancelled

- IChannel: creators -> sub-channels -> video posts (public listing API, search mapped)
- IRequiresMediaInfoCallback -> signed proxy URLs; stream controller rewrites HLS so the
  AES-128 key is fetched server-side with the session cookie, segments stay on the CDN
- delivery/info cached per token lifetime and throttled (~2/min)
- config page: paste sails.sid + validate, extra creators, quality, search test
- scripts/package.sh -> plugin folder + zip + md5 + manifest.json; Gitea release workflow
- research docs, HAR analysis (HAR itself gitignored), install-pi5.sh

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 09:42:27 +01:00
commit 9e33a343ab
162 changed files with 52004 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
name: Release
# release.sh commits a manifest.json whose sourceUrl points at the release
# asset, so the asset has to exist the moment the tag is pushed — otherwise
# every server that has the repository added downloads a 404 (see #19).
# This job owns the artifact: release.sh no longer builds one, because a ZIP
# built on the tagging machine is never byte-identical to the published one and
# its checksum described bytes nobody could download (#49).
on:
push:
tags: ["v*"]
permissions:
contents: write
env:
DOTNET_NOLOGO: "1"
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1"
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
# The annotated tag's own message is the fallback for the release notes,
# and it only arrives with the tag object itself.
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'Directory.Build.props') }}
restore-keys: nuget-${{ runner.os }}-
- name: Check the tag against meta.json
run: |
meta=$(python3 -c "import json;print(json.load(open('Jellyfin.Plugin.Chaosflix/meta.json'))['version'])")
tagged="${GITHUB_REF_NAME#v}"
if [[ "${meta%.*}" != "$tagged" ]]; then
echo "::warning::tag ${GITHUB_REF_NAME} does not match meta.json version ${meta}"
fi
# Same output as release.sh: publish to ./artifacts, drop meta.json next
# to the DLL, and zip exactly those two files flat.
- name: Build the plugin
run: |
dotnet publish Jellyfin.Plugin.Chaosflix/Jellyfin.Plugin.Chaosflix.csproj -c Release -o ./artifacts
cp Jellyfin.Plugin.Chaosflix/meta.json artifacts/
- name: Create the ZIP
id: zip
run: |
zip_name="chaosflix-jellyfin-${GITHUB_REF_NAME}.zip"
(cd artifacts && zip -j "../${zip_name}" Jellyfin.Plugin.Chaosflix.dll meta.json)
md5=$(md5sum "${zip_name}" | cut -d' ' -f1)
echo "name=${zip_name}" >> "$GITHUB_OUTPUT"
echo "md5=${md5}" >> "$GITHUB_OUTPUT"
{
echo "### ${GITHUB_REF_NAME}"
echo
echo "- asset: \`${zip_name}\`"
echo "- md5: \`${md5}\` — recorded as \`checksum\` in manifest.json"
} >> "$GITHUB_STEP_SUMMARY"
# release.sh writes release-notes-v<version>.md but .gitignore keeps it
# out of the commit, so in practice the tag message is what is used.
- name: Collect the release notes
id: notes
run: |
notes_file="release-notes-${GITHUB_REF_NAME}.md"
if [[ -s "${notes_file}" ]]; then
cp "${notes_file}" notes.md
else
git tag -l --format='%(contents)' "${GITHUB_REF_NAME}" > notes.md
fi
if [[ ! -s notes.md ]]; then
echo "Release ${GITHUB_REF_NAME}" > notes.md
fi
# Re-runnable on purpose: a failure in a later step used to be
# unrecoverable, because re-running the job started here and gh refused an
# existing release, leaving the checksum unrecorded (#100).
- name: Create or update the GitHub release
env:
GH_TOKEN: ${{ github.token }}
ASSET: ${{ steps.zip.outputs.name }}
run: |
if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
echo "release ${GITHUB_REF_NAME} exists; uploading the asset again"
gh release upload "${GITHUB_REF_NAME}" "${ASSET}" --clobber
else
gh release create "${GITHUB_REF_NAME}" "${ASSET}" \
--title "${GITHUB_REF_NAME}" \
--notes-file notes.md
fi
# The checksum is taken from the asset as it was downloaded back from the
# release, and it lands on the default branch rather than on the tag: a
# tag is immutable, and what servers read is manifest.json on that branch.
- name: Record the checksum in manifest.json
env:
GH_TOKEN: ${{ github.token }}
ASSET: ${{ steps.zip.outputs.name }}
TAG: ${{ github.ref_name }}
BRANCH: ${{ github.event.repository.default_branch }}
run: |
git fetch origin "${BRANCH}"
git checkout "${BRANCH}"
gh release download "${TAG}" --pattern "${ASSET}" --dir published --clobber
MD5=$(md5sum "published/${ASSET}" | cut -d' ' -f1) && export MD5
rm -rf published
python3 - <<'PYTHON'
import json, os, sys
with open("manifest.json") as f:
data = json.load(f)
wanted = os.environ["TAG"].lstrip("v")
for entry in data[0]["versions"]:
if entry["version"] == wanted or entry["version"].startswith(wanted + "."):
entry["checksum"] = os.environ["MD5"]
break
else:
sys.exit(f"manifest.json has no entry for {wanted}")
with open("manifest.json", "w") as f:
json.dump(data, f, indent=2)
f.write("\n")
PYTHON
if git diff --quiet manifest.json; then
echo "manifest.json already carries this checksum"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add manifest.json
git commit -m "release: record the ${TAG} checksum [skip ci]"
git push origin "HEAD:${BRANCH}"