From 1297bcb6416bc41d4d7bdd7942c9b2d2aacac1e6 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Tue, 27 Sep 2022 23:12:14 +0200 Subject: [PATCH] fix: add livestream extraction --- README.md | 1 - src/client/player.rs | 85 +++++++++++-------- ...layer__tests__map_player_data_android.snap | 2 + ...layer__tests__map_player_data_desktop.snap | 2 + ...__tests__map_player_data_desktopmusic.snap | 2 + ...t__player__tests__map_player_data_ios.snap | 2 + ...__tests__map_player_data_tvhtml5embed.snap | 2 + src/model/mod.rs | 2 + 8 files changed, 63 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 1234433..9eb522f 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/src/client/player.rs b/src/client/player.rs index 4f972db..3f2ec41 100644 --- a/src/client/player.rs +++ b/src/client/player.rs @@ -116,11 +116,9 @@ impl MapResponse 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 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 = Vec::new(); let mut video_only_streams: Vec = Vec::new(); let mut audio_streams: Vec = 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 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"; diff --git a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_android.snap b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_android.snap index 38a2430..d8d2791 100644 --- a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_android.snap +++ b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_android.snap @@ -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"), ) diff --git a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktop.snap b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktop.snap index a79b1d2..3b61a50 100644 --- a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktop.snap +++ b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktop.snap @@ -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"), ) diff --git a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktopmusic.snap b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktopmusic.snap index 21f0712..40b85cd 100644 --- a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktopmusic.snap +++ b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_desktopmusic.snap @@ -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"), ) diff --git a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_ios.snap b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_ios.snap index 999fc7d..08522a5 100644 --- a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_ios.snap +++ b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_ios.snap @@ -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, ) diff --git a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tvhtml5embed.snap b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tvhtml5embed.snap index c40696b..90971d4 100644 --- a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tvhtml5embed.snap +++ b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tvhtml5embed.snap @@ -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"), ) diff --git a/src/model/mod.rs b/src/model/mod.rs index b84f46f..8096227 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -56,6 +56,8 @@ pub struct VideoPlayer { pub subtitles: Vec, /// Lifetime of the stream URLs in seconds pub expires_in_seconds: u32, + pub hls_manifest_url: Option, + pub dash_manifest_url: Option, } /// Video metadata from the player