Communitygithub.com

JaviMaligno/agilabs-video-studio

Remotion으로 기사 설명 영상의 장면을 제작합니다. 승인된 스크립트에서 src/timeline.ts를 작성하고, 애니메이션 컴포넌트(Counter, BarCompare, FlowDiagram, KeyQuote, StatGrid)를 선택 및 구성하며, 테스트된 frameState로 맞춤형 컴포넌트를 추가하고, 세로형 안전 영역을 준수하며 렌더링된 스틸을 직접 확인합니다. 이 플러그인으로 제작된 영상의 장면을 구성, 변경 또는 디버깅할 때 사용하세요.

agilabs-video-studio란 무엇인가요?

이 스킬은 Remotion 기반의 AI 비디오 제작 플러그인으로, 기사 설명 영상의 각 장면을 코드로 구성하고 제어할 수 있게 해줍니다. 사용자는 승인된 스크립트를 바탕으로 `src/timeline.ts` 파일에 영상의 전체 장면 순서를 정의하며, `intro`(훅 카드), `anim`(등록된 애니메이션 컴포넌트), `outro`(종료 카드) 세 가지 유형의 장면을 조합합니다. 각 장면 내에서 `say()` 함수를 사용하여 내레이션 라인을 추가하고, 각 라인의 시작 시간(비트)에 따라 애니메이션 컴포넌트의 단계(예: 막대 성장, 노드 등장)가 자동으로 동기화됩니다. 제공되는 컴포넌트 카탈로그에는 Counter(숫자 카운트업), BarCompare(막대 그래프 비교), FlowDiagram(순서도), KeyQuote(핵심 인용문 강조), StatGrid(통계 타일)가 포함되어 있으며, 각 컴포넌트는 고유한 props를 통해 세밀하게 제어됩니다. 또한, 스크립트에서 'bespoke'로 표시된 장면을 위해 완전히 새로운 맞춤형 애니메이션 컴포넌트를 추가할 수 있습니다. 이때는 먼저 순수 함수로 `frameState`를 작성하고 테스트한 후, React 컴포넌트로 렌더링하고 등록하는 단계를 따릅니다. 모든 작업은 1080x1920의 세로형 안전 영역(Stage: y=280~1280) 내에서 이루어져야 하며, 최종 영상 렌더링 전에 스틸 이미지를 생성하여 시각적으로 검토하는 과정이 필수적입니다. `npm run lint` 명령어로 대상 길이, 자막 속도, 컴포넌트 등록 상태 등을 자동 검사할 수 있습니다.

지원 대상~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/JaviMaligno/agilabs-video-studio/tree/HEAD/skills/explainer-animation

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

설명 애니메이션

비디오는 src/timeline.ts에 있는 장면들의 순서 목록입니다. 세 가지 종류가 있습니다:

  • intro: 훅(hook) 카드입니다. 첫 번째 줄은 훅이며, 크게 표시되고 단어가 음성에 맞춰 강조됩니다(자체 자막 역할을 합니다).
  • anim: 레지스트리에 있는 하나의 애니메이션 컴포넌트로, 장면의 내레이션 라인과 동기화됩니다.
  • outro: AGILabs 배지, javieraguilar.ai 및 SETTINGS.articleTitle의 기사 제목이 포함된 종료 카드입니다.

타임라인 작성하기

import type { Cuts } from './types.ts';
import { L, anim, intro, outro, say, videos } from './narrate.ts';

const build = (): Cuts => ({
  main: [
    intro('hook', [say(0.4, 'EN hook line.', 'ES hook line.')]),
    anim(
      'result',
      'BarCompare',
      { bars: [{ label: L('이유 있음', 'Con el porqué'), value: 14, display: '14/20', highlight: true },
               { label: L('이유 없음', 'Sin él'), value: 4, display: '4/20' }], max: 20 },
      [say(0.3, '첫 번째 줄.', 'Primera línea.'), say(0, '두 번째 줄.', 'Segunda línea.')],
      { min: 4 },
    ),
    outro('outro', [say(0.4, 'javieraguilar.ai에서 더 보기.', 'Más en javieraguilar.ai.')]),
  ],
});

export const VIDEOS = videos(build);
export const TIMELINE = VIDEOS.en.main;
  • say(at, en, es, spoken?): 하나의 내레이션 라인입니다. at은 해당 라인이 시작될 수 있는 가장 빠른 장면 시작 시간(초)입니다. 라인은 절대 겹치지 않으며(0.35초 간격), 장면은 마지막 라인이 끝난 후 0.8초 또는 min 값 중 더 긴 시간까지 지속됩니다. spoken ({ en?, es? })은 TTS가 자막 텍스트와 다르게 읽어야 할 때 사용하는 값입니다(narration 참조).
  • 비트(Beats). 장면 내 각 라인의 시작 시간을 비트라고 합니다. 컴포넌트는 비트 i에서 단계 i를 재생하므로(막대 i가 성장하고, 노드 i가 나타나고, 타일 i가 튀어나옴), 공개하려는 단계당 하나의 라인을 작성하세요. TTS 이전에는 비트가 길이 추정치에서 나오지만, npm run tts 이후에는 실제 오디오에서 비트가 생성되므로 내레이션 후에 스틸을 다시 확인하세요.
  • L(en, es): 이중 언어 화면 레이블입니다. 일반 문자열은 두 언어 모두에 사용됩니다.
  • 장면 ID는 고유해야 합니다. 라인 키(<scene>-01, ...)는 오디오 파일의 이름을 지정합니다.
  • anim의 opts.chapter는 장면에 저장되지만 아직 어떤 컴포넌트에서도 그려지지 않습니다.
  • 변경 후에는 항상 npm run lint를 실행하세요. targetSeconds 대비 길이(±20 %), 0.8초 미만 또는 초당 17자를 초과하는 자막 큐, 레지스트리에서 누락된 컴포넌트 이름을 확인합니다.

컴포넌트 카탈로그

모든 컴포넌트는 장면에서 t, beats, dur, lang, vertical을 받습니다. 아래 props는 타임라인에서 가져옵니다. 화면에 표시되는 숫자는 기사의 정확한 형식을 사용합니다. 기사에서 "14/20", "0.0018" 또는 "83 %"라고 쓰여 있다면 계산된 텍스트보다 display / 문자열 value를 선호하세요.

컴포넌트용도Props
Counter하나의 헤드라인 숫자가 카운트업되고 레이블이 아래에 표시됩니다value: number, label: Label, prefix?, suffix?, decimals? (0-8), beat? (카운트가 시작되는 비트 인덱스, 기본값 0)
BarCompare2-5개의 가로 막대가 비트당 하나씩 성장하며, 강조된 막대는 액센트 그라데이션으로 표시됩니다bars: { label: Label; value: number; display?: string; highlight?: boolean; at?: number }[], max? (스케일 상한, 기본값은 가장 큰 값), unit? (display가 없을 때 추가됨)
FlowDiagram상자와 화살표가 순서대로 나타납니다. broken 엣지는 점선으로 표시되고 흐리게 처리되며 간격이 있습니다nodes: { id; label: Label; at?: number }[], edges: { from; to; label?: Label; broken?: boolean }[], layout: 'vertical' | 'horizontal' (9:16 컷에는 vertical 사용)
KeyQuote핵심 문장이 단어별로 표시되며, 강조된 단어는 액센트 색상과 밑줄로 표시됩니다text: Label, emphasis?: string[] (단어 또는 구절, 대소문자 및 구두점 무시하고 일치), beat?
StatGrid2-4개의 숫자 타일이 비트당 하나씩 튀어나옵니다stats: { value: string; label: Label; at?: number }[]

막대, 노드 또는 타일의 at은 자체 비트 대신 장면 시작 시간(초)에 고정됩니다. 비트나 at이 없는 단계는 이전 단계 이후 0.6초 후에 진행됩니다. 모든 컴포넌트는 장면이 끝날 때까지 완료되도록 맞춰집니다. 마지막 프레임은 항상 완료된 상태를 표시합니다.

전체 라이브러리를 보려면: npm run studio를 실행하고 Gallery-vertical을 열거나 스틸을 렌더링하세요.

맞춤형 컴포넌트 추가하기

스크립트가 장면을 "bespoke"로 표시한 경우:

  1. 상태(state)를 먼저, 테스트와 함께. props 타입과 순수 frameState(props, t, beats, dur) 함수(일반 숫자와 문자열 반환)를 포함한 src/anim/<Name>.state.ts를 작성하세요. 거기에는 Remotion이나 React 임포트가 없어야 합니다. src/anim/steps.ts (sceneTime, stepTimes, fit, progress, beatAt, easeOutCubic)를 기반으로 구축하여 완전성을 확보하세요. 모든 t에서 유한한 값, [0, 1] 범위의 진행 필드, 첫 번째 비트 이전에는 아무것도 표시되지 않음, t = dur에서의 최종 상태.
  2. test/anim.test.ts의 CASES에 케이스를 추가하고(자체 동작 테스트 포함), npm test를 실행하세요. t < 0, 모든 비트, dur, dur 이후를 확인하며, 누락되거나 늦거나 매우 짧은 비트도 테스트합니다.
  3. 그리기 src/anim/<Name>.tsx에서: frameState를 호출하고, <Stage vertical={...}> 내부에 그리고, src/brand.ts의 브랜드 토큰(C, GRADIENT, alpha)과 Inter를 사용하세요. 새로운 색상이나 글꼴은 안 됩니다.
  4. 등록하기 src/anim/index.ts의 ANIMS에 추가하세요. ANIMS는 이름의 일반 객체 리터럴로 유지하세요. npm run lint가 소스에서 읽습니다.
  5. 선택적으로 src/Gallery.tsx에 샘플을 추가하세요. 재사용 가능하다면 플러그인의 템플릿에 다시 속합니다.

생성된 이미지. 대부분의 장면은 코드(SVG 및 브랜드 토큰의 CSS)로 그려야 합니다. 장면에 생성된 이미지(배경, 기본 이미지, 질감이 있는 순간)가 정말로 필요한 경우, 이미지 모델(예: Codex CLI의 이미지 생성)이 만들 수 있습니다. public/ 아래에 저장하고, staticFile()로 로드하고, 브랜드 팔레트 내에 유지하고, 스틸을 확인하세요.

세로형 안전 영역 (1080x1920)

  • 플랫폼 UI는 상단 약 220px, 하단 약 380px를 덮습니다. 측면은 72px를 유지하세요.
  • 애니메이션은 스테이지 내에서만 그려집니다: y = 280(AGILabs 배지 아래)에서 y = 1280(두 줄의 자막 위)까지, 너비 936px. Stage / stageOf()가 해당 박스를 제공합니다.
  • 자막은 스테이지 아래에 위치하며, 최대 두 줄, 약 32자, 너비 약 900px 이내입니다.

스틸: 확인하세요

비디오를 렌더링하기 전에 스틸을 렌더링하고 모든 PNG를 읽으세요. 깔끔한 린트나 빌드는 오버플로우, 잘린 텍스트 또는 잘못된 숫자를 보여주지 않습니다.

npx remotion still src/index.ts Main-EN-vertical out/stills/f0150.png --frame=150

각 장면의 마지막 단계가 재생된 후 한 장의 스틸을 찍고, 약 3초마다(30fps에서 90프레임마다) 한 장씩 더 찍으세요. 확인할 사항: AGILabs 배지와 그 외 브랜딩 없음, 안전 영역 밖으로 나간 것 없음, 텍스트 상자 넘침 없음, 모든 숫자가 기사와 일치함, 화면의 자막이 해당 순간의 애니메이션과 일치함.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

관련 스킬