From b57f4a640a828d66fed3efa0a8e576dfadfc4cde Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 13 Jan 2026 22:55:32 +0100 Subject: [PATCH] fix(electron): pass nativeModulesPath to workers and load native modules in worker to support packaged app --- .../src/app/events/epg.events.ts | 22 +++++++++++++-- .../src/app/workers/epg-parser.worker.ts | 28 ++++++++++++++----- electron-builder.json | 6 +++- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/apps/electron-backend/src/app/events/epg.events.ts b/apps/electron-backend/src/app/events/epg.events.ts index a74c89744..763a3ae64 100644 --- a/apps/electron-backend/src/app/events/epg.events.ts +++ b/apps/electron-backend/src/app/events/epg.events.ts @@ -277,11 +277,17 @@ export default class EpgEvents { let worker: Worker; try { const workerURL = pathToFileURL(workerPath); + // In packaged app, native modules are in app.asar.unpacked/node_modules + // which is separate from the worker location in extraResources + const nativeModulesPath = app.isPackaged + ? path.join(path.dirname(app.getAppPath()), 'app.asar.unpacked', 'node_modules') + : undefined; worker = new Worker(workerURL, { resourceLimits: { maxOldGenerationSizeMb: 4096, maxYoungGenerationSizeMb: 512, }, + workerData: { nativeModulesPath }, }); } catch (error) { console.error( @@ -611,7 +617,13 @@ export default class EpgEvents { let worker: Worker; try { const workerURL = pathToFileURL(workerPath); - worker = new Worker(workerURL); + // In packaged app, native modules are in app.asar.unpacked/node_modules + const nativeModulesPath = app.isPackaged + ? path.join(path.dirname(app.getAppPath()), 'app.asar.unpacked', 'node_modules') + : undefined; + worker = new Worker(workerURL, { + workerData: { nativeModulesPath }, + }); } catch (error) { console.error( this.loggerLabel, @@ -685,7 +697,13 @@ export default class EpgEvents { let worker: Worker; try { const workerURL = pathToFileURL(workerPath); - worker = new Worker(workerURL); + // In packaged app, native modules are in app.asar.unpacked/node_modules + const nativeModulesPath = app.isPackaged + ? path.join(path.dirname(app.getAppPath()), 'app.asar.unpacked', 'node_modules') + : undefined; + worker = new Worker(workerURL, { + workerData: { nativeModulesPath }, + }); } catch (error) { console.error( this.loggerLabel, diff --git a/apps/electron-backend/src/app/workers/epg-parser.worker.ts b/apps/electron-backend/src/app/workers/epg-parser.worker.ts index b0af79482..d96f02f20 100644 --- a/apps/electron-backend/src/app/workers/epg-parser.worker.ts +++ b/apps/electron-backend/src/app/workers/epg-parser.worker.ts @@ -1,11 +1,25 @@ -import { parentPort } from 'worker_threads'; +import { parentPort, workerData } from 'worker_threads'; import { createGunzip } from 'zlib'; import { SaxesParser, SaxesTagPlain } from 'saxes'; import { Readable } from 'stream'; -import Database from 'better-sqlite3'; import { existsSync, mkdirSync } from 'fs'; import { homedir } from 'os'; -import { join } from 'path'; +import { join, dirname } from 'path'; +import { createRequire } from 'module'; +import type BetterSqlite3 from 'better-sqlite3'; + +// In packaged app, native modules are in app.asar.unpacked/node_modules +// which is separate from the worker location in extraResources +let Database: typeof BetterSqlite3; +if (workerData?.nativeModulesPath) { + // Create a require function that resolves from the unpacked node_modules + const nativeRequire = createRequire(join(workerData.nativeModulesPath, 'index.js')); + Database = nativeRequire('better-sqlite3'); +} else { + // Development mode - use normal require + // eslint-disable-next-line @typescript-eslint/no-require-imports + Database = require('better-sqlite3'); +} /** * Internal parsing types with arrays for XML parsing @@ -131,13 +145,13 @@ function getDatabasePath(): string { * Creates its own connection to avoid blocking main thread */ class EpgDatabase { - private db: Database.Database; + private db: BetterSqlite3.Database; private knownChannelIds: Set = new Set(); // Prepared statements for better performance - private insertChannelStmt: Database.Statement; - private insertProgramStmt: Database.Statement; - private deleteChannelsStmt: Database.Statement; + private insertChannelStmt: BetterSqlite3.Statement; + private insertProgramStmt: BetterSqlite3.Statement; + private deleteChannelsStmt: BetterSqlite3.Statement; constructor() { const dbPath = getDatabasePath(); diff --git a/electron-builder.json b/electron-builder.json index 4fca4325c..eea953d3e 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -16,7 +16,11 @@ "web/**/*", "!**/*.map" ], - "asarUnpack": ["**/better-sqlite3/**"], + "asarUnpack": [ + "**/better-sqlite3/**", + "**/bindings/**", + "**/file-uri-to-path/**" + ], "extraMetadata": { "main": "electron-backend/main.js" },