Skip to content

video.conan.school ​

Landing tĩnh giới thiệu các khoá học video (từ 08.10.2026). Web app sản xuất video cũ chuyển sang video2.conan.school (thư mục video2.conan.school/, project Pages video2-conan-school).

Cấu trúc ​

  • Vite + React, Tailwind v4 (@import theme.css), shadcn/ui, motion. Không D1.
  • Nội dung ở JSON: src/data/site.json (ba module) và src/data/videos.json (video mẫu nhúng YouTube). Thêm video là thêm một dòng { id, channel, title, format }, id là mã 11 ký tự của YouTube; kênh mới thì khai vào channels trước.
  • Video phát trong hộp thoại qua youtube-nocookie.com, ảnh bìa lấy từ i.ytimg.com, chỉ nạp iframe khi người xem bấm (trang không kéo trình phát YouTube lúc tải).

Chuyển động ​

Bộ hiệu ứng nằm ở src/components/fx/motion-kit.tsx (Reveal, Stagger, SplitWords, TiltCard, Magnetic, Floaty). Thông số chuẩn hoá theo bộ skill UI UX Pro Max (nextlevelbuilder/ui-ux-pro-max-skill, data/motion.csv): hover 150-300ms, reveal 400-600ms, stagger 60-80ms, thoát nhanh hơn vào, chỉ animate transform/opacity. Bộ đó viết bằng GSAP; repo cấm GSAP nên mọi thông số được dịch sang motion/react, đừng cài GSAP theo snippet của nó.

MotionConfig reducedMotion="user" bọc toàn trang: người bật "giảm chuyển động" trong hệ điều hành không thấy vật thể bay. Vùng bấm tối thiểu 44px, có focus-visible.

Bảng "agent làm thẳng ra được" (09.10.2026) ​

Module Research có thêm agentFit trong site.json: ba nhóm A (agent làm trọn), B (làm được, cần người duyệt), C (agent hỗ trợ một phần), chấm theo ba câu: có cần quay người thật không, hình ảnh có vẽ bằng mã hay sinh bằng AI được không, chạy lại có ra đúng không. Cách chia dạng tham khảo bộ skill Reelcrew cho Claude Code (reelcrew.io); chỉ học cách phân loại, không chép mã của họ. Màu nhóm đi qua token (emerald, amber, muted) trong bảng TONE ở App.tsx.

Thư viện video (09.10.2026) ​

  • /library: lưới video có bộ lọc theo dạng, khoá học, khổ, ngôn ngữ, kênh, cộng ô tìm. Bộ lọc nằm trên URL (?format=Explainer&language=en) để gửi được một lát cắt cho người khác.
  • /video/:id: trang chi tiết, gồm trình phát, metadata, kịch bản từng cảnh, nguồn giọng đọc, nhạc, hình ảnh, hiệu ứng âm thanh và công cụ.
  • Dữ liệu: src/data/library.json. Mỗi video một dòng, id là <slug>-<khổ>. Ảnh bìa của video tự dựng nằm ở public/thumbs/ (cắt từ chính file MP4), vì YouTube mất vài giờ mới có ảnh bìa cho video vừa đăng; video của kênh khác dùng i.ytimg.com.
  • Đường SPA: public/_redirects trả index.html cho mọi đường dẫn, thiếu dòng này thì mở thẳng /video/... ra 404.
  • Video do video-studio/ dựng (xem README trong đó), đăng YouTube kênh @NguyenTatDac ở chế độ không công khai.

Bẫy khi đăng YouTube bằng trình duyệt tự động: chèn chữ vào ô tiêu đề bằng JS không ăn, nên tiêu đề lấy từ tên file. YouTube bỏ dấu - và ngoặc trong tên file khi làm tiêu đề; dùng ·.

Giọng ElevenLabs (10.10.2026) ​

  • video-studio/pipeline/tts.py có say_eleven: kịch bản ghi "voice": "el:<voice_id>" là bộ dựng gọi ElevenLabs thay vì Kokoro. Tuỳ chọn ttsModel (mặc định eleven_multilingual_v2) và lang (tiếng Việt: eleven_v3, "lang": "vi").
  • Khoá đọc từ video-studio/.env.local (ELEVENLABS_API_KEY), file này nằm trong .gitignore. Khoá chỉ nên có quyền Text to Speech và Voices: Read.
  • Bẫy: gói trả phí trong ứng dụng web không áp cho API. Khi API ở mức miễn phí, giọng lấy từ thư viện cộng đồng (ví dụ "MC Anh Đức") trả 402 paid_plan_required; chỉ giọng có sẵn của ElevenLabs gọi được. Video tiếng Việt hiện dùng giọng Brian (tiếng Anh) đọc tiếng Việt.
  • Thư viện có trường voiceEngine (ElevenLabs, Kokoro, Không có giọng, Chưa ghi nhận) và bộ lọc "Nguồn giọng đọc", để so cùng một video với hai nguồn giọng.
  • Đăng YouTube qua Chrome: file trên ~5 MB làm rớt cầu nối tiện ích, nên nén còn ~2 MB (crf 30) trước khi đăng. Cửa sổ Chrome bị thu nhỏ thì trang ngừng vẽ và mọi thao tác treo.

Slug và URL chỉ dùng tiếng Anh (11.10.2026) ​

Mọi đường dẫn, mã video, anchor và giá trị bộ lọc trên URL của video.conan.school là tiếng Anh; chữ Việt chỉ nằm ở nhãn hiển thị. /thu-vien đổi thành /library, loi-nhuan-don-hang-* thành order-profit-*, #kich-ban thành #script-writing, #video-mau thành #sample-videos. Bộ lọc ghi mã qua facetSlug() (?course=video-course&voiceEngine=none). Link cũ được public/_redirects chuyển 301 sang link mới; dòng 301 phải nằm TRƯỚC dòng /* /index.html 200. Thêm một giá trị bộ lọc bằng tiếng Việt thì khai mã tiếng Anh vào bảng SLUG trong library.ts.

Phụ đề rời, không in vào hình (11.10.2026) ​

  • Kịch bản khai "burnCaptions": false, "subtitles": true và mỗi cảnh có vi: danh sách bản dịch, đúng số câu với say (tách theo . ! ?). Sai số câu thì bộ dựng dừng và báo cảnh nào.
  • Mốc giờ lấy từ endpoint with-timestamps của ElevenLabs (mốc từng ký tự), nên phụ đề khớp lời đọc chứ không ước lượng. Ra out/<slug>.en.srt, .vi.srt, và bản tên kiểu Facebook .en_US.srt, .vi_VN.srt. Xuất lại SRT không cần gọi ElevenLabs: python pipeline/subtitles.py <slug>.
  • Cảnh forces nhận notes (bốn dòng ví dụ), bốn ô giãn đều theo độ dài cảnh.
  • Tải SRT lên YouTube Studio bằng trình duyệt tự động: nút "Continue" của hộp "With timing" gọi input.click() mở hộp chọn file của hệ điều hành; chặn bằng cách ghi đè HTMLInputElement.prototype.click cho input file, rồi nạp file vào input ẩn. Nút "Upload manual" ở trang Languages trả "Failed to upload captions"; đi qua trình soạn (Edit subtitles → ⋮ → Upload file, hoặc Add language → Manual subtitles) thì được. Đặt ngôn ngữ video thì bỏ tick "Make this the default for my channel".

Bản dài, đọc từng câu, tập trung một vùng (11.10.2026) ​

  • pipeline/build_long.py + specs/long/: kịch bản song ngữ viết trong forces_content.py (mỗi dòng là cặp EN/VI), sinh ba spec (EN ElevenLabs, VI ElevenLabs, VI VieNeu). Mỗi câu đọc một lần gọi riêng, nghỉ 0,75 giây giữa câu, 1,5 giây giữa cảnh, 2,6 giây trước một phần mới.
  • Âm thanh từng câu lưu đệm ở public/gen/_cache theo hash (giọng, model, câu): dựng lại không tốn ký tự.
  • Cảnh mới trong src/Deep.tsx: model (bốn lực, mỗi câu chiếu sáng một lực), focus (một lực của một ví dụ, năm góc nhìn hiện theo câu, các ô khác làm mờ), vignette (khung chat, bảng tính, lưới video; câu spot làm nổi chi tiết chính). Hiệu ứng âm thanh (pipeline/sfx.py) trộn sẵn vào file âm thanh của cảnh bằng Python: để Remotion trộn hàng trăm luồng riêng thì đầy đĩa giữa chừng.
  • VieNeu (api.vieneu.io, giọng vn:<tên>): khoá ở .env.local (VIENEU_API_KEY). Tài khoản chỉ có 1 khoá Live và không xem lại được, nên dùng khoá Test (tối đa 100 từ mỗi lần, vừa với một câu). Tránh các giọng mang tên người nổi tiếng có thật.
  • Bẫy ElevenLabs: API ở gói miễn phí chỉ có 10.000 ký tự mỗi tháng (lỗi 401 quota_exceeded), không gọi được giọng thư viện (402) và không tạo được giọng mới qua API (403).
  • Video 11 phút ra ~95 MB; đăng qua Chrome phải nén còn ~7 MB (540p, 20 fps, -tune stillimage).

Tài liệu nội bộ nền tảng Conan School.