Skip to content

Commit d6c82c9

Browse files
committed
feat: add Manim promo video generation workflow
1 parent fc07f04 commit d6c82c9

2 files changed

Lines changed: 186 additions & 0 deletions

File tree

.github/workflows/video-build.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Build Promo Video
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
paths:
7+
- 'promo/**'
8+
release:
9+
types: [published]
10+
workflow_dispatch:
11+
12+
concurrency:
13+
group: video-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
permissions:
17+
contents: write
18+
19+
jobs:
20+
render-video:
21+
name: Render Promo Video
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- name: Set up Python
27+
uses: actions/setup-python@v5
28+
with:
29+
python-version: '3.11'
30+
31+
- name: Install Manim & dependencies
32+
run: |
33+
sudo apt-get update
34+
sudo apt-get install -y ffmpeg libcairo2-dev libpango1.0-dev
35+
pip install manim==0.18.1
36+
37+
- name: Render promo video (1080p)
38+
run: |
39+
cd promo
40+
manim render -qh --format mp4 promo_scene.py ProductPromo
41+
echo "Video rendered"
42+
43+
- name: Render social media variants
44+
run: |
45+
cd promo
46+
# Square (1080x1080) for Instagram/LinkedIn
47+
manim render -qh --format mp4 \
48+
--resolution 1080,1080 \
49+
promo_scene.py ProductPromo \
50+
-o ebuild_square.mp4 || true
51+
echo "Social variants rendered"
52+
53+
- name: List outputs
54+
run: |
55+
echo "=== Generated Videos ==="
56+
find promo/media -name "*.mp4" -exec ls -lh {} \;
57+
58+
- name: Upload video artifacts
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: promo-videos
62+
path: promo/media/videos/**/*.mp4
63+
retention-days: 90
64+
65+
- name: Attach to release
66+
if: github.event_name == 'release'
67+
uses: softprops/action-gh-release@v2
68+
with:
69+
files: promo/media/videos/**/*.mp4
70+
tag_name: ${{ github.event.release.tag_name }}
71+
fail_on_unmatched_files: false
72+
env:
73+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74+
75+
summary:
76+
name: Video Build Summary
77+
runs-on: ubuntu-latest
78+
needs: render-video
79+
if: always()
80+
steps:
81+
- name: Report
82+
run: |
83+
echo "## Promo Video Build" >> $GITHUB_STEP_SUMMARY
84+
echo "" >> $GITHUB_STEP_SUMMARY
85+
echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY
86+
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
87+
echo "| Product | ebuild |" >> $GITHUB_STEP_SUMMARY
88+
echo "| Render | ${{ needs.render-video.result }} |" >> $GITHUB_STEP_SUMMARY
89+
echo "| Trigger | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY

promo/promo_scene.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from manim import *
2+
3+
4+
class ProductPromo(Scene):
5+
"""ebuild — promotional video."""
6+
7+
def construct(self):
8+
self.camera.background_color = "#0f172a"
9+
accent = "#16a34a"
10+
11+
# ── Scene 1: Logo Reveal ──
12+
title = Text("ebuild", font_size=96, color=WHITE, weight=BOLD)
13+
tagline = Text(
14+
"EoS Embedded Build System",
15+
font_size=32, color=GRAY_B,
16+
)
17+
tagline.next_to(title, DOWN, buff=0.6)
18+
19+
self.play(Write(title), run_time=1.5)
20+
self.play(FadeIn(tagline, shift=UP * 0.3), run_time=0.8)
21+
self.wait(2)
22+
self.play(FadeOut(title), FadeOut(tagline))
23+
24+
# ── Scene 2: Feature Showcase ──
25+
features = [
26+
"Cross-Compilation Toolchains",
27+
"Incremental Builds",
28+
"Dependency Resolution",
29+
]
30+
for i, feat in enumerate(features):
31+
num = Text(
32+
f"0{i+1}", font_size=120, color=accent, weight=BOLD, font="Monospace",
33+
).set_opacity(0.15)
34+
num.to_edge(LEFT, buff=1.5).shift(UP * 0.5)
35+
36+
feat_text = Text(feat, font_size=52, color=WHITE, weight=BOLD)
37+
feat_text.next_to(num, RIGHT, buff=0.8).align_to(num, UP)
38+
39+
bar = Rectangle(
40+
width=8, height=0.06, color=accent, fill_opacity=1,
41+
)
42+
bar.next_to(feat_text, DOWN, buff=0.3, aligned_edge=LEFT)
43+
44+
self.play(
45+
FadeIn(num, shift=RIGHT * 0.5),
46+
Write(feat_text),
47+
GrowFromEdge(bar, LEFT),
48+
run_time=1.0,
49+
)
50+
self.wait(1.5)
51+
self.play(FadeOut(num), FadeOut(feat_text), FadeOut(bar), run_time=0.5)
52+
53+
# ── Scene 3: Architecture Flash ──
54+
arch_title = Text("Architecture", font_size=28, color=GRAY_B)
55+
arch_title.to_edge(UP, buff=0.8)
56+
57+
boxes = VGroup()
58+
labels = ["Core", "API", "Runtime"]
59+
for j, lbl in enumerate(labels):
60+
box = RoundedRectangle(
61+
corner_radius=0.15, width=2.5, height=1.2,
62+
stroke_color=accent, fill_color="#1e293b", fill_opacity=1,
63+
)
64+
box_label = Text(lbl, font_size=22, color=WHITE)
65+
box_label.move_to(box)
66+
grp = VGroup(box, box_label)
67+
boxes.add(grp)
68+
boxes.arrange(RIGHT, buff=0.6)
69+
70+
arrows = VGroup()
71+
for j in range(len(boxes) - 1):
72+
arr = Arrow(
73+
boxes[j].get_right(), boxes[j + 1].get_left(),
74+
color=accent, buff=0.1, stroke_width=2,
75+
)
76+
arrows.add(arr)
77+
78+
self.play(FadeIn(arch_title))
79+
self.play(LaggedStart(*[FadeIn(b, shift=UP * 0.3) for b in boxes], lag_ratio=0.2))
80+
self.play(LaggedStart(*[GrowArrow(a) for a in arrows], lag_ratio=0.15))
81+
self.wait(2)
82+
self.play(FadeOut(arch_title), FadeOut(boxes), FadeOut(arrows))
83+
84+
# ── Scene 4: CTA ──
85+
cta = Text("ebuild", font_size=72, color=WHITE, weight=BOLD)
86+
url = Text(
87+
"github.com/embeddedos-org/ebuild",
88+
font_size=24, color=accent,
89+
)
90+
url.next_to(cta, DOWN, buff=0.5)
91+
badge = Text("Open Source", font_size=18, color=GRAY_B)
92+
badge.next_to(url, DOWN, buff=0.4)
93+
94+
self.play(Write(cta), run_time=1.0)
95+
self.play(FadeIn(url, shift=UP * 0.2))
96+
self.play(FadeIn(badge, shift=UP * 0.2))
97+
self.wait(3)

0 commit comments

Comments
 (0)