feat: add stream filter
This commit is contained in:
parent
ae72a230a2
commit
21675d5b1d
8 changed files with 2640 additions and 42 deletions
|
|
@ -109,7 +109,6 @@ const DEFAULT_UA: &str =
|
|||
|
||||
const CONSENT_COOKIE: &str = "CONSENT";
|
||||
const CONSENT_COOKIE_YES: &str = "YES+yt.462272069.de+FX+";
|
||||
const CONSENT_COOKIE_NO: &str = "PENDING+";
|
||||
|
||||
const YOUTUBEI_V1_URL: &str = "https://www.youtube.com/youtubei/v1/";
|
||||
const YOUTUBEI_V1_GAPIS_URL: &str = "https://youtubei.googleapis.com/youtubei/v1/";
|
||||
|
|
@ -392,7 +391,6 @@ impl AndroidClient {
|
|||
MOBILE_CLIENT_VERSION, locale.country
|
||||
))
|
||||
.gzip(true)
|
||||
.brotli(true)
|
||||
.build()
|
||||
.expect("unable to build the HTTP client");
|
||||
|
||||
|
|
@ -460,7 +458,6 @@ impl IosClient {
|
|||
MOBILE_CLIENT_VERSION, IOS_DEVICE_MODEL, locale.country
|
||||
))
|
||||
.gzip(true)
|
||||
.brotli(true)
|
||||
.build()
|
||||
.expect("unable to build the HTTP client");
|
||||
|
||||
|
|
@ -512,7 +509,6 @@ impl YTClient for TvHtml5EmbedClient {
|
|||
)
|
||||
.header(header::ORIGIN, "https://www.youtube.com")
|
||||
.header(header::REFERER, "https://www.youtube.com")
|
||||
// .header(header::COOKIE, self.consent_cookie_no.to_owned())
|
||||
.header("X-YouTube-Client-Name", "1")
|
||||
.header("X-YouTube-Client-Version", TVHTML5_CLIENT_VERSION)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
|
|
@ -33,7 +32,7 @@ struct QPlayer {
|
|||
video_id: String,
|
||||
/// Set to true to allow extraction of streams with sensitive content
|
||||
content_check_ok: bool,
|
||||
/// Probably refers to allowing sensitive content, too
|
||||
/// Probably refers to allowin&g sensitive content, too
|
||||
racy_check_ok: bool,
|
||||
}
|
||||
|
||||
|
|
@ -226,6 +225,8 @@ fn map_audio_stream(
|
|||
deobf: &Deobfuscator,
|
||||
last_nsig: &mut [String; 2],
|
||||
) -> Option<AudioStream> {
|
||||
static LANG_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r#"^([a-z]{2})\."#).unwrap());
|
||||
|
||||
let (mtype, codecs) = some_or_bail!(parse_mime(&f.mime_type), None);
|
||||
let (url, throttled) =
|
||||
some_or_bail!(map_url(&f.url, &f.signature_cipher, deobf, last_nsig), None);
|
||||
|
|
@ -244,7 +245,12 @@ fn map_audio_stream(
|
|||
throttled,
|
||||
track: f.audio_track.as_ref().map(|t| AudioTrack {
|
||||
id: t.id.to_owned(),
|
||||
name: t.display_name.to_owned(),
|
||||
lang: LANG_PATTERN
|
||||
.captures(&t.id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|m| m.get(1).unwrap().as_str().to_owned()),
|
||||
lang_name: t.display_name.to_owned(),
|
||||
is_default: t.audio_is_default,
|
||||
}),
|
||||
})
|
||||
|
|
@ -309,30 +315,6 @@ fn get_audio_codec(codecs: Vec<&str>) -> AudioCodec {
|
|||
AudioCodec::Unknown
|
||||
}
|
||||
|
||||
fn cmp_video_streams(a: &VideoStream, b: &VideoStream) -> Ordering {
|
||||
match (a.width * a.height).cmp(&(b.width * b.height)) {
|
||||
Ordering::Less => Ordering::Less,
|
||||
Ordering::Greater => Ordering::Greater,
|
||||
Ordering::Equal => match a.codec.cmp(&b.codec) {
|
||||
Ordering::Less => Ordering::Less,
|
||||
Ordering::Greater => Ordering::Greater,
|
||||
Ordering::Equal => a.average_bitrate.cmp(&b.average_bitrate),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_audio_streams(a: &AudioStream, b: &AudioStream) -> Ordering {
|
||||
fn cmp_bitrate(s: &AudioStream) -> u32 {
|
||||
match s.codec {
|
||||
// Opus is more efficient
|
||||
AudioCodec::Opus => (s.average_bitrate as f32 * 1.3) as u32,
|
||||
_ => s.average_bitrate,
|
||||
}
|
||||
}
|
||||
|
||||
cmp_bitrate(a).cmp(&cmp_bitrate(b))
|
||||
}
|
||||
|
||||
fn map_player_data(response: response::Player, deobf: &Deobfuscator) -> Result<PlayerData> {
|
||||
// Check playability status
|
||||
match response.playability_status {
|
||||
|
|
@ -430,10 +412,9 @@ fn map_player_data(response: response::Player, deobf: &Deobfuscator) -> Result<P
|
|||
}
|
||||
}
|
||||
|
||||
// Sort streams by quality
|
||||
video_streams.sort_by(cmp_video_streams);
|
||||
video_only_streams.sort_by(cmp_video_streams);
|
||||
audio_streams.sort_by(cmp_audio_streams);
|
||||
video_streams.sort();
|
||||
video_only_streams.sort();
|
||||
audio_streams.sort();
|
||||
|
||||
let subtitles = response.captions.map_or(vec![], |captions| {
|
||||
captions
|
||||
|
|
@ -481,15 +462,20 @@ mod tests {
|
|||
})
|
||||
});
|
||||
|
||||
#[allow(dead_code)]
|
||||
// #[test_log::test(tokio::test)]
|
||||
async fn download_testfiles() {
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn download_response_testfiles() {
|
||||
let tf_dir = Path::new("testfiles/player");
|
||||
let video_id = "pPvd8UxmSbQ";
|
||||
|
||||
let rt = RustyTube::new();
|
||||
|
||||
for client_type in CLIENT_TYPES {
|
||||
let mut json_path = tf_dir.to_path_buf();
|
||||
json_path.push(format!("{:?}_video.json", client_type).to_lowercase());
|
||||
if json_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let client = rt.get_ytclient(client_type);
|
||||
let context = client.get_context(false).await;
|
||||
|
||||
|
|
@ -505,15 +491,30 @@ mod tests {
|
|||
.error_for_status()
|
||||
.unwrap();
|
||||
|
||||
let mut json_path = tf_dir.to_path_buf();
|
||||
json_path.push(format!("{:?}_video.json", client_type).to_lowercase());
|
||||
|
||||
let mut file = std::fs::File::create(json_path).unwrap();
|
||||
let mut content = std::io::Cursor::new(resp.bytes().await.unwrap());
|
||||
std::io::copy(&mut content, &mut file).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn download_model_testfiles() {
|
||||
let tf_dir = Path::new("testfiles/player_model");
|
||||
let rt = RustyTube::new();
|
||||
|
||||
for (name, id) in [("multilanguage", "tVWWp1PqDus"), ("hdr", "LXb3EKWsInQ")] {
|
||||
let mut json_path = tf_dir.to_path_buf();
|
||||
json_path.push(format!("{}.json", name).to_lowercase());
|
||||
if json_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let player_data = rt.get_player(id, ClientType::Desktop).await.unwrap();
|
||||
let file = std::fs::File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(file, &player_data).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::desktop("desktop", include_str!("../../testfiles/player/desktop_video.json"))]
|
||||
#[case::desktop_music("desktop_music", include_str!("../../testfiles/player/desktopmusic_video.json"))]
|
||||
|
|
|
|||
Reference in a new issue