From 9a605b9f39063297d3b81cb70eae1294fa8efd43 Mon Sep 17 00:00:00 2001 From: an-lee Date: Sun, 18 Feb 2024 16:31:52 +0800 Subject: [PATCH] Feat: Improve first setup (#319) * remove whisper model checking when setup * fix landing page step * refactor whisper * refactor whisper options * update workflow * update test-enjoy-app.yml --- .github/workflows/release-enjoy-app.yml | 7 +- .github/workflows/test-enjoy-app.yml | 8 +- .gitignore | 1 + enjoy/e2e/main.spec.ts | 18 ++--- enjoy/package.json | 2 +- enjoy/src/main/settings.ts | 9 +-- enjoy/src/main/whisper.ts | 78 +++++-------------- .../components/whisper-model-options.tsx | 56 +------------ enjoy/src/renderer/pages/landing.tsx | 18 +---- 9 files changed, 46 insertions(+), 151 deletions(-) diff --git a/.github/workflows/release-enjoy-app.yml b/.github/workflows/release-enjoy-app.yml index 7e5e279a..87720d50 100644 --- a/.github/workflows/release-enjoy-app.yml +++ b/.github/workflows/release-enjoy-app.yml @@ -6,7 +6,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, macos-13-xlarge, windows-latest, ubuntu-latest] + os: [macos-latest, windows-latest, ubuntu-latest] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -18,3 +18,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PUBLISH_TOKEN }} run: yarn publish:enjoy + - if: matrix.os == 'macos-latest' + env: + GITHUB_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + PACKAGE_OS_ARCH: arm64 + run: yarn run publish:enjoy --arch=arm64 diff --git a/.github/workflows/test-enjoy-app.yml b/.github/workflows/test-enjoy-app.yml index c8bc4f67..d3b66100 100644 --- a/.github/workflows/test-enjoy-app.yml +++ b/.github/workflows/test-enjoy-app.yml @@ -10,7 +10,7 @@ on: - "enjoy/**/*.js" - "enjoy/**/*.mjs" jobs: - test: + e2e: timeout-minutes: 60 runs-on: ${{ matrix.os }} strategy: @@ -20,7 +20,7 @@ jobs: [ macos-latest, macos-13, - macos-13-xlarge, + macos-14, windows-2019, windows-latest, ubuntu-20.04, @@ -38,11 +38,11 @@ jobs: run: | brew update brew install sdl2 - - if: matrix.os == 'ubuntu-latest' + - if: startsWith(matrix.os, 'ubuntu') name: Run tests with xvfb-run on ubuntu run: | xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:enjoy - - if: matrix.os != 'ubuntu-latest' + - if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows') name: Run tests run: yarn test:enjoy - uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index f21ffb48..75917ffc 100644 --- a/.gitignore +++ b/.gitignore @@ -115,6 +115,7 @@ package-lock.json */playwright-report/ */blob-report/ */playwright/.cache/ +*/tmp/ # whisper models ggml-*.bin diff --git a/enjoy/e2e/main.spec.ts b/enjoy/e2e/main.spec.ts index eb81cc28..2681e36d 100644 --- a/enjoy/e2e/main.spec.ts +++ b/enjoy/e2e/main.spec.ts @@ -19,6 +19,7 @@ declare global { } let electronApp: ElectronApplication; +const resultDir = path.join(process.cwd(), "test-results"); test.beforeAll(async () => { // find the latest build in the out directory @@ -28,8 +29,6 @@ test.beforeAll(async () => { // set the CI environment variable to true process.env.CI = "e2e"; - const resultDir = path.join(process.cwd(), "test-results"); - fs.ensureDirSync(resultDir); process.env.SETTINGS_PATH = resultDir; process.env.LIBRARY_PATH = resultDir; @@ -40,7 +39,7 @@ test.beforeAll(async () => { }); electronApp.on("window", async (page) => { const filename = page.url()?.split("/").pop(); - console.log(`Window opened: ${filename}`); + console.info(`Window opened: ${filename}`); // capture errors page.on("pageerror", (error) => { @@ -48,7 +47,7 @@ test.beforeAll(async () => { }); // capture console messages page.on("console", (msg) => { - console.log(msg.text()); + console.info(msg.text()); }); }); }); @@ -57,25 +56,26 @@ test.afterAll(async () => { await electronApp.close(); }); -let page: Page; - test("renders the first page", async () => { - page = await electronApp.firstWindow(); + const page = await electronApp.firstWindow(); const title = await page.title(); expect(title).toBe("Enjoy"); }); test("validate whisper command", async () => { - page = await electronApp.firstWindow(); + const page = await electronApp.firstWindow(); const res = await page.evaluate(() => { return window.__ENJOY_APP__.whisper.check(); }); console.info(res.log); expect(res.success).toBeTruthy(); + + const settings = fs.readJsonSync(path.join(resultDir, "settings.json")); + expect(settings.whisper.service).toBe("local"); }); test("valid ffmpeg command", async () => { - page = await electronApp.firstWindow(); + const page = await electronApp.firstWindow(); const res = await page.evaluate(() => { return window.__ENJOY_APP__.ffmpeg.check(); }); diff --git a/enjoy/package.json b/enjoy/package.json index 74dd2ad4..06c0def1 100644 --- a/enjoy/package.json +++ b/enjoy/package.json @@ -8,7 +8,7 @@ "types": "./src/types.d.ts", "scripts": { "predev": "yarn run download", - "dev": "rimraf .vite && yarn run download && WEB_API_URL=http://localhost:3000 electron-forge start", + "dev": "rimraf .vite && yarn run download && WEB_API_URL=http://localhost:3000 SETTINGS_PATH=./tmp LIBRARY_PATH=./tmp electron-forge start", "start": "rimraf .vite && yarn run download && electron-forge start", "package": "rimraf .vite && yarn run download && electron-forge package", "make": "rimraf .vite && yarn run download && electron-forge make", diff --git a/enjoy/src/main/settings.ts b/enjoy/src/main/settings.ts index c6de2f4b..6b4fe7ad 100644 --- a/enjoy/src/main/settings.ts +++ b/enjoy/src/main/settings.ts @@ -70,13 +70,8 @@ const whisperConfig = (): WhisperConfigType => { ) as WhisperConfigType["service"]; if (!service) { - if (model) { - settings.setSync("whisper.service", "local"); - service = "local"; - } else { - settings.setSync("whisper.service", "azure"); - service = "azure"; - } + settings.setSync("whisper.service", "local"); + service = "local"; } return { diff --git a/enjoy/src/main/whisper.ts b/enjoy/src/main/whisper.ts index c282ea49..5281b936 100644 --- a/enjoy/src/main/whisper.ts +++ b/enjoy/src/main/whisper.ts @@ -13,8 +13,7 @@ class Whipser { private bundledModelsDir: string; public config: WhisperConfigType; - constructor(config?: WhisperConfigType) { - this.config = config || settings.whisperConfig(); + constructor() { const customWhisperPath = path.join( settings.libraryPath(), "whisper", @@ -26,26 +25,10 @@ class Whipser { } else { this.binMain = path.join(__dirname, "lib", "whisper", "main"); } + this.initialize(); } - currentModel() { - if (!this.config.availableModels) return; - - let model: WhisperConfigType["availableModels"][0]; - if (this.config.model) { - model = (this.config.availableModels || []).find( - (m) => m.name === this.config.model - ); - } - if (!model) { - model = this.config.availableModels[0]; - } - - settings.setSync("whisper.model", model.name); - return model.savePath; - } - - async initialize() { + initialize() { const models = []; const bundledModels = fs.readdirSync(this.bundledModelsDir); @@ -74,44 +57,26 @@ class Whipser { settings.setSync("whisper.availableModels", models); settings.setSync("whisper.modelsPath", dir); this.config = settings.whisperConfig(); + } - const command = `"${this.binMain}" --help`; - logger.debug(`Checking whisper command: ${command}`); - return new Promise((resolve, reject) => { - exec( - command, - { - timeout: PROCESS_TIMEOUT, - }, - (error, stdout, stderr) => { - if (error) { - logger.error("error", error); - } + currentModel() { + if (!this.config.availableModels) return; - if (stderr) { - logger.debug("stderr", stderr); - } - - if (stdout) { - logger.debug("stdout", stdout); - } - - const std = (stdout || stderr).toString()?.trim(); - if (std.startsWith("usage:")) { - resolve(true); - } else { - reject( - error || new Error("Whisper check failed: unknown error").message - ); - } - } + let model: WhisperConfigType["availableModels"][0]; + if (this.config.model) { + model = (this.config.availableModels || []).find( + (m) => m.name === this.config.model ); - }); + } + if (!model) { + model = this.config.availableModels[0]; + } + + settings.setSync("whisper.model", model.name); + return model.savePath; } async check() { - await this.initialize(); - const model = this.currentModel(); logger.debug(`Checking whisper model: ${model}`); @@ -262,12 +227,7 @@ class Whipser { registerIpcHandlers() { ipcMain.handle("whisper-config", async () => { - try { - await this.initialize(); - return Object.assign({}, this.config, { ready: true }); - } catch (_err) { - return Object.assign({}, this.config, { ready: false }); - } + return this.config; }); ipcMain.handle("whisper-set-model", async (event, model) => { @@ -295,7 +255,7 @@ class Whipser { ipcMain.handle("whisper-set-service", async (event, service) => { if (service === "local") { try { - await this.initialize(); + await this.check(); settings.setSync("whisper.service", service); this.config.service = service; return this.config; diff --git a/enjoy/src/renderer/components/whisper-model-options.tsx b/enjoy/src/renderer/components/whisper-model-options.tsx index c47f2bcc..9bb7791c 100644 --- a/enjoy/src/renderer/components/whisper-model-options.tsx +++ b/enjoy/src/renderer/components/whisper-model-options.tsx @@ -8,18 +8,12 @@ import { AlertDialogAction, AlertDialogCancel, Button, - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, - CardFooter, ScrollArea, toast, Progress, } from "@renderer/components/ui"; import { t } from "i18next"; -import { InfoIcon, CheckCircle, DownloadIcon, XCircleIcon } from "lucide-react"; +import { CheckCircle, DownloadIcon, XCircleIcon } from "lucide-react"; import { WHISPER_MODELS_OPTIONS } from "@/constants"; import { useState, useContext, useEffect } from "react"; import { @@ -36,54 +30,6 @@ type ModelType = { downloadState?: DownloadStateType; }; -export const WhisperModelOptionsPanel = () => { - const { EnjoyApp } = useContext(AppSettingsProviderContext); - const { whisperConfig, refreshWhisperConfig } = useContext( - AISettingsProviderContext - ); - - useEffect(() => { - refreshWhisperConfig(); - }, []); - - return ( - <> - - - {t("whisperModel")} - - {t("chooseAIModelDependingOnYourHardware")} - - - - - - - - -
- - - {t("yourModelsWillBeDownloadedTo", { - path: whisperConfig.modelsPath, - })} - - -
-
-
- - ); -}; - export const WhisperModelOptions = () => { const [selectingModel, setSelectingModel] = useState(null); const [availableModels, setAvailableModels] = useState([]); diff --git a/enjoy/src/renderer/pages/landing.tsx b/enjoy/src/renderer/pages/landing.tsx index d5878919..aa21e77a 100644 --- a/enjoy/src/renderer/pages/landing.tsx +++ b/enjoy/src/renderer/pages/landing.tsx @@ -2,11 +2,7 @@ import { t } from "i18next"; import { useState, useContext, useEffect } from "react"; import { Button, Progress } from "@renderer/components/ui"; import { Link } from "react-router-dom"; -import { - LoginForm, - ChooseLibraryPathInput, - WhisperModelOptionsPanel, -} from "@renderer/components"; +import { LoginForm, ChooseLibraryPathInput } from "@renderer/components"; import { AppSettingsProviderContext, AISettingsProviderContext, @@ -21,7 +17,7 @@ export default () => { AppSettingsProviderContext ); const { whisperConfig } = useContext(AISettingsProviderContext); - const totalSteps = 4; + const totalSteps = 3; useEffect(() => { validateCurrentStep(); @@ -36,9 +32,6 @@ export default () => { setCurrentStepValid(!!libraryPath); break; case 3: - setCurrentStepValid(true); - break; - case 4: setCurrentStepValid(initialized); break; default: @@ -68,10 +61,6 @@ export default () => { subtitle: t("whereYourResourcesAreStored"), }, 3: { - title: t("AIModel"), - subtitle: t("chooseAIModelToDownload"), - }, - 4: { title: t("finish"), subtitle: t("youAreReadyToGo"), }, @@ -91,8 +80,7 @@ export default () => {
{currentStep == 1 && } {currentStep == 2 && } - {currentStep == 3 && } - {currentStep == 4 && ( + {currentStep == 3 && (