# 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
