diff --git a/apps/electron-backend/src/app/api/main.preload.ts b/apps/electron-backend/src/app/api/main.preload.ts
index 777495ce6..e9291cd7d 100644
--- a/apps/electron-backend/src/app/api/main.preload.ts
+++ b/apps/electron-backend/src/app/api/main.preload.ts
@@ -137,4 +137,5 @@ contextBridge.exposeInMainWorld('electron', {
dbGetContentByXtreamId: (xtreamId: number, playlistId: string) =>
ipcRenderer.invoke('DB_GET_CONTENT_BY_XTREAM_ID', xtreamId, playlistId),
dbDeleteAllPlaylists: () => ipcRenderer.invoke('DB_DELETE_ALL_PLAYLISTS'),
+ getLocalIpAddresses: () => ipcRenderer.invoke('get-local-ip-addresses'),
});
diff --git a/apps/electron-backend/src/app/events/electron.events.ts b/apps/electron-backend/src/app/events/electron.events.ts
index 8985e2fea..47795d0f7 100644
--- a/apps/electron-backend/src/app/events/electron.events.ts
+++ b/apps/electron-backend/src/app/events/electron.events.ts
@@ -4,6 +4,7 @@
*/
import { app, ipcMain } from 'electron';
+import * as os from 'os';
import { environment } from '../../environments/environment';
export default class ElectronEvents {
@@ -23,3 +24,20 @@ ipcMain.handle('get-app-version', (event) => {
ipcMain.on('quit', (event, code) => {
app.exit(code);
});
+
+// Get local IP addresses for remote control URL display
+ipcMain.handle('get-local-ip-addresses', () => {
+ const interfaces = os.networkInterfaces();
+ const addresses: string[] = [];
+
+ for (const name of Object.keys(interfaces)) {
+ for (const iface of interfaces[name] || []) {
+ // Skip internal (loopback) and non-IPv4 addresses
+ if (iface.family === 'IPv4' && !iface.internal) {
+ addresses.push(iface.address);
+ }
+ }
+ }
+
+ return addresses;
+});
diff --git a/apps/electron-backend/src/app/server/http-server.ts b/apps/electron-backend/src/app/server/http-server.ts
index c56b38d2b..23a235133 100644
--- a/apps/electron-backend/src/app/server/http-server.ts
+++ b/apps/electron-backend/src/app/server/http-server.ts
@@ -10,13 +10,21 @@ export class HttpServer {
private server: http.Server | null = null;
private port = 8765;
private isEnabled = false;
- private distPath: string;
+ private distPath: string | null = null;
private remoteControlHandlers: Map<
string,
(req: http.IncomingMessage, res: http.ServerResponse) => void
> = new Map();
- constructor() {
+ /**
+ * Get the path to the remote-control-web static files.
+ * Lazily computed to avoid calling Electron APIs before app is ready.
+ */
+ private getDistPath(): string {
+ if (this.distPath) {
+ return this.distPath;
+ }
+
// Path to the built remote-control-web app
// In development: use workspace root
// In production: use app path
@@ -34,16 +42,16 @@ export class HttpServer {
);
} else {
// Production mode - files are bundled with the app
+ // electron-builder copies remote-control-web/**/* directly to app root
this.distPath = path.join(
appPath,
- 'dist',
- 'apps',
'remote-control-web',
'browser'
);
}
console.log('[HTTP Server] Serving from:', this.distPath);
+ return this.distPath;
}
/**
@@ -151,7 +159,7 @@ export class HttpServer {
// Security: prevent directory traversal
filePath = path.normalize(filePath).replace(/^(\.\.[/\\])+/, '');
- const fullPath = path.join(this.distPath, filePath);
+ const fullPath = path.join(this.getDistPath(), filePath);
fs.readFile(fullPath, (err, data) => {
if (err) {
diff --git a/apps/web/project.json b/apps/web/project.json
index f3b40227d..17bd28c6b 100644
--- a/apps/web/project.json
+++ b/apps/web/project.json
@@ -43,7 +43,13 @@
"maximumError": "10kb"
}
],
- "outputHashing": "all"
+ "outputHashing": "all",
+ "fileReplacements": [
+ {
+ "replace": "apps/web/src/environments/environment.ts",
+ "with": "apps/web/src/environments/environment.prod.ts"
+ }
+ ]
},
"pwa": {
"baseHref": "/",
@@ -62,7 +68,13 @@
"outputHashing": "all",
"optimization": true,
"extractLicenses": true,
- "sourceMap": false
+ "sourceMap": false,
+ "fileReplacements": [
+ {
+ "replace": "apps/web/src/environments/environment.ts",
+ "with": "apps/web/src/environments/environment.prod.ts"
+ }
+ ]
},
"development": {
"optimization": false,
diff --git a/apps/web/src/app/settings/settings.component.html b/apps/web/src/app/settings/settings.component.html
index f882c0261..d184d28f2 100644
--- a/apps/web/src/app/settings/settings.component.html
+++ b/apps/web/src/app/settings/settings.component.html
@@ -326,6 +326,49 @@
+ @if (localIpAddresses().length > 0) {
+
+ {{ 'SETTINGS.REMOTE_CONTROL_URL_DESCRIPTION' | translate }} +
+