Static-first movie catalog (chuyên phim) — kiến trúc CDN-as-database với lớp proxy video (Range/CORS/HLS) ở edge. Đọc 100% file tĩnh trên CDN (không server); ghi bằng job ephemeral (không server); byte phim nặng đi qua proxy edge khi host cần CORS/Range.
A static-first movie catalog: read 100% static files off a CDN (no server), write via ephemeral jobs (no server), and stream heavy video bytes through an edge proxy (Range/CORS/HLS) only when the host needs it.
WRITE (ephemeral) STORE READ (static) STREAM (edge)
GitHub Actions cron ──ghi──► R2 (data + token) ───► Cloudflare CDN ──► client gd/src ─► proxy /p/*
transcode/sub/pack Drive/B2/R2 (video) Cache Rules <video> r2/b2 ─► thẳng (direct)
Fork chuyên hoá từ nghyane/staticat (static catalog cho phim · truyện). staticine bỏ truyện, tập trung VIDEO + PROXY: phim lẻ/phim bộ, phụ đề rời, seek bằng HTTP Range, HLS ABR.
- Write ⟂ Read — gặp nhau chỉ qua file tĩnh R2. Crawler sập → site đọc vẫn sống.
- Data ⟂ Blob — R2 giữ data + token trỏ blob (
gd:/r2:/b2:/src:); video nặng ở host khác, đổi host không đụng data. - Mutable ⟂ Immutable — chỉ pointer nhỏ tươi (SWR); payload versioned immutable, cache mãi.
… cộng một bất biến riêng của staticine:
- Direct ⟂ Proxied —
r2/b2phát thẳng (Range+CORS+egress $0);gd/srcđi qua proxy edge (/p/<token>). Data/v1/*.jsonkhông bao giờ qua proxy.
<video> chỉ tua (seek) mượt khi server trả 206 Partial Content + Accept-Ranges, và <track> phụ đề cross-origin cần CORS. Nhiều host (Google Drive) không tử tế với Range/CORS và chèn trang "confirm download". Proxy edge:
- forward
Range→ giữ206, bật seek; - gắn CORS (cho
<video crossorigin>+<track>sub); - bóc Google Drive confirm-token cho file lớn;
- giấu origin + cache cứng từng byte-range.
→ Chi tiết: docs/proxy.md · spec: spec/proxy.yaml · code: proxy/.
- AGENTS.md — orientation, map repo, các bất biến.
- docs/architecture.md — vì sao thiết kế vậy.
- docs/proxy.md — lớp proxy video (tâm điểm).
- spec/contract.yaml — NGUỒN CHÂN LÝ: schema/path/TTL/example.
- spec/proxy.yaml — hợp đồng proxy (deploy/test).
- contract/contract.ts — mirror TS để code dùng (types + path + resolver).
- docs/ingestion.md — write path (transcode/sub, thứ tự an toàn, cron).
- spec/cache-rules.yaml — chính sách cache.
- spec/dynamic.yaml — lớp dynamic tuỳ chọn (watch-progress + comment).
- title = một phim.
kind = movie(phim lẻ) |series(phim bộ). - part = tập (episode). Phim lẻ thường 1 part; series N part.
- part payload discriminated theo
kind:video-mp4(progressive, seek Range) |video-hls(ABR, playlist + segment). - phụ đề rời (
SubtitleTrack[]) đa ngôn ngữ (.vtt/.srt) gắn<track>. - list view (feed/genre/status/search) denormalized → 1 fetch là render được card.
AGENTS.md · README.md · LICENSE
docs/architecture.md · docs/proxy.md · docs/ingestion.md
spec/contract.yaml · spec/proxy.yaml · spec/cache-rules.yaml · spec/dynamic.yaml
contract/contract.ts
proxy/ # edge video proxy (CHẠY ĐƯỢC)
worker.ts · wrangler.toml # Cloudflare Worker
functions/p/[[path]].ts # Cloudflare Pages Function
package.json · tsconfig.json
ingest/ # crawl/transcode/sub/pack -> R2 + blob (Bun)
src/ · fixtures/ · README.md # pipeline + 2 phim public-domain để chạy thử
.github/workflows/ingest.yml # cron ephemeral (ffmpeg + Bun)
Frontend (Astro) = repo riêng, chỉ import contract.ts (vendor/submodule). Phụ thuộc một chiều.
| Pha | Làm | Kết quả |
|---|---|---|
| 0 | chốt /v1 + contract.ts + proxy + ingest 1-2 phim |
curl ra JSON + seek video qua /p/* |
| 1 | Astro: Home + Title + Player (Range/HLS + phụ đề), progress IndexedDB | xem được |
| 2 | search client-index + genre + năm + popular/rating | duyệt/tìm |
| 3 | đánh bóng UI/UX player + sitemap nhẹ | trải nghiệm mượt |
| 4 (tùy) | account/sync + comment (PocketBase) | đa thiết bị |
R2 data 10GB free, egress $0 · Pages free · Worker proxy 100k req/ngày free ($5/tháng = 10M). MVP read-only ≈ $0; chỉ trả storage video khi lớn (đẩy lên R2/B2).
Kiến trúc gốc & nhiều ý tưởng từ nghyane/staticat. staticine là bản chuyên hoá cho phim + proxy. Xem LICENSE.