diff --git a/src/backend/api/api.yaml b/src/backend/api/api.yaml index 807a73e..8db7123 100644 --- a/src/backend/api/api.yaml +++ b/src/backend/api/api.yaml @@ -13,9 +13,19 @@ paths: application/json: schema: type: object + required: + - filePath properties: filePath: type: string + presetName: + type: string + enum: + - EXTRA_STRICT + - STRICT + - NORMAL + - LENIENT + - EXTRA_LENIENT responses: "200": description: The segments of the file and potential metadata. diff --git a/src/backend/api/audio.py b/src/backend/api/audio.py index c66f17a..f3430c7 100644 --- a/src/backend/api/audio.py +++ b/src/backend/api/audio.py @@ -5,7 +5,7 @@ from flask import Blueprint, Response, jsonify, request, send_file from modules.api_service import ApiService from modules.audio_stream_io import read_audio_file_to_numpy, save_numpy_as_audio_file -from modules.segmentation import segment_file +from modules.segmentation import Preset, segment_file from pathvalidate import sanitize_filename from utils.env import get_env from utils.file_name_formatter import format_file_name @@ -22,9 +22,13 @@ def split(): """ data = request.json file_path = data["filePath"] + preset_name = data["presetName"] + preset = getattr(Preset, preset_name, Preset.NORMAL) + if not os.path.exists(file_path): return "File does not exist!", 400 - generator = segment_file(file_path) + + generator = segment_file(file_path, preset) segments, mismatch_offsets = ApiService().identify_all_from_generator( generator, file_path ) diff --git a/src/backend/api/project.py b/src/backend/api/project.py index 3f2dae9..efcd3a9 100644 --- a/src/backend/api/project.py +++ b/src/backend/api/project.py @@ -62,10 +62,11 @@ def create(): project["files"].append( { - "fileName": file_name, - "filePath": file_path, "name": name, "fileType": file_type, + "fileName": file_name, + "filePath": file_path, + "presetName": "NORMAL", } ) diff --git a/src/frontend/components/BaseSelect.vue b/src/frontend/components/BaseSelect.vue index 10d5306..64ab53e 100644 --- a/src/frontend/components/BaseSelect.vue +++ b/src/frontend/components/BaseSelect.vue @@ -3,7 +3,7 @@ import { onClickOutside } from '@vueuse/core' interface Option { label: string; value: string } -defineProps<{ +const props = defineProps<{ /** * Placeholder for select */ @@ -12,16 +12,22 @@ defineProps<{ * Array of options for select */ options: Option[] + /** + * Is select disabled + */ + disabled?: boolean }>() const modelValue = defineModel({ default: '' }) +const index = ref(props.options.findIndex(o => o.value === modelValue.value) ?? 0) const isExpanded = ref(false) const isMouseOverOpts = ref(false) const button = ref() onClickOutside(button, () => isExpanded.value = false) -function handleChooseOption(v: string) { +function handleChooseOption(v: string, i: number) { modelValue.value = v + index.value = i isExpanded.value = false button.value?.focus() } @@ -34,25 +40,21 @@ function handleChooseOption(v: string) { type="button" role="combobox" :aria-expanded="isExpanded" + :disabled="disabled" class="h-10 w-full flex items-center justify-between border border-input rounded-md bg-transparent px-3 py-2 text-sm ring-offset-background disabled:cursor-not-allowed placeholder:text-muted-foreground disabled:opacity-50 focus:ring-2 focus:ring-offset-2 focus:ring-ring" @click="isExpanded = !isExpanded" > - - - {{ modelValue ?? placeholder }} + + + {{ options[index].label }} - -
  • () const { postAudioSplit, postAudioStore } = getAudioStreamSplittingAPI() @@ -50,34 +51,37 @@ onMounted(() => { }) }) +const presetNameOpts = [ + { value: 'EXTRA_STRICT', label: t('song.preset.extra_strict') }, + { value: 'STRICT', label: t('song.preset.strict') }, + { value: 'NORMAL', label: t('song.preset.normal') }, + { value: 'LENIENT', label: t('song.preset.lenient') }, + { value: 'EXTRA_LENIENT', label: t('song.preset.extra_lenient') }, +] const isProcessing = ref(false) +const presetName = ref(props.file.presetName ?? 'EXTRA_STRICT') +watch(presetName, () => emits('changePresetName', presetName.value)) async function handleProcess() { isProcessing.value = true - try { - let segments: ProjectFileSegment[] - if (props.file.segments) { - segments = props.file.segments - } - else { - toast({ content: t('toast.long_process') }) - const { data } = await postAudioSplit({ filePath: props.file.filePath }) - - if (!data.segments || !data.segments?.length) - throw new Error('This audio cannot be split') - - data.segments = data.segments - .map((s) => { - const notNullOpts = s.metadataOptions?.filter(o => o.album || o.artist || o.title || o.year) - const notDuplicatedOpts = [...new Set((notNullOpts ?? []).map(o => JSON.stringify(o)))] - .map(o => JSON.parse(o)) - return { ...s, metadataOptions: notDuplicatedOpts } - }) - - segments = data.segments.map(s => ({ ...s, metaIndex: 0 })) - - emits('succeedProcess', segments) - } + regions.value?.clearRegions() + try { + toast({ content: t('toast.long_process') }) + const { data } = await postAudioSplit({ filePath: props.file.filePath, presetName: presetName.value }) + + if (!data.segments || !data.segments?.length) + throw new Error('This audio cannot be split') + + data.segments = data.segments + .map((s) => { + const notNullOpts = s.metadataOptions?.filter(o => o.album || o.artist || o.title || o.year) + const notDuplicatedOpts = [...new Set((notNullOpts ?? []).map(o => JSON.stringify(o)))] + .map(o => JSON.parse(o)) + return { ...s, metadataOptions: notDuplicatedOpts } + }) + + const segments = data.segments.map(s => ({ ...s, metaIndex: 0 })) + emits('succeedProcess', segments) addRegion(segments) } catch (e) { @@ -189,15 +193,18 @@ function handleEdit(songIndex: number) { -

    - {{ t('song.no_webm') }} -

    +
    +
    + {{ t('song.preset.index') }} + +
    -
    - +
    diff --git a/src/frontend/pages/settings.vue b/src/frontend/pages/settings.vue index e5cfd71..d01b3cc 100644 --- a/src/frontend/pages/settings.vue +++ b/src/frontend/pages/settings.vue @@ -36,11 +36,7 @@ function handleToggleDark() { v-model="currentLocal" :options="localOpts" class="w-200px" - > - - + />