mirror of
https://github.com/open-webui/open-webui.git
synced 2026-07-23 09:10:55 -05:00
refac
This commit is contained in:
@@ -2126,18 +2126,13 @@ async def export_knowledge_by_id(id: str, user=Depends(get_admin_user), db: Asyn
|
|||||||
zip_buffer.seek(0)
|
zip_buffer.seek(0)
|
||||||
|
|
||||||
# Sanitize knowledge name for filename
|
# Sanitize knowledge name for filename
|
||||||
# ASCII-safe fallback for the basic filename parameter (latin-1 safe)
|
safe_name = ''.join(c if c.isalnum() or c in ' -_' else '_' for c in knowledge.name)
|
||||||
safe_name = ''.join(c if c.isascii() and (c.isalnum() or c in ' -_') else '_' for c in knowledge.name)
|
|
||||||
zip_filename = f'{safe_name}.zip'
|
zip_filename = f'{safe_name}.zip'
|
||||||
|
|
||||||
# Use RFC 5987 filename* for non-ASCII names so the browser gets the real name
|
|
||||||
quoted_name = quote(f'{knowledge.name}.zip')
|
|
||||||
content_disposition = f'attachment; filename="{zip_filename}"; filename*=UTF-8\'\'{quoted_name}'
|
|
||||||
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
zip_buffer,
|
zip_buffer,
|
||||||
media_type='application/zip',
|
media_type='application/zip',
|
||||||
headers={'Content-Disposition': content_disposition},
|
headers={'Content-Disposition': f"attachment; filename*=UTF-8''{quote(zip_filename, safe='')}"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||||
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
||||||
|
import TTSVoiceInput from '$lib/components/workspace/Models/TTSVoiceInput.svelte';
|
||||||
|
|
||||||
import { TTS_RESPONSE_SPLIT } from '$lib/types';
|
import { TTS_RESPONSE_SPLIT } from '$lib/types';
|
||||||
|
|
||||||
@@ -58,8 +59,18 @@
|
|||||||
|
|
||||||
let STT_WHISPER_MODEL_LOADING = false;
|
let STT_WHISPER_MODEL_LOADING = false;
|
||||||
|
|
||||||
|
type Voice = {
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
meta?: {
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
let voices: SpeechSynthesisVoice[] = [];
|
let voices: SpeechSynthesisVoice[] = [];
|
||||||
|
let providerVoices: Voice[] = [];
|
||||||
let models: Awaited<ReturnType<typeof _getModels>>['models'] = [];
|
let models: Awaited<ReturnType<typeof _getModels>>['models'] = [];
|
||||||
|
|
||||||
const getModels = async () => {
|
const getModels = async () => {
|
||||||
@@ -82,6 +93,8 @@
|
|||||||
|
|
||||||
const getVoices = async () => {
|
const getVoices = async () => {
|
||||||
if (TTS_ENGINE === '') {
|
if (TTS_ENGINE === '') {
|
||||||
|
providerVoices = [];
|
||||||
|
|
||||||
const getVoicesLoop = setInterval(() => {
|
const getVoicesLoop = setInterval(() => {
|
||||||
voices = speechSynthesis.getVoices();
|
voices = speechSynthesis.getVoices();
|
||||||
|
|
||||||
@@ -92,14 +105,18 @@
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
} else {
|
} else {
|
||||||
|
voices = [];
|
||||||
|
|
||||||
const res = await _getVoices(localStorage.token).catch((e) => {
|
const res = await _getVoices(localStorage.token).catch((e) => {
|
||||||
toast.error(`${e}`);
|
toast.error(`${e}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
voices = res.voices;
|
providerVoices = res.voices ?? [];
|
||||||
voices.sort((a, b) => a.name.localeCompare(b.name, $i18n.resolvedLanguage));
|
providerVoices.sort((a, b) =>
|
||||||
|
(a.name ?? a.id).localeCompare(b.name ?? b.id, $i18n.resolvedLanguage)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -679,18 +696,12 @@
|
|||||||
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
||||||
<div class="flex w-full">
|
<div class="flex w-full">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<input
|
<TTSVoiceInput
|
||||||
list="voice-list"
|
|
||||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
|
||||||
bind:value={TTS_VOICE}
|
bind:value={TTS_VOICE}
|
||||||
|
voices={providerVoices}
|
||||||
placeholder={$i18n.t('Select a voice')}
|
placeholder={$i18n.t('Select a voice')}
|
||||||
|
className="w-full rounded-lg py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="voice-list">
|
|
||||||
{#each voices as voice}
|
|
||||||
<option value={voice.id}>{voice.name}</option>
|
|
||||||
{/each}
|
|
||||||
</datalist>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -736,18 +747,12 @@
|
|||||||
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
||||||
<div class="flex w-full">
|
<div class="flex w-full">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<input
|
<TTSVoiceInput
|
||||||
list="voice-list"
|
|
||||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
|
||||||
bind:value={TTS_VOICE}
|
bind:value={TTS_VOICE}
|
||||||
|
voices={providerVoices}
|
||||||
placeholder={$i18n.t('Select a voice')}
|
placeholder={$i18n.t('Select a voice')}
|
||||||
|
className="w-full rounded-lg py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="voice-list">
|
|
||||||
{#each voices as voice}
|
|
||||||
<option value={voice.id}>{voice.name}</option>
|
|
||||||
{/each}
|
|
||||||
</datalist>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -777,18 +782,12 @@
|
|||||||
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
||||||
<div class="flex w-full">
|
<div class="flex w-full">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<input
|
<TTSVoiceInput
|
||||||
list="voice-list"
|
|
||||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
|
||||||
bind:value={TTS_VOICE}
|
bind:value={TTS_VOICE}
|
||||||
|
voices={providerVoices}
|
||||||
placeholder={$i18n.t('Select a voice')}
|
placeholder={$i18n.t('Select a voice')}
|
||||||
|
className="w-full rounded-lg py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="voice-list">
|
|
||||||
{#each voices as voice}
|
|
||||||
<option value={voice.id}>{voice.name}</option>
|
|
||||||
{/each}
|
|
||||||
</datalist>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -820,18 +819,12 @@
|
|||||||
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
|
||||||
<div class="flex w-full">
|
<div class="flex w-full">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<input
|
<TTSVoiceInput
|
||||||
list="voice-list"
|
|
||||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
|
||||||
bind:value={TTS_VOICE}
|
bind:value={TTS_VOICE}
|
||||||
|
voices={providerVoices}
|
||||||
placeholder={$i18n.t('Select a voice')}
|
placeholder={$i18n.t('Select a voice')}
|
||||||
|
className="w-full rounded-lg py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="voice-list">
|
|
||||||
{#each voices as voice}
|
|
||||||
<option value={voice.id}>{voice.name}</option>
|
|
||||||
{/each}
|
|
||||||
</datalist>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user