videolabs 사이트 게시
'게시'는 모든 랩을 포함한 전체 사이트가 매번 배포됨을 의미합니다.
사이트 저장소: /opt/opus/videolabs (github.com/bigzaqui/videolabs, main 브랜치에 푸시하면 https://videolabs.pages.dev 로 배포됨; GitHub Actions가 site/ 폴더를 게시함).
랩: /opt/opus/videolabs/labs/<slug>/ (같은 저장소 내 각 랩당 하나의 폴더; slug는 MP4 파일명 out/<slug>.mp4). videolabs_create-video 기술(node engine/cli.mjs <slug> ...)로 빌드됩니다.
비디오는 비공개 R2 버킷 videolabs의 <slug>/<slug>.mp4에 있으며, /media/<slug>/<slug>.mp4를 통해 제공되고, 이 경로는 Turnstile 세션 뒤에 있습니다(세션 없는 요청은 401 반환).
자격 증명: /opt/opus/videolabs/.env (스크립트에서 로드; 템플릿 .env.example). 절대 시크릿을 출력, 복사 또는 커밋하지 마세요. GitHub 저장소는 공개입니다(사용자가 나레이션 오디오, 스크립트, 캡처 및 랩 출력에 대해 공개해도 괜찮으며, 폐기된 랩의 임시 비밀번호가 나타나는 것도 신경 쓰지 않음). 따라서 실제 자격 증명이 어떤 파일에도 나타나서는 안 됩니다. 이 기술의 소스는 저장소 내 skills/videolabs_publish/이며, scripts/setup.sh에 의해 ~/.claude/skills에 링크됩니다.
랩에 필요한 것 (스크립트가 확인)
out/<slug>.mp4(fast-start:moov가mdat보다 먼저 와야 함;ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4로 수정),out/player/chapters.json(node engine/cli.mjs <slug> player),out/spot.jpg(포스터;ffmpeg -ss 425 -i out/<slug>.mp4 -frames:v 1 -q:v 3 out/spot.jpg로 다이어그램 프레임 캡처).- 선택 사항
out/player/lab.json(랩에lab/lab-meta.json,lab/*.sh,lab/captures/가 있을 때node engine/cli.mjs <slug> player로 생성). 이 파일이 있으면site/lab.js가 랩 페이지에 랩 리소스를 표시합니다. 캡처나lab-meta.json이 변경된 경우 먼저node engine/cli.mjs <slug> player를 다시 실행하세요. labs/<slug>/lab.config.json파일에title,description및tags가 포함되어야 함. 스크립트가 이 파일을 읽어site/labs.json에 항목을 추가하거나 업데이트합니다(그리고minutes와lessons를 채움). 따라서 카탈로그를 수동으로 편집하지 마세요. 새 랩의 경우, 제목/설명/태그가 랩 스크립트에서 명확하다면 그대로 사용하고, 그렇지 않으면 사용자에게 물어보세요. 절대 조용히 임의로 만든 값을 사용하지 마세요.
단계
- 계획.
cd /opt/opus/videolabs && scripts/publish-all.sh --dry-run실행. 각 랩에 대해PLAN줄 하나(업로드 여부 또는 건너뛰기 여부,lab.json보유 여부), 게시할 수 없는 랩에 대해ATTENTION줄(파일 누락, fast-start 아님,lab.config.json없음), 프로젝트가 없는 카탈로그 항목에 대해NOTE줄을 출력합니다. 종료 코드 2는ATTENTION이 하나 이상 있음을 의미합니다. 각 문제를 수정하고(lab.config.json작성,node engine/cli.mjs <slug> player실행, 포스터 생성 등) 더 이상 문제가 없을 때까지 반복하거나, 어떤 랩을 제외하는지와 그 이유를 사용자에게 알려주세요. 사용자에게 몇 줄로 계획을 보여주고, 업로드할 비디오와 그 크기를 포함하세요.- 기록된 업로드(
state/<slug>.sha256없음)가 없는 랩은 업로드됩니다. 사용자가 R2에 이미 정확히 같은 파일이 있다고 말하면,--adopt <slug>를 사용하여 해시를 기록하고 업로드를 건너뜁니다.--force-upload는 모든 것을 다시 업로드합니다. 사용자가 비디오 교체를 요청할 때만 사용하세요(재업로드는 최대 24시간 동안 시청자에게 캐시될 수 있음).
- 기록된 업로드(
- 동기화.
scripts/publish-all.sh실행(동일한 플래그). 준비된 각 랩에 대해 먼저 렌더를out/<slug>.web.mp4로 압축(scripts/compress-mp4.sh: 모노 AAC 64k + x264 CRF 28, 약 -65% 크기, 비디오 12분당 약 5분 소요, 렌더가 변경되지 않는 한 캐시됨; 렌더 자체는 절대 수정되지 않음)한 다음, 변경된 경우에만 해당 웹 복사본을 R2에 업로드하고,lab.config.json에서 카탈로그 항목을 추가/업데이트하며,chapters.json,poster.jpg,lab.json(랩에 없으면 사이트 폴더에서 제거)을 복사하고, 새 랩에 대한index.html페이지(공유 템플릿에서, 제목과 설명 포함)를 생성하며,state/<slug>.sha256에 비디오 해시를 기록하고site/labs.json의minutes/lessons를 새로고침합니다. 업로드는 푸시 전에 여기서 이루어지므로 페이지가 비디오 없이 공개되지 않습니다. 다시 실행해도 안전합니다. - 검토 및 커밋. 먼저
scripts/check-secrets.sh를 실행하세요(.env의 값이 어떤 파일에 나타나거나, 파일이 API 키, 토큰 또는 암호 변수에 값을 할당하거나, 개인 키가 있으면 실패). 보고된 문제를 수정한 후 진행하세요. 사용자에게git status와 videolabs 저장소의 diff 요약(새롭거나 변경된 랩,labs.json,state/, 그리고 아래 참조된 공유 플레이어 코드의 변경 사항)을 보여주세요. 랩이 GitHub 비디오 요청(이슈 레이블이video-request인 경우)에 대한 응답이라면, 커밋 메시지 본문에Closes #N을 넣어main브랜치로 푸시할 때 해당 이슈가 닫히도록 하세요. 모든 것을 커밋하고(labs/*/audio/변경 사항 포함, 메시지 예:Publish labs: ceph-first-steps (new), kafka-in-a-nutshell (lab page), 세션의 기여 확인 줄(Co-Authored-By) 포함)git push origin main을 실행하세요(SSH 원격이 설정되어 있음). 작업 트리에 이 요청의 일부가 아닌 공유 플레이어 코드 변경 사항이 있으면 포함하기 전에 사용자에게 물어보세요. - 배포. GitHub Actions 실행이 완료될 때까지 기다리세요(
curl -s https://api.github.com/repos/bigzaqui/videolabs/actions/runs?per_page=1로status/conclusion확인; 약 20초 간격으로 최대 3분 정도 폴링). 실패하면 보고하고 맹목적으로 재시도하지 마세요; 시크릿 누락 오류는 저장소 시크릿CLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_ID에 주의가 필요함을 의미합니다. site/labs.json의 모든 랩 검증(새 랩만 검증하지 않음):/가 200을 반환하고 랩을 나열하는지;/labs/<slug>/가 200을 반환하는지;/labs/<slug>/chapters.json이 JSON을 반환하는지(HTML 폴백 아님: 사이트가 알 수 없는 경로에 200을 응답하므로 콘텐츠 타입 또는 본문을 확인);/labs/<slug>/lab.json이lab.json이 있는 랩에 대해 JSON을 반환하는지;/media/<slug>/<slug>.mp4가 세션 없이 401을 반환하는지(게이트가 작동 중임을 증명). 재생 자체는 Turnstile 확인이 있는 브라우저 세션이 필요하므로 재생되지 않았다고 말하세요. URL을 보고하세요:https://videolabs.pages.dev/labs/<slug>/.
하나의 랩만 게시하려면(드물게), scripts/publish-lab.sh <lab-dir> <slug> "<title>" "<description>"을 실행하고 site/labs.json을 수동으로 업데이트한 다음, 3단계로 계속 진행하세요.
공유 코드
site/labs/kafka-in-a-nutshell/index.html은 모든 새 랩에 대해 복사되는 페이지 템플릿입니다(제목, 머리글, 설명은 런타임에 site/labs.json에서 가져옴). site/lab.js는 랩에 lab.json이 있을 때 랩 리소스를 렌더링합니다. site/gate.js는 Turnstile 확인입니다. 이 코드들을 변경하면 다음 푸시 시 모든 랩에 영향을 미치므로, 먼저 기존 랩에 대해 테스트하세요. 기존 랩은 자체 index.html 복사본을 유지합니다. 템플릿 변경이 기존 랩에 적용되려면 해당 복사본도 업데이트되어야 합니다.