mirror of
https://github.com/Felitendo/jellyfin-plugin-lyrics.git
synced 2026-11-01 17:23:25 +01:00
The Jellyfin 12 packages target net10.0 only, so one DLL cannot serve both server generations. A JellyfinTarget switch (jf10/jf12) selects the framework, the Jellyfin package version and a JF10/JF12 symbol. Each release produces both artifacts with the same plugin version and adds two manifest entries, Jellyfin 12 first. Jellyfin filters entries by targetAbi before its stable sort by version, so manifest order decides which build a Jellyfin 12 server picks. jprm repo add drops entries sharing a version, so scripts/update_manifest.py writes them. The 10.11 build is pinned to the 10.11.0 floor again, so the whole 10.11.x line can install it. CA1873 is relaxed for the .NET 10 analyzers, and CI builds both targets.
97 lines
3.8 KiB
Python
Executable File
97 lines
3.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Add one release to manifest.json as two entries - one per supported Jellyfin line.
|
|
|
|
`jprm repo add` cannot do this: update_plugin_manifest() drops any existing entry whose
|
|
version matches the one being added, so a second call for the same version would delete the
|
|
first artifact's entry instead of sitting next to it.
|
|
|
|
Two entries with the same version work because Jellyfin filters by ABI before it sorts:
|
|
|
|
.Where(x => string.IsNullOrEmpty(x.TargetAbi) || Version.Parse(x.TargetAbi) <= appVer)
|
|
...
|
|
foreach (var v in availableVersions.OrderByDescending(x => x.VersionNumber))
|
|
|
|
A 10.11 server never sees the 12.0.0.0 entry. A 12 server sees both, and since LINQ's
|
|
OrderByDescending is a stable sort and the caller takes the first result, the entry listed
|
|
first in the manifest wins the tie - hence Jellyfin 12 is written before Jellyfin 10.11.
|
|
"""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
BUILD_YAML = REPO_ROOT / "build.yaml"
|
|
MANIFEST = REPO_ROOT / "manifest.json"
|
|
|
|
# Order matters: the first matching entry wins on a server that can install both.
|
|
TARGET_ABIS = (("jf12", "12.0.0.0"), ("jf10", "10.11.0.0"))
|
|
|
|
|
|
def full_version(version):
|
|
"""Pad to four components; Jellyfin is not a fan of short version numbers."""
|
|
parts = str(version).split(".")
|
|
parts += ["0"] * (4 - len(parts))
|
|
return ".".join(parts[:4])
|
|
|
|
|
|
def md5(path):
|
|
digest = hashlib.md5() # noqa: S324 - manifest checksums are defined as md5 by Jellyfin
|
|
with open(path, "rb") as handle:
|
|
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
|
digest.update(chunk)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
for key, _ in TARGET_ABIS:
|
|
parser.add_argument("--{}-zip".format(key), required=True, help="Built artifact for {}".format(key))
|
|
parser.add_argument("--{}-url".format(key), required=True, help="Public download URL for {}".format(key))
|
|
args = parser.parse_args()
|
|
|
|
build_cfg = yaml.safe_load(BUILD_YAML.read_text(encoding="utf-8"))
|
|
version = full_version(build_cfg["version"])
|
|
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
|
plugin = next((item for item in manifest if item.get("guid") == str(build_cfg["guid"]).lower()), None)
|
|
if plugin is None:
|
|
sys.exit("No plugin with guid {} in manifest.json".format(build_cfg["guid"]))
|
|
|
|
for field in ("name", "description", "overview", "owner", "category"):
|
|
plugin[field] = build_cfg[field]
|
|
|
|
kept = [entry for entry in plugin["versions"] if full_version(entry["version"]) != version]
|
|
dropped = len(plugin["versions"]) - len(kept)
|
|
if dropped:
|
|
print("Replacing {} existing entr{} for {}".format(dropped, "y" if dropped == 1 else "ies", version))
|
|
|
|
new_entries = []
|
|
for key, target_abi in TARGET_ABIS:
|
|
zip_path = Path(getattr(args, "{}_zip".format(key)))
|
|
if not zip_path.is_file():
|
|
sys.exit("Artifact not found: {}".format(zip_path))
|
|
new_entries.append({
|
|
"version": version,
|
|
"changelog": build_cfg["changelog"],
|
|
"targetAbi": target_abi,
|
|
"sourceUrl": getattr(args, "{}_url".format(key)),
|
|
"checksum": md5(zip_path),
|
|
"timestamp": timestamp,
|
|
})
|
|
print("{}: targetAbi {} -> {}".format(key, target_abi, zip_path.name))
|
|
|
|
plugin["versions"] = new_entries + kept
|
|
MANIFEST.write_text(json.dumps(manifest, indent=4, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
print("manifest.json now has {} entries".format(len(plugin["versions"])))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|