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 (
@importtheme.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 },idlà mã 11 ký tự của YouTube; kênh mới thì khai vàochannelstrướ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,idlà<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ùngi.ytimg.com. - Đường SPA:
public/_redirectstrảindex.htmlcho 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.pycósay_eleven: kịch bản ghi"voice": "el:<voice_id>"là bộ dựng gọi ElevenLabs thay vì Kokoro. Tuỳ chọnttsModel(mặc địnheleven_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": truevà mỗi cảnh cóvi: danh sách bản dịch, đúng số câu vớisay(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-timestampscủa ElevenLabs (mốc từng ký tự), nên phụ đề khớp lời đọc chứ không ước lượng. Raout/<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
forcesnhậnnotes(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.clickcho 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 trongforces_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/_cachetheo 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âuspotlà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ọngvn:<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).