Claude Skill

podcast-pipeline

Audio file → full publishing kit pipeline. From a single .mp3 or .wav per episode, generates Whisper-large-v3 transcript with word-level timestamps, automatic chapter detection (audio scene change + topic shift), two show-notes lengths (scannable summary + long-form SEO).

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download tinh2-skills-hub-registry-analysis_podcast-pipeline-d38affb.zip · 4 KB
Part of tinh2/skills-hub-registry — 176 skills

Install

skills CLI npx skills add https://github.com/tinh2/skills-hub-registry/tree/main/analysis/podcast-pipeline
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tinh2-skills-hub-registry@llmmart
Git git clone https://github.com/tinh2/skills-hub-registry.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole tinh2/skills-hub-registry collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Podcast Publishing Pipeline

You convert one raw audio file into a complete publishing kit: transcript, chapters, show notes (two formats), SEO blog post, social shorts, quote graphics, and social-thread drafts. Modern podcast SEO requires a per-episode webpage with full transcript + JSON-LD — without it, episodes are invisible to Google and AI Overviews.

============================================================ === PRE-FLIGHT ===

Verify:

  • Audio file accessible (mp3, wav, m4a, flac). Local file or accessible URL.
  • Episode metadata: number, title, guest(s), publish date, primary topics.
  • Transcription engine: Whisper-large-v3 (open source, run locally with GPU/MLX), Deepgram Nova-3 (API, fastest), AssemblyAI Universal-2, OpenAI Whisper API, or Replicate.
  • Brand kit: logo, color palette, hosts' photos (for quote graphics + audiogram).
  • CMS / publish target: WordPress, Ghost, Squarespace, Substack, custom static site.
  • Episode duration: ≤ 90 min runs end-to-end in ~10 min on a Whisper-large GPU; 3+ hours needs chunking.

Recovery:

  • If audio quality is poor (heavy background noise), pre-process with ffmpeg afftdn / RNNoise before transcription.
  • If no GPU available, route to Deepgram or OpenAI Whisper API and surface the per-minute cost.
  • For multi-host episodes, prefer Whisper-large + pyannote.audio diarization to get speaker labels right.

============================================================ === PHASE 1: TRANSCRIPTION ===

Generate transcript with word-level timestamps and speaker labels.

Whisper-large-v3 (preferred, local):

import whisperx  # whisperx adds word-level alignment + diarization

model = whisperx.load_model("large-v3", device="cuda", compute_type="float16")
audio = whisperx.load_audio(episode_path)
result = model.transcribe(audio, batch_size=16, language="en")

# Word-level alignment
align_model, metadata = whisperx.load_align_model(language_code=result["language"], device="cuda")
result = whisperx.align(result["segments"], align_model, metadata, audio, "cuda")

# Diarization (pyannote.audio)
diarize_model = whisperx.DiarizationPipeline(use_auth_token=HF_TOKEN, device="cuda")
diarize_segments = diarize_model(audio, num_speakers=NUM_SPEAKERS)
result = whisperx.assign_word_speakers(diarize_segments, result)

Persist as transcript.json with schema:

{
  "segments": [
    {"start": 0.0, "end": 4.2, "speaker": "SPEAKER_00", "text": "Welcome to the show.", "words": [...]},
    ...
  ],
  "metadata": {"language": "en", "duration_s": 3245.6}
}

Also export transcript.srt (SubRip) and transcript.vtt (WebVTT) for video editing / web players.

VALIDATION: Transcript word count is non-trivial (≥ duration_min × 100, since typical speech is 130-150 wpm). Speakers labeled if > 1 voice in audio.

============================================================ === PHASE 2: CHAPTER DETECTION ===

Generate chapter markers in three ways and merge:

  1. Acoustic scene change: detect ≥ 2s silence + speaker change.
  2. Topic shift: embed each 60-second window with all-MiniLM-L6-v2 or text-embedding-3-small; cosine-distance peaks = chapter boundary.
  3. LLM pass: ask Claude / Gemini to read the transcript and propose 5-10 chapter titles with timestamps.

Reconcile the three into 5-12 chapters. Each chapter:

00:00 — Cold open
01:23 — Introducing today's guest
04:15 — How {topic} broke open
12:47 — The {key insight}
...

Embed as ID3 chapter markers in the mp3 file (for podcast players that support chapters: Apple Podcasts, Overcast, Pocket Casts, Spotify).

VALIDATION: Chapter count 5-12. First chapter starts at 00:00. Title ≤ 60 chars each.

============================================================ === PHASE 3: SHOW NOTES — TWO FORMATS ===

Format A — Scannable (≤ 400 words, for podcast player descriptions and listen-page above-fold):

{Episode title}

{Guest}, {their title at their company}, joins us to discuss {three sentence hook}.

In this episode, we cover:

- {Beat 1 — verb-led}
- {Beat 2}
- {Beat 3}
- {Beat 4}
- {Beat 5}

## Resources mentioned

- {Book / article / tool}
- {...}

## Find {guest}

- {Twitter / X}
- {LinkedIn}
- {Their company / project}

## Chapters

{from Phase 2}

Format B — Long-form SEO (1500-2500 words for the episode webpage):

  • Hook lead (60-80 words answering the episode's core question — AI Overview target).
  • Full chapter-by-chapter summary with timestamp anchors.
  • Quotes block: 3-5 verbatim quotes with attribution.
  • Full transcript appended (or linked).
  • "Listen to " outbound links to Spotify, Apple Podcasts, Overcast, YouTube.
  • Author byline + episode date (E-E-A-T signal).

VALIDATION: Format A respects 400-word cap. Format B has lead in first 80 words + full chapter coverage.

============================================================ === PHASE 4: SEO BLOG POST ===

Generate blog_post.md (~600-1000 words) — a derivative article driving organic traffic. Structure:

  1. H1 — keyword-optimized title (different from episode title; targets a Google query).
  2. TL;DR — 50-word answer to the H1 (AEO target).
  3. Background — 1-2 paragraphs framing the topic.
  4. Key insights from the episode — 3-5 H2 sections, each anchored to a chapter.
  5. Quote callouts — 2-3 pull-quotes inline.
  6. Listen to the full episode — embed player + listen-on-platform links.

Plus blog_jsonld.json:

{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "...",
  "datePublished": "...",
  "author": { "@type": "Person", "name": "..." },
  "image": "...",
  "mainEntityOfPage": "...",
  "associatedMedia": {
    "@type": "PodcastEpisode",
    "name": "...",
    "url": "...",
    "associatedMedia": { "@type": "MediaObject", "contentUrl": "{mp3 url}" },
    "partOfSeries": { "@type": "PodcastSeries", "name": "...", "url": "..." }
  }
}

VALIDATION: JSON-LD validates via Rich Results Test. Blog targets a different query than episode title.

============================================================ === PHASE 5: SOCIAL SHORTS (60-SECOND VERTICAL CLIPS) ===

For each chapter or quote, generate a short:

  1. Pick the 3-5 highest-engagement moments — heuristic: longest applause/laugh pattern, sharpest answer to a question, or a quote-shaped sentence the speaker leaned in on.
  2. Trim to ≤ 60s via ffmpeg -ss start -to end.
  3. Re-encode vertical (9:16, 1080×1920) with auto-crop centered on speaker or static brand-blur background.
  4. Burn captions from word-level transcript (highlighted-word style, ~3-4 words at a time, branded color).
  5. Add intro card (1.5s with episode title + handle) and outro card (1.5s "Full episode → ").
  6. Export as shorts/short_{n}.mp4 ready for direct upload.

Generate captions per platform:

  • TikTok: caption + 3-5 hashtags + handle.
  • Instagram Reels: caption + hashtags.
  • YouTube Shorts: caption + #Shorts tag.

VALIDATION: Each short is ≤ 60s. Captions burned and word-synced. Vertical aspect ratio confirmed.

============================================================ === PHASE 6: QUOTE GRAPHICS ===

For each of the 3-5 best quotes, generate a 1080×1080 image (Instagram-grid friendly):

  • Background: brand color or photo blur.
  • Foreground: large quote text (40-60pt), attribution ("— , "), small show logo.
  • Output PNG via Pillow or playwright + HTML template + screenshot.

Each graphic also gets a paired caption file with one-tap social copy.

VALIDATION: Quote text is verbatim from transcript. Attribution correct. Image dimensions exact.

============================================================ === PHASE 7: SOCIAL THREAD DRAFTS ===

Generate X/LinkedIn/Threads multi-post drafts:

  • Post 1 (hook): provocative claim from episode + episode link.
  • Posts 2-6 (insights): one beat per post, ≤ 280 chars X / ~1200 chars LinkedIn.
  • Post 7 (CTA): "Listen to the full episode → ".

For LinkedIn, longer single-post format (~1500 chars) is more native than multi-post threads — generate both.

VALIDATION: Character limits respected per platform.

============================================================ === PHASE 8: PACKAGE & PUBLISH ===

Final delivery:

episode-{slug}/
├── README.md                    # where each file goes
├── transcript.json
├── transcript.srt
├── transcript.vtt
├── chapters.txt
├── show_notes_short.md
├── show_notes_long.md
├── blog_post.md
├── blog_jsonld.json
├── episode_page.html            # ready to drop into CMS
├── shorts/
│   ├── short_1.mp4 (with caption.txt + hashtags.txt)
│   └── ...
├── graphics/
│   ├── quote_1.png (with caption.txt)
│   └── ...
└── socials/
    ├── x_thread.md
    ├── linkedin_post.md
    └── threads_post.md

VALIDATION: Every artifact present. README explains where each goes per CMS (WordPress media library, Ghost post HTML body, Substack import).

============================================================ === SELF-REVIEW ===

Score 1–5:

  • Complete: All 8 phases produced artifacts?
  • Robust: Long episodes chunked correctly? Diarization assigned speakers?
  • Clean: Transcript word count plausible? Chapters at sensible boundaries?
  • Publishing-credible: Would a podcast producer running 100+ episodes accept this output as "drop-in ready"?

Common gap: trimming shorts at awkward mid-sentence boundaries. Verify start/end snap to sentence boundaries from word-level timestamps.

============================================================ === LEARNINGS CAPTURE ===

Append to ~/.claude/skills/podcast-pipeline/LEARNINGS.md:

  • What worked:
  • What was awkward:
  • Suggested patch:
  • Verdict: [Smooth / Minor friction / Major friction]

============================================================ === STRICT RULES ===

  • Never publish without a transcript. Episodes without transcripts are invisible to Google + AI search and inaccessible to deaf/HoH listeners.
  • Never auto-generate quotes that misattribute. Always pull verbatim with speaker label from diarization.
  • Never trim shorts mid-word or mid-sentence. Snap to word boundaries from Whisper alignment.
  • Always include JSON-LD PodcastEpisode + BlogPosting. Schema is load-bearing for AI Overview citations.
  • Always upload the audio's first 10 seconds intact (player previews; bad opening = drop-off).
Files (skills-hub-registry)
  • SKILL.md 11.5 KB
    ---
    name: podcast-pipeline
    description: "Audio file → full publishing kit pipeline. From a single .mp3 or .wav per episode, generates Whisper-large-v3 transcript with word-level timestamps, automatic chapter detection (audio scene change + topic shift), two show-notes lengths (scannable summary + long-form SEO)."
    version: "1.0.1"
    category: analysis
    platforms:
      - CLAUDE_CODE
    ---
    
    # Podcast Publishing Pipeline
    
    You convert one raw audio file into a complete publishing kit: transcript, chapters, show notes (two formats), SEO blog post, social shorts, quote graphics, and social-thread drafts. Modern podcast SEO requires a per-episode webpage with full transcript + JSON-LD — without it, episodes are invisible to Google and AI Overviews.
    
    ============================================================
    === PRE-FLIGHT ===
    ============================================================
    
    Verify:
    
    - [ ] **Audio file accessible** (mp3, wav, m4a, flac). Local file or accessible URL.
    - [ ] **Episode metadata**: number, title, guest(s), publish date, primary topics.
    - [ ] **Transcription engine**: Whisper-large-v3 (open source, run locally with GPU/MLX), Deepgram Nova-3 (API, fastest), AssemblyAI Universal-2, OpenAI Whisper API, or Replicate.
    - [ ] **Brand kit**: logo, color palette, hosts' photos (for quote graphics + audiogram).
    - [ ] **CMS / publish target**: WordPress, Ghost, Squarespace, Substack, custom static site.
    - [ ] **Episode duration**: ≤ 90 min runs end-to-end in ~10 min on a Whisper-large GPU; 3+ hours needs chunking.
    
    Recovery:
    
    - If audio quality is poor (heavy background noise), pre-process with `ffmpeg` afftdn / RNNoise before transcription.
    - If no GPU available, route to Deepgram or OpenAI Whisper API and surface the per-minute cost.
    - For multi-host episodes, prefer Whisper-large + pyannote.audio diarization to get speaker labels right.
    
    ============================================================
    === PHASE 1: TRANSCRIPTION ===
    ============================================================
    
    Generate transcript with word-level timestamps and speaker labels.
    
    **Whisper-large-v3** (preferred, local):
    
    ```python
    import whisperx  # whisperx adds word-level alignment + diarization
    
    model = whisperx.load_model("large-v3", device="cuda", compute_type="float16")
    audio = whisperx.load_audio(episode_path)
    result = model.transcribe(audio, batch_size=16, language="en")
    
    # Word-level alignment
    align_model, metadata = whisperx.load_align_model(language_code=result["language"], device="cuda")
    result = whisperx.align(result["segments"], align_model, metadata, audio, "cuda")
    
    # Diarization (pyannote.audio)
    diarize_model = whisperx.DiarizationPipeline(use_auth_token=HF_TOKEN, device="cuda")
    diarize_segments = diarize_model(audio, num_speakers=NUM_SPEAKERS)
    result = whisperx.assign_word_speakers(diarize_segments, result)
    ```
    
    Persist as `transcript.json` with schema:
    
    ```json
    {
      "segments": [
        {"start": 0.0, "end": 4.2, "speaker": "SPEAKER_00", "text": "Welcome to the show.", "words": [...]},
        ...
      ],
      "metadata": {"language": "en", "duration_s": 3245.6}
    }
    ```
    
    Also export `transcript.srt` (SubRip) and `transcript.vtt` (WebVTT) for video editing / web players.
    
    VALIDATION: Transcript word count is non-trivial (≥ duration_min × 100, since typical speech is 130-150 wpm). Speakers labeled if > 1 voice in audio.
    
    ============================================================
    === PHASE 2: CHAPTER DETECTION ===
    ============================================================
    
    Generate chapter markers in three ways and merge:
    
    1. **Acoustic scene change**: detect ≥ 2s silence + speaker change.
    2. **Topic shift**: embed each 60-second window with `all-MiniLM-L6-v2` or `text-embedding-3-small`; cosine-distance peaks = chapter boundary.
    3. **LLM pass**: ask Claude / Gemini to read the transcript and propose 5-10 chapter titles with timestamps.
    
    Reconcile the three into 5-12 chapters. Each chapter:
    
    ```
    00:00 — Cold open
    01:23 — Introducing today's guest
    04:15 — How {topic} broke open
    12:47 — The {key insight}
    ...
    ```
    
    Embed as ID3 chapter markers in the mp3 file (for podcast players that support chapters: Apple Podcasts, Overcast, Pocket Casts, Spotify).
    
    VALIDATION: Chapter count 5-12. First chapter starts at 00:00. Title ≤ 60 chars each.
    
    ============================================================
    === PHASE 3: SHOW NOTES — TWO FORMATS ===
    ============================================================
    
    **Format A — Scannable** (≤ 400 words, for podcast player descriptions and listen-page above-fold):
    
    ```markdown
    {Episode title}
    
    {Guest}, {their title at their company}, joins us to discuss {three sentence hook}.
    
    In this episode, we cover:
    
    - {Beat 1 — verb-led}
    - {Beat 2}
    - {Beat 3}
    - {Beat 4}
    - {Beat 5}
    
    ## Resources mentioned
    
    - {Book / article / tool}
    - {...}
    
    ## Find {guest}
    
    - {Twitter / X}
    - {LinkedIn}
    - {Their company / project}
    
    ## Chapters
    
    {from Phase 2}
    ```
    
    **Format B — Long-form SEO** (1500-2500 words for the episode webpage):
    
    - Hook lead (60-80 words answering the episode's core question — AI Overview target).
    - Full chapter-by-chapter summary with timestamp anchors.
    - Quotes block: 3-5 verbatim quotes with attribution.
    - Full transcript appended (or linked).
    - "Listen to {episode title}" outbound links to Spotify, Apple Podcasts, Overcast, YouTube.
    - Author byline + episode date (E-E-A-T signal).
    
    VALIDATION: Format A respects 400-word cap. Format B has lead in first 80 words + full chapter coverage.
    
    ============================================================
    === PHASE 4: SEO BLOG POST ===
    ============================================================
    
    Generate `blog_post.md` (~600-1000 words) — a derivative article driving organic traffic. Structure:
    
    1. **H1** — keyword-optimized title (different from episode title; targets a Google query).
    2. **TL;DR** — 50-word answer to the H1 (AEO target).
    3. **Background** — 1-2 paragraphs framing the topic.
    4. **Key insights from the episode** — 3-5 H2 sections, each anchored to a chapter.
    5. **Quote callouts** — 2-3 pull-quotes inline.
    6. **Listen to the full episode** — embed player + listen-on-platform links.
    
    Plus `blog_jsonld.json`:
    
    ```json
    {
      "@context": "https://schema.org",
      "@type": "BlogPosting",
      "headline": "...",
      "datePublished": "...",
      "author": { "@type": "Person", "name": "..." },
      "image": "...",
      "mainEntityOfPage": "...",
      "associatedMedia": {
        "@type": "PodcastEpisode",
        "name": "...",
        "url": "...",
        "associatedMedia": { "@type": "MediaObject", "contentUrl": "{mp3 url}" },
        "partOfSeries": { "@type": "PodcastSeries", "name": "...", "url": "..." }
      }
    }
    ```
    
    VALIDATION: JSON-LD validates via Rich Results Test. Blog targets a different query than episode title.
    
    ============================================================
    === PHASE 5: SOCIAL SHORTS (60-SECOND VERTICAL CLIPS) ===
    ============================================================
    
    For each chapter or quote, generate a short:
    
    1. **Pick the 3-5 highest-engagement moments** — heuristic: longest applause/laugh pattern, sharpest answer to a question, or a quote-shaped sentence the speaker leaned in on.
    2. **Trim to ≤ 60s** via `ffmpeg -ss start -to end`.
    3. **Re-encode vertical** (9:16, 1080×1920) with auto-crop centered on speaker or static brand-blur background.
    4. **Burn captions** from word-level transcript (highlighted-word style, ~3-4 words at a time, branded color).
    5. **Add intro card** (1.5s with episode title + handle) and outro card (1.5s "Full episode → {link}").
    6. **Export** as `shorts/short_{n}.mp4` ready for direct upload.
    
    Generate captions per platform:
    
    - TikTok: caption + 3-5 hashtags + handle.
    - Instagram Reels: caption + hashtags.
    - YouTube Shorts: caption + #Shorts tag.
    
    VALIDATION: Each short is ≤ 60s. Captions burned and word-synced. Vertical aspect ratio confirmed.
    
    ============================================================
    === PHASE 6: QUOTE GRAPHICS ===
    ============================================================
    
    For each of the 3-5 best quotes, generate a 1080×1080 image (Instagram-grid friendly):
    
    - Background: brand color or photo blur.
    - Foreground: large quote text (40-60pt), attribution ("— {guest}, {episode N}"), small show logo.
    - Output PNG via `Pillow` or `playwright` + HTML template + screenshot.
    
    Each graphic also gets a paired caption file with one-tap social copy.
    
    VALIDATION: Quote text is verbatim from transcript. Attribution correct. Image dimensions exact.
    
    ============================================================
    === PHASE 7: SOCIAL THREAD DRAFTS ===
    ============================================================
    
    Generate X/LinkedIn/Threads multi-post drafts:
    
    - **Post 1 (hook)**: provocative claim from episode + episode link.
    - **Posts 2-6 (insights)**: one beat per post, ≤ 280 chars X / ~1200 chars LinkedIn.
    - **Post 7 (CTA)**: "Listen to the full episode → {link}".
    
    For LinkedIn, longer single-post format (~1500 chars) is more native than multi-post threads — generate both.
    
    VALIDATION: Character limits respected per platform.
    
    ============================================================
    === PHASE 8: PACKAGE & PUBLISH ===
    ============================================================
    
    Final delivery:
    
    ```
    episode-{slug}/
    ├── README.md                    # where each file goes
    ├── transcript.json
    ├── transcript.srt
    ├── transcript.vtt
    ├── chapters.txt
    ├── show_notes_short.md
    ├── show_notes_long.md
    ├── blog_post.md
    ├── blog_jsonld.json
    ├── episode_page.html            # ready to drop into CMS
    ├── shorts/
    │   ├── short_1.mp4 (with caption.txt + hashtags.txt)
    │   └── ...
    ├── graphics/
    │   ├── quote_1.png (with caption.txt)
    │   └── ...
    └── socials/
        ├── x_thread.md
        ├── linkedin_post.md
        └── threads_post.md
    ```
    
    VALIDATION: Every artifact present. README explains where each goes per CMS (WordPress media library, Ghost post HTML body, Substack import).
    
    ============================================================
    === SELF-REVIEW ===
    ============================================================
    
    Score 1–5:
    
    - **Complete**: All 8 phases produced artifacts?
    - **Robust**: Long episodes chunked correctly? Diarization assigned speakers?
    - **Clean**: Transcript word count plausible? Chapters at sensible boundaries?
    - **Publishing-credible**: Would a podcast producer running 100+ episodes accept this output as "drop-in ready"?
    
    Common gap: trimming shorts at awkward mid-sentence boundaries. Verify start/end snap to sentence boundaries from word-level timestamps.
    
    ============================================================
    === LEARNINGS CAPTURE ===
    ============================================================
    
    Append to `~/.claude/skills/podcast-pipeline/LEARNINGS.md`:
    
    ## <YYYY-MM-DD> — <episode length, host count, content type>
    
    - **What worked:**
    - **What was awkward:**
    - **Suggested patch:**
    - **Verdict:** [Smooth / Minor friction / Major friction]
    
    ============================================================
    === STRICT RULES ===
    ============================================================
    
    - Never publish without a transcript. Episodes without transcripts are invisible to Google + AI search and inaccessible to deaf/HoH listeners.
    - Never auto-generate quotes that misattribute. Always pull verbatim with speaker label from diarization.
    - Never trim shorts mid-word or mid-sentence. Snap to word boundaries from Whisper alignment.
    - Always include JSON-LD PodcastEpisode + BlogPosting. Schema is load-bearing for AI Overview citations.
    - Always upload the audio's first 10 seconds intact (player previews; bad opening = drop-off).
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related