From b94576cded78b98cd259940aa8c2dd6501c41e88 Mon Sep 17 00:00:00 2001 From: vegard Date: Wed, 18 Mar 2026 17:59:59 +0000 Subject: [PATCH] Legg til fase 30: komplett podcast-hosting uten ekstern avhengighet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8 oppgaver: iTunes/Podcasting 2.0 RSS-tags, nedlastingsstatistikk (IAB-kompatibel), embed-spiller, import fra eksisterende podcast med prøveimport-flyt (importer → test → re-importer nye → 301), og feed-redirect for å flytte bort. Feature-spec: docs/features/podcast_hosting.md Ingen castopod — podcasten er noder med riktige edges og en feed. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/features/podcast_hosting.md | 208 +++++++++++++++++++++++++++++++ scripts/run-next-task.sh | 1 + tasks.md | 25 ++++ 3 files changed, 234 insertions(+) create mode 100644 docs/features/podcast_hosting.md diff --git a/docs/features/podcast_hosting.md b/docs/features/podcast_hosting.md new file mode 100644 index 0000000..0fa66cb --- /dev/null +++ b/docs/features/podcast_hosting.md @@ -0,0 +1,208 @@ +# Feature: Podcast-hosting — komplett, uten ekstern avhengighet + +## Konsept + +Synops hoster podcast selv — ingen castopod, ingen ekstern +tjeneste. Podcasten er noder med riktige edges og en RSS-feed +med riktige tags. Vi har 80% allerede. + +## Hva vi har + +| Funksjon | Status | Komponent | +|----------|--------|-----------| +| RSS-feed | ✓ | synops-rss | +| Mediefiler med byte-range | ✓ | CAS + Caddy | +| HTML-sider per episode | ✓ | synops-render + Tera | +| Metadata | ✓ | Noder med edges | +| Transkripsjoner | ✓ | synops-transcribe + segmenter | +| Kapitler | ✓ | chapter-edges | +| Show notes | ✓ | show_notes-edge | +| Lydprosessering | ✓ | synops-audio | + +## Hva vi mangler + +### 1. Podcast-spesifikke RSS-tags + +Utvid synops-rss med iTunes og Podcasting 2.0 namespace: + +```xml +Sidelinja + +false + +no + + +``` + +Metadata fra samlingens podcast-trait: + +```jsonc +{ + "traits": { + "podcast": { + "itunes_category": "News & Politics", + "itunes_author": "Sidelinja", + "explicit": false, + "language": "no", + "redirect_feed": null + } + } +} +``` + +### 2. Nedlastingsstatistikk + +Caddy logger allerede alle requests. `synops-stats` parser +loggene og aggregerer: + +```bash +synops-stats --collection-id --period 30d + → Nedlastinger per episode per dag + → Unike lyttere (IP + user-agent per 24t, IAB-regler) + → Geografi (fra IP, valgfritt) + → Klienter (Apple Podcasts, Spotify, etc. fra user-agent) +``` + +Lagres i PG — visbart i admin-dashboard. + +### 3. Embed-spiller + +Liten Svelte-komponent for å embedde episoder på nettsider: + +```html + +``` + +Viser: artwork, tittel, play/pause, progress, waveform, +kapittelmerkering. Responsivt. Fungerer uten JavaScript +(fallback til lydfil-lenke). + +### 4. Katalog-distribusjon + +Apple Podcasts, Spotify, Google Podcasts trenger bare +RSS-URL-en. Submit én gang manuelt, de poller feeden. + +Admin-UI: felt for å lime inn RSS-URL og se status per +katalog (submitted/live/pending). + +## Import fra annen host + +### `synops-import-podcast` + +CLI-verktøy som importerer en eksisterende podcast fra +RSS-feed: + +```bash +synops-import-podcast --feed-url https://gammel-host.no/feed.xml \ + --collection-id \ + [--dry-run] \ + [--write] +``` + +For hver episode: +1. Parse metadata fra RSS (tittel, beskrivelse, pubDate, etc.) +2. Last ned MP3 → CAS (deduplisering gratis) +3. Last ned artwork → CAS +4. Last ned transkripsjon/kapitler hvis tilgjengelig +5. Opprett content-node med all metadata +6. Opprett media-node + has_media-edge +7. Opprett belongs_to-edge → samling +8. Sett published_at fra pubDate + +Podcast-metadata (author, category, artwork) → samlingens +podcast-trait. + +### Prøveimport-flyten + +Import er ikke alt-eller-ingenting. Den støtter en gradvis +migrasjon: + +``` +Uke 1: Prøveimport + synops-import-podcast --feed-url https://gammel.no/feed.xml \ + --collection-id --write + → Alle episoder importert + → Sjekk at alt ser riktig ut i Synops + → Prøvepubliser RSS-feed: synops.no/pub/sidelinja/feed.xml + → Sammenlign med original feed + → Hør på noen episoder, sjekk metadata + → Ikke fornøyd? Slett samlingen, start på nytt + +Uke 2: Test med lyttere + → Del den nye feed-URL-en med noen testlyttere + → Sjekk at spillere (Apple, Spotify) håndterer den + → Publiser en ny episode direkte i Synops + +Uke 3: Restimorter + cutover + synops-import-podcast --feed-url https://gammel.no/feed.xml \ + --collection-id --write + → Idempotent: eksisterende episoder skippes (duplikatdeteksjon via guid) + → Nye episoder siden sist importeres + → Slå på 301 redirect på gammel host + → Apple/Spotify oppdaterer automatisk innen noen dager + → Ferdig — podcasten lever nå i Synops +``` + +### Duplikatdeteksjon + +Import bruker `` fra RSS for å identifisere episoder. +Kjørt to ganger = ingen duplikater. Bare nye episoder +importeres. + +### Hva importeres + +| RSS-felt | Synops | +|----------|--------| +| `` | node.title | +| `<description>` | node.content | +| `<enclosure url>` | media-node i CAS | +| `<pubDate>` | metadata.published_at | +| `<itunes:duration>` | metadata.duration | +| `<itunes:episode>` | metadata.episode_number | +| `<itunes:season>` | metadata.season_number | +| `<itunes:image>` | media-node + og_image-edge | +| `<podcast:transcript>` | last ned → synops-transcribe parsing | +| `<podcast:chapters>` | chapter-edges | +| `<guid>` | metadata.guid (for duplikatdeteksjon) | + +## Eksport / flytte bort + +Brukeren eier dataene sine. Flytte bort er enkelt: + +```jsonc +// I podcast-trait: +{ + "redirect_feed": "https://ny-host.no/feed.xml" +} +``` + +Når satt: Caddy returnerer 301 for feed-URL. +Apple/Spotify oppdaterer automatisk. + +Brukeren kan også eksportere all data: +- RSS-feed med alle episoder +- Lydfiler fra CAS +- Transkripsjoner, kapitler, show notes + +Alt er noder → alt er eksporterbart. + +## Komponenter + +| Feature | Rolle | +|---------|-------| +| synops-rss | RSS-generering med iTunes/Podcasting 2.0 tags | +| synops-import-podcast | Import fra eksisterende RSS-feed | +| synops-stats | Nedlastingsstatistikk fra Caddy-logger | +| CAS + Caddy | Mediaserving med byte-range | +| synops-render | Episode-sider og embed-spiller | +| Admin-UI | Katalogstatus, statistikk, redirect-konfig | + +## Bygger på +- `docs/concepts/podcastfabrikken.md` — produksjonspipeline +- `docs/concepts/studioet.md` — innspilling +- `docs/proposals/podcasting_2_0.md` — Podcasting 2.0 tags +- `docs/features/lydstudio.md` — postproduksjon diff --git a/scripts/run-next-task.sh b/scripts/run-next-task.sh index 1310808..9d602a4 100755 --- a/scripts/run-next-task.sh +++ b/scripts/run-next-task.sh @@ -47,6 +47,7 @@ declare -A PHASE_DEPS=( [27]="26" [28]="27" [29]="28" + [30]="29" ) # --- Finn blokkerte faser --- diff --git a/tasks.md b/tasks.md index d7200cc..6ccb58a 100644 --- a/tasks.md +++ b/tasks.md @@ -50,6 +50,7 @@ Fase 25 → Fase 26 (epost) Fase 26 → Fase 27 (tankekart) Fase 27 → Fase 28 (manglende CLI + AI-ruting) Fase 28 → Fase 29 (universell input) +Fase 29 → Fase 30 (podcast-hosting) ``` Hvis en oppgave er `[?]` eller `[!]`, hoppes den over — og alle @@ -411,3 +412,27 @@ noden er det som lever videre. ### Kalender-import - [ ] 29.11 ICS-import: `synops-calendar` CLI som parser ICS-fil og oppretter noder med `scheduled`-edges. Input: `--file <ics> --collection-id <uuid>`. Duplikatdeteksjon via UID. Oppdatering ved re-import. - [ ] 29.12 CalDAV-abonnement: abonner på ekstern CalDAV-kalender (Google, Outlook). Poller periodisk, synkroniserer endringer. Som RSS-feed men for kalenderhendelser. + +## Fase 30: Podcast-hosting — komplett, uten ekstern avhengighet + +Ref: `docs/features/podcast_hosting.md`. Bygg komplett podcast-hosting i Synops. +Ingen castopod, ingen ekstern tjeneste. Import fra eksisterende podcast med +prøveimport-flyt. + +### RSS og metadata +- [ ] 30.1 iTunes/Podcasting 2.0 RSS-tags: utvid synops-rss med `<itunes:*>` og `<podcast:*>` namespace. Tags fra samlingens podcast-trait metadata (author, category, explicit, language). Podcast:transcript og podcast:chapters fra eksisterende edges. +- [ ] 30.2 Podcast-trait metadata: utvid podcast-trait med iTunes-felt (itunes_category, itunes_author, explicit, language, redirect_feed). Admin-UI for å redigere. + +### Statistikk +- [ ] 30.3 `synops-stats` CLI: parse Caddy access-logger for /media/*-requests. Aggreger nedlastinger per episode per dag. IAB-regler: filtrer bots (user-agent), unik IP per 24t. Output: JSON med episode_id, date, downloads, unique_listeners. `--write` lagrer i PG. +- [ ] 30.4 Statistikk-dashboard: vis nedlastinger per episode, trend over tid, topp-episoder, klienter (Apple/Spotify/andre), geografi. Integrert i admin-panelet. + +### Embed-spiller +- [ ] 30.5 Podcast-spiller komponent: Svelte-komponent med artwork, tittel, play/pause, progress, waveform, kapittelmerkering. Responsiv. Serveres som iframe-embed: `synops.no/pub/<slug>/<episode>/player`. + +### Import +- [ ] 30.6 `synops-import-podcast` CLI: importer eksisterende podcast fra RSS-feed. Parse metadata, last ned lydfiler/artwork/transkripsjoner til CAS, opprett noder med edges. Duplikatdeteksjon via `<guid>`. `--dry-run` for forhåndsvisning. Idempotent: kjør flere ganger, bare nye episoder importeres. +- [ ] 30.7 Prøveimport-flyt i frontend: "Importer podcast"-wizard i admin. Steg 1: lim inn RSS-URL, vis forhåndsvisning av episoder. Steg 2: importer (kan ta tid for mange episoder). Steg 3: sjekk resultat, sammenlign feeds. Steg 4: re-importer nye episoder når klar. Steg 5: aktiver 301-redirect på gammel host. + +### Eksport og redirect +- [ ] 30.8 Feed-redirect: `redirect_feed`-felt i podcast-trait. Når satt: Caddy returnerer 301 for feed-URL. Brukeren kan alltid flytte bort. Admin-UI med én-klikks aktivering og advarsel.