fix: add livestream extraction
This commit is contained in:
parent
fe5468313a
commit
1297bcb641
8 changed files with 63 additions and 35 deletions
|
|
@ -8,7 +8,6 @@ inspired by [NewPipe](https://github.com/TeamNewPipe/NewPipeExtractor).
|
|||
### YouTube
|
||||
|
||||
- [X] **Player** (video/audio streams, subtitles)
|
||||
- TODO: Livestream support
|
||||
- [X] **Playlist**
|
||||
- [X] **VideoDetails** (metadata, comments, recommended videos)
|
||||
- [X] **Channel** (videos, playlists, info)
|
||||
|
|
|
|||
|
|
@ -116,11 +116,9 @@ impl MapResponse<VideoPlayer> for response::Player {
|
|||
let mut warnings = vec![];
|
||||
|
||||
// Check playability status
|
||||
match self.playability_status {
|
||||
let is_live = match self.playability_status {
|
||||
response::player::PlayabilityStatus::Ok { live_streamability } => {
|
||||
if live_streamability.is_some() {
|
||||
bail!("Active livestreams are not supported")
|
||||
}
|
||||
live_streamability.is_some()
|
||||
}
|
||||
response::player::PlayabilityStatus::Unplayable { reason } => {
|
||||
bail!("Video is unplayable. Reason: {}", reason)
|
||||
|
|
@ -192,43 +190,45 @@ impl MapResponse<VideoPlayer> for response::Player {
|
|||
let mut formats = streaming_data.formats.c;
|
||||
formats.append(&mut streaming_data.adaptive_formats.c);
|
||||
|
||||
warnings.append(&mut streaming_data.formats.warnings);
|
||||
warnings.append(&mut streaming_data.adaptive_formats.warnings);
|
||||
|
||||
let mut last_nsig: [String; 2] = [String::new(), String::new()];
|
||||
|
||||
let mut video_streams: Vec<VideoStream> = Vec::new();
|
||||
let mut video_only_streams: Vec<VideoStream> = Vec::new();
|
||||
let mut audio_streams: Vec<AudioStream> = Vec::new();
|
||||
|
||||
for f in formats {
|
||||
if f.format_type == player::FormatType::FormatStreamTypeOtf {
|
||||
continue;
|
||||
}
|
||||
if !is_live {
|
||||
let mut last_nsig: [String; 2] = [String::new(), String::new()];
|
||||
|
||||
match (f.is_video(), f.is_audio()) {
|
||||
(true, true) => {
|
||||
let mut map_res = map_video_stream(f, deobf, &mut last_nsig);
|
||||
warnings.append(&mut map_res.warnings);
|
||||
if let Some(c) = map_res.c {
|
||||
video_streams.push(c);
|
||||
};
|
||||
warnings.append(&mut streaming_data.formats.warnings);
|
||||
warnings.append(&mut streaming_data.adaptive_formats.warnings);
|
||||
|
||||
for f in formats {
|
||||
if f.format_type == player::FormatType::FormatStreamTypeOtf {
|
||||
continue;
|
||||
}
|
||||
(true, false) => {
|
||||
let mut map_res = map_video_stream(f, deobf, &mut last_nsig);
|
||||
warnings.append(&mut map_res.warnings);
|
||||
if let Some(c) = map_res.c {
|
||||
video_only_streams.push(c);
|
||||
};
|
||||
|
||||
match (f.is_video(), f.is_audio()) {
|
||||
(true, true) => {
|
||||
let mut map_res = map_video_stream(f, deobf, &mut last_nsig);
|
||||
warnings.append(&mut map_res.warnings);
|
||||
if let Some(c) = map_res.c {
|
||||
video_streams.push(c);
|
||||
};
|
||||
}
|
||||
(true, false) => {
|
||||
let mut map_res = map_video_stream(f, deobf, &mut last_nsig);
|
||||
warnings.append(&mut map_res.warnings);
|
||||
if let Some(c) = map_res.c {
|
||||
video_only_streams.push(c);
|
||||
};
|
||||
}
|
||||
(false, true) => {
|
||||
let mut map_res = map_audio_stream(f, deobf, &mut last_nsig);
|
||||
warnings.append(&mut map_res.warnings);
|
||||
if let Some(c) = map_res.c {
|
||||
audio_streams.push(c);
|
||||
};
|
||||
}
|
||||
(false, false) => warnings.push(format!("invalid stream: itag {}", f.itag)),
|
||||
}
|
||||
(false, true) => {
|
||||
let mut map_res = map_audio_stream(f, deobf, &mut last_nsig);
|
||||
warnings.append(&mut map_res.warnings);
|
||||
if let Some(c) = map_res.c {
|
||||
audio_streams.push(c);
|
||||
};
|
||||
}
|
||||
(false, false) => warnings.push(format!("invalid stream: itag {}", f.itag)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +261,8 @@ impl MapResponse<VideoPlayer> for response::Player {
|
|||
audio_streams,
|
||||
subtitles,
|
||||
expires_in_seconds: streaming_data.expires_in_seconds,
|
||||
hls_manifest_url: streaming_data.hls_manifest_url,
|
||||
dash_manifest_url: streaming_data.dash_manifest_url,
|
||||
},
|
||||
warnings,
|
||||
})
|
||||
|
|
@ -731,6 +733,21 @@ mod tests {
|
|||
assert!(player_data.expires_in_seconds > 10000);
|
||||
}
|
||||
|
||||
/*
|
||||
#[rstest]
|
||||
#[case::desktop(ClientType::Desktop)]
|
||||
// #[case::tv_html5_embed(ClientType::TvHtml5Embed)]
|
||||
// #[case::android(ClientType::Android)]
|
||||
// #[case::ios(ClientType::Ios)]
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn get_player_live(#[case] client_type: ClientType) {
|
||||
let rp = RustyPipe::builder().strict().build();
|
||||
let player_data = rp.query().player("86YLFOog4GM", client_type).await.unwrap();
|
||||
|
||||
dbg!(&player_data);
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn t_cipher_to_url() {
|
||||
let signature_cipher = "s=w%3DAe%3DA6aDNQLkViKS7LOm9QtxZJHKwb53riq9qEFw-ecBWJCAiA%3DcEg0tn3dty9jEHszfzh4Ud__bg9CEHVx4ix-7dKsIPAhIQRw8JQ0qOA&sp=sig&url=https://rr5---sn-h0jelnez.googlevideo.com/videoplayback%3Fexpire%3D1659376413%26ei%3Dvb7nYvH5BMK8gAfBj7ToBQ%26ip%3D2003%253Ade%253Aaf06%253A6300%253Ac750%253A1b77%253Ac74a%253A80e3%26id%3Do-AB_BABwrXZJN428ZwDxq5ScPn2AbcGODnRlTVhCQ3mj2%26itag%3D251%26source%3Dyoutube%26requiressl%3Dyes%26mh%3DhH%26mm%3D31%252C26%26mn%3Dsn-h0jelnez%252Csn-4g5ednsl%26ms%3Dau%252Conr%26mv%3Dm%26mvi%3D5%26pl%3D37%26initcwndbps%3D1588750%26spc%3DlT-Khi831z8dTejFIRCvCEwx_6romtM%26vprv%3D1%26mime%3Daudio%252Fwebm%26ns%3Db_Mq_qlTFcSGlG9RpwpM9xQH%26gir%3Dyes%26clen%3D3781277%26dur%3D229.301%26lmt%3D1655510291473933%26mt%3D1659354538%26fvip%3D5%26keepalive%3Dyes%26fexp%3D24001373%252C24007246%26c%3DWEB%26rbqsm%3Dfr%26txp%3D4532434%26n%3Dd2g6G2hVqWIXxedQ%26sparams%3Dexpire%252Cei%252Cip%252Cid%252Citag%252Csource%252Crequiressl%252Cspc%252Cvprv%252Cmime%252Cns%252Cgir%252Cclen%252Cdur%252Clmt%26lsparams%3Dmh%252Cmm%252Cmn%252Cms%252Cmv%252Cmvi%252Cpl%252Cinitcwndbps%26lsig%3DAG3C_xAwRQIgCKCGJ1iu4wlaGXy3jcJyU3inh9dr1FIfqYOZEG_MdmACIQCbungkQYFk7EhD6K2YvLaHFMjKOFWjw001_tLb0lPDtg%253D%253D";
|
||||
|
|
|
|||
|
|
@ -424,4 +424,6 @@ VideoPlayer(
|
|||
),
|
||||
],
|
||||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659481355/ei/q1jpYtOPEYSBgQeHmqbwAQ/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr5---sn-h0jeenek.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jeenek%2Csn-h0jelnez/ms/au%2Crdu/mv/m/mvi/5/pl/37/hfr/1/as/fmp4_audio_clear%2Cfmp4_sd_hd_clear/initcwndbps/1527500/vprv/1/mt/1659459429/fvip/4/itag_bl/376%2C377%2C384%2C385%2C612%2C613%2C617%2C619%2C623%2C628%2C655%2C656%2C660%2C662%2C666%2C671/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cvprv%2Citag/sig/AOq0QJ8wRAIgMm4a_MIHA3YUszKeruSy3exs5JwNjJAyLAwxL0yPdNMCIANb9GDMSTp_NT-PPhbvYMwRULJ5a9BO6MYD9FuWprC1/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgETSOwhwWVMy7gmrFXZlJu655ToLzSwOEsT16oRyrWhACIQDkvOEw1fImz5omu4iVIRNFe-z-JC9v8WUyx281dW2NOw%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -541,4 +541,6 @@ VideoPlayer(
|
|||
),
|
||||
],
|
||||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659481355/ei/q1jpYtq3BJCX1gKVyJGQDg/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr4---sn-h0jelnez.googlevideo.com/mh/mQ/mm/31%2C26/mn/sn-h0jelnez%2Csn-4g5edn6k/ms/au%2Conr/mv/m/mvi/4/pl/37/hfr/all/as/fmp4_audio_clear%2Cwebm_audio_clear%2Cwebm2_audio_clear%2Cfmp4_sd_hd_clear%2Cwebm2_sd_hd_clear/initcwndbps/1513750/spc/lT-KhrZGE2opztWyVdAtyUNlb8dXPDs/vprv/1/mt/1659459429/fvip/4/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cspc%2Cvprv%2Citag/sig/AOq0QJ8wRgIhAPEjHK19PKVHqQeia6WF4qubuMYk74LGi8F8lk5ZMPkFAiEAsaB2pKQWBvuPnNUnbdQXHc-izgsHJUP793woC2xNJlg%3D/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgOY4xu4H9wqPVZ7vF2i0hFcOnqrur1XGoA43a7ZEuuSUCIQCyPxBKXUQrKFmknNEGpX5GSWySKgMw_xHBikWpKpKwvg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -387,4 +387,6 @@ VideoPlayer(
|
|||
),
|
||||
],
|
||||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659487474/ei/knDpYub6BojEgAf6jbLgDw/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr5---sn-h0jeenek.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jeenek%2Csn-h0jelnez/ms/au%2Crdu/mv/m/mvi/5/pl/37/hfr/all/as/fmp4_audio_clear%2Cwebm_audio_clear%2Cwebm2_audio_clear%2Cfmp4_sd_hd_clear%2Cwebm2_sd_hd_clear/initcwndbps/1418750/spc/lT-Khox4YuJQ2wmH79zYALRvsWTPCUc/vprv/1/mt/1659465669/fvip/4/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cspc%2Cvprv%2Citag/sig/AOq0QJ8wRAIgErABhAEaoKHUDu9dDbpxE_8gR4b8WWAi61fnu8UKnuICIEYrEKcHvqHdO4V3R7cvSGwi_HGH34IlQsKbziOfMBov/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgJxHmH0Sxo3cY_pW_ZzQ3hW9-7oz6K_pZWcUdrDDQ2sQCIQDJYNINQwLgKelgbO3CZYx7sMxdUAFpWdokmRBQ77vwvw%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -160,4 +160,6 @@ VideoPlayer(
|
|||
),
|
||||
],
|
||||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1659481355/ei/q1jpYq-xHs7NgQev0bfwAQ/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr4---sn-h0jelnez.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jelnez%2Csn-h0jeenek/ms/au%2Crdu/mv/m/mvi/4/pl/37/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/1513750/vprv/1/go/1/mt/1659459429/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/24001373%2C24007246/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cvprv%2Cgo%2Citag%2Cplaylist_type/sig/AOq0QJ8wRQIhAIYnEHvIgJtJ8hehAXNtVY3qsgsq_GdOhWf2hkJZe6lCAiBxaRY_nubYp6hBizcAg_KFkKnkG-t2XYLRQ5wGdM3AjA%3D%3D/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRgIhAM_91Kk_0VLuSsR6nLCY7LdtWojyRAzXSScd_X9ShRROAiEA1AF4VY04F71NsAI8_j3iqjuXnWL9s6NoXHq7P8-bHx8%3D/file/index.m3u8"),
|
||||
dash_manifest_url: None,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -541,4 +541,6 @@ VideoPlayer(
|
|||
),
|
||||
],
|
||||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659481355/ei/q1jpYv-eJ9uF6dsPhvyH8As/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr4---sn-h0jelnez.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jelnez%2Csn-h0jeenek/ms/au%2Crdu/mv/m/mvi/4/pl/37/hfr/all/as/fmp4_audio_clear%2Cfmp4_sd_hd_clear/initcwndbps/1527500/vprv/1/mt/1659459429/fvip/5/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cvprv%2Citag/sig/AOq0QJ8wRQIhANKWS7GCN4pSoHIQ6BMZdOaHAD0I25nHwRj7ds4qrxdEAiBsd9l8WIceqF7-2xyR82DGecCiS9hgUIPJhdNhkwVpHg%3D%3D/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgMbu-wTOcXGCwGh27y0YZHktumKM1sopgxfQf8LCcCnECIQDnhFbgddOxwiQbnMOIcCn6ncpN54UyALRNigUSCp9Deg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ pub struct VideoPlayer {
|
|||
pub subtitles: Vec<Subtitle>,
|
||||
/// Lifetime of the stream URLs in seconds
|
||||
pub expires_in_seconds: u32,
|
||||
pub hls_manifest_url: Option<String>,
|
||||
pub dash_manifest_url: Option<String>,
|
||||
}
|
||||
|
||||
/// Video metadata from the player
|
||||
|
|
|
|||
Reference in a new issue