Spaces:
Running
Running
fix(shuffle): tiến độ upload theo TỪNG khúc, không còn nhảy loạn xạ
Browse filesTrước đây dùng CHUNG 1 state `uploading` cho mọi khúc; up nhiều khúc cùng lúc
thì mỗi lần paint() ghi đè seg của nhau -> thanh % nhảy qua lại giữa các khúc.
Đổi sang map { [segKey]: {done,total,pct,name} }: mỗi khúc (và ô audio) có tiến
độ riêng, xoá key của riêng mình khi tải xong.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
frontend/src/pages/ShuffleVideo.jsx
CHANGED
|
@@ -42,7 +42,7 @@ export default function ShuffleVideo() {
|
|
| 42 |
const [showNote, setShowNote] = useState(true);
|
| 43 |
// Loader tải lên: {seg, done, total, pct, name} — pct tính theo BYTE (chuẩn),
|
| 44 |
// upload SONG SONG nên nhanh; seg = index khúc hoặc "audio".
|
| 45 |
-
const [uploading, setUploading] = useState(
|
| 46 |
// Huỷ vòng poll job khi rời trang.
|
| 47 |
const pollAbort = useRef(new AbortController());
|
| 48 |
useEffect(() => () => pollAbort.current.abort(), []);
|
|
@@ -99,13 +99,18 @@ export default function ShuffleVideo() {
|
|
| 99 |
const loaded = new Array(arr.length).fill(0);
|
| 100 |
const added = new Array(arr.length);
|
| 101 |
let done = 0;
|
|
|
|
|
|
|
| 102 |
const paint = () => {
|
| 103 |
const lb = loaded.reduce((a, b) => a + b, 0);
|
| 104 |
-
setUploading({
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
| 109 |
};
|
| 110 |
const one = async (i) => {
|
| 111 |
try {
|
|
@@ -121,7 +126,12 @@ export default function ShuffleVideo() {
|
|
| 121 |
if (!sid) { await one(0); start = 1; }
|
| 122 |
const limit = sid ? 4 : 1; // nếu file đầu lỗi (chưa có session) -> tuần tự cho an toàn
|
| 123 |
await runPool(arr.length - start, (k) => one(start + k), limit);
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
const ok = added.filter(Boolean);
|
| 126 |
const failed = arr.length - ok.length;
|
| 127 |
if (failed) alert(`Có ${failed}/${arr.length} tệp tải lỗi (đã bỏ qua).`);
|
|
@@ -277,15 +287,15 @@ export default function ShuffleVideo() {
|
|
| 277 |
<div className="small muted">Khúc này bị bỏ khỏi video — không cần clip.</div>
|
| 278 |
) : (
|
| 279 |
<>
|
| 280 |
-
{uploading
|
| 281 |
<div className="upload-loader" style={{ marginBottom: 8 }}>
|
| 282 |
<div className="small flex between" style={{ marginBottom: 4 }}>
|
| 283 |
<span style={{ color: "var(--orange-700)" }}>
|
| 284 |
-
⏳ Đang tải {uploading.done}/{uploading.total} clip · {uploading.pct}%
|
| 285 |
</span>
|
| 286 |
-
<span className="muted" style={{ maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{uploading.name}</span>
|
| 287 |
</div>
|
| 288 |
-
<div className="progress"><i style={{ width: uploading.pct + "%" }} /></div>
|
| 289 |
</div>
|
| 290 |
)}
|
| 291 |
<div className="clip-row">
|
|
@@ -315,12 +325,12 @@ export default function ShuffleVideo() {
|
|
| 315 |
<div style={{ width: 150 }}>
|
| 316 |
<FileDrop compact accept="audio/*" onFiles={addAudio} title="Thả âm thanh" hint="MP3, WAV, M4A" />
|
| 317 |
</div>
|
| 318 |
-
{uploading
|
| 319 |
<div className="upload-loader" style={{ alignSelf: "center", minWidth: 220 }}>
|
| 320 |
<div className="small" style={{ color: "var(--orange-700)", marginBottom: 4 }}>
|
| 321 |
-
⏳ Đang tải {uploading.done}/{uploading.total} · {uploading.pct}%
|
| 322 |
</div>
|
| 323 |
-
<div className="progress"><i style={{ width: uploading.pct + "%" }} /></div>
|
| 324 |
</div>
|
| 325 |
)}
|
| 326 |
{st.audio.map((a, ai) => (
|
|
|
|
| 42 |
const [showNote, setShowNote] = useState(true);
|
| 43 |
// Loader tải lên: {seg, done, total, pct, name} — pct tính theo BYTE (chuẩn),
|
| 44 |
// upload SONG SONG nên nhanh; seg = index khúc hoặc "audio".
|
| 45 |
+
const [uploading, setUploading] = useState({}); // { [segKey]: {done,total,pct,name} }
|
| 46 |
// Huỷ vòng poll job khi rời trang.
|
| 47 |
const pollAbort = useRef(new AbortController());
|
| 48 |
useEffect(() => () => pollAbort.current.abort(), []);
|
|
|
|
| 99 |
const loaded = new Array(arr.length).fill(0);
|
| 100 |
const added = new Array(arr.length);
|
| 101 |
let done = 0;
|
| 102 |
+
// Tiến độ lưu THEO TỪNG KHÚC (map) — up nhiều khúc cùng lúc thì mỗi khúc có
|
| 103 |
+
// thanh riêng, không ghi đè nhau (trước đây dùng chung 1 state -> nhảy loạn xạ).
|
| 104 |
const paint = () => {
|
| 105 |
const lb = loaded.reduce((a, b) => a + b, 0);
|
| 106 |
+
setUploading((prev) => ({
|
| 107 |
+
...prev,
|
| 108 |
+
[segKey]: {
|
| 109 |
+
done, total: arr.length,
|
| 110 |
+
pct: Math.min(100, Math.round((lb / totalBytes) * 100)),
|
| 111 |
+
name: arr[Math.min(done, arr.length - 1)]?.name || "",
|
| 112 |
+
},
|
| 113 |
+
}));
|
| 114 |
};
|
| 115 |
const one = async (i) => {
|
| 116 |
try {
|
|
|
|
| 126 |
if (!sid) { await one(0); start = 1; }
|
| 127 |
const limit = sid ? 4 : 1; // nếu file đầu lỗi (chưa có session) -> tuần tự cho an toàn
|
| 128 |
await runPool(arr.length - start, (k) => one(start + k), limit);
|
| 129 |
+
// xoá tiến độ CỦA RIÊNG khúc này (khúc khác có thể vẫn đang tải)
|
| 130 |
+
setUploading((prev) => {
|
| 131 |
+
const n = { ...prev };
|
| 132 |
+
delete n[segKey];
|
| 133 |
+
return n;
|
| 134 |
+
});
|
| 135 |
const ok = added.filter(Boolean);
|
| 136 |
const failed = arr.length - ok.length;
|
| 137 |
if (failed) alert(`Có ${failed}/${arr.length} tệp tải lỗi (đã bỏ qua).`);
|
|
|
|
| 287 |
<div className="small muted">Khúc này bị bỏ khỏi video — không cần clip.</div>
|
| 288 |
) : (
|
| 289 |
<>
|
| 290 |
+
{uploading[si] && (
|
| 291 |
<div className="upload-loader" style={{ marginBottom: 8 }}>
|
| 292 |
<div className="small flex between" style={{ marginBottom: 4 }}>
|
| 293 |
<span style={{ color: "var(--orange-700)" }}>
|
| 294 |
+
⏳ Đang tải {uploading[si].done}/{uploading[si].total} clip · {uploading[si].pct}%
|
| 295 |
</span>
|
| 296 |
+
<span className="muted" style={{ maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{uploading[si].name}</span>
|
| 297 |
</div>
|
| 298 |
+
<div className="progress"><i style={{ width: uploading[si].pct + "%" }} /></div>
|
| 299 |
</div>
|
| 300 |
)}
|
| 301 |
<div className="clip-row">
|
|
|
|
| 325 |
<div style={{ width: 150 }}>
|
| 326 |
<FileDrop compact accept="audio/*" onFiles={addAudio} title="Thả âm thanh" hint="MP3, WAV, M4A" />
|
| 327 |
</div>
|
| 328 |
+
{uploading["audio"] && (
|
| 329 |
<div className="upload-loader" style={{ alignSelf: "center", minWidth: 220 }}>
|
| 330 |
<div className="small" style={{ color: "var(--orange-700)", marginBottom: 4 }}>
|
| 331 |
+
⏳ Đang tải {uploading["audio"].done}/{uploading["audio"].total} · {uploading["audio"].pct}%
|
| 332 |
</div>
|
| 333 |
+
<div className="progress"><i style={{ width: uploading["audio"].pct + "%" }} /></div>
|
| 334 |
</div>
|
| 335 |
)}
|
| 336 |
{st.audio.map((a, ai) => (
|