Phone remote: LAN web page + WebSocket, QR pairing, blackout/freeze
remote-server.js: http + ws server (off by default, started from the panel,
port 8787 or next free), every request gated by a secret key embedded in a
QR code; the key persists in userData/remote.json so a phone's home-screen
shortcut survives restarts, 'Nuovo codice' revokes it. Phones send commands
that the panel executes through its own functions (transport, track pick,
effect next/prev/random/family/sequence, Auto VJ, pads, mapping on + visible
zones, blackout, freeze) and the panel pushes a compact live state at ~6 Hz
while a phone is connected (track + progress, effect, sequence, pads,
zones, meters, BPM). New panic controls in output and aux windows: blackout
(fade to black above every layer) and freeze (hold the last frame); also
available as panel buttons. src/remote.{html,css,js} is the touch UI in the
Neon Grid style, installable to the home screen, auto-reconnecting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVMQ2JhsJ77pGKqcvvFHqc
This commit is contained in:
1 parent
f246b8bdb8
commit
ab1ab89127
16 files changed
+973
-21
No files matched your search
@@ -3,6 +3,8 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { execFile } = require('child_process');
|
||||
const { pathToFileURL } = require('url');
|
||||
const crypto = require('crypto');
|
||||
const { RemoteServer } = require('./remote-server');
|
||||
|
||||
// djvres:// serves the MediaPipe WASM runtime and the bundled pose model to
|
||||
// the renderer: fetch() does not work on file:// URLs, and the pose tracker
|
||||
@@ -156,6 +158,7 @@ function createWindows() {
|
||||
controlWin = null;
|
||||
for (const win of auxWins.values()) { if (!win.isDestroyed()) win.destroy(); }
|
||||
auxWins.clear();
|
||||
remote.stop();
|
||||
if (outputWin) outputWin.close();
|
||||
app.quit();
|
||||
});
|
||||
@@ -412,6 +415,39 @@ ipcMain.handle('rec:openFolder', () => {
|
||||
return dir;
|
||||
});
|
||||
|
||||
// --- Phone remote (LAN web page + WebSocket) ---
|
||||
// Commands from phones go to the panel, which runs them through its own code
|
||||
// paths; the panel pushes a compact state back for the phones to display.
|
||||
const remoteCfgFile = () => path.join(app.getPath('userData'), 'remote.json');
|
||||
function remoteCfg() {
|
||||
try { return JSON.parse(fs.readFileSync(remoteCfgFile(), 'utf8')); } catch (e) { return {}; }
|
||||
}
|
||||
function saveRemoteCfg(c) { try { fs.writeFileSync(remoteCfgFile(), JSON.stringify(c)); } catch (e) {} }
|
||||
const remote = new RemoteServer({
|
||||
srcDir: path.join(__dirname, 'src'),
|
||||
onCommand: (m) => { if (controlWin && !controlWin.isDestroyed()) controlWin.webContents.send('remote', m); }
|
||||
});
|
||||
ipcMain.handle('remote:start', async () => {
|
||||
const c = remoteCfg();
|
||||
// the key survives restarts so a phone's home-screen shortcut keeps working
|
||||
if (!c.key) c.key = crypto.randomBytes(6).toString('hex');
|
||||
c.on = true; saveRemoteCfg(c);
|
||||
try { return await remote.start(8787, c.key); } catch (e) { return { on: false, error: e.message }; }
|
||||
});
|
||||
ipcMain.handle('remote:stop', () => {
|
||||
remote.stop();
|
||||
const c = remoteCfg(); c.on = false; saveRemoteCfg(c);
|
||||
return { on: false };
|
||||
});
|
||||
ipcMain.handle('remote:newKey', async () => {
|
||||
// revoke: every phone that had the old code is disconnected and locked out
|
||||
remote.stop();
|
||||
const c = remoteCfg(); c.key = crypto.randomBytes(6).toString('hex'); c.on = true; saveRemoteCfg(c);
|
||||
try { return await remote.start(8787, c.key); } catch (e) { return { on: false, error: e.message }; }
|
||||
});
|
||||
ipcMain.handle('remote:info', async () => (remote.running ? remote.info() : { on: false, wanted: !!remoteCfg().on }));
|
||||
ipcMain.on('remote:state', (_e, state) => remote.broadcast(state));
|
||||
|
||||
// --- Pad bank persistence ---
|
||||
const padsFile = () => path.join(app.getPath('userData'), 'pads.json');
|
||||
ipcMain.handle('pads:load', () => {
|
||||
|
||||
Reference in new issue
Block a user