PageSourceSearch

https://marketvideo.ru/src/components/generation-form/audioTrimFlow.js

js marketvideo.ru collected 2026-10-08 08:35:00 UTC 3,331 bytes, 75 lines download raw bytes

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.