diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..578b8cf Binary files /dev/null and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..bea1efe --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install.scopes] +"@jsr" = "https://npm.jsr.io" diff --git a/package.json b/package.json index f7e0292..c23ab65 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,12 @@ "author": "", "license": "MIT", "dependencies": { + "@alikia/random-key": "npm:@jsr/alikia__random-key", "@electron/remote": "^2.1.2", + "@hono/node-server": "^1.13.7", "@unly/universal-language-detector": "^2.0.3", "better-sqlite3": "^11.6.0", + "detect-port": "^2.1.0", "electron-context-menu": "^4.0.4", "electron-reloader": "^1.2.3", "electron-screencapture": "^1.1.0", @@ -27,6 +30,7 @@ "electron-store": "^10.0.0", "electron-window-state": "^5.0.3", "execa": "^9.5.1", + "hono": "^4.6.15", "i18next": "^24.0.2", "i18next-browser-languagedetector": "^8.0.0", "i18next-electron-fs-backend": "^3.0.2", diff --git a/src/electron/backend/encoding.ts b/src/electron/backend/encoding.ts index f4f6634..f8abed8 100644 --- a/src/electron/backend/encoding.ts +++ b/src/electron/backend/encoding.ts @@ -76,7 +76,7 @@ export async function deleteEncodedScreenshots(db: Database) { // Check and process encoding task export function processEncodingTasks(db: Database) { - const tasksPerforming = cache.get("tasksPerforming") as string[] || []; + const tasksPerforming = cache.get("backend:encodingTasksPerforming") as string[] || []; if (tasksPerforming.length >= CONCURRENCY) return; const stmt = db.prepare(` @@ -111,7 +111,7 @@ export function processEncodingTasks(db: Database) { const metaFilePath = path.join(getEncodingTempDir(), `${taskId}_meta.txt`); const metaContent = frames.map(frame => `file '${path.join(getScreenshotsDir(), frame.imgFilename)}'\nduration 0.03333`).join("\n"); fs.writeFileSync(metaFilePath, metaContent); - cache.put("tasksPerforming", [...tasksPerforming, taskId.toString()]); + cache.put("backend:encodingTasksPerforming", [...tasksPerforming, taskId.toString()]); const videoPath = path.join(getRecordingsDir(), `${taskId}.mp4`); const ffmpegCommand = `ffmpeg -f concat -safe 0 -i "${metaFilePath}" -c:v libx264 -r 30 -threads 1 "${videoPath}"`; @@ -138,7 +138,7 @@ export function processEncodingTasks(db: Database) { db.prepare(`COMMIT;`).run(); } - cache.put("tasksPerforming", tasksPerforming.filter(id => id !== taskId.toString())); + cache.put("backend:encodingTasksPerforming", tasksPerforming.filter(id => id !== taskId.toString())); fs.unlinkSync(metaFilePath); }); } diff --git a/src/electron/index.ts b/src/electron/index.ts index a165845..dec657b 100644 --- a/src/electron/index.ts +++ b/src/electron/index.ts @@ -9,6 +9,11 @@ import { startScreenshotLoop } from "./backend/screenshot.js"; import { __dirname } from "./dirname.js"; import { hideDock } from "./utils/electron.js"; import { checkFramesForEncoding, deleteEncodedScreenshots, processEncodingTasks } from "./backend/encoding.js"; +import honoApp from "./server/index.js"; +import { serve } from "@hono/node-server"; +import { findAvailablePort } from "./utils/server.js"; +import cache from "memory-cache"; +import { generate } from '@alikia/random-key'; const i18n = initI18n(); @@ -70,15 +75,26 @@ contextMenu({ app.once("ready", () => { hideDock(); }); -app.on("activate", () => {}); +app.on("activate", () => { +}); app.on("ready", () => { createTray(); + findAvailablePort(12412).then((port) => { + generate().then((key) => { + cache.put("server:APIKey",key); + cache.put("server:port", port); + if (dev) + console.log(`API Key: ${key}`); + serve({ fetch: honoApp.fetch, port: port }); + console.log(`App server running on port ${port}`); + }); + }) initDatabase().then((db) => { screenshotInterval = startScreenshotLoop(db); setInterval(checkFramesForEncoding, 5000, db); setInterval(processEncodingTasks, 10000, db); - setInterval(deleteEncodedScreenshots, 5000, db) + setInterval(deleteEncodedScreenshots, 5000, db); dbConnection = db; }); mainWindow = createMainWindow(port, () => (mainWindow = null)); @@ -89,7 +105,7 @@ app.on("ready", () => { }); }); -app.on("will-quit", ()=> { +app.on("will-quit", () => { dbConnection?.close(); }); @@ -97,6 +113,6 @@ app.on("will-quit", ()=> { // if (process.platform !== "darwin") app.quit(); // }); -ipcMain.on('close-settings', () => { +ipcMain.on("close-settings", () => { settingsWindow?.hide(); }); \ No newline at end of file diff --git a/src/electron/server/index.ts b/src/electron/server/index.ts new file mode 100644 index 0000000..5dc1557 --- /dev/null +++ b/src/electron/server/index.ts @@ -0,0 +1,17 @@ +import { Hono } from 'hono' +import cache from "memory-cache"; + +const app = new Hono(); + +app.use(async (c, next) => { + const key = cache.get("server:APIKey"); + if (key && c.req.header("x-api-key") !== key) { + c.res = undefined + c.res = c.json({ error: "Invalid API key" }, 401); + } + await next(); +}) + +app.get('/ping', (c) => c.text('pong')) + +export default app; \ No newline at end of file diff --git a/src/electron/utils/server.ts b/src/electron/utils/server.ts new file mode 100644 index 0000000..885347f --- /dev/null +++ b/src/electron/utils/server.ts @@ -0,0 +1,17 @@ +import { detect } from 'detect-port'; + +/** + * Finds an available port starting from a given port. + * @param startingFrom - The port number to start searching from. + * @returns A Promise that resolves to the first available port number. + */ +export async function findAvailablePort(startingFrom: number): Promise { + return detect(startingFrom) + .then(realPort => { + return realPort; // Return the available port + }) + .catch(err => { + console.error(`Error detecting port: ${err.message}`); + throw err; // Rethrow the error for further handling if needed + }); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index b7f2e16..eb84752 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ESNext", "module": "ESNext", - "moduleResolution": "node", + "moduleResolution": "bundler", "outDir": "./dist/electron", "rootDir": "./src/electron", "strict": true,