Add plugin repository packaging and manifest metadata.

Include a .gitignore for .NET plugin development and a packaging script that produces Jellyfin-compatible zip artifacts with checksums and manifest updates for catalog installation.

Made-with: Cursor
This commit is contained in:
2026-03-23 08:25:56 -07:00
parent 1dbeb724fc
commit b988cdf8db
3 changed files with 216 additions and 0 deletions

39
.gitignore vendored Normal file
View File

@@ -0,0 +1,39 @@
# Build outputs
bin/
obj/
artifacts/
# User-specific files
*.user
*.rsuser
*.suo
*.userprefs
# IDE/editor files
.idea/
.vscode/
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db
# Logs and temp
*.log
*.tmp
*.temp
# .NET test and coverage artifacts
TestResults/
*.trx
*.coverage
*.coveragexml
coverage/
# NuGet
packages/
*.nupkg
*.snupkg

20
manifest.json Normal file
View File

@@ -0,0 +1,20 @@
[
{
"guid": "cc3f7cd7-e910-4f09-bf89-e9c7ba7fca77",
"name": "Jellyfin New Releases",
"description": "Newly Released Movies based on PremiereDate.",
"overview": "Newly released movies based on PremiereDate.",
"owner": "your-username",
"category": "General",
"versions": [
{
"version": "1.0.0.0",
"changelog": "Initial release",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://gitea.example.invalid/Jellyfin/plugins/releases/download/v1/Jellyfin.Plugin.NewReleases_1.0.0.0.zip",
"checksum": "a0d305168c315c0a0df28b8e1adad836",
"timestamp": "2026-03-23T15:24:20Z"
}
]
}
]

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_NAME="Jellyfin.Plugin.NewReleases"
DEFAULT_VERSION="1.0.0.0"
DEFAULT_TARGET_ABI="10.11.0.0"
usage() {
cat <<'EOF'
Usage:
./scripts/package-new-releases-plugin.sh [options]
Options:
--version <x.y.z.w> Plugin version (default: 1.0.0.0)
--target-abi <abi> Jellyfin ABI (default: 10.10.0.0)
--owner <owner> Owner for metadata (default: your-username)
--category <category> Category for metadata (default: General)
--out <dir> Output directory for the zip (default: ./artifacts)
--zip-name <name> Zip file name (default: Jellyfin.Plugin.NewReleases_<version>.zip)
--source-url <url> Direct URL to the zip on your host (required if --update-manifest)
--update-manifest Update ./manifest.json with sourceUrl/checksum/timestamp/targetAbi/version
Notes:
- You must host the resulting zip somewhere Jellyfin can download (Gitea release asset URL, for example).
- After packaging, upload the zip and set manifest.json 'sourceUrl' to the direct zip URL.
EOF
}
VERSION="$DEFAULT_VERSION"
TARGET_ABI="$DEFAULT_TARGET_ABI"
OWNER="your-username"
CATEGORY="General"
OUT_DIR="./artifacts"
ZIP_NAME="${PLUGIN_NAME}_${VERSION}.zip"
UPDATE_MANIFEST="false"
SOURCE_URL=""
while [[ $# -gt 0 ]]; do
case "$1" in
--version) VERSION="$2"; shift 2 ;;
--target-abi) TARGET_ABI="$2"; shift 2 ;;
--owner) OWNER="$2"; shift 2 ;;
--category) CATEGORY="$2"; shift 2 ;;
--out) OUT_DIR="$2"; shift 2 ;;
--zip-name) ZIP_NAME="$2"; shift 2 ;;
--source-url) SOURCE_URL="$2"; shift 2 ;;
--update-manifest) UPDATE_MANIFEST="true"; shift 1 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown arg: $1"; usage; exit 1 ;;
esac
done
if [[ -z "$ZIP_NAME" ]]; then
ZIP_NAME="${PLUGIN_NAME}_${VERSION}.zip"
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
DLL_PATH="${REPO_ROOT}/bin/Release/net8.0/${PLUGIN_NAME}.dll"
PDB_PATH="${REPO_ROOT}/bin/Release/net8.0/${PLUGIN_NAME}.pdb"
if [[ ! -f "$DLL_PATH" ]]; then
echo "Build output not found: $DLL_PATH"
echo "Run: dotnet build -c Release"
exit 1
fi
mkdir -p "${REPO_ROOT}/artifacts"
if [[ "$OUT_DIR" == /* ]]; then
OUT_DIR_ABS="$OUT_DIR"
else
OUT_DIR_ABS="${REPO_ROOT}/${OUT_DIR#./}"
fi
mkdir -p "${OUT_DIR_ABS}"
TMP_DIR="$(mktemp -d)"
cleanup() { rm -rf "$TMP_DIR"; }
trap cleanup EXIT
META_JSON_PATH="${TMP_DIR}/meta.json"
ZIP_PATH="${OUT_DIR_ABS}/${ZIP_NAME}"
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
cp "$DLL_PATH" "${TMP_DIR}/"
if [[ -f "$PDB_PATH" ]]; then
cp "$PDB_PATH" "${TMP_DIR}/"
fi
cat > "$META_JSON_PATH" <<EOF
{
"category": "${CATEGORY}",
"changelog": "Initial release",
"description": "Newly Released Movies based on PremiereDate.",
"guid": "cc3f7cd7-e910-4f09-bf89-e9c7ba7fca77",
"name": "Jellyfin New Releases",
"overview": "Newly released movies based on PremiereDate.",
"owner": "${OWNER}",
"targetAbi": "${TARGET_ABI}",
"timestamp": "${TIMESTAMP}",
"version": "${VERSION}"
}
EOF
( cd "$TMP_DIR" && zip -q -r "$ZIP_PATH" . )
MD5="$(md5sum "$ZIP_PATH" | awk '{print $1}')"
echo "Created: $ZIP_PATH"
echo "MD5: $MD5"
echo ""
echo "Next steps:"
echo "1) Upload the zip to your Gitea so you have a direct download URL."
echo "2) Update ./manifest.json:"
echo " - sourceUrl: set to the direct zip URL"
echo " - checksum: set to the MD5 value above"
echo " - targetAbi/version/timestamp (if different) to match your choices"
if [[ "$UPDATE_MANIFEST" == "true" ]]; then
if [[ ! -f "${REPO_ROOT}/manifest.json" ]]; then
echo "manifest.json not found at ${REPO_ROOT}/manifest.json"
exit 1
fi
if [[ -z "$SOURCE_URL" ]]; then
echo "Update requested but --source-url not provided."
exit 1
fi
python3 - <<PY
import json
from pathlib import Path
manifest_path = Path("${REPO_ROOT}") / "manifest.json"
data = json.loads(manifest_path.read_text(encoding="utf-8"))
if not isinstance(data, list) or len(data) == 0:
raise SystemExit("manifest.json must be a JSON array with at least one plugin entry.")
plugin = data[0]
versions = plugin.get("versions") or []
if not versions:
raise SystemExit("manifest.json plugin entry must include a non-empty 'versions' array.")
v0 = versions[0]
v0["version"] = "${VERSION}"
v0["targetAbi"] = "${TARGET_ABI}"
v0["sourceUrl"] = "${SOURCE_URL}"
v0["checksum"] = "${MD5}"
v0["timestamp"] = "${TIMESTAMP}"
manifest_path.write_text(json.dumps(data, indent=2) + "\\n", encoding="utf-8")
print("Updated manifest.json")
PY
fi