manus-skills/.kortix/opencode/skills/media/motion-graphics/presets/thumbnail-presets.py
zhukang 1c7abd81d1 All 310 Manus skill-hub community skills as a Kortix marketplace registry
- 310 registry:skill items, 1528 files, 7 categories (hub tags)
- registry.json generated by @kortix/registry buildRegistry
- nested SKILL.md in sn-da-* renamed SUBSKILL.md (no phantom skills)
- provenance + licenses: SOURCES.md
2026-09-15 13:37:20 +08:00

399 lines
11 KiB
Python

"""
Thumbnail Generation Presets
Ready-to-use functions for creating video thumbnails and title cards
"""
from manim import *
import numpy as np
# =============================================================================
# ASPECT RATIO CONFIGURATIONS
# =============================================================================
ASPECT_RATIOS = {
"youtube": {"width": 1920, "height": 1080, "name": "16:9"},
"youtube_short": {"width": 1080, "height": 1920, "name": "9:16"},
"instagram": {"width": 1080, "height": 1080, "name": "1:1"},
"twitter": {"width": 1200, "height": 675, "name": "16:9"},
"linkedin": {"width": 1200, "height": 627, "name": "1.91:1"},
}
# =============================================================================
# THUMBNAIL SCENE BASE
# =============================================================================
class ThumbnailScene(Scene):
"""Base class for thumbnail generation."""
# Override in subclass
TITLE = "Your Title Here"
SUBTITLE = ""
ASPECT_RATIO = "youtube"
def construct(self):
# Set up frame for aspect ratio
config = ASPECT_RATIOS.get(self.ASPECT_RATIO, ASPECT_RATIOS["youtube"])
# Build thumbnail
self.create_background()
self.create_main_visual()
self.create_text_overlay()
# Hold frame (for image export)
self.wait(0.1)
def create_background(self):
"""Override to customize background."""
# Default gradient background
bg = Rectangle(
width=config.frame_width + 1,
height=config.frame_height + 1,
fill_opacity=1,
stroke_width=0
)
bg.set_color(color=[BLUE_E, PURPLE_E])
self.add(bg)
def create_main_visual(self):
"""Override to add main visual element."""
pass
def create_text_overlay(self):
"""Override to customize text."""
title = Text(
self.TITLE,
font_size=72,
weight=BOLD,
color=WHITE
)
title.move_to(ORIGIN)
if self.SUBTITLE:
subtitle = Text(
self.SUBTITLE,
font_size=36,
color=GREY_A
)
subtitle.next_to(title, DOWN, buff=0.5)
self.add(subtitle)
self.add(title)
# =============================================================================
# PRESET THUMBNAIL STYLES
# =============================================================================
def create_math_thumbnail(scene, title, equation, colors=None):
"""
Create a math-focused thumbnail.
Args:
scene: The Scene instance
title: Main title text
equation: LaTeX equation string
colors: Optional dict of {substring: color}
"""
if colors is None:
colors = {}
# Dark gradient background
bg = Rectangle(
width=20, height=12,
fill_opacity=1, stroke_width=0
)
bg.set_color(color=[BLUE_E, BLACK])
scene.add(bg)
# Equation (main focus)
eq = MathTex(equation, font_size=96)
for substr, color in colors.items():
eq.set_color_by_tex(substr, color)
eq.move_to(ORIGIN)
# Title above
title_text = Text(title, font_size=56, weight=BOLD, color=WHITE)
title_text.to_edge(UP, buff=0.8)
# Decorative elements
line_left = Line(LEFT * 6, LEFT * 2, color=BLUE, stroke_width=4)
line_right = Line(RIGHT * 2, RIGHT * 6, color=BLUE, stroke_width=4)
line_left.next_to(title_text, DOWN, buff=0.3)
line_right.next_to(title_text, DOWN, buff=0.3)
scene.add(bg, eq, title_text, line_left, line_right)
def create_concept_thumbnail(scene, title, icon_mobject, subtitle=""):
"""
Create a concept-focused thumbnail with icon.
Args:
scene: The Scene instance
title: Main title text
icon_mobject: A Mobject to use as the central icon
subtitle: Optional subtitle
"""
# Gradient background
bg = Rectangle(width=20, height=12, fill_opacity=1, stroke_width=0)
bg.set_color(color=[PURPLE_E, BLUE_E])
scene.add(bg)
# Icon (scaled and centered)
icon = icon_mobject.copy()
icon.scale_to_fit_height(4)
icon.move_to(ORIGIN)
# Add glow effect
glow = icon.copy()
glow.set_color(WHITE)
glow.set_opacity(0.3)
glow.scale(1.2)
scene.add(glow)
scene.add(icon)
# Title
title_text = Text(title, font_size=64, weight=BOLD, color=WHITE)
title_text.to_edge(UP, buff=0.6)
scene.add(title_text)
# Subtitle
if subtitle:
sub = Text(subtitle, font_size=32, color=GREY_A)
sub.to_edge(DOWN, buff=0.8)
scene.add(sub)
def create_comparison_thumbnail(scene, left_text, right_text, vs_text="VS"):
"""
Create a comparison/versus thumbnail.
Args:
scene: The Scene instance
left_text: Text for left side
right_text: Text for right side
vs_text: Center divider text
"""
# Split background
left_bg = Rectangle(width=8, height=12, fill_opacity=1, stroke_width=0)
left_bg.set_color(BLUE_E)
left_bg.shift(LEFT * 4)
right_bg = Rectangle(width=8, height=12, fill_opacity=1, stroke_width=0)
right_bg.set_color(RED_E)
right_bg.shift(RIGHT * 4)
scene.add(left_bg, right_bg)
# VS circle
vs_circle = Circle(radius=1, fill_opacity=1, fill_color=WHITE, stroke_width=0)
vs_label = Text(vs_text, font_size=48, weight=BOLD, color=BLACK)
vs_group = VGroup(vs_circle, vs_label)
scene.add(vs_group)
# Left text
left = Text(left_text, font_size=56, weight=BOLD, color=WHITE)
left.move_to(LEFT * 4)
scene.add(left)
# Right text
right = Text(right_text, font_size=56, weight=BOLD, color=WHITE)
right.move_to(RIGHT * 4)
scene.add(right)
def create_numbered_thumbnail(scene, number, title, subtitle=""):
"""
Create a numbered episode/part thumbnail.
Args:
scene: The Scene instance
number: Episode/part number
title: Main title
subtitle: Optional subtitle
"""
# Background
bg = Rectangle(width=20, height=12, fill_opacity=1, stroke_width=0)
bg.set_color(color=[GREY_E, BLACK])
scene.add(bg)
# Large number
num = Text(str(number), font_size=200, weight=BOLD, color=BLUE)
num.set_opacity(0.3)
num.move_to(LEFT * 3)
scene.add(num)
# Title
title_text = Text(title, font_size=64, weight=BOLD, color=WHITE)
title_text.move_to(RIGHT * 1 + UP * 0.5)
scene.add(title_text)
# Part label
part_label = Text(f"PART {number}", font_size=28, color=BLUE)
part_label.next_to(title_text, UP, buff=0.5)
scene.add(part_label)
# Subtitle
if subtitle:
sub = Text(subtitle, font_size=32, color=GREY_A)
sub.next_to(title_text, DOWN, buff=0.5)
scene.add(sub)
def create_quote_thumbnail(scene, quote, author=""):
"""
Create a quote-style thumbnail.
Args:
scene: The Scene instance
quote: The quote text
author: Optional author attribution
"""
# Dark background
bg = Rectangle(width=20, height=12, fill_opacity=1, stroke_width=0)
bg.set_color(BLACK)
scene.add(bg)
# Quote marks
open_quote = Text('"', font_size=200, color=BLUE)
open_quote.set_opacity(0.5)
open_quote.to_corner(UL, buff=0.5)
scene.add(open_quote)
close_quote = Text('"', font_size=200, color=BLUE)
close_quote.set_opacity(0.5)
close_quote.to_corner(DR, buff=0.5)
scene.add(close_quote)
# Quote text
quote_text = Text(
quote,
font_size=48,
color=WHITE,
line_spacing=1.5
)
quote_text.move_to(ORIGIN)
# Wrap if too wide
if quote_text.width > 12:
quote_text.scale_to_fit_width(12)
scene.add(quote_text)
# Author
if author:
author_text = Text(f"— {author}", font_size=32, color=GREY_A)
author_text.next_to(quote_text, DOWN, buff=0.8)
scene.add(author_text)
# =============================================================================
# TITLE CARD PRESETS
# =============================================================================
class IntroTitleCard(Scene):
"""Animated intro title card."""
TITLE = "Your Title"
SUBTITLE = "Your Subtitle"
DURATION = 3
def construct(self):
# Background
bg = Rectangle(width=20, height=12, fill_opacity=1, stroke_width=0)
bg.set_color(color=[BLUE_E, BLACK])
self.add(bg)
# Title
title = Text(self.TITLE, font_size=72, weight=BOLD, color=WHITE)
title.move_to(UP * 0.5)
# Subtitle
subtitle = Text(self.SUBTITLE, font_size=36, color=GREY_A)
subtitle.next_to(title, DOWN, buff=0.5)
# Underline
underline = Line(LEFT * 4, RIGHT * 4, color=BLUE, stroke_width=4)
underline.next_to(subtitle, DOWN, buff=0.5)
# Animate
self.play(Write(title), run_time=1)
self.play(FadeIn(subtitle, shift=UP * 0.2), run_time=0.5)
self.play(Create(underline), run_time=0.5)
self.wait(self.DURATION - 2)
class OutroTitleCard(Scene):
"""Animated outro/end card."""
THANKS_TEXT = "Thanks for watching!"
CTA_TEXT = "Subscribe for more"
DURATION = 4
def construct(self):
# Background
bg = Rectangle(width=20, height=12, fill_opacity=1, stroke_width=0)
bg.set_color(BLACK)
self.add(bg)
# Thanks
thanks = Text(self.THANKS_TEXT, font_size=64, weight=BOLD, color=WHITE)
thanks.move_to(UP * 1)
# CTA
cta = Text(self.CTA_TEXT, font_size=36, color=BLUE)
cta.move_to(DOWN * 0.5)
# Animate
self.play(FadeIn(thanks, scale=1.2), run_time=1)
self.wait(0.5)
self.play(FadeIn(cta, shift=UP * 0.3), run_time=0.5)
self.wait(self.DURATION - 2)
self.play(FadeOut(thanks), FadeOut(cta), run_time=0.5)
# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================
def export_thumbnail(scene_class, output_path, **scene_kwargs):
"""
Export a thumbnail scene as an image.
Usage:
export_thumbnail(MyThumbnail, "thumbnail.png", TITLE="Custom Title")
"""
# Set scene attributes
for key, value in scene_kwargs.items():
setattr(scene_class, key, value)
# Configure for image export
config.frame_rate = 1
config.pixel_width = 1920
config.pixel_height = 1080
# Render
scene = scene_class()
scene.render()
# The output will be in media/images/
def batch_export_thumbnails(scenes, output_dir):
"""
Export multiple thumbnails.
Args:
scenes: List of (scene_class, filename, kwargs) tuples
output_dir: Directory for output files
"""
import os
os.makedirs(output_dir, exist_ok=True)
for scene_class, filename, kwargs in scenes:
output_path = os.path.join(output_dir, filename)
export_thumbnail(scene_class, output_path, **kwargs)
print(f"Exported: {output_path}")