[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C30AYVR6A02FBDDY3M55BD":3,"skill-same:01M6C30AYVR6A02FBDDY3M55BD":67},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":35,"linkOnly":36,"markdownContent":37,"markdownBody":38,"frontMatter":39,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":43,"repositoryHtmlUrl":44,"repositoryHomepageUrl":6,"ownerHtmlUrl":45,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":46,"install":47,"resources":60},"01M6C30AYVR6A02FBDDY3M55BD","video-caption",null,"Use when asked to analyze a video file and add visual description captions or text overlays burned into the output video","jleechanorg\u002Fjleechan-skills","jleechanorg","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F221404966?v=4","01M6C1VJ99XG950X1DWPEBCBGX","01M6C1VJDK5YVDNT3362RT5WSB",6,"MIT","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills_archive\u002Flegacy-pre-2026-08-27\u002Fpackages\u002Fvideo-caption\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34],"Video","video caption","file","Captions","analyze","text","use","visual","add","output","overlays","when","2026-11-10 10:12:30",false,"---\nname: video-caption\ndescription: Use when asked to analyze a video file and add visual description captions or text overlays burned into the output video\n---\n\n# Video Captioning\n\n## Overview\n\nAnalyze a video by extracting frames at intervals, describe each segment visually, then burn captions at the top of the video using PIL-rendered PNG overlays and ffmpeg's `overlay` filter. This approach avoids dependency on ffmpeg's `libass`\u002F`drawtext` filters, which are often absent in Homebrew builds.\n\n## When to Use\n\n- \"Add captions to this video\"\n- \"Describe visually what's happening in this video\"\n- \"Annotate this screen recording\"\n- Evidence-standard runs where tmux\u002FUI videos are required in PRs\n\n## Evidence Standards Mode (tmux\u002FUI PR evidence)\n\nWhen the video is evidence for a PR:\n- Captions are mandatory (burned-in preferred)\n- Output should be an attachable GitHub-friendly video (`.mp4`, H.264)\n- Also export a caption sidecar (`.vtt` or `.srt`) for gist packaging\n- Keep caption text neutral and descriptive (no unverifiable claims)\n\n## Workflow\n\n### 1. Inspect the video\n\n```bash\nffprobe -v quiet -print_format json -show_format -show_streams \"video.mov\" | python3 -c \"\nimport json, sys\nd = json.load(sys.stdin)\nfmt = d['format']\nvs = [s for s in d['streams'] if s['codec_type'] == 'video'][0]\nhas_audio = any(s['codec_type'] == 'audio' for s in d['streams'])\nprint(f'Duration: {float(fmt[\\\"duration\\\"]):.1f}s, {vs[\\\"width\\\"]}x{vs[\\\"height\\\"]} @ {vs[\\\"r_frame_rate\\\"]}fps, audio={has_audio}')\n\"\n```\n\nNote: track whether audio exists — it affects the final ffmpeg command.\n\n### 2. Extract frames for analysis\n\n```bash\nmkdir -p \u002Ftmp\u002Fvideo_frames\nffmpeg -i \"video.mov\" -vf \"fps=1\u002F10,scale=1280:-1\" -q:v 3 \u002Ftmp\u002Fvideo_frames\u002Fframe_%03d.jpg -y\n```\n\nOne frame per 10 seconds is sufficient. Adjust `fps=1\u002FN` for longer videos.\n\n### 3. Read and analyze frames\n\nUse the `Read` tool on each frame image — Claude can view them directly. Read 4 at a time in parallel. Build a timeline:\n\n```\n0-10s:  What's happening in the UI\u002Fscreen\u002Fscene\n10-20s: What changed, what action is occurring\n...\n```\n\n### 4. Render caption PNGs with PIL\n\n```python\nfrom PIL import Image, ImageDraw, ImageFont\nimport textwrap, os\n\nVIDEO_W = 3354   # match your video width\nFONT_SIZE = 62\nPADDING = 40\nFONT = '\u002FSystem\u002FLibrary\u002FFonts\u002FHelvetica.ttc'  # macOS\n\n# captions: list of (start_sec, end_sec, text)\ncaptions = [\n    (0, 10, \"Description of what's happening...\"),\n    ...\n]\n\nfont = ImageFont.truetype(FONT, FONT_SIZE)\nos.makedirs('\u002Ftmp\u002Fcaps', exist_ok=True)\n\nfor i, (start, end, text) in enumerate(captions):\n    avg_char_w = FONT_SIZE * 0.55\n    max_chars = int((VIDEO_W - 2 * PADDING) \u002F avg_char_w)\n    lines = textwrap.wrap(text, width=max_chars)\n    line_h = FONT_SIZE + 10\n    cap_h = line_h * len(lines) + 2 * PADDING\n\n    img = Image.new('RGBA', (VIDEO_W, cap_h), (0, 0, 0, 170))  # semi-transparent black\n    draw = ImageDraw.Draw(img)\n    y = PADDING\n    for line in lines:\n        draw.text((PADDING, y), line, font=font, fill=(255, 255, 255, 255))\n        y += line_h\n\n    img.save(f'\u002Ftmp\u002Fcaps\u002Fcap_{i:02d}_{start}_{end}.png')\n```\n\n### 5. Burn captions with ffmpeg overlay\n\n```python\nimport subprocess\n\n# Build inputs and filtergraph\ncmd = ['ffmpeg', '-i', 'video.mov']\nfor start, end, path in [(s, e, f'\u002Ftmp\u002Fcaps\u002Fcap_{i:02d}_{s}_{e}.png') for i, (s, e, _) in enumerate(captions)]:\n    cmd += ['-i', path]\n\nn = len(captions)\nprev = '0:v'\nparts = []\nfor i, (start, end, _) in enumerate(captions):\n    out = f'v{i+1}' if i \u003C n - 1 else 'vout'\n    parts.append(f\"[{prev}][{i+1}:v]overlay=0:0:enable='between(t,{start},{end})'[{out}]\")\n    prev = out\n\ncmd += ['-filter_complex', ';'.join(parts), '-map', '[vout]']\n\n# Only add audio map if audio stream exists\nif has_audio:\n    cmd += ['-map', '0:a', '-c:a', 'copy']\n\ncmd += ['-c:v', 'libx264', '-crf', '18', '-preset', 'fast', 'output captioned.mov', '-y']\nsubprocess.run(cmd, check=True, timeout=600)\n```\n\n## Quick Reference\n\n| Step | Tool | Key detail |\n|---|---|---|\n| Inspect | `ffprobe` | Get duration, resolution, audio presence |\n| Extract frames | `ffmpeg fps=1\u002F10` | 1 frame\u002F10s is enough |\n| Analyze | `Read` tool | Read 4 frames in parallel |\n| Render captions | PIL `Image.new('RGBA')` | Semi-transparent: alpha=170 |\n| Burn in | ffmpeg `overlay` + `enable='between(t,s,e)'` | Chain all overlays in one filtergraph |\n\n## Common Mistakes\n\n- **`-map 0:a` without audio** → ffmpeg errors. Check streams first.\n- **`subtitles=` or `ass=` filter** → Often absent in Homebrew ffmpeg (no libass). Use PIL+overlay instead.\n- **`drawtext` filter** → Also absent if ffmpeg lacks freetype. Same fallback.\n- **Caption too long for one line** → Use `textwrap.wrap` with `width = (video_w - 2*padding) \u002F (font_size * 0.55)`.\n- **Wrong video width in PIL** → Must match source exactly or overlay misaligns.\n\n## Font Paths (macOS)\n\n```\n\u002FSystem\u002FLibrary\u002FFonts\u002FHelvetica.ttc\n\u002FSystem\u002FLibrary\u002FFonts\u002FSFNS.ttf\n\u002FSystem\u002FLibrary\u002FFonts\u002FSFNSMono.ttf\n```\n","# Video Captioning\n\n## Overview\n\nAnalyze a video by extracting frames at intervals, describe each segment visually, then burn captions at the top of the video using PIL-rendered PNG overlays and ffmpeg's `overlay` filter. This approach avoids dependency on ffmpeg's `libass`\u002F`drawtext` filters, which are often absent in Homebrew builds.\n\n## When to Use\n\n- \"Add captions to this video\"\n- \"Describe visually what's happening in this video\"\n- \"Annotate this screen recording\"\n- Evidence-standard runs where tmux\u002FUI videos are required in PRs\n\n## Evidence Standards Mode (tmux\u002FUI PR evidence)\n\nWhen the video is evidence for a PR:\n- Captions are mandatory (burned-in preferred)\n- Output should be an attachable GitHub-friendly video (`.mp4`, H.264)\n- Also export a caption sidecar (`.vtt` or `.srt`) for gist packaging\n- Keep caption text neutral and descriptive (no unverifiable claims)\n\n## Workflow\n\n### 1. Inspect the video\n\n```bash\nffprobe -v quiet -print_format json -show_format -show_streams \"video.mov\" | python3 -c \"\nimport json, sys\nd = json.load(sys.stdin)\nfmt = d['format']\nvs = [s for s in d['streams'] if s['codec_type'] == 'video'][0]\nhas_audio = any(s['codec_type'] == 'audio' for s in d['streams'])\nprint(f'Duration: {float(fmt[\\\"duration\\\"]):.1f}s, {vs[\\\"width\\\"]}x{vs[\\\"height\\\"]} @ {vs[\\\"r_frame_rate\\\"]}fps, audio={has_audio}')\n\"\n```\n\nNote: track whether audio exists — it affects the final ffmpeg command.\n\n### 2. Extract frames for analysis\n\n```bash\nmkdir -p \u002Ftmp\u002Fvideo_frames\nffmpeg -i \"video.mov\" -vf \"fps=1\u002F10,scale=1280:-1\" -q:v 3 \u002Ftmp\u002Fvideo_frames\u002Fframe_%03d.jpg -y\n```\n\nOne frame per 10 seconds is sufficient. Adjust `fps=1\u002FN` for longer videos.\n\n### 3. Read and analyze frames\n\nUse the `Read` tool on each frame image — Claude can view them directly. Read 4 at a time in parallel. Build a timeline:\n\n```\n0-10s:  What's happening in the UI\u002Fscreen\u002Fscene\n10-20s: What changed, what action is occurring\n...\n```\n\n### 4. Render caption PNGs with PIL\n\n```python\nfrom PIL import Image, ImageDraw, ImageFont\nimport textwrap, os\n\nVIDEO_W = 3354   # match your video width\nFONT_SIZE = 62\nPADDING = 40\nFONT = '\u002FSystem\u002FLibrary\u002FFonts\u002FHelvetica.ttc'  # macOS\n\n# captions: list of (start_sec, end_sec, text)\ncaptions = [\n    (0, 10, \"Description of what's happening...\"),\n    ...\n]\n\nfont = ImageFont.truetype(FONT, FONT_SIZE)\nos.makedirs('\u002Ftmp\u002Fcaps', exist_ok=True)\n\nfor i, (start, end, text) in enumerate(captions):\n    avg_char_w = FONT_SIZE * 0.55\n    max_chars = int((VIDEO_W - 2 * PADDING) \u002F avg_char_w)\n    lines = textwrap.wrap(text, width=max_chars)\n    line_h = FONT_SIZE + 10\n    cap_h = line_h * len(lines) + 2 * PADDING\n\n    img = Image.new('RGBA', (VIDEO_W, cap_h), (0, 0, 0, 170))  # semi-transparent black\n    draw = ImageDraw.Draw(img)\n    y = PADDING\n    for line in lines:\n        draw.text((PADDING, y), line, font=font, fill=(255, 255, 255, 255))\n        y += line_h\n\n    img.save(f'\u002Ftmp\u002Fcaps\u002Fcap_{i:02d}_{start}_{end}.png')\n```\n\n### 5. Burn captions with ffmpeg overlay\n\n```python\nimport subprocess\n\n# Build inputs and filtergraph\ncmd = ['ffmpeg', '-i', 'video.mov']\nfor start, end, path in [(s, e, f'\u002Ftmp\u002Fcaps\u002Fcap_{i:02d}_{s}_{e}.png') for i, (s, e, _) in enumerate(captions)]:\n    cmd += ['-i', path]\n\nn = len(captions)\nprev = '0:v'\nparts = []\nfor i, (start, end, _) in enumerate(captions):\n    out = f'v{i+1}' if i \u003C n - 1 else 'vout'\n    parts.append(f\"[{prev}][{i+1}:v]overlay=0:0:enable='between(t,{start},{end})'[{out}]\")\n    prev = out\n\ncmd += ['-filter_complex', ';'.join(parts), '-map', '[vout]']\n\n# Only add audio map if audio stream exists\nif has_audio:\n    cmd += ['-map', '0:a', '-c:a', 'copy']\n\ncmd += ['-c:v', 'libx264', '-crf', '18', '-preset', 'fast', 'output captioned.mov', '-y']\nsubprocess.run(cmd, check=True, timeout=600)\n```\n\n## Quick Reference\n\n| Step | Tool | Key detail |\n|---|---|---|\n| Inspect | `ffprobe` | Get duration, resolution, audio presence |\n| Extract frames | `ffmpeg fps=1\u002F10` | 1 frame\u002F10s is enough |\n| Analyze | `Read` tool | Read 4 frames in parallel |\n| Render captions | PIL `Image.new('RGBA')` | Semi-transparent: alpha=170 |\n| Burn in | ffmpeg `overlay` + `enable='between(t,s,e)'` | Chain all overlays in one filtergraph |\n\n## Common Mistakes\n\n- **`-map 0:a` without audio** → ffmpeg errors. Check streams first.\n- **`subtitles=` or `ass=` filter** → Often absent in Homebrew ffmpeg (no libass). Use PIL+overlay instead.\n- **`drawtext` filter** → Also absent if ffmpeg lacks freetype. Same fallback.\n- **Caption too long for one line** → Use `textwrap.wrap` with `width = (video_w - 2*padding) \u002F (font_size * 0.55)`.\n- **Wrong video width in PIL** → Must match source exactly or overlay misaligns.\n\n## Font Paths (macOS)\n\n```\n\u002FSystem\u002FLibrary\u002FFonts\u002FHelvetica.ttc\n\u002FSystem\u002FLibrary\u002FFonts\u002FSFNS.ttf\n\u002FSystem\u002FLibrary\u002FFonts\u002FSFNSMono.ttf\n```\n",{"name":5,"description":7},"main","42ccd02cbf5e8e47839dc3eab6fc754dc6b339cf",".claude\u002Fskills_archive\u002Flegacy-pre-2026-08-27\u002Fpackages\u002Fvideo-caption",0,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills","https:\u002F\u002Fgithub.com\u002Fjleechanorg","2026-11-14 14:55:35",{"source":8,"skillName":5,"commands":48,"prompt":58,"zipAvailable":59},[49,52,55],{"tool":50,"command":51},"npx","npx skills add jleechanorg\u002Fjleechan-skills --skill video-caption",{"tool":53,"command":54},"bunx","bunx skills add jleechanorg\u002Fjleechan-skills --skill video-caption",{"tool":56,"command":57},"pnpm","pnpm dlx skills add jleechanorg\u002Fjleechan-skills --skill video-caption","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills_archive\u002Flegacy-pre-2026-08-27\u002Fpackages\u002Fvideo-caption\u002FSKILL.md",true,[61],{"path":62,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":65,"sourceHtmlUrl":66},"reference\u002Fplatform-and-evidence.md","REFERENCE","参考文档",19118,"https:\u002F\u002Fgithub.com\u002FMuvon\u002Foctomind-tap\u002Fblob\u002Fmaster\u002Fskills\u002Fsocial-threads\u002Freference\u002Fplatform-and-evidence.md",[68,84,106,120,134,150],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":74,"publishedAt":35},"01M716Y5MTJ6GYJZ2ATXCJK8G4","instinct","Use when the user invokes \u002Finstinct or \u002Fin, or asks to \"message Instinct\", \"ask Instinct\", \"tell Instinct\", \"check Instinct\", or read replies from the configured peer assistant. iMessage first, WhatsApp Web as fallback.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Finstinct\u002FSKILL.md",[],[70,75,34,76,77,29,78,79,80,81,82,83],"user","replies","check","identity","whatsapp","gmail","read","message","ask",{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":95,"publishedAt":35},"01M716Y4HF7FVVMJSA2CCVN5VF","hr","Compatibility alias for history-resume; produce the same fresh-state, provenance-aware resume briefing.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhr\u002FSKILL.md",[90],{"categoryNo":91,"categoryName":92,"taxonomyType":93,"taxonomyTypeDesc":94},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[86,96,97,98,99,100,101,102,103,104,105],"resume","compatibility","provenance","for","history","produce","alias","briefing","state","fresh",{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":113,"publishedAt":35},"01M716Y1FFNFKFJYYKF8HQA6T8","history-resume","Resume briefing for the current repository branch or worktree using fresh state and admitted history sources.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhistory-resume\u002FSKILL.md",[112],{"categoryNo":91,"categoryName":92,"taxonomyType":93,"taxonomyTypeDesc":94},[100,96,114,115,75,116,103,117,99,118,119,104,105],"history resume","branch","analysis","repository","worktree","sources",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":126,"publishedAt":35},"01M716XWXJ8EPHK4RH9T14TR85","grok","Alias for the canonical grokbot browser conversation workflow; \u002Fgrok talks to the Grok bot the same way \u002Fdot talks to Dot.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgrok\u002FSKILL.md",[],[122,127,128,129,99,130,102,131,132,133],"dot","bot","workflow","canonical","conversation","browser","grokbot",{"skillNo":135,"name":136,"nameZh":6,"description":137,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":140,"publishedAt":35},"01M70N9Q07DTE0EW5KFXMMMSJ0","codexa-bench","Compare `codexa` and normal `codex` on a fair split of comparable work by default; use `--strict` for duplicate runs of the exact same task.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fcodexa-bench\u002FSKILL.md",[],[141,142,143,29,144,99,145,146,147,148,149],"codexa bench","work","split","default","duplicate","task","compare","normal","codex",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":154,"licenseFileUrl":155,"sourceHtmlUrl":156,"categories":157,"keywords":167,"publishedAt":35},"01M6C24S2X60SP8BBFGAEKPEGW","frontend-design","Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML\u002FCSS layouts, or when styling\u002Fbeautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FSKILL.md",[158,161,164],{"categoryNo":159,"categoryName":160,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":162,"categoryName":163,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":165,"categoryName":166,"taxonomyType":93,"taxonomyTypeDesc":94},"01JBSEEDCAT000000000000032","设计师",[168,169,170,29,171,172,173,75,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,34,192],"frontend","design","frontend design","generic","ui design","react","html","components","code","artifacts","ui","aesthetics","Production","web components","landing","applications","quality","web","css","create","examples","with","react components","web ui","pages"]