Skill: hviktortsoi/xhs-video-analyzer (version 1.0.1)

--- name: xhs-video-analyzer description: 下载并分析小红书视频内容。当用户提供小红书链接（xiaohongshu.com）时，自动下载视频、提取语音文字、整理总结内容。Use when user provides a xiaohongshu.com URL and wants video content analysis. --- # 小红书视频分析器 下载小红书视频并提取语音内容进行总结。 ## 快速使用 当用户提供小红书链接时，执行以下流程： ```bash ~/.openclaw/skills/xhs-video-analyzer/scripts/full-analyze.sh "https://www.xiaohongshu.com/explore/..." ``` ## 工作流程 1. **下载视频** - 使用 Python 脚本或 yt-dlp 2. **提取音频** - 使用 ffmpeg 3. **语音转文字** - 使用 Poe API + Gemini (云端转录) 4. **详细总结** - 基于转录文本生成深度分析报告 ## 依赖 - `ffmpeg` - 提取音频 - `curl` - API 请求 - `Poe API Key` - 需要配置 POE_API_KEY 环境变量或在 ~/.openclaw/openclaw.json 中配置 ## API 配置 在 `~/.openclaw/openclaw.json` 中配置 Poe API： ```json { "models": { "providers": { "poe": { "baseUrl": "https://api.poe.com/v1", "apiKey": "YOUR_POE_API_KEY" } } } } ``` 或设置环境变量： ```bash export POE_API_KEY="your-api-key" ``` ## 转录技术 使用 Poe API 的 `file` 格式发送音频到 Gemini 模型进行转录： ```json { "type": "file", "file": { "filename": "audio.mp3", "file_data": "data:audio/mp3;base64,<base64>" } } ``` ## 输出 - 视频文件: `video.mp4` - 音频文件: `audio.mp3` - 转录文本: `audio.txt` - 音频片段: `chunks/` (3分钟一个片段) ## 详细总结指南 转录完成后，**必须**对转录文本进行深度分析和详细总结。总结应包含以下内容： ### 总结结构 1. **视频主题** - 一句话概括视频的核心话题 2. **核心观点** - 提取视频中的主要论点和见解（3-5条） 3. **详细内容分解** - 按话题/章节拆解视频内容，每个部分包含： - 子话题标题 - 具体内容和论据 - 举例或案例说明 - 讲述者的个人见解或建议 4. **实用价值** - 视频对观众的实际帮助或启发 5. **关键金句** - 摘录视频中有价值的原话（2-3句） ### 总结原则 - **详尽而非简略** - 不要只列出要点，要展开说明每个观点的具体内容 - **保留原意** - 准确传达讲述者的观点，不要过度概括导致信息丢失 - **结构清晰** - 用标题、列表、引用等方式组织内容，便于阅读 - **突出价值** - 强调视频中对观众有实际帮助的信息和建议 - **语言自然** - 使用流畅的中文表达，避免机械式罗列 ### 示例格式 \`\`\` ## 视频主题 [一句话概括] ## 核心观点 1. [观点1] - [具体解释] 2. [观点2] - [具体解释] 3. [观点3] - [具体解释] ## 详细内容 ### [子话题1] [详细说明内容，包括背景、论据、案例等] ### [子话题2] [详细说明内容，包括背景、论据、案例等] ### [子话题3] [详细说明内容，包括背景、论据、案例等] ## 实用价值 - [对观众的实际帮助1] - [对观众的实际帮助2] ## 关键金句 > "[原话摘录1]" > "[原话摘录2]" \`\`\` 分析完成后，先输出转录文本的详细总结，再提及工作目录信息。

path: scripts/full-analyze.sh
content: #!/bin/bash
# 完整的小红书视频分析流程

set -e

URL="$1"
WORK_DIR="${2:-/tmp/xhs-analysis-$(date +%s)}"

if [ -z "$URL" ]; then
    echo "Usage: $0 <xhs_url> [work_dir]"
    exit 1
fi

# 获取脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# 创建工作目录
mkdir -p "$WORK_DIR"
cd "$WORK_DIR"

echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎬 小红书视频分析器"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""

# Step 1: 下载视频
echo "📥 Step 1: 下载视频..."
python3 "$SCRIPT_DIR/download.py" "$URL" "$WORK_DIR"

if [ ! -f "video.mp4" ]; then
    echo ""
    echo "❌ 视频下载失败，尝试使用 yt-dlp..."
    yt-dlp "$URL" -o "video.%(ext)s" --no-playlist 2>/dev/null || true
    
    # 重命名文件
    for ext in mp4 webm mkv; do
        if [ -f "video.$ext" ]; then
            mv "video.$ext" "video.mp4"
            break
        fi
    done
fi

if [ ! -f "video.mp4" ]; then
    echo "❌ 无法下载视频"
    exit 1
fi

echo ""
# Step 2: 提取音频
echo "🎵 Step 2: 提取音频..."
ffmpeg -i video.mp4 -vn -acodec libmp3lame -ab 192k audio.mp3 -y 2>/dev/null

if [ ! -f "audio.mp3" ]; then
    echo "❌ 音频提取失败"
    exit 1
fi

echo "✅ 音频已提取: audio.mp3"
echo ""

# Step 3: 语音转文字 (使用 Poe API + Gemini)
echo "🎙️ Step 3: 语音转文字..."
echo ""

# 检查 API Key
if [ -z "$POE_API_KEY" ]; then
    # 尝试从 openclaw.json 读取
    POE_API_KEY=$(python3 -c "
import json
try:
    with open('$HOME/.openclaw/openclaw.json') as f:
        d = json.load(f)
        print(d.get('models', {}).get('providers', {}).get('poe', {}).get('apiKey', ''))
except:
    print('')
" 2>/dev/null)
fi

if [ -z "$POE_API_KEY" ]; then
    echo "❌ 未找到 POE_API_KEY，请设置环境变量或在 ~/.openclaw/openclaw.json 中配置"
    exit 1
fi

# 分割音频为60秒的chunks
echo "📂 分割音频文件..."
mkdir -p chunks
ffmpeg -i audio.mp3 -f segment -segment_time 180 -c copy chunks/chunk_%03d.mp3 -y 2>/dev/null

# 获取chunk数量
CHUNK_COUNT=$(ls chunks/chunk_*.mp3 2>/dev/null | wc -l | tr -d ' ')
echo "✅ 分割为 $CHUNK_COUNT 个片段"
echo ""

# 转录每个chunk
mkdir -p transcripts
TRANSCRIPT_FILE="audio.txt"
> "$TRANSCRIPT_FILE"

for chunk_file in chunks/chunk_*.mp3; do
    if [ -f "$chunk_file" ]; then
        chunk_name=$(basename "$chunk_file" .mp3)
        echo "🔄 处理: $chunk_name..."
        
        # 使用 Poe API + Gemini 转录
        AUDIO_BASE64=$(base64 -i "$chunk_file")
        
        # 创建请求JSON
        cat > request.json << EOF
{
  "model": "gemini-3-flash",
  "messages": [{
    "role": "user",
    "content": [
      {"type": "text", "text": "请完整转录这段音频中的所有中文内容，不要遗漏任何内容。只输出转录的文字。"},
      {
        "type": "file",
        "file": {
          "filename": "audio.mp3",
          "file_data": "data:audio/mp3;base64,$AUDIO_BASE64"
        }
      }
    ]
  }]
}
EOF
        
        # 发送请求
        response=$(curl -sS https://api.poe.com/v1/chat/completions \
            -H "Authorization: Bearer $POE_API_KEY" \
            -H "Content-Type: application/json" \
            -d @request.json 2>&1)
        
        # 提取转录内容
        transcript=$(echo "$response" | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
    print(d['choices'][0]['message']['content'])
except Exception as e:
    print(f'Error: {e}', file=sys.stderr)
" 2>/dev/null)
        
        if [ -n "$transcript" ]; then
            echo "$transcript" >> "$TRANSCRIPT_FILE"
            echo "✅ $chunk_name 完成"
        else
            echo "⚠️ $chunk_name 转录失败"
        fi
    fi
done

# 清理临时文件
rm -f request.json

if [ ! -s "$TRANSCRIPT_FILE" ]; then
    echo "❌ 转录失败"
    exit 1
fi

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 转录结果"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
cat "$TRANSCRIPT_FILE"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📁 工作目录: $WORK_DIR"
echo "   - video.mp4  (原始视频)"
echo "   - audio.mp3  (提取的音频)"
echo "   - audio.txt  (转录文本)"
echo "   - chunks/    (音频片段)"


path: scripts/download.py
content: #!/usr/bin/env python3
"""小红书视频下载器"""

import requests
import re
import json
import sys
import os

def download_xhs_video(url, output_dir="."):
    """下载小红书视频"""
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Referer': 'https://www.xiaohongshu.com/',
    }
    
    # 提取 note ID
    note_id_match = re.search(r'xiaohongshu\.com/explore/([a-f0-9]+)', url)
    if not note_id_match:
        note_id_match = re.search(r'xiaohongshu\.com/discovery/item/([a-f0-9]+)', url)
    
    note_id = note_id_match.group(1) if note_id_match else None
    
    try:
        # 方法1: 使用 API
        if note_id:
            api_url = f"https://www.xiaohongshu.com/discovery/item/{note_id}"
            resp = requests.get(api_url, headers=headers, timeout=30)
        else:
            resp = requests.get(url, headers=headers, timeout=30)
        
        # 从页面提取视频信息
        match = re.search(r'__INITIAL_STATE__\s*=\s*({.*?})\s*</script>', resp.text, re.DOTALL)
        
        if match:
            data = json.loads(match.group(1))
            note = data.get('note', {})
            video = note.get('video', {})
            media = video.get('media', {})
            stream = media.get('stream', {})
            
            # 尝试多种视频格式
            video_url = None
            for key in ['h264', 'h265', 'av1']:
                if stream.get(key):
                    h = stream[key][0] if isinstance(stream[key], list) else stream[key]
                    video_url = h.get('masterUrl')
                    if video_url:
                        break
            
            if video_url:
                print(f"📥 找到视频链接...")
                v_resp = requests.get(video_url, headers=headers, timeout=120)
                output_path = os.path.join(output_dir, 'video.mp4')
                with open(output_path, 'wb') as f:
                    f.write(v_resp.content)
                print(f"✅ 视频已保存: {output_path}")
                
                # 返回视频信息
                title = note.get('title', '未知标题')
                desc = note.get('desc', '')
                print(f"\n📋 标题: {title}")
                if desc:
                    print(f"📝 描述: {desc[:100]}...")
                
                return output_path
        
        print("❌ 未找到视频链接")
        return None
        
    except Exception as e:
        print(f"❌ 下载失败: {e}")
        return None

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python download.py <xhs_url> [output_dir]")
        sys.exit(1)
    
    url = sys.argv[1]
    output_dir = sys.argv[2] if len(sys.argv) > 2 else "."
    download_xhs_video(url, output_dir)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)