- A folder holding one character's GPT + SoVITS weights and its clips is that character, named after the folder: two characters whose weights are both "model" no longer merge, pinyin weights in a Chinese-named folder take the folder's name, version folders read "派蒙 v2". Package weight folders keep the name-by-stem rule. - Skip rules match the node (interface v9): Python environments and base models always; GPT_SoVITS / logs / output / TEMP / tools only inside a package; the chosen or dropped folder never. The drop walker and the folder dialog apply them where the listing is known (the old re-check on each file's path skipped a chosen folder called "output"), local scans prefix the chosen folder so every source sorts alike, and the window names the skipped and too-deep folders instead of losing files quietly. Drops only open importable files and stop at 5000. - Same-name files inside a character are renamed with their folder (难过_01.wav, sent as the file's "name") instead of dropped; an unclaimed .list goes to every character. - Big adds open 20 characters (about 800 files) at a time; "next batch" opens the rest, and it opens by itself once a batch is imported, replacing the imported cards. Unassigned files upload only once put into a character. - Every clip can be previewed while importing, local paths included (through the node's preview route): one at a time, ▶ turns into stop, another clip switches. - Upload queue and preview player move to their own modules, the "add more" menu to the screens module (ui_tts_import.js 647 → 652 lines with batches and renames added). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RMuwbsWZHnx2eCWpxYqAB
46 lines
1.5 KiB
JavaScript
46 lines
1.5 KiB
JavaScript
import { uploadFile } from './tts_setup_api.js';
|
|
|
|
/**
|
|
* The import window's uploads: browser files go to the node in chunks, a few files at
|
|
* a time, in the order they were queued. A row is `{ file, controller, uploadId,
|
|
* progress, spec, error }` (ui_tts_import.js); an aborted row is dropped. `onProgress(row)`
|
|
* repaints the row; `onDone(row)` runs once it is uploaded (`spec` set) or failed (`error`).
|
|
*/
|
|
|
|
const UPLOADS_AT_ONCE = 3;
|
|
|
|
export function createUploadQueue({ onProgress, onDone }) {
|
|
const queue = [];
|
|
let running = 0;
|
|
|
|
async function upload(row) {
|
|
try {
|
|
await uploadFile(row.file, {
|
|
signal: row.controller.signal,
|
|
onStart: (id) => { row.uploadId = id; },
|
|
onProgress: (fraction) => { row.progress = fraction; onProgress(row); },
|
|
});
|
|
row.spec = { upload: row.uploadId };
|
|
} catch (e) {
|
|
if (row.controller.signal.aborted) return;
|
|
row.error = e.message || String(e);
|
|
}
|
|
onDone(row);
|
|
}
|
|
|
|
function pump() {
|
|
while (running < UPLOADS_AT_ONCE && queue.length) {
|
|
const row = queue.shift();
|
|
if (row.controller.signal.aborted) continue;
|
|
running++;
|
|
upload(row).finally(() => { running--; pump(); });
|
|
}
|
|
}
|
|
|
|
return {
|
|
add: (row) => { queue.push(row); pump(); },
|
|
/** The window closes: nothing more starts (running uploads stop through their rows' controllers). */
|
|
clear: () => { queue.length = 0; },
|
|
};
|
|
}
|