This commit is contained in:
Timothy Jaeryang Baek
2026-06-29 14:03:11 -05:00
parent caadfdec0b
commit 5134e5ecfc
2 changed files with 33 additions and 45 deletions
+2 -7
View File
@@ -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='')}"},
)
+31 -38
View File
@@ -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<ReturnType<typeof _getModels>>['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 @@
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
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"
<TTSVoiceInput
bind:value={TTS_VOICE}
voices={providerVoices}
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>
@@ -736,18 +747,12 @@
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
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"
<TTSVoiceInput
bind:value={TTS_VOICE}
voices={providerVoices}
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>
@@ -777,18 +782,12 @@
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
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"
<TTSVoiceInput
bind:value={TTS_VOICE}
voices={providerVoices}
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>
@@ -820,18 +819,12 @@
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('TTS Voice')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
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"
<TTSVoiceInput
bind:value={TTS_VOICE}
voices={providerVoices}
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>