diff --git a/clients/terminal/src/main.rs b/clients/terminal/src/main.rs index 1505d4a..b71373f 100644 --- a/clients/terminal/src/main.rs +++ b/clients/terminal/src/main.rs @@ -6,6 +6,7 @@ use clap::{App, Arg, SubCommand}; use tiny_http::{Server, Response, ListenAddr, Header}; use tungstenite::stream::MaybeTlsStream; use url::Url; +use std::collections::HashMap; use std::net::IpAddr; use std::str::FromStr; use std::sync::Mutex; @@ -102,6 +103,14 @@ fn run() -> Result<(), Box> { .default_value("1") .takes_value(true) ) + .arg(Arg::with_name("header") + .short('H') + .long("header") + .value_name("HEADER") + .help("Custom request headers in key:value format") + .required(false) + .multiple_occurrences(true) + ) ) .subcommand(SubCommand::with_name("seek") .about("Seek to a timestamp") @@ -177,18 +186,41 @@ fn run() -> Result<(), Box> { println!("Connection established."); + let mut join_handle: Option>> = None; if let Some(play_matches) = matches.subcommand_matches("play") { + let mime_type = match play_matches.value_of("mime_type") { + Some(s) => s.to_string(), + _ => return Err("MIME type is required.".into()) + }; + + let time = match play_matches.value_of("timestamp") { + Some(s) => s.parse::().ok(), + _ => None + }; + + let speed = match play_matches.value_of("speed") { + Some(s) => s.parse::().ok(), + _ => None + }; + + let headers = play_matches.values_of("header") + .map(|values| values + .filter_map(|s| { + let mut parts = s.splitn(2, ':'); + if let (Some(key), Some(value)) = (parts.next(), parts.next()) { + Some((key.trim().to_string(), value.trim().to_string())) + } else { + None + } + } + ).collect::>()); + let file_path = play_matches.value_of("file"); let mut play_message = if let Some(file_path) = file_path { match local_ip { Some(lip) => { - let mime_type = match play_matches.value_of("mime_type") { - Some(s) => s.to_string(), - _ => return Err("MIME type is required.".into()) - }; - let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -208,24 +240,16 @@ fn run() -> Result<(), Box> { mime_type, Some(url), None, - match play_matches.value_of("timestamp") { - Some(s) => s.parse::().ok(), - _ => None - }, - match play_matches.value_of("speed") { - Some(s) => s.parse::().ok(), - _ => None - } + time, + speed, + headers ) }, _ => return Err("Local IP was not able to be resolved.".into()) } } else { PlayMessage::new( - match play_matches.value_of("mime_type") { - Some(s) => s.to_string(), - _ => return Err("MIME type is required.".into()) - }, + mime_type, match play_matches.value_of("url") { Some(s) => Some(s.to_string()), _ => None @@ -234,14 +258,9 @@ fn run() -> Result<(), Box> { Some(s) => Some(s.to_string()), _ => None }, - match play_matches.value_of("timestamp") { - Some(s) => s.parse::().ok(), - _ => None - }, - match play_matches.value_of("speed") { - Some(s) => s.parse::().ok(), - _ => None - } + time, + speed, + headers ) }; diff --git a/clients/terminal/src/models.rs b/clients/terminal/src/models.rs index 08ebfbd..f3a5d38 100644 --- a/clients/terminal/src/models.rs +++ b/clients/terminal/src/models.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use serde::{Serialize, Deserialize}; #[derive(Serialize, Debug)] @@ -6,12 +8,13 @@ pub struct PlayMessage { pub url: Option, pub content: Option, pub time: Option, - pub speed: Option + pub speed: Option, + pub headers: Option> } impl PlayMessage { - pub fn new(container: String, url: Option, content: Option, time: Option, speed: Option) -> Self { - Self { container, url, content, time, speed } + pub fn new(container: String, url: Option, content: Option, time: Option, speed: Option, headers: Option>) -> Self { + Self { container, url, content, time, speed, headers } } } diff --git a/receivers/electron/package-lock.json b/receivers/electron/package-lock.json index 8cc42ae..59d9217 100644 --- a/receivers/electron/package-lock.json +++ b/receivers/electron/package-lock.json @@ -1,17 +1,20 @@ { "name": "fcast-receiver", - "version": "1.0.9", + "version": "1.0.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fcast-receiver", - "version": "1.0.9", + "version": "1.0.10", "license": "MIT", "dependencies": { "bufferutil": "^4.0.8", + "http": "^0.0.1-security", "https": "^1.0.0", "qrcode": "^1.5.3", + "url": "^0.11.3", + "uuid": "^9.0.1", "ws": "^8.14.2" }, "devDependencies": { @@ -1966,6 +1969,19 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2269,6 +2285,19 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -2726,10 +2755,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -2749,15 +2780,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, - "optional": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2872,6 +2902,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", @@ -2928,8 +2969,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "optional": true, "dependencies": { "get-intrinsic": "^1.1.1" }, @@ -2937,12 +2976,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "optional": true, "engines": { "node": ">= 0.4" }, @@ -2950,12 +2998,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/http": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", + "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -4362,6 +4426,14 @@ "node": ">=8" } }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -4650,6 +4722,20 @@ "node": ">=10.13.0" } }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -4869,6 +4955,20 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -4902,6 +5002,19 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -5359,6 +5472,20 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", + "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.11.2" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, "node_modules/utf-8-validate": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz", @@ -5373,6 +5500,18 @@ "node": ">=6.14.2" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", diff --git a/receivers/electron/package.json b/receivers/electron/package.json index 9e2e1fc..00da86d 100644 --- a/receivers/electron/package.json +++ b/receivers/electron/package.json @@ -1,6 +1,6 @@ { "name": "fcast-receiver", - "version": "1.0.10", + "version": "1.0.11", "description": "An application implementing a FCast receiver.", "main": "dist/bundle.js", "author": "Koen Jeukendrup", @@ -27,8 +27,11 @@ }, "dependencies": { "bufferutil": "^4.0.8", + "http": "^0.0.1-security", "https": "^1.0.0", "qrcode": "^1.5.3", + "url": "^0.11.3", + "uuid": "^9.0.1", "ws": "^8.14.2" } } diff --git a/receivers/electron/src/Main.ts b/receivers/electron/src/Main.ts index a37df99..967932f 100644 --- a/receivers/electron/src/Main.ts +++ b/receivers/electron/src/Main.ts @@ -1,12 +1,16 @@ import { BrowserWindow, ipcMain, IpcMainEvent, nativeImage, Tray, Menu, dialog } from 'electron'; -import path = require('path'); import { TcpListenerService } from './TcpListenerService'; -import { PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage } from './Packets'; +import { PlayMessage, PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage } from './Packets'; import { DiscoveryService } from './DiscoveryService'; import { Updater } from './Updater'; import { WebSocketListenerService } from './WebSocketListenerService'; -import * as os from 'os'; import { Opcode } from './FCastSession'; +import * as os from 'os'; +import * as path from 'path'; +import * as http from 'http'; +import * as url from 'url'; +import { AddressInfo } from 'ws'; +import { v4 as uuidv4 } from 'uuid'; export default class Main { static shouldOpenMainWindow = true; @@ -19,6 +23,9 @@ export default class Main { static tray: Tray; static key: string = null; static cert: string = null; + static proxyServer: http.Server; + static proxyServerAddress: AddressInfo; + static proxiedFiles: Map = new Map(); private static createTray() { const icon = (process.platform === 'win32') ? path.join(__dirname, 'app.ico') : path.join(__dirname, 'app.png'); @@ -105,7 +112,7 @@ export default class Main { const listeners = [Main.tcpListenerService, Main.webSocketListenerService]; listeners.forEach(l => { - l.emitter.on("play", (message) => { + l.emitter.on("play", async (message) => { if (Main.playerWindow == null) { Main.playerWindow = new BrowserWindow({ fullscreen: true, @@ -119,14 +126,14 @@ export default class Main { Main.playerWindow.show(); Main.playerWindow.loadFile(path.join(__dirname, 'player/index.html')); - Main.playerWindow.on('ready-to-show', () => { - Main.playerWindow?.webContents?.send("play", message); + Main.playerWindow.on('ready-to-show', async () => { + Main.playerWindow?.webContents?.send("play", await Main.proxyPlayIfRequired(message)); }); Main.playerWindow.on('closed', () => { Main.playerWindow = null; }); } else { - Main.playerWindow?.webContents?.send("play", message); + Main.playerWindow?.webContents?.send("play", await Main.proxyPlayIfRequired(message)); } }); @@ -179,6 +186,98 @@ export default class Main { } } + + private static setupProxyServer(): Promise { + return new Promise((resolve, reject) => { + try { + console.log(`Proxy server starting`); + + const port = 0; + Main.proxyServer = http.createServer((req, res) => { + console.log(`Request received`); + const requestUrl = `http://${req.headers.host}${req.url}`; + + const proxyInfo = Main.proxiedFiles.get(requestUrl); + + if (!proxyInfo) { + res.writeHead(404); + res.end('Not found'); + return; + } + + const omitHeaders = new Set([ + 'host', + 'connection', + 'keep-alive', + 'proxy-authenticate', + 'proxy-authorization', + 'te', + 'trailers', + 'transfer-encoding', + 'upgrade' + ]); + + const filteredHeaders = Object.fromEntries(Object.entries(req.headers) + .filter(([key]) => !omitHeaders.has(key.toLowerCase())) + .map(([key, value]) => [key, Array.isArray(value) ? value.join(', ') : value])); + + const parsedUrl = url.parse(proxyInfo.url); + const options: http.RequestOptions = { + ... parsedUrl, + method: req.method, + headers: { ...filteredHeaders, ...proxyInfo.headers } + }; + + const proxyReq = http.request(options, (proxyRes) => { + res.writeHead(proxyRes.statusCode, proxyRes.headers); + proxyRes.pipe(res, { end: true }); + }); + + req.pipe(proxyReq, { end: true }); + proxyReq.on('error', (e) => { + console.error(`Problem with request: ${e.message}`); + res.writeHead(500); + res.end(); + }); + }); + Main.proxyServer.on('error', e => { + reject(e); + }); + Main.proxyServer.listen(port, '127.0.0.1', () => { + Main.proxyServerAddress = Main.proxyServer.address() as AddressInfo; + console.log(`Proxy server running at http://127.0.0.1:${Main.proxyServerAddress.port}/`); + resolve(); + }); + } catch (e) { + reject(e); + } + }); + } + + static streamingMediaTypes = [ + "application/vnd.apple.mpegurl", + "application/x-mpegURL", + "application/dash+xml" + ]; + + static async proxyPlayIfRequired(message: PlayMessage): Promise { + if (message.headers && message.url && !Main.streamingMediaTypes.find(v => v === message.container.toLocaleLowerCase())) { + return { ...message, url: await Main.proxyFile(message.url, message.headers) }; + } + return message; + } + + static async proxyFile(url: string, headers: { [key: string]: string }): Promise { + if (!Main.proxyServer) { + await Main.setupProxyServer(); + } + + const proxiedUrl = `http://127.0.0.1:${Main.proxyServerAddress.port}/${uuidv4()}`; + console.log("Proxied url", { proxiedUrl, url, headers }); + Main.proxiedFiles.set(proxiedUrl, { url: url, headers: headers }); + return proxiedUrl; + } + static getAllIPv4Addresses() { const interfaces = os.networkInterfaces(); const ipv4Addresses: string[] = []; diff --git a/receivers/electron/src/Packets.ts b/receivers/electron/src/Packets.ts index f907493..267b6d3 100644 --- a/receivers/electron/src/Packets.ts +++ b/receivers/electron/src/Packets.ts @@ -1,10 +1,11 @@ export class PlayMessage { constructor( - public container: String, - public url: String = null, - public content: String = null, + public container: string, + public url: string = null, + public content: string = null, public time: number = null, - public speed: number = null + public speed: number = null, + public headers: { [key: string]: string } = null ) {} } @@ -26,7 +27,7 @@ export class PlaybackUpdateMessage { export class PlaybackErrorMessage { constructor( - public message: String + public message: string ) {} } diff --git a/receivers/electron/src/player/renderer.js b/receivers/electron/src/player/renderer.js index b2d0be1..6d1605e 100644 --- a/receivers/electron/src/player/renderer.js +++ b/receivers/electron/src/player/renderer.js @@ -5,6 +5,13 @@ function toggleFullScreen(ev) { const options = { textTrackSettings: false }; + +let customHeaders = null; +videojs.Vhs.xhr.beforeRequest = function(options) { + options.headers = { ... options.headers, ... customHeaders }; + return options; +}; + const player = videojs("video-player", options, function onPlayerReady() { const fullScreenControls = document.getElementsByClassName("vjs-fullscreen-control"); for (let i = 0; i < fullScreenControls.length; i++) { @@ -58,6 +65,7 @@ player.on('error', () => { window.electronAPI.sendPlaybackError({ window.electronAPI.onPlay((_event, value) => { console.log("Handle play message renderer", value); + customHeaders = value.headers; if (value.content) { player.src({ type: value.container, src: `data:${value.container};base64,` + window.btoa(value.content) });