Files
rootandClaude Opus 5.5 f9933a4883 iOS: prov uses the main bundle ID, release becomes -pub
prov    = org.cdnutter.<name> (personal profile)
release = org.cdnutter.<name>-pub

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 10:25:56 -08:00

85 lines
3.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Writes ios/source.json, an AltStore source (SideStore and Feather read it too), from the
ios-v* releases on Gitea. Only releases with a stemify-<version>.ipa are listed, since older
ones used a different bundle ID. Set GITEA_TOKEN if the repo is private."""
import json, os, re, urllib.request
REPO = "https://git.iamchrisama.com/cdnutter/stemify"
API = "https://git.iamchrisama.com/api/v1/repos/cdnutter/stemify"
RAW = f"{REPO}/raw/branch/main"
HERE = os.path.dirname(os.path.abspath(__file__))
def get(url):
req = urllib.request.Request(url)
if os.environ.get("GITEA_TOKEN"):
req.add_header("Authorization", "token " + os.environ["GITEA_TOKEN"])
with urllib.request.urlopen(req) as r:
return json.load(r)
yml = open(os.path.join(HERE, "..", "project.yml")).read()
# Build numbers: older ones from the last source.json, the current one from project.yml.
builds = {}
try:
for v in json.load(open(os.path.join(HERE, "..", "source.json")))["apps"][0]["versions"]:
builds[v["downloadURL"].rsplit("/stemify-", 1)[-1].removesuffix(".ipa")] = v["buildVersion"]
except (OSError, ValueError, KeyError, IndexError):
pass
# Tags and IPAs use the label as a slug ("1.0 RC 1" -> 1.0-rc1); AltStore gets the plain bundle
# version, so it matches what's installed, and the label to show.
label = re.search(r'VERSION_LABEL: "(.*)"', yml)[1]
slug = label.lower().replace(" rc ", "-rc").replace(" ", "-")
builds[slug] = re.search(r'CURRENT_PROJECT_VERSION: "(.*)"', yml)[1]
versions = []
for rel in get(API + "/releases?limit=50"):
v = rel["tag_name"].removeprefix("ios-v")
ipa = next((a for a in rel["assets"] if a["name"] == f"stemify-{v}.ipa"), None)
# Pre-releases are dev builds (org.cdnutter.stemify-dev), they go to Sideload Developer instead.
if not rel["tag_name"].startswith("ios-v") or not ipa or rel["prerelease"]:
continue
versions.append({
"version": v.split("-")[0],
"marketingVersion": v.replace("-rc", " RC ").replace("-", " ") if "-" in v else v,
"buildVersion": builds.get(v, v),
"date": rel["published_at"],
"localizedDescription": rel["body"].strip() or f"Stemify {v}",
"downloadURL": ipa["browser_download_url"],
"size": ipa["size"],
"minOSVersion": "17.0",
})
source = {
"name": "Stemify",
"identifier": "org.cdnutter.stemify.source",
"sourceURL": f"{RAW}/ios/source.json",
"iconURL": f"{RAW}/ios/Stemify/Resources/Assets.xcassets/AppIcon.appiconset/icon.png",
"website": REPO,
"apps": [{
"name": "Stemify",
"bundleIdentifier": "org.cdnutter.stemify-pub",
"developerName": "cdnutter",
"subtitle": "Plex music player with live stem mixing.",
"localizedDescription": "Plays your Plex library and lets you mute vocals or remix stems on the fly. "
"Works offline with downloads, and in the car with CarPlay.",
"iconURL": f"{RAW}/ios/Stemify/Resources/Assets.xcassets/AppIcon.appiconset/icon.png",
"tintColor": "#FA2D48",
"category": "entertainment",
"versions": versions,
"appPermissions": {
"entitlements": ["com.apple.developer.carplay-audio", "com.apple.developer.siri"],
"privacy": {
"NSLocalNetworkUsageDescription": "Stemify talks to Plex and the stems server on your home network.",
"NSSiriUsageDescription": "Ask Siri to play music from your Plex library.",
},
},
}],
"news": [],
}
with open(os.path.join(HERE, "..", "source.json"), "w") as f:
json.dump(source, f, indent=2)
f.write("\n")
print(f"source.json: {len(versions)} version(s)")