diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 629d82ac98..442ca53cb9 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -327,6 +327,15 @@ importers: viewer: dependencies: + '@ffmpeg/core': + specifier: 0.12.10 + version: 0.12.10 + '@ffmpeg/ffmpeg': + specifier: 0.12.15 + version: 0.12.15 + '@ffmpeg/util': + specifier: 0.12.2 + version: 0.12.2 '@formatjs/intl-durationformat': specifier: ^0.7.4 version: 0.7.4 @@ -1106,6 +1115,22 @@ packages: '@fastify/busboy@3.1.1': resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==} + '@ffmpeg/core@0.12.10': + resolution: {integrity: sha512-dzNplnn2Nxle2c2i2rrDhqcB19q9cglCkWnoMTDN9Q9l3PvdjZWd1HfSPjCNWc/p8Q3CT+Es9fWOR0UhAeYQZA==} + engines: {node: '>=16.x'} + + '@ffmpeg/ffmpeg@0.12.15': + resolution: {integrity: sha512-1C8Obr4GsN3xw+/1Ww6PFM84wSQAGsdoTuTWPOj2OizsRDLT4CXTaVjPhkw6ARyDus1B9X/L2LiXHqYYsGnRFw==} + engines: {node: '>=18.x'} + + '@ffmpeg/types@0.12.4': + resolution: {integrity: sha512-k9vJQNBGTxE5AhYDtOYR5rO5fKsspbg51gbcwtbkw2lCdoIILzklulcjJfIDwrtn7XhDeF2M+THwJ2FGrLeV6A==} + engines: {node: '>=16.x'} + + '@ffmpeg/util@0.12.2': + resolution: {integrity: sha512-ouyoW+4JB7WxjeZ2y6KpRvB+dLp7Cp4ro8z0HIVpZVCM7AwFlHa0c4R8Y/a4M3wMqATpYKhC7lSFHQ0T11MEDw==} + engines: {node: '>=18.x'} + '@floating-ui/core@1.7.2': resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} @@ -7143,6 +7168,16 @@ snapshots: '@fastify/busboy@3.1.1': {} + '@ffmpeg/core@0.12.10': {} + + '@ffmpeg/ffmpeg@0.12.15': + dependencies: + '@ffmpeg/types': 0.12.4 + + '@ffmpeg/types@0.12.4': {} + + '@ffmpeg/util@0.12.2': {} + '@floating-ui/core@1.7.2': dependencies: '@floating-ui/utils': 0.2.10 diff --git a/frontend/viewer/package.json b/frontend/viewer/package.json index 8a0f778660..5a300c5e0f 100644 --- a/frontend/viewer/package.json +++ b/frontend/viewer/package.json @@ -11,6 +11,7 @@ "scripts": { "dev": "vite", "build": "vite build", + "build-ffmpeg-worker": "vite build --config vite.config.ffmpeg-worker.ts", "preview": "vite preview", "pretest:playwright": "playwright install", "test:playwright": "playwright test", @@ -89,6 +90,9 @@ "@microsoft/signalr": "^8.0.7", "autoprefixer": "^10.4.21", "fast-json-patch": "^3.1.1", + "@ffmpeg/ffmpeg": "0.12.15", + "@ffmpeg/util": "0.12.2", + "@ffmpeg/core": "0.12.10", "jsdom": "^26.1.0", "just-throttle": "^4.2.0", "postcss": "catalog:", diff --git a/frontend/viewer/src/lib/components/audio/AudioDialog.svelte b/frontend/viewer/src/lib/components/audio/AudioDialog.svelte index 9b5b0a394e..a847847523 100644 --- a/frontend/viewer/src/lib/components/audio/AudioDialog.svelte +++ b/frontend/viewer/src/lib/components/audio/AudioDialog.svelte @@ -5,10 +5,8 @@ import {useDialogsService} from '$lib/services/dialogs-service.js'; import {useBackHandler} from '$lib/utils/back-handler.svelte'; import {watch} from 'runed'; - import {delay} from '$lib/utils/time'; import AudioProvider from './audio-provider.svelte'; import AudioEditor from './audio-editor.svelte'; - import Loading from '$lib/components/Loading.svelte'; import {useLexboxApi} from '$lib/services/service-provider'; import {UploadFileResult} from '$lib/dotnet-types/generated-types/MiniLcm/Media/UploadFileResult'; import {AppNotification} from '$lib/notifications/notifications'; @@ -21,8 +19,8 @@ let submitting = $state(false); let selectedFile = $state(); - let audio = $state(); - const tooBig = $derived((audio?.size ?? 0) > 10 * 1024 * 1024); + let finalAudio = $state(); + const tooBig = $derived((finalAudio?.size ?? 0) > 10 * 1024 * 1024); let requester: { resolve: (mediaUri: string | undefined) => void @@ -41,6 +39,10 @@ if (!open) reset(); }); + watch(() => selectedFile, () => { + if (!selectedFile) finalAudio = undefined; + }) + function close() { open = false; reset(); @@ -53,12 +55,12 @@ } function clearAudio() { - audio = selectedFile = undefined; + selectedFile = undefined; submitting = false; } async function submitAudio() { - if (!audio) throw new Error('No audio to upload'); + if (!selectedFile) throw new Error('No audio to upload'); if (!requester) throw new Error('No requester'); submitting = true; @@ -72,8 +74,8 @@ } async function uploadAudio() { - if (!audio || !selectedFile) throw new Error($t`No file selected`); - const response = await lexboxApi.saveFile(audio, {filename: selectedFile.name, mimeType: audio.type}); + if (!finalAudio) throw new Error($t`No file to upload`); + const response = await lexboxApi.saveFile(finalAudio, {filename: finalAudio.name, mimeType: finalAudio.type}); switch (response.result) { case UploadFileResult.SavedLocally: AppNotification.display($t`Audio saved locally`, 'success'); @@ -94,16 +96,13 @@ return response.mediaUri; } - async function onFileSelected(file: File) { + function onFileSelected(file: File) { selectedFile = file; - audio = await processAudio(file); } - async function onRecordingComplete(blob: Blob) { + function onRecordingComplete(blob: Blob) { let fileExt = mimeTypeToFileExtension(blob.type); selectedFile = new File([blob], `recording-${Date.now()}.${fileExt}`, {type: blob.type}); - if (!open) return; - audio = await processAudio(blob); } function mimeTypeToFileExtension(mimeType: string) { @@ -133,17 +132,8 @@ } function onDiscard() { - audio = undefined; selectedFile = undefined; } - - let loading = $state(false); - async function processAudio(blob: Blob): Promise { - loading = true; - await delay(1000); // Simulate processing delay - loading = false; - return blob; - } @@ -152,20 +142,16 @@ {$t`Add audio`} - {#if !audio || !selectedFile} - {#if loading} - - {:else} - - {/if} + {#if !selectedFile} + {:else} - + {#if tooBig}

{$t`File too big`}

{/if} - diff --git a/frontend/viewer/src/lib/components/audio/audio-editor.svelte b/frontend/viewer/src/lib/components/audio/audio-editor.svelte index 7979859aa8..5f3288f1e5 100644 --- a/frontend/viewer/src/lib/components/audio/audio-editor.svelte +++ b/frontend/viewer/src/lib/components/audio/audio-editor.svelte @@ -1,54 +1,119 @@
+ {#if loading || !finalAudio} + + {:else} - {$t`${formatedDuration}`} + {$t`${formattedDuration}`} {$t`${mb} MB`} - {#if name} + {#if finalAudio.name} - {$t`${name}`} + {$t`${finalAudio.name}`} {/if} - {$t`${audio.type}`} + {$t`${finalAudio.type}`} - - -
- -
-
- -
+ + +
+ +
+
+ + +
+ {/if} + {#if error} +

{error}

+ {/if}
diff --git a/frontend/viewer/src/lib/components/audio/ffmpeg/bundled-ffmpeg-worker.js b/frontend/viewer/src/lib/components/audio/ffmpeg/bundled-ffmpeg-worker.js new file mode 100644 index 0000000000..8da5125f02 --- /dev/null +++ b/frontend/viewer/src/lib/components/audio/ffmpeg/bundled-ffmpeg-worker.js @@ -0,0 +1,104 @@ +/* This file generated by Vite / pnpm build-ffmpeg-worker. */ +// @ts-nocheck +const f = "0.12.9", R = `https://unpkg.com/@ffmpeg/core@${f}/dist/umd/ffmpeg-core.js`; +var E; +(function(t) { + t.LOAD = "LOAD", t.EXEC = "EXEC", t.FFPROBE = "FFPROBE", t.WRITE_FILE = "WRITE_FILE", t.READ_FILE = "READ_FILE", t.DELETE_FILE = "DELETE_FILE", t.RENAME = "RENAME", t.CREATE_DIR = "CREATE_DIR", t.LIST_DIR = "LIST_DIR", t.DELETE_DIR = "DELETE_DIR", t.ERROR = "ERROR", t.DOWNLOAD = "DOWNLOAD", t.PROGRESS = "PROGRESS", t.LOG = "LOG", t.MOUNT = "MOUNT", t.UNMOUNT = "UNMOUNT"; +})(E || (E = {})); +const u = new Error("unknown message type"), O = new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first"), m = new Error("failed to import ffmpeg-core.js"); +let r; +const l = async ({ coreURL: t, wasmURL: n, workerURL: e }) => { + const o = !r; + try { + t || (t = R), importScripts(t); + } catch { + if ((!t || t === R) && (t = R.replace("/umd/", "/esm/")), self.createFFmpegCore = (await import( + /* @vite-ignore */ + t + )).default, !self.createFFmpegCore) + throw m; + } + const s = t, c = n || t.replace(/.js$/g, ".wasm"), a = e || t.replace(/.js$/g, ".worker.js"); + return r = await self.createFFmpegCore({ + // Fix `Overload resolution failed.` when using multi-threaded ffmpeg-core. + // Encoded wasmURL and workerURL in the URL as a hack to fix locateFile issue. + mainScriptUrlOrBlob: `${s}#${btoa(JSON.stringify({ wasmURL: c, workerURL: a }))}` + }), r.setLogger((i) => self.postMessage({ type: E.LOG, data: i })), r.setProgress((i) => self.postMessage({ + type: E.PROGRESS, + data: i + })), o; +}, D = ({ args: t, timeout: n = -1 }) => { + r.setTimeout(n), r.exec(...t); + const e = r.ret; + return r.reset(), e; +}, S = ({ args: t, timeout: n = -1 }) => { + r.setTimeout(n), r.ffprobe(...t); + const e = r.ret; + return r.reset(), e; +}, I = ({ path: t, data: n }) => (r.FS.writeFile(t, n), !0), L = ({ path: t, encoding: n }) => r.FS.readFile(t, { encoding: n }), N = ({ path: t }) => (r.FS.unlink(t), !0), A = ({ oldPath: t, newPath: n }) => (r.FS.rename(t, n), !0), k = ({ path: t }) => (r.FS.mkdir(t), !0), w = ({ path: t }) => { + const n = r.FS.readdir(t), e = []; + for (const o of n) { + const s = r.FS.stat(`${t}/${o}`), c = r.FS.isDir(s.mode); + e.push({ name: o, isDir: c }); + } + return e; +}, b = ({ path: t }) => (r.FS.rmdir(t), !0), p = ({ fsType: t, options: n, mountPoint: e }) => { + const o = t, s = r.FS.filesystems[o]; + return s ? (r.FS.mount(s, n, e), !0) : !1; +}, d = ({ mountPoint: t }) => (r.FS.unmount(t), !0); +self.onmessage = async ({ data: { id: t, type: n, data: e } }) => { + const o = []; + let s; + try { + if (n !== E.LOAD && !r) + throw O; + switch (n) { + case E.LOAD: + s = await l(e); + break; + case E.EXEC: + s = D(e); + break; + case E.FFPROBE: + s = S(e); + break; + case E.WRITE_FILE: + s = I(e); + break; + case E.READ_FILE: + s = L(e); + break; + case E.DELETE_FILE: + s = N(e); + break; + case E.RENAME: + s = A(e); + break; + case E.CREATE_DIR: + s = k(e); + break; + case E.LIST_DIR: + s = w(e); + break; + case E.DELETE_DIR: + s = b(e); + break; + case E.MOUNT: + s = p(e); + break; + case E.UNMOUNT: + s = d(e); + break; + default: + throw u; + } + } catch (c) { + self.postMessage({ + id: t, + type: E.ERROR, + data: c.toString() + }); + return; + } + s instanceof Uint8Array && o.push(s.buffer), self.postMessage({ id: t, type: n, data: s }, o); +}; diff --git a/frontend/viewer/src/lib/components/audio/ffmpeg/ffmpeg-api.ts b/frontend/viewer/src/lib/components/audio/ffmpeg/ffmpeg-api.ts new file mode 100644 index 0000000000..9157932b4f --- /dev/null +++ b/frontend/viewer/src/lib/components/audio/ffmpeg/ffmpeg-api.ts @@ -0,0 +1,136 @@ +import {FFmpeg} from '@ffmpeg/ffmpeg'; +import coreURL from '@ffmpeg/core?url'; +import {fetchFile} from '@ffmpeg/util'; +import inlineDataUrlWorker from './bundled-ffmpeg-worker.js?url&inline'; +import wasmUrl from '@ffmpeg/core/wasm?url'; +import {randomId} from '$lib/utils'; + + +async function ensureLoaded(ffmpeg: FFmpeg) { + if (ffmpeg.loaded) return; + await ffmpeg.load({ + coreURL: coreURL, + wasmURL: wasmUrl, + classWorkerURL: import.meta.env.DEV ? inlineDataUrlWorker : undefined, + }); +} + +async function loadFFmpegInternal(): Promise { + const ffmpeg = new FFmpeg(); + ffmpeg.on('log', (msg) => { + console.log('FFmpeg log:', msg); + }); + await ensureLoaded(ffmpeg); + + return ffmpeg; +} + +export class FFmpegFile { + readonly filename: string; + private id: string = randomId(); + private prefix: string; + + constructor(mimeType: string, filename: string, prefix: string) { + this.mimeType = mimeType; + this.filename = filename; + this.prefix = prefix; + } + + get internalFilePath() { + return `${this.internalFileDir}/${this.filename}`; + } + get internalFileDir() { + return `/${this.prefix}/${this.id}`; + } + readonly mimeType: string; + + changeExtension(newExtension: string, mimeType?: string, prefix?: string): FFmpegFile { + return new FFmpegFile(mimeType ?? this.mimeType, this.filename.replace(/\.[^.]+$/, `.${newExtension}`), prefix ?? this.prefix); + } +} + +export class FFmpegApi { + private static loadingPromise: Promise | null = null; + + public static async create(): Promise { + console.log('Loading FFmpeg...'); + FFmpegApi.loadingPromise ??= loadFFmpegInternal(); + const ffmpeg = await FFmpegApi.loadingPromise; + await ensureLoaded(ffmpeg); + console.log('FFmpeg loaded:', ffmpeg); + return new FFmpegApi(ffmpeg); + } + + private constructor(private ffmpeg: FFmpeg) { + } + + public terminate() { + this.ffmpeg.terminate(); + } + + private async createDir(path: string, signal: AbortSignal) { + console.log('Creating dir:', path); + const paths = path.split('/').filter(v => !!v); + let newDir = ''; + for (const dir of paths) { + console.log('Checking dir:', newDir, dir); + const dirExists = (await this.ffmpeg.listDir(newDir || '/', {signal})).some(f => f.name === dir); + newDir += `/${dir}`; + if (dirExists) { + continue; + } + console.log('Creating dir:', newDir); + //create dir never returns if the dir already exists or if you're trying to create a sub directory of a directory which doesn't exist + await this.ffmpeg.createDir(newDir, {signal}); + } + } + async toFFmpegFile(file: File, signal: AbortSignal): Promise { + console.log('Writing file to ffmpeg FS:', file.name); + const ffmpegFile = new FFmpegFile(file.type, file.name, 'input'); + await this.createDir(ffmpegFile.internalFileDir, signal); + await this.ffmpeg.writeFile(ffmpegFile.internalFilePath, await fetchFile(file), {signal}); + return ffmpegFile; + } + + async readFile(file: FFmpegFile, signal: AbortSignal): Promise { + console.log('Reading file from ffmpeg FS:', file.filename); + const data = await this.ffmpeg.readFile(file.internalFilePath, undefined, {signal}) as Uint8Array; + return new File([data], file.filename, {type: file.mimeType}); + } + + async convertToWav(file: FFmpegFile, signal: AbortSignal): Promise { + const convertedFile = file.changeExtension('wav', 'audio/wav', 'convert'); + await this.createDir(convertedFile.internalFileDir, signal); + await this.ffmpeg.exec( + [ + '-i', file.internalFilePath, + '-af', 'loudnorm', + '-ar', '44100', + '-ac', '2', + '-codec:a', 'pcm_s16le', + convertedFile.internalFilePath + ], + undefined, + {signal} + ); + return convertedFile; + } + + async convertToFlac(file: FFmpegFile, signal: AbortSignal): Promise { + console.log('Converting to FLAC:', file.filename); + const convertedFile = file.changeExtension('flac', 'audio/flac', 'convert'); + await this.createDir(convertedFile.internalFileDir, signal); + await this.ffmpeg.exec( + [ + '-i', file.internalFilePath, + '-af', 'loudnorm', + '-ar', '44100', + '-codec:a', 'flac', + convertedFile.internalFilePath + ], + undefined, + {signal} + ); + return convertedFile; + } +} diff --git a/frontend/viewer/src/lib/components/audio/ffmpeg/index.ts b/frontend/viewer/src/lib/components/audio/ffmpeg/index.ts new file mode 100644 index 0000000000..d3662d20fa --- /dev/null +++ b/frontend/viewer/src/lib/components/audio/ffmpeg/index.ts @@ -0,0 +1 @@ +export * from './ffmpeg-api'; diff --git a/frontend/viewer/src/lib/components/field-editors/audio-input.svelte b/frontend/viewer/src/lib/components/field-editors/audio-input.svelte index 1d9fe3e440..46b437a11a 100644 --- a/frontend/viewer/src/lib/components/field-editors/audio-input.svelte +++ b/frontend/viewer/src/lib/components/field-editors/audio-input.svelte @@ -51,7 +51,6 @@ import {t} from 'svelte-i18n-lingui'; import {ReadFileResult} from '$lib/dotnet-types/generated-types/MiniLcm/Media/ReadFileResult'; import {useDialogsService} from '$lib/services/dialogs-service'; - import {isDev} from '$lib/layout/DevContent.svelte'; import * as ResponsiveMenu from '$lib/components/responsive-menu'; const handled = Symbol(); @@ -208,15 +207,9 @@ {#if supportsAudio} {#if !audioId} - {#if $isDev} - - {:else} -
- {$t`No audio`} -
- {/if} + {:else if isNotFoundAudioId(audioId)}
{$t`Audio file not included in Send & Receive`} diff --git a/frontend/viewer/src/lib/sandbox/Sandbox.svelte b/frontend/viewer/src/lib/sandbox/Sandbox.svelte index 86912fb249..addd451639 100644 --- a/frontend/viewer/src/lib/sandbox/Sandbox.svelte +++ b/frontend/viewer/src/lib/sandbox/Sandbox.svelte @@ -33,7 +33,7 @@ import {formatDate, FormatDate, formatNumber} from '$lib/components/ui/format'; import {SvelteDate} from 'svelte/reactivity'; import {RichTextToggle} from '$lib/dotnet-types/generated-types/MiniLcm/Models/RichTextToggle'; - + import {FFmpegApi} from '$lib/components/audio/ffmpeg'; const testingService = tryUseService(DotnetService.TestingService); @@ -125,6 +125,12 @@ let reseter = $state(0); let currentDate = new SvelteDate(); + + async function preloadFFmpeg() { + console.log('Loading FFmpeg...'); + let ffmpeg = await FFmpegApi.create(); + console.log('FFmpeg loaded:', ffmpeg); + }
@@ -136,6 +142,7 @@ {/snippet} +
diff --git a/frontend/viewer/src/locales/en.po b/frontend/viewer/src/locales/en.po index 37306d18e9..60a998b6b4 100644 --- a/frontend/viewer/src/locales/en.po +++ b/frontend/viewer/src/locales/en.po @@ -17,9 +17,9 @@ msgstr "" msgid "– ({0} changes)" msgstr "– ({0} changes)" -#: src/lib/components/audio/audio-editor.svelte:28 -#: src/lib/components/audio/audio-editor.svelte:33 -#: src/lib/components/audio/audio-editor.svelte:37 +#: src/lib/components/audio/audio-editor.svelte:86 +#: src/lib/components/audio/audio-editor.svelte:91 +#: src/lib/components/audio/audio-editor.svelte:95 msgid "{0}" msgstr "{0}" @@ -39,7 +39,7 @@ msgstr "{0} (FieldWorks Lite)" msgid "{0} (FieldWorks)" msgstr "{0} (FieldWorks)" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "{0} MB" msgstr "{0} MB" @@ -76,8 +76,8 @@ msgstr "Account" msgid "Activity" msgstr "Activity" -#: src/lib/components/field-editors/audio-input.svelte:213 -#: src/lib/components/audio/AudioDialog.svelte:153 +#: src/lib/components/field-editors/audio-input.svelte:211 +#: src/lib/components/audio/AudioDialog.svelte:143 msgid "Add audio" msgstr "Add audio" @@ -126,15 +126,15 @@ msgstr "Application version" msgid "Are you sure you want to delete {0}?" msgstr "Are you sure you want to delete {0}?" -#: src/lib/components/field-editors/audio-input.svelte:222 +#: src/lib/components/field-editors/audio-input.svelte:215 msgid "Audio file not included in Send & Receive" msgstr "Audio file not included in Send & Receive" -#: src/lib/components/audio/AudioDialog.svelte:82 +#: src/lib/components/audio/AudioDialog.svelte:84 msgid "Audio saved and uploaded to Lexbox" msgstr "Audio saved and uploaded to Lexbox" -#: src/lib/components/audio/AudioDialog.svelte:79 +#: src/lib/components/audio/AudioDialog.svelte:81 msgid "Audio saved locally" msgstr "Audio saved locally" @@ -168,7 +168,7 @@ msgstr "Browse" #: src/lib/entry-editor/EntryOrSensePicker.svelte:245 #: src/lib/components/field-editors/select.svelte:166 #: src/lib/components/field-editors/multi-select.svelte:280 -#: src/lib/components/audio/AudioDialog.svelte:167 +#: src/lib/components/audio/AudioDialog.svelte:153 msgid "Cancel" msgstr "Cancel" @@ -300,7 +300,7 @@ msgstr "Dictionary" msgid "Dictionary Preview" msgstr "Dictionary Preview" -#: src/lib/components/audio/audio-editor.svelte:46 +#: src/lib/components/audio/audio-editor.svelte:107 msgid "Discard" msgstr "Discard" @@ -314,6 +314,7 @@ msgid "Don't delete" msgstr "Don't delete" #: src/lib/notifications/NotificationOutlet.svelte:26 +#: src/lib/components/audio/audio-editor.svelte:106 #: src/home/Server.svelte:128 msgid "Download" msgstr "Download" @@ -407,24 +408,24 @@ msgstr "Field Labels" msgid "FieldWorks logo" msgstr "FieldWorks logo" -#: src/lib/components/audio/AudioDialog.svelte:89 +#: src/lib/components/audio/AudioDialog.svelte:91 msgid "File already exists" msgstr "File already exists" -#: src/lib/components/audio/audio-editor.svelte:32 +#: src/lib/components/audio/audio-editor.svelte:90 msgid "File name:" msgstr "File name:" -#: src/lib/components/field-editors/audio-input.svelte:79 +#: src/lib/components/field-editors/audio-input.svelte:78 msgid "File not found" msgstr "File not found" -#: src/lib/components/audio/AudioDialog.svelte:87 +#: src/lib/components/audio/AudioDialog.svelte:89 msgid "File saving not supported" msgstr "File saving not supported" -#: src/lib/components/audio/AudioDialog.svelte:164 -#: src/lib/components/audio/AudioDialog.svelte:85 +#: src/lib/components/audio/AudioDialog.svelte:150 +#: src/lib/components/audio/AudioDialog.svelte:87 msgid "File too big" msgstr "File too big" @@ -504,7 +505,7 @@ msgstr "Import" msgid "Last change: {0}" msgstr "Last change: {0}" -#: src/lib/components/audio/audio-editor.svelte:28 +#: src/lib/components/audio/audio-editor.svelte:86 msgid "Length:" msgstr "Length:" @@ -608,10 +609,6 @@ msgstr "New Entry" msgid "No activity found" msgstr "No activity found" -#: src/lib/components/field-editors/audio-input.svelte:217 -msgid "No audio" -msgstr "No audio" - #: src/lib/history/HistoryView.svelte:78 msgid "No change name" msgstr "No change name" @@ -625,9 +622,9 @@ msgstr "No Dictionaries found" msgid "No entries found" msgstr "No entries found" -#: src/lib/components/audio/AudioDialog.svelte:75 -msgid "No file selected" -msgstr "No file selected" +#: src/lib/components/audio/AudioDialog.svelte:77 +msgid "No file to upload" +msgstr "No file to upload" #: src/lib/history/HistoryView.svelte:68 msgid "No history found" @@ -670,7 +667,7 @@ msgstr "Note" msgid "Offline" msgstr "Offline" -#: src/lib/components/field-editors/audio-input.svelte:82 +#: src/lib/components/field-editors/audio-input.svelte:81 msgid "Offline, unable to download" msgstr "Offline, unable to download" @@ -737,7 +734,7 @@ msgstr "Refresh Projects" msgid "Remove" msgstr "Remove" -#: src/lib/components/field-editors/audio-input.svelte:268 +#: src/lib/components/field-editors/audio-input.svelte:261 msgid "Remove audio" msgstr "Remove audio" @@ -745,11 +742,11 @@ msgstr "Remove audio" msgid "Reopen" msgstr "Reopen" -#: src/lib/components/field-editors/audio-input.svelte:265 +#: src/lib/components/field-editors/audio-input.svelte:258 msgid "Replace audio" msgstr "Replace audio" -#: src/lib/components/audio/AudioDialog.svelte:169 +#: src/lib/components/audio/AudioDialog.svelte:155 msgid "Save audio" msgstr "Save audio" @@ -813,7 +810,7 @@ msgstr "Show {0} more..." msgid "Simple" msgstr "Simple" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "Size:" msgstr "Size:" @@ -886,7 +883,7 @@ msgstr "Translation" msgid "Troubleshoot" msgstr "Troubleshoot" -#: src/lib/components/audio/audio-editor.svelte:36 +#: src/lib/components/audio/audio-editor.svelte:94 msgid "Type:" msgstr "Type:" @@ -902,11 +899,11 @@ msgid "Unknown" msgstr "Unknown" #: src/project/ProjectSidebar.svelte:115 -#: src/lib/components/audio/AudioDialog.svelte:91 +#: src/lib/components/audio/AudioDialog.svelte:93 msgid "Unknown error" msgstr "Unknown error" -#: src/lib/components/field-editors/audio-input.svelte:85 +#: src/lib/components/field-editors/audio-input.svelte:84 msgid "Unknown error {0}" msgstr "Unknown error {0}" diff --git a/frontend/viewer/src/locales/es.po b/frontend/viewer/src/locales/es.po index 660ff8b3df..b5d115efe0 100644 --- a/frontend/viewer/src/locales/es.po +++ b/frontend/viewer/src/locales/es.po @@ -22,9 +22,9 @@ msgstr "" msgid "– ({0} changes)" msgstr "- ({0} changes)" -#: src/lib/components/audio/audio-editor.svelte:28 -#: src/lib/components/audio/audio-editor.svelte:33 -#: src/lib/components/audio/audio-editor.svelte:37 +#: src/lib/components/audio/audio-editor.svelte:86 +#: src/lib/components/audio/audio-editor.svelte:91 +#: src/lib/components/audio/audio-editor.svelte:95 msgid "{0}" msgstr "{0}" @@ -44,7 +44,7 @@ msgstr "{0} (FieldWorks Lite)" msgid "{0} (FieldWorks)" msgstr "{0} (FieldWorks)" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "{0} MB" msgstr "{0} MB" @@ -81,8 +81,8 @@ msgstr "Cuenta" msgid "Activity" msgstr "Actividad" -#: src/lib/components/field-editors/audio-input.svelte:213 -#: src/lib/components/audio/AudioDialog.svelte:153 +#: src/lib/components/field-editors/audio-input.svelte:211 +#: src/lib/components/audio/AudioDialog.svelte:143 msgid "Add audio" msgstr "Añadir audio" @@ -131,15 +131,15 @@ msgstr "Versión de la aplicación" msgid "Are you sure you want to delete {0}?" msgstr "¿Estás seguro de que quieres borrar {0}?" -#: src/lib/components/field-editors/audio-input.svelte:222 +#: src/lib/components/field-editors/audio-input.svelte:215 msgid "Audio file not included in Send & Receive" msgstr "Archivo de audio no incluido en Enviar y Recibir" -#: src/lib/components/audio/AudioDialog.svelte:82 +#: src/lib/components/audio/AudioDialog.svelte:84 msgid "Audio saved and uploaded to Lexbox" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:79 +#: src/lib/components/audio/AudioDialog.svelte:81 msgid "Audio saved locally" msgstr "" @@ -173,7 +173,7 @@ msgstr "Visite" #: src/lib/entry-editor/EntryOrSensePicker.svelte:245 #: src/lib/components/field-editors/select.svelte:166 #: src/lib/components/field-editors/multi-select.svelte:280 -#: src/lib/components/audio/AudioDialog.svelte:167 +#: src/lib/components/audio/AudioDialog.svelte:153 msgid "Cancel" msgstr "Cancelar" @@ -305,7 +305,7 @@ msgstr "Diccionario" msgid "Dictionary Preview" msgstr "Vista previa del diccionario" -#: src/lib/components/audio/audio-editor.svelte:46 +#: src/lib/components/audio/audio-editor.svelte:107 msgid "Discard" msgstr "Descartar" @@ -319,6 +319,7 @@ msgid "Don't delete" msgstr "No borrar" #: src/lib/notifications/NotificationOutlet.svelte:26 +#: src/lib/components/audio/audio-editor.svelte:106 #: src/home/Server.svelte:128 msgid "Download" msgstr "Descargar" @@ -412,24 +413,24 @@ msgstr "Etiquetas de campo" msgid "FieldWorks logo" msgstr "Logotipo de FieldWorks" -#: src/lib/components/audio/AudioDialog.svelte:89 +#: src/lib/components/audio/AudioDialog.svelte:91 msgid "File already exists" msgstr "" -#: src/lib/components/audio/audio-editor.svelte:32 +#: src/lib/components/audio/audio-editor.svelte:90 msgid "File name:" msgstr "Nombre del fichero:" -#: src/lib/components/field-editors/audio-input.svelte:79 +#: src/lib/components/field-editors/audio-input.svelte:78 msgid "File not found" msgstr "Archivo no encontrado" -#: src/lib/components/audio/AudioDialog.svelte:87 +#: src/lib/components/audio/AudioDialog.svelte:89 msgid "File saving not supported" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:164 -#: src/lib/components/audio/AudioDialog.svelte:85 +#: src/lib/components/audio/AudioDialog.svelte:150 +#: src/lib/components/audio/AudioDialog.svelte:87 msgid "File too big" msgstr "" @@ -509,7 +510,7 @@ msgstr "Importar" msgid "Last change: {0}" msgstr "Última modificación: {0}" -#: src/lib/components/audio/audio-editor.svelte:28 +#: src/lib/components/audio/audio-editor.svelte:86 msgid "Length:" msgstr "Longitud:" @@ -613,10 +614,6 @@ msgstr "Nueva entrada" msgid "No activity found" msgstr "No se ha encontrado actividad" -#: src/lib/components/field-editors/audio-input.svelte:217 -msgid "No audio" -msgstr "Sin audio" - #: src/lib/history/HistoryView.svelte:78 msgid "No change name" msgstr "Sin cambio de nombre" @@ -630,8 +627,8 @@ msgstr "No se han encontrado diccionarios" msgid "No entries found" msgstr "No se han encontrado entradas" -#: src/lib/components/audio/AudioDialog.svelte:75 -msgid "No file selected" +#: src/lib/components/audio/AudioDialog.svelte:77 +msgid "No file to upload" msgstr "" #: src/lib/history/HistoryView.svelte:68 @@ -675,7 +672,7 @@ msgstr "Nota" msgid "Offline" msgstr "Fuera de línea" -#: src/lib/components/field-editors/audio-input.svelte:82 +#: src/lib/components/field-editors/audio-input.svelte:81 msgid "Offline, unable to download" msgstr "Desconectado, no se puede descargar" @@ -742,7 +739,7 @@ msgstr "Actualizar proyectos" msgid "Remove" msgstr "Eliminar" -#: src/lib/components/field-editors/audio-input.svelte:268 +#: src/lib/components/field-editors/audio-input.svelte:261 msgid "Remove audio" msgstr "Eliminar audio" @@ -750,11 +747,11 @@ msgstr "Eliminar audio" msgid "Reopen" msgstr "Vuelva a abrir" -#: src/lib/components/field-editors/audio-input.svelte:265 +#: src/lib/components/field-editors/audio-input.svelte:258 msgid "Replace audio" msgstr "Sustituir audio" -#: src/lib/components/audio/AudioDialog.svelte:169 +#: src/lib/components/audio/AudioDialog.svelte:155 msgid "Save audio" msgstr "Guardar audio" @@ -818,7 +815,7 @@ msgstr "Mostrar {0} más..." msgid "Simple" msgstr "Simple" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "Size:" msgstr "Tamaño:" @@ -891,7 +888,7 @@ msgstr "Traducción" msgid "Troubleshoot" msgstr "Solución de problemas" -#: src/lib/components/audio/audio-editor.svelte:36 +#: src/lib/components/audio/audio-editor.svelte:94 msgid "Type:" msgstr "Tipo:" @@ -907,11 +904,11 @@ msgid "Unknown" msgstr "Desconocido" #: src/project/ProjectSidebar.svelte:115 -#: src/lib/components/audio/AudioDialog.svelte:91 +#: src/lib/components/audio/AudioDialog.svelte:93 msgid "Unknown error" msgstr "Error desconocido" -#: src/lib/components/field-editors/audio-input.svelte:85 +#: src/lib/components/field-editors/audio-input.svelte:84 msgid "Unknown error {0}" msgstr "Error desconocido {0}" diff --git a/frontend/viewer/src/locales/fr.po b/frontend/viewer/src/locales/fr.po index c22076b387..63d91f7380 100644 --- a/frontend/viewer/src/locales/fr.po +++ b/frontend/viewer/src/locales/fr.po @@ -22,9 +22,9 @@ msgstr "" msgid "– ({0} changes)" msgstr "- ({0} changes)" -#: src/lib/components/audio/audio-editor.svelte:28 -#: src/lib/components/audio/audio-editor.svelte:33 -#: src/lib/components/audio/audio-editor.svelte:37 +#: src/lib/components/audio/audio-editor.svelte:86 +#: src/lib/components/audio/audio-editor.svelte:91 +#: src/lib/components/audio/audio-editor.svelte:95 msgid "{0}" msgstr "{0}" @@ -44,7 +44,7 @@ msgstr "{0} (FieldWorks Lite)" msgid "{0} (FieldWorks)" msgstr "{0} (FieldWorks)" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "{0} MB" msgstr "{0} MB" @@ -81,8 +81,8 @@ msgstr "Compte" msgid "Activity" msgstr "Activité" -#: src/lib/components/field-editors/audio-input.svelte:213 -#: src/lib/components/audio/AudioDialog.svelte:153 +#: src/lib/components/field-editors/audio-input.svelte:211 +#: src/lib/components/audio/AudioDialog.svelte:143 msgid "Add audio" msgstr "Ajouter de l'audio" @@ -131,15 +131,15 @@ msgstr "Version de l'application" msgid "Are you sure you want to delete {0}?" msgstr "Êtes-vous sûr de vouloir supprimer {0}?" -#: src/lib/components/field-editors/audio-input.svelte:222 +#: src/lib/components/field-editors/audio-input.svelte:215 msgid "Audio file not included in Send & Receive" msgstr "Le fichier audio n'est pas inclus dans l'envoi et la réception" -#: src/lib/components/audio/AudioDialog.svelte:82 +#: src/lib/components/audio/AudioDialog.svelte:84 msgid "Audio saved and uploaded to Lexbox" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:79 +#: src/lib/components/audio/AudioDialog.svelte:81 msgid "Audio saved locally" msgstr "" @@ -173,7 +173,7 @@ msgstr "Parcourir" #: src/lib/entry-editor/EntryOrSensePicker.svelte:245 #: src/lib/components/field-editors/select.svelte:166 #: src/lib/components/field-editors/multi-select.svelte:280 -#: src/lib/components/audio/AudioDialog.svelte:167 +#: src/lib/components/audio/AudioDialog.svelte:153 msgid "Cancel" msgstr "Annuler" @@ -305,7 +305,7 @@ msgstr "Dictionnaire" msgid "Dictionary Preview" msgstr "Aperçu du dictionnaire" -#: src/lib/components/audio/audio-editor.svelte:46 +#: src/lib/components/audio/audio-editor.svelte:107 msgid "Discard" msgstr "Rejeter" @@ -319,6 +319,7 @@ msgid "Don't delete" msgstr "Ne pas supprimer" #: src/lib/notifications/NotificationOutlet.svelte:26 +#: src/lib/components/audio/audio-editor.svelte:106 #: src/home/Server.svelte:128 msgid "Download" msgstr "Télécharger" @@ -412,24 +413,24 @@ msgstr "Étiquettes de champ" msgid "FieldWorks logo" msgstr "Logo FieldWorks" -#: src/lib/components/audio/AudioDialog.svelte:89 +#: src/lib/components/audio/AudioDialog.svelte:91 msgid "File already exists" msgstr "" -#: src/lib/components/audio/audio-editor.svelte:32 +#: src/lib/components/audio/audio-editor.svelte:90 msgid "File name:" msgstr "Nom du fichier :" -#: src/lib/components/field-editors/audio-input.svelte:79 +#: src/lib/components/field-editors/audio-input.svelte:78 msgid "File not found" msgstr "Fichier non trouvé" -#: src/lib/components/audio/AudioDialog.svelte:87 +#: src/lib/components/audio/AudioDialog.svelte:89 msgid "File saving not supported" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:164 -#: src/lib/components/audio/AudioDialog.svelte:85 +#: src/lib/components/audio/AudioDialog.svelte:150 +#: src/lib/components/audio/AudioDialog.svelte:87 msgid "File too big" msgstr "" @@ -509,7 +510,7 @@ msgstr "Importation" msgid "Last change: {0}" msgstr "Dernière modification : {0}" -#: src/lib/components/audio/audio-editor.svelte:28 +#: src/lib/components/audio/audio-editor.svelte:86 msgid "Length:" msgstr "Longueur :" @@ -613,10 +614,6 @@ msgstr "Nouvelle entrée" msgid "No activity found" msgstr "Aucune activité trouvée" -#: src/lib/components/field-editors/audio-input.svelte:217 -msgid "No audio" -msgstr "Pas d'audio" - #: src/lib/history/HistoryView.svelte:78 msgid "No change name" msgstr "Pas de changement de nom" @@ -630,8 +627,8 @@ msgstr "Aucun dictionnaire trouvé" msgid "No entries found" msgstr "Aucune entrée trouvée" -#: src/lib/components/audio/AudioDialog.svelte:75 -msgid "No file selected" +#: src/lib/components/audio/AudioDialog.svelte:77 +msgid "No file to upload" msgstr "" #: src/lib/history/HistoryView.svelte:68 @@ -675,7 +672,7 @@ msgstr "Note" msgid "Offline" msgstr "Hors ligne" -#: src/lib/components/field-editors/audio-input.svelte:82 +#: src/lib/components/field-editors/audio-input.svelte:81 msgid "Offline, unable to download" msgstr "Hors ligne, impossible de télécharger" @@ -742,7 +739,7 @@ msgstr "Projets de rafraîchissement" msgid "Remove" msgstr "Retirer" -#: src/lib/components/field-editors/audio-input.svelte:268 +#: src/lib/components/field-editors/audio-input.svelte:261 msgid "Remove audio" msgstr "Supprimer l'audio" @@ -750,11 +747,11 @@ msgstr "Supprimer l'audio" msgid "Reopen" msgstr "Réouverture" -#: src/lib/components/field-editors/audio-input.svelte:265 +#: src/lib/components/field-editors/audio-input.svelte:258 msgid "Replace audio" msgstr "Remplacer l'audio" -#: src/lib/components/audio/AudioDialog.svelte:169 +#: src/lib/components/audio/AudioDialog.svelte:155 msgid "Save audio" msgstr "Sauvegarder l'audio" @@ -818,7 +815,7 @@ msgstr "Afficher {0} plus..." msgid "Simple" msgstr "Simple" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "Size:" msgstr "Taille :" @@ -891,7 +888,7 @@ msgstr "Traduction" msgid "Troubleshoot" msgstr "Dépannage" -#: src/lib/components/audio/audio-editor.svelte:36 +#: src/lib/components/audio/audio-editor.svelte:94 msgid "Type:" msgstr "Type :" @@ -907,11 +904,11 @@ msgid "Unknown" msgstr "Inconnu" #: src/project/ProjectSidebar.svelte:115 -#: src/lib/components/audio/AudioDialog.svelte:91 +#: src/lib/components/audio/AudioDialog.svelte:93 msgid "Unknown error" msgstr "Erreur inconnue" -#: src/lib/components/field-editors/audio-input.svelte:85 +#: src/lib/components/field-editors/audio-input.svelte:84 msgid "Unknown error {0}" msgstr "Erreur inconnue {0}" diff --git a/frontend/viewer/src/locales/id.po b/frontend/viewer/src/locales/id.po index 265c74eb11..11226e7853 100644 --- a/frontend/viewer/src/locales/id.po +++ b/frontend/viewer/src/locales/id.po @@ -22,9 +22,9 @@ msgstr "" msgid "– ({0} changes)" msgstr "- ({0} perubahan)" -#: src/lib/components/audio/audio-editor.svelte:28 -#: src/lib/components/audio/audio-editor.svelte:33 -#: src/lib/components/audio/audio-editor.svelte:37 +#: src/lib/components/audio/audio-editor.svelte:86 +#: src/lib/components/audio/audio-editor.svelte:91 +#: src/lib/components/audio/audio-editor.svelte:95 msgid "{0}" msgstr "{0}" @@ -44,7 +44,7 @@ msgstr "{0} (FieldWorks Lite)" msgid "{0} (FieldWorks)" msgstr "{0} (Pekerjaan Lapangan)" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "{0} MB" msgstr "{0} MB" @@ -81,8 +81,8 @@ msgstr "Akun" msgid "Activity" msgstr "Aktivitas" -#: src/lib/components/field-editors/audio-input.svelte:213 -#: src/lib/components/audio/AudioDialog.svelte:153 +#: src/lib/components/field-editors/audio-input.svelte:211 +#: src/lib/components/audio/AudioDialog.svelte:143 msgid "Add audio" msgstr "Menambahkan audio" @@ -131,15 +131,15 @@ msgstr "Versi aplikasi" msgid "Are you sure you want to delete {0}?" msgstr "Apakah Anda yakin ingin menghapus {0}?" -#: src/lib/components/field-editors/audio-input.svelte:222 +#: src/lib/components/field-editors/audio-input.svelte:215 msgid "Audio file not included in Send & Receive" msgstr "File audio tidak disertakan dalam Kirim & Terima" -#: src/lib/components/audio/AudioDialog.svelte:82 +#: src/lib/components/audio/AudioDialog.svelte:84 msgid "Audio saved and uploaded to Lexbox" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:79 +#: src/lib/components/audio/AudioDialog.svelte:81 msgid "Audio saved locally" msgstr "" @@ -173,7 +173,7 @@ msgstr "Jelajahi" #: src/lib/entry-editor/EntryOrSensePicker.svelte:245 #: src/lib/components/field-editors/select.svelte:166 #: src/lib/components/field-editors/multi-select.svelte:280 -#: src/lib/components/audio/AudioDialog.svelte:167 +#: src/lib/components/audio/AudioDialog.svelte:153 msgid "Cancel" msgstr "Batal" @@ -305,7 +305,7 @@ msgstr "Kamus" msgid "Dictionary Preview" msgstr "Pratinjau Kamus" -#: src/lib/components/audio/audio-editor.svelte:46 +#: src/lib/components/audio/audio-editor.svelte:107 msgid "Discard" msgstr "Buang" @@ -319,6 +319,7 @@ msgid "Don't delete" msgstr "Jangan hapus" #: src/lib/notifications/NotificationOutlet.svelte:26 +#: src/lib/components/audio/audio-editor.svelte:106 #: src/home/Server.svelte:128 msgid "Download" msgstr "Unduh" @@ -412,24 +413,24 @@ msgstr "Label Bidang" msgid "FieldWorks logo" msgstr "Logo FieldWorks" -#: src/lib/components/audio/AudioDialog.svelte:89 +#: src/lib/components/audio/AudioDialog.svelte:91 msgid "File already exists" msgstr "" -#: src/lib/components/audio/audio-editor.svelte:32 +#: src/lib/components/audio/audio-editor.svelte:90 msgid "File name:" msgstr "Nama file:" -#: src/lib/components/field-editors/audio-input.svelte:79 +#: src/lib/components/field-editors/audio-input.svelte:78 msgid "File not found" msgstr "File tidak ditemukan" -#: src/lib/components/audio/AudioDialog.svelte:87 +#: src/lib/components/audio/AudioDialog.svelte:89 msgid "File saving not supported" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:164 -#: src/lib/components/audio/AudioDialog.svelte:85 +#: src/lib/components/audio/AudioDialog.svelte:150 +#: src/lib/components/audio/AudioDialog.svelte:87 msgid "File too big" msgstr "" @@ -509,7 +510,7 @@ msgstr "Impor" msgid "Last change: {0}" msgstr "Perubahan terakhir: {0}" -#: src/lib/components/audio/audio-editor.svelte:28 +#: src/lib/components/audio/audio-editor.svelte:86 msgid "Length:" msgstr "Panjang:" @@ -613,10 +614,6 @@ msgstr "Entri Baru" msgid "No activity found" msgstr "Tidak ada aktivitas yang ditemukan" -#: src/lib/components/field-editors/audio-input.svelte:217 -msgid "No audio" -msgstr "Tidak ada audio" - #: src/lib/history/HistoryView.svelte:78 msgid "No change name" msgstr "Tidak ada perubahan nama" @@ -630,8 +627,8 @@ msgstr "Tidak ditemukan Kamus" msgid "No entries found" msgstr "Tidak ada entri yang ditemukan" -#: src/lib/components/audio/AudioDialog.svelte:75 -msgid "No file selected" +#: src/lib/components/audio/AudioDialog.svelte:77 +msgid "No file to upload" msgstr "" #: src/lib/history/HistoryView.svelte:68 @@ -675,7 +672,7 @@ msgstr "Catatan" msgid "Offline" msgstr "Offline" -#: src/lib/components/field-editors/audio-input.svelte:82 +#: src/lib/components/field-editors/audio-input.svelte:81 msgid "Offline, unable to download" msgstr "Offline, tidak dapat mengunduh" @@ -742,7 +739,7 @@ msgstr "Menyegarkan Proyek" msgid "Remove" msgstr "Menghapus" -#: src/lib/components/field-editors/audio-input.svelte:268 +#: src/lib/components/field-editors/audio-input.svelte:261 msgid "Remove audio" msgstr "Menghapus audio" @@ -750,11 +747,11 @@ msgstr "Menghapus audio" msgid "Reopen" msgstr "Buka kembali" -#: src/lib/components/field-editors/audio-input.svelte:265 +#: src/lib/components/field-editors/audio-input.svelte:258 msgid "Replace audio" msgstr "Mengganti audio" -#: src/lib/components/audio/AudioDialog.svelte:169 +#: src/lib/components/audio/AudioDialog.svelte:155 msgid "Save audio" msgstr "Menyimpan audio" @@ -818,7 +815,7 @@ msgstr "Tampilkan {0} selengkapnya..." msgid "Simple" msgstr "Sederhana" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "Size:" msgstr "Ukuran:" @@ -891,7 +888,7 @@ msgstr "Terjemahan" msgid "Troubleshoot" msgstr "Memecahkan masalah" -#: src/lib/components/audio/audio-editor.svelte:36 +#: src/lib/components/audio/audio-editor.svelte:94 msgid "Type:" msgstr "Ketik:" @@ -907,11 +904,11 @@ msgid "Unknown" msgstr "Tidak diketahui" #: src/project/ProjectSidebar.svelte:115 -#: src/lib/components/audio/AudioDialog.svelte:91 +#: src/lib/components/audio/AudioDialog.svelte:93 msgid "Unknown error" msgstr "Kesalahan yang tidak diketahui" -#: src/lib/components/field-editors/audio-input.svelte:85 +#: src/lib/components/field-editors/audio-input.svelte:84 msgid "Unknown error {0}" msgstr "Kesalahan tidak dikenal {0}" diff --git a/frontend/viewer/src/locales/ko.po b/frontend/viewer/src/locales/ko.po index f3549fa9c0..bc88c54170 100644 --- a/frontend/viewer/src/locales/ko.po +++ b/frontend/viewer/src/locales/ko.po @@ -22,9 +22,9 @@ msgstr "" msgid "– ({0} changes)" msgstr "- ({0} 변경)" -#: src/lib/components/audio/audio-editor.svelte:28 -#: src/lib/components/audio/audio-editor.svelte:33 -#: src/lib/components/audio/audio-editor.svelte:37 +#: src/lib/components/audio/audio-editor.svelte:86 +#: src/lib/components/audio/audio-editor.svelte:91 +#: src/lib/components/audio/audio-editor.svelte:95 msgid "{0}" msgstr "{0}" @@ -44,7 +44,7 @@ msgstr "{0} (필드웍스 라이트)" msgid "{0} (FieldWorks)" msgstr "{0} (필드웍스)" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "{0} MB" msgstr "{0} MB" @@ -81,8 +81,8 @@ msgstr "계정" msgid "Activity" msgstr "활동" -#: src/lib/components/field-editors/audio-input.svelte:213 -#: src/lib/components/audio/AudioDialog.svelte:153 +#: src/lib/components/field-editors/audio-input.svelte:211 +#: src/lib/components/audio/AudioDialog.svelte:143 msgid "Add audio" msgstr "오디오 추가" @@ -131,15 +131,15 @@ msgstr "애플리케이션 버전" msgid "Are you sure you want to delete {0}?" msgstr "{0}을 삭제하시겠습니까?" -#: src/lib/components/field-editors/audio-input.svelte:222 +#: src/lib/components/field-editors/audio-input.svelte:215 msgid "Audio file not included in Send & Receive" msgstr "보내기 및 받기에 포함되지 않은 오디오 파일" -#: src/lib/components/audio/AudioDialog.svelte:82 +#: src/lib/components/audio/AudioDialog.svelte:84 msgid "Audio saved and uploaded to Lexbox" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:79 +#: src/lib/components/audio/AudioDialog.svelte:81 msgid "Audio saved locally" msgstr "" @@ -173,7 +173,7 @@ msgstr "찾아보기" #: src/lib/entry-editor/EntryOrSensePicker.svelte:245 #: src/lib/components/field-editors/select.svelte:166 #: src/lib/components/field-editors/multi-select.svelte:280 -#: src/lib/components/audio/AudioDialog.svelte:167 +#: src/lib/components/audio/AudioDialog.svelte:153 msgid "Cancel" msgstr "취소" @@ -305,7 +305,7 @@ msgstr "사전" msgid "Dictionary Preview" msgstr "사전 미리보기" -#: src/lib/components/audio/audio-editor.svelte:46 +#: src/lib/components/audio/audio-editor.svelte:107 msgid "Discard" msgstr "폐기" @@ -319,6 +319,7 @@ msgid "Don't delete" msgstr "삭제하지 마세요." #: src/lib/notifications/NotificationOutlet.svelte:26 +#: src/lib/components/audio/audio-editor.svelte:106 #: src/home/Server.svelte:128 msgid "Download" msgstr "다운로드" @@ -412,24 +413,24 @@ msgstr "필드 레이블" msgid "FieldWorks logo" msgstr "FieldWorks 로고" -#: src/lib/components/audio/AudioDialog.svelte:89 +#: src/lib/components/audio/AudioDialog.svelte:91 msgid "File already exists" msgstr "" -#: src/lib/components/audio/audio-editor.svelte:32 +#: src/lib/components/audio/audio-editor.svelte:90 msgid "File name:" msgstr "파일 이름:" -#: src/lib/components/field-editors/audio-input.svelte:79 +#: src/lib/components/field-editors/audio-input.svelte:78 msgid "File not found" msgstr "파일을 찾을 수 없음" -#: src/lib/components/audio/AudioDialog.svelte:87 +#: src/lib/components/audio/AudioDialog.svelte:89 msgid "File saving not supported" msgstr "" -#: src/lib/components/audio/AudioDialog.svelte:164 -#: src/lib/components/audio/AudioDialog.svelte:85 +#: src/lib/components/audio/AudioDialog.svelte:150 +#: src/lib/components/audio/AudioDialog.svelte:87 msgid "File too big" msgstr "" @@ -509,7 +510,7 @@ msgstr "가져오기" msgid "Last change: {0}" msgstr "마지막 변경 사항: {0}" -#: src/lib/components/audio/audio-editor.svelte:28 +#: src/lib/components/audio/audio-editor.svelte:86 msgid "Length:" msgstr "길이:" @@ -613,10 +614,6 @@ msgstr "새 항목" msgid "No activity found" msgstr "활동을 찾을 수 없습니다." -#: src/lib/components/field-editors/audio-input.svelte:217 -msgid "No audio" -msgstr "오디오 없음" - #: src/lib/history/HistoryView.svelte:78 msgid "No change name" msgstr "이름 변경 안 함" @@ -630,8 +627,8 @@ msgstr "사전을 찾을 수 없습니다." msgid "No entries found" msgstr "항목을 찾을 수 없습니다." -#: src/lib/components/audio/AudioDialog.svelte:75 -msgid "No file selected" +#: src/lib/components/audio/AudioDialog.svelte:77 +msgid "No file to upload" msgstr "" #: src/lib/history/HistoryView.svelte:68 @@ -675,7 +672,7 @@ msgstr "참고" msgid "Offline" msgstr "오프라인" -#: src/lib/components/field-editors/audio-input.svelte:82 +#: src/lib/components/field-editors/audio-input.svelte:81 msgid "Offline, unable to download" msgstr "오프라인 상태, 다운로드할 수 없음" @@ -742,7 +739,7 @@ msgstr "프로젝트 새로 고침" msgid "Remove" msgstr "제거" -#: src/lib/components/field-editors/audio-input.svelte:268 +#: src/lib/components/field-editors/audio-input.svelte:261 msgid "Remove audio" msgstr "오디오 제거" @@ -750,11 +747,11 @@ msgstr "오디오 제거" msgid "Reopen" msgstr "다시 열기" -#: src/lib/components/field-editors/audio-input.svelte:265 +#: src/lib/components/field-editors/audio-input.svelte:258 msgid "Replace audio" msgstr "오디오 교체" -#: src/lib/components/audio/AudioDialog.svelte:169 +#: src/lib/components/audio/AudioDialog.svelte:155 msgid "Save audio" msgstr "오디오 저장" @@ -818,7 +815,7 @@ msgstr "보기 {0} 더보기..." msgid "Simple" msgstr "Simple" -#: src/lib/components/audio/audio-editor.svelte:30 +#: src/lib/components/audio/audio-editor.svelte:88 msgid "Size:" msgstr "크기:" @@ -891,7 +888,7 @@ msgstr "번역" msgid "Troubleshoot" msgstr "문제 해결" -#: src/lib/components/audio/audio-editor.svelte:36 +#: src/lib/components/audio/audio-editor.svelte:94 msgid "Type:" msgstr "유형:" @@ -907,11 +904,11 @@ msgid "Unknown" msgstr "알 수 없음" #: src/project/ProjectSidebar.svelte:115 -#: src/lib/components/audio/AudioDialog.svelte:91 +#: src/lib/components/audio/AudioDialog.svelte:93 msgid "Unknown error" msgstr "알 수 없는 오류" -#: src/lib/components/field-editors/audio-input.svelte:85 +#: src/lib/components/field-editors/audio-input.svelte:84 msgid "Unknown error {0}" msgstr "알 수 없는 오류 {0}" diff --git a/frontend/viewer/vite.config.ffmpeg-worker.ts b/frontend/viewer/vite.config.ffmpeg-worker.ts new file mode 100644 index 0000000000..530253dd46 --- /dev/null +++ b/frontend/viewer/vite.config.ffmpeg-worker.ts @@ -0,0 +1,22 @@ +import {createRequire} from 'module'; +import {defineConfig} from 'vite'; + +const require = createRequire(import.meta.url); +const ffmpegWorkerPath = require.resolve('@ffmpeg/ffmpeg/worker'); + +export default defineConfig({ + esbuild: { + banner: `/* This file generated by Vite / pnpm build-ffmpeg-worker. */\n// @ts-nocheck`, + include: [ffmpegWorkerPath], + }, + build: { + emptyOutDir: false, + lib: { + entry: ffmpegWorkerPath, + formats: ['es'], + fileName: () => 'bundled-ffmpeg-worker.js', + }, + outDir: 'src/lib/components/audio/ffmpeg', + copyPublicDir: false, + }, +});