Deps: upgrade deps & use ESM (#343)

* upgrade deps

* upgrade forge

* make app work in ESM

* fix CI
This commit is contained in:
an-lee
2024-02-23 16:03:39 +08:00
committed by GitHub
parent f3e84fd65c
commit ce47c8e138
53 changed files with 1030 additions and 881 deletions

View File

@@ -19,11 +19,14 @@ declare global {
}
let electronApp: ElectronApplication;
let page: Page;
const resultDir = path.join(process.cwd(), "test-results");
test.beforeAll(async () => {
// find the latest build in the out directory
const latestBuild = findLatestBuild();
console.log(`Latest build: ${latestBuild}`);
// parse the directory and find paths and other info
const appInfo = parseElectronApp(latestBuild);
// set the CI environment variable to true
@@ -37,18 +40,19 @@ test.beforeAll(async () => {
args: [appInfo.main],
executablePath: appInfo.executable,
});
electronApp.on("window", async (page) => {
const filename = page.url()?.split("/").pop();
console.info(`Window opened: ${filename}`);
console.log("Electron app launched");
// capture errors
page.on("pageerror", (error) => {
console.error(error);
});
// capture console messages
page.on("console", (msg) => {
console.info(msg.text());
});
page = await electronApp.firstWindow();
const filename = page.url()?.split("/").pop();
console.info(`Window opened: ${filename}`);
// capture errors
page.on("pageerror", (error) => {
console.error(error);
});
// capture console messages
page.on("console", (msg) => {
console.info(msg.text());
});
});
@@ -57,7 +61,6 @@ test.afterAll(async () => {
});
test("validate whisper command", async () => {
const page = await electronApp.firstWindow();
const res = await page.evaluate(() => {
return window.__ENJOY_APP__.whisper.check();
});
@@ -69,7 +72,6 @@ test("validate whisper command", async () => {
});
test("valid ffmpeg command", async () => {
const page = await electronApp.firstWindow();
const res = await page.evaluate(() => {
return window.__ENJOY_APP__.ffmpeg.check();
});

View File

@@ -14,11 +14,14 @@ const user = {
};
let electronApp: ElectronApplication;
let page: Page;
const resultDir = path.join(process.cwd(), "test-results");
test.beforeAll(async () => {
// find the latest build in the out directory
const latestBuild = findLatestBuild();
console.log(`Latest build: ${latestBuild}`);
// parse the directory and find paths and other info
const appInfo = parseElectronApp(latestBuild);
// set the CI environment variable to true
@@ -32,18 +35,19 @@ test.beforeAll(async () => {
args: [appInfo.main],
executablePath: appInfo.executable,
});
electronApp.on("window", async (page) => {
const filename = page.url()?.split("/").pop();
console.info(`Window opened: ${filename}`);
console.log("Electron app launched");
// capture errors
page.on("pageerror", (error) => {
console.error(error);
});
// capture console messages
page.on("console", (msg) => {
console.info(msg.text());
});
page = await electronApp.firstWindow();
const filename = page.url()?.split("/").pop();
console.info(`Window opened: ${filename}`);
// capture errors
page.on("pageerror", (error) => {
console.error(error);
});
// capture console messages
page.on("console", (msg) => {
console.info(msg.text());
});
});
@@ -52,14 +56,11 @@ test.afterAll(async () => {
});
test.describe("with login", async () => {
let page: Page;
test.beforeAll(async () => {
const settings = fs.readJsonSync(path.join(resultDir, "settings.json"));
settings.user = user;
fs.writeJsonSync(path.join(resultDir, "settings.json"), settings);
page = await electronApp.firstWindow();
page.route("**/api/me", (route) => {
route.fulfill({
json: user,
@@ -89,9 +90,6 @@ test.describe("with login", async () => {
);
page = await electronApp.firstWindow();
page.on("console", (msg) => {
console.info(msg.text());
});
await page.route("**/api/ai/audio/speech", (route) => {
route.fulfill({

94
enjoy/forge.config.js Normal file
View File

@@ -0,0 +1,94 @@
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { MakerZIP } from "@electron-forge/maker-zip";
import { MakerDeb } from "@electron-forge/maker-deb";
import { MakerRpm } from "@electron-forge/maker-rpm";
import { VitePlugin } from "@electron-forge/plugin-vite";
import { FusesPlugin } from "@electron-forge/plugin-fuses";
import { FuseV1Options, FuseVersion } from "@electron/fuses";
const config = {
packagerConfig: {
asar: false,
icon: "./assets/icon",
name: "Enjoy",
executableName: "enjoy",
protocols: [
{
name: "Enjoy",
schemes: ["enjoy"],
},
],
},
rebuildConfig: {},
makers: [
new MakerSquirrel({
name: "Enjoy",
setupIcon: "./assets/icon.ico",
}),
new MakerZIP({}, ["darwin", "win32"]),
new MakerDeb({
options: {
name: "enjoy",
productName: "Enjoy",
icon: "./assets/icon.png",
mimeType: ["x-scheme-handler/enjoy"],
},
}),
new MakerRpm({
options: {
name: "enjoy",
productName: "Enjoy",
icon: "./assets/icon.png",
mimeType: ["x-scheme-handler/enjoy"],
},
}),
],
publishers: [
{
name: "@electron-forge/publisher-github",
config: {
repository: {
owner: "xiaolai",
name: "everyone-can-use-english",
},
draft: true,
},
},
],
plugins: [
new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
// If you are familiar with Vite configuration, it will look really familiar.
build: [
{
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
entry: "src/main.ts",
config: "vite.main.config.ts",
},
{
entry: "src/preload.ts",
config: "vite.preload.config.ts",
},
],
renderer: [
{
name: "main_window",
config: "vite.renderer.config.ts",
},
],
}),
// Fuses are used to enable/disable various Electron functionality
// at package time, before code signing the application
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: true,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: false,
}),
],
};
export default config;

View File

@@ -1,180 +0,0 @@
import type { ForgeConfig } from "@electron-forge/shared-types";
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { MakerZIP } from "@electron-forge/maker-zip";
import { MakerDeb } from "@electron-forge/maker-deb";
import { MakerRpm } from "@electron-forge/maker-rpm";
import { VitePlugin } from "@electron-forge/plugin-vite";
import { dirname } from "node:path";
import { Walker, DepType, type Module } from "flora-colossus";
// any packages that you must mark as "external" in vite
const NATIVE_MODULES_TO_PACKAGE = [
"sequelize",
"umzug",
"sqlite3",
"fluent-ffmpeg",
"electron-squirrel-startup",
"ffmpeg-static",
"@andrkrn/ffprobe-static",
];
const INCLUDE_NESTED_DEPS = true as const;
let nativeModuleDependenciesToPackage: Set<string>;
const config: ForgeConfig = {
packagerConfig: {
icon: "./assets/icon",
name: "Enjoy",
executableName: "enjoy",
protocols: [
{
name: "Enjoy",
schemes: ["enjoy"],
},
],
},
rebuildConfig: {},
makers: [
new MakerSquirrel({
name: "Enjoy",
setupIcon: "./assets/icon.ico",
}),
new MakerZIP({}, ["darwin", "win32"]),
new MakerDeb({
options: {
name: "enjoy",
productName: "Enjoy",
icon: "./assets/icon.png",
mimeType: ["x-scheme-handler/enjoy"],
},
}),
new MakerRpm({
options: {
name: "enjoy",
productName: "Enjoy",
icon: "./assets/icon.png",
mimeType: ["x-scheme-handler/enjoy"],
},
}),
],
publishers: [
{
name: "@electron-forge/publisher-github",
config: {
repository: {
owner: "xiaolai",
name: "everyone-can-use-english",
},
draft: true,
},
},
],
plugins: [
new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
// If you are familiar with Vite configuration, it will look really familiar.
build: [
{
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
entry: "src/main.ts",
config: "vite.main.config.mts",
},
{
entry: "src/preload.ts",
config: "vite.preload.config.mts",
},
],
renderer: [
{
name: "main_window",
config: "vite.renderer.config.mts",
},
],
}),
],
hooks: {
// TODO: remove this once this issue is resolved: https://github.com/electron/forge/pull/3336
prePackage: async (forgeConfig) => {
if (process.platform === "linux") return;
if (forgeConfig.packagerConfig.ignore !== undefined) {
throw new Error(
"forgeConfig.packagerConfig.ignore is already defined. Please remove it from your forge config and instead use the prePackage hook to dynamically set it."
);
}
const getExternalNestedDependencies = async (
nodeModuleNames: string[],
includeNestedDeps = true
) => {
const foundModules = new Set(nodeModuleNames);
if (includeNestedDeps) {
for (const external of nodeModuleNames) {
type MyPublicClass<T> = {
[P in keyof T]: T[P];
};
type MyPublicWalker = MyPublicClass<Walker> & {
modules: Module[];
walkDependenciesForModule: (
moduleRoot: string,
depType: DepType
) => Promise<void>;
};
const moduleRoot = dirname(
require.resolve(`${external}/package.json`, {
paths: [__dirname],
})
);
const walker = new Walker(moduleRoot) as unknown as MyPublicWalker;
walker.modules = [];
await walker.walkDependenciesForModule(moduleRoot, DepType.PROD);
walker.modules
.filter(
(dep) => (dep.nativeModuleType as number) === DepType.PROD
)
.map((dep) => dep.name)
.forEach((name) => foundModules.add(name));
}
}
return foundModules;
};
nativeModuleDependenciesToPackage = await getExternalNestedDependencies(
NATIVE_MODULES_TO_PACKAGE,
INCLUDE_NESTED_DEPS
);
forgeConfig.packagerConfig.ignore = (path) => {
// .vite bundled build files
if (path.startsWith("/.vite")) {
return false;
}
// main package.json file
if (path === "/package.json") {
return false;
}
if (!path) {
return false;
}
// need to first NOT ignore the root node_modules folder
if (path === "/node_modules") {
return false;
}
// if path is in nativeModuleDependenciesToPackage, return false (to package it)
const foundModules: Set<string> = nativeModuleDependenciesToPackage;
for (const module of foundModules) {
if (
path.startsWith(`/node_modules/${module}`) ||
path.startsWith(`/node_modules/${module.split("/")[0]}`)
) {
return false;
}
}
// for everything else, ignore it
return true;
};
},
},
};
export default config;

31
enjoy/forge.env.d.ts vendored Normal file
View File

@@ -0,0 +1,31 @@
export {}; // Make this a module
declare global {
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Vite
// plugin that tells the Electron app where to look for the Vite-bundled app code (depending on
// whether you're running in development or production).
const MAIN_WINDOW_VITE_DEV_SERVER_URL: string;
const MAIN_WINDOW_VITE_NAME: string;
namespace NodeJS {
interface Process {
// Used for hot reload after preload scripts.
viteDevServers: Record<string, import('vite').ViteDevServer>;
}
}
type VitePluginConfig = ConstructorParameters<typeof import('@electron-forge/plugin-vite').VitePlugin>[0];
interface VitePluginRuntimeKeys {
VITE_DEV_SERVER_URL: `${string}_VITE_DEV_SERVER_URL`;
VITE_NAME: `${string}_VITE_NAME`;
}
}
declare module 'vite' {
interface ConfigEnv<K extends keyof VitePluginConfig = keyof VitePluginConfig> {
root: string;
forgeConfig: VitePluginConfig;
forgeConfigSelf: VitePluginConfig[K][number];
}
}

View File

@@ -1,4 +1,5 @@
{
"type": "module",
"private": true,
"name": "enjoy",
"productName": "Enjoy",
@@ -27,18 +28,21 @@
},
"license": "MIT",
"devDependencies": {
"@electron-forge/cli": "^7.2.0",
"@electron-forge/maker-deb": "^7.2.0",
"@electron-forge/maker-rpm": "^7.2.0",
"@electron-forge/maker-squirrel": "^7.2.0",
"@electron-forge/maker-zip": "^7.2.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.2.0",
"@electron-forge/plugin-vite": "^7.2.0",
"@electron-forge/publisher-github": "^7.2.0",
"@electron-forge/cli": "^7.3.0",
"@electron-forge/maker-deb": "^7.3.0",
"@electron-forge/maker-rpm": "^7.3.0",
"@electron-forge/maker-squirrel": "^7.3.0",
"@electron-forge/maker-zip": "^7.3.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.3.0",
"@electron-forge/plugin-fuses": "^7.3.0",
"@electron-forge/plugin-vite": "^7.3.0",
"@electron-forge/publisher-github": "^7.3.0",
"@electron/fuses": "^1.7.0",
"@playwright/test": "^1.41.2",
"@tailwindcss/typography": "^0.5.10",
"@types/autosize": "^4.0.3",
"@types/command-exists": "^1.2.3",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/fluent-ffmpeg": "^2.1.24",
"@types/html-to-text": "^9.0.4",
"@types/intl-tel-input": "^18.1.4",
@@ -53,7 +57,7 @@
"@typescript-eslint/parser": "^7.0.2",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.17",
"electron": "^28.2.3",
"electron": "^29.0.1",
"electron-playwright-helpers": "^1.7.1",
"eslint": "^8.56.0",
"eslint-import-resolver-typescript": "^3.6.1",
@@ -67,6 +71,7 @@
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"vite-plugin-static-copy": "^1.0.1",
"zx": "^7.2.3"
},
@@ -75,7 +80,7 @@
"@ffmpeg/ffmpeg": "^0.12.10",
"@ffmpeg/util": "^0.12.1",
"@hookform/resolvers": "^3.3.4",
"@langchain/community": "^0.0.30",
"@langchain/community": "^0.0.32",
"@langchain/google-genai": "^0.0.10",
"@mozilla/readability": "^0.5.0",
"@radix-ui/react-accordion": "^1.1.2",
@@ -124,12 +129,12 @@
"fs-extra": "^11.2.0",
"html-to-text": "^9.0.5",
"https-proxy-agent": "^7.0.4",
"i18next": "^23.9.0",
"intl-tel-input": "^19.2.19",
"i18next": "^23.10.0",
"intl-tel-input": "^19.2.20",
"js-md5": "^0.8.3",
"langchain": "^0.1.20",
"langchain": "^0.1.21",
"lodash": "^4.17.21",
"lucide-react": "^0.335.0",
"lucide-react": "^0.336.0",
"mark.js": "^8.11.1",
"microsoft-cognitiveservices-speech-sdk": "^1.35.0",
"next-themes": "^0.2.1",

View File

@@ -1,6 +1,6 @@
module.exports = {
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
};

View File

@@ -3,21 +3,12 @@ import path from "path";
import settings from "@main/settings";
import "@main/i18n";
import mainWindow from "@main/window";
import crypto from "crypto";
import log from "electron-log/main";
log.transports.file.level = "info";
log.transports.file.resolvePathFn = () =>
path.join(settings.libraryPath(), "logs", "main.log");
log.errorHandler.startCatching();
// Fix "getRandomValues() not supported"
global.crypto = crypto;
import ElectronSquirrelStartup from "electron-squirrel-startup";
app.commandLine.appendSwitch("enable-features", "SharedArrayBuffer");
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require("electron-squirrel-startup")) {
if (ElectronSquirrelStartup) {
app.quit();
}

View File

@@ -1,6 +1,6 @@
import * as sdk from "microsoft-cognitiveservices-speech-sdk";
import fs from "fs-extra";
import log from "electron-log/main";
import log from "@main/logger";
const logger = log.scope("AZURE");
export class AzureSpeechSdk {

View File

@@ -8,7 +8,7 @@ const filename = path.resolve(
);
const template = `
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
// code here
@@ -18,8 +18,7 @@ async function down({ context: queryInterface }) {
// code here
}
module.exports = { up, down };
`;
export { up, down };`;
await fs.mkdir(path.resolve(__dirname, "migrations"), { recursive: true });
await fs.writeFile(filename, template.trim());

View File

@@ -2,7 +2,7 @@ import { ipcMain, IpcMainEvent } from "electron";
import { Audio, Transcription } from "@main/db/models";
import { FindOptions, WhereOptions, Attributes } from "sequelize";
import downloader from "@main/downloader";
import log from "electron-log/main";
import log from "@main/logger";
import { t } from "i18next";
import youtubedr from "@main/youtubedr";

View File

@@ -1,7 +1,7 @@
import { ipcMain, IpcMainEvent } from "electron";
import { Conversation, Message } from "@main/db/models";
import { FindOptions, WhereOptions, Attributes } from "sequelize";
import log from "electron-log/main";
import log from "@main/logger";
import { t } from "i18next";
class ConversationsHandler {

View File

@@ -1,7 +1,7 @@
import { ipcMain, IpcMainEvent } from "electron";
import { Message, Speech, Conversation } from "@main/db/models";
import { FindOptions, WhereOptions, Attributes } from "sequelize";
import log from "electron-log/main";
import log from "@main/logger";
import { t } from "i18next";
import db from "@main/db";

View File

@@ -14,7 +14,7 @@ import {
} from "sequelize";
import dayjs from "dayjs";
import { t } from "i18next";
import log from "electron-log/main";
import log from "@main/logger";
const logger = log.scope("db/handlers/recordings-handler");

View File

@@ -1,7 +1,7 @@
import { ipcMain, IpcMainEvent } from "electron";
import { Transcription, Audio, Video } from "@main/db/models";
import { Attributes } from "sequelize";
import log from "electron-log/main";
import log from "@main/logger";
const logger = log.scope("db/handlers/transcriptions-handler");
class TranscriptionsHandler {

View File

@@ -2,7 +2,7 @@ import { ipcMain, IpcMainEvent } from "electron";
import { Video, Transcription } from "@main/db/models";
import { FindOptions, WhereOptions, Attributes } from "sequelize";
import downloader from "@main/downloader";
import log from "electron-log/main";
import log from "@main/logger";
import { t } from "i18next";
import youtubedr from "@main/youtubedr";

View File

@@ -23,6 +23,11 @@ import {
transcriptionsHandler,
videosHandler,
} from "./handlers";
import path from "path";
import url from 'url';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const db = {
connection: null as Sequelize | null,

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -78,4 +78,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("audios");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -78,4 +78,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("videos");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -73,4 +73,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("recordings");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -74,5 +74,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("pronunciation_assessments");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable("conversations", {
@@ -35,5 +35,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("conversations");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable("messages", {
@@ -34,4 +34,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("messages");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -59,4 +59,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("speeches");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -46,5 +46,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("cache_objects");
}
module.exports = { up, down };
export { up, down };

View File

@@ -1,4 +1,4 @@
const { DataTypes } = require("sequelize");
import { DataTypes } from "sequelize";
async function up({ context: queryInterface }) {
queryInterface.createTable(
@@ -64,5 +64,4 @@ async function down({ context: queryInterface }) {
queryInterface.dropTable("transcriptions");
}
module.exports = { up, down };
export { up, down };

View File

@@ -22,7 +22,7 @@ import path from "path";
import fs from "fs-extra";
import { t } from "i18next";
import mainWindow from "@main/window";
import log from "electron-log/main";
import log from "@main/logger";
import storage from "@main/storage";
import Ffmpeg from "@main/ffmpeg";
import { Client } from "@/api";

View File

@@ -7,7 +7,7 @@ import {
DataType,
AllowNull,
} from "sequelize-typescript";
import log from "electron-log/main";
import log from "@main/logger";
const logger = log.scope("cache-object");

View File

@@ -27,7 +27,7 @@ import settings from "@main/settings";
import db from "@main/db";
import mainWindow from "@main/window";
import { t } from "i18next";
import log from "electron-log/main";
import log from "@main/logger";
import fs from "fs-extra";
import path from "path";
import Ffmpeg from "@main/ffmpeg";

View File

@@ -17,7 +17,7 @@ import { Recording } from "@main/db/models";
import { Client } from "@/api";
import { WEB_API_URL } from "@/constants";
import settings from "@main/settings";
import log from "electron-log/main";
import log from "@main/logger";
@Table({
modelName: "PronunciationAssessment",

View File

@@ -20,7 +20,7 @@ import fs from "fs-extra";
import path from "path";
import settings from "@main/settings";
import { hashFile } from "@main/utils";
import log from "electron-log/main";
import log from "@main/logger";
import storage from "@main/storage";
import { Client } from "@/api";
import { WEB_API_URL } from "@/constants";

View File

@@ -22,7 +22,7 @@ import OpenAI, { type ClientOptions } from "openai";
import { t } from "i18next";
import { hashFile } from "@main/utils";
import { Audio, Message } from "@main/db/models";
import log from "electron-log/main";
import log from "@main/logger";
import { WEB_API_URL } from "@/constants";
import proxyAgent from "@main/proxy-agent";

View File

@@ -13,7 +13,7 @@ import {
} from "sequelize-typescript";
import { Audio, Video } from "@main/db/models";
import mainWindow from "@main/window";
import log from "electron-log/main";
import log from "@main/logger";
import { Client } from "@/api";
import { WEB_API_URL, PROCESS_TIMEOUT } from "@/constants";
import settings from "@main/settings";

View File

@@ -22,7 +22,7 @@ import path from "path";
import fs from "fs-extra";
import { t } from "i18next";
import mainWindow from "@main/window";
import log from "electron-log/main";
import log from "@main/logger";
import storage from "@main/storage";
import Ffmpeg from "@main/ffmpeg";
import { Client } from "@/api";

View File

@@ -2,7 +2,7 @@ import { ipcMain, app } from "electron";
import path from "path";
import fs from "fs";
import mainWin from "@main/window";
import log from "electron-log/main";
import log from "@main/logger";
const logger = log.scope("downloader");
class Downloader {

View File

@@ -2,10 +2,14 @@ import { ipcMain } from "electron";
import ffmpegPath from "ffmpeg-static";
import ffprobePath from "@andrkrn/ffprobe-static";
import Ffmpeg from "fluent-ffmpeg";
import log from "electron-log/main";
import log from "@main/logger";
import path from "path";
import fs from "fs-extra";
import settings from "./settings";
import url from 'url';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Ffmpeg.setFfmpegPath(ffmpegPath);
Ffmpeg.setFfprobePath(ffprobePath);

12
enjoy/src/main/logger.ts Normal file
View File

@@ -0,0 +1,12 @@
import log from "electron-log/main";
import path from "path";
import settings from "@main/settings";
log.initialize({ preload: true });
log.transports.file.level = "info";
log.transports.file.resolvePathFn = () =>
path.join(settings.libraryPath(), "logs", "main.log");
log.errorHandler.startCatching();
export default log;

View File

@@ -1,4 +1,4 @@
import log from "electron-log/main";
import log from "@main/logger";
import $ from "cheerio";
import { BrowserView, ipcMain } from "electron";

View File

@@ -1,4 +1,4 @@
import log from "electron-log/main";
import log from "@main/logger";
import $ from "cheerio";
import { BrowserView, ipcMain } from "electron";

View File

@@ -3,7 +3,6 @@ import { LIBRARY_PATH_SUFFIX, DATABASE_NAME } from "@/constants";
import { ipcMain, app } from "electron";
import path from "path";
import fs from "fs-extra";
import log from "electron-log";
import * as i18n from "i18next";
if (process.env.SETTINGS_PATH) {
@@ -13,7 +12,6 @@ if (process.env.SETTINGS_PATH) {
});
}
const logger = log.scope("settings");
const language = () => {
const _language = settings.getSync("language");

View File

@@ -1,7 +1,7 @@
import { STORAGE_WORKER_ENDPOINT } from "@/constants";
import axios, { AxiosInstance } from "axios";
import fs from "fs-extra";
import log from "electron-log/main";
import log from "@main/logger";
const logger = log.scope("STORAGE");
const ONE_MINUTE = 1000 * 60; // 1 minute

View File

@@ -4,7 +4,11 @@ import path from "path";
import { WHISPER_MODELS_OPTIONS, PROCESS_TIMEOUT } from "@/constants";
import { exec, spawn } from "child_process";
import fs from "fs-extra";
import log from "electron-log/main";
import log from "@main/logger";
import url from 'url';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const logger = log.scope("whisper");

View File

@@ -15,13 +15,16 @@ import downloader from "@main/downloader";
import whisper from "@main/whisper";
import fs from "fs-extra";
import "@main/i18n";
import log from "electron-log/main";
import log from "@main/logger";
import { WEB_API_URL, REPO_URL } from "@/constants";
import { AudibleProvider, TedProvider } from "@main/providers";
import Ffmpeg from "@main/ffmpeg";
import { Waveform } from "./waveform";
import url from 'url';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
log.initialize({ preload: true });
const logger = log.scope("window");
const audibleProvider = new AudibleProvider();

View File

@@ -3,9 +3,13 @@ import path from "path";
import { exec } from "child_process";
import fs from "fs-extra";
import os from "os";
import log from "electron-log/main";
import log from "@main/logger";
import snakeCase from "lodash/snakeCase";
import settings from "@main/settings";
import url from 'url';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const logger = log.scope("YOUTUBEDR");

View File

@@ -5,13 +5,13 @@
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"noImplicitAny": true,
"sourceMap": true,
"baseUrl": ".",
"outDir": "dist",
"moduleResolution": "node",
"resolveJsonModule": true,
"jsx": "react-jsx",
"paths": {
"@/*": ["./src/*"],
"@renderer/*": ["./src/renderer/*"],

94
enjoy/vite.base.config.ts Normal file
View File

@@ -0,0 +1,94 @@
import { builtinModules } from 'node:module';
import type { AddressInfo } from 'node:net';
import type { ConfigEnv, Plugin, UserConfig } from 'vite';
import pkg from './package.json';
export const builtins = ['electron', ...builtinModules.map((m) => [m, `node:${m}`]).flat()];
export const external = [...builtins, ...Object.keys('dependencies' in pkg ? (pkg.dependencies as Record<string, unknown>) : {})];
export function getBuildConfig(env: ConfigEnv<'build'>): UserConfig {
const { root, mode, command } = env;
return {
root,
mode,
build: {
target: 'esnext',
// Prevent multiple builds from interfering with each other.
emptyOutDir: false,
// 🚧 Multiple builds may conflict.
outDir: '.vite/build',
watch: command === 'serve' ? {} : null,
minify: command === 'build',
},
clearScreen: false,
};
}
export function getDefineKeys(names: string[]) {
const define: { [name: string]: VitePluginRuntimeKeys } = {};
return names.reduce((acc, name) => {
const NAME = name.toUpperCase();
const keys: VitePluginRuntimeKeys = {
VITE_DEV_SERVER_URL: `${NAME}_VITE_DEV_SERVER_URL`,
VITE_NAME: `${NAME}_VITE_NAME`,
};
return { ...acc, [name]: keys };
}, define);
}
export function getBuildDefine(env: ConfigEnv<'build'>) {
const { command, forgeConfig } = env;
const names = forgeConfig.renderer.filter(({ name }) => name != null).map(({ name }) => name!);
const defineKeys = getDefineKeys(names);
const define = Object.entries(defineKeys).reduce((acc, [name, keys]) => {
const { VITE_DEV_SERVER_URL, VITE_NAME } = keys;
const def = {
[VITE_DEV_SERVER_URL]: command === 'serve' ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) : undefined,
[VITE_NAME]: JSON.stringify(name),
};
return { ...acc, ...def };
}, {} as Record<string, any>);
return define;
}
export function pluginExposeRenderer(name: string): Plugin {
const { VITE_DEV_SERVER_URL } = getDefineKeys([name])[name];
return {
name: '@electron-forge/plugin-vite:expose-renderer',
configureServer(server) {
process.viteDevServers ??= {};
// Expose server for preload scripts hot reload.
process.viteDevServers[name] = server;
server.httpServer?.once('listening', () => {
const addressInfo = server.httpServer!.address() as AddressInfo;
// Expose env constant for main process use.
process.env[VITE_DEV_SERVER_URL] = `http://localhost:${addressInfo?.port}`;
});
},
};
}
export function pluginHotRestart(command: 'reload' | 'restart'): Plugin {
return {
name: '@electron-forge/plugin-vite:hot-restart',
closeBundle() {
if (command === 'reload') {
for (const server of Object.values(process.viteDevServers)) {
// Preload scripts hot reload.
server.ws.send({ type: 'full-reload' });
}
} else {
// Main process hot restart.
// https://github.com/electron/forge/blob/v7.2.0/packages/api/core/src/api/start.ts#L216-L223
process.stdin.emit('data', 'rs');
}
},
};
}

View File

@@ -1,62 +0,0 @@
import { defineConfig } from "vite";
import { viteStaticCopy } from "vite-plugin-static-copy";
import path from "path";
import os from "os";
// https://vitejs.dev/config
export default defineConfig({
resolve: {
// Some libs that can run in both Web and Node.js, such as `axios`, we need to tell Vite to build them in Node.js.
browserField: false,
mainFields: ["module", "jsnext:main", "jsnext"],
alias: {
"@": path.resolve(__dirname, "./src"),
"@main": path.resolve(__dirname, "./src/main"),
"@commands": path.resolve(__dirname, "./src/commands"),
},
},
build: {
rollupOptions: {
external: [
"sequelize",
"umzug",
"sqlite3",
"fluent-ffmpeg",
"bufferutil",
"utf-8-validate",
"ffmpeg-static",
"@andrkrn/ffprobe-static",
],
},
},
plugins: [
viteStaticCopy({
targets: [
{
src: `lib/whisper.cpp/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/whisper",
},
{
src: `lib/whisper.cpp/models/*`,
dest: "lib/whisper/models",
},
{
src: `lib/youtubedr/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/youtubedr",
},
{
src: "src/main/db/migrations/*",
dest: "migrations",
},
{
src: "samples/*",
dest: "samples",
},
],
}),
],
});

92
enjoy/vite.main.config.ts Normal file
View File

@@ -0,0 +1,92 @@
import { viteStaticCopy } from "vite-plugin-static-copy";
import os from "os";
import path from "path";
import type { ConfigEnv, UserConfig } from "vite";
import { defineConfig, mergeConfig } from "vite";
import {
getBuildConfig,
getBuildDefine,
pluginHotRestart,
external,
} from "./vite.base.config";
// https://vitejs.dev/config
export default defineConfig((env) => {
const forgeEnv = env as ConfigEnv<"build">;
const { forgeConfigSelf } = forgeEnv;
const define = getBuildDefine(forgeEnv);
const config: UserConfig = {
build: {
lib: {
entry: forgeConfigSelf.entry!,
fileName: () => "[name].js",
formats: ["es"],
},
rollupOptions: {
external,
// external: [
// "axios",
// "child_process",
// "crypto",
// "fs-extra",
// "fs",
// "path",
// "sequelize",
// "umzug",
// "sqlite3",
// "fluent-ffmpeg",
// "ffmpeg-static",
// "@andrkrn/ffprobe-static",
// ],
},
commonjsOptions: {
transformMixedEsModules: true,
defaultIsModuleExports: true,
esmExternals: true,
},
},
plugins: [
pluginHotRestart("restart"),
viteStaticCopy({
targets: [
{
src: `lib/whisper.cpp/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/whisper",
},
{
src: `lib/whisper.cpp/models/*`,
dest: "lib/whisper/models",
},
{
src: `lib/youtubedr/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/youtubedr",
},
{
src: "src/main/db/migrations/*",
dest: "migrations",
},
{
src: "samples/*",
dest: "samples",
},
],
}),
],
define,
resolve: {
// Load the Node.js entry.
mainFields: ["module", "jsnext:main", "jsnext"],
alias: {
"@": path.resolve(__dirname, "./src"),
"@main": path.resolve(__dirname, "./src/main"),
"@commands": path.resolve(__dirname, "./src/commands"),
},
},
};
return mergeConfig(getBuildConfig(forgeEnv), config);
});

View File

@@ -1,4 +0,0 @@
import { defineConfig } from 'vite';
// https://vitejs.dev/config
export default defineConfig({});

View File

@@ -0,0 +1,29 @@
import type { ConfigEnv, UserConfig } from "vite";
import { defineConfig, mergeConfig } from "vite";
import { getBuildConfig, external, pluginHotRestart } from "./vite.base.config";
// https://vitejs.dev/config
export default defineConfig((env) => {
const forgeEnv = env as ConfigEnv<"build">;
const { forgeConfigSelf } = forgeEnv;
const config: UserConfig = {
build: {
rollupOptions: {
external,
// Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`.
input: forgeConfigSelf.entry!,
output: {
format: "cjs",
// It should not be split chunks.
inlineDynamicImports: true,
entryFileNames: "[name].js",
chunkFileNames: "[name].js",
assetFileNames: "[name].[ext]",
},
},
},
plugins: [pluginHotRestart("reload")],
};
return mergeConfig(getBuildConfig(forgeEnv), config);
});

View File

@@ -1,29 +0,0 @@
import path from "path";
import react from "@vitejs/plugin-react";
import { viteStaticCopy } from "vite-plugin-static-copy";
import { defineConfig } from "vite";
// https://vitejs.dev/config
export default defineConfig({
plugins: [
react(),
viteStaticCopy({
targets: [
{
src: "assets/*",
dest: "assets",
},
],
}),
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@renderer": path.resolve(__dirname, "./src/renderer"),
"@commands": path.resolve(__dirname, "./src/commands"),
},
},
optimizeDeps: {
exclude: ["@ffmpeg/ffmpeg", "@ffmpeg/util"],
}
});

View File

@@ -0,0 +1,46 @@
import path from "path";
import react from "@vitejs/plugin-react";
import { viteStaticCopy } from "vite-plugin-static-copy";
import type { ConfigEnv, UserConfig } from "vite";
import { defineConfig } from "vite";
import { pluginExposeRenderer } from "./vite.base.config";
// https://vitejs.dev/config
export default defineConfig((env) => {
const forgeEnv = env as ConfigEnv<"renderer">;
const { root, mode, forgeConfigSelf } = forgeEnv;
const name = forgeConfigSelf.name ?? "";
return {
root,
mode,
base: "./",
build: {
outDir: `.vite/renderer/${name}`,
},
plugins: [
pluginExposeRenderer(name),
react(),
viteStaticCopy({
targets: [
{
src: "assets/*",
dest: "assets",
},
],
}),
],
resolve: {
preserveSymlinks: true,
alias: {
"@": path.resolve(__dirname, "./src"),
"@renderer": path.resolve(__dirname, "./src/renderer"),
"@commands": path.resolve(__dirname, "./src/commands"),
},
},
optimizeDeps: {
exclude: ["@ffmpeg/ffmpeg", "@ffmpeg/util"],
},
clearScreen: false,
} as UserConfig;
});

1011
yarn.lock

File diff suppressed because it is too large Load Diff