titusgiap Claude Opus 4.8 commited on
Commit
056724a
·
1 Parent(s): f0e1c94

fix(shuffle): tiến độ upload theo TỪNG khúc, không còn nhảy loạn xạ

Browse files

Trướ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>

Files changed (1) hide show
  1. frontend/src/pages/ShuffleVideo.jsx +24 -14
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(null);
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
- seg: segKey, done, total: arr.length,
106
- pct: Math.min(100, Math.round((lb / totalBytes) * 100)),
107
- name: arr[Math.min(done, arr.length - 1)]?.name || "",
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
- setUploading(null);
 
 
 
 
 
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?.seg === si && (
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?.seg === "audio" && (
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) => (