clipwise: 긴 비디오 → 세로형 Shorts
모든 작업은 로컬에서 실행됩니다 (CPU도 가능; CUDA가 있으면 자막 추출에 사용됨).
SKILL_DIR은 이 파일이 있는 폴더를 의미합니다. 모든 명령어는
python3 SKILL_DIR/scripts/run.py ... 형식입니다; 스크립트는 자동으로 clipwise 가상 환경으로 전환됩니다.
작업 결과는 ~/clipwise-work/<job>/ ($CLIPWISE_WORK) 또는 --workdir DIR에 기록됩니다.
0. 설정 확인 (매 세션, 한 번)
python3 SKILL_DIR/scripts/run.py check
ready로 끝나야 합니다. 그렇지 않으면 사용자에게 clipwise에 일회성 설정(약 2GB: Python 패키지 + whisper 모델)이 필요하다고 알리고, SKILL_DIR/setup.sh를 읽어보라고 요청한 후 동의하면 bash SKILL_DIR/setup.sh를 실행하라고 안내하세요. ffmpeg 또는 Python 3.10+가 없으면 setup.sh에서 해당 OS에 맞는 설치 명령어를 출력합니다; 시스템 패키지를 묻지 않고 설치하지 마세요.
작업 흐름
- 준비 (가져오기 + 자막 추출, 한 명령어):
작업 폴더에 출력:python3 SKILL_DIR/scripts/run.py prep "<경로 또는 링크>" --job <이름> [--start S --max-seconds N] [--cookies cookies.txt] [--model large-v3-turbo] [--language xx]source.mp4(첫 번째 비디오 + 오디오 스트림, SDR, 재인코딩 시 CFR 30),probe.json,transcript.txt(세그먼트당[시작-종료] (wN) 텍스트한 줄, SOURCE 초),transcript.json,words.tsv. 긴 비디오의 경우--start/--max-seconds를 사용하여 클리핑할 부분을 지정하세요; 자막 추출은 CPU에서 약 0.6~1배 실시간 속도입니다. - 직접 구간을 선택하세요.
transcript.txt(그리고 상단의# WARNING줄)를SKILL_DIR/references/viral-rubric.md와 함께 읽으세요.<job>/picks.json을 작성하세요: 각 선택은 10~50초, 후크가 먼저, 깔끔한 페이오프 엔딩. 개수: 아래 규칙 참조. - 렌더링 전 승인을 받으세요. 선택별로 한 줄씩 표시: 타임스탬프, 후크, 제목, 한 줄 이유. 작은 CPU에서 클립당 약 1~2분이 소요됩니다. 수동으로 자막 텍스트를 작성한 경우(비영어 또는 혼용 언어), 해당 줄도 함께 표시하세요.
- 렌더링:
python3 SKILL_DIR/scripts/run.py render --job <이름> [--fast] [--only 2] [--music bed.mp3]<job>/out/에 선택별로:clip_<id>.mp4,clip_<id>.txt(제목, 설명, 고정 댓글, 해시태그, 이유),clip_<id>.json(프로브 수치, 레이아웃, 얼굴 범위, 효과음, 타이밍),clip_<id>_cutmap.json,clip_<id>_sheet.png.--fast= 미리보기 품질. - 모든
clip_<id>_sheet.png를 이미지/비전 도구로 확인하세요 (10프레임; 빨간 선 = y 1620, 플랫폼 UI 아래). 확인할 사항: 얼굴이 프레임 안에 있는지, 자막이 읽을 수 있는지, 자막이 얼굴 위에 있지 않은지, 빨간 선 아래에 중요한 내용이 없는지, 색상이 바래지 않았는지. 수정(선택, 자막 재정의,--layout,--font-size)하고--only <id>로 다시 렌더링하세요. - MP4 경로와
.txt내용을 전달하세요.run.py clean --job <이름>(source.mp4 및 임시 디렉토리 삭제,out/유지)을 제안하세요.
비디오당 Shorts 개수
-
사용자가 숫자를 지정한 경우 ("<링크>에서 Shorts 5개 만들어줘"): 정확히 그 수만큼 만듭니다. 비디오에 그만한 강력한 순간이 없으면 렌더링 전에 말하고 정직한 숫자를 제안하세요("체크리스트를 통과하는 순간은 3개입니다; 나머지 2개는 약할 것입니다").
-
숫자가 주어지지 않은 경우: 소스 길이(또는 자막 추출된 창)에 따라 조정:
소스 길이 Shorts 5분 미만 1-2 5-15분 3-5 15-45분 5-8 45분 이상 (팟캐스트, 강연) 8-15 -
루브릭 체크리스트를 통과한 순간만 포함하세요. 숫자를 맞추기 위해 약한 클립으로 채우지 마세요.
picks.json (최소)
[{"id": "1", "start": 65.5, "end": 110.3, "title": "모든 홍수가 기후 변화 때문인가?",
"hook_start": 74.85, "hook_end": 80.1, "payoff_start": 102.0,
"why": "처음에 예상치 못한 주장을 하고, 마지막에 해결합니다.", "comment_bait": "이거 알고 있었나요?",
"description": "...", "hashtags": ["#shorts", "#기후"],
"caption_overrides": [{"w": 219, "text": ""}, {"start": 90.1, "end": 92.4, "text": "여기에 정확한 단어"}]}]
필수: id, start, end (소스 초). hook_start/hook_end는 2~3초짜리 라인을 앞으로 이동합니다 ("hook_mode": "repeat"는 본문에도 유지). payoff_start는 낮은 thud 효과음을 배치합니다. caption_overrides: {"w": N, "text": ""}는 단어 N을 편집/삭제합니다 (transcript.txt의 (wN), words.tsv의 i); {"start", "end", "text"}는 해당 범위의 모든 단어를 대체합니다. 전체 스키마 및 점수: references/viral-rubric.md.
렌더링 플래그
| 플래그 | 효과 |
|---|---|
--fast | x264 ultrafast CRF 24, 4fps 얼굴 샘플링 (미리보기). 기본값: preset faster, CRF 20 |
--only ID | 하나의 선택 렌더링 (반복 가능) |
--layout auto|crop|pad | pad = 흐린 배경에 전체 16:9 프레임 (넓은 다중 인물 샷에 사용). auto는 넓은 소스의 얼굴 범위가 40% 미만일 때 pad 선택 |
--music FILE | 사용자 제공 배경 음악, 목소리 아래 약 17 LU, 사이드체인 압축으로 덕킹. --music-db 20 = 더 조용하게 |
--sfx-dir DIR | 합성된 효과음 대신 자체 whoosh_fast, whoosh_soft, thud_low, pop, tick (.wav/.mp3/.flac/.ogg) 사용 |
--no-sfx, --no-silence-cut | 해당 기능 비활성화 |
--font "Lato Black", --font-size 84, --highlight yellow|green, --no-upper | 자막 스타일 |
--preset, --crf, --keep-tmp | 인코더 재정의; 디버깅을 위해 음성/믹스 wav 및 captions.ass 유지 |
환경 변수: CLIPWISE_HOME (기본값 ~/.clipwise), CLIPWISE_VENV, CLIPWISE_WORK,
CLIPWISE_MODEL (기본값 large-v3-turbo), CLIPWISE_DEVICE (auto|cpu|cuda).
선택별 렌더링 방식
후크를 앞으로 이동하고, 0.35초 이상의 무음을 자르고 (클립 자체의 음성/노이즈 수준 임계값 사용), 잘린 부분을 30fps 그리드에 맞춰 오디오 샘플이 프레임과 일치하도록 합니다. 잘린 프레임에서 YuNet으로 얼굴 추적: 지배적인 얼굴, 고정적 (새 얼굴이 1.2초 동안 1.6배 더 커야 인계), 데드존이 있는 부드러운 카메라, 컷에서만 스냅. 2~4초마다 1.0/1.12 펀치인 줌. 단어별 ASS 자막 (최대 4단어, 활성 단어 강조 표시) 턱 아래, y 1620 위에 배치. 음성 정리, 합성 효과음, 선택적 덕킹 음악, 두 패스 loudnorm으로 -14 LUFS / -1.5 dBTP. H.264 + AAC 48kHz, 1080x1920, 30fps. 최종 A/V 스트림 길이가 일치하는지 확인합니다.
측정된 타이밍 (2코어 CPU, GPU 없음)
| 단계 | 측정값 |
|---|---|
| 가져오기: 85MB 1080p VP9 웹 다운로드 + 3분 창 재인코딩 H.264 | 총 7.7분 (다운로드가 지배적, 약 5분) |
| 가져오기: 20초 10비트 HDR iPhone 클립 (톤 매핑 + CFR) | 45초 (HDR은 길이의 약 2.3배) |
| large-v3-turbo 자막 추출, 3분 영어 | ASR 110초 (0.61배 실시간) + 모델 로드 19초 |
| 기본 렌더링, 40.6초 클립 | 85초 |
--fast 렌더링, 26.4초 클립 / 17.1초 pad 클립 | 32초 / 30초 |
주의 사항
- YouTube 봇 벽 ("로봇이 아님을 확인하려면 로그인하세요"): 간헐적, IP에 따라 다름.
가져오기 종료 코드 3. 사용자에게
--cookies cookies.txt(브라우저에서 내보낸) 또는 파일 자체 / Drive 또는 Dropbox 링크를 요청하세요. yt-dlp 플레이어 클라이언트나 프록시를 반복하지 마세요. 비공개 Drive 링크는 종료 코드 2: "링크가 있는 모든 사용자" 공유 또는 로컬 파일을 요청하세요. - A/V 동기화는 엄격한 규칙입니다. 모든 출력의 오디오 및 비디오 스트림 길이는 0.1초 이내로 일치해야 합니다 (렌더링에서 확인). 0이 아닌 시작점에서 스트림 복사로 자르지 말고,
aselect로 오디오를 자르지 마세요; 스크립트를 사용하세요. - ASR 레시피는 고정: 먼저 오디오를 정규화하고, 언어를 자동 감지하며, VAD 없음,
initial_prompt없음 (대본에 누출됨). large-v3-turbo를 유지하세요;small은 악센트나 혼용 언어에서 반복 및 환각을 일으킵니다 (자막 추출에서 반복에 대해 경고). - 비영어 음성이 번역되어 반환될 수 있습니다. 비라틴 문자로 작성된 많은 언어(및 혼용 언어)의 경우 whisper는 영어를 반환합니다. 그러면 대본은 타이밍 및 의미 가이드로만 사용됩니다: 화자의 언어와 문자로
caption_overrides{"start", "end", "text"}범위를 사용하여 자막 줄을 작성하고, 렌더링 전에 사용자에게 표시하세요. 제목과 해시태그도 화자의 언어를 따릅니다. - 자막: 얼굴 위에 절대 배치하지 말고, 하단 약 300px (플랫폼 UI) 위에 배치하세요. 시트에 충돌이 표시되면 선택을 줄이거나,
--layout pad를 사용하거나,--font-size를 낮추세요. - 추적기는 화자가 아닌 가장 큰 얼굴을 따릅니다. 와이드 샷에서 두 사람이 대화하는 경우
--layout pad를 사용하거나 한 사람이 말하는 창을 선택하세요. - 업스케일링: 1080p 16:9 소스는 608x1080으로 자르고 1.8배 확대합니다; 720p 소스는 2.7배이며 흐릿하게 보입니다. 1080p+ 다운로드를 선호하세요.
- 소형 CPU: 두 개의 인코딩 또는 자막 추출과 인코딩을 동시에 실행하지 마세요; 렌더링은 하나씩 처리합니다 (run.py에서 이미 처리).