Fix repo.json: embed the manifest, don't just list its fields
Read the Homebrew Channel's own DetailsPanel.js: when an app is opened from Browse, refresh() only ever takes entry.manifest directly, or fetches entry.manifestUrl if that's absent — never the entry's own top-level fields. Our repo.json had neither, so resolveURL(undefined, repositoryURL) threw before the fetch could even start, and the details screen spun on "Loading" forever with no visible error. This is what happened when it was tried against a real repo. Fixed by nesting the full manifest under a "manifest" key per package entry, alongside the id/title/iconUri the Browse grid reads directly. Verified structurally (grid fields present, manifest embedded and non-empty, ipkUrl absolute) since there's no local Homebrew Channel build to run this against. Also caught in the same investigation: a stale --base-url in the docs used the tag v1.0.0 while the actual release was tagged 1.0.0, which 404s the icon and ipk silently — same symptom, different cause. Noted in both the tool's docstring and the docs that a tag mismatch looks identical to the manifest bug from the client's side. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4f008c558b
commit
f622c3a0bf
+20
-12
@@ -113,29 +113,37 @@ or ssh — the app installs itself once the TV can reach a URL.
|
||||
### Your own repository (no review, no waiting)
|
||||
|
||||
The Homebrew Channel's *Settings → Repositories → Add repository* accepts any
|
||||
URL that returns `{"packages": [...]}`, where each entry is the same manifest
|
||||
[`make-manifest.py`](../tools/make-manifest.py) already writes. Point one at
|
||||
your own git host's release assets and the app shows up in Browse with no
|
||||
submission process at all — this is what `--repo-out` (on by default) is for.
|
||||
URL that returns `{"packages": [...]}`. Each entry needs its own `id`/
|
||||
`title`/`iconUri` for the Browse grid, plus the full manifest nested under a
|
||||
`manifest` key for the details screen — [`make-manifest.py`](../tools/make-manifest.py)
|
||||
builds exactly that shape. Point one at your own git host's release assets and
|
||||
the app shows up in Browse with no submission process at all — this is what
|
||||
`--repo-out` (on by default) is for.
|
||||
|
||||
1. Bump `version` in `frontend/appinfo.json`, `servicefiles/package.json` and
|
||||
`package.json`.
|
||||
2. Build the ipk: `./tools/docker-build.sh && ./tools/build.sh package` (or
|
||||
`./tools/build.sh` on Linux with the NDK installed).
|
||||
3. Create a release tagged e.g. `v1.0.0` and attach three files to it: the
|
||||
ipk, `frontend/assets/icon.png`, and a repo index generated with
|
||||
`--base-url` set to that release's asset URL:
|
||||
3. Create a release — note the **exact tag** Gitea/GitHub gives it, `1.0.0` or
|
||||
`v1.0.0`, whichever it actually is — and attach three files: the ipk,
|
||||
`frontend/assets/icon.png`, and a repo index generated with `--base-url`
|
||||
set to that release's real download URL:
|
||||
|
||||
```sh
|
||||
python3 tools/make-manifest.py \
|
||||
--base-url https://git.crylia.de/Crylia/lgtv_audio_cap/releases/download/v1.0.0
|
||||
# -> out/manifest.json (one app entry)
|
||||
# -> out/repo.json (that entry wrapped as {"packages": [...]})
|
||||
--base-url https://git.crylia.de/Crylia/lgtv_audio_cap/releases/download/1.0.0
|
||||
# -> out/manifest.json (one app entry, for the official-repo route below)
|
||||
# -> out/repo.json ({"packages": [{id, title, iconUri, manifest: {...}}]})
|
||||
```
|
||||
|
||||
A mismatched tag in `--base-url` doesn't error — it just makes the icon and
|
||||
ipk links inside `repo.json` 404 silently, which looks identical to "the
|
||||
details screen hangs" from the client's point of view. If the app was
|
||||
already added and only the tag was wrong, re-run with the fixed tag and
|
||||
re-upload `repo.json`; no need to touch the "Add repository" entry itself,
|
||||
since its URL didn't change.
|
||||
Attach `out/repo.json` itself too — its own download URL is what you paste
|
||||
into the TV, and it must match `--base-url` exactly or the ipk/icon links
|
||||
inside it point at the wrong place.
|
||||
into the TV.
|
||||
4. On the TV: Homebrew Channel → gear icon → *Add repository* → paste the
|
||||
`repo.json` release URL → back out to Browse → find *Audio Cap* → Install.
|
||||
|
||||
|
||||
+32
-6
@@ -6,15 +6,27 @@ The manifest (out/manifest.json) is one app entry: id, ipkUrl, ipkHash, and so
|
||||
on. It is what you submit to webosbrew/apps-repo to get into the official
|
||||
store.
|
||||
|
||||
The repo index (out/repo.json) is that same entry wrapped as
|
||||
`{"packages": [...]}`, which is the format the Homebrew Channel's own
|
||||
"Add repository" dialog expects (Settings -> Repositories -> Add repository).
|
||||
Host it anywhere static, paste its URL in, and the app shows up in Browse —
|
||||
no submission, no review, no shell access to the TV at all.
|
||||
The repo index (out/repo.json) is `{"packages": [...]}`, which is the format
|
||||
the Homebrew Channel's own "Add repository" dialog expects (Settings ->
|
||||
Repositories -> Add repository). Host it anywhere static, paste its URL in,
|
||||
and the app shows up in Browse — no submission, no review, no shell access to
|
||||
the TV at all.
|
||||
|
||||
Each package entry embeds the full manifest under a "manifest" key. That is
|
||||
not decoration: the app's details screen (DetailsPanel.refresh(), read
|
||||
straight from its source) only ever uses entry.manifest directly, or fetches
|
||||
entry.manifestUrl if entry.manifest is absent. Ship a repo.json without either
|
||||
one and the details view calls resolveURL(undefined, ...), throws, and spins
|
||||
on "Loading" forever with no error shown. Embedding beats a manifestUrl
|
||||
because there is only one file to keep in sync.
|
||||
|
||||
python3 tools/make-manifest.py \\
|
||||
--base-url https://git.example/you/lgtv-audio-cap/releases/download/v1.0.0
|
||||
|
||||
--base-url must be the exact release download URL, tag and all — the icon and
|
||||
ipk links are absolute, so a tag typo (v1.0.0 vs 1.0.0) 404s silently rather
|
||||
than falling back to anything.
|
||||
|
||||
Defaults to the newest ipk in out/ and writes out/manifest.json + out/repo.json.
|
||||
"""
|
||||
|
||||
@@ -91,9 +103,23 @@ def main():
|
||||
print("\nwrote %s" % os.path.relpath(args.out, ROOT), file=sys.stderr)
|
||||
|
||||
if args.repo_out:
|
||||
# The grid view (BrowserPanel) reads id/title/iconUri straight off the
|
||||
# package entry. The details view (DetailsPanel) only ever looks at
|
||||
# entry.manifest directly, or fetches entry.manifestUrl if that is
|
||||
# missing — never the entry's own top-level fields. Skipping
|
||||
# manifestUrl (a second file, a second URL to keep in sync) by
|
||||
# embedding the manifest here means DetailsPanel takes its
|
||||
# already-ready fast path and never issues that fetch at all.
|
||||
package_entry = {
|
||||
"id": manifest["id"],
|
||||
"title": manifest["title"],
|
||||
"iconUri": manifest["iconUri"],
|
||||
"shortDescription": manifest["appDescription"],
|
||||
"manifest": manifest,
|
||||
}
|
||||
os.makedirs(os.path.dirname(args.repo_out) or ".", exist_ok=True)
|
||||
with open(args.repo_out, "w") as fh:
|
||||
json.dump({"packages": [manifest]}, fh, indent=2)
|
||||
json.dump({"packages": [package_entry]}, fh, indent=2)
|
||||
fh.write("\n")
|
||||
print("wrote %s" % os.path.relpath(args.repo_out, ROOT), file=sys.stderr)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user