Spaces:
Running
Running
Commit History
-
fix(infra): chặn 502 từ gốc — uvicorn đóng kết nối rảnh quá sớm
Administrator +1: Claude Opus 5Đo thẳng lên Space: 5/20 lời gọi API trả 502. Thân của 502 là trang HTML của huggingface.co (3044 byte) và KHÔNG có header `server: uvicorn` -> request chưa hề tới app. Không phải app lỗi: router của HF không nối được tới uvicorn. Lý do: router giữ sẵn một nắm kết nối tới app rồi dùng lại, trong khi uvicorn mặc định đóng kết nối rảnh sau 5 GIÂY. Router gửi request vào kết nối vừa bị đóng -> 502. Đây chính là nguồn đẻ ra "Unexpected token '<'" mà người dùng gặp: các bản vá trước chỉ dịch nó thành câu tiếng Việt đọc được, gốc rễ nằm ở đây. - Dockerfile: uvicorn --timeout-keep-alive 120, cao hơn hẳn thời gian rảnh của router, để bên đóng kết nối là router và đóng đàng hoàng từ phía nó. - api.js: lời gọi CHỈ ĐỌC (GET/HEAD) gặp trục trặc tạm thời thì tự thử lại 2 nhịp (0.6s, 1.2s). Lệnh GHI KHÔNG tự thử lại — bấm một lần mà chạy hai job thì tai hại hơn là báo lỗi để người dùng bấm lại. Kiểm: chạy thử api.js — GET vượt qua 2 nhịp 502 rồi trả dữ liệu, POST vẫn báo lỗi sau đúng 1 lần …a04fadb -
fix(api): 7 màn còn lại dùng chung lớp đọc JSON an toàn
Administrator +1: Claude Opus 5Nốt các trang gọi fetch trần rồi r.json(): Listing ảnh, Listing free, Flux2, Tạo video, Lịch sử cá nhân, Đăng nhập, Quản trị người dùng (+ tab Thời lượng). Space trả trang HTML lúc bận là các trang này vẫn văng "Unexpected token '<'". - api.js mở ra `request/jget/jpost` cho các trang dùng chung; AbortError đi xuyên qua nguyên vẹn để Listing ảnh còn nhận ra "quá thời gian chờ 2 phút". - Flux2: vòng hỏi trạng thái chịu đựng 90s mất liên lạc thay vì bỏ mẻ ảnh đang vẽ dở ngay lần hỏng đầu — cùng cách đã làm cho pollJob. - Đăng nhập: tách "chưa được cấp quyền" (máy chủ trả lời rành mạch) khỏi "máy chủ trục trặc". Trước đây Space bận là báo oan người dùng chưa có quyền rồi gửi luôn yêu cầu cấp quyền cho người vốn đã có. - Quản trị: chỉ 403 mới là sai mật khẩu; lỗi khác không còn đá người quản trị ra khỏi phiên đang mở. - Lịch sử cá nhân: bỏ bản jpost chép tay, và xoá/liên kết Drive lỗi thì báo thay vì nuốt thành unhandled rejection. - Hai nút tải .zip: 200-mà-HTML không còn lưu …d94104e -
fix(api): trang HTML của cổng vào Space không còn giết job đang chạy dở
Administrator +1: Claude Opus 5Lỗi người dùng gặp: "Unexpected token '<', "<!DOCTYPE "... is not valid JSON" văng ra giữa lúc dịch đang chạy 60%. Gốc: api.job() (và các GET khác) gọi thẳng r.json() không xem r.ok. Khi Space bận / vừa khởi động lại / cổng vào HF hết giờ chờ, nó trả trang HTML 502-504; JSON.parse ném ra câu tiếng Anh khó hiểu, pollJob reject NGAY từ lần hỏng đầu tiên — trong khi job vẫn đang chạy ngon lành trên máy chủ. Sửa: - Mọi lời gọi đi qua một chỗ đọc body: nhận ra HTML/không-JSON, trả câu tiếng Việt đọc được, gắn cờ `transient` cho lỗi tạm thời (5xx, 429, 408, đứt mạng). - pollJob thử lại tối đa 90s khi mất liên lạc thay vì bỏ job; 404 (máy chủ khởi động lại, mất tiến trình trong RAM) thì báo rõ và dừng luôn. - Upload qua XHR cũng không còn ném nguyên khối HTML vào alert, và 200-mà-HTML không còn âm thầm resolve {}. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>5b27e49
-
fix(cut): cắt 2+ video không còn 500; tách khúc tự động nhanh gấp ~3 lần
Lỗi: thả nhiều video một lượt lúc chưa có video nào -> `cur` là biến chụp lúc render nên mọi lần upload đều gửi session rỗng -> backend tạo mỗi video một phiên riêng. Lúc cắt, FE chỉ gửi phiên của video đầu nên video thứ 2 trở đi không tìm thấy file, ffprobe chết, cả request 500 "Internal Server Error". Một video thì không bao giờ dính. Sửa lỗi: - FE giữ sid chạy theo vòng lặp upload -> mọi video vào cùng một phiên; và gửi kèm session của TỪNG video khi cắt để các phiên cũ đã lỡ tách vẫn chạy. - resolve_src() chặn thoát thư mục phiên; probe() báo lý do đọc được thay vì CalledProcessError trống trơn. - execute-batch đưa việc nặng vào job: 1 video hỏng chỉ hỏng phần của nó, hỏng hết thì job báo lỗi thật. - Khúc end <= start trước đây bị ffmpeg nuốt im lặng (-frames:v âm = không giới hạn) và xuất ra clip dài tới hết video -> nay chặn. - alert hiện `detail` thay vì nguyên cục JSON. Tăng tốc (đo thật, video QC 1080x1920 30s): - Tách khúc tự động 11.9s -> 3.9s/video. PySceneDetect …07d3e14
-
feat(analytics): tab "Quản lý thời lượng" theo dõi người dùng cho quản trị
Ghi log thô raw_events rồi suy ra 9 chỉ số (A1-A5, B1-B4) bằng ETL, không lưu sẵn chỉ số. Lưu Postgres nếu có DATABASE_URL, không thì SQLite trong STORAGE_DIR (giữ 90 ngày, tự dọn). - backend: services/analytics (storage + etl + track), api/analytics (POST /api/events + các endpoint quản trị), middleware reqctx đọc header X-TNT-* để dòng log máy chạy gắn đúng task; JobManager tự phát job_queued/started/done kèm latency_ms (đã trừ thời gian chờ hàng đợi). - frontend: lib/track.js (đồng hồ người), nút 👍/👎 (ThumbsFeedback) cho B3, dashboard SVG thuần trong tab Quản lý thời lượng; gắn điểm đo vào 8 tool. Kèm sửa lỗi phát hiện khi test: - khôi phục core.files.rel_path() + gom mọi đường dẫn phiên ở cut/dub/shuffle/ download về "/" (trước nối "\" của Windows làm hỏng URL). - get_file trả 404 khi trỏ vào thư mục (thay vì 500). - run_tests.py cô lập STORAGE_DIR sang temp (trước dọn nhầm phiên thật). - cập nhật test_cut/test_dub theo API hiện hành + thêm test_analytics (14 test). …00d2c12
-
feat(text): thêm tab "Chèn chữ vào video" (nhiều video) + nhận dạng lời (ASR)
- Tab mới /chen-chu: vào 1..N video, chèn chữ hard-sub, ra N video tương ứng. - Dùng lại SubtitleEditor (đủ phông/màu/11 kiểu nền/3 kiểu hiển thị/kéo thả) qua props tùy chọn initialSegs/initialMode/onChange/headerExtra/onFetchLines (mặc định null -> tab Dịch/đổi giọng không đổi). - Lấy text: nút "Tự lấy lời từ video (nhận dạng)" -> ASR Whisper (Groq) không dịch, đổ lời + mốc vào editor để sửa; hoặc gõ tay. - Áp form giữa các video: "Áp cả lời + kiểu" (cùng nội dung) và "Áp kiểu (giữ lời riêng)" (mỗi video tự nhận dạng lời khác nhau). - Backend: endpoint /api/dub/subtitle/transcribe (extract_tracks + asr, nhẹ); burn + preview-frame ra thư mục RIÊNG theo từng video (hash) -> burn nhiều video 1 phiên không đè nhau. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>788bc93
-
fix(sub): preview khớp 100% burn bằng render server (bỏ libass-wasm)
libass-wasm và ffmpeg dùng 2 BẢN libass khác phiên bản -> render KHÁC nhau (chữ bị rớt do bug fallback libass #460, khác font-hinting...). Không thể đảm bảo giống hệt bằng bộ vẽ thứ 2 trong trình duyệt. Cách chắc chắn = dùng CHÍNH ffmpeg/libass của bản burn để xem trước: - Bỏ hẳn libass-wasm (gỡ package + public/libass + endpoint /subtitle/ass). - Khi BẬT "Xem trước khớp 100%" + video TẠM DỪNG + không gõ/kéo -> tự gọi /subtitle/preview-frame render đúng khung tại thời điểm hiện tại (server ffmpeg) rồi đè ảnh lên video. Ảnh này = ĐÚNG video sẽ xuất. - Đang phát/gõ/kéo -> hiện CSS nhanh (mượt); dừng lại tự cập nhật khung chuẩn (debounce 350ms, chống race bằng frameSeq). - Giữ fix cỡ video = nội dung thật (bỏ letterbox) để ảnh đè khớp vùng video. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>4f9859d -
feat(sub): xem trước phụ đề KHỚP 100% bản burn (libass-wasm + render khung server)
Preview CSS chỉ 'bắt chước' libass -> không bao giờ giống hệt. Nay preview dùng CHÍNH libass, cùng ASS + font như bản burn: - Backend: tách build_ass_str (1 nguồn ASS cho burn/preview), thêm render_frame (render đúng 1 khung), 2 endpoint: /subtitle/ass (ASS cho libass-wasm), /subtitle/preview-frame (ảnh khớp 100% để đối chiếu). - FE (A) libass-wasm (SubtitlesOctopus): vẽ phụ đề THẬT đè video, real-time; nạp ASS từ backend (debounce), font từ /fonts. Toggle "Xem trước khớp 100%"; lỗi -> tự lùi về CSS. Ẩn lớp CSS khi libass bật (tránh chồng đôi); ẩn canvas khi đang gõ. - FE (B) nút "Xem trước chuẩn (ảnh)" -> gọi render 1 khung server. - Sửa lệch hình học gốc: cỡ hiển thị video = nội dung THẬT (bỏ letterbox ngang) nên overlay ánh xạ đúng toạ độ video (trước tính theo khung thẻ kèm viền đen). - worker/wasm libass để LFS (khớp *.wasm), HF chấp nhận. Đã kiểm: build FE pass; backend build_ass_str/render_frame ra ASS+khung đúng (highlight đúng thời điểm); libass-wasm nạp worker+was…d908e31
-
feat(dub): prompt dịch persona 20 năm + xưng hô nhất quán; nạp SRT tự tải
- Prompt dịch (Gemini + Groq dùng chung): đóng vai chuyên gia dịch <gốc>→<đích> 20 năm KN, ép xưng hô nhất quán theo nhân vật/hoàn cảnh. Ngôn ngữ gốc lấy tự động từ ASR (pipeline truyền source_lang xuống translate_all). - Nạp SRT tự tải: endpoint /api/dub/subtitle/import parse SRT + BỎ nhãn trong [] (giữ mốc + nội dung); nút "Dùng file SRT khác" ở khu xem/sửa bản dịch để thay bản dịch tự động khi user không ưng. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>ab852b0
-
feat(cut): tách video thành video không lời + MP3, tải cái nào tuỳ ý
Thêm tính năng trong tool Cut: user tải/kéo thả video -> tách thành 1 video KHÔNG lời (mp4) và 1 file âm thanh (mp3), cho tải riêng từng cái. - services/cutter/cut.py: split_audio() — video không lời (copy luồng hình, lỗi thì re-encode H.264) + mp3 (libmp3lame). has_audio_stream() để bỏ qua mp3 khi video vốn không có tiếng. Trả SplitResult. - api/cut.py: POST /api/cut/split-audio (chạy job) trả URL 2 file + has_audio; auto-save vào lịch sử/Drive nếu có username. - api.js: cutSplitAudio(). - CutVideo.jsx: card "🔊 Tách âm thanh" (dùng video đang chọn) + 2 nút tải; báo rõ khi video gốc không có tiếng. Test: video có tiếng -> video không lời (hết audio) + mp3 hợp lệ; video không tiếng -> chỉ video không lời; endpoint end-to-end OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>b7ce941
-
perf: queue theo loại + code-split FE + streaming upload cho nhiều user
Backend: - Viết lại JobManager thành hàng đợi theo loại tác vụ (heavy/io), có vị trí hàng đợi (queue_pos/total) và tự dọn job cũ để không rò rỉ RAM. Cấu hình qua env JOB_HEAVY_WORKERS/JOB_IO_WORKERS/JOB_HISTORY_MAX. - core/bg.py: pool nền có giới hạn (BG_WORKERS) thay raw thread không giới hạn ở listing/listing_free/flux2. - Đưa OpenAI blocking ra khỏi event loop (listing analyze/create_session, listing_free analyze/auto-generate: async def -> def). - Streaming upload (save_upload_stream) thay vì đọc cả file vào RAM; enforce MAX_UPLOAD_MB. Áp cho dub/cut/shuffle. - Cache: Demucs model, ffprobe theo (path,mtime), client Groq. - thumbnail_strip gộp 40 lần ffmpeg -> 1 pass (fallback chậm khi lỗi). - Endpoint GET /api/queue/stats. Frontend: - Sửa bug double <Outlet> trong Layout (mọi trang mount 2 lần -> chạy đôi upload/poll/API). - Code-split các page bằng React.lazy + Suspense; tách react-vendor chunk. - pollJob nhận AbortSignal; hủy poll khi rời trang (chống rò rỉ setTimeou…3370310 -
update cut, guide, dub
c55c292
-
update
30b7d3a
-
update add subtitle
c55989f
-
feat: auto-save kết quả tool lên Drive + lịch sử cho download/cut/shuffle/listing
b4fd22e -
feat: lịch sử cá nhân + auto-save lên Google Drive
- Backend: service Google Drive (service account), history storage - API: /api/drive/setup, /api/drive/status, /api/history, /api/history/auto-save - Dub tool: tự động lưu kết quả sau khi xử lý + username tracking - Frontend: trang Lịch sử cá nhân, liên kết Drive, button sidebarcabddd5 -
update shuffle: optimize algorithm to be fast, design new function
e014541
-
deploy merged code
9b53f70