From 90f38e92268909836aff6aa168997e98e9aa7402 Mon Sep 17 00:00:00 2001 From: an-lee Date: Sat, 16 Mar 2024 19:42:37 +0800 Subject: [PATCH] Transcription force alignment & more (#416) * add wavesurfer-provider * brand new layout for player * refactor pitch contour * clean up * update styl * refactor * update layout * use new layout for video * refactor * may select word * may edit word timestamp * may toggle multiselect words * clean code * improve word region update * improve layout * update layout * add echogarden * fix test * use aligned transcription * fix ipa * some refactor * improve code * implement ipa & translate & lookup * recording play & share * fix * fix post audio * improve layout * may delete recording * may record * fix video player layout * fix player in conversation * render recording along with orignal audio * may custom create region in recording * fix float issue when seekTo * fix recording player * fix load more recordings * fix seekTo * clean up * refactor pitch contour * fix some warnings * upgrade deps * fix group transcription sentence * zoom to fit when segment update * add more hotkeys * update player layout * improve style * play recording overlap audio when comparing * update echogarden dep * add recorded mark on transcription * fix recording pitch contour rendering * improve recording * adjust pitch finder params --- 1000-hours/package.json | 6 +- enjoy/e2e/main.spec.ts | 10 + enjoy/e2e/renderer.spec.ts | 66 +- enjoy/forge.config.js | 2 +- enjoy/package.json | 47 +- enjoy/playwright.config.ts | 2 +- enjoy/src/constants.ts | 148 + enjoy/src/i18n/en.json | 23 +- enjoy/src/i18n/zh-CN.json | 21 +- enjoy/src/index.css | 6 + enjoy/src/main/db/models/audio.ts | 5 + enjoy/src/main/db/models/recording.ts | 27 +- enjoy/src/main/db/models/video.ts | 5 + enjoy/src/main/echogarden.ts | 68 + enjoy/src/main/ffmpeg.ts | 5 +- enjoy/src/main/window.ts | 13 +- enjoy/src/preload.ts | 8 + .../components/audios/audio-detail.tsx | 394 --- .../components/audios/audio-player.tsx | 72 + enjoy/src/renderer/components/audios/index.ts | 3 +- .../conversations/speech-player.tsx | 27 +- enjoy/src/renderer/components/index.ts | 1 - enjoy/src/renderer/components/medias/index.ts | 10 +- .../components/medias/media-caption.tsx | 635 ++-- .../medias/media-current-recording.tsx | 511 +++ .../components/medias/media-info-panel.tsx | 40 + .../components/medias/media-loading-modal.tsx | 104 + .../medias/media-player-controls.tsx | 1033 ++++-- .../components/medias/media-player.tsx | 652 +--- .../components/medias/media-recorder.tsx | 145 + .../components/medias/media-recordings.tsx | 149 + .../renderer/components/medias/media-tabs.tsx | 78 + .../components/medias/media-transcription.tsx | 252 +- .../components/messages/assistant-message.tsx | 8 +- .../src/renderer/components/pitch-contour.tsx | 79 - .../renderer/components/posts/post-audio.tsx | 43 +- .../components/posts/post-recording.tsx | 44 +- .../components/preferences/hotkeys.tsx | 95 +- .../preferences/openai-settings.tsx | 1 - .../recordings/recording-player.tsx | 26 +- enjoy/src/renderer/components/videos/index.ts | 2 +- .../components/videos/video-detail.tsx | 407 --- .../components/videos/video-player.tsx | 72 + .../components/videos/videos-component.tsx | 2 +- .../renderer/context/ai-settings-provider.tsx | 2 +- enjoy/src/renderer/context/index.ts | 2 + .../context/media-player-provider.tsx | 454 +++ .../renderer/context/wavesurfer-provider.tsx | 185 ++ enjoy/src/renderer/hooks/index.ts | 7 + enjoy/src/renderer/hooks/use-audio.tsx | 43 + enjoy/src/renderer/hooks/use-recordings.tsx | 101 + enjoy/src/renderer/hooks/use-transcribe.tsx | 42 +- .../src/renderer/hooks/use-transcriptions.tsx | 192 ++ enjoy/src/renderer/hooks/use-video.tsx | 43 + enjoy/src/renderer/lib/utils.ts | 61 +- enjoy/src/renderer/pages/audio.tsx | 13 +- enjoy/src/renderer/pages/conversation.tsx | 93 +- enjoy/src/renderer/pages/conversations.tsx | 10 +- enjoy/src/renderer/pages/video.tsx | 13 +- enjoy/src/types/audio.d.ts | 1 + enjoy/src/types/enjoy-app.d.ts | 8 + enjoy/src/types/transcription.d.ts | 2 +- enjoy/src/types/video.d.ts | 1 + enjoy/src/utils.ts | 42 +- enjoy/tailwind.config.js | 1 + enjoy/vite.main.config.ts | 20 +- yarn.lock | 2858 +++++++++++++++-- 67 files changed, 6898 insertions(+), 2643 deletions(-) create mode 100644 enjoy/src/main/echogarden.ts delete mode 100644 enjoy/src/renderer/components/audios/audio-detail.tsx create mode 100644 enjoy/src/renderer/components/audios/audio-player.tsx create mode 100644 enjoy/src/renderer/components/medias/media-current-recording.tsx create mode 100644 enjoy/src/renderer/components/medias/media-info-panel.tsx create mode 100644 enjoy/src/renderer/components/medias/media-loading-modal.tsx create mode 100644 enjoy/src/renderer/components/medias/media-recorder.tsx create mode 100644 enjoy/src/renderer/components/medias/media-recordings.tsx create mode 100644 enjoy/src/renderer/components/medias/media-tabs.tsx delete mode 100644 enjoy/src/renderer/components/pitch-contour.tsx delete mode 100644 enjoy/src/renderer/components/videos/video-detail.tsx create mode 100644 enjoy/src/renderer/components/videos/video-player.tsx create mode 100644 enjoy/src/renderer/context/media-player-provider.tsx create mode 100644 enjoy/src/renderer/context/wavesurfer-provider.tsx create mode 100644 enjoy/src/renderer/hooks/use-audio.tsx create mode 100644 enjoy/src/renderer/hooks/use-recordings.tsx create mode 100644 enjoy/src/renderer/hooks/use-transcriptions.tsx create mode 100644 enjoy/src/renderer/hooks/use-video.tsx diff --git a/1000-hours/package.json b/1000-hours/package.json index 6e6c2941..e652f549 100644 --- a/1000-hours/package.json +++ b/1000-hours/package.json @@ -7,9 +7,9 @@ "markdown-it-mathjax3": "^4.3.2", "markdown-it-sub": "^2.0.0", "markdown-it-sup": "^2.0.0", - "mermaid": "^10.8.0", - "sass": "^1.71.1", - "vitepress": "^1.0.0-rc.42", + "mermaid": "^10.9.0", + "sass": "^1.72.0", + "vitepress": "^1.0.0-rc.45", "vitepress-plugin-mermaid": "^2.0.16", "vue": "^3.4.21" }, diff --git a/enjoy/e2e/main.spec.ts b/enjoy/e2e/main.spec.ts index 9120b086..7b2d10a9 100644 --- a/enjoy/e2e/main.spec.ts +++ b/enjoy/e2e/main.spec.ts @@ -78,6 +78,16 @@ test("valid ffmpeg command", async () => { expect(res).toBeTruthy(); }); +test("validate echogarden align command", async () => { + const res = await page.evaluate(() => { + return window.__ENJOY_APP__.echogarden.check(); + }); + expect(res).toBeTruthy(); + + const settings = fs.readJsonSync(path.join(resultDir, "settings.json")); + expect(settings.whisper.service).toBe("local"); +}); + test("should setup default library path", async () => { const settings = fs.readJsonSync(path.join(resultDir, "settings.json")); expect(settings.library).not.toBeNull(); diff --git a/enjoy/e2e/renderer.spec.ts b/enjoy/e2e/renderer.spec.ts index 86d83d93..9a313848 100644 --- a/enjoy/e2e/renderer.spec.ts +++ b/enjoy/e2e/renderer.spec.ts @@ -122,9 +122,39 @@ test.describe("with login", async () => { }, }); }); + }); + /* + * steps: + * 1. create a tts conversation + * 2. submit a message to the conversation + * 3. the speech should auto create + */ + test("tts conversation", async () => { // navigate to the conversations page await page.getByTestId("sidebar-conversations").click(); + + // trigger new conversation modal + await page.getByTestId("conversation-new-button").click(); + + // create a tts conversation + await page.click("[data-testid=conversation-preset-tts]"); + await page.getByTestId("conversation-form").waitFor(); + await page.click("[data-testid=conversation-form-submit]"); + + // wait for the conversation to be created + await page.getByTestId("conversation-page").waitFor(); + + // submit a message to the conversation + await page.getByTestId("conversation-page-input").fill("How are you?"); + await page.getByTestId("conversation-page-submit").click(); + await page.locator(".ai-message").waitFor(); + const player = page + .locator(".ai-message") + .getByTestId("wavesurfer-container"); + await player.waitFor(); + + expect(await player.isVisible()).toBeTruthy(); }); /* @@ -136,6 +166,9 @@ test.describe("with login", async () => { * 5. audio waveform player should be visible and transcription should be generated */ test("gpt conversation", async () => { + // navigate to the conversations page + await page.getByTestId("sidebar-conversations").click(); + // trigger new conversation modal await page.getByTestId("conversation-new-button").click(); @@ -166,43 +199,12 @@ test.describe("with login", async () => { // add to library await page.getByTestId("message-start-shadow").click(); - await page.getByTestId("audio-detail").waitFor(); + await page.getByTestId("audio-player").waitFor(); await page.getByTestId("media-player-container").waitFor(); - await page.getByTestId("media-transcription").waitFor(); await page.getByTestId("media-transcription-result").waitFor(); expect( await page.getByTestId("media-transcription-result").isVisible() ).toBeTruthy(); }); - - /* - * steps: - * 1. create a tts conversation - * 2. submit a message to the conversation - * 3. the speech should auto create - */ - test("tts conversation", async () => { - // trigger new conversation modal - await page.getByTestId("conversation-new-button").click(); - - // create a tts conversation - await page.click("[data-testid=conversation-preset-tts]"); - await page.getByTestId("conversation-form").waitFor(); - await page.click("[data-testid=conversation-form-submit]"); - - // wait for the conversation to be created - await page.getByTestId("conversation-page").waitFor(); - - // submit a message to the conversation - await page.getByTestId("conversation-page-input").fill("How are you?"); - await page.getByTestId("conversation-page-submit").click(); - await page.locator(".ai-message").waitFor(); - const player = page - .locator(".ai-message") - .getByTestId("wavesurfer-container"); - await player.waitFor(); - - expect(await player.isVisible()).toBeTruthy(); - }); }); }); diff --git a/enjoy/forge.config.js b/enjoy/forge.config.js index 3c1f93d0..c85e0954 100644 --- a/enjoy/forge.config.js +++ b/enjoy/forge.config.js @@ -12,7 +12,7 @@ const config = { asar: { // Binary files won't work in asar, so we need to unpack them unpackDir: - "{.vite/build/lib,.vite/build/samples,node_modules/ffmpeg-static,node_modules/@andrkrn/ffprobe-static}", + "{.vite/build/lib,.vite/build/samples,node_modules/ffmpeg-static,node_modules/@andrkrn/ffprobe-static,node_modules/onnxruntime-node/bin}", }, icon: "./assets/icon", name: "Enjoy", diff --git a/enjoy/package.json b/enjoy/package.json index 5b85d64b..13579162 100644 --- a/enjoy/package.json +++ b/enjoy/package.json @@ -47,18 +47,18 @@ "@types/fluent-ffmpeg": "^2.1.24", "@types/html-to-text": "^9.0.4", "@types/intl-tel-input": "^18.1.4", - "@types/lodash": "^4.14.202", + "@types/lodash": "^4.17.0", "@types/mark.js": "^8.11.12", - "@types/node": "^20.11.24", - "@types/react": "^18.2.62", - "@types/react-dom": "^18.2.19", + "@types/node": "^20.11.27", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", "@types/validator": "^13.11.9", "@types/wavesurfer.js": "^6.0.12", - "@typescript-eslint/eslint-plugin": "^7.1.1", - "@typescript-eslint/parser": "^7.1.1", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.18", - "electron": "^29.1.0", + "electron": "^29.1.4", "electron-playwright-helpers": "^1.7.1", "eslint": "^8.57.0", "eslint-import-resolver-typescript": "^3.6.1", @@ -67,12 +67,13 @@ "octokit": "^3.1.2", "progress": "^2.0.3", "tailwind-merge": "^2.2.1", + "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "ts-node": "^10.9.2", "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vite": "^5.1.5", + "typescript": "^5.4.2", + "vite": "^5.1.6", "vite-plugin-static-copy": "^1.0.1", "zx": "^7.2.3" }, @@ -81,7 +82,7 @@ "@ffmpeg/ffmpeg": "^0.12.10", "@ffmpeg/util": "^0.12.1", "@hookform/resolvers": "^3.3.4", - "@langchain/community": "^0.0.34", + "@langchain/community": "^0.0.39", "@langchain/google-genai": "^0.0.10", "@mozilla/readability": "^0.5.0", "@radix-ui/react-accordion": "^1.1.2", @@ -112,6 +113,7 @@ "axios": "^1.6.7", "camelcase": "^8.0.0", "camelcase-keys": "^9.1.3", + "chart.js": "^4.4.2", "cheerio": "^1.0.0-rc.12", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -122,7 +124,8 @@ "dayjs": "^1.11.10", "decamelize": "^6.0.0", "decamelize-keys": "^2.0.1", - "electron-log": "^5.1.1", + "echogarden": "https://github.com/an-lee/echogarden", + "electron-log": "^5.1.2", "electron-settings": "^4.0.2", "electron-squirrel-startup": "^1.0.0", "ffmpeg-static": "^5.2.0", @@ -130,27 +133,27 @@ "fs-extra": "^11.2.0", "html-to-text": "^9.0.5", "https-proxy-agent": "^7.0.4", - "i18next": "^23.10.0", - "intl-tel-input": "^19.5.5", + "i18next": "^23.10.1", + "intl-tel-input": "^19.5.7", "js-md5": "^0.8.3", - "langchain": "^0.1.25", + "langchain": "^0.1.28", "lodash": "^4.17.21", - "lucide-react": "^0.344.0", + "lucide-react": "^0.358.0", "mark.js": "^8.11.1", - "microsoft-cognitiveservices-speech-sdk": "^1.35.0", - "next-themes": "^0.2.1", - "openai": "^4.28.4", + "microsoft-cognitiveservices-speech-sdk": "^1.36.0", + "next-themes": "^0.3.0", + "openai": "^4.29.0", "pitchfinder": "^2.3.2", "postcss": "^8.4.35", "proxy-agent": "^6.4.0", "react": "^18.2.0", - "react-activity-calendar": "^2.2.7", + "react-activity-calendar": "^2.2.8", "react-dom": "^18.2.0", "react-hook-form": "^7.51.0", "react-hotkeys-hook": "^4.5.0", - "react-i18next": "^14.0.5", + "react-i18next": "^14.1.0", "react-markdown": "^9.0.1", - "react-router-dom": "^6.22.2", + "react-router-dom": "^6.22.3", "react-tooltip": "^5.26.3", "reflect-metadata": "^0.2.1", "rimraf": "^5.0.5", @@ -160,7 +163,7 @@ "sqlite3": "^5.1.7", "tailwind-scrollbar-hide": "^1.1.7", "umzug": "^3.7.0", - "wavesurfer.js": "^7.7.3", + "wavesurfer.js": "^7.7.5", "zod": "^3.22.4" } } diff --git a/enjoy/playwright.config.ts b/enjoy/playwright.config.ts index 765b7864..64f8addc 100644 --- a/enjoy/playwright.config.ts +++ b/enjoy/playwright.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, + workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ diff --git a/enjoy/src/constants.ts b/enjoy/src/constants.ts index 1f706c32..bc35eabf 100644 --- a/enjoy/src/constants.ts +++ b/enjoy/src/constants.ts @@ -7,6 +7,24 @@ export const WEB_API_URL = "https://enjoy-web.fly.dev"; export const REPO_URL = "https://github.com/xiaolai/everyone-can-use-english"; +export const MAGIC_TOKEN_REGEX = + /\b(Mrs|Ms|Mr|Dr|Prof|St|[a-zA-Z]{1,2}|\d{1,2})\.\b/g; +export const END_OF_SENTENCE_REGEX = /[^\.!,\?][\.!\?]/g; + +export const FFMPEG_TRIM_SILENCE_OPTIONS = [ + "-af", + "silenceremove=1:start_duration=1:start_threshold=-50dB:detection=peak,aformat=dblp,areverse,silenceremove=start_periods=1:start_duration=1:start_threshold=-50dB:detection=peak,aformat=dblp,areverse", +]; + +export const FFMPEG_CONVERT_WAV_OPTIONS = [ + "-ar", + "16000", + "-ac", + "1", + "-c:a", + "pcm_s16le", +]; + // https://huggingface.co/ggerganov/whisper.cpp/tree/main export const WHISPER_MODELS_OPTIONS = [ { @@ -344,3 +362,133 @@ export const CONVERSATION_PRESETS = [ }, }, ]; + +export const IPA_MAPPING = { + p: "p", + b: "b", + t: "t", + d: "d", + ʈ: "t", + ɖ: "d", + c: "k", + ɟ: "g", + k: "k", + g: "g", + q: "k", + ɢ: "g", + ʔ: "", + ɡ: "g", + m: "m", + ɱ: "m", + n: "n", + ɳ: "n", + ɲ: "j", + ŋ: "ŋ", + ɴ: "ŋ", + n̩: "n", + ʙ: "r", + r: "r", + ʀ: "r", + ⱱ: "", + ɾ: "r", + ɽ: "r", + ɸ: "f", + β: "v", + f: "f", + v: "v", + θ: "θ", + ð: "ð", + s: "s", + z: "z", + ʃ: "ʃ", + ʒ: "ʒ", + ʂ: "s", + ʐ: "z", + ç: "", + ʝ: "j", + x: "h", + ɣ: "g", + χ: "h", + ʁ: "r", + ħ: "h", + ʕ: "", + h: "h", + ɦ: "h", + ɬ: "", + ɮ: "", + tʃ: "tʃ", + ʈʃ: "tʃ", + dʒ: "dʒ", + ʋ: "v", + ɹ: "r", + ɻ: "r", + j: "j", + ɰ: "w", + w: "w", + l: "l", + ɭ: "l", + ʎ: "j", + ʟ: "l", + i: "iː", + yɨ: "iː", + ʉɯ: "uː", + u: "uː", + iː: "iː", + ɪ: "ɪ", + ʏ: "ɪ", + ʊ: "ʊ", + ɨ: "ɪ", + ᵻ: "ɪ", + e: "e", + ø: "e", + ɘ: "ə", + ɵ: "ə", + ɤ: "ɒ", + o: "ɔː", + ə: "ə", + oː: "ɔː", + ɛ: "æ", + œ: "æ", + ɜ: "əː", + ɞ: "əː", + ʌ: "ʌ", + ɔ: "ɔː", + ɜː: "əː", + uː: "uː", + ɔː: "ɔː", + ɛː: "æ", + æ: "æ", + a: "ɑː", + ɶ: "ɑː", + ɐ: "ɑː", + ɑ: "ɑː", + ɒ: "ɒ", + ɑː: "ɑː", + "◌˞": "", + ɚ: "ɪə", + ɝ: "ɪə", + ɹ̩: "r", + eɪ: "eɪ", + əʊ: "əʊ", + oʊ: "əʊ", + aɪ: "aɪ", + ɔɪ: "ɔɪ", + aʊ: "aʊ", + iə: "ɪə", + ɜr: "ɪə(r)", + ɑr: "ɑː(r)", + ɔr: "ɔː(r)", + oʊr: "əʊ(r)", + oːɹ: "ɔː(r)", + ir: "iː(r)", + ɪɹ: "ɪ(r)", + ɔːɹ: "ɔː(r)", + ɑːɹ: "ɑː(r)", + ʊɹ: "ʊ(r)", + ʊr: "ʊ(r)", + ɛr: "æ(r)", + ɛɹ: "æ(r)", + əl: "ə", + aɪɚ: "aɪ", + aɪə: "aɪ", +}; diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json index 4421eb5a..30063255 100644 --- a/enjoy/src/i18n/en.json +++ b/enjoy/src/i18n/en.json @@ -151,6 +151,7 @@ "yesterday": "yesterday", "play": "play", "pause": "pause", + "switchPlayMode": "switch play mode", "playSingleSegment": "play single segment", "playAllSegments": "play all segments", "playInLoop": "play in loop", @@ -241,9 +242,13 @@ "logoutAndRemoveAllPersonalData": "Logout and remove all personal data", "logoutAndRemoveAllPersonalSettings": "Logout and remove all personal settings", "hotkeys": "Hotkeys", + "system": "System", + "player": "Player", "quitApp": "Quit APP", "openPreferences": "Open preferences", "playOrPause": "Play or pause", + "playOrPauseRecording": "Play or pause recording", + "startOrStopRecording": "start or stop recording", "about": "About", "currentVersion": "Current version", "checkUpdate": "Check update", @@ -268,8 +273,7 @@ "editResource": "edit resource", "deleteResource": "delete resource", "deleteResourceConfirmation": "Are you sure to delete {{name}}?", - "transcribeAudioConfirmation": "It will remove the old transcription. Are you sure to transcribe {{name}}", - "transcribeVideoConfirmation": "It will remove the old transcription. Are you sure to transcribe {{name}}", + "transcribeMediaConfirmation": "It will remove the old transcription. Are you sure to transcribe {{name}}", "localFile": "local file", "resourcesYouAddedRecently": "resources you added recently", "recentlyAdded": "recently added", @@ -291,6 +295,7 @@ "deleteRecording": "delete recording", "deleteRecordingConfirmation": "Are you sure to delete this recording?", "myRecordings": "my recordings", + "noRecordingForThisSegmentYet": "No recordings for this segment yet. Press R to start recording.", "lastYear": "last year", "less": "less", "more": "more", @@ -474,7 +479,19 @@ "itMayTakeAWhileToPrepareForTheFirstLoad": "It may take a while to prepare for the first load. Please be patient.", "loadingTranscription": "Loading transcription", "cannotFindMicrophone": "Cannot find microphone", + "savingRecording": "Saving recording", + "recordingSaved": "Recording saved", "failedToSaveRecording": "Failed to save recording", "speechNotCreatedYet": "Speech not created yet", - "goToConversation": "Go to conversation" + "goToConversation": "Go to conversation", + "mediaInfo": "Media Info", + "editRegion": "edit region", + "dragRegionBorderToEdit": "Drag region border to edit", + "startRecording": "start recording", + "stopRecording": "stop recording", + "playRecording": "play recording", + "clickAnyWordToSelect": "Click any words to select. Press shift to select multiple words.", + "currentRegionIsBeingEdited": "Current region is being edited", + "compare": "compare", + "selectRegion": "select region" } diff --git a/enjoy/src/i18n/zh-CN.json b/enjoy/src/i18n/zh-CN.json index fbd07e27..d737ceb7 100644 --- a/enjoy/src/i18n/zh-CN.json +++ b/enjoy/src/i18n/zh-CN.json @@ -151,6 +151,7 @@ "yesterday": "昨天", "play": "播放", "pause": "暂停", + "switchPlayMode": "切换播放模式", "playSingleSegment": "播放单句", "playAllSegments": "播放所有", "playInLoop": "单句循环", @@ -241,9 +242,13 @@ "logoutAndRemoveAllPersonalData": "退出登录并删除所有个人数据", "logoutAndRemoveAllPersonalSettings": "退出登录并删除所有个人设置选项", "hotkeys": "快捷键", + "system": "系统", + "player": "播放器", "quitApp": "退出应用", "openPreferences": "打开设置", "playOrPause": "播放/暂停", + "playOrPauseRecording": "播放/暂停录音", + "startOrStopRecording": "开始/结束录音", "about": "关于", "currentVersion": "当前版本", "checkUpdate": "检查更新", @@ -269,7 +274,6 @@ "deleteResource": "删除资源", "deleteResourceConfirmation": "您确定要删除资源 {{name}} 吗?", "transcribeAudioConfirmation": "这将删除原来的语音文本,您确定要重新对 {{name}} 进行语音转文本吗?", - "transcribeVideoConfirmation": "这将删除原来的语音文本,您确定要重新对 {{name}} 进行语音转文本吗?", "localFile": "本地文件", "recentlyAdded": "最近添加", "resourcesYouAddedRecently": "最近添加的资源", @@ -291,6 +295,7 @@ "deleteRecording": "删除录音", "deleteRecordingConfirmation": "您确定要删除录音吗?", "myRecordings": "我的练习", + "noRecordingForThisSegmentYet": "当前句子还没有练习过。按 R 键开始录音。", "lastYear": "过去一年", "less": "更少", "more": "更多", @@ -473,7 +478,19 @@ "itMayTakeAWhileToPrepareForTheFirstLoad": "首次加载可能需要一些时间,请耐心等候", "loadingTranscription": "正在加载语音文本", "cannotFindMicrophone": "无法找到麦克风", + "savingRecording": "正在保存录音", + "recordingSaved": "录音已保存", "failedToSaveRecording": "保存录音失败", "speechNotCreatedYet": "尚未生成语音", - "goToConversation": "前往对话" + "goToConversation": "前往对话", + "mediaInfo": "资源信息", + "editRegion": "修改当前区域", + "dragRegionBorderToEdit": "拖动区域边界以修改", + "startRecording": "开始录音", + "stopRecording": "结束录音", + "playRecording": "播放录音", + "clickAnyWordToSelect": "点击任意单词可以选中,同时按下 Shift 键可以多选", + "currentRegionIsBeingEdited": "当前区域正在编辑中", + "compare": "对比", + "selectRegion": "选取区域" } diff --git a/enjoy/src/index.css b/enjoy/src/index.css index 622e5d1b..fe6a9eba 100644 --- a/enjoy/src/index.css +++ b/enjoy/src/index.css @@ -81,6 +81,12 @@ } } +@layer components { + .scroll { + @appply scrollbar-thin scrollbar-thumb-primary scrollbar-track-secondary; + } +} + body { user-select: none; } diff --git a/enjoy/src/main/db/models/audio.ts b/enjoy/src/main/db/models/audio.ts index db9cfcb8..be7b5a29 100644 --- a/enjoy/src/main/db/models/audio.ts +++ b/enjoy/src/main/db/models/audio.ts @@ -129,6 +129,11 @@ export class Audio extends Model