Når redirect_feed er satt i podcast-trait, returnerer maskinrommet HTTP 301 Moved Permanently med Location-header i stedet for å serve feeden. iTunes new-feed-url-taggen bevares også i RSS-en for klienter som ikke følger 301. Admin-UI: erstatter det enkle tekstfeltet med tre tilstander: - Inaktiv: knapp "Flytt podcast til annen plattform..." - Bekreftelse: advarsel + URL-felt + rød "Aktiver redirect"-knapp - Aktiv: gul statusindikator med deaktiver-knapp Backend: sjekker redirect_feed tidlig i generate_feed() og returnerer 301 før noe annet arbeid gjøres (DB-oppslag for episodes osv).
235 lines
7.2 KiB
Markdown
235 lines
7.2 KiB
Markdown
# 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~~ ✓
|
|
|
|
Implementert i synops-rss og maskinrommet/src/rss.rs. Begge genererer nå:
|
|
|
|
**Channel-level:** `itunes:author`, `itunes:category`, `itunes:explicit`,
|
|
`itunes:image` (fra og_image-edge), `itunes:type`, `podcast:locked`.
|
|
|
|
**Item-level:** `itunes:title`, `itunes:duration`, `itunes:explicit`,
|
|
`itunes:image` (episode-bilde), `podcast:transcript` (SRT fra
|
|
transcription_segments), `podcast:chapters` (JSON fra chapter-edges).
|
|
|
|
Metadata leses fra samlingens podcast-trait:
|
|
|
|
```jsonc
|
|
{
|
|
"traits": {
|
|
"podcast": {
|
|
"itunes_category": "News & Politics",
|
|
"itunes_author": "Sidelinja",
|
|
"explicit": false,
|
|
"language": "no",
|
|
"redirect_feed": null
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Merk:** Transcript- og chapters-URL-ene (`/{short_id}/transcript.srt`,
|
|
`/{short_id}/chapters.json`) krever at offentlige endepunkt legges til i
|
|
maskinrommet for å servere disse. De genereres i feeden, men serveres ikke ennå.
|
|
|
|
### 2. Nedlastingsstatistikk
|
|
|
|
Caddy logger allerede alle requests. `synops-stats` parser
|
|
loggene og aggregerer:
|
|
|
|
```bash
|
|
synops-stats --collection-id <uuid> --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
|
|
<iframe src="https://synops.no/pub/sidelinja/ep42/player"
|
|
width="100%" height="180" frameborder="0"></iframe>
|
|
```
|
|
|
|
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 <uuid> \
|
|
[--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 <uuid> --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 <uuid> --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 `<guid>` fra RSS for å identifisere episoder.
|
|
Kjørt to ganger = ingen duplikater. Bare nye episoder
|
|
importeres.
|
|
|
|
### Hva importeres
|
|
|
|
| RSS-felt | Synops |
|
|
|----------|--------|
|
|
| `<title>` | 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) |
|
|
|
|
## Erstatning av lydfiler (re-publisering)
|
|
|
|
Når en episode re-publiseres med ny lydfil (f.eks. etter
|
|
redigering i lydstudioet):
|
|
|
|
```
|
|
1. Opprett ny media-node i CAS
|
|
2. Opprett derived_from-edge (ny → gammel)
|
|
3. Flytt has_media-edge fra gammel fil → ny fil
|
|
4. Gammel fil: ingen aktive edges → pruning-kandidat
|
|
5. Grace period: 30 dager (konfigurerbart)
|
|
→ RSS-cacher hos Apple/Spotify trenger tid til å oppdatere
|
|
6. Etter grace period: gammel fil prunes fra CAS
|
|
```
|
|
|
|
Den gamle filen har `derived_from`-edge innover (ny peker
|
|
på gammel) men ingen `has_media`-edge utover. Den er
|
|
historikk, ikke aktiv innhold.
|
|
|
|
Noden lever videre som tombstone — metadata bevares,
|
|
binærfilen slettes fra disk. Historikken er sporbar via
|
|
`derived_from`-kjeden.
|
|
|
|
## 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: maskinrommet returnerer HTTP 301 Moved Permanently for
|
|
`/pub/{slug}/feed.xml` med `Location`-header til ny URL.
|
|
Apple/Spotify oppdaterer automatisk. I tillegg inkluderes
|
|
`<itunes:new-feed-url>` i RSS-en for klienter som ikke følger 301.
|
|
|
|
Admin-UI har én-klikks aktivering med advarsel. Redirecten kan
|
|
deaktiveres når som helst fra podcast-trait-innstillingene.
|
|
|
|
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
|