mirror of
https://gitlab.com/futo-org/fcast.git
synced 2025-06-24 21:25:23 +00:00
Added support for request headers.
This commit is contained in:
parent
9b14e665b5
commit
28886045af
7 changed files with 331 additions and 59 deletions
|
@ -6,6 +6,7 @@ use clap::{App, Arg, SubCommand};
|
||||||
use tiny_http::{Server, Response, ListenAddr, Header};
|
use tiny_http::{Server, Response, ListenAddr, Header};
|
||||||
use tungstenite::stream::MaybeTlsStream;
|
use tungstenite::stream::MaybeTlsStream;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -102,6 +103,14 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.default_value("1")
|
.default_value("1")
|
||||||
.takes_value(true)
|
.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")
|
.subcommand(SubCommand::with_name("seek")
|
||||||
.about("Seek to a timestamp")
|
.about("Seek to a timestamp")
|
||||||
|
@ -177,18 +186,41 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
println!("Connection established.");
|
println!("Connection established.");
|
||||||
|
|
||||||
|
|
||||||
let mut join_handle: Option<JoinHandle<Result<(), String>>> = None;
|
let mut join_handle: Option<JoinHandle<Result<(), String>>> = None;
|
||||||
if let Some(play_matches) = matches.subcommand_matches("play") {
|
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::<f64>().ok(),
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let speed = match play_matches.value_of("speed") {
|
||||||
|
Some(s) => s.parse::<f64>().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::<HashMap<String, String>>());
|
||||||
|
|
||||||
let file_path = play_matches.value_of("file");
|
let file_path = play_matches.value_of("file");
|
||||||
|
|
||||||
let mut play_message = if let Some(file_path) = file_path {
|
let mut play_message = if let Some(file_path) = file_path {
|
||||||
match local_ip {
|
match local_ip {
|
||||||
Some(lip) => {
|
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 running = Arc::new(AtomicBool::new(true));
|
||||||
let r = running.clone();
|
let r = running.clone();
|
||||||
|
|
||||||
|
@ -208,24 +240,16 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
mime_type,
|
mime_type,
|
||||||
Some(url),
|
Some(url),
|
||||||
None,
|
None,
|
||||||
match play_matches.value_of("timestamp") {
|
time,
|
||||||
Some(s) => s.parse::<f64>().ok(),
|
speed,
|
||||||
_ => None
|
headers
|
||||||
},
|
|
||||||
match play_matches.value_of("speed") {
|
|
||||||
Some(s) => s.parse::<f64>().ok(),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
_ => return Err("Local IP was not able to be resolved.".into())
|
_ => return Err("Local IP was not able to be resolved.".into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PlayMessage::new(
|
PlayMessage::new(
|
||||||
match play_matches.value_of("mime_type") {
|
mime_type,
|
||||||
Some(s) => s.to_string(),
|
|
||||||
_ => return Err("MIME type is required.".into())
|
|
||||||
},
|
|
||||||
match play_matches.value_of("url") {
|
match play_matches.value_of("url") {
|
||||||
Some(s) => Some(s.to_string()),
|
Some(s) => Some(s.to_string()),
|
||||||
_ => None
|
_ => None
|
||||||
|
@ -234,14 +258,9 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Some(s) => Some(s.to_string()),
|
Some(s) => Some(s.to_string()),
|
||||||
_ => None
|
_ => None
|
||||||
},
|
},
|
||||||
match play_matches.value_of("timestamp") {
|
time,
|
||||||
Some(s) => s.parse::<f64>().ok(),
|
speed,
|
||||||
_ => None
|
headers
|
||||||
},
|
|
||||||
match play_matches.value_of("speed") {
|
|
||||||
Some(s) => s.parse::<f64>().ok(),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
|
@ -6,12 +8,13 @@ pub struct PlayMessage {
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub content: Option<String>,
|
pub content: Option<String>,
|
||||||
pub time: Option<f64>,
|
pub time: Option<f64>,
|
||||||
pub speed: Option<f64>
|
pub speed: Option<f64>,
|
||||||
|
pub headers: Option<HashMap<String, String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayMessage {
|
impl PlayMessage {
|
||||||
pub fn new(container: String, url: Option<String>, content: Option<String>, time: Option<f64>, speed: Option<f64>) -> Self {
|
pub fn new(container: String, url: Option<String>, content: Option<String>, time: Option<f64>, speed: Option<f64>, headers: Option<HashMap<String, String>>) -> Self {
|
||||||
Self { container, url, content, time, speed }
|
Self { container, url, content, time, speed, headers }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
175
receivers/electron/package-lock.json
generated
175
receivers/electron/package-lock.json
generated
|
@ -1,17 +1,20 @@
|
||||||
{
|
{
|
||||||
"name": "fcast-receiver",
|
"name": "fcast-receiver",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fcast-receiver",
|
"name": "fcast-receiver",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bufferutil": "^4.0.8",
|
"bufferutil": "^4.0.8",
|
||||||
|
"http": "^0.0.1-security",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
|
"url": "^0.11.3",
|
||||||
|
"uuid": "^9.0.1",
|
||||||
"ws": "^8.14.2"
|
"ws": "^8.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1966,6 +1969,19 @@
|
||||||
"node": ">=8"
|
"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": {
|
"node_modules/callsites": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
@ -2269,6 +2285,19 @@
|
||||||
"node": ">=10"
|
"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": {
|
"node_modules/define-properties": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||||
|
@ -2726,10 +2755,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
"dev": true
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gensync": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
|
@ -2749,15 +2780,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.2",
|
||||||
"has": "^1.0.3",
|
"has-proto": "^1.0.1",
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3",
|
||||||
|
"hasown": "^2.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
@ -2872,6 +2902,17 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/got": {
|
||||||
"version": "11.8.6",
|
"version": "11.8.6",
|
||||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
|
||||||
|
@ -2928,8 +2969,6 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||||
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.1.1"
|
"get-intrinsic": "^1.1.1"
|
||||||
},
|
},
|
||||||
|
@ -2937,12 +2976,21 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/has-symbols": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
|
@ -2950,12 +2998,28 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/html-escaper": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/http-cache-semantics": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||||
|
@ -4362,6 +4426,14 @@
|
||||||
"node": ">=8"
|
"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": {
|
"node_modules/object-keys": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
|
@ -4650,6 +4722,20 @@
|
||||||
"node": ">=10.13.0"
|
"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": {
|
"node_modules/quick-lru": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
"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": {
|
"node_modules/shallow-clone": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
||||||
|
@ -4902,6 +5002,19 @@
|
||||||
"node": ">=8"
|
"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": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
@ -5359,6 +5472,20 @@
|
||||||
"punycode": "^2.1.0"
|
"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": {
|
"node_modules/utf-8-validate": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz",
|
||||||
|
@ -5373,6 +5500,18 @@
|
||||||
"node": ">=6.14.2"
|
"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": {
|
"node_modules/v8-to-istanbul": {
|
||||||
"version": "9.2.0",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "fcast-receiver",
|
"name": "fcast-receiver",
|
||||||
"version": "1.0.10",
|
"version": "1.0.11",
|
||||||
"description": "An application implementing a FCast receiver.",
|
"description": "An application implementing a FCast receiver.",
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"author": "Koen Jeukendrup",
|
"author": "Koen Jeukendrup",
|
||||||
|
@ -27,8 +27,11 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bufferutil": "^4.0.8",
|
"bufferutil": "^4.0.8",
|
||||||
|
"http": "^0.0.1-security",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
|
"url": "^0.11.3",
|
||||||
|
"uuid": "^9.0.1",
|
||||||
"ws": "^8.14.2"
|
"ws": "^8.14.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import { BrowserWindow, ipcMain, IpcMainEvent, nativeImage, Tray, Menu, dialog } from 'electron';
|
import { BrowserWindow, ipcMain, IpcMainEvent, nativeImage, Tray, Menu, dialog } from 'electron';
|
||||||
import path = require('path');
|
|
||||||
import { TcpListenerService } from './TcpListenerService';
|
import { TcpListenerService } from './TcpListenerService';
|
||||||
import { PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage } from './Packets';
|
import { PlayMessage, PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage } from './Packets';
|
||||||
import { DiscoveryService } from './DiscoveryService';
|
import { DiscoveryService } from './DiscoveryService';
|
||||||
import { Updater } from './Updater';
|
import { Updater } from './Updater';
|
||||||
import { WebSocketListenerService } from './WebSocketListenerService';
|
import { WebSocketListenerService } from './WebSocketListenerService';
|
||||||
import * as os from 'os';
|
|
||||||
import { Opcode } from './FCastSession';
|
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 {
|
export default class Main {
|
||||||
static shouldOpenMainWindow = true;
|
static shouldOpenMainWindow = true;
|
||||||
|
@ -19,6 +23,9 @@ export default class Main {
|
||||||
static tray: Tray;
|
static tray: Tray;
|
||||||
static key: string = null;
|
static key: string = null;
|
||||||
static cert: string = null;
|
static cert: string = null;
|
||||||
|
static proxyServer: http.Server;
|
||||||
|
static proxyServerAddress: AddressInfo;
|
||||||
|
static proxiedFiles: Map<string, { url: string, headers: { [key: string]: string } }> = new Map();
|
||||||
|
|
||||||
private static createTray() {
|
private static createTray() {
|
||||||
const icon = (process.platform === 'win32') ? path.join(__dirname, 'app.ico') : path.join(__dirname, 'app.png');
|
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];
|
const listeners = [Main.tcpListenerService, Main.webSocketListenerService];
|
||||||
listeners.forEach(l => {
|
listeners.forEach(l => {
|
||||||
l.emitter.on("play", (message) => {
|
l.emitter.on("play", async (message) => {
|
||||||
if (Main.playerWindow == null) {
|
if (Main.playerWindow == null) {
|
||||||
Main.playerWindow = new BrowserWindow({
|
Main.playerWindow = new BrowserWindow({
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
|
@ -119,14 +126,14 @@ export default class Main {
|
||||||
Main.playerWindow.show();
|
Main.playerWindow.show();
|
||||||
|
|
||||||
Main.playerWindow.loadFile(path.join(__dirname, 'player/index.html'));
|
Main.playerWindow.loadFile(path.join(__dirname, 'player/index.html'));
|
||||||
Main.playerWindow.on('ready-to-show', () => {
|
Main.playerWindow.on('ready-to-show', async () => {
|
||||||
Main.playerWindow?.webContents?.send("play", message);
|
Main.playerWindow?.webContents?.send("play", await Main.proxyPlayIfRequired(message));
|
||||||
});
|
});
|
||||||
Main.playerWindow.on('closed', () => {
|
Main.playerWindow.on('closed', () => {
|
||||||
Main.playerWindow = null;
|
Main.playerWindow = null;
|
||||||
});
|
});
|
||||||
} else {
|
} 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<void> {
|
||||||
|
return new Promise<void>((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<PlayMessage> {
|
||||||
|
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<string> {
|
||||||
|
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() {
|
static getAllIPv4Addresses() {
|
||||||
const interfaces = os.networkInterfaces();
|
const interfaces = os.networkInterfaces();
|
||||||
const ipv4Addresses: string[] = [];
|
const ipv4Addresses: string[] = [];
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
export class PlayMessage {
|
export class PlayMessage {
|
||||||
constructor(
|
constructor(
|
||||||
public container: String,
|
public container: string,
|
||||||
public url: String = null,
|
public url: string = null,
|
||||||
public content: String = null,
|
public content: string = null,
|
||||||
public time: number = 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 {
|
export class PlaybackErrorMessage {
|
||||||
constructor(
|
constructor(
|
||||||
public message: String
|
public message: string
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,13 @@ function toggleFullScreen(ev) {
|
||||||
const options = {
|
const options = {
|
||||||
textTrackSettings: false
|
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 player = videojs("video-player", options, function onPlayerReady() {
|
||||||
const fullScreenControls = document.getElementsByClassName("vjs-fullscreen-control");
|
const fullScreenControls = document.getElementsByClassName("vjs-fullscreen-control");
|
||||||
for (let i = 0; i < fullScreenControls.length; i++) {
|
for (let i = 0; i < fullScreenControls.length; i++) {
|
||||||
|
@ -58,6 +65,7 @@ player.on('error', () => { window.electronAPI.sendPlaybackError({
|
||||||
|
|
||||||
window.electronAPI.onPlay((_event, value) => {
|
window.electronAPI.onPlay((_event, value) => {
|
||||||
console.log("Handle play message renderer", value);
|
console.log("Handle play message renderer", value);
|
||||||
|
customHeaders = value.headers;
|
||||||
|
|
||||||
if (value.content) {
|
if (value.content) {
|
||||||
player.src({ type: value.container, src: `data:${value.container};base64,` + window.btoa(value.content) });
|
player.src({ type: value.container, src: `data:${value.container};base64,` + window.btoa(value.content) });
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue