workspaces to dashboards, imap email sync, ffmpeg tool, tts fix, file browser refresh, automation sidebar reorder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,14 +8,12 @@ import * as errors from '@@/custom-errors';
|
||||
import { readTtsConfig } from '@@/api/server-settings/tts';
|
||||
import { readSttConfig } from '@@/api/server-settings/stt';
|
||||
import { readOcrConfig } from '@@/api/server-settings/ocr';
|
||||
import { getUserSettings } from 'officerdb';
|
||||
|
||||
async function getUserTtsVoice(email: string): Promise<string | null> {
|
||||
async function getUserTtsVoice(userId: number): Promise<string | null> {
|
||||
try {
|
||||
const settingsFile = Bun.file(getUserSettingsFile(email));
|
||||
if (await settingsFile.exists()) {
|
||||
const settings = (await settingsFile.json()) as { tts?: { voice?: string | null } };
|
||||
return settings.tts?.voice ?? null;
|
||||
}
|
||||
const settings = await getUserSettings(userId) as { tts?: { voice?: string | null } };
|
||||
return settings.tts?.voice ?? null;
|
||||
} catch {}
|
||||
return null;
|
||||
}
|
||||
@@ -387,7 +385,7 @@ router.post('/tts', async (ctx) => {
|
||||
const ttsConfig = await readTtsConfig();
|
||||
if (!ttsConfig) throw errors.BAD_REQUEST('TTS not configured — set it up in Settings → Text to Speech');
|
||||
|
||||
const userVoice = await getUserTtsVoice(user.email);
|
||||
const userVoice = await getUserTtsVoice(user.id);
|
||||
const voice = userVoice ?? ttsConfig.voice;
|
||||
|
||||
const userDataDir = getUserDataDir(user.email);
|
||||
@@ -439,7 +437,7 @@ router.post('/tts-text', async (ctx) => {
|
||||
const ttsConfig = await readTtsConfig();
|
||||
if (!ttsConfig) throw errors.BAD_REQUEST('TTS not configured — set it up in Settings → Text to Speech');
|
||||
|
||||
const userVoice = await getUserTtsVoice(user.email);
|
||||
const userVoice = await getUserTtsVoice(user.id);
|
||||
const voice = userVoice ?? ttsConfig.voice;
|
||||
|
||||
const userDataDir = getUserDataDir(user.email);
|
||||
@@ -906,16 +904,18 @@ router.post('/download-video', async (ctx) => {
|
||||
if (!path) throw errors.BAD_REQUEST('path is required');
|
||||
|
||||
const absPath = resolveUserPath(rootDir, path);
|
||||
const args = ['yt-dlp', '-o', '%(title)s.%(ext)s'];
|
||||
await mkdir(absPath, { recursive: true });
|
||||
const cookiesPath = join(DATA_PATH, '..', 'yt-dlp-cookies.txt');
|
||||
const args = ['yt-dlp', '--js-runtimes', 'bun', '--cookies', cookiesPath, '-o', '%(title)s.%(ext)s'];
|
||||
if (audioOnly) args.push('-x', '--audio-format', 'mp3');
|
||||
args.push(url);
|
||||
|
||||
const proc = Bun.spawn(args, { cwd: absPath, stdout: 'pipe', stderr: 'pipe' });
|
||||
const proc = Bun.spawn(args, { cwd: absPath, stdout: 'ignore', stderr: 'pipe' });
|
||||
const stderrText = await new Response(proc.stderr).text();
|
||||
const exitCode = await proc.exited;
|
||||
|
||||
if (exitCode !== 0) {
|
||||
const stderr = await new Response(proc.stderr).text();
|
||||
throw errors.BAD_REQUEST(stderr.trim() || 'yt-dlp download failed');
|
||||
throw errors.BAD_REQUEST(stderrText.trim() || 'yt-dlp download failed');
|
||||
}
|
||||
|
||||
return ctx.json({ ok: true });
|
||||
@@ -932,12 +932,12 @@ router.post('/git-clone', async (ctx) => {
|
||||
const absPath = resolveUserPath(rootDir, path);
|
||||
await mkdir(absPath, { recursive: true });
|
||||
|
||||
const proc = Bun.spawn(['git', 'clone', url], { cwd: absPath, stdout: 'pipe', stderr: 'pipe' });
|
||||
const proc = Bun.spawn(['git', 'clone', url], { cwd: absPath, stdout: 'ignore', stderr: 'pipe' });
|
||||
const stderrText = await new Response(proc.stderr).text();
|
||||
const exitCode = await proc.exited;
|
||||
|
||||
if (exitCode !== 0) {
|
||||
const stderr = await new Response(proc.stderr).text();
|
||||
throw errors.BAD_REQUEST(stderr.trim() || 'git clone failed');
|
||||
throw errors.BAD_REQUEST(stderrText.trim() || 'git clone failed');
|
||||
}
|
||||
|
||||
return ctx.json({ ok: true });
|
||||
|
||||
Reference in New Issue
Block a user