Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Improve first setup #319

Merged
merged 6 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/release-enjoy-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
8 changes: 4 additions & 4 deletions .github/workflows/test-enjoy-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
- "enjoy/**/*.js"
- "enjoy/**/*.mjs"
jobs:
test:
e2e:
timeout-minutes: 60
runs-on: ${{ matrix.os }}
strategy:
Expand All @@ -20,7 +20,7 @@ jobs:
[
macos-latest,
macos-13,
macos-13-xlarge,
macos-14,
windows-2019,
windows-latest,
ubuntu-20.04,
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ package-lock.json
*/playwright-report/
*/blob-report/
*/playwright/.cache/
*/tmp/

# whisper models
ggml-*.bin
Expand Down
18 changes: 9 additions & 9 deletions enjoy/e2e/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -40,15 +39,15 @@ 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) => {
console.error(error);
});
// capture console messages
page.on("console", (msg) => {
console.log(msg.text());
console.info(msg.text());
});
});
});
Expand All @@ -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();
});
Expand Down
2 changes: 1 addition & 1 deletion enjoy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 2 additions & 7 deletions enjoy/src/main/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
78 changes: 19 additions & 59 deletions enjoy/src/main/whisper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}

if (stderr) {
logger.debug("stderr", stderr);
}

if (stdout) {
logger.debug("stdout", stdout);
}
currentModel() {
if (!this.config.availableModels) return;

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}`);

Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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;
Expand Down
56 changes: 1 addition & 55 deletions enjoy/src/renderer/components/whisper-model-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -36,54 +30,6 @@ type ModelType = {
downloadState?: DownloadStateType;
};

export const WhisperModelOptionsPanel = () => {
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const { whisperConfig, refreshWhisperConfig } = useContext(
AISettingsProviderContext
);

useEffect(() => {
refreshWhisperConfig();
}, []);

return (
<>
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle>{t("whisperModel")}</CardTitle>
<CardDescription>
{t("chooseAIModelDependingOnYourHardware")}
</CardDescription>
</CardHeader>

<CardContent>
<WhisperModelOptions />
</CardContent>

<CardFooter>
<div className="text-xs flex items-start space-x-2">
<InfoIcon className="mr-1.5 w-4 h-4" />
<span className="flex-1 opacity-70">
{t("yourModelsWillBeDownloadedTo", {
path: whisperConfig.modelsPath,
})}
</span>
<Button
onClick={() => {
EnjoyApp.shell.openPath(whisperConfig.modelsPath);
}}
variant="outline"
size="sm"
>
{t("open")}
</Button>
</div>
</CardFooter>
</Card>
</>
);
};

export const WhisperModelOptions = () => {
const [selectingModel, setSelectingModel] = useState<ModelType | null>(null);
const [availableModels, setAvailableModels] = useState<ModelType[]>([]);
Expand Down
18 changes: 3 additions & 15 deletions enjoy/src/renderer/pages/landing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -21,7 +17,7 @@ export default () => {
AppSettingsProviderContext
);
const { whisperConfig } = useContext(AISettingsProviderContext);
const totalSteps = 4;
const totalSteps = 3;

useEffect(() => {
validateCurrentStep();
Expand All @@ -36,9 +32,6 @@ export default () => {
setCurrentStepValid(!!libraryPath);
break;
case 3:
setCurrentStepValid(true);
break;
case 4:
setCurrentStepValid(initialized);
break;
default:
Expand Down Expand Up @@ -68,10 +61,6 @@ export default () => {
subtitle: t("whereYourResourcesAreStored"),
},
3: {
title: t("AIModel"),
subtitle: t("chooseAIModelToDownload"),
},
4: {
title: t("finish"),
subtitle: t("youAreReadyToGo"),
},
Expand All @@ -91,8 +80,7 @@ export default () => {
<div className="flex-1 flex justify-center items-center">
{currentStep == 1 && <LoginForm />}
{currentStep == 2 && <ChooseLibraryPathInput />}
{currentStep == 3 && <WhisperModelOptionsPanel />}
{currentStep == 4 && (
{currentStep == 3 && (
<div className="flex justify-center items-center">
<CheckCircle2Icon className="text-green-500 w-24 h-24" />
</div>
Expand Down
Loading