이 스킬이 시스템에 하는 일 (설치 전 확인)
- 로컬에서
ffmpeg과 음성 인식 모델을 돌립니다. CPU·디스크를 많이 씁니다.- 녹화 파일을 읽고 자막·영상 파일을 새로 만듭니다. 원본은 수정하지 않습니다.
- 유튜브 업로드 단계는 별도 인증이 필요하고, 기본값은 업로드하지 않는 것입니다.
강의 녹화본 자막판
결과물: 1920x1080 30fps mp4 하나. 1부(슬라이드 발표) + 2부(질의응답 웹캠) + 엔드카드. 겟백 팔레트(#0d0d0d / 라임 #D4F000 / 퍼플 #7B2FFF), 자막 나눔고딕 ExtraBold, 타이틀·일러스트 배달의민족 주아체(assets/fonts/BMJUA_ttf.ttf, 상업 사용 가능, 엔드카드에 출처 표기).
무거운 단계(전사·교정·렌더)는 전부 맥미니에서 돌린다(macmini-offload 스킬). 맥북에서는 크롭·축소 전처리만 videotoolbox로 한다.
먼저 정할 것 — 판매용인가 기수 공유용인가
두 경로가 갈린다. 소스 화면이 슬라이드로 고정되어 있는지 먼저 본다(0단계 콘택트시트).
| 판매용 풀 파이프라인 | 기수 공유용 경량 | |
|---|---|---|
| 소스 조건 | 발표 내내 슬라이드 고정 | 화면이 슬라이드·브라우저·웹캠으로 계속 바뀜 |
| 포함 | 자막 + 타이틀카드 + 확대 + 일러스트 + PIP | 자막 + 엔드카드만 |
| 걸리는 시간 | 반나절 이상 | 2~3시간 |
기수 세션 녹화본은 보통 줌 창 전체를 녹화해서 맥 메뉴바·독·디스코드 사이드바가 다 찍힌다. 이런 소스에 슬라이드 구간표를 만들면 타이틀카드가 엉뚱한 화면에 뜨고 확대가 엉뚱한 데를 잡는다. runs를 통짜 하나([[0, 끝, 1]])로 두고 titles·zoom·illustrations를 빈 객체로 두면 자막과 엔드카드만 적용된다. 이 조합은 2026-09-07 7기 OT에서 실측됐다.
손실 없이 빠지는 것과 아닌 것을 구분한다. 자막은 복습 가치가 크므로 반드시 넣고, 타이틀카드·확대는 화면이 고정된 소스에서만 값을 한다.
절차
0. 소스 파악과 싱크
- 영상 소스와 음성 소스를 분리해 적는다. 맥 화면기록은 오디오가 없다.
- 두 파일을 10분 간격으로 샘플링해 콘택트시트로 본다. 슬라이드가 언제 끝나는지, 화면공유가 언제 꺼지는지, 웹캠 타일이 어디 있는지, 참가자 이름·채팅이 어디 찍히는지 표시한다.
- 오프셋은 "슬라이드 제목을 읽는 발화"와 그 슬라이드의 등장 시각으로 두 군데 이상 맞춰 확정한다. 이후 모든 시각은 음성(전사) 타임라인으로 통일한다.
- 실제 발화 끝은 볼륨 측정으로 확인한다. 전사가 "하하하"만 찍혀도 말이 이어지고 있을 수 있다.
1. 전처리 (맥북, videotoolbox)
필요 구간만 잘라 뽑는다. 원본은 보내지 않는다.
base.mp4슬라이드 크롭 → 1920x1080 30fps,-c:v h264_videotoolbox -b:v 3500kface1.mp4발표 중 얼굴 타일 크롭(원본 해상도 유지)bam2.mp4질의응답 웹캠 타일 크롭 → 1440x810audio.m4a음성-vn -c:a copy- 마지막 슬라이드 PNG를 1920x1080으로
s18.png처럼 준비(공유 해제 구간 덮기용)ssh localhost로 nohup 실행하고, 중단할 땐pkill -f '[p]rep.sh'형식으로.
2. 전사 (맥미니)
줌 클라우드 녹화본이면 전사·교정 단계를 통째로 건너뛸 수 있다. 줌이 audio_transcript.transcript(WEBVTT)를 같이 주고, 한국어 품질이 whisper 못지않다(2026-09-07 실측: 830큐, 밤밤 발화 정확). 절차는 references/zoom-cloud-source.md 참고.
scripts/vtt_to_segs.py <transcript> segs_final.json— WEBVTT → 세그먼트(긴 발화는 문장 단위로 분할)- 고유명사 교정 — 줌 전사도 브랜드명은 틀린다("갯배/겟배" → "겟백", "칠 기" → "7기"). 정규식 치환으로 한 번에 고친다.
scripts/add_words.py segs_final.json— 필수.make_video.py는 어절 하이라이트를 위해s['words']를 요구하는데 줌 전사는 문장 단위라 없다. 글자 수 비례로 어절 시간을 배분한다. 이걸 건너뛰면KeyError: 'words'로 죽는다.
줌 녹화본이 없거나 품질이 나쁘면 기존 경로로 간다 — scripts/transcribe_chunks.sh <audio> <outdir> "<고유명사 목록>" → 10분 청크, 단어 타임스탬프. 그다음 scripts/collect_tr.py <outdir>/tr <work> → segs_new.json + 교정용 chunks2/. 10분당 글자 수가 고르지 않으면 환각이니 그 청크만 다시 돌린다.
3. 교정 (맥미니)
assets/fix_prompt.txt를 작업 폴더에 복사하고 고유명사 사전을 이번 강연에 맞게 고친 뒤 scripts/fix_run.sh <work>. 끝나면 scripts/apply_fix.py <work> → segs_final.json.
4. 슬라이드 구간표
scripts/slide_runs.py base.mp4 <slides_dir> runs.json으로 초안을 만들고, 반드시 프레임을 뽑아 눈으로 대조해 손으로 고친다. 어두운 슬라이드끼리, 스크롤 중인 프레임은 자주 틀린다. 슬라이드를 앞뒤로 넘긴 구간은 정착한 시각을 시작으로 잡는다.
5. 일러스트
개념 5~6개만. scripts/illust_card.py --out 두갈래.png --title "..." --line "1. ..." --line "2. ..." --warn "..." --note "...". 만든 뒤 콘택트시트로 글리프 누락(①·→)과 금지 표현을 확인한다.
6. 설정과 조립
references/example-config.json을 복사해 채운다. 핵심 필드:
phase1.runs[[시작,끝,슬라이드번호]],titles{번호: [키커, 제목]},zoom{번호: [지연, 길이, 배율, fx, fy]} (fx·fy는 팬 범위 내 비율, 0=왼쪽·위 끝),illustrations{번호: [지연, 길이, 파일]},face.valid_from,freeze.fromphase2.cam_until(웹캠 소스가 끝나는 시각),tail(그 뒤를 잇는 다른 소스),cover.until(공유 해제 중 참가자 정보가 찍히는 구간을 마지막 슬라이드로 덮음),card_at,tagffmpeg는 맥미니에서/opt/homebrew/opt/ffmpeg-full/bin/ffmpegpython3 scripts/make_video.py config.json→sub1/2/3.ass,render.sh. 먼저render.sh의 1부 명령을-t 60으로 줄여 시험 렌더하고 프레임을 본 뒤 전체를run.sh start로 돌린다. 1부와 2부는 별도 명령이라 문제 있는 쪽만 다시 뽑으면 된다.
7. 검수 (건너뛰지 말 것)
최종본에서 다음 시각의 프레임을 뽑아 콘택트시트로 본다: 각 슬라이드 타이틀 카드 직후, 각 확대 구간 중간, 각 일러스트 표시 중, 1부→2부 경계 ±3초, 웹캠 등장 시점, 마지막 페이드, 엔드카드. 확인 항목:
- 참가자 이름·아바타·채팅이 한 프레임도 없는가
- 확대가 콘텐츠를 자르지 않는가
- 자막이 슬라이드 하단 글과 겹쳐도 읽히는가(반투명 띠)
- 얼굴 PIP가 검은 화면이 아닌가(
valid_from이전 구간) - 총 길이가 의도한 값인가(
-loop입력 때문에 무한 렌더가 된 적 있음)
8. 전달
scp로 artifacts에 가져오고(1.5GB ≈ 5분), 콘택트시트 한 장과 함께 구성·길이·바꾼 점을 보고한다. 자동 전사라 오타가 남을 수 있음을 밝히고, 시각을 알려주면 그 줄만 고쳐 다시 뽑을 수 있다고 말한다. config와 segs_final.json은 프로젝트 폴더에 보관한다.
9. 배포 — 유튜브 + 디스코드 (기수 세션일 때)
기수 녹화본은 디스코드에 파일을 올리지 않는다(8MB 제한). 유튜브 unlisted로 올리고 링크만 건다.
- 유튜브 업로드 — 맥미니
~/Projects/getback/pipeline/youtube_uploader.py의upload_video(path, title, desc, tags). 기본값이unlisted다. 인증은 통합 토큰~/.config/get100/google_token.json(scripts/google_auth.py). 718MB ≈ 1분. - 디스코드 공지 — 포럼
#겟백-세션-녹화본(id<DISCORD_ID>)에 밤냥이 봇 명의로POST /channels/{forum}/threads. 양식과 기준은references/discord-recording-post.md. - 7일 만료는 자동 — 봇의
recording_expiry.py가 1시간마다 포럼을 훑어 본문에🗓 시청 기한: ~MM/DD를 붙이고, 기한이 지나면 유튜브 링크를 지운다. 봇이 쓴 글에만 적용된다 — 밤밤 계정으로 올리면 안 붙는다. 기한은EXPIRY_DAYS상수(2026-09-08부터 7일). 나중에 다시 열려면 본문에 링크를 다시 넣으면 된다.
판단 기준
- 말투·톤 지시는
memory/USER.md의 밤밤 금지 표현을 일러스트 문구에도 그대로 적용한다("이 아니라", "자리" 등). - 확대는 슬라이드당 한 번, 8~20초 뒤 시작, 12초 유지. 일러스트는 확대와 시간이 겹치지 않게.
- 2부에서 상대(호스트)가 말할 때도 밤밤 웹캠을 유지한다. 화자 분리는 하지 않는다.
references/pitfalls.md에 실측에서 걸린 함정을 정리해 두었다. 막히면 먼저 그 파일을 본다.


