#!/usr/bin/env python3 """Add subtitle text to each screenshot, then build video with ffmpeg.""" import os, subprocess from PIL import Image, ImageDraw, ImageFont DEMO_DIR = os.path.dirname(os.path.abspath(__file__)) FONT_PATH = "/System/Library/Fonts/STHeiti Medium.ttc" FONT_SIZE = 36 # (filename, subtitle_text) screenshots = [ ("s01-dashboard-top.png", "总部驾驶舱 — 全局经营总览,KPI一屏掌握"), ("s02-dashboard-charts.png", "象限散点图·瀑布图·平台成本率 — 数据深度分析"), ("s03-regional-overview.png", "区域经理工作台 — 12区经营汇总,红黄绿风险分布"), ("s04-store-manager-full.png", "店长工作台 — 经营概览·指标进度·SKU备货·任务闭环"), ("s05-store-manager-top.png", "店长工作台 — 异常标注+指标进度条,几分钟看完就知道今天干什么"), ("s06-store-detail-overview.png", "门店详情·概览 — 菜百店全链路:KPI·日度趋势·平台经济性"), ("s07-store-detail-meal-period.png", "门店详情·餐段分析 — 午市/晚市/下午茶/早市客流与客单价"), ("s08-store-detail-category.png", "门店详情·品类结构 — 销售集中度与搭售机会分析"), ("s09-store-detail-cost.png", "门店详情·成本分析 — 理论成本vs实际成本,精准定位差异"), ("s10-store-detail-member.png", "门店详情·会员分析 — RFM分层,沉睡会员召回机会"), ("s11-store-detail-anomaly.png", "门店详情·异常账单 — 风险内控,自动识别与闭环处理"), ("s12-monthly-review-summary.png", "月度验收·验收汇总 — 从数字化到智能化,只推可执行指令"), ("s13-monthly-review-grade.png", "月度验收·升降级面板 — P0/P1/P2等级变化,复制盈利模型"), ("s14-monthly-review-completion.png","月度验收·任务完成率 — 按优先级汇总,闭环验证改善效果"), ("s15-monthly-review-activity.png", "月度验收·活动清单 — 逐活动贡献毛利,停改留由数据驱动"), ("s16-monthly-review-sku.png", "月度验收·SKU治理 — ABC分类,长尾精简,标准化输出"), ] sub_dir = os.path.join(DEMO_DIR, "sub") os.makedirs(sub_dir, exist_ok=True) font = ImageFont.truetype(FONT_PATH, FONT_SIZE) for fname, text in screenshots: src = os.path.join(DEMO_DIR, fname) img = Image.open(src).convert("RGBA") w, h = img.size # Create overlay for text background bar overlay = Image.new("RGBA", (w, h), (0, 0, 0, 0)) draw = ImageDraw.Draw(overlay) # Measure text bbox = draw.textbbox((0, 0), text, font=font) tw = bbox[2] - bbox[0] th = bbox[3] - bbox[1] # Draw semi-transparent black bar at bottom bar_h = th + 30 bar_y = h - bar_h draw.rectangle([0, bar_y, w, h], fill=(0, 0, 0, 180)) # Draw text centered tx = (w - tw) // 2 ty = bar_y + 15 draw.text((tx, ty), text, font=font, fill=(255, 255, 255, 255)) # Composite result = Image.alpha_composite(img, overlay).convert("RGB") dst = os.path.join(sub_dir, fname) result.save(dst, "PNG") print(f" {fname} -> sub/{fname}") print(f"\nGenerated {len(screenshots)} annotated screenshots in sub/") # Build video print("\nBuilding video with ffmpeg...") cmd = [ "ffmpeg", "-y", "-framerate", "1/2", "-pattern_type", "glob", "-i", os.path.join(sub_dir, "s*.png"), "-c:v", "libx264", "-pix_fmt", "yuv420p", "-vf", "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:0x0F0F0F", "-r", "30", os.path.join(DEMO_DIR, "product-demo.mp4") ] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode == 0: print("Video built: product-demo.mp4") else: print(f"Error: {result.stderr[-500:]}")