3 Commits

Author SHA1 Message Date
Ray Slakinski
679a674fc9 Share an episode as a page, on ipx-play (#447)
Share handed over the raw file address or the item's link: no preview,
no time, nothing a person without iPX could do much with. Now, as
Overcast does, an episode shares as a short link to its own page,
play.ipxreader.app/e/<id>, which anyone can open: the show's art and
name, the episode's title, date, length and notes, a player on the
publisher's own file starting at ?t= if a time was shared, buttons to
subscribe in Apple Podcasts, Overcast, Pocket Casts or Castro, and Open
Graph tags for a preview where the link is pasted.

ipx-play is a service of its own (crates/play), built like the
directory: ipx sends it the episode with its token (POST /v1/shares)
and gets the link back. The id comes from the feed's address and the
guid, so the same episode is one link whoever shares it. Only episodes
of feeds the directory would be sent go there, by the directory's own
rule (snapshot_entries), so the two cannot disagree on what is private;
ipx-play also refuses an address that looks private. Anything else, and
any server without ipx-play set up, shares as before.

In the page, Share on an episode opens a menu once the link is back:
Share link, Share from 12:34 (the player's clock, or where you left
off), Copy link. A menu because the share sheet needs a press of its
own, and the request for the link used up the one that opened it.

The page runs no script: the start time is the media fragment #t=, and
its CSP loads nothing but its stylesheet, the art and the file.

Admin, Directory, Sharing episodes sets the address and token. The
browser suite starts its own ipx-play; it was not run here (no Caddy on
this Mac).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 09:24:18 -05:00
b509ea4b35 Coverage for the Rust and the page, and tests for the offline gaps (#390, #391)
Nothing measured what the tests reach. scripts/coverage.sh runs the
cargo tests under cargo-llvm-cov in the ipx-coverage image
(Dockerfile.coverage, built by build-image.sh coverage), in a container
held to the image builder's 6 GB and six threads, one crate at a time,
with the same room and load checks: an instrumented build rebuilds every
dependency, the kind of build that took Tower down on 2026-11-04. The
first run stayed under load 8 with 11 GB free; 69% of lines. web.rs and
daemon.rs read low because the browser suite drives them against a
daemon that is not instrumented.

npm run coverage:page runs the browser suite on a build of the page
(IPX_COVERAGE=1) whose script keeps a source map to each web/src file:
web/concat-map.mts maps the joined files line by line and swc takes it
as its input map. Each test's page records V8's coverage
(tests/ui/coverage.ts, which the specs now take test and expect from),
and monocart-coverage-reports writes it file by file: 73% of lines.
build.mjs requires the map helper only for that build, since
Dockerfile.web's Node does not run TypeScript, and not with a top-level
await, which stopped the CommonJS tests requiring build.mjs.

The offline tests move to offline.spec.ts, in TypeScript, and stand
alone (they add their feed and set the read state they need), with what
#391 found untested: another account signing in clears what was kept, a
position saved offline waits and is sent, and pictures past the budget
go oldest first (keepPictures takes the budget, and trims whether or
not anything new came). The fixture JPEGs were a few bytes no browser
could draw; they are real now, and the artwork test checks one draws.
tsconfig.tools.json type-checks the tests' and build's TypeScript, in
npm run typecheck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 01:55:19 +00:00
c8cbb3047a Images build in a capped BuildKit builder
A plain `docker buildx build` of ipx on 2026-11-04, its dependency layer
invalidated by a Cargo.toml change (#382), rebuilt everything in release
on all twelve threads with 5 GB free and no swap. Tower thrashed for
three hours with every core pegged and had to be rebooted.

scripts/build-image.sh now builds every image: it refuses under 9 GB
available or above load 12, and builds in the docker-container builder
ipx-capped, held to 6 GB and six threads by its cgroup, so a build that
outgrows that is killed instead of stalling production. The builder
pulls base images through mirror.gcr.io, which replaces tagging local
copies to dodge Docker Hub's 429s. CLAUDE.md deploys only through it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 20:23:23 +00:00