Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Video Obs Youtube Music

ASecurity

Remux OBS recordings to MP4, add YouTube music, generate intro/outro videos with WebGL shaders, and song credit overlays. Use when preparing screen recordings for YouTube or X/Twitter upload.

22 stars
0 votes
0 copies
0 views
Added 9/20/2026
developmentjavascriptrustgojavaswiftbashreactnodegitperformance

Works with

cli

Security Analysis

A96/100
mediumInstalls packages at runtime which could introduce malicious dependencies

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add lev-os/agents --skill video-obs-youtube-music --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Video Obs Youtube Music?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Video Obs Youtube Music
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lev-os-video-obs-youtube-music/badge)](https://www.skillsdirectory.com/skills/lev-os-video-obs-youtube-music)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: video-obs-youtube-music
description: >-
  Remux OBS recordings to MP4, add YouTube music, generate intro/outro videos with WebGL shaders, and song credit overlays. Use when preparing screen recordings for YouTube or X/Twitter upload.
---

# video-obs-youtube-music — OBS YouTube Music

Remux OBS MKV → MP4, add stunning intro/outro videos with WebGL shaders (FloquetTopoCA, smeared_life, SpinGlassCA), replace audio with YouTube music, and overlay animated song credits.

## Quick Start

```bash
# Basic: remux with single song
ffmpeg -i "recording.mkv" -i "song.mp3" \
  -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k \
  -movflags +faststart "output.mp4"
```

## Workflow

- [ ] Get video path, title, description, and song names from user
- [ ] Probe video duration: `ffprobe -v error -show_entries format=duration -of csv=p=0 "file"`
- [ ] Download songs: `yt-dlp -x --audio-format mp3 "ytsearch1:Song Name"`
- [ ] Check total music duration covers video
- [ ] **Optional**: Generate intro/outro videos with WebGL shaders (see below)
- [ ] **Optional**: Generate animated song credit cards (see below)
- [ ] **Optional**: Use remote machines for parallel rendering (see below)
- [ ] Concatenate intro + recording + outro, add audio and overlays
- [ ] Audio should start at intro (t=0), not when screen recording starts
- [ ] **Encode platform-specific versions** (see Platform Export section)

## Platform Export Requirements

### YouTube (Maximum Quality)
- Resolution: Up to 8K
- Frame rate: Up to 60fps
- Codec: H.264 or HEVC
- No special requirements beyond `-movflags +faststart`

### X/Twitter (CRITICAL - Different Limits!)

| Spec | Limit |
|------|-------|
| **Resolution** | Max 1920x1200 (NOT 4K!) |
| **Frame rate** | Up to 60fps |
| **Bitrate** | Max 25 Mbps |
| **Duration** | 2:20 regular, 4 hours Premium |
| **File size** | 512MB regular, 16GB Premium |
| **Codec** | H.264 + AAC only |

**X-Compatible Encoding Command:**
```bash
ffmpeg -i source_4k.mp4 \
  -vf "scale=1920:1080:flags=lanczos" \
  -c:v libx264 -preset medium -crf 18 \
  -profile:v high -level:v 4.2 -pix_fmt yuv420p \
  -b:v 20M -maxrate 25M -bufsize 25M \
  -c:a aac -b:a 192k \
  -movflags +faststart \
  output_for_x.mp4
```

### QuickTime Compatibility
- HEVC may not play - always provide H.264 version
- Use standard resolutions (3840x2160, 1920x1080)
- Avoid non-standard aspect ratios like 4096x2304

## Intro Video

Generate stunning 5-second intro videos with:
- **WebGL Background**: Animated cellular automata (cosmic, plasma, aurora, neon, nebula effects)
- **3D Particle Effects**: Three.js floating particles with additive blending
- **Animated Text**: Title, description, author info with smooth fade transitions
- **Professional Layout**: Date in corner, social handles at bottom

### Generate Intro

```bash
# Basic intro
./scripts/generate-intro.py \
  --title "Building a CLI Tool in Rust" \
  --output intro.mp4

# Full customization
./scripts/generate-intro.py \
  --title "React Performance Deep Dive" \
  --description "Optimizing renders and reducing bundle size" \
  --effect plasma \
  --duration 5 \
  --width 1920 --height 1080 \
  --output intro.mp4
```

### Effect Types

| Effect | Description |
|--------|-------------|
| `cosmic` | Default - deep purples and blues, organic flow |
| `plasma` | Warm oranges and reds, energetic movement |
| `aurora` | Greens and cyans, smooth northern lights feel |
| `neon` | High contrast pinks and cyans, electric vibe |
| `nebula` | Rich colors with energy glow, space-like |

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--title` | required | Video title (large, centered) |
| `--description` | "" | Subtitle text |
| `--name` | Jeffrey Emanuel | Author name |
| `--twitter` | @doodlestein | X/Twitter handle |
| `--github` | Dicklesworthstone | GitHub username |
| `--date` | today | Date displayed |
| `--effect` | cosmic | Background effect type |
| `--random-effect` | false | Choose random effect |
| `--duration` | 5 | Duration in seconds |
| `--width` | 1920 | Video width |
| `--height` | 1080 | Video height |
| `--fps` | 60 | Frames per second |

### Prepend Intro to Recording

```bash
# Concatenate intro + recording (same resolution/codec)
ffmpeg -i intro.mp4 -i recording.mp4 \
  -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" \
  -map "[v]" \
  -c:v libx264 -preset fast -crf 18 \
  "with_intro.mp4"

# With audio from songs
ffmpeg -i intro.mp4 -i recording.mp4 -i song.mp3 \
  -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" \
  -map "[v]" -map 2:a \
  -c:v libx264 -preset fast -crf 18 -c:a aac -b:a 192k \
  -shortest \
  "final.mp4"
```

## Song Credit Cards

Animated "now playing" cards with polished visual effects:
- **Scale animation**: Cards grow from 85% with overshoot bounce on entry/exit
- **Floating motion**: Subtle up/down floating while displayed
- **Pulsing music icon**: Rhythmic scale animation on the music note
- **Shimmer highlight**: Animated gradient sweep across top edge
- **Glow pulse**: Subtle ambient glow that pulses
- **Custom branding**: "Doodlestein's Choice" text with Pacifico font

### Card Dimensions

| Use Case | Width | Height | Notes |
|----------|-------|--------|-------|
| Standard | 2280px | 480px | Full-width overlay |
| Narrow (transparent) | 1900px | 480px | Avoids white rectangle artifacts |
| 4K compatible | 1900px | 480px | Scales well at any resolution |

**Important**: Use narrower cards (1900px) to avoid visible white rectangles on sides.

### Generate Cards with Playwright

```javascript
// render_cards.js - Render card animation to PNG frames
const { chromium } = require("playwright");
const fs = require("fs");
const path = require("path");

async function renderCard(cardNum) {
  const cardPath = "./cards/0" + cardNum + "_card.html";
  const outDir = "./cards/frames_0" + cardNum;

  if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });

  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  await page.setViewportSize({ width: 1900, height: 480 });
  await page.goto("file://" + path.resolve(cardPath));
  await page.waitForTimeout(500);

  const FPS = 60, DURATION = 6, TOTAL = FPS * DURATION;

  for (let f = 0; f < TOTAL; f++) {
    const framePath = path.join(outDir, "frame_" + String(f).padStart(4, "0") + ".png");
    await page.screenshot({ path: framePath, omitBackground: true }); // Alpha!
    await page.evaluate(() => window.stepFrame());
  }

  await browser.close();
}

renderCard(process.argv[2] || "1");
```

### Encode Cards with Alpha (WebM VP9)

```bash
# Encode PNG frames to WebM with transparency
ffmpeg -framerate 60 -i "frames/frame_%04d.png" \
  -c:v libvpx-vp9 -pix_fmt yuva420p -b:v 2M \
  card.webm
```

### Card Overlay FFmpeg

Position cards in **upper-right corner** (not bottom-right) for better visibility:

```bash
# Single song with animated card (6s video, card at t=10s after intro)
ffmpeg -i "video.mp4" -i "song.mp3" -i "card.webm" \
  -filter_complex \
    "[0:v][2:v]overlay=x=W-w-50:y=50:enable='between(t,10,16)'[v]" \
  -map "[v]" -map 1:a -c:v libx264 -preset fast -crf 18 -c:a aac -b:a 192k \
  -movflags +faststart "output.mp4"

# With intro + multiple songs (audio starts at intro)
ffmpeg -i intro.mp4 -i recording.mp4 -i outro.mp4 -i songs.m4a \
  -i card1.webm -i card2.webm \
  -filter_complex "
    [0:v][1:v][2:v]concat=n=3:v=1:a=0[base];
    [4:v]setpts=PTS+10/TB[c1];
    [5:v]setpts=PTS+205/TB[c2];
    [base][c1]overlay=x=W-w-50:y=50:enable='between(t,10,16)'[tmp1];
    [tmp1][c2]overlay=x=W-w-50:y=50:enable='between(t,205,211)'[vout]
  " \
  -map "[vout]" -map 3:a \
  -c:v libx264 -preset medium -crf 18 -c:a aac -b:a 192k \
  final.mp4
```

### Card Timing Formula

| Song | Card Appears | Card Duration | Animation |
|------|--------------|---------------|-----------|
| 1 | 0s | 6s | Baked into video |
| 2 | song1_duration | 6s | Baked into video |
| 3 | song1 + song2 duration | 6s | Baked into video |

Overlay pattern: `enable='between(t,{start},{start+6})':eof_action=pass`

## Multi-Song (No Cards)

```bash
# Concat audio, copy video
ffmpeg -i "video.mkv" -i "s1.mp3" -i "s2.mp3" \
  -filter_complex "[1:a][2:a]concat=n=2:v=0:a=1[music]" \
  -map 0:v -map "[music]" \
  -c:v copy -c:a aac -b:a 192k \
  -movflags +faststart "output.mp4"
```

## Core Commands

| Task | Command |
|------|---------|
| Duration | `ffprobe -v error -show_entries format=duration -of csv=p=0 "file"` |
| Download song | `yt-dlp -x --audio-format mp3 "ytsearch1:Artist Song"` |
| Get metadata | `yt-dlp --print-json --no-download "ytsearch1:query"` |
| Download thumbnail | `yt-dlp --write-thumbnail --convert-thumbnails jpg "URL"` |

## Dependencies

- `ffmpeg` + `ffprobe`
- `yt-dlp`
- `node` + `playwright` npm package (for intro and card video recording)

Install dependencies:
```bash
cd /path/to/video-obs-youtube-music
npm install playwright
npx playwright install chromium
```

Check: `./scripts/check-deps.sh`

## Remote Machine Parallel Rendering

For complex projects with long intro/outro sequences, distribute rendering across multiple machines.

### Available Machines

| Host | CPU | GPU | Best For |
|------|-----|-----|----------|
| `trj` | High core | Dual 4090 | NVENC encoding |
| `jain` | High core | None | CPU encoding, fast |
| `csd` | Medium | None | Frame rendering |
| `css` | Medium | None | Frame rendering |
| `fmd` | Medium | None | Frame rendering |

### Setup Remote Machines

```bash
# Each machine needs: node, playwright, chromium
ssh machine "npm install playwright && npx playwright install chromium"

# Copy HTML files to remote
scp intro.html machine:/tmp/
scp render_script.js machine:/tmp/
```

### Robust Render Script (with timeouts)

```javascript
// render-frames.js - Distributed rendering with retries
const { chromium } = require("playwright");
const fs = require("fs");
const path = require("path");

const START_FRAME = parseInt(process.argv[2]) || 0;
const END_FRAME = parseInt(process.argv[3]) || 100;
const HTML_FILE = process.argv[4] || '/tmp/intro.html';
const OUTPUT_DIR = process.argv[5] || '/tmp/frames';
const WIDTH = parseInt(process.argv[6]) || 1920;
const HEIGHT = parseInt(process.argv[7]) || 1080;

async function render() {
  if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true });

  const browser = await chromium.launch({
    headless: true,
    timeout: 120000  // 2 minute browser launch timeout
  });

  const page = await browser.newPage();
  page.setDefaultTimeout(60000);  // 60s for page operations

  await page.setViewportSize({ width: WIDTH, height: HEIGHT });
  await page.goto('file://' + path.resolve(HTML_FILE), { waitUntil: 'networkidle' });
  await page.waitForTimeout(1000);

  // Jump to start frame
  for (let i = 0; i < START_FRAME; i++) {
    await page.evaluate(() => window.stepFrame && window.stepFrame());
  }

  console.log(`Rendering frames ${START_FRAME} to ${END_FRAME}...`);
  const startTime = Date.now();

  for (let f = START_FRAME; f < END_FRAME; f++) {
    const framePath = path.join(OUTPUT_DIR, `frame_${String(f).padStart(5, '0')}.png`);

    // Retry logic for flaky screenshots
    for (let retry = 0; retry < 3; retry++) {
      try {
        await page.screenshot({ path: framePath });
        break;
      } catch (e) {
        if (retry === 2) throw e;
        await page.waitForTimeout(500);
      }
    }

    await page.evaluate(() => window.stepFrame && window.stepFrame());

    if ((f - START_FRAME) % 100 === 0) {
      const elapsed = (Date.now() - startTime) / 1000;
      const fps = (f - START_FRAME) / elapsed;
      console.log(`Frame ${f}/${END_FRAME} (${fps.toFixed(1)} fps)`);
    }
  }

  await browser.close();
  console.log(`Done! Rendered ${END_FRAME - START_FRAME} frames.`);
}

render().catch(e => { console.error(e); process.exit(1); });
```

### Parallel Frame Rendering

Split frame ranges across machines and render in parallel:

```bash
# Calculate total frames: duration_seconds * fps
TOTAL_FRAMES=5430  # 90.5 seconds at 60fps

# Distribute across 5 machines
ssh css "node /tmp/render.js 0 1086 /tmp/intro.html /tmp/frames" &
ssh csd "node /tmp/render.js 1086 2172 /tmp/intro.html /tmp/frames" &
ssh jain "node /tmp/render.js 2172 3258 /tmp/intro.html /tmp/frames" &
ssh fmd "node /tmp/render.js 3258 4344 /tmp/intro.html /tmp/frames" &
ssh trj "node /tmp/render.js 4344 5430 /tmp/intro.html /tmp/frames" &

wait  # Wait for all to complete
```

### Reallocating Slow Machines

If a machine is too slow, stop it and redistribute:

```bash
# Check progress
ssh slow_machine 'ls /tmp/frames/*.png | wc -l'

# Stop slow machine at frame N, redistribute to faster machines
ssh slow_machine 'pkill -f node'

# Give remaining frames to faster machines
ssh fast1 "node /tmp/render.js $STOPPED_AT $MIDPOINT /tmp/intro.html /tmp/frames" &
ssh fast2 "node /tmp/render.js $MIDPOINT $END /tmp/intro.html /tmp/frames" &
```

### Collect and Assemble Frames

```bash
# Download frames from each machine
rsync -avz css:/tmp/frames/ ./frames/
rsync -avz csd:/tmp/frames/ ./frames/
rsync -avz jain:/tmp/frames/ ./frames/
rsync -avz fmd:/tmp/frames/ ./frames/
rsync -avz trj:/tmp/frames/ ./frames/

# Verify frame count
ls frames/*.png | wc -l  # Should match TOTAL_FRAMES

# Assemble into video
ffmpeg -framerate 60 -i "frames/frame_%05d.png" \
  -c:v libx264 -pix_fmt yuv420p -crf 18 \
  output.mp4
```

### GPU-Accelerated Encoding on Remote

For machines with NVIDIA GPUs (e.g., dual 4090s), use NVENC:

```bash
# HEVC for 4K - fast encoding
ssh trj 'ffmpeg -i input.mp4 -c:v hevc_nvenc -preset p4 -b:v 20M output.mp4'

# H.264 for max compatibility
ssh trj 'ffmpeg -i input.mp4 \
  -c:v h264_nvenc -preset p4 -profile:v high -b:v 20M output.mp4'
```

### Frame Rate Matching

**Critical**: When concatenating videos, all inputs must have the same frame rate:

```bash
# Convert 30fps source to 60fps before concat
ffmpeg -i source_30fps.mkv -r 60 -c:v libx264 -crf 18 source_60fps.mp4

# Now concat with 60fps intro/outro
ffmpeg -i intro_60fps.mp4 -i source_60fps.mp4 -i outro_60fps.mp4 \
  -filter_complex "[0:v][1:v][2:v]concat=n=3:v=1:a=0[v]" \
  -map "[v]" output.mp4
```

## 4K Video Production

### Resolution Considerations

| Resolution | H.264 Support | HEVC Support | QuickTime | X/Twitter |
|------------|---------------|--------------|-----------|-----------|
| 1920x1080 | Yes | Yes | Yes | Yes |
| 3840x2160 | Yes (level 5.2) | Yes | Yes | **NO** |
| 4096x2304 | No (exceeds level) | Yes | Maybe | **NO** |

### Recommended 4K Workflow

```bash
# 1. Render intro/outro at desired resolution and 60fps
# 2. Convert source to 60fps (matching)
ffmpeg -i source.mkv -r 60 -c:v libx264 -crf 18 source_60fps.mp4

# 3. Assemble master at full resolution
ffmpeg -i intro.mp4 -i source_60fps.mp4 -i outro.mp4 -i audio.m4a \
  -filter_complex "[0:v][1:v][2:v]concat=n=3:v=1:a=0[v]" \
  -map "[v]" -map 3:a \
  -c:v libx264 -crf 18 -c:a aac -b:a 192k \
  master.mp4

# 4. Export platform-specific versions
# YouTube (keep 4K):
cp master.mp4 youtube_4k.mp4

# X/Twitter (MUST downscale to 1080p):
ffmpeg -i master.mp4 \
  -vf "scale=1920:1080:flags=lanczos" \
  -c:v libx264 -preset medium -crf 18 \
  -profile:v high -level:v 4.2 -pix_fmt yuv420p \
  -c:a aac -b:a 192k \
  -movflags +faststart \
  x_twitter_1080p60.mp4
```

## Gotchas

### Encoding
- `ytsearch1:` returns first result — include artist name for accuracy
- Cards/intros require video re-encode (`-c:v libx264`), basic remux can use `-c:v copy`
- `-movflags +faststart` required for YouTube/X streaming
- **Frame rate mismatch**: Concat of different fps causes bitrate/quality issues - convert first!
- **H.264 level limits**: 4096x2304 exceeds level 5.2, use HEVC or scale to 3840x2160

### Platform Compatibility
- **X/Twitter rejects 4K** - Always encode 1080p version for X, even if video plays in preview
- **QuickTime HEVC**: May not play - provide H.264 version for compatibility
- **X Premium** allows 4 hour videos, but still max 1920x1200 resolution

### Card Overlays
- Card overlay position: `x=W-w-50:y=50` = upper-right corner (50px margins)
- Card videos are 6 seconds with animation baked in (no ffmpeg fade needed)
- WebM format supports transparency for clean overlay compositing
- **Card transparency**: Use narrower cards (1900px) to avoid white rectangle artifacts

### Audio
- **Audio timing**: Start audio at intro (t=0), cards appear when screen recording starts

### Remote Rendering
- Remote machines may need SwiftShader for software WebGL rendering
- Always use timeout and retry logic in render scripts
- Monitor render speed - reallocate slow machines' work to faster ones
- Check disk space on remote machines before large frame transfers

Attribution

lev-oslev-os
View sourceMore from lev-os →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →