2
0
mirror of https://github.com/ad1217/PrinterStatus synced 2024-11-21 15:03:48 -05:00

server: Switch from generic http-proxy to mjpeg-proxy for webcam streams

This commit is contained in:
Adam Goldsmith 2021-11-03 19:36:25 -04:00
parent 5d19c77dce
commit ecd5185dc4
5 changed files with 44 additions and 102 deletions

101
server/package-lock.json generated
View File

@ -7,15 +7,14 @@
"dependencies": { "dependencies": {
"express": "^4.17.1", "express": "^4.17.1",
"express-ws": "^5.0.2", "express-ws": "^5.0.2",
"http-proxy": "^1.18.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"mjpeg-proxy": "^0.3.0",
"node-fetch": "^2.6.5", "node-fetch": "^2.6.5",
"ws": "^8.2.3" "ws": "^8.2.3"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.8", "@types/express": "^4.17.8",
"@types/express-ws": "^3.0.0", "@types/express-ws": "^3.0.0",
"@types/http-proxy": "^1.17.4",
"@types/js-yaml": "^4.0.3", "@types/js-yaml": "^4.0.3",
"@types/node": "^16.10.3", "@types/node": "^16.10.3",
"@types/node-fetch": "^2.5.12", "@types/node-fetch": "^2.5.12",
@ -122,15 +121,6 @@
"@types/ws": "*" "@types/ws": "*"
} }
}, },
"node_modules/@types/http-proxy": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
"integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/js-yaml": { "node_modules/@types/js-yaml": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz",
@ -388,11 +378,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"node_modules/express": { "node_modules/express": {
"version": "4.17.1", "version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
@ -484,25 +469,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": { "node_modules/form-data": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
@ -548,19 +514,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/http-proxy": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dependencies": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -653,6 +606,14 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mjpeg-proxy": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/mjpeg-proxy/-/mjpeg-proxy-0.3.0.tgz",
"integrity": "sha512-XpdOIPIw/as8djKkJyiT1YaxlM10vqA/KJmymnVl+bZcZYdLrNPYJjkJt+x+5I77vLxyD8QmDbmJrpb63YyRJw==",
"engines": {
"node": ">=10"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -743,11 +704,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -1053,15 +1009,6 @@
"@types/ws": "*" "@types/ws": "*"
} }
}, },
"@types/http-proxy": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
"integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/js-yaml": { "@types/js-yaml": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz",
@ -1274,11 +1221,6 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
}, },
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"express": { "express": {
"version": "4.17.1", "version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
@ -1346,11 +1288,6 @@
"unpipe": "~1.0.0" "unpipe": "~1.0.0"
} }
}, },
"follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g=="
},
"form-data": { "form-data": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
@ -1384,16 +1321,6 @@
"toidentifier": "1.0.0" "toidentifier": "1.0.0"
} }
}, },
"http-proxy": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"requires": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -1459,6 +1386,11 @@
"mime-db": "1.50.0" "mime-db": "1.50.0"
} }
}, },
"mjpeg-proxy": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/mjpeg-proxy/-/mjpeg-proxy-0.3.0.tgz",
"integrity": "sha512-XpdOIPIw/as8djKkJyiT1YaxlM10vqA/KJmymnVl+bZcZYdLrNPYJjkJt+x+5I77vLxyD8QmDbmJrpb63YyRJw=="
},
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -1525,11 +1457,6 @@
"unpipe": "1.0.0" "unpipe": "1.0.0"
} }
}, },
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",

View File

@ -2,7 +2,6 @@
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.8", "@types/express": "^4.17.8",
"@types/express-ws": "^3.0.0", "@types/express-ws": "^3.0.0",
"@types/http-proxy": "^1.17.4",
"@types/js-yaml": "^4.0.3", "@types/js-yaml": "^4.0.3",
"@types/node": "^16.10.3", "@types/node": "^16.10.3",
"@types/node-fetch": "^2.5.12", "@types/node-fetch": "^2.5.12",
@ -13,12 +12,12 @@
"dependencies": { "dependencies": {
"express": "^4.17.1", "express": "^4.17.1",
"express-ws": "^5.0.2", "express-ws": "^5.0.2",
"http-proxy": "^1.18.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"mjpeg-proxy": "^0.3.0",
"node-fetch": "^2.6.5", "node-fetch": "^2.6.5",
"ws": "^8.2.3" "ws": "^8.2.3"
}, },
"scripts": { "scripts": {
"start": "ts-node ./src/server.ts" "start": "ts-node --files ./src/server.ts"
} }
} }

View File

@ -1,5 +1,7 @@
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
/// <reference path="mjpeg-proxy.d.ts"/>
import { MjpegProxy } from 'mjpeg-proxy';
import {ExtendedMessage} from '../../types/messages'; import {ExtendedMessage} from '../../types/messages';
import * as octoprint from '../../types/octoprint'; import * as octoprint from '../../types/octoprint';
@ -9,8 +11,8 @@ const PING_TIME = 10000;
type Timeout = ReturnType<typeof setTimeout>; type Timeout = ReturnType<typeof setTimeout>;
export default class OctoprintConnection { export default class OctoprintConnection {
public webcamURL?: URL;
public name?: string; public name?: string;
public webcamProxy?: MjpegProxy;
protected lastStatus?: ExtendedMessage; protected lastStatus?: ExtendedMessage;
constructor( constructor(
@ -34,7 +36,11 @@ export default class OctoprintConnection {
async connect_websocket() { async connect_websocket() {
const settings = await this.api_get('settings'); const settings = await this.api_get('settings');
this.webcamURL = new URL(settings.webcam.streamUrl, this.address); const webcamURL = new URL(settings.webcam.streamUrl, this.address);
// TODO: handle recreating proxy on URL change
if (this.webcamProxy === undefined) {
this.webcamProxy = new MjpegProxy(webcamURL.toString());
}
this.name = settings.appearance.name; this.name = settings.appearance.name;
// do passive login to get a session key from the API key // do passive login to get a session key from the API key

12
server/src/mjpeg-proxy.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
declare module 'mjpeg-proxy' {
import {Request, Response} from 'express';
export class MjpegProxy {
mjpegOptions: URL;
constructor(mjpegUrl: string | URL);
proxyRequest(
req: Request,
res: Response
): void;
}
}

View File

@ -1,7 +1,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as express from 'express'; import * as express from 'express';
import * as httpProxy from 'http-proxy';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
import * as expressWs from 'express-ws'; import * as expressWs from 'express-ws';
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
@ -22,11 +21,6 @@ const config: configuration = yaml.load(
fs.readFileSync('config.yaml', 'utf8') fs.readFileSync('config.yaml', 'utf8')
) as configuration; ) as configuration;
const proxy = httpProxy.createProxyServer({});
proxy.on('error', function (e) {
console.error('Proxy failed:');
console.error(e);
});
let octoprintConnections: OctoPrintConnection[] = []; let octoprintConnections: OctoPrintConnection[] = [];
function broadcast(data: WebSocket.Data) { function broadcast(data: WebSocket.Data) {
@ -52,9 +46,8 @@ app.get('/webcam/:printer', (req, res) => {
let printer: OctoPrintConnection | undefined = octoprintConnections.find( let printer: OctoPrintConnection | undefined = octoprintConnections.find(
(op) => op.slug === req.params.printer (op) => op.slug === req.params.printer
); );
if (printer?.webcamURL) { if (printer?.webcamProxy) {
req.url = ''; // truncate the url for passing to the proxy return printer.webcamProxy.proxyRequest(req, res);
proxy.web(req, res, { target: printer.webcamURL.toString() });
} else res.status(404).send('Not Found: Printer not known or has no webcam.'); } else res.status(404).send('Not Found: Printer not known or has no webcam.');
}); });
@ -63,7 +56,12 @@ app.listen(PORT);
function initPrinters() { function initPrinters() {
octoprintConnections = Object.entries(config.printers).map( octoprintConnections = Object.entries(config.printers).map(
([slug, printer]) => ([slug, printer]) =>
new OctoPrintConnection(slug, printer.address, printer.apikey, broadcastPayload) new OctoPrintConnection(
slug,
printer.address,
printer.apikey,
broadcastPayload
)
); );
} }