diff --git a/.gitignore b/.gitignore index 3e0f26a..ad20047 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ rustypipe_reports rustypipe_cache*.json bg_snapshot.bin + +*.webm +*.mp4 +*.m4a diff --git a/Cargo.toml b/Cargo.toml index ae8bbec..547b84e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ description = "Client for the public YouTube / YouTube Music API (Innertube), in include = ["/src", "README.md", "CHANGELOG.md", "LICENSE", "!snapshots"] [workspace] -members = [".", "codegen", "downloader", "cli"] +members = [".", "codegen", "downloader", "cli", "abr-proto"] [workspace.package] edition = "2021" @@ -56,6 +56,7 @@ urlencoding = "2.1.0" quick-xml = { version = "0.37.0", features = ["serialize"] } tracing = { version = "0.1.0", features = ["log"] } localzone = "0.3.1" +protobuf = "3.0.0" # CLI indicatif = "0.17.0" @@ -79,6 +80,7 @@ rustypipe-downloader = { path = "./downloader", version = "0.3.1", default-featu "indicatif", "audiotag", ] } +rustypipe-abr-proto = { path = "./abr-proto", version = "0.1.0" } [features] default = ["default-tls"] diff --git a/abr-proto/Cargo.toml b/abr-proto/Cargo.toml new file mode 100644 index 0000000..d5624f0 --- /dev/null +++ b/abr-proto/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rustypipe-abr-proto" +version = "0.1.0" +edition.workspace = true +authors.workspace = true +license = "MIT" +repository.workspace = true +keywords.workspace = true +categories.workspace = true +description = "Protobuf definitions for YouTube adaptive bitrate streaming" + +[dependencies] +protobuf.workspace = true + +[build-dependencies] +protobuf-codegen = "3" diff --git a/abr-proto/LICENSE b/abr-proto/LICENSE new file mode 100644 index 0000000..dbb5408 --- /dev/null +++ b/abr-proto/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2025 ThetaDev + +MIT LICENSE +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/abr-proto/README.md b/abr-proto/README.md new file mode 100644 index 0000000..39e70f4 --- /dev/null +++ b/abr-proto/README.md @@ -0,0 +1,5 @@ +# ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg) abr-proto + +[![Current crates.io version](https://img.shields.io/crates/v/rustypipe-abr-proto.svg)](https://crates.io/crates/rustypipe-abr-proto) +[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat)](https://opensource.org/licenses/MIT) +[![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml) diff --git a/abr-proto/build.rs b/abr-proto/build.rs new file mode 100644 index 0000000..e8f4db2 --- /dev/null +++ b/abr-proto/build.rs @@ -0,0 +1,65 @@ +use std::{ + env, fs, + ops::Deref, + path::{Path, PathBuf}, +}; + +fn out_dir() -> PathBuf { + Path::new(&env::var("OUT_DIR").expect("env")).to_path_buf() +} + +fn cleanup() { + let _ = fs::remove_dir_all(out_dir()); +} + +fn compile() { + let proto_dir = Path::new(&env::var("CARGO_MANIFEST_DIR").expect("env")).join("proto"); + let vsdir = proto_dir.join("video_streaming"); + + let files = &[ + proto_dir.join("misc").join("common.proto"), + vsdir.join("buffered_range.proto"), + vsdir.join("client_abr_state.proto"), + vsdir.join("crypto_params.proto"), + vsdir.join("encrypted_player_request.proto"), + vsdir.join("format_initialization_metadata.proto"), + vsdir.join("live_metadata.proto"), + vsdir.join("media_capabilities.proto"), + vsdir.join("media_header.proto"), + vsdir.join("next_request_policy.proto"), + vsdir.join("onesie_header_type.proto"), + vsdir.join("onesie_header.proto"), + vsdir.join("onesie_player_request.proto"), + vsdir.join("onesie_player_response.proto"), + vsdir.join("onesie_request.proto"), + vsdir.join("playback_cookie.proto"), + vsdir.join("playback_start_policy.proto"), + vsdir.join("proxy_status.proto"), + vsdir.join("reload_player_response.proto"), + vsdir.join("request_cancellation_policy.proto"), + vsdir.join("sabr_error.proto"), + vsdir.join("sabr_redirect.proto"), + vsdir.join("stream_protection_status.proto"), + vsdir.join("streamer_context.proto"), + vsdir.join("time_range.proto"), + vsdir.join("video_playback_abr_request.proto"), + ]; + + let slices = files.iter().map(Deref::deref).collect::>(); + + let out_dir = out_dir(); + fs::create_dir(&out_dir).expect("create_dir"); + + protobuf_codegen::Codegen::new() + .pure() + .out_dir(&out_dir) + .inputs(&slices) + .include(&proto_dir) + .run() + .expect("Codegen failed."); +} + +fn main() { + cleanup(); + compile(); +} diff --git a/abr-proto/proto/misc/common.proto b/abr-proto/proto/misc/common.proto new file mode 100644 index 0000000..e494b7f --- /dev/null +++ b/abr-proto/proto/misc/common.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; +package misc; + +message HttpHeader { + optional string name = 1; + optional string value = 2; +} + +message FormatId { + optional int32 itag = 1; + optional uint64 last_modified = 2; + optional string xtags = 3; +} + +message InitRange { + optional int32 start = 1; + optional int32 end = 2; +} + +message IndexRange { + optional int32 start = 1; + optional int32 end = 2; +} + +message KeyValuePair { + optional string key = 1; + optional string value = 2; +} diff --git a/abr-proto/proto/video_streaming/buffered_range.proto b/abr-proto/proto/video_streaming/buffered_range.proto new file mode 100644 index 0000000..1cef4eb --- /dev/null +++ b/abr-proto/proto/video_streaming/buffered_range.proto @@ -0,0 +1,32 @@ + +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; +import "video_streaming/time_range.proto"; + +message BufferedRange { + required .misc.FormatId format_id = 1; + required int64 start_time_ms = 2; + required int64 duration_ms = 3; + required int32 start_segment_index = 4; + required int32 end_segment_index = 5; + optional TimeRange time_range = 6; + optional Kob field9 = 9; + optional YPa field11 = 11; + optional YPa field12 = 12; +} + +message Kob { + message Pa { + optional string video_id = 1; + optional uint64 lmt = 2; + } + repeated Pa EW = 1; +} + +message YPa { + optional int32 field1 = 1; + optional int32 field2 = 2; + optional int32 field3 = 3; +} diff --git a/abr-proto/proto/video_streaming/client_abr_state.proto b/abr-proto/proto/video_streaming/client_abr_state.proto new file mode 100644 index 0000000..dc39dba --- /dev/null +++ b/abr-proto/proto/video_streaming/client_abr_state.proto @@ -0,0 +1,44 @@ +syntax = "proto2"; +package video_streaming; + +message ClientAbrState { + optional int32 time_since_last_manual_format_selection_ms = 13; + optional int32 last_manual_direction = 14; + optional int32 last_manual_selected_resolution = 16; + optional int32 detailed_network_type = 17; + optional int32 client_viewport_width = 18; + optional int32 client_viewport_height = 19; + optional int64 client_bitrate_cap = 20; + optional int32 sticky_resolution = 21; + optional bool client_viewport_is_flexible = 22; + optional int32 bandwidth_estimate = 23; + optional int64 player_time_ms = 28; + optional int64 time_since_last_seek = 29; + optional bool data_saver_mode = 30; + optional int32 visibility = 34; + optional float playback_rate = 35; + optional int64 elapsed_wall_time_ms = 36; + optional bytes media_capabilities = 38; + optional int64 time_since_last_action_ms = 39; + optional int32 enabled_track_types_bitfield = 40; + optional int32 max_pacing_rate = 43; + optional int64 player_state = 44; + optional bool drc_enabled = 46; + optional int32 Jda = 48; + optional int32 qw = 50; + optional int32 Ky = 51; + optional int32 sabr_report_request_cancellation_info = 54; + optional bool l = 56; + optional int64 G7 = 57; + optional bool prefer_vp9 = 58; + optional int32 qj = 59; + optional int32 Hx = 60; + optional bool is_prefetch = 61; + optional int32 sabr_support_quality_constraints = 62; + optional bytes sabr_license_constraint = 63; + optional int32 allow_proxima_live_latency = 64; + optional int32 sabr_force_proxima = 66; + optional int32 Tqb = 67; + optional int64 sabr_force_max_network_interruption_duration_ms = 68; + optional string audio_track_id = 69; +} diff --git a/abr-proto/proto/video_streaming/crypto_params.proto b/abr-proto/proto/video_streaming/crypto_params.proto new file mode 100644 index 0000000..8c3b077 --- /dev/null +++ b/abr-proto/proto/video_streaming/crypto_params.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; +package video_streaming; + +message CryptoParams { + enum CompressionType { + VAL_0 = 0; + VAL_1 = 1; + VAL_2 = 2; + } + optional bytes hmac = 4; + optional bytes iv = 5; + optional CompressionType compression_type = 6; +} diff --git a/abr-proto/proto/video_streaming/encrypted_player_request.proto b/abr-proto/proto/video_streaming/encrypted_player_request.proto new file mode 100644 index 0000000..bc553e2 --- /dev/null +++ b/abr-proto/proto/video_streaming/encrypted_player_request.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; +package video_streaming; + +import "video_streaming/onesie_player_request.proto"; + +message EncryptedPlayerRequest { + optional bytes context = 1; // InnerTubeContext proto? + optional bytes encrypted_onesie_player_request = 2; + optional bytes encrypted_client_key = 5; + optional bytes iv = 6; + optional bytes hmac = 7; + optional string reverse_proxy_config = 9; + optional bool serialize_response_as_json = 10; + optional bool pM = 13; + optional bool enable_compression = 14; + optional bytes unencrypted_onesie_player_request = 16; + optional bool TQ = 17; +} diff --git a/abr-proto/proto/video_streaming/format_initialization_metadata.proto b/abr-proto/proto/video_streaming/format_initialization_metadata.proto new file mode 100644 index 0000000..ce7e08e --- /dev/null +++ b/abr-proto/proto/video_streaming/format_initialization_metadata.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; + +message FormatInitializationMetadata { + optional string video_id = 1; + optional .misc.FormatId format_id = 2; + optional int32 end_time_ms = 3; + optional int32 sequence_count = 4; + optional string mime_type = 5; + optional .misc.InitRange init_range = 6; + optional .misc.IndexRange index_range = 7; + optional int32 field8 = 8; + optional int32 duration_ms = 9; + optional int32 field10 = 10; +} diff --git a/abr-proto/proto/video_streaming/live_metadata.proto b/abr-proto/proto/video_streaming/live_metadata.proto new file mode 100644 index 0000000..83c1540 --- /dev/null +++ b/abr-proto/proto/video_streaming/live_metadata.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; +package video_streaming; + +message LiveMetadata { + optional uint32 head_sequence_number = 3; + optional uint64 head_time_ms = 4; + optional uint64 wall_time_ms = 5; + optional uint64 field10 = 10; + optional uint64 field12 = 12; + optional uint64 field13 = 13; + optional uint64 head_time_usec = 14; + optional uint64 field15 = 15; +} diff --git a/abr-proto/proto/video_streaming/media_capabilities.proto b/abr-proto/proto/video_streaming/media_capabilities.proto new file mode 100644 index 0000000..78dfcec --- /dev/null +++ b/abr-proto/proto/video_streaming/media_capabilities.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; +package video_streaming; + +message MediaCapabilities { + repeated VideoFormatCapability video_format_capabilities = 1; + repeated AudioFormatCapability audio_format_capabilities = 2; + optional int32 hdr_mode_bitmask = 5; + + message VideoFormatCapability { + optional int32 video_codec = 1; + optional int32 max_height = 3; + optional int32 max_width = 4; + optional int32 max_framerate = 11; + optional int32 max_bitrate_bps = 12; + optional bool is_10_bit_supported = 15; + } + + message AudioFormatCapability { + optional int32 audio_codec = 1; + optional int32 num_channels = 2; + optional int32 max_bitrate_bps = 3; + optional int32 spatial_capability_bitmask = 6; + } +} diff --git a/abr-proto/proto/video_streaming/media_header.proto b/abr-proto/proto/video_streaming/media_header.proto new file mode 100644 index 0000000..58aa007 --- /dev/null +++ b/abr-proto/proto/video_streaming/media_header.proto @@ -0,0 +1,30 @@ +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; +import "video_streaming/time_range.proto"; + +message MediaHeader { + optional uint32 header_id = 1; + optional string video_id = 2; + optional int32 itag = 3; + optional uint64 lmt = 4; + optional string xtags = 5; + optional int32 start_data_range = 6; + optional Compression compression = 7; + optional bool is_init_seg = 8; + optional int64 sequence_number = 9; + optional int64 field10 = 10; + optional int32 start_ms = 11; + optional int32 duration_ms = 12; + optional .misc.FormatId format_id = 13; + optional int64 content_length = 14; + optional TimeRange time_range = 15; + + enum Compression { + VAL0 = 0; + VAL1 = 1; + GZIP = 2; + } + +} diff --git a/abr-proto/proto/video_streaming/next_request_policy.proto b/abr-proto/proto/video_streaming/next_request_policy.proto new file mode 100644 index 0000000..6556912 --- /dev/null +++ b/abr-proto/proto/video_streaming/next_request_policy.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; +package video_streaming; + +import "video_streaming/playback_cookie.proto"; + +message NextRequestPolicy { + optional int32 target_audio_readahead_ms = 1; + optional int32 target_video_readahead_ms = 2; + optional int32 backoff_time_ms = 4; + optional .video_streaming.PlaybackCookie playback_cookie = 7; + optional string video_id = 8; +} diff --git a/abr-proto/proto/video_streaming/onesie_header.proto b/abr-proto/proto/video_streaming/onesie_header.proto new file mode 100644 index 0000000..5cd8167 --- /dev/null +++ b/abr-proto/proto/video_streaming/onesie_header.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; +package video_streaming; + +import "video_streaming/onesie_header_type.proto"; +import "video_streaming/crypto_params.proto"; + +message OnesieHeader { + message Field23 { + optional string video_id = 2; + } + + message Field34 { + repeated string itag_denylist = 1; + } + + optional OnesieHeaderType type = 1; + optional string video_id = 2; + optional string itag = 3; + optional CryptoParams crypto_params = 4; + optional uint64 last_modified = 5; + optional int64 media_size_bytes = 7; + repeated string restricted_formats = 11; + optional string xtags = 15; + optional int64 sequence_number = 18; + optional Field23 field23 = 23; + optional Field34 field34 = 34; +} diff --git a/abr-proto/proto/video_streaming/onesie_header_type.proto b/abr-proto/proto/video_streaming/onesie_header_type.proto new file mode 100644 index 0000000..021f01b --- /dev/null +++ b/abr-proto/proto/video_streaming/onesie_header_type.proto @@ -0,0 +1,31 @@ +syntax = "proto2"; +package video_streaming; + +enum OnesieHeaderType { + PLAYER_RESPONSE = 0; + HEADER_TYPE_1 = 1; + MEDIA_DECRYPTION_KEY = 2; + HEADER_TYPE_3 = 3; + HEADER_TYPE_4 = 4; + HEADER_TYPE_5 = 5; + NEW_HOST = 6; + HEADER_TYPE_7 = 7; + HEADER_TYPE_8 = 8; + HEADER_TYPE_9 = 9; + HEADER_TYPE_10 = 10; + HEADER_TYPE_11 = 11; + HEADER_TYPE_12 = 12; + HEADER_TYPE_13 = 13; + RESTRICTED_FORMATS_HINT = 14; + HEADER_TYPE_15 = 15; + STREAM_METADATA = 16; + HEADER_TYPE_17 = 17; + HEADER_TYPE_18 = 18; + HEADER_TYPE_19 = 19; + HEADER_TYPE_20 = 20; + HEADER_TYPE_21 = 21; + HEADER_TYPE_22 = 22; + HEADER_TYPE_23 = 23; + HEADER_TYPE_24 = 24; + ENCRYPTED_INNERTUBE_RESPONSE_PART = 25; +} diff --git a/abr-proto/proto/video_streaming/onesie_player_request.proto b/abr-proto/proto/video_streaming/onesie_player_request.proto new file mode 100644 index 0000000..844bd66 --- /dev/null +++ b/abr-proto/proto/video_streaming/onesie_player_request.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; + +message OnesiePlayerRequest { + optional string url = 1; + repeated misc.HttpHeader headers = 2; + optional string body = 3; + optional bool proxied_by_trusted_bandaid = 4; + optional bool skip_response_encryption = 6; +} diff --git a/abr-proto/proto/video_streaming/onesie_player_response.proto b/abr-proto/proto/video_streaming/onesie_player_response.proto new file mode 100644 index 0000000..41d97a4 --- /dev/null +++ b/abr-proto/proto/video_streaming/onesie_player_response.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; + +enum OnesieProxyStatus { + ONESIE_PROXY_STATUS_UNKNOWN = 0; + ONESIE_PROXY_STATUS_OK = 1; + ONESIE_PROXY_STATUS_DECRYPTION_FAILED = 2; + ONESIE_PROXY_STATUS_PARSING_FAILED = 3; + ONESIE_PROXY_STATUS_MISSING_X_FORWARDED_FOR = 4; + ONESIE_PROXY_STATUS_INVALID_X_FORWARDED_FOR = 5; + ONESIE_PROXY_STATUS_INVALID_CONTENT_TYPE = 6; + ONESIE_PROXY_STATUS_BACKEND_ERROR = 7; + ONESIE_PROXY_STATUS_CLIENT_ERROR = 8; + ONESIE_PROXY_STATUS_MISSING_CRYPTER = 9; + ONESIE_PROXY_STATUS_RESPONSE_JSON_SERIALIZATION_FAILED = 10; + ONESIE_PROXY_STATUS_DECOMPRESSION_FAILED = 11; + ONESIE_PROXY_STATUS_JSON_PARSING_FAILED = 12; + ONESIE_PROXY_STATUS_UNKNOWN_COMPRESSION_TYPE = 13; +} + +message OnesiePlayerResponse { + optional OnesieProxyStatus onesie_proxy_status = 1; + optional int32 http_status = 2; + repeated .misc.HttpHeader headers = 3; + optional bytes body = 4; +} diff --git a/abr-proto/proto/video_streaming/onesie_request.proto b/abr-proto/proto/video_streaming/onesie_request.proto new file mode 100644 index 0000000..5dbf396 --- /dev/null +++ b/abr-proto/proto/video_streaming/onesie_request.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; +package video_streaming; + +import "video_streaming/client_abr_state.proto"; +import "video_streaming/encrypted_player_request.proto"; +import "video_streaming/streamer_context.proto"; +import "video_streaming/buffered_range.proto"; + +message OnesieRequest { + repeated string urls = 1; + optional ClientAbrState client_abr_state = 2; + optional EncryptedPlayerRequest player_request = 3; + optional bytes onesie_ustreamer_config = 4; + optional int32 max_vp9_height = 5; + optional int32 client_display_height = 6; + optional StreamerContext streamer_context = 10; + optional int32 request_target = 13; // MLOnesieRequestTarget + repeated BufferedRange buffered_ranges = 14; +} diff --git a/abr-proto/proto/video_streaming/playback_cookie.proto b/abr-proto/proto/video_streaming/playback_cookie.proto new file mode 100644 index 0000000..eb8ca09 --- /dev/null +++ b/abr-proto/proto/video_streaming/playback_cookie.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; + +message PlaybackCookie { + optional int32 field1 = 1; // Always 999999?? + optional int32 field2 = 2; + optional .misc.FormatId video_fmt = 7; + optional .misc.FormatId audio_fmt = 8; +} diff --git a/abr-proto/proto/video_streaming/playback_start_policy.proto b/abr-proto/proto/video_streaming/playback_start_policy.proto new file mode 100644 index 0000000..2b43367 --- /dev/null +++ b/abr-proto/proto/video_streaming/playback_start_policy.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; +package video_streaming; + +message PlaybackStartPolicy { + message ReadaheadPolicy { + optional int32 min_readahead_ms = 2; + optional int32 min_bandwidth_bytes_per_sec = 1; + } + + optional ReadaheadPolicy start_min_readahead_policy = 1; + optional ReadaheadPolicy resume_min_readahead_policy = 2; +} diff --git a/abr-proto/proto/video_streaming/proxy_status.proto b/abr-proto/proto/video_streaming/proxy_status.proto new file mode 100644 index 0000000..18fe610 --- /dev/null +++ b/abr-proto/proto/video_streaming/proxy_status.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; +package video_streaming; + +enum ProxyStatus { + VAL_0 = 0; + OK = 1; + VAL_2 = 2; + VAL_3 = 3; + VAL_4 = 4; + VAL_5 = 5; + VAL_6 = 6; + VAL_7 = 7; + VAL_8 = 8; + VAL_9 = 9; +} diff --git a/abr-proto/proto/video_streaming/reload_player_response.proto b/abr-proto/proto/video_streaming/reload_player_response.proto new file mode 100644 index 0000000..27937c7 --- /dev/null +++ b/abr-proto/proto/video_streaming/reload_player_response.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; +package video_streaming; + +message ReloadPlaybackParams { + optional string token = 1; +} + +message ReloadPlayerResponse { + optional ReloadPlaybackParams reload_playback_params = 1; +} diff --git a/abr-proto/proto/video_streaming/request_cancellation_policy.proto b/abr-proto/proto/video_streaming/request_cancellation_policy.proto new file mode 100644 index 0000000..110c259 --- /dev/null +++ b/abr-proto/proto/video_streaming/request_cancellation_policy.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; +package video_streaming; + +message RequestCancellationPolicy { + message Item { + optional int32 fR = 1; + optional int32 NK = 2; + optional int32 minReadaheadMs = 3; + } + + optional int32 N0 = 1; + repeated Item items = 2; + optional int32 jq = 3; +} diff --git a/abr-proto/proto/video_streaming/sabr_error.proto b/abr-proto/proto/video_streaming/sabr_error.proto new file mode 100644 index 0000000..f05311b --- /dev/null +++ b/abr-proto/proto/video_streaming/sabr_error.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; +package video_streaming; + +message SabrError { + optional string type = 1; + optional int32 code = 2; +} diff --git a/abr-proto/proto/video_streaming/sabr_redirect.proto b/abr-proto/proto/video_streaming/sabr_redirect.proto new file mode 100644 index 0000000..e660ad3 --- /dev/null +++ b/abr-proto/proto/video_streaming/sabr_redirect.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; +package video_streaming; + +message SabrRedirect { + optional string url = 1; +} diff --git a/abr-proto/proto/video_streaming/stream_protection_status.proto b/abr-proto/proto/video_streaming/stream_protection_status.proto new file mode 100644 index 0000000..2f2cba4 --- /dev/null +++ b/abr-proto/proto/video_streaming/stream_protection_status.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; +package video_streaming; + +message StreamProtectionStatus { + optional int32 status = 1; + optional int32 field2 = 2; +} diff --git a/abr-proto/proto/video_streaming/streamer_context.proto b/abr-proto/proto/video_streaming/streamer_context.proto new file mode 100644 index 0000000..3a766c3 --- /dev/null +++ b/abr-proto/proto/video_streaming/streamer_context.proto @@ -0,0 +1,66 @@ +syntax = "proto2"; +package video_streaming; + +message StreamerContext { + message ClientInfo { + optional string device_make = 12; + optional string device_model = 13; + optional int32 client_name = 16; + optional string client_version = 17; + optional string os_name = 18; + optional string os_version = 19; + optional string accept_language = 21; + optional string accept_region = 22; + optional int32 screen_width_points = 37; + optional int32 screen_height_points = 38; + optional float screen_width_inches = 39; + optional float screen_height_inches = 40; + optional int32 screen_pixel_density = 41; + optional ClientFormFactor client_form_factor = 46; + optional int32 gmscore_version_code = 50; // e.g. 243731017 + optional int32 window_width_points = 55; + optional int32 window_height_points = 56; + optional int32 android_sdk_version = 64; + optional float screen_density_float = 65; + optional int64 utc_offset_minutes = 67; + optional string time_zone = 80; + optional string chipset = 92; // e.g. "qcom;taro" + optional GLDeviceInfo gl_device_info = 102; + } + + enum ClientFormFactor { + UNKNOWN_FORM_FACTOR = 0; + FORM_FACTOR_VAL1 = 1; + FORM_FACTOR_VAL2 = 2; + } + + message GLDeviceInfo { + optional string gl_renderer = 1; + optional int32 gl_es_version_major = 2; + optional int32 gl_es_version_minor = 3; + } + + message Fqa { + optional int32 type = 1; + optional bytes value = 2; + } + + message Gqa { + message Hqa { + optional int32 code = 1; + optional string message = 2; + } + + optional bytes field1 = 1; + optional Hqa field2 = 2; + } + + optional ClientInfo client_info = 1; + optional bytes po_token = 2; + optional bytes playback_cookie = 3; + optional bytes gp = 4; + repeated Fqa field5 = 5; + repeated int32 field6 = 6; + optional string field7 = 7; + optional Gqa field8 = 8; +} diff --git a/abr-proto/proto/video_streaming/time_range.proto b/abr-proto/proto/video_streaming/time_range.proto new file mode 100644 index 0000000..e85d2f3 --- /dev/null +++ b/abr-proto/proto/video_streaming/time_range.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; +package video_streaming; + +message TimeRange { + optional int64 start = 1; + optional int64 duration = 2; + optional int32 timescale = 3; +} diff --git a/abr-proto/proto/video_streaming/video_playback_abr_request.proto b/abr-proto/proto/video_streaming/video_playback_abr_request.proto new file mode 100644 index 0000000..cc16499 --- /dev/null +++ b/abr-proto/proto/video_streaming/video_playback_abr_request.proto @@ -0,0 +1,51 @@ +syntax = "proto2"; +package video_streaming; + +import "misc/common.proto"; +import "video_streaming/client_abr_state.proto"; +import "video_streaming/streamer_context.proto"; +import "video_streaming/buffered_range.proto"; + +message VideoPlaybackAbrRequest { + optional ClientAbrState client_abr_state = 1; + repeated .misc.FormatId selected_format_ids = 2; + repeated BufferedRange buffered_ranges = 3; + optional int64 player_time_ms = 4; + optional bytes video_playback_ustreamer_config = 5; + optional Lo lo = 6; + repeated .misc.FormatId selected_audio_format_ids = 16; + repeated .misc.FormatId selected_video_format_ids = 17; + optional StreamerContext streamer_context = 19; + optional OQa field21 = 21; + optional int32 field22 = 22; + optional int32 field23 = 23; + repeated Pqa field1000 = 1000; +} + +message Lo { + message Field4 { + optional int32 field1 = 1; + optional int32 field2 = 2; + optional int32 field3 = 3; + } + optional .misc.FormatId format_id = 1; + optional int32 Lj = 2; + optional int32 sequence_number = 3; + optional Field4 field4 = 4; + optional int32 MZ = 5; +} + +message OQa { + repeated string field1 = 1; + optional bytes field2 = 2; + optional string field3 = 3; + optional int32 field4 = 4; + optional int32 field5 = 5; + optional string field6 = 6; +} + +message Pqa { + repeated .misc.FormatId formats = 1; + repeated BufferedRange ud = 2; + optional string clip_id = 3; +} diff --git a/abr-proto/src/lib.rs b/abr-proto/src/lib.rs new file mode 100644 index 0000000..16a04e9 --- /dev/null +++ b/abr-proto/src/lib.rs @@ -0,0 +1,3 @@ +#![doc = include_str!("../README.md")] + +include!(concat!(env!("OUT_DIR"), "/mod.rs")); diff --git a/codegen/src/abtest.rs b/codegen/src/abtest.rs index de8001a..8c5bd86 100644 --- a/codegen/src/abtest.rs +++ b/codegen/src/abtest.rs @@ -42,6 +42,7 @@ pub enum ABTest { MusicContinuationItemRenderer = 20, AlbumRecommends = 21, CommandExecutorCommand = 22, + AbrStreamOnly = 23, } /// List of active A/B tests that are run when none is manually specified @@ -49,6 +50,7 @@ const TESTS_TO_RUN: &[ABTest] = &[ ABTest::MusicAlbumGroupsReordered, ABTest::AlbumRecommends, ABTest::CommandExecutorCommand, + ABTest::AbrStreamOnly, ]; #[derive(Debug, Serialize, Deserialize)] @@ -126,6 +128,7 @@ pub async fn run_test( } ABTest::AlbumRecommends => album_recommends(&query).await, ABTest::CommandExecutorCommand => command_executor_command(&query).await, + ABTest::AbrStreamOnly => abr_stream_only(&query).await, } .unwrap(); pb.inc(1); @@ -478,3 +481,13 @@ pub async fn command_executor_command(rp: &RustyPipeQuery) -> Result { .await?; Ok(res.contains("\"commandExecutorCommand\"")) } + +pub async fn abr_stream_only(rp: &RustyPipeQuery) -> Result { + let id = "pxY4OXVyMe4"; + let res = rp + .clone() + .allow_abr_only() + .player_from_client(id, ClientType::Desktop) + .await?; + Ok(res.video_only_streams.iter().all(|s| s.url.is_none())) +} diff --git a/downloader/Cargo.toml b/downloader/Cargo.toml index dba74ce..bd0b0f7 100644 --- a/downloader/Cargo.toml +++ b/downloader/Cargo.toml @@ -34,13 +34,14 @@ audiotag = ["dep:lofty", "dep:image", "dep:smartcrop2"] [dependencies] rustypipe.workspace = true +rustypipe-abr-proto.workspace = true once_cell.workspace = true regex.workspace = true thiserror.workspace = true futures-util.workspace = true reqwest = { workspace = true, features = ["stream"] } rand.workspace = true -tokio = { workspace = true, features = ["macros", "fs", "process"] } +tokio = { workspace = true, features = ["macros", "fs", "process", "rt-multi-thread"] } indicatif = { workspace = true, optional = true } filenamify.workspace = true tracing.workspace = true @@ -52,10 +53,21 @@ image = { version = "0.25.0", optional = true, default-features = false, feature "webp", ] } smartcrop2 = { version = "0.4.0", optional = true } +data-encoding.workspace = true +protobuf.workspace = true +bytes = "1.0.0" +byteorder = "1.0.0" +async-stream = "0.3.6" +pin-project-lite = "0.2.11" + +serde.workspace = true +serde_json.workspace = true +tracing-subscriber.workspace = true [dev-dependencies] path_macro.workspace = true rstest.workspace = true +tracing-test.workspace = true serde_json.workspace = true temp_testdir = "0.2.3" diff --git a/downloader/src/abr.rs b/downloader/src/abr.rs new file mode 100644 index 0000000..350d9b0 --- /dev/null +++ b/downloader/src/abr.rs @@ -0,0 +1,676 @@ +#![allow(unused)] + +use std::collections::{HashMap, HashSet}; + +use bytes::Bytes; +use data_encoding::BASE64URL; +use protobuf::{Message, MessageField}; +use reqwest::Client; +use rustypipe::model::{AudioStream, VideoStream}; + +pub use rustypipe_abr_proto::client_abr_state::ClientAbrState; +use rustypipe_abr_proto::{ + buffered_range::BufferedRange, + common::FormatId, + format_initialization_metadata::FormatInitializationMetadata, + media_header::MediaHeader, + next_request_policy::NextRequestPolicy, + playback_cookie::PlaybackCookie, + sabr_error::SabrError, + sabr_redirect::SabrRedirect, + stream_protection_status::StreamProtectionStatus, + streamer_context::{streamer_context::ClientInfo, StreamerContext}, + time_range::TimeRange, + video_playback_abr_request::VideoPlaybackAbrRequest, +}; + +use crate::abr_model::PartType; +use crate::error::AbrError; + +pub struct AbrStream { + http: Client, + initial_url: String, + ustreamer_config: Vec, + po_token: Option>, +} + +#[derive(Debug)] +pub struct StreamingState { + abr_streaming_url: String, + abr_state: ClientAbrState, + playback_cookie: Option, + backoff_time_ms: i32, + formats_by_key: HashMap, + header_id_to_format_key_map: HashMap, + previous_sequences: HashMap>, +} + +struct InitializedFormat { + format_id: FormatId, + duration_ms: Option, + mime_type: Option, + sequence_count: Option, + sequence_list: Vec, + media_chunks: Vec, + state: BufferedRange, +} + +#[derive(Debug)] +struct Sequence { + itag: Option, + format_id: FormatId, + is_init_segment: bool, + duration_ms: Option, + start_ms: Option, + start_data_range: Option, + sequence_number: Option, + content_length: Option, + time_range: Option, +} + +struct UmpPart<'a> { + part_type: PartType, + size: u64, + data: &'a [u8], +} + +impl std::fmt::Debug for InitializedFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InitializedFormat") + .field("format_id", &self.format_id) + .field("duration_ms", &self.duration_ms) + .field("mime_type", &self.mime_type) + .field("sequence_count", &self.sequence_count) + .field("sequence_list", &self.sequence_list) + .field("media_chunks", &self.media_chunks.len()) + .field("state", &self.state) + .finish() + } +} + +impl std::fmt::Debug for UmpPart<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UmpPart") + .field("part_type", &self.part_type) + .field("size", &self.size) + .field("data", &self.data.len()) + .finish() + } +} + +impl AbrStream { + pub fn new( + url: String, + ustreamer_config: &str, + po_token: Option<&str>, + http: Client, + ) -> Result { + let ustreamer_config = BASE64URL.decode(ustreamer_config.as_bytes()).map_err(|e| { + AbrError::Invalid(format!("could not parse ustreamer_config: {e}").into()) + })?; + let po_token = + match po_token { + Some(pot) => Some(BASE64URL.decode(pot.as_bytes()).map_err(|e| { + AbrError::Invalid(format!("could not parse po_token: {e}").into()) + })?), + None => None, + }; + Ok(Self { + http, + initial_url: url, + ustreamer_config, + po_token, + }) + } + + pub async fn init( + &self, + audio_streams: &[&AudioStream], + video_streams: &[&VideoStream], + mut abr_state: ClientAbrState, + ) -> Result { + if !abr_state.has_last_manual_direction() { + abr_state.set_last_manual_direction(0); + } + if !abr_state.has_time_since_last_manual_format_selection_ms() { + abr_state.set_time_since_last_manual_format_selection_ms(0); + } + if !abr_state.has_player_time_ms() { + abr_state.set_player_time_ms(0); + } + if !abr_state.has_visibility() { + abr_state.set_visibility(0); + } + if !abr_state.has_enabled_track_types_bitfield() { + abr_state.set_enabled_track_types_bitfield(i32::from(video_streams.is_empty())); + } + if let Some(v) = video_streams.first() { + if !abr_state.has_last_manual_selected_resolution() { + abr_state.set_last_manual_selected_resolution(v.height as i32); + } + if !abr_state.has_sticky_resolution() { + abr_state.set_sticky_resolution(v.height as i32); + } + } + + let audio_format_ids = audio_streams + .iter() + .map(|s| FormatId { + itag: Some(s.itag as i32), + last_modified: s.last_modified, + xtags: s.xtags.clone(), + ..Default::default() + }) + .collect::>(); + let video_format_ids = video_streams + .iter() + .map(|s| FormatId { + itag: Some(s.itag as i32), + last_modified: s.last_modified, + xtags: s.xtags.clone(), + ..Default::default() + }) + .collect::>(); + let mut state = StreamingState::new(self.initial_url.to_owned(), abr_state); + let mut ri = 0; + + loop { + let data = self + .fetch_media(audio_format_ids.clone(), video_format_ids.clone(), &state) + .await?; + tracing::debug!("request #{ri} fetched {} bytes", data.len()); + + let ump_parts = Self::parse_ump(&data); + if state.process_ump_parts(&ump_parts)? { + break; + } + ri += 1; + } + + Ok(state) + } + + async fn fetch_media( + &self, + audio_format_ids: Vec, + video_format_ids: Vec, + state: &StreamingState, + ) -> Result { + let body = VideoPlaybackAbrRequest { + client_abr_state: MessageField::some(state.abr_state.clone()), + selected_format_ids: state + .formats_by_key + .values() + .map(|f| f.format_id.clone()) + .collect(), + buffered_ranges: state + .formats_by_key + .values() + .map(|f| f.state.clone()) + .collect(), + // player_time_ms: todo!(), + video_playback_ustreamer_config: Some(self.ustreamer_config.clone()), + selected_audio_format_ids: audio_format_ids, + selected_video_format_ids: video_format_ids, + streamer_context: MessageField::some(StreamerContext { + client_info: MessageField::some(ClientInfo { + client_name: Some(1), + client_version: Some("2.20250314.01.00".to_owned()), + ..Default::default() + }), + po_token: self.po_token.clone(), + playback_cookie: state + .playback_cookie + .as_ref() + .and_then(|c| c.write_to_bytes().ok()), + ..Default::default() + }), + ..Default::default() + }; + + let res = self + .http + .post(&state.abr_streaming_url) + .body(body.write_to_bytes()?) + .send() + .await? + .error_for_status()?; + + // let dstream = res.bytes_stream(); + let data = res.bytes().await?; + tracing::debug!("got {} bytes", data.len()); + + Ok(data) + } + + fn parse_ump(data: &[u8]) -> Vec> { + let mut parts = Vec::new(); + let mut offset = 0; + while offset < data.len() { + let (part_type, new_offset) = Self::read_varint(&data[offset..]); + if new_offset == 0 { + break; + } + offset += new_offset; + let (part_size, new_offset) = Self::read_varint(&data[offset..]); + if new_offset == 0 { + break; + } + offset += new_offset; + + let data_offset = offset + part_size as usize; + let part_data = &data[offset..data_offset.min(data.len())]; + offset = data_offset; + + parts.push(UmpPart { + part_type: part_type.into(), + size: part_size, + data: part_data, + }); + } + parts + } + + fn read_varint(data: &[u8]) -> (u64, usize) { + /* + fn varint_size(byte: u8) -> u8 { + let mut lo = 0; + for i in (4u8..8).rev() { + if byte & (1 << i) == 0 { + break; + } else { + lo += 1; + } + } + return (lo + 1).min(5); + } + + if data.is_empty() { + return (0, 0); + } + + let prefix = data[0]; + let size = varint_size(prefix); + + let mut result = 0u64; + let mut shift = 0; + + if size != 5 { + shift = 8 - size; + let mask = (1 << shift) - 1; + result |= u64::from(prefix) & mask; + } + + let total_size = usize::from(size) + 1; + for byte in &data[1..total_size] { + result |= u64::from(*byte) << shift; + shift += 8; + } + + (result, total_size) + */ + + // Determine the length of the val + + if data.is_empty() { + return (0, 0); + } + + let fb = data[0]; + let byte_length = if fb < 128 { + 1 + } else if fb < 192 { + 2 + } else if fb < 224 { + 3 + } else if fb < 240 { + 4 + } else { + 5 + }; + + if data.len() < byte_length { + return (0, 0); + } + + let n = match byte_length { + 1 => data[0].into(), + 2 => { + let b1 = u64::from(data[0]); + let b2 = u64::from(data[1]); + (b1 & 0x3f) + 64 * b2 + } + 3 => { + let b1 = u64::from(data[0]); + let b2 = u64::from(data[1]); + let b3 = u64::from(data[2]); + (b1 & 0x1f) + 32 * (b2 + 256 * b3) + } + 4 => { + let b1 = u64::from(data[0]); + let b2 = u64::from(data[1]); + let b3 = u64::from(data[2]); + let b4 = u64::from(data[3]); + (b1 & 0x0f) + 16 * (b2 + 256 * (b3 + 256 * b4)) + } + _ => u32::from_be_bytes([data[1], data[2], data[3], data[4]]).into(), + }; + (n, byte_length) + } +} + +impl StreamingState { + fn new(url: String, abr_state: ClientAbrState) -> Self { + Self { + abr_streaming_url: url, + abr_state, + playback_cookie: None, + backoff_time_ms: 0, + formats_by_key: HashMap::new(), + header_id_to_format_key_map: HashMap::new(), + previous_sequences: HashMap::new(), + } + } + + fn process_ump_parts(&mut self, ump_parts: &[UmpPart]) -> Result { + for f in self.formats_by_key.values_mut() { + f.sequence_list.clear(); + } + + for part in ump_parts { + match part.part_type { + PartType::MEDIA_HEADER => { + self.process_media_header(part.data)?; + } + PartType::MEDIA => { + self.process_media_data(part.data)?; + } + PartType::MEDIA_END => { + self.process_media_end(part.data); + } + PartType::NEXT_REQUEST_POLICY => { + self.process_next_request_policy(part.data)?; + } + PartType::FORMAT_INITIALIZATION_METADATA => { + self.process_format_initialization(part.data)?; + } + PartType::SABR_ERROR => { + return Err(AbrError::Sabr(SabrError::parse_from_bytes(part.data)?)); + } + PartType::SABR_REDIRECT => { + self.process_sabr_redirect(part.data)?; + } + PartType::STREAM_PROTECTION_STATUS => { + let prot = StreamProtectionStatus::parse_from_bytes(part.data)?; + match prot.status() { + 1 => tracing::debug!("[StreamProtectionStatus]: Good"), + 2 => tracing::warn!("[StreamProtectionStatus]: Attestation pending"), + 3 => return Err(AbrError::Attestation), + _ => {} + } + } + _ => {} + } + } + + let main_format = self + .formats_by_key + .values() + .find(|fmt| { + fmt.mime_type + .as_deref() + .map(|t| t.contains("video")) + .unwrap_or_default() + }) + .or(self.formats_by_key.values().next()); + if let Some(main_format) = main_format { + self.abr_state.set_player_time_ms( + self.abr_state.player_time_ms() + + main_format.sequence_list.iter().fold(0, |acc, seq| { + acc + i64::from(seq.duration_ms.unwrap_or_default()) + }), + ); + + tracing::debug!( + "sequence_count={}, last={}", + main_format.sequence_count.unwrap_or_default(), + main_format + .sequence_list + .last() + .and_then(|x| x.sequence_number) + .unwrap_or_default() + ); + + let is_last = main_format + .sequence_count + .zip(main_format.sequence_list.last()) + .map(|(sc, last)| last.sequence_number == Some(sc.into())) + .unwrap_or_default(); + Ok(is_last) + } else { + Ok(true) + } + } + + fn process_media_header(&mut self, data: &[u8]) -> Result<(), AbrError> { + let mh = MediaHeader::parse_from_bytes(data)?; + + let format_id = mh + .format_id + .as_ref() + .ok_or(AbrError::Invalid("media header: no format_id".into()))?; + let header_id = mh + .header_id + .and_then(|hid| u8::try_from(hid).ok()) + .ok_or(AbrError::Invalid("media header: no header_id".into()))?; + + let format_key = get_format_key(format_id); + + if !self.formats_by_key.contains_key(&format_key) { + self.formats_by_key + .insert(format_key.to_owned(), InitializedFormat::try_from(&mh)?); + } + let current_format = self.formats_by_key.get_mut(&format_key).unwrap(); + + // This is a hacky workaround to prevent duplicate sequences from being added. This should be fixed in the future + // (preferably by figuring out how to make the server not send duplicates). + if let Some(sequence_number) = mh.sequence_number { + let pseq = self + .previous_sequences + .entry(format_key.to_owned()) + .or_default(); + if !pseq.insert(sequence_number) { + tracing::warn!("duplicate sequence {sequence_number}"); + return Ok(()); + } + } + + // Save the header's ID so we can identify its stream data later. + self.header_id_to_format_key_map + .entry(header_id) + .or_insert(format_key); + + if current_format + .sequence_list + .iter() + .all(|x| x.sequence_number != Some(mh.sequence_number())) + { + current_format.sequence_list.push(Sequence { + itag: mh.itag, + format_id: format_id.clone(), + is_init_segment: mh.is_init_seg(), + duration_ms: mh.duration_ms, + start_ms: mh.start_ms, + start_data_range: mh.start_data_range, + sequence_number: mh.sequence_number, + content_length: mh.content_length, + time_range: mh.time_range.as_ref().cloned(), + }); + + if mh.has_sequence_number() { + current_format.state.set_duration_ms( + current_format.state.duration_ms() + i64::from(mh.duration_ms()), + ); + current_format + .state + .set_end_segment_index(current_format.state.end_segment_index() + 1); + } + } + + Ok(()) + } + + fn process_media_data(&mut self, data: &[u8]) -> Result<(), AbrError> { + let header_id = data[0]; + let format_key = self + .header_id_to_format_key_map + .get(&header_id) + .ok_or_else(|| { + AbrError::Invalid(format!("media: unknown header id {header_id}").into()) + })?; + let current_format = self + .formats_by_key + .get_mut(format_key) + .ok_or(AbrError::Invalid("no current format".into()))?; + + current_format.media_chunks.extend_from_slice(&data[1..]); + Ok(()) + } + + fn process_media_end(&mut self, data: &[u8]) { + let header_id = data[0]; + self.header_id_to_format_key_map.remove(&header_id); + } + + fn process_next_request_policy(&mut self, data: &[u8]) -> Result<(), AbrError> { + let mut policy = NextRequestPolicy::parse_from_bytes(data)?; + self.playback_cookie = policy.playback_cookie.take(); + self.backoff_time_ms = policy.backoff_time_ms(); + Ok(()) + } + + fn process_format_initialization(&mut self, data: &[u8]) -> Result<(), AbrError> { + let init = FormatInitializationMetadata::parse_from_bytes(data)?; + let format = InitializedFormat::try_from(init)?; + let format_key = get_format_key(&format.format_id); + self.formats_by_key.insert(format_key, format); + Ok(()) + } + + fn process_sabr_redirect(&mut self, data: &[u8]) -> Result<(), AbrError> { + let redir = SabrRedirect::parse_from_bytes(data)?; + self.abr_streaming_url = redir + .url + .ok_or(AbrError::Invalid("sabr redirect: no URL".into()))?; + Ok(()) + } +} + +fn get_format_key(format_id: &FormatId) -> String { + format!("{};{};", format_id.itag(), format_id.last_modified()) +} + +impl TryFrom<&MediaHeader> for InitializedFormat { + type Error = AbrError; + + fn try_from(value: &MediaHeader) -> Result { + let format_id = value + .format_id + .as_ref() + .ok_or(AbrError::Invalid("media header: no format_id".into()))?; + Ok(InitializedFormat { + format_id: format_id.clone(), + duration_ms: value.duration_ms, + mime_type: None, + sequence_count: None, + sequence_list: Vec::new(), + media_chunks: Vec::new(), + state: BufferedRange { + format_id: MessageField::some(format_id.clone()), + start_time_ms: Some(0), + duration_ms: Some(0), + start_segment_index: Some(1), + end_segment_index: Some(0), + ..Default::default() + }, + }) + } +} + +impl TryFrom for InitializedFormat { + type Error = AbrError; + + fn try_from(value: FormatInitializationMetadata) -> Result { + let format_id = *value.format_id.0.ok_or(AbrError::Invalid( + "format initialization: no format_id".into(), + ))?; + Ok(InitializedFormat { + format_id: format_id.clone(), + duration_ms: value.duration_ms, + mime_type: value.mime_type.clone(), + sequence_count: value.sequence_count, + sequence_list: Vec::new(), + media_chunks: Vec::new(), + state: BufferedRange { + format_id: MessageField::some(format_id), + start_time_ms: Some(0), + duration_ms: Some(0), + start_segment_index: Some(1), + end_segment_index: Some(0), + ..Default::default() + }, + }) + } +} + +#[cfg(test)] +mod tests { + use rustypipe::{ + client::RustyPipe, + model::{AudioCodec, VideoFormat}, + param::StreamFilter, + }; + + use crate::abr::{AbrStream, StreamingState}; + + #[tokio::test] + #[tracing_test::traced_test] + async fn experiment() { + let rp = RustyPipe::new(); + let player = rp.query().player("UMm7Pq0BdRg").await.unwrap(); + let stream_filter = StreamFilter::new() + .allow_abr_only() + .video_max_res(360) + .video_formats([VideoFormat::Webm]) + .audio_codecs([AudioCodec::Opus]); + let (video, audio) = player.select_video_audio_stream(&stream_filter); + let _video = video.unwrap(); + let audio = audio.unwrap(); + + let http = reqwest::Client::new(); + let abr = AbrStream::new( + player.abr_streaming_url.clone().unwrap(), + player.abr_ustreamer_config.as_deref().unwrap(), + player.po_token.as_deref(), + http, + ) + .unwrap(); + let state = abr.init(&[audio], &[], Default::default()).await.unwrap(); + dbg!(&state); + + // let f = state.formats_by_key.values().next().unwrap(); + // std::fs::write("test.webm", &f.media_chunks); + } + + #[test] + #[tracing_test::traced_test] + fn parse_ump() { + let data = std::fs::read("abr.bin").unwrap(); + let ump_parts = AbrStream::parse_ump(&data); + dbg!(&ump_parts); + + let mut state = StreamingState::new(Default::default(), Default::default()); + assert!(!state.process_ump_parts(&ump_parts).unwrap()); + dbg!(&state); + } +} diff --git a/downloader/src/abr2.rs b/downloader/src/abr2.rs new file mode 100644 index 0000000..0c78c4c --- /dev/null +++ b/downloader/src/abr2.rs @@ -0,0 +1,800 @@ +#![allow(unused, missing_docs)] + +use std::{ + collections::{HashMap, HashSet}, + fs::File, + io::Write, + marker::PhantomData, + pin::Pin, + task::{ready, Poll}, +}; + +use async_stream::try_stream; +use bytes::{Buf, Bytes}; +use data_encoding::BASE64URL; +use futures_util::{stream::BoxStream, FutureExt, Stream, StreamExt, TryStreamExt}; +use protobuf::{Message, MessageField}; +use reqwest::Client; +use rustypipe::model::{AudioStream, VideoStream}; +use rustypipe_abr_proto::{ + buffered_range::BufferedRange, + client_abr_state::ClientAbrState, + common::FormatId, + format_initialization_metadata::FormatInitializationMetadata, + media_header::MediaHeader, + next_request_policy::NextRequestPolicy, + playback_cookie::PlaybackCookie, + reload_player_response::ReloadPlayerResponse, + sabr_error::SabrError, + sabr_redirect::SabrRedirect, + stream_protection_status::StreamProtectionStatus, + streamer_context::{streamer_context::ClientInfo, StreamerContext}, + video_playback_abr_request::VideoPlaybackAbrRequest, +}; +use serde::{Deserialize, Serialize}; + +use crate::{abr_model::PartType, error::AbrError, util::BytesBuffer}; + +pub struct AbrStream { + http: Client, + url: String, + ustreamer_config: Vec, + po_token: Option>, + abr_state: ClientAbrState, + audio_format_ids: Vec, + video_format_ids: Vec, + playback_cookie: Option, + backoff_time_ms: i32, + formats_by_key: HashMap, + header_id_to_format_key_map: HashMap, + previous_sequences: HashMap>, + buffer: BytesBuffer, + last_header: Option, +} + +#[derive(Default, Clone)] +pub struct AbrStreamOptions<'a> { + pub http: Client, + pub url: String, + pub ustreamer_config: &'a str, + pub po_token: Option<&'a str>, + pub audio_streams: &'a [&'a AudioStream], + pub video_streams: &'a [&'a VideoStream], + pub player_time: i64, + pub abr_state: ClientAbrState, +} + +#[derive(Debug, Clone)] +pub struct AbrStreamItem { + pub format: FormatInfo, + pub data: Bytes, +} + +#[derive(Debug, Clone)] +pub struct FormatInfo { + pub itag: i32, + pub last_modified: u64, + pub xtags: Option, + pub mime_type: Option, +} + +#[derive(Debug)] +struct InitializedFormat { + format_id: FormatId, + duration_ms: Option, + mime_type: Option, + sequence_count: Option, + sequence_list: Vec, + sequence_list2: Vec, + state: BufferedRange, +} + +#[derive(Debug, Clone, Serialize)] +struct Sequence { + pub itag: Option, + #[serde(skip)] + pub format_id: FormatId, + pub is_init_segment: bool, + pub duration_ms: Option, + pub start_ms: Option, + pub start_data_range: Option, + pub sequence_number: Option, + pub content_length: Option, + pub time_range: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct TimeRange { + pub start: i64, + pub duration: i64, + pub timescale: i32, +} + +#[derive(Debug, Clone)] +struct UmpHeader { + part_type: PartType, + size: u64, + header_id: Option, +} + +impl From<&InitializedFormat> for FormatInfo { + fn from(value: &InitializedFormat) -> Self { + Self { + itag: value.format_id.itag(), + last_modified: value.format_id.last_modified(), + xtags: value.format_id.xtags.clone(), + mime_type: value.mime_type.clone(), + } + } +} + +impl AbrStream { + pub fn new(options: AbrStreamOptions) -> Result { + let ustreamer_config = BASE64URL + .decode(options.ustreamer_config.as_bytes()) + .map_err(|e| { + AbrError::Invalid(format!("could not parse ustreamer_config: {e}").into()) + })?; + let po_token = + match options.po_token { + Some(pot) => Some(BASE64URL.decode(pot.as_bytes()).map_err(|e| { + AbrError::Invalid(format!("could not parse po_token: {e}").into()) + })?), + None => None, + }; + + let mut abr_state = options.abr_state; + if !abr_state.has_last_manual_direction() { + abr_state.set_last_manual_direction(0); + } + if !abr_state.has_time_since_last_manual_format_selection_ms() { + abr_state.set_time_since_last_manual_format_selection_ms(0); + } + if !abr_state.has_player_time_ms() { + abr_state.set_player_time_ms(options.player_time); + } + if !abr_state.has_visibility() { + abr_state.set_visibility(0); + } + if !abr_state.has_enabled_track_types_bitfield() { + abr_state.set_enabled_track_types_bitfield(i32::from(options.video_streams.is_empty())); + } + if !abr_state.has_audio_track_id() { + if let Some(audio_track) = options.audio_streams.first().and_then(|s| s.track.as_ref()) + { + abr_state.set_audio_track_id(audio_track.id.to_owned()); + } + } + if let Some(v) = options.video_streams.first() { + if !abr_state.has_last_manual_selected_resolution() { + abr_state.set_last_manual_selected_resolution(v.height as i32); + } + if !abr_state.has_sticky_resolution() { + abr_state.set_sticky_resolution(v.height as i32); + } + } + if !abr_state.has_drc_enabled() { + if let Some(audio) = options.audio_streams.first() { + abr_state.set_drc_enabled(audio.is_drc); + } + } + + let audio_format_ids = options + .audio_streams + .iter() + .map(|s| FormatId { + itag: Some(s.itag as i32), + last_modified: s.last_modified, + xtags: s.xtags.clone(), + ..Default::default() + }) + .collect::>(); + let video_format_ids = options + .video_streams + .iter() + .map(|s| FormatId { + itag: Some(s.itag as i32), + last_modified: s.last_modified, + xtags: s.xtags.clone(), + ..Default::default() + }) + .collect::>(); + + Ok(Self { + http: options.http, + url: options.url, + ustreamer_config, + po_token, + abr_state, + audio_format_ids, + video_format_ids, + playback_cookie: None, + backoff_time_ms: 0, + formats_by_key: HashMap::new(), + header_id_to_format_key_map: HashMap::new(), + previous_sequences: HashMap::new(), + buffer: BytesBuffer::new(), + last_header: None, + }) + } + + fn read_varint(&mut self, offset: usize) -> (u64, usize) { + // Determine the length of the val + let buffer_len = self.buffer.remaining().saturating_sub(offset); + if buffer_len == 0 { + return (0, 0); + } + + let fb = self.buffer[offset]; + let byte_length = if fb < 128 { + 1 + } else if fb < 192 { + 2 + } else if fb < 224 { + 3 + } else if fb < 240 { + 4 + } else { + 5 + }; + + if buffer_len < byte_length { + return (0, 0); + } + + let n = match byte_length { + 1 => self.buffer[offset].into(), + 2 => { + let b1 = u64::from(self.buffer[offset]); + let b2 = u64::from(self.buffer[offset + 1]); + (b1 & 0x3f) + 64 * b2 + } + 3 => { + let b1 = u64::from(self.buffer[offset]); + let b2 = u64::from(self.buffer[offset + 1]); + let b3 = u64::from(self.buffer[offset + 2]); + (b1 & 0x1f) + 32 * (b2 + 256 * b3) + } + 4 => { + let b1 = u64::from(self.buffer[offset]); + let b2 = u64::from(self.buffer[offset + 1]); + let b3 = u64::from(self.buffer[offset + 2]); + let b4 = u64::from(self.buffer[offset + 3]); + (b1 & 0x0f) + 16 * (b2 + 256 * (b3 + 256 * b4)) + } + _ => u32::from_be_bytes([ + self.buffer[offset + 1], + self.buffer[offset + 2], + self.buffer[offset + 3], + self.buffer[offset + 4], + ]) + .into(), + }; + (n, offset + byte_length) + } + + fn parse_ump_header(&mut self) -> Option { + let (part_type, o1) = self.read_varint(0); + if o1 == 0 { + return None; + } + let (part_size, o2) = self.read_varint(o1); + if o2 == 0 { + return None; + } + self.buffer.advance(o2); + + Some(UmpHeader { + part_type: part_type.into(), + size: part_size, + header_id: None, + }) + } + + fn clear_sequence_list(&mut self) { + for f in self.formats_by_key.values_mut() { + f.sequence_list.clear(); + } + } + + fn process_ump_part(&mut self) -> Result<(Option, bool), AbrError> { + if let Some(last_header) = self.last_header.clone() { + let hsize = last_header.size as usize; + if last_header.part_type == PartType::MEDIA { + // Media data may be processed if the part is only partially downloaded + self.process_media_data().map(|x| (x, true)) + } else if self.buffer.remaining() < hsize { + // Wait for the entire part to be downloaded + tracing::info!("waiting for entire part"); + Ok((None, true)) + } else { + let part_data = self.buffer.copy_to_bytes(hsize); + match last_header.part_type { + PartType::MEDIA_HEADER => self.process_media_header(&part_data)?, + PartType::MEDIA_END => self.process_media_end(&part_data), + PartType::NEXT_REQUEST_POLICY => { + self.process_next_request_policy(&part_data)? + } + PartType::RELOAD_PLAYER_RESPONSE => self.process_reload_player(&part_data)?, + PartType::FORMAT_INITIALIZATION_METADATA => { + self.process_format_initialization(&part_data)? + } + PartType::SABR_ERROR => { + return Err(AbrError::Sabr(SabrError::parse_from_bytes(&part_data)?)); + } + PartType::SABR_REDIRECT => self.process_sabr_redirect(&part_data)?, + PartType::STREAM_PROTECTION_STATUS => { + let prot = StreamProtectionStatus::parse_from_bytes(&part_data)?; + match prot.status() { + 1 => tracing::debug!("[StreamProtectionStatus]: Good"), + 2 => tracing::warn!("[StreamProtectionStatus]: Attestation pending"), + 3 => return Err(AbrError::Attestation), + _ => {} + } + } + _ => {} + }; + self.last_header = None; + Ok((None, false)) + } + } else { + Ok((None, true)) + } + } + + fn process_media_header(&mut self, data: &[u8]) -> Result<(), AbrError> { + let mh = MediaHeader::parse_from_bytes(data)?; + + let format_id = mh + .format_id + .as_ref() + .ok_or(AbrError::Invalid("media header: no format_id".into()))?; + let header_id = mh + .header_id + .and_then(|hid| u8::try_from(hid).ok()) + .ok_or(AbrError::Invalid("media header: no header_id".into()))?; + + let format_key = get_format_key(format_id); + + if !self.formats_by_key.contains_key(&format_key) { + self.formats_by_key + .insert(format_key.to_owned(), InitializedFormat::try_from(&mh)?); + } + let current_format = self.formats_by_key.get_mut(&format_key).unwrap(); + + // This is a hacky workaround to prevent duplicate sequences from being added. This should be fixed in the future + // (preferably by figuring out how to make the server not send duplicates). + if let Some(sequence_number) = mh.sequence_number { + let pseq = self + .previous_sequences + .entry(format_key.to_owned()) + .or_default(); + if !pseq.insert(sequence_number) { + tracing::warn!("duplicate sequence {sequence_number}"); + return Ok(()); + } + } + + // Save the header's ID so we can identify its stream data later. + self.header_id_to_format_key_map + .entry(header_id) + .or_insert(format_key); + + if current_format + .sequence_list + .iter() + .all(|x| x.sequence_number != Some(mh.sequence_number())) + { + let tr = mh.time_range.as_ref(); + let seq = Sequence { + itag: mh.itag, + format_id: format_id.clone(), + is_init_segment: mh.is_init_seg(), + duration_ms: mh.duration_ms, + start_ms: mh.start_ms, + start_data_range: mh.start_data_range, + sequence_number: mh.sequence_number, + content_length: mh.content_length, + time_range: tr.and_then(|tr| { + tr.start.zip(tr.duration).zip(tr.timescale).map( + |((start, duration), timescale)| TimeRange { + start, + duration, + timescale, + }, + ) + }), + }; + + // TODO: testing + let sname = if mh.itag == Some(251) { + "audio" + } else { + "video" + }; + tracing::debug!( + "{sname} #{}: content_length={} start_data_range={} time_start={} time_duration={}", + seq.sequence_number.unwrap_or_default(), + seq.content_length.unwrap_or_default(), + seq.start_data_range.unwrap_or_default(), + seq.time_range.as_ref().map(|t| t.start).unwrap_or_default(), + seq.time_range + .as_ref() + .map(|t| t.duration) + .unwrap_or_default(), + ); + + current_format.sequence_list2.push(seq.clone()); + current_format.sequence_list.push(seq); + + if mh.has_sequence_number() { + current_format.state.set_duration_ms( + current_format.state.duration_ms() + i64::from(mh.duration_ms()), + ); + current_format + .state + .set_end_segment_index(current_format.state.end_segment_index() + 1); + } + } + + Ok(()) + } + + fn process_media_data(&mut self) -> Result, AbrError> { + if let Some(last_header) = &mut self.last_header { + if let Some(header_id) = last_header.header_id.or_else(|| { + let x = self.buffer.try_get_u8().ok(); + if x.is_some() { + last_header.header_id = x; + last_header.size -= 1; + } + x + }) { + let to_copy = self.buffer.remaining().min(last_header.size as usize); + if to_copy == 0 { + return Ok(None); + } + let format_key = self + .header_id_to_format_key_map + .get(&header_id) + .ok_or_else(|| { + AbrError::Invalid(format!("media: unknown header id {header_id}").into()) + })?; + let current_format = self + .formats_by_key + .get(format_key) + .ok_or(AbrError::Invalid("no current format".into()))?; + let format = FormatInfo::from(current_format); + let data = self.buffer.copy_to_bytes(to_copy); + last_header.size -= data.len() as u64; + if last_header.size == 0 { + self.last_header = None; + } + Ok(Some(AbrStreamItem { format, data })) + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + fn process_media_end(&mut self, data: &[u8]) { + let header_id = data[0]; + self.header_id_to_format_key_map.remove(&header_id); + } + + fn process_next_request_policy(&mut self, data: &[u8]) -> Result<(), AbrError> { + let mut policy = NextRequestPolicy::parse_from_bytes(data)?; + tracing::debug!("NextRequestPolicy {policy:?}"); + self.playback_cookie = policy.playback_cookie.take(); + self.backoff_time_ms = policy.backoff_time_ms(); + Ok(()) + } + + fn process_reload_player(&mut self, data: &[u8]) -> Result<(), AbrError> { + let mut reload = ReloadPlayerResponse::parse_from_bytes(data)?; + tracing::debug!("ReloadPlayerResponse {reload:?}"); + Ok(()) + } + + fn process_format_initialization(&mut self, data: &[u8]) -> Result<(), AbrError> { + let init = FormatInitializationMetadata::parse_from_bytes(data)?; + let format = InitializedFormat::try_from(init)?; + let format_key = get_format_key(&format.format_id); + self.formats_by_key.insert(format_key, format); + Ok(()) + } + + fn process_sabr_redirect(&mut self, data: &[u8]) -> Result<(), AbrError> { + let redir = SabrRedirect::parse_from_bytes(data)?; + self.url = redir + .url + .ok_or(AbrError::Invalid("sabr redirect: no URL".into()))?; + Ok(()) + } + + fn main_format(&self) -> Option<&InitializedFormat> { + self.formats_by_key + .values() + .find(|fmt| { + fmt.mime_type + .as_deref() + .map(|t| t.contains("video")) + .unwrap_or_default() + }) + .or(self.formats_by_key.values().next()) + } + + /// A stream is done if all sequences from all formats were received or the last request did not contain any sequences + fn is_done(&self) -> bool { + self.formats_by_key.values().all(|f| { + f.sequence_list.is_empty() + || f.sequence_count + .zip(f.sequence_list.last()) + .map(|(sc, last)| last.sequence_number == Some(sc.into())) + .unwrap_or_default() + }) + } + + async fn fetch_stream_data(&self) -> Result { + tracing::debug!( + "fetching stream data: {}ms", + self.abr_state.player_time_ms() + ); + let body = VideoPlaybackAbrRequest { + client_abr_state: MessageField::some(self.abr_state.clone()), + selected_format_ids: self + .formats_by_key + .values() + .map(|f| f.format_id.clone()) + .collect(), + buffered_ranges: self + .formats_by_key + .values() + .map(|f| f.state.clone()) + .collect(), + video_playback_ustreamer_config: Some(self.ustreamer_config.clone()), + selected_audio_format_ids: self.audio_format_ids.clone(), + selected_video_format_ids: self.video_format_ids.clone(), + streamer_context: MessageField::some(StreamerContext { + client_info: MessageField::some(ClientInfo { + client_name: Some(1), + client_version: Some("2.20250314.01.00".to_owned()), + ..Default::default() + }), + po_token: self.po_token.clone(), + playback_cookie: self + .playback_cookie + .as_ref() + .and_then(|c| c.write_to_bytes().ok()), + ..Default::default() + }), + ..Default::default() + }; + + let resp = self + .http + .post(&self.url) + .body(body.write_to_bytes()?) + .send() + .await? + .error_for_status()?; + Ok(resp) + } + + pub fn stream(mut self) -> impl Stream> { + let stream = try_stream! { + let mut ri = 1; + loop { + let resp = self.fetch_stream_data().await?; + let mut data_stream = resp.bytes_stream(); + let mut page_len = 0; + self.clear_sequence_list(); + + while let Some(chunk) = data_stream.next().await { + let chunk = chunk?; + let clen = chunk.len(); + page_len += clen; + tracing::debug!("chunk {clen}"); + self.buffer.push(chunk); + + loop { + if self.last_header.is_none() { + self.last_header = self.parse_ump_header(); + } + tracing::debug!("loop {:?}", self.last_header); + if self.last_header.is_none() { + break; + } + let (x, y) = self.process_ump_part()?; + if let Some(item) = x { + yield item; + } + if y { + break; + } + } + } + tracing::debug!("request #{ri} fetched {page_len} bytes"); + + let main_format = self.main_format().ok_or(AbrError::Invalid(format!("no main format: {:?}", self.formats_by_key.values()).into()))?; + let player_time = self.abr_state.player_time_ms() + + main_format.sequence_list.iter().fold(0, |acc, seq| { + acc + i64::from(seq.duration_ms.unwrap_or_default()) + }); + + tracing::debug!( + "player time: {player_time}; sequence_count={}, last={:?}", + main_format.sequence_count.unwrap_or_default(), + main_format.sequence_list.last().and_then(|x| x.sequence_number), + ); + if self.is_done() { + break; + } + + self.abr_state.set_player_time_ms(player_time); + + if self.backoff_time_ms > 0 { + tracing::debug!("backoff: {} ms", self.backoff_time_ms); + tokio::time::sleep(std::time::Duration::from_millis( + self.backoff_time_ms.try_into().unwrap_or_default(), + )).await; + self.backoff_time_ms = 0; + } + + ri += 1; + } + + // DEBUG: + // for f in self.formats_by_key.values() { + // let mut of = File::create(format!("/home/thetadev/Documents/Programmieren/Rust/rustypipe/tmp/abrmap/{}_{}.json", std::env::args().into_iter().nth(1).unwrap(), f.format_id.itag())).unwrap(); + // serde_json::to_writer(of, &f.sequence_list2).unwrap(); + // } + }; + Box::pin(stream) + } +} + +fn get_format_key(format_id: &FormatId) -> String { + format!("{};{};", format_id.itag(), format_id.last_modified()) +} + +impl TryFrom<&MediaHeader> for InitializedFormat { + type Error = AbrError; + + fn try_from(value: &MediaHeader) -> Result { + let format_id = value + .format_id + .as_ref() + .ok_or(AbrError::Invalid("media header: no format_id".into()))?; + Ok(InitializedFormat { + format_id: format_id.clone(), + duration_ms: value.duration_ms, + mime_type: None, + sequence_count: None, + sequence_list: Vec::new(), + sequence_list2: Vec::new(), + state: BufferedRange { + format_id: MessageField::some(format_id.clone()), + start_time_ms: Some(0), + duration_ms: Some(0), + start_segment_index: Some(1), + end_segment_index: Some(0), + ..Default::default() + }, + }) + } +} + +impl TryFrom for InitializedFormat { + type Error = AbrError; + + fn try_from(value: FormatInitializationMetadata) -> Result { + let format_id = *value.format_id.0.ok_or(AbrError::Invalid( + "format initialization: no format_id".into(), + ))?; + Ok(InitializedFormat { + format_id: format_id.clone(), + duration_ms: value.duration_ms, + mime_type: value.mime_type.clone(), + sequence_count: value.sequence_count, + sequence_list: Vec::new(), + sequence_list2: Vec::new(), + state: BufferedRange { + format_id: MessageField::some(format_id), + start_time_ms: Some(0), + duration_ms: Some(0), + start_segment_index: Some(1), + end_segment_index: Some(0), + ..Default::default() + }, + }) + } +} + +#[cfg(test)] +mod tests { + use std::io::Write; + + use rustypipe::{ + client::RustyPipe, + model::{AudioCodec, VideoFormat}, + param::StreamFilter, + }; + + use super::*; + + #[tokio::test] + #[tracing_test::traced_test] + async fn experiment() { + let rp = RustyPipe::builder().build().unwrap(); + let player = rp.query().player("ZeerrnuLi5E").await.unwrap(); + let stream_filter = StreamFilter::new() + .allow_abr_only() + .video_max_res(360) + .video_formats([VideoFormat::Webm]) + .audio_codecs([AudioCodec::Opus]); + let (video, audio) = player.select_video_audio_stream(&stream_filter); + let video = video.unwrap(); + let audio = audio.unwrap(); + + let mut abr = AbrStream::new(AbrStreamOptions { + url: player.abr_streaming_url.clone().unwrap(), + ustreamer_config: player.abr_ustreamer_config.as_deref().unwrap(), + po_token: player.po_token.as_deref(), + audio_streams: &[audio], + // video_streams: &[video], + video_streams: &[], + player_time: 10584, + ..Default::default() + }) + .unwrap(); + + { + let mut stream = abr.stream(); + let mut files = HashMap::new(); + + while let Some(item) = stream.try_next().await.unwrap() { + let mut f = files.entry(item.format.itag).or_insert_with(|| { + std::fs::File::create(format!("test_{}.webm", item.format.itag)).unwrap() + }); + + f.write_all(&item.data).unwrap(); + } + } + + let audio_md = std::fs::metadata(format!("test_{}.webm", audio.itag)).unwrap(); + assert_eq!(audio_md.len(), audio.size); + // let video_md = std::fs::metadata(format!("test_{}.webm", video.itag)).unwrap(); + // assert_eq!(video_md.len(), video.size.unwrap()); + } + + #[tokio::test] + #[tracing_test::traced_test] + async fn first_header() { + let rp = RustyPipe::builder().build().unwrap(); + let player = rp.query().player("8AeOP8u89iE").await.unwrap(); + let stream_filter = StreamFilter::new() + .allow_abr_only() + // .video_max_res(360) + .video_formats([VideoFormat::Webm]) + .audio_codecs([AudioCodec::Opus]); + let (video, audio) = player.select_video_audio_stream(&stream_filter); + let video = video.unwrap(); + let audio = audio.unwrap(); + + let mut abr = AbrStream::new(AbrStreamOptions { + url: player.abr_streaming_url.clone().unwrap(), + ustreamer_config: player.abr_ustreamer_config.as_deref().unwrap(), + po_token: player.po_token.as_deref(), + audio_streams: &[audio], + video_streams: &[video], + // video_streams: &[], + ..Default::default() + }) + .unwrap(); + + todo!() + } +} diff --git a/downloader/src/abr_model.rs b/downloader/src/abr_model.rs new file mode 100644 index 0000000..35df60e --- /dev/null +++ b/downloader/src/abr_model.rs @@ -0,0 +1,87 @@ +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PartType { + ONESIE_HEADER = 10, + ONESIE_DATA = 11, + MEDIA_HEADER = 20, + MEDIA = 21, + MEDIA_END = 22, + LIVE_METADATA = 31, + HOSTNAME_CHANGE_HINT = 32, + LIVE_METADATA_PROMISE = 33, + LIVE_METADATA_PROMISE_CANCELLATION = 34, + NEXT_REQUEST_POLICY = 35, + USTREAMER_VIDEO_AND_FORMAT_DATA = 36, + FORMAT_SELECTION_CONFIG = 37, + USTREAMER_SELECTED_MEDIA_STREAM = 38, + FORMAT_INITIALIZATION_METADATA = 42, + SABR_REDIRECT = 43, + SABR_ERROR = 44, + SABR_SEEK = 45, + RELOAD_PLAYER_RESPONSE = 46, + PLAYBACK_START_POLICY = 47, + ALLOWED_CACHED_FORMATS = 48, + START_BW_SAMPLING_HINT = 49, + PAUSE_BW_SAMPLING_HINT = 50, + SELECTABLE_FORMATS = 51, + REQUEST_IDENTIFIER = 52, + REQUEST_CANCELLATION_POLICY = 53, + ONESIE_PREFETCH_REJECTION = 54, + TIMELINE_CONTEXT = 55, + REQUEST_PIPELINING = 56, + SABR_CONTEXT_UPDATE = 57, + STREAM_PROTECTION_STATUS = 58, + SABR_CONTEXT_SENDING_POLICY = 59, + LAWNMOWER_POLICY = 60, + SABR_ACK = 61, + END_OF_TRACK = 62, + CACHE_LOAD_POLICY = 63, + LAWNMOWER_MESSAGING_POLICY = 64, + PREWARM_CONNECTION = 65, + UNKNOWN, +} + +impl From for PartType { + fn from(value: u64) -> Self { + match value { + 10 => Self::ONESIE_HEADER, + 11 => Self::ONESIE_DATA, + 20 => Self::MEDIA_HEADER, + 21 => Self::MEDIA, + 22 => Self::MEDIA_END, + 31 => Self::LIVE_METADATA, + 32 => Self::HOSTNAME_CHANGE_HINT, + 33 => Self::LIVE_METADATA_PROMISE, + 34 => Self::LIVE_METADATA_PROMISE_CANCELLATION, + 35 => Self::NEXT_REQUEST_POLICY, + 36 => Self::USTREAMER_VIDEO_AND_FORMAT_DATA, + 37 => Self::FORMAT_SELECTION_CONFIG, + 38 => Self::USTREAMER_SELECTED_MEDIA_STREAM, + 42 => Self::FORMAT_INITIALIZATION_METADATA, + 43 => Self::SABR_REDIRECT, + 44 => Self::SABR_ERROR, + 45 => Self::SABR_SEEK, + 46 => Self::RELOAD_PLAYER_RESPONSE, + 47 => Self::PLAYBACK_START_POLICY, + 48 => Self::ALLOWED_CACHED_FORMATS, + 49 => Self::START_BW_SAMPLING_HINT, + 50 => Self::PAUSE_BW_SAMPLING_HINT, + 51 => Self::SELECTABLE_FORMATS, + 52 => Self::REQUEST_IDENTIFIER, + 53 => Self::REQUEST_CANCELLATION_POLICY, + 54 => Self::ONESIE_PREFETCH_REJECTION, + 55 => Self::TIMELINE_CONTEXT, + 56 => Self::REQUEST_PIPELINING, + 57 => Self::SABR_CONTEXT_UPDATE, + 58 => Self::STREAM_PROTECTION_STATUS, + 59 => Self::SABR_CONTEXT_SENDING_POLICY, + 60 => Self::LAWNMOWER_POLICY, + 61 => Self::SABR_ACK, + 62 => Self::END_OF_TRACK, + 63 => Self::CACHE_LOAD_POLICY, + 64 => Self::LAWNMOWER_MESSAGING_POLICY, + 65 => Self::PREWARM_CONNECTION, + _ => Self::UNKNOWN, + } + } +} diff --git a/downloader/src/bin/abrmap.rs b/downloader/src/bin/abrmap.rs new file mode 100644 index 0000000..02d9047 --- /dev/null +++ b/downloader/src/bin/abrmap.rs @@ -0,0 +1,52 @@ +use futures_util::TryStreamExt; +use rustypipe::{ + client::RustyPipe, + model::{AudioCodec, VideoFormat}, + param::StreamFilter, +}; +use rustypipe_downloader::abr2::{AbrStream, AbrStreamOptions}; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let id = std::env::args().into_iter().nth(1).unwrap(); + + let rp = RustyPipe::builder().build().unwrap(); + let player = rp + .query() + .visitor_data("Cgt0SlJzQllxNFhuMCjZ1Z3BBjIKCgJERRIEEgAgXg%3D%3D") + .player_from_client(id, rustypipe::client::ClientType::Desktop) + .await + .unwrap(); + assert_eq!(player.client_type, rustypipe::client::ClientType::Desktop); + + let stream_filter = StreamFilter::new() + .allow_abr_only() + .video_max_res(360) + .video_formats([VideoFormat::Webm]) + .audio_codecs([AudioCodec::Opus]); + let (video, audio) = player.select_video_audio_stream(&stream_filter); + let video = video.unwrap(); + let audio = audio.unwrap(); + + dbg!(&audio); + + let abr = AbrStream::new(AbrStreamOptions { + url: player.abr_streaming_url.clone().unwrap(), + ustreamer_config: player.abr_ustreamer_config.as_deref().unwrap(), + po_token: player.po_token.as_deref(), + audio_streams: &[audio], + video_streams: &[video], + // video_streams: &[], + ..Default::default() + }) + .unwrap(); + + { + let mut stream = abr.stream(); + while let Some(itm) = stream.try_next().await.unwrap() { + println!("got {} bytes", itm.data.len()); + } + } +} diff --git a/downloader/src/error.rs b/downloader/src/error.rs index d45405d..cf56f25 100644 --- a/downloader/src/error.rs +++ b/downloader/src/error.rs @@ -1,6 +1,7 @@ use std::{borrow::Cow, path::PathBuf}; use rustypipe::client::ClientType; +use rustypipe_abr_proto::sabr_error::SabrError; /// Error from the video downloader #[derive(thiserror::Error, Debug)] @@ -57,3 +58,20 @@ impl From for DownloadError { Self::AudioTag(value.to_string().into()) } } + +#[derive(thiserror::Error, Debug)] +pub enum AbrError { + /// Error encoding/decoding protobuf + #[error("protobuf: {0}")] + Protobuf(#[from] protobuf::Error), + /// Error from the HTTP client + #[error("http error: {0}")] + Http(#[from] reqwest::Error), + /// Received invalid data + #[error("invalid data: {0}")] + Invalid(Cow<'static, str>), + #[error("sabr error {}: {}", .0.code(), .0.type_())] + Sabr(SabrError), + #[error("attestation required")] + Attestation, +} diff --git a/downloader/src/lib.rs b/downloader/src/lib.rs index c63d5c5..35024bc 100644 --- a/downloader/src/lib.rs +++ b/downloader/src/lib.rs @@ -2,7 +2,12 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs, clippy::todo, clippy::dbg_macro)] +mod abr; +pub mod abr2; +mod abr_model; mod error; +mod range_predict; +mod streamtest; mod util; use std::{ @@ -815,7 +820,7 @@ impl DownloadQuery { if let Some(v) = video { downloads.push(StreamDownload { file: output_path.with_extension(format!("video{}", v.format.extension())), - url: v.url.clone(), + url: v.url.clone().unwrap(), video_codec: Some(v.codec), audio_codec: None, }); @@ -823,7 +828,7 @@ impl DownloadQuery { if let Some(a) = audio { downloads.push(StreamDownload { file: output_path.with_extension(format!("audio{}", a.format.extension())), - url: a.url.clone(), + url: a.url.clone().unwrap(), video_codec: None, audio_codec: Some(a.codec), }); diff --git a/downloader/src/range_predict.rs b/downloader/src/range_predict.rs new file mode 100644 index 0000000..a071fd1 --- /dev/null +++ b/downloader/src/range_predict.rs @@ -0,0 +1,110 @@ +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +use crate::abr2::TimeRange; + +struct Sx { + stream_len: i64, + header_len: i64, + duration: i64, + positions: BTreeMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct Sequence { + pub itag: i32, + pub is_init_segment: bool, + pub duration_ms: Option, + pub start_ms: Option, + pub start_data_range: i32, + pub sequence_number: Option, + pub content_length: i64, + pub time_range: Option, +} + +impl Sx { + fn new(stream_len: i64, header_len: i64, duration: i64) -> Self { + let mut positions = BTreeMap::new(); + positions.insert(header_len, 0); + positions.insert(stream_len - 1, duration); + + Self { + stream_len, + header_len, + duration, + positions, + } + } + + fn pos_predict(&self, pos: i64) -> i64 { + if pos < self.header_len { + return 0; + } + if pos >= self.stream_len { + panic!("out of range"); + } + + let blow = *self.positions.keys().rev().find(|x| **x <= pos).unwrap(); + let bhigh = *self.positions.keys().find(|x| **x >= pos).unwrap(); + let brange = bhigh - blow; + let bperc = pos as f64 / brange as f64; + + let tlow = self.positions[&blow]; + let thigh = self.positions[&bhigh]; + let trange = thigh - tlow; + + let tpoint = (trange as f64 * bperc) as i64 + tlow; + (tpoint - 5000).max(tlow) + } +} + +#[cfg(test)] +mod tests { + use std::{fs::File, io::BufReader}; + + use super::{Sequence, Sx}; + + fn fetch_seg(sequences: &[Sequence], ts: i64) -> &Sequence { + if ts <= 10000 { + &sequences[0] + } else { + sequences + .iter() + .rev() + .find(|s| s.time_range.as_ref().is_some_and(|t| t.start <= ts)) + .unwrap() + } + } + + #[test] + fn t_range_predict() { + let f = File::open( + "/home/thetadev/Documents/Programmieren/Rust/rustypipe/tmp/abrmap/HSkTpseMd-A_251_nodrc.json", + ) + .unwrap(); + let sequences: Vec = serde_json::from_reader(BufReader::new(f)).unwrap(); + let last = sequences.last().unwrap(); + let stream_len = i64::from(last.start_data_range) + last.content_length; + let header_len = sequences[0].content_length; + let ltr = last.time_range.as_ref().unwrap(); + let duration = ltr.start + ltr.duration; + + let sx = Sx::new(stream_len, header_len, duration); + + let max_sl = sequences.iter().map(|s| s.content_length).max().unwrap(); + + for offset in (0..stream_len).step_by(1000) { + let pred = sx.pos_predict(offset); + let s = fetch_seg(&sequences, pred); + let to_skip = offset - i64::from(s.start_data_range); + if to_skip < 0 { + println!("after target ({to_skip}): offset={offset} pred={pred} s={s:?}"); + } else if to_skip <= max_sl { + println!("on target ({to_skip})"); + } else { + println!("before target ({to_skip})"); + } + } + } +} diff --git a/downloader/src/streamtest.rs b/downloader/src/streamtest.rs new file mode 100644 index 0000000..f9cd5b2 --- /dev/null +++ b/downloader/src/streamtest.rs @@ -0,0 +1,25 @@ +use async_stream::stream; +use futures_util::Stream; + +fn stream() -> impl Stream { + stream! { + let mut x = 0; + loop { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + yield x; + x += 1; + } + }; + + let (mut __yield_tx, __yield_rx) = unsafe { async_stream::__private::yielder::pair() }; + async_stream::__private::AsyncStream::new(__yield_rx, async move { + let mut x = 0; + loop { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + { + __yield_tx.send(x).await + }; + x += 1; + } + }) +} diff --git a/downloader/src/util.rs b/downloader/src/util.rs index 5f87339..9ce600a 100644 --- a/downloader/src/util.rs +++ b/downloader/src/util.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; +use bytes::{Buf, BufMut, Bytes, BytesMut}; use reqwest::Url; use crate::DownloadError; @@ -22,3 +23,147 @@ pub fn url_to_params(url: &str) -> Result<(Url, BTreeMap), Downl Ok((parsed_url, url_params)) } + +#[derive(Default, Debug, Clone)] +pub struct BytesBuffer { + b: Vec, + pos: usize, +} + +impl BytesBuffer { + pub fn new() -> Self { + Self::default() + } + + pub fn push(&mut self, bytes: Bytes) { + if !bytes.is_empty() { + self.b.push(bytes); + } + } + + fn get_ref(&self, index: usize) -> Option<&u8> { + let mut offset = 0; + for p in &self.b { + let pi = index - offset; + if pi < p.len() { + return Some(&p[pi]); + } + offset += p.len(); + } + None + } + + pub fn get(&self, index: usize) -> Option { + self.get_ref(index).cloned() + } + + pub fn pos(&self) -> usize { + self.pos + } +} + +impl std::ops::Index for BytesBuffer { + type Output = u8; + + fn index(&self, index: usize) -> &Self::Output { + self.get_ref(index).expect("index out of range") + } +} + +impl Buf for BytesBuffer { + fn remaining(&self) -> usize { + self.b + .iter() + .map(|b| b.remaining()) + .fold(0, |a, b| a.saturating_add(b)) + } + + fn chunk(&self) -> &[u8] { + for p in &self.b { + if p.has_remaining() { + return p.chunk(); + } + } + &[] + } + + fn advance(&mut self, mut cnt: usize) { + self.pos += cnt; + let mut to_trunc = 0; + for p in &mut self.b { + if cnt == 0 { + break; + } + + let to_adv = cnt.min(p.remaining()); + p.advance(to_adv); + cnt -= to_adv; + + if p.is_empty() { + to_trunc += 1; + } + } + + if to_trunc > 0 { + self.b.drain(0..to_trunc); + } + } + + fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize { + let mut n = 0; + for p in &self.b { + n += p.chunks_vectored(&mut dst[n..]); + } + n + } + + fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { + self.pos += len; + if let Some(first) = self.b.first_mut() { + if first.remaining() >= len { + return first.copy_to_bytes(len); + } + } + + let rem = self.remaining(); + if rem < len { + assert!(len <= rem, "`len` greater than remaining"); + } + + let mut ret = BytesMut::with_capacity(len); + ret.put(self.take(len)); + ret.freeze() + } +} + +impl IntoIterator for BytesBuffer { + type Item = u8; + + type IntoIter = bytes::buf::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + bytes::buf::IntoIter::new(self) + } +} + +#[cfg(test)] +mod tests { + use bytes::{Buf, Bytes}; + + use super::BytesBuffer; + + #[test] + fn buffer() { + let mut buf = BytesBuffer::new(); + buf.push(Bytes::from_static(b"hello")); + buf.push(Bytes::from_static(b" ")); + buf.push(Bytes::from_static(b"world")); + + assert_eq!(buf[0], b'h'); + buf.advance(5); + let w1 = buf.copy_to_bytes(6); + assert_eq!(w1.as_ref(), b" world"); + assert!(buf.b.is_empty()); + assert_eq!(buf.pos(), 17); + } +} diff --git a/notes/AB_Tests.md b/notes/AB_Tests.md index 8742ef6..dd341fa 100644 --- a/notes/AB_Tests.md +++ b/notes/AB_Tests.md @@ -1101,3 +1101,10 @@ YouTube playlists may use a commandExecutorCommand which holds a list of command } } ``` + +## [23] YouTube Desktop only returns ABR streams + +- **Encountered on:** 16.03.2025 +- **Impact:** 🔴 High +- **Endpoint:** player (YT) +- **Status:** Stabilized diff --git a/src/client/player.rs b/src/client/player.rs index 9bae601..760e820 100644 --- a/src/client/player.rs +++ b/src/client/player.rs @@ -50,15 +50,33 @@ struct QPlayer<'a> { #[serde(rename_all = "camelCase")] struct QPlaybackContext<'a> { content_playback_context: QContentPlaybackContext<'a>, + device_playback_capabilities: QDevicePlaybackCapabilities, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct QContentPlaybackContext<'a> { - /// Signature timestamp extracted from player.js - signature_timestamp: &'a str, + auto_captions_default_on: bool, + autonav_state: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + current_url: Option, + html5_preference: &'a str, + lact_milliseconds: i8, /// Referer URL from website referer: String, + /// Signature timestamp extracted from player.js + signature_timestamp: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + splay: Option, + #[serde(skip_serializing_if = "Option::is_none")] + vis: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct QDevicePlaybackCapabilities { + supports_vp9_encoding: bool, + support_xhr: bool, } #[derive(Debug, Serialize)] @@ -210,8 +228,27 @@ impl RustyPipeQuery { let playback_context = deobf.as_ref().map(|deobf| QPlaybackContext { content_playback_context: QContentPlaybackContext { - signature_timestamp: &deobf.sts, + auto_captions_default_on: false, + autonav_state: "STATE_OFF", + current_url: if client_type == ClientType::Tv { + None + } else { + Some(format!("/watch?v={video_id}")) + }, + html5_preference: "HTML5_PREF_WANTS", + lact_milliseconds: if client_type == ClientType::Tv { + 10 + } else { + -1 + }, referer: format!("https://www.youtube.com/watch?v={video_id}"), + signature_timestamp: &deobf.sts, + splay: Some(false).filter(|_| client_type != ClientType::Tv), + vis: Some(0).filter(|_| client_type != ClientType::Tv), + }, + device_playback_capabilities: QDevicePlaybackCapabilities { + supports_vp9_encoding: true, + support_xhr: true, }, }); @@ -387,21 +424,6 @@ impl MapResponse for response::Player { video_details.video_id, ctx.id ))); } - // Sometimes YouTube Desktop does not output any URLs for adaptive streams. - // Since this is currently rare, it is best to retry the request in this case. - if !is_live - && !streaming_data.adaptive_formats.c.is_empty() - && streaming_data - .adaptive_formats - .c - .iter() - .all(|f| f.url.is_none() && f.signature_cipher.is_none()) - { - return Err(ExtractionError::Unavailable { - reason: UnavailabilityReason::TryAgain, - msg: "no adaptive stream URLs".to_owned(), - }); - } let video_info = VideoPlayerDetails { id: video_details.video_id, @@ -417,11 +439,27 @@ impl MapResponse for response::Player { is_live_content: video_details.is_live_content, }; + let mut mapper = StreamsMapper::new( + ctx.deobf, + ctx.session_po_token.as_ref().map(|t| t.po_token.as_str()), + )?; + + let abr_streaming_url = match &streaming_data.server_abr_streaming_url { + Some(url) => match mapper.map_url_abr(url) { + Ok(res) => Some(res), + Err(e) => { + warnings.push(format!("could not map abr_streaming_url: {e}")); + None + } + }, + None => None, + }; + let abr_ustreamer_config = self.player_config.media_common_config.map(|cfg| { + cfg.media_ustreamer_request_config + .video_playback_ustreamer_config + }); + let streams = if !is_live { - let mut mapper = StreamsMapper::new( - ctx.deobf, - ctx.session_po_token.as_ref().map(|t| t.po_token.as_str()), - )?; mapper.map_streams(streaming_data.formats); mapper.map_streams(streaming_data.adaptive_formats); let mut res = mapper.output()?; @@ -530,6 +568,12 @@ impl MapResponse for response::Player { valid_until, hls_manifest_url: streaming_data.hls_manifest_url, dash_manifest_url: streaming_data.dash_manifest_url, + abr_streaming_url, + abr_ustreamer_config, + po_token: ctx + .session_po_token + .as_ref() + .map(|pot| pot.po_token.to_owned()), preview_frames, drm, client_type: ctx.client_type, @@ -693,7 +737,7 @@ impl<'a> StreamsMapper<'a> { &mut self, url: &Option, signature_cipher: &Option, - ) -> Result { + ) -> Result, ExtractionError> { let (url_base, mut url_params) = match url { Some(url) => util::url_to_params(url).map_err(|_| { @@ -708,9 +752,7 @@ impl<'a> StreamsMapper<'a> { ) }) } - None => Err(ExtractionError::InvalidData( - "stream contained neither url or cipher".into(), - )), + None => return Ok(None), }, }?; @@ -722,10 +764,17 @@ impl<'a> StreamsMapper<'a> { let url = Url::parse_with_params(url_base.as_str(), url_params.iter()) .map_err(|_| ExtractionError::InvalidData("could not combine URL".into()))?; - Ok(UrlMapRes { - url: url.to_string(), - xtags: url_params.get("xtags").cloned(), - }) + Ok(Some(url.to_string())) + } + + fn map_url_abr(&mut self, url: &str) -> Result { + let (url_base, mut url_params) = util::url_to_params(url).map_err(|_| { + ExtractionError::InvalidData(format!("Could not parse url `{url}`").into()) + })?; + self.deobf_nsig(&mut url_params)?; + let url = Url::parse_with_params(url_base.as_str(), url_params.iter()) + .map_err(|_| ExtractionError::InvalidData("could not combine URL".into()))?; + Ok(url.to_string()) } fn map_video_stream(&mut self, f: player::Format) -> Result { @@ -743,14 +792,14 @@ impl<'a> StreamsMapper<'a> { format!("invalid video format. itag: {}", f.itag).into(), )); }; - let map_res = self.map_url(&f.url, &f.signature_cipher)?; Ok(VideoStream { - url: map_res.url, + url: self.map_url(&f.url, &f.signature_cipher)?, itag: f.itag, bitrate: f.bitrate, average_bitrate: f.average_bitrate.unwrap_or(f.bitrate), size: f.content_length, + last_modified: f.last_modified, index_range: f.index_range, init_range: f.init_range, duration_ms: f.approx_duration_ms, @@ -765,6 +814,7 @@ impl<'a> StreamsMapper<'a> { format, codec: get_video_codec(codecs), mime: f.mime_type, + xtags: f.xtags, drm_track_type: f.drm_track_type.map(|t| t.into()), drm_systems: f.drm_families.into_iter().map(|t| t.into()).collect(), }) @@ -783,10 +833,9 @@ impl<'a> StreamsMapper<'a> { let format = get_audio_format(mtype).ok_or_else(|| { ExtractionError::InvalidData(format!("invalid audio format. itag: {}", f.itag).into()) })?; - let map_res = self.map_url(&f.url, &f.signature_cipher)?; Ok(AudioStream { - url: map_res.url, + url: self.map_url(&f.url, &f.signature_cipher)?, itag: f.itag, bitrate: f.bitrate, average_bitrate: f.average_bitrate.unwrap_or(f.bitrate), @@ -795,6 +844,7 @@ impl<'a> StreamsMapper<'a> { format!("no audio content length. itag: {}", f.itag).into(), ) })?, + last_modified: f.last_modified, index_range: f.index_range, init_range: f.init_range, duration_ms: f.approx_duration_ms, @@ -803,9 +853,11 @@ impl<'a> StreamsMapper<'a> { mime: f.mime_type, channels: f.audio_channels, loudness_db: f.loudness_db, + is_drc: f.is_drc, track: f .audio_track - .map(|t| self.map_audio_track(t, map_res.xtags)), + .map(|t| self.map_audio_track(t, f.xtags.as_deref())), + xtags: f.xtags, drm_track_type: f.drm_track_type.map(|t| t.into()), drm_systems: f.drm_families.into_iter().map(|t| t.into()).collect(), }) @@ -814,30 +866,36 @@ impl<'a> StreamsMapper<'a> { fn map_audio_track( &mut self, track: response::player::AudioTrack, - xtags: Option, + xtags: Option<&str>, ) -> AudioTrack { let mut lang = None; let mut track_type = None; if let Some(xtags) = xtags { - xtags - .split(':') - .filter_map(|param| param.split_once('=')) - .for_each(|(k, v)| match k { - "lang" => { - lang = Some(v.to_owned()); + let xtags_data = urlencoding::decode(xtags) + .ok() + .and_then(|dec| util::b64_decode(dec.as_bytes()).ok()) + .and_then(|bts| util::kv_from_pb(bts)); + + match xtags_data { + Some(mut xtags_data) => { + if let Some(acont) = xtags_data.get("acont") { + match serde_plain::from_str(&acont) { + Ok(v) => { + track_type = Some(v); + } + Err(_) => { + self.warnings + .push(format!("could not parse audio track type `{acont}`")); + } + } } - "acont" => match serde_plain::from_str(v) { - Ok(v) => { - track_type = Some(v); - } - Err(_) => { - self.warnings - .push(format!("could not parse audio track type `{v}`")); - } - }, - _ => {} - }); + lang = xtags_data.remove("lang"); + } + None => self + .warnings + .push(format!("could not parse xtags `{xtags}`")), + } } AudioTrack { @@ -850,11 +908,6 @@ impl<'a> StreamsMapper<'a> { } } -struct UrlMapRes { - url: String, - xtags: Option, -} - fn parse_mime(mime: &str) -> Option<(&str, Vec<&str>)> { static PATTERN: Lazy = Lazy::new(|| Regex::new(r#"(\w+/\w+);\scodecs="([a-zA-Z-0-9.,\s]*)""#).unwrap()); @@ -1013,7 +1066,7 @@ mod tests { let url = mapper .map_url(&None, &Some(signature_cipher.to_owned())) .unwrap() - .url; + .unwrap(); assert_eq!(url, "https://rr5---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=3781277&dur=229.301&ei=vb7nYvH5BMK8gAfBj7ToBQ&expire=1659376413&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-AB_BABwrXZJN428ZwDxq5ScPn2AbcGODnRlTVhCQ3mj2&initcwndbps=1588750&ip=2003%3Ade%3Aaf06%3A6300%3Ac750%3A1b77%3Ac74a%3A80e3&itag=251&keepalive=yes&lmt=1655510291473933&lsig=AG3C_xAwRQIgCKCGJ1iu4wlaGXy3jcJyU3inh9dr1FIfqYOZEG_MdmACIQCbungkQYFk7EhD6K2YvLaHFMjKOFWjw001_tLb0lPDtg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=hH&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5ednsl&ms=au%2Conr&mt=1659354538&mv=m&mvi=5&n=XzXGSfGusw6OCQ&ns=b_Mq_qlTFcSGlG9RpwpM9xQH&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAPIsKd7-xi4xVHEC9gb__dU4hzfzsHEj9ytd3nt0gEceAiACJWBcw-wFEq9qir35bwKHJZxtQ9mOL7SKiVkLQNDa6A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&spc=lT-Khi831z8dTejFIRCvCEwx_6romtM&txp=4532434&vprv=1"); } diff --git a/src/client/response/player.rs b/src/client/response/player.rs index a880dd5..491779d 100644 --- a/src/client/response/player.rs +++ b/src/client/response/player.rs @@ -92,6 +92,7 @@ pub(crate) struct StreamingData { pub dash_manifest_url: Option, /// Only on livestreams pub hls_manifest_url: Option, + pub server_abr_streaming_url: Option, pub drm_params: Option, #[serde(default)] #[serde_as(deserialize_as = "VecSkipError<_>")] @@ -109,8 +110,12 @@ pub(crate) struct Format { pub format_type: FormatType, pub mime_type: String, - + #[serde_as(as = "Option")] + pub last_modified: Option, + #[serde_as(as = "Option")] + pub content_length: Option, pub bitrate: u32, + pub xtags: Option, pub width: Option, pub height: Option, @@ -122,9 +127,6 @@ pub(crate) struct Format { #[serde_as(as = "Option")] pub init_range: Option>, - #[serde_as(as = "Option")] - pub content_length: Option, - #[serde(default)] #[serde_as(deserialize_as = "DefaultOnError")] pub quality: Option, @@ -141,6 +143,8 @@ pub(crate) struct Format { pub audio_sample_rate: Option, pub audio_channels: Option, pub loudness_db: Option, + #[serde(default)] + pub is_drc: bool, pub audio_track: Option, pub signature_cipher: Option, @@ -294,9 +298,13 @@ pub(crate) struct StoryboardRenderer { pub spec: String, } +#[serde_as] #[derive(Default, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct PlayerConfig { + #[serde(default)] + #[serde_as(deserialize_as = "DefaultOnError")] + pub media_common_config: Option, pub web_drm_config: Option, } @@ -306,6 +314,18 @@ pub(crate) struct WebDrmConfig { pub widevine_service_cert: Option, } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MediaCommonConfig { + pub media_ustreamer_request_config: UstreamerRequestConfig, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UstreamerRequestConfig { + pub video_playback_ustreamer_config: String, +} + #[derive(Default, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct HeartbeatParams { 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 56cbc1c..937d6b0 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 @@ -61,11 +61,12 @@ VideoPlayer( ), video_streams: [ VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=1619781&dur=163.143&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=17&lmt=1580005480199246&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2F3gpp&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAJ2s7Pm4w42X3u3PWYViDeqIaE2tE9J6oIGpd0KB9gFsAiAH84QaJ4oUNivdRDUBi1ZYI7JSxESsPQ53mLInajKzcQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=1619781&dur=163.143&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=17&lmt=1580005480199246&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2F3gpp&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAJ2s7Pm4w42X3u3PWYViDeqIaE2tE9J6oIGpd0KB9gFsAiAH84QaJ4oUNivdRDUBi1ZYI7JSxESsPQ53mLInajKzcQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 17, bitrate: 79452, average_bitrate: 79428, size: Some(1619781), + last_modified: Some(1580005480199246), index_range: None, init_range: None, duration_ms: Some(163143), @@ -77,15 +78,17 @@ VideoPlayer( mime: "video/3gpp; codecs=\"mp4v.20.3, mp4a.40.2\"", format: r#3gp, codec: mp4v, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=11439331&dur=163.096&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=18&lmt=1580005476071743&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAJAH-tWof01vrs8phEoz51XkWwdMzQ77k1UTrdY5XiuTAiA38z-qANX0jtfCiAl4EVMZaKo1ncrzJFRrCffZ6LagrA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=11439331&dur=163.096&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=18&lmt=1580005476071743&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAJAH-tWof01vrs8phEoz51XkWwdMzQ77k1UTrdY5XiuTAiA38z-qANX0jtfCiAl4EVMZaKo1ncrzJFRrCffZ6LagrA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 18, bitrate: 561339, average_bitrate: 561109, size: Some(11439331), + last_modified: Some(1580005476071743), index_range: None, init_range: None, duration_ms: Some(163096), @@ -97,15 +100,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&dur=163.096&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=22&lmt=1580005750956837&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgFlQZgR63Yz9UgY9gVqiyGDVkZmSmACRP3-MmKN7CRzQCIAMHAwZbHmWL1qNH4Nu3A0pXZwErXMVPzMIt-PyxeZqa&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cratebypass%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&dur=163.096&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=22&lmt=1580005750956837&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgFlQZgR63Yz9UgY9gVqiyGDVkZmSmACRP3-MmKN7CRzQCIAMHAwZbHmWL1qNH4Nu3A0pXZwErXMVPzMIt-PyxeZqa&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cratebypass%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 22, bitrate: 1574434, average_bitrate: 1574434, size: None, + last_modified: Some(1580005750956837), index_range: None, init_range: None, duration_ms: Some(163096), @@ -117,17 +122,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), ], video_only_streams: [ VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=1224002&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=394&keepalive=yes&lmt=1608045375671513&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgYCPleG9F86UoDRvzFL2xSUUI-HLZGw_P7qBOixlcmKsCIQChdmrJ1NvKo5Ra4QJ9ivR5V8fEcQs0f_3aUiqMhGB5DQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=1224002&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=394&keepalive=yes&lmt=1608045375671513&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgYCPleG9F86UoDRvzFL2xSUUI-HLZGw_P7qBOixlcmKsCIQChdmrJ1NvKo5Ra4QJ9ivR5V8fEcQs0f_3aUiqMhGB5DQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 394, bitrate: 67637, average_bitrate: 60063, size: Some(1224002), + last_modified: Some(1608045375671513), index_range: Some(Range( start: 700, end: 1115, @@ -145,15 +152,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.00M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=2238952&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=395&keepalive=yes&lmt=1608045728968690&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAKCXHOCh_P3VlNWebTeWw0WdSln-zYe3BjZeEm2QiltCAiAQNcJBI4G-8dK5z1IUoqBZctk6ddjkl_QYKRFAKXyOcw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=2238952&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=395&keepalive=yes&lmt=1608045728968690&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAKCXHOCh_P3VlNWebTeWw0WdSln-zYe3BjZeEm2QiltCAiAQNcJBI4G-8dK5z1IUoqBZctk6ddjkl_QYKRFAKXyOcw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 395, bitrate: 135747, average_bitrate: 109867, size: Some(2238952), + last_modified: Some(1608045728968690), index_range: Some(Range( start: 700, end: 1115, @@ -171,15 +180,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.00M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=7808990&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIjjrMvCEzSLlbvbrjItT4V9JdpggnO5IHye9i4PxTyzAiAmbaFCB2hH7evf9JX3JUx-tU9S6zv2IzSKz8ObGSVRjw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=7808990&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIjjrMvCEzSLlbvbrjItT4V9JdpggnO5IHye9i4PxTyzAiAmbaFCB2hH7evf9JX3JUx-tU9S6zv2IzSKz8ObGSVRjw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 134, bitrate: 538143, average_bitrate: 383195, size: Some(7808990), + last_modified: Some(1580005649163759), index_range: Some(Range( start: 740, end: 1155, @@ -197,15 +208,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d401e\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=4130385&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=396&keepalive=yes&lmt=1608045761576250&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgBrQhbygTP6RGjUk0lGbxBI5e3NdeR6C_SW8R_ckZ2PkCIQDaBg5cJxYVWfwRrrELQFgRMOJ4xS3oOOROayoQMjxaCA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=4130385&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=396&keepalive=yes&lmt=1608045761576250&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgBrQhbygTP6RGjUk0lGbxBI5e3NdeR6C_SW8R_ckZ2PkCIQDaBg5cJxYVWfwRrrELQFgRMOJ4xS3oOOROayoQMjxaCA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 396, bitrate: 258097, average_bitrate: 202682, size: Some(4130385), + last_modified: Some(1608045761576250), index_range: Some(Range( start: 700, end: 1115, @@ -223,15 +236,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.01M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=6873325&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=397&keepalive=yes&lmt=1608045990917419&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAMqBb1hKVVzWl3Awrh1T8GQG9IrSWF84zW_ZfjgbAN5QAiAaP3jYyI4ox2aclcOCzYFzqWgByWCxj_FgTN-SfsARXw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=6873325&dur=163.029&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=397&keepalive=yes&lmt=1608045990917419&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAMqBb1hKVVzWl3Awrh1T8GQG9IrSWF84zW_ZfjgbAN5QAiAaP3jYyI4ox2aclcOCzYFzqWgByWCxj_FgTN-SfsARXw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 397, bitrate: 436843, average_bitrate: 337281, size: Some(6873325), + last_modified: Some(1608045990917419), index_range: Some(Range( start: 700, end: 1115, @@ -249,15 +264,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.04M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=22365208&dur=163.046&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=398&keepalive=yes&lmt=1608048380553749&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgR6KqCOoig_FMl2tWKa7qHSmCjIZa9S7ABzEI16qdO2sCIFXccwql4bqV9CHlqXY4tgxyMFUsp7vW4XUjxs3AyG6H&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=22365208&dur=163.046&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=398&keepalive=yes&lmt=1608048380553749&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgR6KqCOoig_FMl2tWKa7qHSmCjIZa9S7ABzEI16qdO2sCIFXccwql4bqV9CHlqXY4tgxyMFUsp7vW4XUjxs3AyG6H&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 398, bitrate: 1348419, average_bitrate: 1097369, size: Some(22365208), + last_modified: Some(1608048380553749), index_range: Some(Range( start: 700, end: 1115, @@ -275,15 +292,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.08M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=65400181&dur=163.046&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAPjxbuzkozPDc1Nd_0q5X8x8H2SiDvAUFuqqMadtz3SNAiEA_3kXCeePb2kci-WB2779tzI56E6E0iKwoHnUSkKCzwU%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=65400181&dur=163.046&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAPjxbuzkozPDc1Nd_0q5X8x8H2SiDvAUFuqqMadtz3SNAiEA_3kXCeePb2kci-WB2779tzI56E6E0iKwoHnUSkKCzwU%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 299, bitrate: 4190323, average_bitrate: 3208919, size: Some(65400181), + last_modified: Some(1580005649161486), index_range: Some(Range( start: 740, end: 1155, @@ -301,15 +320,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.64002a\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=42567727&dur=163.046&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=399&keepalive=yes&lmt=1608052932785283&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgFguw-cmBNOQegpyRRzcCScp2WaSnq_o7FB1-AiBgFpICIAGlMj9-kzNCWb3nhpg98Mc239ls6YYyoL8z1QpM8VmL&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=42567727&dur=163.046&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=399&keepalive=yes&lmt=1608052932785283&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgFguw-cmBNOQegpyRRzcCScp2WaSnq_o7FB1-AiBgFpICIAGlMj9-kzNCWb3nhpg98Mc239ls6YYyoL8z1QpM8VmL&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 399, bitrate: 2572342, average_bitrate: 2088624, size: Some(42567727), + last_modified: Some(1608052932785283), index_range: Some(Range( start: 700, end: 1115, @@ -327,17 +348,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.09M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), ], audio_streams: [ AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=995840&dur=163.189&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=139&keepalive=yes&lmt=1580005582214385&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhALhtrbIL9_CQBeXsEwxFqyPY1XqBCOceQc7y00h7XBS9AiAH9VkMnkuFCU1ACkYU__uApTwcYeDoYNU74VYmKED3Gw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=995840&dur=163.189&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=139&keepalive=yes&lmt=1580005582214385&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhALhtrbIL9_CQBeXsEwxFqyPY1XqBCOceQc7y00h7XBS9AiAH9VkMnkuFCU1ACkYU__uApTwcYeDoYNU74VYmKED3Gw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 139, bitrate: 49724, average_bitrate: 48818, size: 995840, + last_modified: Some(1580005582214385), index_range: Some(Range( start: 641, end: 876, @@ -353,15 +376,17 @@ VideoPlayer( channels: Some(2), loudness_db: None, track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=934449&dur=163.061&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=249&keepalive=yes&lmt=1608509101590706&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgPmadKd9As393GMRmu1Ow4RfQkDQhY6SbPRnkLMYyZOoCIE9AIgMMJ7n5HD2gKv3c8-HrnkMeakq_uWUOivnWquJX&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=934449&dur=163.061&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=249&keepalive=yes&lmt=1608509101590706&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgPmadKd9As393GMRmu1Ow4RfQkDQhY6SbPRnkLMYyZOoCIE9AIgMMJ7n5HD2gKv3c8-HrnkMeakq_uWUOivnWquJX&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 249, bitrate: 53039, average_bitrate: 45845, size: 934449, + last_modified: Some(1608509101590706), index_range: Some(Range( start: 266, end: 551, @@ -377,15 +402,17 @@ VideoPlayer( channels: Some(2), loudness_db: None, track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=1245866&dur=163.061&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=250&keepalive=yes&lmt=1608509101111096&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIge8uetzhejNg3DegY9EQkpvVam1gp8Jm-q6oqtb6Rn9wCIQD_VeQle7Z2H1uXB6qsYMGDU4OWA4h6YTTwMDmw5DDvuA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=1245866&dur=163.061&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=250&keepalive=yes&lmt=1608509101111096&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIge8uetzhejNg3DegY9EQkpvVam1gp8Jm-q6oqtb6Rn9wCIQD_VeQle7Z2H1uXB6qsYMGDU4OWA4h6YTTwMDmw5DDvuA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 250, bitrate: 71268, average_bitrate: 61123, size: 1245866, + last_modified: Some(1608509101111096), index_range: Some(Range( start: 266, end: 551, @@ -401,15 +428,17 @@ VideoPlayer( channels: Some(2), loudness_db: None, track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=2640283&dur=163.096&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAI8YylDImOPxxRo251u_RX6ir_j0p-gP_yQPcI6SxareAiArCxOcgrF9pxYS5bNYEnLGEQxRiEFJ0sT2Ydpa1G7x5A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=2640283&dur=163.096&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAI8YylDImOPxxRo251u_RX6ir_j0p-gP_yQPcI6SxareAiArCxOcgrF9pxYS5bNYEnLGEQxRiEFJ0sT2Ydpa1G7x5A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=2211222&vprv=1"), itag: 140, bitrate: 130268, average_bitrate: 129508, size: 2640283, + last_modified: Some(1580005579712232), index_range: Some(Range( start: 632, end: 867, @@ -425,15 +454,17 @@ VideoPlayer( channels: Some(2), loudness_db: None, track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=2476314&dur=163.061&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=251&keepalive=yes&lmt=1608509101894140&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgErpt4HOgIybMGrMD2qg9JB4O53n0jsCxkiI9JBxbz8ECIQDixyFJ54m4NsxhyFtIYPscMVp_G6RyvwrfKzdoya-62Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=ANDROID&clen=2476314&dur=163.061&ei=q1jpYtOPEYSBgQeHmqbwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AEDMTCojVtwpIKOdhBaxEHE5s322qnAJHGqa2r1F46BM&initcwndbps=1527500&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=251&keepalive=yes&lmt=1608509101894140&lsig=AG3C_xAwRgIhAOiL-qJ04sA8FSOkEJfOYl3gFe4SzwYu_rAf3DMLHYigAiEA0Upi1HqqIu7NH_LTDL0jT1R5TTozQypL5FiSP9RoqtU%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659459429&mv=m&mvi=5&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgErpt4HOgIybMGrMD2qg9JB4O53n0jsCxkiI9JBxbz8ECIQDixyFJ54m4NsxhyFtIYPscMVp_G6RyvwrfKzdoya-62Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&txp=1311222&vprv=1"), itag: 251, bitrate: 140633, average_bitrate: 121491, size: 2476314, + last_modified: Some(1608509101894140), index_range: Some(Range( start: 266, end: 551, @@ -449,6 +480,7 @@ VideoPlayer( channels: Some(2), loudness_db: None, track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), @@ -465,6 +497,9 @@ VideoPlayer( valid_until: "[date]", 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"), + abr_streaming_url: None, + abr_ustreamer_config: Some("Cs0CCAAlMZkqPi0AAIA_NT0Klz9YAXjoAoABAaABAbUB9ijcP-ABAegBA_ABAfkBAAAAAAAA0D-BAgAAAAAAABhAmALwAaAC6AK4AgDaAlUQsOoBGKhGIKCcASjYNjCYdXCIJ4AB9AO4AQHgAQGYAgyoAgGwAgG4AgHAAgHIAgHQAgLgAgHoAgLwAgGAAwaIA4gnmAMBqAMIsAMBuAMBwAMB2AMB-gIrCAwQGBgyIDItAABwQjUAAIxCQAFIAWUAAIBAaMBwzQEAAIA_8AEB6ALgA4IDAJADAaADAbADA-ADkE6wBAG4BAHKBFgKFQiA4gkQmHUYrAIlAAAAACgAMABAARDg1AMY0A8qNgoKdGJfY29zdF81MCAIKQAAAAAAAAAASAFQAV2amZk-ZQAAAD9tAAAAP3UAAAA_eMCpB5IBADAB6AQB8AQB-AQBkAUBGAEgATIMCKsCEI662NuboOcCMgwIjwMQg-na_r_Q7QIyDAiqAhCOutjbm6DnAjIMCI4DEJXUhISv0O0CMgwIhwEQjrrY25ug5wIyDAiNAxCr6siQptDtAjIMCIYBEO_L2NuboOcCMgwIjAMQuvqao6XQ7QIyDAiFARCOutjbm6DnAjIMCIsDEPLf1JOl0O0CMgwIoAEQjrrY25ug5wIyDAiKAxDZmZnro9DtAjIMCIsBEPGp4ruboOcCMgwIjAEQ6M3Jupug5wIyDAj5ARCykfms493tAjIMCPoBELju26zj3e0CMgwI-wEQ_NOLrePd7QI6AA=="), + po_token: None, preview_frames: [ Frameset( url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLAXobPyrylgm8IEvjlZzqYTiPe1Ow", 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 d140467..f2ded40 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 @@ -66,11 +66,12 @@ VideoPlayer( ), video_streams: [ VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=11439331&dur=163.096&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=18&lmt=1580005476071743&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=ig_QojS86GYHYg&ns=cOm8mnsR9HFwfq55dDyGyqYH&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgNqstD2C4HG7Vn5En5Z4aUyH2mk7gAB9cyfOAWGCaWeoCIQDbxxJZuOnz_3RJNviFYADvgTO7u8YBYKtpFtp9Ujmk2w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=11439331&dur=163.096&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=18&lmt=1580005476071743&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=ig_QojS86GYHYg&ns=cOm8mnsR9HFwfq55dDyGyqYH&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgNqstD2C4HG7Vn5En5Z4aUyH2mk7gAB9cyfOAWGCaWeoCIQDbxxJZuOnz_3RJNviFYADvgTO7u8YBYKtpFtp9Ujmk2w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1"), itag: 18, bitrate: 561339, average_bitrate: 561109, size: Some(11439331), + last_modified: Some(1580005476071743), index_range: None, init_range: None, duration_ms: Some(163096), @@ -82,17 +83,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), ], video_only_streams: [ VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=1484736&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=278&keepalive=yes&lmt=1608509388295661&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgDYs7xrSqi-Co90zypk9zutEJ-aaEpNAHWnE57zVIfxgCIQDE0exs9SN8JH5OEJ8728Ke6bfa0CsUucFETHLk3IFF7w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=1484736&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=278&keepalive=yes&lmt=1608509388295661&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgDYs7xrSqi-Co90zypk9zutEJ-aaEpNAHWnE57zVIfxgCIQDE0exs9SN8JH5OEJ8728Ke6bfa0CsUucFETHLk3IFF7w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 278, bitrate: 87458, average_bitrate: 72857, size: Some(1484736), + last_modified: Some(1608509388295661), index_range: Some(Range( start: 218, end: 751, @@ -110,15 +113,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=1224002&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=394&keepalive=yes&lmt=1608045375671513&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAI-uoNLUkMHpH35niVh1tBvwwFLtmSbeHyknmyCvccFVAiB2XriyJd0u2q-tGIRTx5qtKt6bJCs5ndXtMsdSxOheuA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=1224002&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=394&keepalive=yes&lmt=1608045375671513&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAI-uoNLUkMHpH35niVh1tBvwwFLtmSbeHyknmyCvccFVAiB2XriyJd0u2q-tGIRTx5qtKt6bJCs5ndXtMsdSxOheuA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 394, bitrate: 67637, average_bitrate: 60063, size: Some(1224002), + last_modified: Some(1608045375671513), index_range: Some(Range( start: 700, end: 1115, @@ -136,15 +141,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.00M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=2973283&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=242&keepalive=yes&lmt=1608509388282028&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgEleuqkeo7x7BsHur5aGPfHaT6KjKEG4c1d_xXwqlrsYCIQD85X_m050XwWyYlfLiWtZz-TX--H8H0UvfZCWKpY7m4Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=2973283&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=242&keepalive=yes&lmt=1608509388282028&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgEleuqkeo7x7BsHur5aGPfHaT6KjKEG4c1d_xXwqlrsYCIQD85X_m050XwWyYlfLiWtZz-TX--H8H0UvfZCWKpY7m4Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 242, bitrate: 184064, average_bitrate: 145902, size: Some(2973283), + last_modified: Some(1608509388282028), index_range: Some(Range( start: 219, end: 753, @@ -162,15 +169,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=2238952&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=395&keepalive=yes&lmt=1608045728968690&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIBttTR02kTdGb4vdxQ9Gro88JOAY7u5z69nJbdmVS1sAiBr61rqkUtra4PHLdnp2w-s8ZSaN_4qZ3OEeeuIr5C13w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=2238952&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=395&keepalive=yes&lmt=1608045728968690&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIBttTR02kTdGb4vdxQ9Gro88JOAY7u5z69nJbdmVS1sAiBr61rqkUtra4PHLdnp2w-s8ZSaN_4qZ3OEeeuIr5C13w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 395, bitrate: 135747, average_bitrate: 109867, size: Some(2238952), + last_modified: Some(1608045728968690), index_range: Some(Range( start: 700, end: 1115, @@ -188,15 +197,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.00M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=7808990&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMBRhMAZ5GXFSZHN6D-XhXRdG_EWSNwnN2eLPlwVNQ6PAiEA75eH0iJLgwRkujaABZnaJxG2ni-4irYHEGD42x6uaQg%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=7808990&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMBRhMAZ5GXFSZHN6D-XhXRdG_EWSNwnN2eLPlwVNQ6PAiEA75eH0iJLgwRkujaABZnaJxG2ni-4irYHEGD42x6uaQg%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1"), itag: 134, bitrate: 538143, average_bitrate: 383195, size: Some(7808990), + last_modified: Some(1580005649163759), index_range: Some(Range( start: 740, end: 1155, @@ -214,15 +225,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d401e\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=5169510&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=243&keepalive=yes&lmt=1608509388282405&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgNi0fwQbep6oKsEeEGfms2Ay4x2OL2G0hUX5GFhycgKkCIANiC-j-Gz3-noxsNeSKKPxy--T9mFBu_8V7Vi5-zDYS&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=5169510&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=243&keepalive=yes&lmt=1608509388282405&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgNi0fwQbep6oKsEeEGfms2Ay4x2OL2G0hUX5GFhycgKkCIANiC-j-Gz3-noxsNeSKKPxy--T9mFBu_8V7Vi5-zDYS&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 243, bitrate: 319085, average_bitrate: 253673, size: Some(5169510), + last_modified: Some(1608509388282405), index_range: Some(Range( start: 220, end: 754, @@ -240,15 +253,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=4130385&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=396&keepalive=yes&lmt=1608045761576250&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgFuBoOIkqwq0D1_OmnNJx3C0jmhHUyskpzPrTMoaWRYECIFZ1Y4QbQ41GsWS8yRHox8l_nGVosfXhXfKu3v18AyeT&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=4130385&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=396&keepalive=yes&lmt=1608045761576250&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgFuBoOIkqwq0D1_OmnNJx3C0jmhHUyskpzPrTMoaWRYECIFZ1Y4QbQ41GsWS8yRHox8l_nGVosfXhXfKu3v18AyeT&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 396, bitrate: 258097, average_bitrate: 202682, size: Some(4130385), + last_modified: Some(1608045761576250), index_range: Some(Range( start: 700, end: 1115, @@ -266,15 +281,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.01M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=8890590&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=244&keepalive=yes&lmt=1608509388284632&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgMYF0KQQNgYI8oOhgdCwyRY6E_hvFnJiaAadyMf89MRoCIHnDnROTvUoy0iIBM3MzFAxJh_bLA-2vFl9KFDrHOf1B&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=8890590&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=244&keepalive=yes&lmt=1608509388284632&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgMYF0KQQNgYI8oOhgdCwyRY6E_hvFnJiaAadyMf89MRoCIHnDnROTvUoy0iIBM3MzFAxJh_bLA-2vFl9KFDrHOf1B&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 244, bitrate: 539056, average_bitrate: 436270, size: Some(8890590), + last_modified: Some(1608509388284632), index_range: Some(Range( start: 220, end: 754, @@ -292,15 +309,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=6873325&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=397&keepalive=yes&lmt=1608045990917419&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAOtLGFoFtLHIXzNRoSrR7ULbIz91OYmaVQkcSatqNKAiAiEA23ZF7h2BZZCAGc0Zdd2p3PWRotmwLDyH6yYCuQpE8xw%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=6873325&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=397&keepalive=yes&lmt=1608045990917419&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAOtLGFoFtLHIXzNRoSrR7ULbIz91OYmaVQkcSatqNKAiAiEA23ZF7h2BZZCAGc0Zdd2p3PWRotmwLDyH6yYCuQpE8xw%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 397, bitrate: 436843, average_bitrate: 337281, size: Some(6873325), + last_modified: Some(1608045990917419), index_range: Some(Range( start: 700, end: 1115, @@ -318,15 +337,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.04M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=16547577&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=247&keepalive=yes&lmt=1608509388326822&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgfYKbT_196P-2EtjuqcTKdataiM480y65Ko0a73dv7WECIQC6nqWienQvu7swC1OW9HlwFWRH7VwTwj6H4yjY6FYvzg%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=16547577&dur=163.029&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=247&keepalive=yes&lmt=1608509388326822&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgfYKbT_196P-2EtjuqcTKdataiM480y65Ko0a73dv7WECIQC6nqWienQvu7swC1OW9HlwFWRH7VwTwj6H4yjY6FYvzg%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 247, bitrate: 982813, average_bitrate: 812006, size: Some(16547577), + last_modified: Some(1608509388326822), index_range: Some(Range( start: 220, end: 754, @@ -344,15 +365,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=35955780&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=302&keepalive=yes&lmt=1608509234088626&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgQG8GPj3w_5_Lr2apagmte66IFBY3bYcZ2KnhwnUpshYCIFgvHYIZsz8WdYGSk9adpfMNKX0pzSP_l8cW47Gq2RTi&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=35955780&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=302&keepalive=yes&lmt=1608509234088626&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgQG8GPj3w_5_Lr2apagmte66IFBY3bYcZ2KnhwnUpshYCIFgvHYIZsz8WdYGSk9adpfMNKX0pzSP_l8cW47Gq2RTi&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 302, bitrate: 2354009, average_bitrate: 1764202, size: Some(35955780), + last_modified: Some(1608509234088626), index_range: Some(Range( start: 219, end: 771, @@ -370,15 +393,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=22365208&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=398&keepalive=yes&lmt=1608048380553749&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAI-VhcBU6o8LGmeuVYC2_zbxeGvC6XWf7yIOQ1RvjURhAiEA0YcZlVOI2ZUtKl-31__Hzax2SOUPeekCRjqjfw4m15s%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=22365208&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=398&keepalive=yes&lmt=1608048380553749&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAI-VhcBU6o8LGmeuVYC2_zbxeGvC6XWf7yIOQ1RvjURhAiEA0YcZlVOI2ZUtKl-31__Hzax2SOUPeekCRjqjfw4m15s%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 398, bitrate: 1348419, average_bitrate: 1097369, size: Some(22365208), + last_modified: Some(1608048380553749), index_range: Some(Range( start: 700, end: 1115, @@ -396,15 +421,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.08M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=65400181&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAIdbG-deTvLhp7mD2b-QZYQamPFv75l1bNBEEOMihrxPAiEA1NYvRlFphbRRvFIBCP-Ij9-5q8OTwUskgsL6LyIrD7c%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=65400181&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAIdbG-deTvLhp7mD2b-QZYQamPFv75l1bNBEEOMihrxPAiEA1NYvRlFphbRRvFIBCP-Ij9-5q8OTwUskgsL6LyIrD7c%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1"), itag: 299, bitrate: 4190323, average_bitrate: 3208919, size: Some(65400181), + last_modified: Some(1580005649161486), index_range: Some(Range( start: 740, end: 1155, @@ -422,15 +449,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.64002a\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=62993617&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=303&keepalive=yes&lmt=1608509371758331&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAJ8n34LQhg6iEg1Ux9rDkk48e8l3vBR4WwuHeIpKnorlAiBopK4z-nq-pJTPTmrdbbKPW1Lfufdz2f9sGUKY-dzk5A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=62993617&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=303&keepalive=yes&lmt=1608509371758331&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAJ8n34LQhg6iEg1Ux9rDkk48e8l3vBR4WwuHeIpKnorlAiBopK4z-nq-pJTPTmrdbbKPW1Lfufdz2f9sGUKY-dzk5A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 303, bitrate: 3832648, average_bitrate: 3090839, size: Some(62993617), + last_modified: Some(1608509371758331), index_range: Some(Range( start: 219, end: 776, @@ -448,15 +477,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=42567727&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=399&keepalive=yes&lmt=1608052932785283&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAMewAT3SgJRGn7wqDaDzNWcsAfrjFRu6k0wm7O_5YJeQAiANVhGmILp_gmNXnmixDesxsZ44_72YBT2SqjLLSZV32w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303%2C394%2C395%2C396%2C397%2C398%2C399&c=WEB&clen=42567727&dur=163.046&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=399&keepalive=yes&lmt=1608052932785283&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAMewAT3SgJRGn7wqDaDzNWcsAfrjFRu6k0wm7O_5YJeQAiANVhGmILp_gmNXnmixDesxsZ44_72YBT2SqjLLSZV32w%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 399, bitrate: 2572342, average_bitrate: 2088624, size: Some(42567727), + last_modified: Some(1608052932785283), index_range: Some(Range( start: 700, end: 1115, @@ -474,17 +505,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"av01.0.09M.08\"", format: mp4, codec: av01, + xtags: None, drm_track_type: None, drm_systems: [], ), ], audio_streams: [ AudioStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=934449&dur=163.061&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=249&keepalive=yes&lmt=1608509101590706&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAOdVu1woKNveQspV4WPm1PHrOBuzlrnPu2ZLvyiYZCSbAiAYODN_y5t1oU334SHUqqgyc4Wnt-9If-W98Fd966fy2A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=934449&dur=163.061&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=249&keepalive=yes&lmt=1608509101590706&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAOdVu1woKNveQspV4WPm1PHrOBuzlrnPu2ZLvyiYZCSbAiAYODN_y5t1oU334SHUqqgyc4Wnt-9If-W98Fd966fy2A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 249, bitrate: 53039, average_bitrate: 45845, size: 934449, + last_modified: Some(1608509101590706), index_range: Some(Range( start: 266, end: 551, @@ -500,15 +533,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2200003), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=1245866&dur=163.061&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=250&keepalive=yes&lmt=1608509101111096&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAMJJ-uGQureE70LIxHjHP9hFxqcWwsSlxXX6EjGKmFfEAiAvQ98YKkqUrweNnBZOI7pXJk1kuU_1hSsQ0KeNU4CbyQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=1245866&dur=163.061&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=250&keepalive=yes&lmt=1608509101111096&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAMJJ-uGQureE70LIxHjHP9hFxqcWwsSlxXX6EjGKmFfEAiAvQ98YKkqUrweNnBZOI7pXJk1kuU_1hSsQ0KeNU4CbyQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 250, bitrate: 71268, average_bitrate: 61123, size: 1245866, + last_modified: Some(1608509101111096), index_range: Some(Range( start: 266, end: 551, @@ -524,15 +559,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2200003), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=2640283&dur=163.096&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhANXIw4pIwIPvMGWnJSrA_bnmBX6KPBPqak18aPtKsI8jAiBvisRnEtFax7OTrwKbOiktCihoMraLkCi7Rnnu6JGmeQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=2640283&dur=163.096&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhANXIw4pIwIPvMGWnJSrA_bnmBX6KPBPqak18aPtKsI8jAiBvisRnEtFax7OTrwKbOiktCihoMraLkCi7Rnnu6JGmeQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=2211222&vprv=1"), itag: 140, bitrate: 130268, average_bitrate: 129508, size: 2640283, + last_modified: Some(1580005579712232), index_range: Some(Range( start: 632, end: 867, @@ -548,15 +585,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2159004), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=2476314&dur=163.061&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=251&keepalive=yes&lmt=1608509101894140&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAKP_KjT_SSnz5WGXaveO56pJAEw166qT3cpBdAZI1zwCAiBWZKVQZxfOPWnqSp5FnRDyQBQ-6a2nZopyA1eHicgHtw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=WEB&clen=2476314&dur=163.061&ei=q1jpYtq3BJCX1gKVyJGQDg&expire=1659481355&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AGfBIFoT5D_NZAwXN7lVCS2VYLDMMegfaJQqvSJp-Hhy&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=251&keepalive=yes&lmt=1608509101894140&lsig=AG3C_xAwRgIhANxHzq0WC6OvdTpPJp52z3eGAm-jzUX7fcKiWlJ0T9kEAiEA02Bjesi_an2-pUh0kHdKQe0s_7micbcv3JKiBlxsYGs%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C26&mn=sn-h0jelnez%2Csn-4g5edn6k&ms=au%2Conr&mt=1659459429&mv=m&mvi=4&n=T16m7p0RvV7UhQ&ns=tWuNfisHu8yiCA6Avm7nUlwH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAKP_KjT_SSnz5WGXaveO56pJAEw166qT3cpBdAZI1zwCAiBWZKVQZxfOPWnqSp5FnRDyQBQ-6a2nZopyA1eHicgHtw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-KhrZGE2opztWyVdAtyUNlb8dXPDs&txp=1311222&vprv=1"), itag: 251, bitrate: 140633, average_bitrate: 121491, size: 2476314, + last_modified: Some(1608509101894140), index_range: Some(Range( start: 266, end: 551, @@ -572,6 +611,7 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2200003), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), @@ -588,6 +628,9 @@ VideoPlayer( valid_until: "[date]", 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"), + abr_streaming_url: None, + abr_ustreamer_config: None, + po_token: None, preview_frames: [ Frameset( url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLAXobPyrylgm8IEvjlZzqYTiPe1Ow", 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 47fbb1e..7308a6e 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 @@ -34,11 +34,12 @@ VideoPlayer( ), video_streams: [ VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=11439331&dur=163.096&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=18&lmt=1580005476071743&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=mAEwZepBJSQPkQ&ns=orl5qWACo00YlHHyQZ7a6awH&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhANfXubbDcXpc25m3F5xQ97ygJRjrTvm8ruVxgnxgFAUBAiAEnj_3KacDNTTLUk-6ZEbL-52zxmBLU1iuTEDx0NvJzA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=11439331&dur=163.096&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=18&lmt=1580005476071743&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=mAEwZepBJSQPkQ&ns=orl5qWACo00YlHHyQZ7a6awH&pl=37&ratebypass=yes&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhANfXubbDcXpc25m3F5xQ97ygJRjrTvm8ruVxgnxgFAUBAiAEnj_3KacDNTTLUk-6ZEbL-52zxmBLU1iuTEDx0NvJzA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1"), itag: 18, bitrate: 561339, average_bitrate: 561109, size: Some(11439331), + last_modified: Some(1580005476071743), index_range: None, init_range: None, duration_ms: Some(163096), @@ -50,17 +51,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), ], video_only_streams: [ VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=1484736&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=278&keepalive=yes&lmt=1608509388295661&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgEQ0-VVvo41T4l2X26p5zP8Wo8sXOPmBWvCf2OW33ilgCIH2bIFOYgpmsml7FvRQj_SoLzPh7yBvmLZ61Kgsj4FUe&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=1484736&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=278&keepalive=yes&lmt=1608509388295661&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgEQ0-VVvo41T4l2X26p5zP8Wo8sXOPmBWvCf2OW33ilgCIH2bIFOYgpmsml7FvRQj_SoLzPh7yBvmLZ61Kgsj4FUe&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 278, bitrate: 87458, average_bitrate: 72857, size: Some(1484736), + last_modified: Some(1608509388295661), index_range: Some(Range( start: 218, end: 751, @@ -78,15 +81,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=2973283&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=242&keepalive=yes&lmt=1608509388282028&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAO7DI5E91yHpLhgiWg9C99NsMoJBVOWsNTNF3os9kREQAiAr2oC8vFtXIHwkJJt45q0sdmjiJdkTO2i8VAjUodk6Xw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=2973283&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=242&keepalive=yes&lmt=1608509388282028&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAO7DI5E91yHpLhgiWg9C99NsMoJBVOWsNTNF3os9kREQAiAr2oC8vFtXIHwkJJt45q0sdmjiJdkTO2i8VAjUodk6Xw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 242, bitrate: 184064, average_bitrate: 145902, size: Some(2973283), + last_modified: Some(1608509388282028), index_range: Some(Range( start: 219, end: 753, @@ -104,15 +109,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=7808990&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgTkOjFd0nExEtpr8sBIaNu9HhkxWNdjhSKufHMhLR8-8CIHJAmOuCD7VBv_krH6rn5zqXFqAfsq9rQPXlC3CcQrjM&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=7808990&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgTkOjFd0nExEtpr8sBIaNu9HhkxWNdjhSKufHMhLR8-8CIHJAmOuCD7VBv_krH6rn5zqXFqAfsq9rQPXlC3CcQrjM&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1"), itag: 134, bitrate: 538143, average_bitrate: 383195, size: Some(7808990), + last_modified: Some(1580005649163759), index_range: Some(Range( start: 740, end: 1155, @@ -130,15 +137,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d401e\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=5169510&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=243&keepalive=yes&lmt=1608509388282405&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAPqQfxwIANgIC3DrQ6avaWOhCvIMLdzMPQtFOx2gwEXNAiAwJp2mgN9-zl4vPOB2uoQXOfmGsYDB470q1zg7wRW4Sw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=5169510&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=243&keepalive=yes&lmt=1608509388282405&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAPqQfxwIANgIC3DrQ6avaWOhCvIMLdzMPQtFOx2gwEXNAiAwJp2mgN9-zl4vPOB2uoQXOfmGsYDB470q1zg7wRW4Sw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 243, bitrate: 319085, average_bitrate: 253673, size: Some(5169510), + last_modified: Some(1608509388282405), index_range: Some(Range( start: 220, end: 754, @@ -156,15 +165,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=8890590&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=244&keepalive=yes&lmt=1608509388284632&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIjdvhcThMxoo_v2bzEjaR_w0ryWFQDs0f0INaI5WPcVAiApQZUYTqcQJdfxZlNSsp7cl3FK8XPfDZ-qbVvj9GuauQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=8890590&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=244&keepalive=yes&lmt=1608509388284632&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIjdvhcThMxoo_v2bzEjaR_w0ryWFQDs0f0INaI5WPcVAiApQZUYTqcQJdfxZlNSsp7cl3FK8XPfDZ-qbVvj9GuauQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 244, bitrate: 539056, average_bitrate: 436270, size: Some(8890590), + last_modified: Some(1608509388284632), index_range: Some(Range( start: 220, end: 754, @@ -182,15 +193,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=16547577&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=247&keepalive=yes&lmt=1608509388326822&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgBV4Oa1IQ0YNDvRrKO5ec3Pfbg65MxzmIxCcm0gOuwT0CIFysQdow6DQXzz1W9KZVuqACTdjXQ3-yiBj9GcmNw3HE&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=16547577&dur=163.029&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=247&keepalive=yes&lmt=1608509388326822&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgBV4Oa1IQ0YNDvRrKO5ec3Pfbg65MxzmIxCcm0gOuwT0CIFysQdow6DQXzz1W9KZVuqACTdjXQ3-yiBj9GcmNw3HE&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 247, bitrate: 982813, average_bitrate: 812006, size: Some(16547577), + last_modified: Some(1608509388326822), index_range: Some(Range( start: 220, end: 754, @@ -208,15 +221,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=35955780&dur=163.046&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=302&keepalive=yes&lmt=1608509234088626&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAOiqSNfGfOprZ9InWVMc7gY0KrTf8weLibcpK0W2Hfa6AiAFHW213qsByzlar5ivCAYttjo1rPciQnLEnh-izJ3ZhA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=35955780&dur=163.046&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=302&keepalive=yes&lmt=1608509234088626&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAOiqSNfGfOprZ9InWVMc7gY0KrTf8weLibcpK0W2Hfa6AiAFHW213qsByzlar5ivCAYttjo1rPciQnLEnh-izJ3ZhA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 302, bitrate: 2354009, average_bitrate: 1764202, size: Some(35955780), + last_modified: Some(1608509234088626), index_range: Some(Range( start: 219, end: 771, @@ -234,15 +249,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=65400181&dur=163.046&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgdkJv6w9_Azf0m6poA-ULyX0eH_GKBtSJRwUY1lNBAZgCIDCrC0lnu__ycTaIhg0pUcsRUqay60S3QMo5084EWifd&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=65400181&dur=163.046&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgdkJv6w9_Azf0m6poA-ULyX0eH_GKBtSJRwUY1lNBAZgCIDCrC0lnu__ycTaIhg0pUcsRUqay60S3QMo5084EWifd&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1"), itag: 299, bitrate: 4190323, average_bitrate: 3208919, size: Some(65400181), + last_modified: Some(1580005649161486), index_range: Some(Range( start: 740, end: 1155, @@ -260,15 +277,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.64002a\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=62993617&dur=163.046&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=303&keepalive=yes&lmt=1608509371758331&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgZi9dDSMWh10NID8-QNn3azIH1zw5UooZrRTPZjVn7hYCIAm9bFc6NBwJ_DzY4V2R_zGmJSpOwQl8LEsfCb7hf6i7&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=62993617&dur=163.046&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=303&keepalive=yes&lmt=1608509371758331&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgZi9dDSMWh10NID8-QNn3azIH1zw5UooZrRTPZjVn7hYCIAm9bFc6NBwJ_DzY4V2R_zGmJSpOwQl8LEsfCb7hf6i7&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 303, bitrate: 3832648, average_bitrate: 3090839, size: Some(62993617), + last_modified: Some(1608509371758331), index_range: Some(Range( start: 219, end: 776, @@ -286,17 +305,19 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), ], audio_streams: [ AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=934449&dur=163.061&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=249&keepalive=yes&lmt=1608509101590706&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMUfr2X-eQJt1abn-IK1H4d5DtvKZuBaETo4opNi6mqCAiEAvBmrmuaoFjB1CJ2Kug87w-Uv6OCdxyrJ_3HIaHX9KBI%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=934449&dur=163.061&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=249&keepalive=yes&lmt=1608509101590706&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMUfr2X-eQJt1abn-IK1H4d5DtvKZuBaETo4opNi6mqCAiEAvBmrmuaoFjB1CJ2Kug87w-Uv6OCdxyrJ_3HIaHX9KBI%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 249, bitrate: 53039, average_bitrate: 45845, size: 934449, + last_modified: Some(1608509101590706), index_range: Some(Range( start: 266, end: 551, @@ -312,15 +333,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(0.0006532669), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=1245866&dur=163.061&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=250&keepalive=yes&lmt=1608509101111096&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIflUU4t4Ukf4CXW3ttB5c8SnP4z4z3ef-7EFVMFv4U8AiAlcKmmofCTzzr2NRRsRVosQdpDBphUqWyVzS7noGrixw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=1245866&dur=163.061&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=250&keepalive=yes&lmt=1608509101111096&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIhAIflUU4t4Ukf4CXW3ttB5c8SnP4z4z3ef-7EFVMFv4U8AiAlcKmmofCTzzr2NRRsRVosQdpDBphUqWyVzS7noGrixw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 250, bitrate: 71268, average_bitrate: 61123, size: 1245866, + last_modified: Some(1608509101111096), index_range: Some(Range( start: 266, end: 551, @@ -336,15 +359,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(0.0006532669), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=2640283&dur=163.096&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhALBveArAZ7DP9r1BIpNz6ZXst5MtzvUM72jhtYMrildCAiEArvwqaqcowZwR_UrRM-O7jq2CMxgBtbnmul27AEcBqEI%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=2640283&dur=163.096&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhALBveArAZ7DP9r1BIpNz6ZXst5MtzvUM72jhtYMrildCAiEArvwqaqcowZwR_UrRM-O7jq2CMxgBtbnmul27AEcBqEI%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=2211222&vprv=1"), itag: 140, bitrate: 130268, average_bitrate: 129508, size: 2640283, + last_modified: Some(1580005579712232), index_range: Some(Range( start: 632, end: 867, @@ -360,15 +385,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(-0.003446579), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=2476314&dur=163.061&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=251&keepalive=yes&lmt=1608509101894140&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgARPqD-6172OshMHeV8DpONV7tnPvdsxcg8QlaIGxcuMCICSe8LWvhRTEO2bdAQ43OzOoc5XfJcj3veyhScVXVz8O&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=WEB_REMIX&clen=2476314&dur=163.061&ei=knDpYub6BojEgAf6jbLgDw&expire=1659487474&fexp=24001373%2C24007246&fvip=4&gir=yes&id=o-AM-wcJVO-yYYbVFnuifnzM4eRnD-AG1bS1AhLoDqi_is&initcwndbps=1418750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=251&keepalive=yes&lmt=1608509101894140&lsig=AG3C_xAwRgIhAMwYJqxve8BSujC-oaSFBbq67p-rFi7saU5V8Yb3qrjLAiEAlrMKR_sadHrkFpy7o7lGzKOCmU1OQazCNBbXjDT2a-o%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1659465669&mv=m&mvi=5&n=1taQMNHGExb_Vg&ns=UTT8RXHZNhPYTw6NgkzWMWEH&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRAIgARPqD-6172OshMHeV8DpONV7tnPvdsxcg8QlaIGxcuMCICSe8LWvhRTEO2bdAQ43OzOoc5XfJcj3veyhScVXVz8O&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&spc=lT-Khox4YuJQ2wmH79zYALRvsWTPCUc&txp=1311222&vprv=1"), itag: 251, bitrate: 140633, average_bitrate: 121491, size: 2476314, + last_modified: Some(1608509101894140), index_range: Some(Range( start: 266, end: 551, @@ -384,6 +411,7 @@ VideoPlayer( channels: Some(2), loudness_db: Some(0.0006532669), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), @@ -400,6 +428,9 @@ VideoPlayer( valid_until: "[date]", 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"), + abr_streaming_url: None, + abr_ustreamer_config: None, + po_token: None, preview_frames: [ Frameset( url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLAXobPyrylgm8IEvjlZzqYTiPe1Ow", 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 94d9f38..7914ab5 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 @@ -57,11 +57,12 @@ VideoPlayer( video_streams: [], video_only_streams: [ VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=7808990&dur=163.029&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMFQo_RyC3Ud44QVGtKckMcuq5UQ3J7CgYsYl0bXaWMUAiEAhMi1h0ru4zoIGX0jBZT23dozvtrhf_m61p4qbAfEhZo%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=7808990&dur=163.029&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=134&keepalive=yes&lmt=1580005649163759&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMFQo_RyC3Ud44QVGtKckMcuq5UQ3J7CgYsYl0bXaWMUAiEAhMi1h0ru4zoIGX0jBZT23dozvtrhf_m61p4qbAfEhZo%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1"), itag: 134, bitrate: 538143, average_bitrate: 383195, size: Some(7808990), + last_modified: Some(1580005649163759), index_range: Some(Range( start: 740, end: 1155, @@ -79,15 +80,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4D401E\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=65400181&dur=163.046&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAP6zxXXA18ToZWUfalauhhsgOsDHTu-R0QrqNrJR7D5kAiEAi8HBa9OkYwmA0bcRxhgvXfN9JsFlXwCWJ-x4ty6TjoY%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=65400181&dur=163.046&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=299&keepalive=yes&lmt=1580005649161486&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAP6zxXXA18ToZWUfalauhhsgOsDHTu-R0QrqNrJR7D5kAiEAi8HBa9OkYwmA0bcRxhgvXfN9JsFlXwCWJ-x4ty6TjoY%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1"), itag: 299, bitrate: 4190323, average_bitrate: 3208919, size: Some(65400181), + last_modified: Some(1580005649161486), index_range: Some(Range( start: 740, end: 1155, @@ -105,17 +108,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.64002A\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), ], audio_streams: [ AudioStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=995840&dur=163.189&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=139&keepalive=yes&lmt=1580005582214385&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMXDvZZznm1LafIKh_pdGf-TjY5Kz-F9N67o6gXenKouAiEA5qji45i5ABmAytPDOORjw0OaBmwX88S7bgUWcmF-_LU%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=995840&dur=163.189&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=139&keepalive=yes&lmt=1580005582214385&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRgIhAMXDvZZznm1LafIKh_pdGf-TjY5Kz-F9N67o6gXenKouAiEA5qji45i5ABmAytPDOORjw0OaBmwX88S7bgUWcmF-_LU%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1"), itag: 139, bitrate: 49724, average_bitrate: 48818, size: 995840, + last_modified: Some(1580005582214385), index_range: Some(Range( start: 641, end: 876, @@ -131,15 +136,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2159004), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=2640283&dur=163.096&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgcsPm6rrUAwCi1VTGf0FMDTjzjM01__iTC13PnzDTFeQCIQCJ_EGeKVZztkmK3Cr7gVuxUP83XCSlP01YLx5FO-PPcQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1", + url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&clen=2640283&dur=163.096&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&gir=yes&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&itag=140&keepalive=yes&lmt=1580005579712232&lsig=AG3C_xAwRQIgWKVoDpyI6QmVnkdGzdirFtjMAXhmLex64VTO7UUJd-4CIQDoJKkT2-Kpa7j0merJJoZDs4IkkXSjdNm3bvdCL8t2Pg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&otfp=1&pl=37&rbqsm=fr&requiressl=yes&sig=AOq0QJ8wRQIgcsPm6rrUAwCi1VTGf0FMDTjzjM01__iTC13PnzDTFeQCIQCJ_EGeKVZztkmK3Cr7gVuxUP83XCSlP01YLx5FO-PPcQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cotfp%2Cdur%2Clmt&svpuc=1&txp=2211222&vprv=1"), itag: 140, bitrate: 130268, average_bitrate: 129508, size: 2640283, + last_modified: Some(1580005579712232), index_range: Some(Range( start: 632, end: 867, @@ -155,6 +162,7 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2159004), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), @@ -171,6 +179,9 @@ VideoPlayer( valid_until: "[date]", 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, + abr_streaming_url: Some("https://rr4---sn-h0jelnez.googlevideo.com/videoplayback?c=IOS&ei=q1jpYq-xHs7NgQev0bfwAQ&expire=1659481355&fexp=24001373%2C24007246&fvip=5&id=o-ANNg3iPHI56jhLSlPQk4pi4mdub5iAby0hmJBVrtiJgY&initcwndbps=1513750&ip=2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e&keepalive=yes&lsig=AG3C_xAwRgIhAK6T5ehnFBsc0FOurPHH1ME_vGcVysI-g5jrtEsvX64sAiEArY-iAvQCsc4R8yg8dvMdpnuHPIcPMCnRgyh8E527HF0%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mm=31%2C29&mn=sn-h0jelnez%2Csn-h0jeenek&ms=au%2Crdu&mt=1659459429&mv=m&mvi=4&pl=37&rbqsm=fr&requiressl=yes&sabr=1&sig=AOq0QJ8wRgIhAJCJpb5gE12jQc2qOUy-Y61vEHeiAP_F78weNCzj8VklAiEAwR2PK52CmwsVHfRVk75OOYOxwYKNW2g1eDBw3COBP9w%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Csvpuc%2Csabr&svpuc=1"), + abr_ustreamer_config: Some("CpYECp0DCAAQgAUY6AIl-n6qPi0AAIA_NT0Klz9oAXI6ChJtZnMyX2NtZnNfdjNfMV8wNDMSIAoeCgxkZXZpY2VfbW9kZWwSDgoMCgppcGhvbmUxNCw1GAAgAXIWChJtZnMyX2NtZnNfdjNfMV8wNDMYAHjoAoABAagBAbUB9ijcP6AC6AK4AgDaAmEQsOoBGKhGIKCcASjYNjCYdXCIJ4AB9AO4AQHgAQP4AQGQAgGYAgygAgGoAgGwAgG4AgHAAgHIAgHQAgLgAgHoAgLwAgGAAwKIA4gnmAMBqAMIwAMByAMB2AMB-LWR5QwB-gIvCAwQGBgyIDItAABwQjUAAIxCQAFIAVAKWAplAACAQGjAcM0BAACAP_ABAegC0AWCAwCQAwGgAwGoAwGwAwPQAwHYAwHgA5BOuAQBygRWChUI4NQDEJh1GOgHJQAAAAAoADAAQAEQ4NQDGNAPKjYKCnRiX2Nvc3RfNTAgDykAAAAAAAAAAEgBUAFdZmbmPmXNzEw-bZqZGT91mpkZP3jAqQeSAQDoBAHwBAH4BAGQBQGgBQEYASABMgwIqwIQjrrY25ug5wIyDAiqAhCOutjbm6DnAjIMCIcBEI662NuboOcCMgwIhgEQ78vY25ug5wIyDAiFARCOutjbm6DnAjIMCKABEI662NuboOcCMgwIiwEQ8aniu5ug5wIyDAiMARDozcm6m6DnAjoAEkwA8tjvwTBFAiAyo6mtW-M8Y8noBO_6Y3RmNo5U2LntCwiKcI2yye_zRgIhAK27d1UbqzhtkQhminIBAajD70ONeAQNwjt7VRpJRDimGgJlaQ=="), + po_token: None, preview_frames: [ Frameset( url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCsCT8Lprh2S0ptmCRsWH7VtDl3YQ", diff --git a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tv.snap b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tv.snap index b7d5a29..bada27e 100644 --- a/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tv.snap +++ b/src/client/snapshots/rustypipe__client__player__tests__map_player_data_tv.snap @@ -24,11 +24,12 @@ VideoPlayer( ), video_streams: [ VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IT4iUCpJNJWUitTMgIi6njuKSsi3MNed1Szyf0qysTX0v1Nf6AyCvjIGbek5Fn50kuBrGtRJ5q&c=TVHTML5&clen=10262148&dur=163.096&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=18&lmt=1700885551970466&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=BMzwItzIOB1HhmG&ns=YmgbZhlLp0C-9ilsQWGAyUAQ&pl=26&ratebypass=yes&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgUah4qH8RqPzmo75ExCWSiRYlUlsAk0v9gl638LitVNICICxFs5lK3CsmOAja0bsXavXkyykzpdhHZKGXOZQYT1f8&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&svpuc=1&txp=1318224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IT4iUCpJNJWUitTMgIi6njuKSsi3MNed1Szyf0qysTX0v1Nf6AyCvjIGbek5Fn50kuBrGtRJ5q&c=TVHTML5&clen=10262148&dur=163.096&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=18&lmt=1700885551970466&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=BMzwItzIOB1HhmG&ns=YmgbZhlLp0C-9ilsQWGAyUAQ&pl=26&ratebypass=yes&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgUah4qH8RqPzmo75ExCWSiRYlUlsAk0v9gl638LitVNICICxFs5lK3CsmOAja0bsXavXkyykzpdhHZKGXOZQYT1f8&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&svpuc=1&txp=1318224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 18, bitrate: 503574, average_bitrate: 503367, size: Some(10262148), + last_modified: Some(1700885551970466), index_range: None, init_range: None, duration_ms: Some(163096), @@ -40,17 +41,19 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), ], video_only_streams: [ VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2273274&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=160&keepalive=yes&lmt=1705967288821438&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgb8eXnQ6MSJ3PuvFVBdYIWTnFobH8mTC9zbZpBNxLbBYCICkPLKEm3gNbW5HIFXs7bwF5rSqUKHHnXNK91qMslQog&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2273274&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=160&keepalive=yes&lmt=1705967288821438&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgb8eXnQ6MSJ3PuvFVBdYIWTnFobH8mTC9zbZpBNxLbBYCICkPLKEm3gNbW5HIFXs7bwF5rSqUKHHnXNK91qMslQog&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 160, bitrate: 114816, average_bitrate: 111551, size: Some(2273274), + last_modified: Some(1705967288821438), index_range: Some(Range( start: 738, end: 1165, @@ -68,15 +71,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d400c\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=1151892&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=278&keepalive=yes&lmt=1705966620402771&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAP4IybR7cZRpx7IX1ke6UIu_hdFZN3LOuHBDywg_xv5WAiB8_XEx8VhT9OlFxmM-cY0fl6-7GT9uj3clMIPDk2w7cA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=1151892&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=278&keepalive=yes&lmt=1705966620402771&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAP4IybR7cZRpx7IX1ke6UIu_hdFZN3LOuHBDywg_xv5WAiB8_XEx8VhT9OlFxmM-cY0fl6-7GT9uj3clMIPDk2w7cA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 278, bitrate: 70630, average_bitrate: 56524, size: Some(1151892), + last_modified: Some(1705966620402771), index_range: Some(Range( start: 218, end: 767, @@ -94,15 +99,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=5026513&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=133&keepalive=yes&lmt=1705967298859029&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgPF0ms4OEe15BTjOFVCkvf52UeTUf0b62_pavCfEyGjcCIH-0AoxzyT8iioWFFaX7iYjqzzaUTpo8rgAPQ0uX8DJa&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=5026513&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=133&keepalive=yes&lmt=1705967298859029&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgPF0ms4OEe15BTjOFVCkvf52UeTUf0b62_pavCfEyGjcCIH-0AoxzyT8iioWFFaX7iYjqzzaUTpo8rgAPQ0uX8DJa&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 133, bitrate: 257417, average_bitrate: 246656, size: Some(5026513), + last_modified: Some(1705967298859029), index_range: Some(Range( start: 739, end: 1166, @@ -120,15 +127,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d4015\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2541351&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=242&keepalive=yes&lmt=1705966614837727&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgKj1JyMGwYtf16zLJsmbnizz5_v3jaZSa7-j-ls8-qzECIQDKUd50iIc52h7zOX50Hf1SkbV9h-hP4QHs-wkik1fk6Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2541351&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=242&keepalive=yes&lmt=1705966614837727&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgKj1JyMGwYtf16zLJsmbnizz5_v3jaZSa7-j-ls8-qzECIQDKUd50iIc52h7zOX50Hf1SkbV9h-hP4QHs-wkik1fk6Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 242, bitrate: 149589, average_bitrate: 124706, size: Some(2541351), + last_modified: Some(1705966614837727), index_range: Some(Range( start: 219, end: 768, @@ -146,15 +155,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=7810925&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=134&keepalive=yes&lmt=1705967286812435&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAJ92IgZgdk3_WLsfzJV_ZyrSFSbzpsoJh3DkRKDHbNxzAiEA9UbnVlXQ2S3BUimLmWC5TZQfhIkc-PlLnZ81fL0S5yA%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=7810925&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=134&keepalive=yes&lmt=1705967286812435&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAJ92IgZgdk3_WLsfzJV_ZyrSFSbzpsoJh3DkRKDHbNxzAiEA9UbnVlXQ2S3BUimLmWC5TZQfhIkc-PlLnZ81fL0S5yA%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 134, bitrate: 537902, average_bitrate: 383290, size: Some(7810925), + last_modified: Some(1705967286812435), index_range: Some(Range( start: 740, end: 1167, @@ -172,15 +183,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d401e\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=4188954&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=243&keepalive=yes&lmt=1705966624121874&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgSCLGQvdZKNXym0zt7c3Yw_4e0J8-wNxtPagPRRn4dRoCIQCOj0IzalNG4EcowBIyK2LC6NLFDr8Zt6sNVkqPjw6lGg%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=4188954&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=243&keepalive=yes&lmt=1705966624121874&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgSCLGQvdZKNXym0zt7c3Yw_4e0J8-wNxtPagPRRn4dRoCIQCOj0IzalNG4EcowBIyK2LC6NLFDr8Zt6sNVkqPjw6lGg%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 243, bitrate: 248858, average_bitrate: 205556, size: Some(4188954), + last_modified: Some(1705966624121874), index_range: Some(Range( start: 220, end: 770, @@ -198,15 +211,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=14723538&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=135&keepalive=yes&lmt=1705967282545273&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAM843wAa1e7Gc1S69gfXckm7hdgIKPXp0bUSh3hO6W5zAiEA-DDEPGsZBmF5N8VbPy75dhy3rLpE1F18KtWgmrUm2Pg%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=14723538&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=135&keepalive=yes&lmt=1705967282545273&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAM843wAa1e7Gc1S69gfXckm7hdgIKPXp0bUSh3hO6W5zAiEA-DDEPGsZBmF5N8VbPy75dhy3rLpE1F18KtWgmrUm2Pg%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 135, bitrate: 978945, average_bitrate: 722499, size: Some(14723538), + last_modified: Some(1705967282545273), index_range: Some(Range( start: 740, end: 1167, @@ -224,15 +239,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d401f\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=7788899&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=244&keepalive=yes&lmt=1705966622098793&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAKGyn799bfkVHYE195sPmD60dCMppqJrBM0O-sjgYTzzAiAoBjkNAtL90sXw2YP9UTW9JrMhPSvPiBI_KiCVMJAkFQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=7788899&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=244&keepalive=yes&lmt=1705966622098793&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAKGyn799bfkVHYE195sPmD60dCMppqJrBM0O-sjgYTzzAiAoBjkNAtL90sXw2YP9UTW9JrMhPSvPiBI_KiCVMJAkFQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 244, bitrate: 467884, average_bitrate: 382209, size: Some(7788899), + last_modified: Some(1705966622098793), index_range: Some(Range( start: 220, end: 770, @@ -250,15 +267,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=24616305&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=136&keepalive=yes&lmt=1705967307531372&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAM57L2Utesn4xVyT0HSwR9Khv_S-efx4uFAbCPkZFoRXAiEAtIu63-jF2_FZkOMmZAqGU3SRU9QgxoajRjBhMFwcOuk%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=24616305&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=136&keepalive=yes&lmt=1705967307531372&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAM57L2Utesn4xVyT0HSwR9Khv_S-efx4uFAbCPkZFoRXAiEAtIu63-jF2_FZkOMmZAqGU3SRU9QgxoajRjBhMFwcOuk%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 136, bitrate: 1560439, average_bitrate: 1207947, size: Some(24616305), + last_modified: Some(1705967307531372), index_range: Some(Range( start: 739, end: 1166, @@ -276,15 +295,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d401f\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=14723992&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=247&keepalive=yes&lmt=1705966613897741&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAL-upITxk7r9FQL5F4WL0A6SjPw673qyyzmXIC48eKfTAiEAlkdkx7IFYtehbhKakbffvIebpPXRtxSgBWLl7WEHCrE%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=14723992&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=247&keepalive=yes&lmt=1705966613897741&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAL-upITxk7r9FQL5F4WL0A6SjPw673qyyzmXIC48eKfTAiEAlkdkx7IFYtehbhKakbffvIebpPXRtxSgBWLl7WEHCrE%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 247, bitrate: 929607, average_bitrate: 722521, size: Some(14723992), + last_modified: Some(1705966613897741), index_range: Some(Range( start: 220, end: 770, @@ -302,15 +323,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=34544823&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=298&keepalive=yes&lmt=1705967092637061&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAIIGU41JunuODw9qIlSoYQcwkCYO6k9XOVlDn1Nxqnu7AiEAoiMOgYU8s8lp01fW0L86hHrSrtlvOLSI9XA50iyIGBc%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=34544823&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=298&keepalive=yes&lmt=1705967092637061&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAIIGU41JunuODw9qIlSoYQcwkCYO6k9XOVlDn1Nxqnu7AiEAoiMOgYU8s8lp01fW0L86hHrSrtlvOLSI9XA50iyIGBc%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 298, bitrate: 2188961, average_bitrate: 1694973, size: Some(34544823), + last_modified: Some(1705967092637061), index_range: Some(Range( start: 739, end: 1166, @@ -328,15 +351,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.4d4020\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=30205331&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=302&keepalive=yes&lmt=1705966545733919&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAL428Az_BKxxff4FlH4WleHSy4Igq3mR71NuTMOc9xU3AiBN4lXfH9DklGaQUMnOT8wAhiMuzR73bW3cwr744TSoNA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=30205331&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=302&keepalive=yes&lmt=1705966545733919&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAL428Az_BKxxff4FlH4WleHSy4Igq3mR71NuTMOc9xU3AiBN4lXfH9DklGaQUMnOT8wAhiMuzR73bW3cwr744TSoNA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 302, bitrate: 2250391, average_bitrate: 1482051, size: Some(30205331), + last_modified: Some(1705966545733919), index_range: Some(Range( start: 219, end: 786, @@ -354,15 +379,17 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=62057888&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=299&keepalive=yes&lmt=1705967093743693&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgBEemc0Cvd3KhNooNRblgX64_fjNSP30RmWDfFwDR7qYCIQCXpQ9FO0_X93ZHcyvRZCKX5gbJuusCReaRcJbRLFsM_g%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=62057888&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=299&keepalive=yes&lmt=1705967093743693&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgBEemc0Cvd3KhNooNRblgX64_fjNSP30RmWDfFwDR7qYCIQCXpQ9FO0_X93ZHcyvRZCKX5gbJuusCReaRcJbRLFsM_g%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 299, bitrate: 3926810, average_bitrate: 3044926, size: Some(62057888), + last_modified: Some(1705967093743693), index_range: Some(Range( start: 740, end: 1167, @@ -380,15 +407,17 @@ VideoPlayer( mime: "video/mp4; codecs=\"avc1.64002a\"", format: mp4, codec: avc1, + xtags: None, drm_track_type: None, drm_systems: [], ), VideoStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=55300085&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=303&keepalive=yes&lmt=1705966651743358&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgTZlmOcsLYJ_a9SnVLehXnaoajtreQO97qawEIDPEi8sCIQDKFdtBWWMuQUb9X8H-x92B3q-y0g8TvAPanR95cfklXQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=55300085&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=303&keepalive=yes&lmt=1705966651743358&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgTZlmOcsLYJ_a9SnVLehXnaoajtreQO97qawEIDPEi8sCIQDKFdtBWWMuQUb9X8H-x92B3q-y0g8TvAPanR95cfklXQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 303, bitrate: 3473307, average_bitrate: 2713348, size: Some(55300085), + last_modified: Some(1705966651743358), index_range: Some(Range( start: 219, end: 792, @@ -406,17 +435,19 @@ VideoPlayer( mime: "video/webm; codecs=\"vp9\"", format: webm, codec: vp9, + xtags: None, drm_track_type: None, drm_systems: [], ), ], audio_streams: [ AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=934750&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=249&keepalive=yes&lmt=1714877357172339&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAItfaWkRs94vqyae7GR4M1xHoQO2lduvNRFugRSf0h-IAiA9fdLOJMwPI8vAO2C13igyv2qGSpOlKQptS4sN6p5Ffw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=934750&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=249&keepalive=yes&lmt=1714877357172339&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAItfaWkRs94vqyae7GR4M1xHoQO2lduvNRFugRSf0h-IAiA9fdLOJMwPI8vAO2C13igyv2qGSpOlKQptS4sN6p5Ffw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 249, bitrate: 53073, average_bitrate: 45860, size: 934750, + last_modified: Some(1714877357172339), index_range: Some(Range( start: 266, end: 551, @@ -432,15 +463,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.21), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=1245582&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=250&keepalive=yes&lmt=1714877466693058&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgdJ1SjWwaloQecEblSIMFp2qFmpG_kKYZP1vX_M55dE0CIQCDSfa_FsaiFRcNL-1LRTgCIRSO7dj5vrpKR1Ya-KbmMw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=1245582&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=250&keepalive=yes&lmt=1714877466693058&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgdJ1SjWwaloQecEblSIMFp2qFmpG_kKYZP1vX_M55dE0CIQCDSfa_FsaiFRcNL-1LRTgCIRSO7dj5vrpKR1Ya-KbmMw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 250, bitrate: 71197, average_bitrate: 61109, size: 1245582, + last_modified: Some(1714877466693058), index_range: Some(Range( start: 266, end: 551, @@ -456,15 +489,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.21), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2640283&dur=163.096&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=140&keepalive=yes&lmt=1705966477945761&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgSxdbLrbojMVJcyRzsI2TrzOf78LN28bWcsHpbs4QXDwCIHidfXoriWMHfuiktUCdzLuUmksU7r5vITdh6u0puNmx&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2640283&dur=163.096&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=140&keepalive=yes&lmt=1705966477945761&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgSxdbLrbojMVJcyRzsI2TrzOf78LN28bWcsHpbs4QXDwCIHidfXoriWMHfuiktUCdzLuUmksU7r5vITdh6u0puNmx&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 140, bitrate: 130268, average_bitrate: 129508, size: 2640283, + last_modified: Some(1705966477945761), index_range: Some(Range( start: 632, end: 867, @@ -480,15 +515,17 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.2200003), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), AudioStream( - url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2480393&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=251&keepalive=yes&lmt=1714877359450110&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgO0jG-x2l6AF7tjryIX_oM3np78WgNDiseezppLfbQrgCIQCVLdpDhclKc8vQgWGzKXcqsAxgNl5S3MlLT8u1Jeok2A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D", + url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2480393&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=251&keepalive=yes&lmt=1714877359450110&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgO0jG-x2l6AF7tjryIX_oM3np78WgNDiseezppLfbQrgCIQCVLdpDhclKc8vQgWGzKXcqsAxgNl5S3MlLT8u1Jeok2A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D"), itag: 251, bitrate: 140833, average_bitrate: 121691, size: 2480393, + last_modified: Some(1714877359450110), index_range: Some(Range( start: 266, end: 551, @@ -504,6 +541,7 @@ VideoPlayer( channels: Some(2), loudness_db: Some(5.21), track: None, + xtags: None, drm_track_type: None, drm_systems: [], ), @@ -520,6 +558,9 @@ VideoPlayer( valid_until: "[date]", hls_manifest_url: None, dash_manifest_url: None, + abr_streaming_url: Some("https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?c=TVHTML5&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&keepalive=yes&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=eBXmY26Y0c3VPyt&ns=Kl83P0QZk1oI9742KUD7ly8Q&pl=26&requiressl=yes&rqh=1&sabr=1&sig=AJfQdSswRAIgJRK55pIkQ3Pak9jZ4fHPDsxXv0YgkxKE-FFdIN12ph8CIFHlFEvAoUOoX4Fd1RmyCJqgLZhDkSLwD6s-xVW25kYL&smc=1&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Cxpc%2Csvpuc%2Cns%2Csabr%2Crqh&svpuc=1&xpc=EgVo2aDSNQ%3D%3D"), + abr_ustreamer_config: Some("CswJCpcHCAAlAACAPy1SuF4_NQAAwD9YAWABaAFyFgoSbWZzMl9jbWZzX3YzXzJfMTA5GAB4j06gAQGoAQCQAgG4AgDIAgHaAroBELDqARioRiCgnAEoiCcwmHVwiCeAAfQDuAEB4AEDkAIBmAIMoAIBwAIB0AIC4AIB6AIEgAMCiAOIJ5gDAagDA8ADAcgDAdADAfgDAYAEAYgEAZAEAZgEAaAEAagEAcgEAdAEAdgEAeAEAOgEAfgEB4AFfYgFAbAFAbgFAcAFAcgFAdAFAdgFAeAF0A_oBQH4BdAPgAYBuAYBwAYB0AYB2AYB6AYB8AYB-AYBkAcBqAcB2AcB-LWR5QwB-gKeAi0AAIJCNQAAlkJIAWUAAIBAaMBwqAHQhgOwAeADuAEBzQEAAIA_8AEB_QEAAIA_hQKamRk-jQIAAIA_lQIAAAJCmAIBtQIAAIA_wALgA9ICEbD__________wEePEZaXF1e2gIFMjA6MDDgAnjoAugC9QIK16M7_QLNzMw9gAMBkAMBnQMK1yM9oAMBuAMByAMB2AMB5QNiSkRA7QMyyvM-8AMB_QNmZoY_hQQAAIBAmAQB1QQAACBB6ATwEPAEAb0Fo0Afu8UF308tP8gFAeAFAZgGAaAGAagGAbUGvTeGNb0GMzODQJAHAcAHAcgHAdUHAICdQ-UHAIAJRKEIAAAAAAAA8L-pCAAAAAAAAPC_sAjwAbgIAdgI8AHoCAGCAwCQAwGoAwGwAwPQAwHYAwHgA5BOuAQBygQcChMIwKkHEJh1GOgHJQAAAAAoADAAEODUAxjQD9IEDQoICLAJELAJIAEgiCfaBAsKBgjwLhDwLiCIJ-gEAfgEAYAFAYgFAZAFAagFAbAFAdAFAdgFAegFAfAFAYgGAZgGAagGgIACwAYByAYB0gYUCOgHEGQaDQiIJxUAAAA_Hc3MTD-CBwoVAACAPxhkIJBOiAcBoAcBsAcBuAcBwAcB-AcBgAgBoAgBsAgBuAgB0ggGCAEQARgBmAkBqQkAAAAAAADwv7EJAAAAAAAA8L_ICQHaCSRFRzRmTDl1Sm9tL2NWdklmNjg4bnB6c2t4SVQrMXl0N09POHXgCQGwCgHYCgHwCgGICwGYCwG4CwHICwHQCwHYCwHqCwSLBowG8AsB-AsBkAwBoAwBqAyQAbAMAbgMAcAMAdAMAeAMAegMAYANAaANAdANAeANAYgOAZAOAbAOAYinocoLARgBIAEyDAirAhDNgPzUlvKDAzIMCK8CEP64moKV8oMDMgwIiAEQ7Mj0upfygwMyDAj3ARCNxJTwlPKDAzIMCKoCEIW7uNSW8oMDMgwIrgIQn5LUz5TygwMyDAiHARD5xP-ul_KDAzIMCPQBEOmKifSU8oMDMgwIhgEQk_6DsZfygwMyDAjzARCSyIT1lPKDAzIMCIUBEJWg47aX8oMDMgwI8gEQ3_PN8JTygwMyDAigARC-zf6xl_KDAzIMCJYCENPIofOU8oMDMgwIjAEQodeqr5TygwMyDAj5ARDzzNT9v_WFAzIMCPoBEMKb8bHA9YUDMgwI-wEQ_s_f_r_1hQM6AEgAUiYaAmVuKAAyGFVDYnh4RWktSW1QbGJMeDVGLWZIZXRFZzgAQABYAJDL048OARJNAKEYC4YwRgIhAIj4Ug4dw_gq15NXvgcfXpI1Fm_fhmwl-4ad-rX3Ffg_AiEAkZDsUgoAGLOXIvWZlNyuyfu8HLWt-snFl3gkTiPo2acaAmVp"), + po_token: None, preview_frames: [ Frameset( url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCsCT8Lprh2S0ptmCRsWH7VtDl3YQ", diff --git a/src/model/mod.rs b/src/model/mod.rs index 1829adc..b6f7ef8 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -147,6 +147,12 @@ pub struct VideoPlayer { pub hls_manifest_url: Option, /// Dash manifest URL (for livestreams) pub dash_manifest_url: Option, + /// ABR (adaptive bitrate) streaming URL + pub abr_streaming_url: Option, + /// ABR streaming config + pub abr_ustreamer_config: Option, + /// PO token for fetching ABR stream + pub po_token: Option, /// Video frames for seek preview pub preview_frames: Vec, /// Video player DRM config @@ -194,7 +200,7 @@ pub struct VideoPlayerDetails { #[non_exhaustive] pub struct VideoStream { /// Video stream URL - pub url: String, + pub url: Option, /// YouTube stream format identifier pub itag: u32, /// Stream bitrate (in bits/second) @@ -203,6 +209,8 @@ pub struct VideoStream { pub average_bitrate: u32, /// Video file size in bytes pub size: Option, + /// Last modified timestamp + pub last_modified: Option, /// Index range (used for DASH streaming) pub index_range: Option>, /// Init range (used for DASH streaming) @@ -225,6 +233,8 @@ pub struct VideoStream { pub format: VideoFormat, /// Video codec pub codec: VideoCodec, + /// Stream tags + pub xtags: Option, /// DRM track type /// /// [`None`] if the track is not DRM-protected @@ -238,7 +248,7 @@ pub struct VideoStream { #[non_exhaustive] pub struct AudioStream { /// Audio stream URL - pub url: String, + pub url: Option, /// YouTube stream format identifier pub itag: u32, /// Stream bitrate (in bits/second) @@ -247,6 +257,8 @@ pub struct AudioStream { pub average_bitrate: u32, /// Audio file size in bytes pub size: u64, + /// Last modified timestamp + pub last_modified: Option, /// Index range (used for DASH streaming) pub index_range: Option>, /// Init range (used for DASH streaming) @@ -276,6 +288,11 @@ pub struct AudioStream { /// /// The loudness parameter is not available when using the Android client. pub loudness_db: Option, + /// True if the audio stream uses dynamic range compression (Stable Volume option) + /// + /// + /// + pub is_drc: bool, /// Audio track information /// /// Videos can have multiple audio tracks (different languages). @@ -283,6 +300,8 @@ pub struct AudioStream { /// /// This is None if the video contains only 1 audio track. pub track: Option, + /// Stream tags (e.g. audio track language and type) + pub xtags: Option, /// DRM track type /// /// [`None`] if the track is not DRM-protected diff --git a/src/model/ordering.rs b/src/model/ordering.rs index 445dcad..8fe8718 100644 --- a/src/model/ordering.rs +++ b/src/model/ordering.rs @@ -36,6 +36,7 @@ impl QualityOrd for AudioStream { .as_ref() .map(|t| track_type_rating(t.track_type)), ) + .then_with(|| other.is_drc.cmp(&self.is_drc)) .then_with(|| self.channels.cmp(&other.channels)) .then_with(|| cmp_bitrate(self).cmp(&cmp_bitrate(other))) } diff --git a/src/model/traits.rs b/src/model/traits.rs index d4f6253..e621199 100644 --- a/src/model/traits.rs +++ b/src/model/traits.rs @@ -9,7 +9,7 @@ use super::*; /// Trait for YouTube streams (video and audio) pub trait YtStream { /// Stream URL - fn url(&self) -> &str; + fn url(&self) -> Option<&str>; /// YouTube stream format identifier fn itag(&self) -> u32; /// Stream bitrate (in bits/second) @@ -18,6 +18,8 @@ pub trait YtStream { fn averate_bitrate(&self) -> u32; /// File size in bytes fn size(&self) -> Option; + /// Last modified timestamp + fn last_modified(&self) -> Option; /// Index range (used for DASH streaming) fn index_range(&self) -> Option>; /// Init range (used for DASH streaming) @@ -26,11 +28,13 @@ pub trait YtStream { fn duration_ms(&self) -> Option; /// MIME file type fn mime(&self) -> &str; + /// Stream tags (e.g. audio track language and type) + fn xtags(&self) -> Option<&str>; } impl YtStream for VideoStream { - fn url(&self) -> &str { - &self.url + fn url(&self) -> Option<&str> { + self.url.as_deref() } fn itag(&self) -> u32 { @@ -49,6 +53,10 @@ impl YtStream for VideoStream { self.size } + fn last_modified(&self) -> Option { + self.last_modified + } + fn index_range(&self) -> Option> { self.index_range.clone() } @@ -64,11 +72,15 @@ impl YtStream for VideoStream { fn mime(&self) -> &str { &self.mime } + + fn xtags(&self) -> Option<&str> { + self.xtags.as_deref() + } } impl YtStream for AudioStream { - fn url(&self) -> &str { - &self.url + fn url(&self) -> Option<&str> { + self.url.as_deref() } fn itag(&self) -> u32 { @@ -87,6 +99,10 @@ impl YtStream for AudioStream { Some(self.size) } + fn last_modified(&self) -> Option { + self.last_modified + } + fn index_range(&self) -> Option> { self.index_range.clone() } @@ -102,6 +118,10 @@ impl YtStream for AudioStream { fn mime(&self) -> &str { &self.mime } + + fn xtags(&self) -> Option<&str> { + self.xtags.as_deref() + } } /// Trait for file types diff --git a/src/param/stream_filter.rs b/src/param/stream_filter.rs index b690bb0..5143b1f 100644 --- a/src/param/stream_filter.rs +++ b/src/param/stream_filter.rs @@ -17,17 +17,19 @@ pub struct StreamFilter { audio_languages: Vec, audio_autodub: bool, audio_descriptive: bool, + audio_drc: bool, video_max_res: Option, video_max_fps: Option, video_formats: Option>, video_codecs: Option>, video_hdr: bool, video_none: bool, + abr_only: bool, drm_track_types: Vec, drm_system: Option, } -const N_RES_AUDIO: usize = 4; +const N_RES_AUDIO: usize = 5; const N_RES_VIDEO: usize = 5; type AudioRes = Option<[i64; N_RES_AUDIO]>; type VideoRes = Option<[i64; N_RES_VIDEO]>; @@ -109,6 +111,16 @@ impl StreamFilter { self } + /// Prefer audio streams that use dynamic range compression (Stable volume). + /// + /// + /// + #[must_use] + pub fn audio_drc(mut self) -> Self { + self.audio_drc = true; + self + } + /// Set the maximum video resolution. Resolution is determined by the /// pixel count of the shorter edge (e.g. 1080p). /// @@ -176,6 +188,13 @@ impl StreamFilter { self } + /// Allow ABR-only streams without URL + #[must_use] + pub fn allow_abr_only(mut self) -> Self { + self.abr_only = true; + self + } + fn check_drm(&self, track_type: Option, drm_systems: &[DrmSystem]) -> Option<()> { if let Some(track_type) = track_type { if !self.drm_track_types.contains(&track_type) { @@ -189,6 +208,10 @@ impl StreamFilter { } fn apply_audio(&self, stream: &AudioStream) -> AudioRes { + if stream.url.is_none() && !self.abr_only { + return None; + } + let bitrate = match self.audio_max_bitrate { Some(max) => { if stream.average_bitrate > max { @@ -259,6 +282,8 @@ impl StreamFilter { None => 0, }; + let drc = i64::from(self.audio_drc == stream.is_drc); + let channels = stream.channels.unwrap_or_default(); if let Some(max_channels) = self.audio_max_channels { if channels > max_channels { @@ -268,10 +293,14 @@ impl StreamFilter { self.check_drm(stream.drm_track_type, &stream.drm_systems)?; - Some([language, track_type, channels.into(), bitrate]) + Some([language, track_type, drc, channels.into(), bitrate]) } - fn apply_video(&self, stream: &VideoStream) -> VideoRes { + fn apply_video(&self, stream: &VideoStream, video_only: bool) -> VideoRes { + if stream.url.is_none() && !(self.abr_only && video_only) { + return None; + } + let vres = stream.height.min(stream.width); let res = match self.video_max_res { Some(max) => filter_max(vres, max), @@ -347,6 +376,7 @@ impl VideoPlayer { fn _select_video_stream<'a>( streams: &'a [VideoStream], filter: &StreamFilter, + video_only: bool, ) -> Option<&'a VideoStream> { if filter.video_none { return None; @@ -354,19 +384,19 @@ impl VideoPlayer { streams .iter() - .filter_map(|s| filter.apply_video(s).map(|r| (s, r))) + .filter_map(|s| filter.apply_video(s, video_only).map(|r| (s, r))) .max_by_key(|(_, r)| *r) .map(|(s, _)| s) } /// Select the video stream which is the best match for the given [`StreamFilter`] pub fn select_video_stream(&self, filter: &StreamFilter) -> Option<&VideoStream> { - Self::_select_video_stream(&self.video_streams, filter) + Self::_select_video_stream(&self.video_streams, filter, false) } /// Select the video-only stream which is the best match for the given [`StreamFilter`] pub fn select_video_only_stream(&self, filter: &StreamFilter) -> Option<&VideoStream> { - Self::_select_video_stream(&self.video_only_streams, filter) + Self::_select_video_stream(&self.video_only_streams, filter, true) } /// Select a video and audio stream which is the best match for the given [`StreamFilter`] @@ -466,7 +496,7 @@ mod tests { let selection = player.select_audio_stream(&filter); match expect_url { - Some(expect_url) => assert_eq!(selection.unwrap().url, expect_url), + Some(expect_url) => assert_eq!(selection.unwrap().url.as_deref().unwrap(), expect_url), None => assert_eq!(selection, None), } } @@ -491,7 +521,7 @@ mod tests { let selection = player.select_video_only_stream(&filter); match expect_url { - Some(expect_url) => assert_eq!(selection.unwrap().url, expect_url), + Some(expect_url) => assert_eq!(selection.unwrap().url.as_deref().unwrap(), expect_url), None => assert_eq!(selection, None), } } @@ -526,12 +556,12 @@ mod tests { let (video, audio) = PLAYER_HDR.select_video_audio_stream(&filter); match expect_video_url { - Some(expect_url) => assert_eq!(video.unwrap().url, expect_url), + Some(expect_url) => assert_eq!(video.unwrap().url.as_deref().unwrap(), expect_url), None => assert_eq!(video, None), } match expect_audio_url { - Some(expect_url) => assert_eq!(audio.unwrap().url, expect_url), + Some(expect_url) => assert_eq!(audio.unwrap().url.as_deref().unwrap(), expect_url), None => assert_eq!(audio, None), } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 368d114..9e54eec 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -6,7 +6,7 @@ pub mod dictionary; pub mod timeago; pub use date::{now_sec, shift_months, shift_weeks_monday, shift_years}; -pub use protobuf::{string_from_pb, ProtoBuilder}; +pub use protobuf::{kv_from_pb, string_from_pb, ProtoBuilder}; pub use visitor_data::VisitorDataCache; use std::{ @@ -486,7 +486,17 @@ pub fn b64_encode>(input: T) -> String { } pub fn b64_decode>(input: T) -> Result, data_encoding::DecodeError> { - data_encoding::BASE64URL.decode(input.as_ref()) + // Remove trailing padding + let mut x = input.as_ref(); + while !x.is_empty() { + let li = x.len() - 1; + if x[li] == b'=' { + x = &x[0..li]; + } else { + break; + } + } + data_encoding::BASE64URL_NOPAD.decode(x) } /// Get the country from its English name diff --git a/src/util/protobuf.rs b/src/util/protobuf.rs index 84492f6..b56dae9 100644 --- a/src/util/protobuf.rs +++ b/src/util/protobuf.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + /// [`ProtoBuilder`] is used to construct protobuf messages using a builder pattern #[derive(Debug, Default)] pub struct ProtoBuilder { @@ -100,7 +102,7 @@ fn parse_varint>(pb: &mut P) -> Option { } } -fn parse_field>(pb: &mut P) -> Option<(u32, u8)> { +fn parse_field(pb: &mut impl Iterator) -> Option<(u32, u8)> { parse_varint(pb).map(|v| { let f = (v >> 3) as u32; let w = (v & 0x07) as u8; @@ -108,13 +110,12 @@ fn parse_field>(pb: &mut P) -> Option<(u32, u8)> { }) } -pub fn string_from_pb>(pb: P, field: u32) -> Option { - let mut pb = pb.into_iter(); - while let Some((this_field, wire)) = parse_field(&mut pb) { +fn next_string_from_pb(pb: &mut impl Iterator, filter: &[u32]) -> Option<(u32, String)> { + while let Some((this_field, wire)) = parse_field(pb) { let to_skip = match wire { // varint 0 => { - parse_varint(&mut pb); + parse_varint(pb); 0 } // fixed 64bit @@ -123,15 +124,16 @@ pub fn string_from_pb>(pb: P, field: u32) -> Option 4, // string 2 => { - let len = parse_varint(&mut pb)?; - if this_field == field { + let len = parse_varint(pb)?; + if filter.contains(&this_field) { let mut buf = Vec::new(); for _ in 0..len { buf.push(pb.next()?); } - return String::from_utf8(buf).ok(); + return Some((this_field, String::from_utf8(buf).ok()?)); + } else { + len } - len } _ => return None, }; @@ -142,6 +144,64 @@ pub fn string_from_pb>(pb: P, field: u32) -> Option>(pb: P, field: u32) -> Option { + let mut pb = pb.into_iter(); + next_string_from_pb(&mut pb, &[field]).map(|x| x.1) +} + +/// Parse a protobuf-encoded key/value map +/// +/// K/V pairs are embedded structs of field 1 +/// K/V struct: {key = 1; value = 2} +pub fn kv_from_pb>(pb: P) -> Option> { + let mut res = HashMap::new(); + let pb = &mut (pb.into_iter()); + while let Some((this_field, wire)) = parse_field(pb) { + let to_skip = match wire { + // varint + 0 => { + parse_varint(pb); + 0 + } + // fixed 64bit + 1 => 8, + // fixed 32bit + 5 => 4, + // embed + 2 => { + let len = parse_varint(pb)?; + if this_field == 1 { + let mut tsh = pb.take(len as usize); + let (f1, v1) = next_string_from_pb(&mut tsh, &[1, 2])?; + let (f2, v2) = next_string_from_pb(&mut tsh, &[1, 2])?; + let rem = tsh.size_hint().1.unwrap_or_default(); + if rem > 0 { + tsh.nth(rem - 1); + } + + let (k, v) = if f1 == 1 && f2 == 2 { + (v1, v2) + } else if f2 == 1 && f1 == 2 { + (v2, v1) + } else { + return None; + }; + res.insert(k, v); + 0 + } else { + len + } + } + _ => return None, + }; + for _ in 0..to_skip { + pb.next(); + } + } + Some(res) +} + #[cfg(test)] mod tests { use crate::util; @@ -170,4 +230,15 @@ mod tests { let res = string_from_pb(p_bytes, 3).unwrap(); assert_eq!(res, "UC9vrvNSL3xcWGSkV86REBSg"); } + + #[test] + fn parse_proto_kv() { + let p = "ChEKBWFjb250EghvcmlnaW5hbAoKCgRsYW5nEgJlbg"; + let p_bytes = util::b64_decode(urlencoding::decode(p).unwrap().as_bytes()).unwrap(); + + let res = kv_from_pb(p_bytes).unwrap(); + assert_eq!(res.len(), 2); + assert_eq!(res["acont"], "original"); + assert_eq!(res["lang"], "en"); + } } diff --git a/tests/youtube.rs b/tests/youtube.rs index fb7f860..423cc65 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -149,7 +149,7 @@ async fn check_video_stream(s: impl YtStream) { let http = reqwest::Client::new(); let resp = http - .get(s.url()) + .get(s.url().expect("no url")) .send() .await .unwrap()