with one click
podcast-recap
// Transcribe and summarize any podcast episode or YouTube video. Given a URL (YouTube, RSS feed, or direct audio), it fetches the transcript, extracts key takeaways with timestamps, and saves a structured markdown note.
// Transcribe and summarize any podcast episode or YouTube video. Given a URL (YouTube, RSS feed, or direct audio), it fetches the transcript, extracts key takeaways with timestamps, and saves a structured markdown note.
| name | podcast-recap |
| description | Transcribe and summarize any podcast episode or YouTube video. Given a URL (YouTube, RSS feed, or direct audio), it fetches the transcript, extracts key takeaways with timestamps, and saves a structured markdown note. |
| version | 1.0.0 |
| metadata | {"openclaw":{"emoji":"๐๏ธ","homepage":"https://github.com/openclaw/skills","requires":{"bins":["yt-dlp","ffmpeg"]},"anyBins":["whisper","python3"],"install":[{"kind":"brew","formula":"yt-dlp","bins":["yt-dlp"]},{"kind":"brew","formula":"ffmpeg","bins":["ffmpeg"]}],"envVars":[{"name":"PODCAST_NOTES_DIR","required":false,"description":"Directory where recap notes are saved. Defaults to ~/podcast-notes."}]}} |
Summarize a podcast episode or YouTube video from a URL. Produces a structured markdown note with key takeaways, chapter summaries, and notable quotes โ saved locally for future reference.
The user sends a message containing a URL and a request to recap or summarize it. Examples:
Inspect the URL to determine its type:
youtube.com/watch, youtu.be/, or youtube.com/shorts/.xml, .rss, contains /feed, or returns application/rss+xml or application/atom+xml on HEAD request.mp3, .m4a, .ogg, .wav, .opusUse yt-dlp to extract auto-generated or manual captions:
yt-dlp \
--skip-download \
--write-auto-sub \
--write-sub \
--sub-lang en \
--sub-format vtt \
--output "/tmp/podcast-recap/%(title)s.%(ext)s" \
"<URL>"
If captions are unavailable, fall back to downloading the audio and running Whisper (Step 2C).
Parse the .vtt file into plain text with timestamps:
# Strip VTT metadata and de-duplicate overlapping lines
grep -v '^WEBVTT\|^NOTE\|^[0-9].*-->' /tmp/podcast-recap/*.vtt \
| sed '/^$/d' \
| awk '!seen[$0]++' \
> /tmp/podcast-recap/transcript.txt
Also capture episode metadata (title, channel, duration, upload date):
yt-dlp --dump-json --no-download "<URL>" > /tmp/podcast-recap/meta.json
Extract with jq:
jq -r '{title, uploader, upload_date, duration, description}' /tmp/podcast-recap/meta.json
Fetch the feed XML:
curl -sL "<URL>" -o /tmp/podcast-recap/feed.xml
Extract the latest episode's enclosure URL (the audio file):
grep -oP '(?<=url=")[^"]+\.(mp3|m4a|ogg|opus)' /tmp/podcast-recap/feed.xml | head -1
Or, if the user specified an episode number/title, find the matching <item> block.
Download the audio and proceed to Step 2C.
Download the audio file:
curl -sL "<AUDIO_URL>" -o /tmp/podcast-recap/episode.mp3
Transcribe with Whisper (if installed):
whisper /tmp/podcast-recap/episode.mp3 \
--model small \
--output_format txt \
--output_dir /tmp/podcast-recap/
If Whisper is not installed, instruct the user:
"Whisper is not installed. Install it with:
pip install openai-whisper. I can still summarize from the episode description if you'd like." Then proceed using only the episode title and description.
Read the full transcript from /tmp/podcast-recap/transcript.txt.
Apply the summarization template in prompts/summarize.md.
Generate the following sections:
Determine the output directory:
NOTES_DIR="${PODCAST_NOTES_DIR:-$HOME/podcast-notes}"
mkdir -p "$NOTES_DIR"
Generate a filename from the episode title:
# Sanitize title: lowercase, replace spaces/special chars with hyphens
TITLE_SLUG=$(echo "<EPISODE_TITLE>" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')
DATE=$(date +%Y-%m-%d)
OUTPUT_FILE="$NOTES_DIR/${DATE}-${TITLE_SLUG}.md"
Write the structured markdown note to $OUTPUT_FILE using the template below.
# <Episode Title>
> **Source**: <URL>
> **Published**: <Upload date or feed date>
> **Show / Channel**: <Podcast name or YouTube channel>
> **Duration**: <HH:MM:SS>
> **Recap generated**: <today's date>
---
## TL;DR
<2โ3 sentence summary>
---
## Key Takeaways
- <takeaway 1>
- <takeaway 2>
- <takeaway 3>
- <takeaway 4>
- <takeaway 5>
---
## Chapter Summaries
### <Chapter 1 Title or "Part 1">
<paragraph>
### <Chapter 2 Title or "Part 2">
<paragraph>
...
---
## Notable Quotes
> "<quote>"
โ *<Speaker name or "Host">*, <approximate timestamp>
> "<quote>"
โ *<Speaker name>*, <approximate timestamp>
---
## Topics & Tags
`<tag1>` `<tag2>` `<tag3>` `<tag4>` `<tag5>`
---
*Generated by podcast-recap skill*
Send a message confirming the recap was saved:
๐๏ธ Podcast recap saved!
"" Show: <Podcast / Channel> ยท Duration:
TL;DR: <the TL;DR text>
Saved to:
<OUTPUT_FILE>Want me to read out the key takeaways or dive into any section?
| Situation | Response |
|---|---|
yt-dlp not installed | Tell the user: "Run brew install yt-dlp to enable YouTube support." |
| No captions available and Whisper not installed | Summarize from title + description only; flag the limitation |
| RSS feed has no enclosure URL | Ask the user to provide the direct audio URL |
| Transcript is very short (<200 words) | Warn the user: "Transcript appears incomplete โ summary may be limited." |
| Network error fetching URL | Report the error and suggest the user check the URL |
| Output directory not writable | Fall back to ~/Downloads/podcast-notes/ and inform the user |
/tmp/podcast-recap/ and can be cleaned up after the note is saved with rm -rf /tmp/podcast-recap/.small model without GPU.--model tiny for Whisper to keep transcription time reasonable.PODCAST_NOTES_DIR env var allows routing notes to an Obsidian vault or any synced folder.