Initial: forked from runesleo/x-reader (MIT License) - thank you @runes_leo!
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Content Analyzer Skill
|
||||
|
||||
> Any content → structured analysis report with actionable insights
|
||||
|
||||
## Trigger
|
||||
|
||||
When user sends content (URL, text, or transcript) with analysis intent:
|
||||
- `/analyze [URL]`
|
||||
- "Analyze this article"
|
||||
- "What are the key takeaways?"
|
||||
- Auto-triggered after video/podcast transcription (from video skill)
|
||||
|
||||
## Pipeline
|
||||
|
||||
### Step 1: Get Content
|
||||
|
||||
Choose tool based on input type:
|
||||
|
||||
| Input | Tool |
|
||||
|-------|------|
|
||||
| Tweet URL | `fetch_tweet` or Jina Reader |
|
||||
| Web URL | `WebFetch` or Jina Reader |
|
||||
| Local file | Read file directly |
|
||||
| Transcript from video skill | Use directly |
|
||||
|
||||
### Step 2: Multi-Dimensional Analysis
|
||||
|
||||
Scan content across these dimensions. Only output dimensions with actual content — skip empty ones.
|
||||
|
||||
```markdown
|
||||
## 📖 Summary
|
||||
|
||||
[1-3 sentence core thesis]
|
||||
|
||||
**Source**: [author/publisher] · [date]
|
||||
**Type**: [tweet/article/video/podcast/report]
|
||||
|
||||
---
|
||||
|
||||
## 💡 Key Insights
|
||||
|
||||
### 🎯 Core Arguments
|
||||
- **Thesis**: [Main argument or finding]
|
||||
- **Evidence**: [Supporting data or reasoning]
|
||||
- **Strength**: [How convincing? What's missing?]
|
||||
|
||||
### 🤖 Tools & Methods
|
||||
- **What**: [Tools, frameworks, or techniques mentioned]
|
||||
- **How**: [How they're used or applied]
|
||||
- **Relevance**: [Could you use this?]
|
||||
|
||||
### ⚙️ Workflow Ideas
|
||||
- **Optimization**: [Process improvements mentioned]
|
||||
- **Automation**: [What could be automated]
|
||||
- **Integration**: [How to fit into existing workflow]
|
||||
|
||||
### 📊 Data & Numbers
|
||||
- **Key metrics**: [Important numbers mentioned]
|
||||
- **Trends**: [Patterns in the data]
|
||||
- **Gaps**: [What data is missing]
|
||||
|
||||
### ⚠️ Risks & Warnings
|
||||
- **Pitfalls**: [Explicitly mentioned risks]
|
||||
- **Blind spots**: [What the author might be missing]
|
||||
- **Counter-arguments**: [Alternative perspectives]
|
||||
|
||||
### 🔗 Resources
|
||||
- **Tools/APIs**: [Mentioned tools or data sources]
|
||||
- **People**: [Worth following or referencing]
|
||||
- **Further reading**: [Related content]
|
||||
|
||||
### 🧠 Mental Model Shifts
|
||||
- **Before**: [Common assumption]
|
||||
- **After**: [New understanding from this content]
|
||||
- **Impact**: [How this changes decisions]
|
||||
|
||||
---
|
||||
|
||||
## ✅ Action Items
|
||||
|
||||
### Quick Wins (< 30 min)
|
||||
- [ ] [Action 1] — Impact: ★★★★ | Effort: Easy
|
||||
- [ ] [Action 2] — Impact: ★★★ | Effort: Easy
|
||||
|
||||
### Deeper Work (1-3 hours)
|
||||
- [ ] [Action 3] — Impact: ★★★ | Effort: Medium
|
||||
- [ ] [Action 4] — Impact: ★★ | Effort: Medium
|
||||
|
||||
### Exploration (needs validation)
|
||||
- [ ] [Action 5] — Impact: ★★★ | Effort: Hard | Nature: Exploratory
|
||||
```
|
||||
|
||||
### Step 3: Personalized Relevance (Customizable)
|
||||
|
||||
Map insights to YOUR context. Edit the dimensions below to match your own projects, interests, and systems.
|
||||
|
||||
```markdown
|
||||
## 🔄 How This Applies to Me
|
||||
|
||||
### My Projects
|
||||
- **[Project A]**: [How this insight connects]
|
||||
- **[Project B]**: [What I could apply]
|
||||
|
||||
### My Knowledge Base
|
||||
- **Update**: [Which notes/docs to update]
|
||||
- **New entry**: [What to add to my knowledge system]
|
||||
|
||||
### My Decision Log
|
||||
- **Changed my mind about**: [what and why]
|
||||
- **Confirmed my belief that**: [what]
|
||||
```
|
||||
|
||||
> **Customization**: Edit the dimensions in Step 2 and Step 3 to match your own
|
||||
> domain. A trader might add "Market Impact" and "Risk Assessment". A developer
|
||||
> might add "Architecture Patterns" and "Tech Debt". Make it yours.
|
||||
|
||||
## Output Modes
|
||||
|
||||
| Mode | Trigger | Output |
|
||||
|------|---------|--------|
|
||||
| **Full** (default) | `/analyze [URL]` | All dimensions |
|
||||
| **Sparse** | `/analyze [URL] --sparse` | Only hit dimensions, skip empty |
|
||||
| **Brief** | `/analyze [URL] --brief` | Action items only |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Scan all dimensions, but don't force-fill** — skip empty dimensions cleanly
|
||||
2. **Actions must be specific** — not "learn about X" but "read X docs chapter Y"
|
||||
3. **Distinguish fact from opinion** — mark the author's claims vs verified facts
|
||||
4. **Source everything** — tag where each insight comes from in the original content
|
||||
5. **ROI awareness** — not every action is worth doing, assess effort vs impact
|
||||
@@ -0,0 +1,292 @@
|
||||
# Video & Podcast Digest Skill
|
||||
|
||||
> Send a video/podcast link → get full transcript + structured summary
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
| Platform | Type | Subtitles | Whisper Transcription |
|
||||
|----------|------|-----------|----------------------|
|
||||
| YouTube | Video | ✅ | ✅ |
|
||||
| Bilibili | Video | ✅ | ✅ |
|
||||
| X/Twitter | Video | ❌ | ✅ |
|
||||
| Xiaoyuzhou (小宇宙) | Podcast | ❌ | ✅ |
|
||||
| Apple Podcasts | Podcast | ❌ | ✅ |
|
||||
| Direct links (mp3/mp4/m3u8) | Any | ❌ | ✅ |
|
||||
|
||||
## Trigger
|
||||
|
||||
Auto-triggered when a media URL is detected:
|
||||
- YouTube: `youtube.com`, `youtu.be`
|
||||
- Bilibili: `bilibili.com`, `b23.tv`
|
||||
- X/Twitter: `x.com`, `twitter.com` (tweets with video)
|
||||
- Xiaoyuzhou: `xiaoyuzhoufm.com`
|
||||
- Apple Podcasts: `podcasts.apple.com`
|
||||
- Direct: `.mp3`, `.mp4`, `.m3u8`, `.m4a`, `.webm`
|
||||
|
||||
## Pipeline
|
||||
|
||||
### Step 0: Detect Media Type
|
||||
|
||||
| URL Pattern | Type | Pipeline |
|
||||
|-------------|------|----------|
|
||||
| `xiaoyuzhoufm.com/episode/` | Podcast | → Step 1b (Xiaoyuzhou) |
|
||||
| `podcasts.apple.com` | Podcast | → Step 1c (Apple) |
|
||||
| `bilibili.com`, `b23.tv` | Video | → Step 1d (Bilibili API) |
|
||||
| `.mp3`, `.m4a` direct link | Audio | → Step 2b (direct download) |
|
||||
| Other | Video | → Step 1a (subtitle extraction) |
|
||||
|
||||
### Step 1a: Video — Extract Subtitles
|
||||
|
||||
```bash
|
||||
# Clean up temp files
|
||||
rm -f /tmp/media_sub*.vtt /tmp/media_audio.mp3 /tmp/media_transcript*.json /tmp/media_segment_*.mp3 2>/dev/null || true
|
||||
|
||||
# YouTube (prefer English, fallback Chinese)
|
||||
yt-dlp --skip-download --write-auto-sub --sub-lang "en,zh-Hans" -o "/tmp/media_sub" "VIDEO_URL"
|
||||
|
||||
# Bilibili
|
||||
yt-dlp --skip-download --write-auto-sub --sub-lang "zh-Hans,zh" -o "/tmp/media_sub" "VIDEO_URL"
|
||||
```
|
||||
|
||||
Check for subtitles:
|
||||
```bash
|
||||
ls /tmp/media_sub*.vtt 2>/dev/null
|
||||
```
|
||||
- **Has subtitles** → Read VTT content, skip to Step 3
|
||||
- **No subtitles** → Step 2a (download audio)
|
||||
|
||||
### Step 1b: Xiaoyuzhou (小宇宙) — Extract Audio URL
|
||||
|
||||
```bash
|
||||
# Extract CDN direct link from __NEXT_DATA__
|
||||
# Xiaoyuzhou is a Next.js SPA, but initial HTML contains audio URL in __NEXT_DATA__
|
||||
AUDIO_URL=$(curl -sL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" \
|
||||
"EPISODE_URL" \
|
||||
| grep -oE 'https://media\.xyzcdn\.net/[^"]+\.(m4a|mp3)' \
|
||||
| head -1)
|
||||
|
||||
echo "Audio URL: $AUDIO_URL"
|
||||
|
||||
# Download audio
|
||||
curl -L -o /tmp/media_audio.mp3 "$AUDIO_URL"
|
||||
```
|
||||
|
||||
> If curl extraction is empty (rare), fallback: use Puppeteer/browser to get rendered page and extract.
|
||||
|
||||
→ Step 2b (check size & transcribe)
|
||||
|
||||
### Step 1c: Apple Podcasts — via yt-dlp
|
||||
|
||||
```bash
|
||||
yt-dlp -f "ba[ext=m4a]/ba/b" --extract-audio --audio-format mp3 --audio-quality 5 \
|
||||
-o "/tmp/media_audio.%(ext)s" "APPLE_PODCAST_URL"
|
||||
```
|
||||
|
||||
→ Step 2b (check size & transcribe)
|
||||
|
||||
### Step 1d: Bilibili — API Direct Audio Stream
|
||||
|
||||
yt-dlp returns 412 for Bilibili even with cookies. Use Bilibili's API instead:
|
||||
|
||||
```bash
|
||||
# 1. Extract BV number from URL
|
||||
BV="BV1xxxxx" # Replace with actual BV number
|
||||
|
||||
# 2. Get video info (title, duration, CID)
|
||||
curl -s "https://api.bilibili.com/x/web-interface/view?bvid=$BV" \
|
||||
-H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" \
|
||||
| python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f\"Title: {d['title']}\nDuration: {d['duration']}s\nCID: {d['cid']}\")"
|
||||
|
||||
# 3. Get audio stream URL
|
||||
CID=<CID from previous step>
|
||||
AUDIO_URL=$(curl -s "https://api.bilibili.com/x/player/playurl?bvid=$BV&cid=$CID&fnval=16&qn=64" \
|
||||
-H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin)['data']['dash']['audio'][0]['baseUrl'])")
|
||||
|
||||
# 4. Download audio (Referer header required, otherwise 403)
|
||||
curl -L -o /tmp/media_audio.m4s \
|
||||
-H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" "$AUDIO_URL"
|
||||
|
||||
# 5. Convert to mp3
|
||||
ffmpeg -y -i /tmp/media_audio.m4s -acodec libmp3lame -q:a 5 /tmp/media_audio.mp3
|
||||
```
|
||||
|
||||
→ Step 2b (check size & transcribe)
|
||||
|
||||
### Step 2a: Video — Download Audio (when no subtitles)
|
||||
|
||||
```bash
|
||||
# YouTube may need --cookies-from-browser chrome to bypass bot detection
|
||||
yt-dlp --cookies-from-browser chrome -f "ba[ext=m4a]/ba/b" --extract-audio --audio-format mp3 --audio-quality 5 \
|
||||
-o "/tmp/media_audio.%(ext)s" "VIDEO_URL"
|
||||
```
|
||||
|
||||
### Step 2b: Check Audio Size & Segment
|
||||
|
||||
```bash
|
||||
FILE_SIZE=$(stat -f%z /tmp/media_audio.* 2>/dev/null || stat -c%s /tmp/media_audio.* 2>/dev/null)
|
||||
echo "File size: $FILE_SIZE bytes"
|
||||
```
|
||||
|
||||
- **≤ 25MB (25000000)** → Step 2c (transcribe directly)
|
||||
- **> 25MB** → Split first, then transcribe each segment
|
||||
|
||||
**Splitting large audio (>25MB)**:
|
||||
```bash
|
||||
# Get total duration
|
||||
DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 /tmp/media_audio.* | head -1)
|
||||
|
||||
# Split into 10-minute segments (keeps each under 25MB)
|
||||
SEGMENT_SEC=600
|
||||
SEGMENTS=$(python3 -c "import math; print(math.ceil(float('$DURATION')/$SEGMENT_SEC))")
|
||||
|
||||
# Cut segments
|
||||
for i in $(seq 0 $((SEGMENTS-1))); do
|
||||
START=$((i * SEGMENT_SEC))
|
||||
ffmpeg -y -i /tmp/media_audio.* -ss $START -t $SEGMENT_SEC -acodec libmp3lame -q:a 5 \
|
||||
"/tmp/media_segment_${i}.mp3" 2>/dev/null
|
||||
done
|
||||
```
|
||||
|
||||
→ Call Step 2c for each segment **sequentially** (parallel triggers Groq 524 timeout), concatenate results
|
||||
|
||||
### Step 2c: Whisper Transcription
|
||||
|
||||
**Prerequisite**: `GROQ_API_KEY` environment variable
|
||||
|
||||
```bash
|
||||
# Check API key
|
||||
if [ -z "$GROQ_API_KEY" ]; then
|
||||
echo "❌ GROQ_API_KEY not set. Get one at: https://console.groq.com/keys"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Transcribe single file (replace AUDIO_FILE with actual path)
|
||||
curl -s -X POST "https://api.groq.com/openai/v1/audio/transcriptions" \
|
||||
-H "Authorization: Bearer $GROQ_API_KEY" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F "file=@AUDIO_FILE" \
|
||||
-F "model=whisper-large-v3-turbo" \
|
||||
-F "response_format=verbose_json" \
|
||||
-F "language=zh" \
|
||||
> /tmp/media_transcript.json
|
||||
|
||||
# Extract plain text
|
||||
python3 -c "import json; print(json.load(open('/tmp/media_transcript.json'))['text'])"
|
||||
```
|
||||
|
||||
**Whisper model options**:
|
||||
| Model | Speed | Accuracy | Use Case |
|
||||
|-------|-------|----------|----------|
|
||||
| `whisper-large-v3-turbo` | 10x realtime | High | Default choice |
|
||||
| `whisper-large-v3` | 5x realtime | Highest | Professional/noisy content |
|
||||
|
||||
**Language parameter**:
|
||||
- Chinese: `language=zh`
|
||||
- English: `language=en`
|
||||
- Auto-detect: omit language parameter
|
||||
|
||||
### Step 3: Structured Summary
|
||||
|
||||
Choose output format based on media type:
|
||||
|
||||
**Video (≤20 min)**:
|
||||
1. **Overview** (1-2 sentences)
|
||||
2. **Key Points** (3-5 bullet points)
|
||||
3. **Notable Quotes** (if any)
|
||||
4. **Action Items** (if applicable)
|
||||
|
||||
**Podcast (>20 min)**:
|
||||
1. **Overview** (2-3 sentences: who discussed what)
|
||||
2. **Chapter Summary** (segmented by topic, 2-3 sentences each)
|
||||
3. **Key Points** (5-8 bullet points)
|
||||
4. **Notable Quotes**
|
||||
5. **Action Items** (if applicable)
|
||||
|
||||
## Output Format
|
||||
|
||||
### Video
|
||||
|
||||
```
|
||||
## 📺 Video Digest
|
||||
|
||||
**Title**: [Video Title]
|
||||
**Duration**: [x minutes]
|
||||
**Language**: [Chinese/English]
|
||||
|
||||
### Overview
|
||||
[1-2 sentence summary]
|
||||
|
||||
### Key Points
|
||||
1. [Point 1]
|
||||
2. [Point 2]
|
||||
...
|
||||
|
||||
### Notable Quotes
|
||||
> "xxx" — [timestamp]
|
||||
|
||||
### Action Items
|
||||
- [if applicable]
|
||||
```
|
||||
|
||||
### Podcast
|
||||
|
||||
```
|
||||
## 🎙️ Podcast Digest
|
||||
|
||||
**Show**: [Podcast Name]
|
||||
**Episode**: [Episode Title]
|
||||
**Duration**: [x minutes]
|
||||
**Guests**: [if any]
|
||||
|
||||
### Overview
|
||||
[2-3 sentences: who discussed what, core conclusions]
|
||||
|
||||
### Chapter Summary
|
||||
#### 1. [Topic] (~xx:xx-xx:xx)
|
||||
[2-3 sentences of core content]
|
||||
|
||||
#### 2. [Topic] (~xx:xx-xx:xx)
|
||||
[2-3 sentences of core content]
|
||||
...
|
||||
|
||||
### Key Points
|
||||
1. [Point 1]
|
||||
2. [Point 2]
|
||||
...
|
||||
|
||||
### Notable Quotes
|
||||
> "xxx"
|
||||
|
||||
### Action Items
|
||||
- [if applicable]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| No subtitles + no GROQ_API_KEY | Prompt user to set API key |
|
||||
| No subtitles + has API key | Auto Whisper transcription |
|
||||
| Xiaoyuzhou curl extraction empty | Use Puppeteer/browser to get rendered HTML |
|
||||
| Audio >25MB | ffmpeg segment (10min/segment), transcribe sequentially |
|
||||
| Podcast >2 hours | Warn user about duration, confirm before proceeding |
|
||||
| Groq 524 timeout | Do NOT parallelize — transcribe sequentially, sleep 5-8s between segments |
|
||||
| Groq 429 rate limit | 7200s/hour limit, wait for retry-after header, then retry |
|
||||
| yt-dlp Bilibili 412 | Use Bilibili API instead (Step 1d) |
|
||||
| yt-dlp YouTube bot detection | Add `--cookies-from-browser chrome` |
|
||||
| Network timeout | Retry once |
|
||||
| Spotify links | Inform user: not supported (DRM protected) |
|
||||
|
||||
## Groq Whisper Limits
|
||||
|
||||
- Max 25MB per request
|
||||
- Free tier: 7200 seconds of audio/hour (rolling window), ~20 hours/day
|
||||
- Supported formats: mp3, mp4, mpeg, mpga, m4a, wav, webm
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `yt-dlp`: video download + subtitle extraction
|
||||
- `ffmpeg`: audio conversion + segmentation
|
||||
- `curl`: Xiaoyuzhou audio download, Bilibili API
|
||||
- `GROQ_API_KEY`: Whisper transcription API (free at https://console.groq.com/keys)
|
||||
Reference in New Issue
Block a user