Communitygithub.com

bigzaqui/videolabs

전체 videolabs 사이트(https://videolabs.pages.dev)를 게시합니다: 저장소의 labs/ 폴더에 있는 모든 랩을 사이트로 동기화하고(비디오는 변경된 경우에만 Cloudflare R2로), 플레이어 에셋, 랩 리소스, 카탈로그를 포함하며, videolabs 저장소를 커밋하고 푸시하여 GitHub Actions가 Cloudflare Pages에 배포하도록 한 후 각 랩을 검증합니다. 사용자가 비디오 랩을 videolabs.pages.dev에 게시, 배포, 추가, 업데이트 또는 재업로드하려고 할 때 사용합니다. 한 번 실행으로 모든 랩을 게시하며, 하나만 게시하지 않습니다.

videolabs란 무엇인가요?

이 기술은 `bigzaqui/videolabs` 저장소에 있는 모든 비디오 랩을 자동으로 Cloudflare R2 스토리지에 동기화하고, Cloudflare Pages에 배포하여 전체 videolabs 사이트를 게시합니다. `labs/` 디렉토리의 각 랩은 `out/<slug>.mp4` 비디오, `chapters.json`, `poster.jpg`, 선택적인 `lab.json` 리소스를 포함하며, 비디오는 변경된 경우에만 업로드됩니다. 이 기술은 GitHub Actions를 통해 사이트를 배포하고, 배포 후 모든 랩 페이지와 미디어 접근성을 검증합니다. 주로 Claude Code에서 사용되며, 사용자가 '게시', '배포', '업데이트'를 요청할 때 실행됩니다. 내부적으로 `scripts/publish-all.sh` 스크립트를 사용하며, 드라이런 모드, 강제 업로드, 기존 R2 비디오 채택 등의 옵션을 지원하고, 커밋 전 `.env` 시크릿 노출을 검사하는 `check-secrets.sh`를 실행합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/bigzaqui/videolabs/tree/HEAD/skills/videolabs_publish

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

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

문서

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를 채움). 따라서 카탈로그를 수동으로 편집하지 마세요. 새 랩의 경우, 제목/설명/태그가 랩 스크립트에서 명확하다면 그대로 사용하고, 그렇지 않으면 사용자에게 물어보세요. 절대 조용히 임의로 만든 값을 사용하지 마세요.

단계

  1. 계획. 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시간 동안 시청자에게 캐시될 수 있음).
  2. 동기화. 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를 새로고침합니다. 업로드는 푸시 전에 여기서 이루어지므로 페이지가 비디오 없이 공개되지 않습니다. 다시 실행해도 안전합니다.
  3. 검토 및 커밋. 먼저 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 원격이 설정되어 있음). 작업 트리에 이 요청의 일부가 아닌 공유 플레이어 코드 변경 사항이 있으면 포함하기 전에 사용자에게 물어보세요.
  4. 배포. 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에 주의가 필요함을 의미합니다.
  5. 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 복사본을 유지합니다. 템플릿 변경이 기존 랩에 적용되려면 해당 복사본도 업데이트되어야 합니다.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬