Files
ipx/docs/play.md
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

79 lines
3.2 KiB
Markdown

# ipx-play: shared episodes (#447)
When someone shares an episode in iPX, their server sends it to ipx-play, which keeps it and
answers with a short link, `https://play.ipxreader.app/e/<id>`. Anyone can open that link: it
shows the episode, plays it from the publisher's own file (from the time it was shared at, with
`?t=`), and offers to subscribe in the reader's own podcast app. Overcast's links are the model.
Only episodes of public feeds are sent, by the same rule that decides what the directory is sent:
nothing from a feed marked private, one with a login, one whose address carries a key, or one in
a private OPML. ipx-play refuses an address that looks like one too. Everything else is shared by
its own address, as before. ipx-play keeps what an episode is and where its file is; it serves no
file itself.
## Running it
A container of its own, `ipx-play`, in the Arcane project `ipx`, from its own image:
```sh
sh scripts/build-image.sh play
docker compose -f /mnt/fast/arcane/projects/ipx/compose.yaml pull ipx-play
docker compose -f /mnt/fast/arcane/projects/ipx/compose.yaml up -d ipx-play
```
The compose service, beside `ipx-directory`:
```yaml
ipx-play:
image: 192.168.1.130:5000/ipx-play:latest
container_name: ipx-play
restart: unless-stopped
env_file: play.env # PLAY_DATABASE_URL=postgres://...
environment:
PLAY_LOG_FORMAT: json
OTEL_EXPORTER_OTLP_ENDPOINT: http://tempo:4318
volumes:
- /mnt/fast/appdata/ipx-play:/config
ports:
- "8102:8102"
```
Its config, `/mnt/fast/appdata/ipx-play/play.toml`:
```toml
bind = "0.0.0.0:8102"
public_url = "https://play.ipxreader.app"
# One for each ipx server allowed to make links; `ipx-play token <name>` prints the lines.
[[tokens]]
name = "tower"
token_sha256 = "..."
```
Tokens are read at start: after adding one, restart the container.
Its database is Postgres, `ipx_play` on the `databases` project's server, its URL in `play.env`
beside the compose file (mode 600), as the directory's is. It makes its one table, `shares`, on
start.
The tunnel publishes `play.ipxreader.app` to `http://ipx-play:8102` with no Access in front: the
pages are anyone's. `/v1` takes a token for everything.
## Setting up ipx
In Admin, Directory, Sharing episodes: the address ipx reaches it at, `http://ipx-play:8102`
inside Docker (the public address would go out through Cloudflare and back), and the token
`ipx-play token tower` printed. Without them Share hands over the episode's own address.
## The page
`/e/<id>` shows the show's art, its name, the episode's title, date and length, a player starting
at `?t=` (seconds, or `12:34`), the episode's own page if it has one, its notes (sanitized by
ammonia), and buttons that open Apple Podcasts, Overcast, Pocket Casts and Castro at the feed, with
the feed's address to paste into anything else. Open Graph tags give it a preview in Messages,
Slack and the like. It runs no script: the start time is the media fragment `#t=` on the file. Its
Content-Security-Policy lets it load only its stylesheet, the art and the file.
A show that inserts its ads per listener can move the time by an ad's length, as Overcast's links
can: the file is the publisher's, fetched fresh by each listener.