1import { 2 getAudioTrimConfig, 3 getAudioBudgetRemaining, 4 isAudioBudgetExhausted, 5 planAudioUpload, 6 audioBudgetExhaustedMessage, 7 AUDIO_DURATION_TOLERANCE_SEC, 8} from './audioBudget.js'; 9import { syncSlotValues } from './formState.js'; 10import { probeAudioDurationSec } from '../../utils/audioDecode.js'; 11import { openAudioTrimModal } from '../AudioTrimModal.js'; 12 13/** 14 * ÐагÑÑзка аÑдио-Ñайла в ÑÐ°Ð¼ÐºÐ°Ñ Ð±ÑджеÑа ÑеÑеÑенÑов (Seedance 2.5 Omni). 15 * ÐÑкеÑÑÑаÑÐ¾Ñ Ð²Ð»Ð°Ð´ÐµÐµÑ ÑеÑением Ñеликом: пÑÑÐ¼Ð°Ñ Ð·Ð°Ð³ÑÑзка, модалка обÑезки 16 * (загÑÑзка внÑÑÑи Ð½ÐµÑ ÑеÑез confirm), оÑказ пÑи иÑÑеÑпанном бÑджеÑе. 17 * 18 * СеÑÐ²ÐµÑ â авÑоÑиÑÐµÑ Ð¿Ð¾ длиÑелÑноÑÑи: еÑли клиенÑÑÐºÐ°Ñ Ð¿Ñоба не Ñмогла (или 19 * ÑовÑала) и напÑÑмÑÑ Ð·Ð°Ð³ÑÑзилÑÑ Ñайл, коÑоÑÑй не Ð²Ð»ÐµÐ·Ð°ÐµÑ Ð² бÑджеÑ, ÑÐ»Ð¾Ñ 20 * оÑвобождаеÑÑÑ Ð¸ оÑкÑÑваеÑÑÑ Ð¼Ð¾Ð´Ð°Ð»ÐºÐ° Ñже Ñ ÑеÑвеÑной длиÑелÑноÑÑÑÑ. 21 * 22 * @param {File} file 23 * @param {string} paramName 24 * @param {number} index - ÑлоÑ, кÑда идÑÑ Ð·Ð°Ð³ÑÑзка (не ÑÑиÑаеÑÑÑ Ð² бÑджеÑе) 25 * @param {Object} formState 26 * @param {Object} hooks 27 * @param {(trim: {startSec:number,endSec:number}|null) => Promise<boolean>} hooks.performUpload 28 * @param {() => void} [hooks.onSlotChanged] - пеÑеÑиÑоваÑÑ ÑлоÑÑ Ð¿Ð¾Ñле оÑÐ²Ð¾Ð±Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ 29 * @returns {Promise<void>} 30 */ 31export async function uploadAudioWithinBudget(file, paramName, index, formState, { performUpload, onSlotChanged }) { 32 const cfg = getAudioTrimConfig(formState.selectedModelId, paramName); 33 if (!cfg) { 34 await performUpload(null); 35 return; 36 } 37 38 if (isAudioBudgetExhausted(formState, paramName, cfg, index)) { 39 alert(audioBudgetExhaustedMessage(cfg.totalSec)); 40 return; 41 } 42 43 const remainingSec = getAudioBudgetRemaining(formState, paramName, cfg.totalSec, index); 44 const durationSec = await probeAudioDurationSec(file); 45 const plan = planAudioUpload(durationSec, remainingSec, cfg); 46 47 if (plan.mode === 'trim') { 48 await openAudioTrimModal(file, { minSec: plan.minSec, maxSec: plan.maxSec, durationSec, confirm: performUpload }); 49 return; 50 } 51 52 const uploaded = await performUpload(null); 53 if (!uploaded) return; 54 55 const entry = formState.uploadedFiles?.[paramName]?.[index]; 56 const serverSec = Number(entry?.duration); 57 if (!entry || !Number.isFinite(serverSec)) return; 58 if (serverSec <= remainingSec + AUDIO_DURATION_TOLERANCE_SEC && serverSec >= cfg.minSec) return; 59 60 // Ðе влезло по ÑеÑвеÑной меÑке â оÑвобождаем ÑÐ»Ð¾Ñ (инаÑе ÑÑпик на кнопке генеÑаÑии) 61 delete formState.uploadedFiles[paramName][index]; 62 syncSlotValues(formState, paramName); 63 if (onSlotChanged) onSlotChanged(); 64 65 if (serverSec < cfg.minSec) { 66 alert(`ÐÑдио коÑоÑе ${cfg.minSec} ÑекÑнд â загÑÑзиÑе Ñайл длиннее`); 67 return; 68 } 69 await openAudioTrimModal(file, { 70 minSec: cfg.minSec, 71 maxSec: Math.min(cfg.maxSec, remainingSec), 72 durationSec: serverSec, 73 confirm: performUpload, 74 }); 75}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.