Skip to content

Commit cc54d23

Browse files
authored
Allow request headers to be specified (#5)
This lets the user specify one or more `--header`/`-H` flags to the `record` command.
1 parent 67472a2 commit cc54d23

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

src/main.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use std::net::IpAddr;
22
use std::path::PathBuf;
3+
use std::str::FromStr;
34

45
use clap::{Parser, Subcommand};
6+
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
57
use tokio::spawn;
68
use tokio_util::sync::CancellationToken;
79
use url::Url;
@@ -71,6 +73,11 @@ enum CliCommand {
7173
/// - If unset, the recording stops at the last segment of the last media playlist.
7274
#[arg(long, allow_hyphen_values = true, verbatim_doc_comment)]
7375
end: Option<f32>,
76+
/// Custom HTTP header to send with all requests.
77+
///
78+
/// Can be specified multiple times. Format: "Name: Value"
79+
#[arg(short = 'H', long = "header", value_name = "Name: Value", value_parser = parse_header)]
80+
headers: Vec<(HeaderName, HeaderValue)>,
7481
},
7582
/// Replay a HLS VOD or live stream.
7683
Replay {
@@ -113,6 +120,7 @@ async fn main() {
113120
bandwidth,
114121
start,
115122
end,
123+
headers,
116124
} => {
117125
let variant_select = if let Some(bandwidth) = bandwidth {
118126
VariantSelectOptions::Bandwidth(bandwidth)
@@ -126,6 +134,7 @@ async fn main() {
126134
audio,
127135
video,
128136
subtitle,
137+
headers: headers.into_iter().collect::<HeaderMap>(),
129138
};
130139
let token = CancellationToken::new();
131140
let record_task = {
@@ -166,3 +175,14 @@ async fn main() {
166175
}
167176
}
168177
}
178+
179+
fn parse_header(s: &str) -> Result<(HeaderName, HeaderValue), String> {
180+
let (name, value) = s
181+
.split_once(':')
182+
.ok_or_else(|| "invalid header (expected \"Name: Value\")".to_string())?;
183+
184+
Ok((
185+
HeaderName::from_str(name.trim()).map_err(|e| e.to_string())?,
186+
HeaderValue::from_str(value.trim()).map_err(|e| e.to_string())?,
187+
))
188+
}

src/record/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use futures::future::{BoxFuture, FutureExt};
44
use futures::stream::{StreamExt, TryStreamExt, iter};
55
use m3u8_rs::*;
66
use reqwest::Client;
7+
use reqwest::header::HeaderMap;
78
use std::io;
89
use std::path::{Path, PathBuf};
910
use std::sync::Arc;
@@ -24,14 +25,15 @@ mod rewrite;
2425

2526
const MAX_CONCURRENT_DOWNLOADS: usize = 4;
2627

27-
#[derive(Debug, Copy, Clone)]
28+
#[derive(Debug, Clone)]
2829
pub struct RecordOptions {
2930
pub variant_select: VariantSelectOptions,
3031
pub audio: MediaSelect,
3132
pub video: MediaSelect,
3233
pub subtitle: MediaSelect,
3334
pub start: Option<f32>,
3435
pub end: Option<f32>,
36+
pub headers: HeaderMap,
3537
}
3638

3739
#[derive(thiserror::Error, Debug)]
@@ -58,6 +60,7 @@ pub async fn record(
5860
let recording = Arc::new(Mutex::new(recording));
5961
let client = Client::builder()
6062
.cookie_store(true)
63+
.default_headers(options.headers.clone())
6164
.build()
6265
.map_err(|_| RecordError::Config("Error while building HTTP client"))?;
6366
// Download initial playlist
@@ -177,6 +180,7 @@ async fn record_master_playlist(
177180
QuotedOrUnquoted::Quoted(variant_url.as_str().to_string()),
178181
);
179182
variant.uri = format!("{variant_dir}index.m3u8");
183+
let options = options.clone();
180184
let token = token.clone();
181185
join_set.spawn(async move {
182186
record_media_playlist(
@@ -208,6 +212,7 @@ async fn record_master_playlist(
208212
QuotedOrUnquoted::Quoted(media_url.as_str().to_string()),
209213
);
210214
media.uri = Some(format!("{media_dir}index.m3u8"));
215+
let options = options.clone();
211216
let token = token.clone();
212217
join_set.spawn(async move {
213218
record_media_playlist(

0 commit comments

Comments
 (0)