lecture-recording-captions

SkillDocs & knowledge

Turns lecture and live session recordings into captioned videos with word-level captions, slide cards, and distribution.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the lecture-recording-captions skill

About this capability

Read this when captioned versions of BamBam's lecture, live, or cohort session recordings (Zoom cloud, Discord, Mac screen recordings) need to be produced and distributed. One pipeline covering word-level highlighted captions, per-slide title cards, key-area zooms, ruby-text illustrations, face PIP,

What this skill tells your AI

The instructions your AI receives, as published by bam-bam-2/solo-skills in skills/lecture-recording-captions/SKILL.md and read by ahel’s review.

이 스킬이 시스템에 하는 일 (설치 전 확인)

  • 로컬에서 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. 소스 파악과 싱크

  1. 영상 소스와 음성 소스를 분리해 적는다. 맥 화면기록은 오디오가 없다.
  2. 두 파일을 10분 간격으로 샘플링해 콘택트시트로 본다. 슬라이드가 언제 끝나는지, 화면공유가 언제 꺼지는지, 웹캠 타일이 어디 있는지, 참가자 이름·채팅이 어디 찍히는지 표시한다.
  3. 오프셋은 "슬라이드 제목을 읽는 발화"와 그 슬라이드의 등장 시각으로 두 군데 이상 맞춰 확정한다. 이후 모든 시각은 음성(전사) 타임라인으로 통일한다.
  4. 실제 발화 끝은 볼륨 측정으로 확인한다. 전사가 "하하하"만 찍혀도 말이 이어지고 있을 수 있다.

1. 전처리 (맥북, videotoolbox)

필요 구간만 잘라 뽑는다. 원본은 보내지 않는다.

  • base.mp4 슬라이드 크롭 → 1920x1080 30fps, -c:v h264_videotoolbox -b:v 3500k
  • face1.mp4 발표 중 얼굴 타일 크롭(원본 해상도 유지)
  • bam2.mp4 질의응답 웹캠 타일 크롭 → 1440x810
  • audio.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 참고.

  1. scripts/vtt_to_segs.py <transcript> segs_final.json — WEBVTT → 세그먼트(긴 발화는 문장 단위로 분할)
  2. 고유명사 교정 — 줌 전사도 브랜드명은 틀린다("갯배/겟배" → "겟백", "칠 기" → "7기"). 정규식 치환으로 한 번에 고친다.
  3. 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.from
  • phase2.cam_until(웹캠 소스가 끝나는 시각), tail(그 뒤를 잇는 다른 소스), cover.until(공유 해제 중 참가자 정보가 찍히는 구간을 마지막 슬라이드로 덮음), card_at, tag
  • ffmpeg는 맥미니에서 /opt/homebrew/opt/ffmpeg-full/bin/ffmpeg python3 scripts/make_video.py config.jsonsub1/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로 올리고 링크만 건다.

  1. 유튜브 업로드 — 맥미니 ~/Projects/getback/pipeline/youtube_uploader.pyupload_video(path, title, desc, tags). 기본값이 unlisted다. 인증은 통합 토큰 ~/.config/get100/google_token.json(scripts/google_auth.py). 718MB ≈ 1분.
  2. 디스코드 공지 — 포럼 #겟백-세션-녹화본(id <DISCORD_ID>)에 밤냥이 봇 명의로 POST /channels/{forum}/threads. 양식과 기준은 references/discord-recording-post.md.
  3. 7일 만료는 자동 — 봇의 recording_expiry.py가 1시간마다 포럼을 훑어 본문에 🗓 시청 기한: ~MM/DD를 붙이고, 기한이 지나면 유튜브 링크를 지운다. 봇이 쓴 글에만 적용된다 — 밤밤 계정으로 올리면 안 붙는다. 기한은 EXPIRY_DAYS 상수(2026-09-08부터 7일). 나중에 다시 열려면 본문에 링크를 다시 넣으면 된다.

판단 기준

  • 말투·톤 지시는 memory/USER.md의 밤밤 금지 표현을 일러스트 문구에도 그대로 적용한다("이 아니라", "자리" 등).
  • 확대는 슬라이드당 한 번, 8~20초 뒤 시작, 12초 유지. 일러스트는 확대와 시간이 겹치지 않게.
  • 2부에서 상대(호스트)가 말할 때도 밤밤 웹캠을 유지한다. 화자 분리는 하지 않는다.

references/pitfalls.md에 실측에서 걸린 함정을 정리해 두었다. 막히면 먼저 그 파일을 본다.

Signals

GitHub stars
365
Forks
90
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
lecture-recording-captions
Source
github.com/bam-bam-2/solo-skills