diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index 6d7ac4e9f2..b7a5772c1d 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -2126,18 +2126,13 @@ async def export_knowledge_by_id(id: str, user=Depends(get_admin_user), db: Asyn zip_buffer.seek(0) # Sanitize knowledge name for filename - # ASCII-safe fallback for the basic filename parameter (latin-1 safe) - safe_name = ''.join(c if c.isascii() and (c.isalnum() or c in ' -_') else '_' for c in knowledge.name) + safe_name = ''.join(c if c.isalnum() or c in ' -_' else '_' for c in knowledge.name) 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( zip_buffer, media_type='application/zip', - headers={'Content-Disposition': content_disposition}, + headers={'Content-Disposition': f"attachment; filename*=UTF-8''{quote(zip_filename, safe='')}"}, ) diff --git a/src/lib/components/admin/Settings/Audio.svelte b/src/lib/components/admin/Settings/Audio.svelte index 271c5b8fa9..cf893a24b0 100644 --- a/src/lib/components/admin/Settings/Audio.svelte +++ b/src/lib/components/admin/Settings/Audio.svelte @@ -14,6 +14,7 @@ import Spinner from '$lib/components/common/Spinner.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'; @@ -58,8 +59,18 @@ let STT_WHISPER_MODEL_LOADING = false; + type Voice = { + id: string; + name?: string; + description?: string; + meta?: { + description?: string; + }; + }; + // eslint-disable-next-line no-undef let voices: SpeechSynthesisVoice[] = []; + let providerVoices: Voice[] = []; let models: Awaited>['models'] = []; const getModels = async () => { @@ -82,6 +93,8 @@ const getVoices = async () => { if (TTS_ENGINE === '') { + providerVoices = []; + const getVoicesLoop = setInterval(() => { voices = speechSynthesis.getVoices(); @@ -92,14 +105,18 @@ } }, 100); } else { + voices = []; + const res = await _getVoices(localStorage.token).catch((e) => { toast.error(`${e}`); }); if (res) { console.log(res); - voices = res.voices; - voices.sort((a, b) => a.name.localeCompare(b.name, $i18n.resolvedLanguage)); + providerVoices = res.voices ?? []; + providerVoices.sort((a, b) => + (a.name ?? a.id).localeCompare(b.name ?? b.id, $i18n.resolvedLanguage) + ); } } }; @@ -679,18 +696,12 @@
{$i18n.t('TTS Voice')}
- - - - {#each voices as voice} - - {/each} -
@@ -736,18 +747,12 @@
{$i18n.t('TTS Voice')}
- - - - {#each voices as voice} - - {/each} -
@@ -777,18 +782,12 @@
{$i18n.t('TTS Voice')}
- - - - {#each voices as voice} - - {/each} -
@@ -820,18 +819,12 @@
{$i18n.t('TTS Voice')}
- - - - {#each voices as voice} - - {/each} -