api-and-interface-desi…
Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints,…
PIL + ffmpeg slideshow videos: product reviews, promos, TikTok/Reels/Shorts.
$ npx -y skills add kevinnft/ai-agent-skills --skill social-media-slideshow-video --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/social-media-slideshow-videoContext preview
The summary Claude sees to decide when to auto-load this skill.
PIL + ffmpeg slideshow videos: product reviews, promos, TikTok/Reels/Shorts.
name: social-media-slideshow-video description: "PIL + ffmpeg slideshow videos: product reviews, promos, TikTok/Reels/Shorts." origin: unknown source_license: see upstream language: en
Use when users request: product review videos, promotional slideshow videos, TikTok/Reels/Shorts content from static images, photo-based video with text overlays, hijab/fashion/beauty review videos, unboxing recap videos, or any image-to-video social media content with designed slides.
| Layer | Tool | Purpose | |-------|------|---------| | Imaging | Pillow (PIL) | Slide design, text rendering, image manipulation | | Encoding | ffmpeg (CLI) | Frame sequence → MP4 encoding | | Core | Python 3 | Orchestration |
No GPU, no moviepy, no heavy dependencies needed.
**Critical: Do NOT store all frames as numpy arrays in memory.** A 15-second 1080×1920 video at 24fps = 360 frames × ~6MB each = 2.1GB RAM → OOM kill.
1. Render each SLIDE as a static PIL Image (5-10 slides in memory is fine) 2. For each slide, generate per-frame variations (fade, zoom) and SAVE AS PNG to tmpdir 3. Feed the PNG sequence to ffmpeg via -i pattern 4. Clean up temp files
Piping raw RGB frames to ffmpeg stdin causes deadlocks and broken pipe errors in many environments. The file-based approach is robust and debuggable.
| Platform | Resolution | Aspect | FPS | |----------|-----------|--------|-----| | TikTok / Reels / Shorts | 1080×1920 | 9:16 | 24 | | YouTube landscape | 1920×1080 | 16:9 | 24-30 | | Instagram square | 1080×1080 | 1:1 | 24 | | Story/Status | 1080×1920 | 9:16 | 24 |
def gradient_bg(w, h, c1, c2):
img = Image.new("RGB", (w, h))
px = img.load()
for y in range(h):
r = y / h
for x in range(w):
px[x, y] = (int(c1[0]*(1-r)+c2[0]*r), ...)
return imgmask = Image.new("L", (size, size), 0)
ImageDraw.Draw(mask).rounded_rectangle([0, 0, size, size], radius=35, fill=255)
# or .ellipse([0, 0, size, size], fill=255) for circular
frame.paste(photo, (x, y), mask)bdr = 8
border_img = Image.new("RGB", (size+bdr*2, size+bdr*2), border_color)
border_mask = Image.new("L", border_img.size, 0)
ImageDraw.Draw(border_mask).rounded_rectangle([0,0,...], radius=40, fill=255)
frame.paste(border_img, (x-bdr, y-bdr), border_mask)
frame.paste(photo, (x, y), photo_mask) # photo on topcard = Image.new("RGBA", (cw, ch), (255, 248, 240, 220))
cmask = Image.new("L", (cw, ch), 0)
ImageDraw.Draw(cmask).rounded_rectangle([0,0,cw,ch], radius=25, fill=220)
frame_rgba = frame.convert("RGBA")
frame_rgba.paste(card, (x, y), cmask)
frame = frame_rgba.convert("RGB")bg = prepare_photo(WIDTH, HEIGHT, zoom=1.5)
bg = bg.filter(ImageFilter.GaussianBlur(radius=20))
dark = Image.new("RGB", (WIDTH, HEIGHT), (40, 30, 30))
frame = Image.blend(bg, dark, 0.55)gmask = Image.new("L", (w, h), 255)
gd = ImageDraw.Draw(gmask)
for y in range(h - fade_height, h):
alpha = int(255 * (1 - (y - (h - fade_height)) / fade_height))
gd.rectangle([(0, y), (w, y)], fill=alpha)
frame.paste(photo, (0, 0), gmask)black = Image.new("RGB", (WIDTH, HEIGHT), (0, 0, 0))
if frame_idx < TRANSITION_FRAMES:
alpha = frame_idx / TRANSITION_FRAMES
out = Image.blend(black, slide_img, alpha)
elif frame_idx > total - TRANSITION_FRAMES:
alpha = (total - frame_idx) / TRANSITION_FRAMES
out = Image.blend(black, slide_img, alpha)cmd = [
"ffmpeg", "-y",
"-framerate", str(FPS),
"-i", os.path.join(tmpdir, "frame_%05d.png"),
"-c:v", "libx264",
"-preset", "fast",
"-crf", "23",
"-pix_fmt", "yuv420p",
"-movflags", "+faststart",
output_path,
]
subprocess.run(cmd, capture_output=True, text=True, timeout=300)def get_font(size, bold=False):
fp = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold \
else "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
if os.path.exists(fp):
return ImageFont.truetype(fp, size)
return ImageFont.load_default()Center text: `bbox = draw.textbbox((0,0), text, font=f); x = (WIDTH - (bbox[2]-bbox[0])) // 2`
| Slide type | Duration | Transition | |-----------|----------|------------| | Title | 3-4s | 0.3s fade | | Detail | 3-3.5s | 0.3s fade | | Verdict | 3.5-4s | 0.3s fade | | **Total** | 15-20s | — |
1. **OOM Kill**: Never accumulate all frames as numpy arrays. Use file-based pipeline. 2. **ffmpeg stdin pipe**: Deadlocks on long videos. Use PNG sequence input instead. 3. **pip on Ubuntu 24.04+**: Needs `--break-system-packages` flag. 4. **Emoji/Unicode in text**: DejaVu fonts don't render emoji. Use text descriptions or install emoji fonts. 5. **Color matching**: Extract dominant color from product photo
191 attribution-first agent skills for Hermes Agent, Claude Code, Cursor — one installer, 28 categories, searchable catalog. See NOTICE for upstream attribution.
Repo: kevinnft/ai-agent-skills
Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints,…
Tests in real browsers. Use when building or debugging anything that runs in a browser. Use when you need to inspect the DOM, capture console errors, analyze…
Automates CI/CD pipeline setup. Use when setting up or modifying build and deployment pipelines. Use when you need to automate quality gates, configure test…
Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to…
Simplifies code for clarity. Use when refactoring code for clarity without changing behavior. Use when code works but is harder to read, maintain, or extend…
Optimizes agent context setup. Use when starting a new session, when agent output quality degrades, when switching between tasks, or when you need to configure…