Fix align failed in win32 (#1205)
* upgrade deps * upgrade yarn * fix bugs caused by config * prepare bump * downgrade echogarden for test * upgrade deps * db backup may failed * add more tests
This commit is contained in:
2
.github/workflows/test-enjoy-app.yml
vendored
2
.github/workflows/test-enjoy-app.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn enjoy:test:main
|
||||
|
||||
- name: Run main tests on Windows
|
||||
# continue-on-error: true
|
||||
continue-on-error: true
|
||||
if: contains(matrix.os, 'windows')
|
||||
run: yarn enjoy:test:main
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,4 +4,4 @@ nmHoistingLimits: workspaces
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.5.1.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.5.2.cjs
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/seo": "^2.0.0-rc.23",
|
||||
"nuxt": "^3.14.159",
|
||||
"nuxt-og-image": "^3.0.8",
|
||||
"@nuxtjs/seo": "^2.0.2",
|
||||
"nuxt": "^3.14.1592",
|
||||
"nuxt-og-image": "^4.0.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.4.5"
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.20",
|
||||
|
||||
@@ -90,6 +90,14 @@ test("validate echogarden recognition by whisper.cpp", async () => {
|
||||
expect(res.success).toBeTruthy();
|
||||
});
|
||||
|
||||
test("validate echogarden alignment", async () => {
|
||||
const res = await page.evaluate(() => {
|
||||
return window.__ENJOY_APP__.echogarden.checkAlign();
|
||||
});
|
||||
console.info(res.log);
|
||||
expect(res.success).toBeTruthy();
|
||||
});
|
||||
|
||||
test("valid ffmpeg command", async () => {
|
||||
const res = await page.evaluate(() => {
|
||||
return window.__ENJOY_APP__.ffmpeg.check();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"name": "enjoy",
|
||||
"productName": "Enjoy",
|
||||
"version": "0.7.3",
|
||||
"version": "0.7.4-preview",
|
||||
"description": "Enjoy desktop app",
|
||||
"main": ".vite/build/main.js",
|
||||
"types": "./src/types.d.ts",
|
||||
@@ -53,7 +53,7 @@
|
||||
"@types/mark.js": "^8.11.12",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/mustache": "^4.2.5",
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/node": "^22.10.0",
|
||||
"@types/prop-types": "^15.7.13",
|
||||
"@types/rails__actioncable": "^6.1.11",
|
||||
"@types/react": "^18.3.12",
|
||||
@@ -62,8 +62,8 @@
|
||||
"@types/unzipper": "^0.10.10",
|
||||
"@types/validator": "^13.12.2",
|
||||
"@types/wavesurfer.js": "^6.0.12",
|
||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
||||
"@typescript-eslint/parser": "^8.15.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
||||
"@typescript-eslint/parser": "^8.16.0",
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"electron": "^33.2.0",
|
||||
@@ -76,14 +76,14 @@
|
||||
"octokit": "^4.0.2",
|
||||
"progress": "^2.0.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwind-scrollbar": "^3.1.0",
|
||||
"tailwindcss": "^3.4.15",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.4.11",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.0",
|
||||
"vite-plugin-static-copy": "^2.1.0",
|
||||
"zx": "^8.2.2"
|
||||
},
|
||||
@@ -92,8 +92,8 @@
|
||||
"@divisey/js-mdict": "^5.0.0",
|
||||
"@electron-forge/publisher-s3": "^7.5.0",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@langchain/community": "^0.3.15",
|
||||
"@langchain/core": "^0.3.18",
|
||||
"@langchain/community": "^0.3.16",
|
||||
"@langchain/core": "^0.3.19",
|
||||
"@langchain/ollama": "^0.1.2",
|
||||
"@mozilla/readability": "^0.5.0",
|
||||
"@radix-ui/react-accordion": "^1.2.1",
|
||||
@@ -126,12 +126,12 @@
|
||||
"@vidstack/react": "^1.12.12",
|
||||
"ahoy.js": "^0.4.4",
|
||||
"autosize": "^6.0.1",
|
||||
"axios": "^1.7.7",
|
||||
"axios": "^1.7.8",
|
||||
"camelcase": "^8.0.0",
|
||||
"camelcase-keys": "^9.1.3",
|
||||
"chart.js": "^4.4.6",
|
||||
"cheerio": "^1.0.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.4",
|
||||
"command-exists": "^1.2.9",
|
||||
@@ -141,9 +141,9 @@
|
||||
"dayjs": "^1.11.13",
|
||||
"decamelize": "^6.0.0",
|
||||
"decamelize-keys": "^2.0.1",
|
||||
"echogarden": "^2.0.7",
|
||||
"echogarden": "<2.0",
|
||||
"electron-context-menu": "^4.0.4",
|
||||
"electron-log": "^5.2.2",
|
||||
"electron-log": "^5.2.3",
|
||||
"electron-settings": "^4.0.4",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
@@ -153,21 +153,21 @@
|
||||
"fs-extra": "^11.2.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"https-proxy-agent": "^7.0.5",
|
||||
"i18next": "^23.16.5",
|
||||
"i18next": "^24.0.2",
|
||||
"input-otp": "^1.4.1",
|
||||
"intl-tel-input": "^24.7.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"langchain": "^0.3.6",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^11.0.2",
|
||||
"lucide-react": "^0.460.0",
|
||||
"lucide-react": "^0.461.0",
|
||||
"mark.js": "^8.11.1",
|
||||
"media-captions": "^0.0.18",
|
||||
"microsoft-cognitiveservices-speech-sdk": "^1.41.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"mustache": "^4.2.0",
|
||||
"next-themes": "^0.4.3",
|
||||
"openai": "^4.72.0",
|
||||
"openai": "^4.73.1",
|
||||
"pitchfinder": "^2.3.2",
|
||||
"postcss": "^8.4.49",
|
||||
"proxy-agent": "^6.4.0",
|
||||
@@ -179,10 +179,10 @@
|
||||
"react-frame-component": "^5.2.7",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hotkeys-hook": "^4.6.1",
|
||||
"react-i18next": "^15.1.1",
|
||||
"react-i18next": "^15.1.2",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"react-router-dom": "^7.0.1",
|
||||
"react-shadow-root": "^6.2.0",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
@@ -198,7 +198,7 @@
|
||||
"umzug": "^3.8.2",
|
||||
"unzipper": "^0.12.3",
|
||||
"update-electron-app": "^3.0.0",
|
||||
"wavesurfer.js": "^7.8.8",
|
||||
"wavesurfer.js": "^7.8.9",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
}
|
||||
|
||||
@@ -175,7 +175,10 @@ db.connect = async () => {
|
||||
if (pendingMigrations.length > 0) {
|
||||
try {
|
||||
await db.backup({ force: true });
|
||||
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
try {
|
||||
// migrate up to the latest state
|
||||
await umzug.up();
|
||||
} catch (err) {
|
||||
|
||||
@@ -57,6 +57,9 @@ class EchogardenWrapper {
|
||||
|
||||
constructor() {
|
||||
this.recognize = (sampleFile: string, options: RecognitionOptions) => {
|
||||
if (!options) {
|
||||
throw new Error("No config options provided");
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const handler = (reason: any) => {
|
||||
// Remove the handler after it's triggered
|
||||
@@ -89,6 +92,9 @@ class EchogardenWrapper {
|
||||
});
|
||||
};
|
||||
this.align = (input, transcript, options) => {
|
||||
if (!options) {
|
||||
throw new Error("No config options provided");
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const handler = (reason: any) => {
|
||||
// Remove the handler after it's triggered
|
||||
@@ -109,6 +115,9 @@ class EchogardenWrapper {
|
||||
});
|
||||
};
|
||||
this.alignSegments = (input, timeline, options) => {
|
||||
if (!options) {
|
||||
throw new Error("No config options provided");
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const handler = (reason: any) => {
|
||||
// Remove the handler after it's triggered
|
||||
@@ -139,8 +148,8 @@ class EchogardenWrapper {
|
||||
wordTimelineToSegmentSentenceTimeline;
|
||||
}
|
||||
|
||||
async check(
|
||||
options: RecognitionOptions = {
|
||||
async check(options: RecognitionOptions) {
|
||||
options = options || {
|
||||
engine: "whisper",
|
||||
whisper: {
|
||||
model: "tiny.en",
|
||||
@@ -148,8 +157,7 @@ class EchogardenWrapper {
|
||||
whisperCpp: {
|
||||
model: "tiny.en",
|
||||
},
|
||||
}
|
||||
) {
|
||||
};
|
||||
const sampleFile = path.join(__dirname, "samples", "jfk.wav");
|
||||
|
||||
try {
|
||||
@@ -174,12 +182,33 @@ class EchogardenWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
async checkAlign(options: AlignmentOptions) {
|
||||
options = options || {
|
||||
language: "en",
|
||||
};
|
||||
const sampleFile = path.join(__dirname, "samples", "jfk.wav");
|
||||
const transcript =
|
||||
"And so my fellow Americans ask not what your country can do for you ask what you can do for your country.";
|
||||
try {
|
||||
const timeline = await this.align(sampleFile, transcript, options);
|
||||
logger.info("timeline:", !!timeline);
|
||||
return { success: true, log: "" };
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return { success: false, log: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transcodes the audio file at the enjoy:// protocol URL into a WAV format.
|
||||
* @param url - The URL of the audio file to transcode.
|
||||
* @returns A promise that resolves to the enjoy:// protocal URL of the transcoded WAV file.
|
||||
*/
|
||||
async transcode(url: string, sampleRate = 16000): Promise<string> {
|
||||
async transcode(
|
||||
url: string,
|
||||
sampleRate: number | null = 16000
|
||||
): Promise<string> {
|
||||
sampleRate = sampleRate || 16000;
|
||||
logger.info("echogarden-transcode:", url, sampleRate);
|
||||
const filePath = enjoyUrlToPath(url);
|
||||
const rawAudio = await this.ensureRawAudio(filePath, sampleRate);
|
||||
@@ -295,6 +324,11 @@ class EchogardenWrapper {
|
||||
return this.check(options);
|
||||
});
|
||||
|
||||
ipcMain.handle("echogarden-check-align", async (_event, options: any) => {
|
||||
logger.info("echogarden-check-align:", options);
|
||||
return this.checkAlign(options);
|
||||
});
|
||||
|
||||
ipcMain.handle("echogarden-get-packages-dir", async (_event) => {
|
||||
return ensureAndGetPackagesDir();
|
||||
});
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
import { contextBridge, ipcRenderer, IpcRendererEvent } from "electron";
|
||||
import { version } from "../package.json";
|
||||
import { Timeline } from "echogarden/dist/utilities/Timeline";
|
||||
import { RecognitionOptions } from "echogarden/dist/api/API";
|
||||
import {
|
||||
type AlignmentOptions,
|
||||
type RecognitionOptions,
|
||||
} from "echogarden/dist/api/API";
|
||||
|
||||
contextBridge.exposeInMainWorld("__ENJOY_APP__", {
|
||||
app: {
|
||||
@@ -546,6 +549,9 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
|
||||
check: (options: RecognitionOptions) => {
|
||||
return ipcRenderer.invoke("echogarden-check", options);
|
||||
},
|
||||
checkAlign: (options: AlignmentOptions) => {
|
||||
return ipcRenderer.invoke("echogarden-check-align", options);
|
||||
},
|
||||
},
|
||||
ffmpeg: {
|
||||
check: () => {
|
||||
|
||||
@@ -6,6 +6,9 @@ import {
|
||||
import { SttEngineOptionEnum, UserSettingKeyEnum } from "@/types/enums";
|
||||
import { GPT_PROVIDERS, TTS_PROVIDERS } from "@renderer/components";
|
||||
import { WHISPER_MODELS } from "@/constants";
|
||||
import log from "electron-log/renderer";
|
||||
|
||||
const logger = log.scope("ai-settings-provider.tsx");
|
||||
|
||||
type AISettingsProviderState = {
|
||||
sttEngine?: SttEngineOptionEnum;
|
||||
@@ -118,9 +121,8 @@ export const AISettingsProvider = ({
|
||||
|
||||
if (!config) {
|
||||
let model = "tiny";
|
||||
const whisperModel = await EnjoyApp.userSettings.get(
|
||||
UserSettingKeyEnum.WHISPER
|
||||
);
|
||||
const whisperModel =
|
||||
(await EnjoyApp.userSettings.get(UserSettingKeyEnum.WHISPER)) || "";
|
||||
if (WHISPER_MODELS.includes(whisperModel)) {
|
||||
model = whisperModel;
|
||||
} else {
|
||||
|
||||
@@ -118,6 +118,7 @@ export const useTranscribe = () => {
|
||||
new Uint8Array(await blob.arrayBuffer()),
|
||||
segmentTimeline,
|
||||
{
|
||||
engine: "dtw",
|
||||
language: language.split("-")[0],
|
||||
isolate,
|
||||
}
|
||||
@@ -141,6 +142,7 @@ export const useTranscribe = () => {
|
||||
new Uint8Array(await blob.arrayBuffer()),
|
||||
transcript,
|
||||
{
|
||||
engine: "dtw",
|
||||
language: language.split("-")[0],
|
||||
isolate,
|
||||
}
|
||||
@@ -263,6 +265,7 @@ export const useTranscribe = () => {
|
||||
throw new Error(t("whisperTranscribeFailed", { error: err.message }));
|
||||
}
|
||||
|
||||
setOutput("Whisper transcribe done");
|
||||
const { transcript, timeline } = res;
|
||||
|
||||
return {
|
||||
@@ -306,7 +309,7 @@ export const useTranscribe = () => {
|
||||
timestamp_granularities: ["word", "segment"],
|
||||
})) as any;
|
||||
|
||||
setOutput("Aligning the transcript...");
|
||||
setOutput("OpenAI transcribe done");
|
||||
const segmentTimeline = (res.segments || []).map((segment) => {
|
||||
return {
|
||||
type: "segment" as TimelineEntryType,
|
||||
@@ -352,6 +355,7 @@ export const useTranscribe = () => {
|
||||
)
|
||||
).data;
|
||||
|
||||
setOutput("Cloudflare transcribe done");
|
||||
const segmentTimeline: TimelineEntry[] = [];
|
||||
if (res.vtt) {
|
||||
const caption = await parseText(res.vtt, { type: "vtt" });
|
||||
@@ -426,17 +430,17 @@ export const useTranscribe = () => {
|
||||
|
||||
reco.canceled = (_s, e) => {
|
||||
if (e.reason === sdk.CancellationReason.Error) {
|
||||
logger.error("CANCELED: Reason=" + e.reason);
|
||||
logger.error("Azure transcribe canceled: Reason=" + e.reason);
|
||||
return reject(new Error(e.errorDetails));
|
||||
}
|
||||
|
||||
reco.stopContinuousRecognitionAsync();
|
||||
logger.info("CANCELED: Reason=" + e.reason);
|
||||
logger.info("Azure transcribe canceled: Reason=" + e.reason);
|
||||
};
|
||||
|
||||
reco.sessionStopped = async (_s, e) => {
|
||||
logger.info(
|
||||
"Session stopped. Stop continuous recognition.",
|
||||
"Azure transcribe session stopped. Stop continuous recognition.",
|
||||
e.sessionId
|
||||
);
|
||||
reco.stopContinuousRecognitionAsync();
|
||||
|
||||
4
enjoy/src/types/enjoy-app.d.ts
vendored
4
enjoy/src/types/enjoy-app.d.ts
vendored
@@ -311,6 +311,10 @@ type EnjoyAppType = {
|
||||
) => Promise<Timeline>;
|
||||
transcode: (input: string) => Promise<string>;
|
||||
check: (options?: any) => Promise<{ success: boolean; log: string }>;
|
||||
checkAlign: (options?: any) => Promise<{
|
||||
success: boolean;
|
||||
log: string;
|
||||
}>;
|
||||
};
|
||||
ffmpeg: {
|
||||
check: () => Promise<boolean>;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"docs:preview": "yarn workspace 1000-hours preview",
|
||||
"portal:generate": "yarn workspace 1000h-portal generate"
|
||||
},
|
||||
"packageManager": "yarn@4.5.1",
|
||||
"packageManager": "yarn@4.5.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user