Skip to content

Commit

Permalink
Drop once_cell
Browse files Browse the repository at this point in the history
  • Loading branch information
2bc4 committed Jan 20, 2024
1 parent 09058fe commit 50b4c08
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 137 deletions.
7 changes: 0 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ static-openssl = ["curl/static-ssl"]
anyhow = "1.0.79"
curl = { version = "0.4.44", git = "https://github.com/alexcrichton/curl-rust.git", rev = "0c1dddf" }
log = { version = "0.4.20", features = ["std"] }
once_cell = "1.19.0"
pico-args = { version = "0.5.0", features = ["eq-separator"] }
rand = "0.8.5"
time = { version = "0.3.31", features = ["local-offset", "formatting", "macros"] }
Expand Down
119 changes: 63 additions & 56 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,44 @@ use pico_args::Arguments;

use crate::constants;

#[derive(Debug)]
#[allow(clippy::module_name_repetitions)] //nothing else to name it
pub struct HttpArgs {
pub force_https: bool,
pub force_ipv4: bool,
pub retries: u64,
pub timeout: Duration,
}

impl Default for HttpArgs {
fn default() -> Self {
Self {
retries: 3,
timeout: Duration::from_secs(10),
force_https: bool::default(),
force_ipv4: bool::default(),
}
}
}

#[derive(Debug)]
#[allow(clippy::module_name_repetitions)] //nothing else to name it
pub struct HlsArgs {
pub codecs: String,
pub channel: String,
pub quality: String,
}

impl Default for HlsArgs {
fn default() -> Self {
Self {
codecs: "av1,h265,h264".to_owned(),
channel: String::default(),
quality: String::default(),
}
}
}

#[derive(Clone, Debug)]
#[allow(clippy::module_name_repetitions)] //nothing else to name it
pub struct PlayerArgs {
Expand All @@ -17,58 +55,30 @@ pub struct PlayerArgs {
impl Default for PlayerArgs {
fn default() -> Self {
Self {
path: String::default(),
args: "-".to_owned(),
path: String::default(),
quiet: bool::default(),
no_kill: bool::default(),
}
}
}

#[derive(Debug)]
#[allow(clippy::struct_field_names)] //.player_args
#[allow(clippy::struct_excessive_bools)]
#[derive(Default, Debug)]
pub struct Args {
pub hls: HlsArgs,
pub player: PlayerArgs,
pub servers: Option<Vec<String>>,
pub debug: bool,
pub passthrough: bool,
pub force_https: bool,
pub force_ipv4: bool,
pub client_id: Option<String>,
pub auth_token: Option<String>,
pub never_proxy: Option<Vec<String>>,
pub codecs: String,
pub http_retries: u64,
pub http_timeout: Duration,
pub channel: String,
pub quality: String,
}

impl Default for Args {
fn default() -> Self {
Self {
player: PlayerArgs::default(),
servers: Option::default(),
debug: bool::default(),
passthrough: bool::default(),
force_https: bool::default(),
force_ipv4: bool::default(),
client_id: Option::default(),
auth_token: Option::default(),
never_proxy: Option::default(),
codecs: "av1,h265,h264".to_owned(),
http_retries: 3,
http_timeout: Duration::from_secs(10),
channel: String::default(),
quality: String::default(),
}
}
}

impl Args {
pub fn parse() -> Result<Self> {
pub fn parse() -> Result<(Self, HttpArgs)> {
let mut args = Self::default();
let mut http_args = HttpArgs::default();
let mut parser = Arguments::from_env();
if parser.contains("-h") || parser.contains("--help") {
println!(include_str!("usage"));
Expand All @@ -92,22 +102,22 @@ impl Args {
None => default_config_path()?,
};

args.parse_config(&config_path)?;
args.parse_config(&mut http_args, &config_path)?;
}

args.merge_cli(&mut parser)?;
args.merge_cli(&mut parser, &mut http_args)?;
if let Some(never_proxy) = &args.never_proxy {
if never_proxy.iter().any(|a| a.eq(&args.channel)) {
if never_proxy.iter().any(|a| a.eq(&args.hls.channel)) {
args.servers = None;
}
}

ensure!(!args.player.path.is_empty(), "Player must be set");
ensure!(!args.quality.is_empty(), "Quality must be set");
Ok(args)
ensure!(!args.hls.quality.is_empty(), "Quality must be set");
Ok((args, http_args))
}

fn parse_config(&mut self, path: &str) -> Result<()> {
fn parse_config(&mut self, http: &mut HttpArgs, path: &str) -> Result<()> {
if !Path::new(path).is_file() {
return Ok(());
}
Expand All @@ -128,15 +138,15 @@ impl Args {
"quiet" => self.player.quiet = split.1.parse()?,
"passthrough" => self.passthrough = split.1.parse()?,
"no-kill" => self.player.no_kill = split.1.parse()?,
"force-https" => self.force_https = split.1.parse()?,
"force-ipv4" => self.force_ipv4 = split.1.parse()?,
"force-https" => http.force_https = split.1.parse()?,
"force-ipv4" => http.force_ipv4 = split.1.parse()?,
"client-id" => self.client_id = Some(split.1.into()),
"auth-token" => self.auth_token = Some(split.1.into()),
"never-proxy" => self.never_proxy = Some(split_comma(split.1)?),
"codecs" => self.codecs = split.1.into(),
"http-retries" => self.http_retries = split.1.parse()?,
"http-timeout" => self.http_timeout = parse_duration(split.1)?,
"quality" => self.quality = split.1.into(),
"codecs" => self.hls.codecs = split.1.into(),
"http-retries" => http.retries = split.1.parse()?,
"http-timeout" => http.timeout = parse_duration(split.1)?,
"quality" => self.hls.quality = split.1.into(),
_ => bail!("Unknown key in config: {}", split.0),
}
} else {
Expand All @@ -147,7 +157,7 @@ impl Args {
Ok(())
}

fn merge_cli(&mut self, p: &mut Arguments) -> Result<()> {
fn merge_cli(&mut self, p: &mut Arguments, http: &mut HttpArgs) -> Result<()> {
merge_opt_opt(&mut self.servers, p.opt_value_from_fn("-s", split_comma)?);
merge_opt(&mut self.player.path, p.opt_value_from_str("-p")?);
merge_opt(&mut self.player.args, p.opt_value_from_str("-a")?);
Expand All @@ -158,31 +168,28 @@ impl Args {
);
merge_switch(&mut self.passthrough, p.contains("--passthrough"));
merge_switch(&mut self.player.no_kill, p.contains("--no-kill"));
merge_switch(&mut self.force_https, p.contains("--force-https"));
merge_switch(&mut self.force_ipv4, p.contains("--force-ipv4"));
merge_switch(&mut http.force_https, p.contains("--force-https"));
merge_switch(&mut http.force_ipv4, p.contains("--force-ipv4"));
merge_opt_opt(&mut self.client_id, p.opt_value_from_str("--client-id")?);
merge_opt_opt(&mut self.auth_token, p.opt_value_from_str("--auth-token")?);
merge_opt_opt(
&mut self.never_proxy,
p.opt_value_from_fn("--never-proxy", split_comma)?,
);
merge_opt(&mut self.codecs, p.opt_value_from_str("--codecs")?);
merge_opt(
&mut self.http_retries,
p.opt_value_from_str("--http-retries")?,
);
merge_opt(&mut self.hls.codecs, p.opt_value_from_str("--codecs")?);
merge_opt(&mut http.retries, p.opt_value_from_str("--http-retries")?);
merge_opt(
&mut self.http_timeout,
&mut http.timeout,
p.opt_value_from_fn("--http-timeout", parse_duration)?,
);

self.channel = p
self.hls.channel = p
.free_from_str::<String>()
.context("missing channel argument")?
.to_lowercase()
.replace("twitch.tv/", "");

merge_opt(&mut self.quality, p.opt_free_from_str()?);
merge_opt(&mut self.hls.quality, p.opt_free_from_str()?);

Ok(())
}
Expand Down
60 changes: 32 additions & 28 deletions src/hls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use rand::{
use url::Url;

use crate::{
args::HlsArgs,
constants,
http::{self, TextRequest},
http::{self, Agent, TextRequest},
};

#[derive(Debug)]
Expand Down Expand Up @@ -164,12 +165,12 @@ pub struct MediaPlaylist {
}

impl MediaPlaylist {
pub fn new(url: &Url) -> Result<Self> {
pub fn new(url: &Url, agent: &Agent) -> Result<Self> {
let mut playlist = Self {
header_url: SegmentHeaderUrl::default(),
urls: PrefetchUrls::default(),
duration: SegmentDuration::default(),
request: TextRequest::get(url)?,
request: agent.get(url)?,
};

playlist.header_url = playlist.reload()?.parse()?;
Expand Down Expand Up @@ -223,7 +224,12 @@ struct PlaybackAccessToken {
}

impl PlaybackAccessToken {
fn new(client_id: &Option<String>, auth_token: &Option<String>, channel: &str) -> Result<Self> {
fn new(
client_id: &Option<String>,
auth_token: &Option<String>,
channel: &str,
agent: &Agent,
) -> Result<Self> {
#[rustfmt::skip]
let gql = concat!(
"{",
Expand All @@ -243,12 +249,12 @@ impl PlaybackAccessToken {
"}",
"}").replace("{channel}", channel);

let mut request = TextRequest::post(&constants::TWITCH_GQL_ENDPOINT.parse()?, &gql)?;
let mut request = agent.post(&constants::TWITCH_GQL_ENDPOINT.parse()?, &gql)?;
request.header("Content-Type: text/plain;charset=UTF-8")?;
request.header(&format!("X-Device-ID: {}", &Self::gen_id()))?;
request.header(&format!(
"Client-Id: {}",
Self::choose_client_id(client_id, auth_token)?
Self::choose_client_id(client_id, auth_token, agent)?
))?;

if let Some(auth_token) = auth_token {
Expand Down Expand Up @@ -281,12 +287,16 @@ impl PlaybackAccessToken {
})
}

fn choose_client_id(client_id: &Option<String>, auth_token: &Option<String>) -> Result<String> {
fn choose_client_id(
client_id: &Option<String>,
auth_token: &Option<String>,
agent: &Agent,
) -> Result<String> {
//--client-id > (if auth token) client id from twitch > default
let client_id = if let Some(client_id) = client_id {
client_id.to_owned()
} else if let Some(auth_token) = auth_token {
let mut request = TextRequest::get(&constants::TWITCH_OAUTH_ENDPOINT.parse()?)?;
let mut request = agent.get(&constants::TWITCH_OAUTH_ENDPOINT.parse()?)?;
request.header(&format!("Authorization: OAuth {auth_token}"))?;

request
Expand Down Expand Up @@ -314,14 +324,13 @@ impl PlaybackAccessToken {
pub fn fetch_twitch_playlist(
client_id: &Option<String>,
auth_token: &Option<String>,
codecs: &str,
channel: &str,
quality: &str,
args: &HlsArgs,
agent: &Agent,
) -> Result<Url> {
info!("Fetching playlist for channel {channel} (Twitch)");
let access_token = PlaybackAccessToken::new(client_id, auth_token, channel)?;
info!("Fetching playlist for channel {} (Twitch)", args.channel);
let access_token = PlaybackAccessToken::new(client_id, auth_token, &args.channel, agent)?;
let url = Url::parse_with_params(
&format!("{}{channel}.m3u8", constants::TWITCH_HLS_BASE),
&format!("{}{}.m3u8", constants::TWITCH_HLS_BASE, args.channel),
&[
("acmb", "e30="),
("allow_source", "true"),
Expand All @@ -331,7 +340,7 @@ pub fn fetch_twitch_playlist(
("playlist_include_framerate", "true"),
("player_backend", "mediaplayer"),
("reassignments_supported", "true"),
("supported_codecs", codecs),
("supported_codecs", &args.codecs),
("transcode_mode", "cbr_v1"),
(
"p",
Expand All @@ -346,29 +355,24 @@ pub fn fetch_twitch_playlist(
)?;

parse_variant_playlist(
&TextRequest::get(&url)?.text().map_err(map_if_offline)?,
quality,
&agent.get(&url)?.text().map_err(map_if_offline)?,
&args.quality,
)
}

pub fn fetch_proxy_playlist(
servers: &[String],
codecs: &str,
channel: &str,
quality: &str,
) -> Result<Url> {
info!("Fetching playlist for channel {} (proxy)", channel);
pub fn fetch_proxy_playlist(servers: &[String], args: &HlsArgs, agent: &Agent) -> Result<Url> {
info!("Fetching playlist for channel {} (proxy)", args.channel);
let servers = servers
.iter()
.map(|s| {
Url::parse_with_params(
&s.replace("[channel]", channel),
&s.replace("[channel]", &args.channel),
&[
("allow_source", "true"),
("allow_audio_only", "true"),
("fast_bread", "true"),
("warp", "true"),
("supported_codecs", codecs),
("supported_codecs", &args.codecs),
],
)
})
Expand All @@ -384,7 +388,7 @@ pub fn fetch_proxy_playlist(
s.host_str().unwrap_or("<unknown>")
);

let mut request = match TextRequest::get(s) {
let mut request = match agent.get(s) {
Ok(request) => request,
Err(e) => {
error!("{e}");
Expand All @@ -410,7 +414,7 @@ pub fn fetch_proxy_playlist(
})
.ok_or(Error::Offline)?;

parse_variant_playlist(&playlist, quality)
parse_variant_playlist(&playlist, &args.quality)
}

fn parse_variant_playlist(master_playlist: &str, quality: &str) -> Result<Url> {
Expand Down
Loading

0 comments on commit 50b4c08

Please sign in to comment.