Skip to content

New tool: PBS slug resolver — Media Manager slug → pbswisconsin.org URL (design doc) #21

Description

@mriechers

Idea

Port the "Jump to PBSWI slug" Alfred workflow (alfred-setup#1, #2) to a browser tool: paste a slug copied from the Media Manager backend (or any portal/pbs.org/station URL), get the canonical pbswisconsin.org/watch/{show}/{slug}/ URL with copy/open buttons — plus a searchable browser of every show the station carries. Useful from any machine (work laptop, phone) without Alfred.

What the Alfred build established (all verified 2026-07)

  • Asset slugs end in an auto-generated 6-char suffix (racine-zoo-ho3yhq) — detectable by shape: ^[a-z0-9]+(?:-[a-z0-9]+)*-[a-z0-9]{6}$.
  • video.pbswisconsin.org/video/{slug}/ resolves any published asset with no show knowledge (200 = valid, 404 = unpublished/expired) and its HTML names the show. The station's own /watch/ pages do NOT validate server-side.
  • Full show catalog (~2,690) enumerable from videosearch.php?let=A..Z plus the local-show slug lists embedded in /shows-a-z/ (the A-Z index omits some local shows — inside-wisconsin-politics proved it).
  • Station watch URL: https://pbswisconsin.org/watch/{show-slug}/{asset-slug}/.

The browser constraint (tested 2026-07-03)

None of the endpoints send Access-Control-Allow-Origin (checked with an Origin: https://tools.bymarkriechers.com header against the portal, wpt-cove/css/videosearch.php, and the MM API base). Cross-origin fetches from the tool will be blocked, so the Alfred workflow's live resolution/validation cannot run client-side. Design works around it; no proxies (violates the repo's privacy-first/no-server principles).

Architecture — v1 (fully static)

Follows the existing build-suggested-tools.yml precedent: CI does the fetching, the browser stays offline.

.github/workflows/build-pbs-catalog.yml — scheduled (weekly cron + workflow_dispatch), runs a zero-dep Node script that:

  1. Sweeps videosearch.php?let=A..Z → slug + title pairs.
  2. Scrapes /shows-a-z/ embedded poster_shows lists (title-cased from slug when the sweep lacks it).
  3. Writes pbs-slug/catalog.json ({generated, shows: [{slug, title}]}, ~100 KB) and commits if changed.

pbs-slug/ tool (vanilla HTML/CSS/JS, shared/ styles, dark mode, a11y per repo principles):

  • One input, auto-detects intent:
    • Complete asset slug or pasted URL → normalize (accept portal / pbs.org / station URL forms) → emit portal URL (always constructible, needs no show) + station URL when the show is resolvable: slug-prefix match against the catalog (MM bakes the show prefix into most station-produced asset slugs). Each result: copy button + open-in-new-tab (opening doubles as manual validation — the tool says so honestly: "can't verify from the browser; portal 404 = unpublished/expired").
    • Show name → instant client-side token search over catalog.json → /watch/{slug}/ links. Same all-tokens-covered ranking logic as the Alfred version.
  • Show catalog freshness shown from generated timestamp; ?slug= / ?q= query params for shareable links (matches og-image's ?url= pattern).

Landing page: add tool tile + README section, per repo structure.

v2 (optional live layer, later)

The wpt-cove plugin is station-controlled — if an Access-Control-Allow-Origin: * header were added to videosearch.php (read-only, public data, no credentials), the tool could add live show search parity with Alfred. Portal-side validation would still be blocked (pbs.org infrastructure, not ours) short of the Media Manager API — which needs a key and therefore a server, so it stays out of scope for this repo (the pending MM API access request in pbswi is the right home for that).

Non-goals

  • CORS proxies or any server-side component (repo principles).
  • Replacing the Alfred workflow (it keeps live validation; this is the anywhere version).

Open questions

  • Tool naming/placement: pbs-slug/? It's station-specific on an otherwise general-purpose public site — fine, or prefer unlisted (no landing tile)?
  • Catalog cadence: weekly cron enough? (Alfred uses 7-day TTL; live fallback covers gaps there, which v1 here won't have.)
  • Ship the initial catalog.json in the first PR or let the Action generate it on first run?

🤖 Generated with Claude Code

https://claude.ai/code/session_012EXrV9taMGFN2iDa317caX

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions