feat: add frameset
This commit is contained in:
parent
e184341625
commit
aa5cd47dcd
12 changed files with 339 additions and 10 deletions
|
|
@ -12,8 +12,8 @@ use crate::{
|
|||
deobfuscate::Deobfuscator,
|
||||
error::{internal::DeobfError, Error, ExtractionError, UnavailabilityReason},
|
||||
model::{
|
||||
traits::QualityOrd, AudioCodec, AudioFormat, AudioStream, AudioTrack, ChannelId, Subtitle,
|
||||
VideoCodec, VideoFormat, VideoPlayer, VideoPlayerDetails, VideoStream,
|
||||
traits::QualityOrd, AudioCodec, AudioFormat, AudioStream, AudioTrack, ChannelId, Frameset,
|
||||
Subtitle, VideoCodec, VideoFormat, VideoPlayer, VideoPlayerDetails, VideoStream,
|
||||
},
|
||||
param::Language,
|
||||
util,
|
||||
|
|
@ -313,6 +313,54 @@ impl MapResponse<VideoPlayer> for response::Player {
|
|||
.collect()
|
||||
});
|
||||
|
||||
let preview_frames = self
|
||||
.storyboards
|
||||
.and_then(|sb| {
|
||||
let spec = sb.player_storyboard_spec_renderer.spec;
|
||||
let mut spec_parts = spec.split('|');
|
||||
let url_tmpl = spec_parts.next()?;
|
||||
|
||||
Some(
|
||||
spec_parts
|
||||
.enumerate()
|
||||
.filter_map(|(i, fs_spec)| {
|
||||
// Example: 160#90#131#5#5#2000#M$M#rs$AOn4CLCV3TJ2Nty5fbw2r-Lqg4VDOZcVvQ
|
||||
let mut parts = fs_spec.split('#');
|
||||
|
||||
let frame_width = parts.next()?.parse().ok()?;
|
||||
let frame_height = parts.next()?.parse().ok()?;
|
||||
let total_count = parts.next()?.parse().ok()?;
|
||||
let frames_per_page_x = parts.next()?.parse().ok()?;
|
||||
let frames_per_page_y = parts.next()?.parse().ok()?;
|
||||
let duration_per_frame = parts.next()?.parse().ok()?;
|
||||
|
||||
let n = parts.next()?;
|
||||
let sigh = parts.next()?;
|
||||
|
||||
let url = url_tmpl.replace("$L", &i.to_string()).replace("$N", n)
|
||||
+ "&sigh="
|
||||
+ sigh;
|
||||
|
||||
let sprite_count = ((total_count as f64)
|
||||
/ (frames_per_page_x * frames_per_page_y) as f64)
|
||||
.ceil() as u32;
|
||||
|
||||
Some(Frameset {
|
||||
url_template: url,
|
||||
frame_width,
|
||||
frame_height,
|
||||
page_count: sprite_count,
|
||||
total_count,
|
||||
duration_per_frame,
|
||||
frames_per_page_x,
|
||||
frames_per_page_y,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(MapResult {
|
||||
c: VideoPlayer {
|
||||
details: video_info,
|
||||
|
|
@ -323,6 +371,7 @@ impl MapResponse<VideoPlayer> for response::Player {
|
|||
expires_in_seconds: streaming_data.expires_in_seconds,
|
||||
hls_manifest_url: streaming_data.hls_manifest_url,
|
||||
dash_manifest_url: streaming_data.dash_manifest_url,
|
||||
preview_frames,
|
||||
visitor_data: self.response_context.visitor_data,
|
||||
},
|
||||
warnings,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use serde_with::{json::JsonString, DefaultOnError};
|
|||
use super::{ResponseContext, Thumbnails};
|
||||
use crate::serializer::{text::Text, MapResult};
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Player {
|
||||
|
|
@ -14,6 +15,9 @@ pub(crate) struct Player {
|
|||
pub streaming_data: Option<StreamingData>,
|
||||
pub captions: Option<Captions>,
|
||||
pub video_details: Option<VideoDetails>,
|
||||
#[serde(default)]
|
||||
#[serde_as(deserialize_as = "DefaultOnError")]
|
||||
pub storyboards: Option<Storyboards>,
|
||||
pub response_context: ResponseContext,
|
||||
}
|
||||
|
||||
|
|
@ -246,3 +250,15 @@ pub(crate) struct VideoDetails {
|
|||
pub author: String,
|
||||
pub is_live_content: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Storyboards {
|
||||
pub player_storyboard_spec_renderer: StoryboardRenderer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct StoryboardRenderer {
|
||||
pub spec: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,5 +450,37 @@ VideoPlayer(
|
|||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659481355/ei/q1jpYtOPEYSBgQeHmqbwAQ/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr5---sn-h0jeenek.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jeenek%2Csn-h0jelnez/ms/au%2Crdu/mv/m/mvi/5/pl/37/hfr/1/as/fmp4_audio_clear%2Cfmp4_sd_hd_clear/initcwndbps/1527500/vprv/1/mt/1659459429/fvip/4/itag_bl/376%2C377%2C384%2C385%2C612%2C613%2C617%2C619%2C623%2C628%2C655%2C656%2C660%2C662%2C666%2C671/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cvprv%2Citag/sig/AOq0QJ8wRAIgMm4a_MIHA3YUszKeruSy3exs5JwNjJAyLAwxL0yPdNMCIANb9GDMSTp_NT-PPhbvYMwRULJ5a9BO6MYD9FuWprC1/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgETSOwhwWVMy7gmrFXZlJu655ToLzSwOEsT16oRyrWhACIQDkvOEw1fImz5omu4iVIRNFe-z-JC9v8WUyx281dW2NOw%3D%3D"),
|
||||
preview_frames: [
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLAXobPyrylgm8IEvjlZzqYTiPe1Ow",
|
||||
frame_width: 48,
|
||||
frame_height: 27,
|
||||
page_count: 1,
|
||||
total_count: 100,
|
||||
duration_per_frame: 0,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L1/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCWd3ylPF7ViQFBu5RUODMcusr_5g",
|
||||
frame_width: 80,
|
||||
frame_height: 45,
|
||||
page_count: 1,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L2/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLA6xat5cfw0e3EX_5SW-TPwkmExxA",
|
||||
frame_width: 160,
|
||||
frame_height: 90,
|
||||
page_count: 4,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 5,
|
||||
frames_per_page_y: 5,
|
||||
),
|
||||
],
|
||||
visitor_data: Some("Cgt2aHFtQU5YZFBvYyirsaWXBg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -569,5 +569,37 @@ VideoPlayer(
|
|||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659481355/ei/q1jpYtq3BJCX1gKVyJGQDg/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr4---sn-h0jelnez.googlevideo.com/mh/mQ/mm/31%2C26/mn/sn-h0jelnez%2Csn-4g5edn6k/ms/au%2Conr/mv/m/mvi/4/pl/37/hfr/all/as/fmp4_audio_clear%2Cwebm_audio_clear%2Cwebm2_audio_clear%2Cfmp4_sd_hd_clear%2Cwebm2_sd_hd_clear/initcwndbps/1513750/spc/lT-KhrZGE2opztWyVdAtyUNlb8dXPDs/vprv/1/mt/1659459429/fvip/4/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cspc%2Cvprv%2Citag/sig/AOq0QJ8wRgIhAPEjHK19PKVHqQeia6WF4qubuMYk74LGi8F8lk5ZMPkFAiEAsaB2pKQWBvuPnNUnbdQXHc-izgsHJUP793woC2xNJlg%3D/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgOY4xu4H9wqPVZ7vF2i0hFcOnqrur1XGoA43a7ZEuuSUCIQCyPxBKXUQrKFmknNEGpX5GSWySKgMw_xHBikWpKpKwvg%3D%3D"),
|
||||
preview_frames: [
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLAXobPyrylgm8IEvjlZzqYTiPe1Ow",
|
||||
frame_width: 48,
|
||||
frame_height: 27,
|
||||
page_count: 1,
|
||||
total_count: 100,
|
||||
duration_per_frame: 0,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L1/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCWd3ylPF7ViQFBu5RUODMcusr_5g",
|
||||
frame_width: 80,
|
||||
frame_height: 45,
|
||||
page_count: 1,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L2/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLA6xat5cfw0e3EX_5SW-TPwkmExxA",
|
||||
frame_width: 160,
|
||||
frame_height: 90,
|
||||
page_count: 4,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 5,
|
||||
frames_per_page_y: 5,
|
||||
),
|
||||
],
|
||||
visitor_data: Some("CgtoS1pCMVJTNUJISSirsaWXBg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -387,5 +387,37 @@ VideoPlayer(
|
|||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659487474/ei/knDpYub6BojEgAf6jbLgDw/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr5---sn-h0jeenek.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jeenek%2Csn-h0jelnez/ms/au%2Crdu/mv/m/mvi/5/pl/37/hfr/all/as/fmp4_audio_clear%2Cwebm_audio_clear%2Cwebm2_audio_clear%2Cfmp4_sd_hd_clear%2Cwebm2_sd_hd_clear/initcwndbps/1418750/spc/lT-Khox4YuJQ2wmH79zYALRvsWTPCUc/vprv/1/mt/1659465669/fvip/4/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cspc%2Cvprv%2Citag/sig/AOq0QJ8wRAIgErABhAEaoKHUDu9dDbpxE_8gR4b8WWAi61fnu8UKnuICIEYrEKcHvqHdO4V3R7cvSGwi_HGH34IlQsKbziOfMBov/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgJxHmH0Sxo3cY_pW_ZzQ3hW9-7oz6K_pZWcUdrDDQ2sQCIQDJYNINQwLgKelgbO3CZYx7sMxdUAFpWdokmRBQ77vwvw%3D%3D"),
|
||||
preview_frames: [
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLAXobPyrylgm8IEvjlZzqYTiPe1Ow",
|
||||
frame_width: 48,
|
||||
frame_height: 27,
|
||||
page_count: 1,
|
||||
total_count: 100,
|
||||
duration_per_frame: 0,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L1/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCWd3ylPF7ViQFBu5RUODMcusr_5g",
|
||||
frame_width: 80,
|
||||
frame_height: 45,
|
||||
page_count: 1,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L2/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLA6xat5cfw0e3EX_5SW-TPwkmExxA",
|
||||
frame_width: 160,
|
||||
frame_height: 90,
|
||||
page_count: 4,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 5,
|
||||
frames_per_page_y: 5,
|
||||
),
|
||||
],
|
||||
visitor_data: Some("CgszSHZWNWs0SDhpTSiS4aWXBg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -168,5 +168,37 @@ VideoPlayer(
|
|||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1659481355/ei/q1jpYq-xHs7NgQev0bfwAQ/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr4---sn-h0jelnez.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jelnez%2Csn-h0jeenek/ms/au%2Crdu/mv/m/mvi/4/pl/37/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/1513750/vprv/1/go/1/mt/1659459429/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/24001373%2C24007246/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cvprv%2Cgo%2Citag%2Cplaylist_type/sig/AOq0QJ8wRQIhAIYnEHvIgJtJ8hehAXNtVY3qsgsq_GdOhWf2hkJZe6lCAiBxaRY_nubYp6hBizcAg_KFkKnkG-t2XYLRQ5wGdM3AjA%3D%3D/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRgIhAM_91Kk_0VLuSsR6nLCY7LdtWojyRAzXSScd_X9ShRROAiEA1AF4VY04F71NsAI8_j3iqjuXnWL9s6NoXHq7P8-bHx8%3D/file/index.m3u8"),
|
||||
dash_manifest_url: None,
|
||||
preview_frames: [
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCsCT8Lprh2S0ptmCRsWH7VtDl3YQ",
|
||||
frame_width: 48,
|
||||
frame_height: 27,
|
||||
page_count: 1,
|
||||
total_count: 100,
|
||||
duration_per_frame: 0,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L1/M$M.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLBXrdgfuYV1WLnTGXqZtSAUm8oZCA",
|
||||
frame_width: 80,
|
||||
frame_height: 45,
|
||||
page_count: 1,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L2/M$M.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCRazj84zMuwJLaCCc_PiUakX_YdQ",
|
||||
frame_width: 160,
|
||||
frame_height: 90,
|
||||
page_count: 4,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 5,
|
||||
frames_per_page_y: 5,
|
||||
),
|
||||
],
|
||||
visitor_data: Some("Cgs4TXV4dk13WVEyWSirsaWXBg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -569,5 +569,37 @@ VideoPlayer(
|
|||
expires_in_seconds: 21540,
|
||||
hls_manifest_url: None,
|
||||
dash_manifest_url: Some("https://manifest.googlevideo.com/api/manifest/dash/expire/1659481355/ei/q1jpYv-eJ9uF6dsPhvyH8As/ip/2003%3Ade%3Aaf0e%3A2f00%3Ade47%3A297%3Aa6db%3A774e/id/a4fbddf14c6649b4/source/youtube/requiressl/yes/playback_host/rr4---sn-h0jelnez.googlevideo.com/mh/mQ/mm/31%2C29/mn/sn-h0jelnez%2Csn-h0jeenek/ms/au%2Crdu/mv/m/mvi/4/pl/37/hfr/all/as/fmp4_audio_clear%2Cfmp4_sd_hd_clear/initcwndbps/1527500/vprv/1/mt/1659459429/fvip/5/keepalive/yes/fexp/24001373%2C24007246/itag/0/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Chfr%2Cas%2Cvprv%2Citag/sig/AOq0QJ8wRQIhANKWS7GCN4pSoHIQ6BMZdOaHAD0I25nHwRj7ds4qrxdEAiBsd9l8WIceqF7-2xyR82DGecCiS9hgUIPJhdNhkwVpHg%3D%3D/lsparams/playback_host%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps/lsig/AG3C_xAwRQIgMbu-wTOcXGCwGh27y0YZHktumKM1sopgxfQf8LCcCnECIQDnhFbgddOxwiQbnMOIcCn6ncpN54UyALRNigUSCp9Deg%3D%3D"),
|
||||
preview_frames: [
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCsCT8Lprh2S0ptmCRsWH7VtDl3YQ",
|
||||
frame_width: 48,
|
||||
frame_height: 27,
|
||||
page_count: 1,
|
||||
total_count: 100,
|
||||
duration_per_frame: 0,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L1/M$M.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLBXrdgfuYV1WLnTGXqZtSAUm8oZCA",
|
||||
frame_width: 80,
|
||||
frame_height: 45,
|
||||
page_count: 1,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 10,
|
||||
frames_per_page_y: 10,
|
||||
),
|
||||
Frameset(
|
||||
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L2/M$M.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCRazj84zMuwJLaCCc_PiUakX_YdQ",
|
||||
frame_width: 160,
|
||||
frame_height: 90,
|
||||
page_count: 4,
|
||||
total_count: 83,
|
||||
duration_per_frame: 2000,
|
||||
frames_per_page_x: 5,
|
||||
frames_per_page_y: 5,
|
||||
),
|
||||
],
|
||||
visitor_data: Some("CgtacUJOMG81dTI3cyirsaWXBg%3D%3D"),
|
||||
)
|
||||
|
|
|
|||
61
src/model/frameset.rs
Normal file
61
src/model/frameset.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Set of video frames for seek preview
|
||||
///
|
||||
/// YouTube generates a set of images containing a grid of frames for each video.
|
||||
/// These images are used by the player for the seekbar preview.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct Frameset {
|
||||
/// Url template of the frameset
|
||||
///
|
||||
/// The `$M` placeholder has to be replaced with the page index (starting from 0).
|
||||
pub url_template: String,
|
||||
/// Width of a single frame in pixels
|
||||
pub frame_width: u32,
|
||||
/// Height of a single frame in pixels
|
||||
pub frame_height: u32,
|
||||
/// Number of pages (individual images)
|
||||
pub page_count: u32,
|
||||
/// Total number of frames in the set
|
||||
pub total_count: u32,
|
||||
/// Duration per frame in milliseconds
|
||||
pub duration_per_frame: u32,
|
||||
/// Number of frames in the x direction
|
||||
pub frames_per_page_x: u32,
|
||||
/// Number of frames in the y direction.
|
||||
pub frames_per_page_y: u32,
|
||||
}
|
||||
|
||||
/// Iterator producing frameset page urls
|
||||
pub struct FramesetUrls<'a> {
|
||||
frameset: &'a Frameset,
|
||||
i: u32,
|
||||
}
|
||||
|
||||
impl Frameset {
|
||||
/// Gets an iterator over the page URLs of the frameset
|
||||
pub fn urls(&self) -> FramesetUrls {
|
||||
FramesetUrls {
|
||||
frameset: self,
|
||||
i: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FramesetUrls<'_> {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.i < self.frameset.page_count {
|
||||
let url = self
|
||||
.frameset
|
||||
.url_template
|
||||
.replace("$M", &self.i.to_string());
|
||||
self.i += 1;
|
||||
Some(url)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
//! YouTube API response models
|
||||
|
||||
mod convert;
|
||||
mod frameset;
|
||||
mod ordering;
|
||||
|
||||
pub mod paginator;
|
||||
pub mod richtext;
|
||||
|
||||
pub mod traits;
|
||||
|
||||
use serde_with::serde_as;
|
||||
pub use frameset::{Frameset, FramesetUrls};
|
||||
|
||||
use std::{collections::BTreeSet, ops::Range};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
use crate::{error::Error, param::Country, serializer::DateYmd, util};
|
||||
|
||||
use self::{paginator::Paginator, richtext::RichText};
|
||||
use crate::{error::Error, param::Country, serializer::DateYmd, util};
|
||||
|
||||
/*
|
||||
#COMMON
|
||||
|
|
@ -155,6 +154,8 @@ pub struct VideoPlayer {
|
|||
pub hls_manifest_url: Option<String>,
|
||||
/// Dash manifest URL (for livestreams)
|
||||
pub dash_manifest_url: Option<String>,
|
||||
/// Video frames for seek preview
|
||||
pub preview_frames: Vec<Frameset>,
|
||||
/// YouTube visitor data cookie
|
||||
pub visitor_data: Option<String>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1124,5 +1124,6 @@
|
|||
"subtitles": [],
|
||||
"expires_in_seconds": 21540,
|
||||
"hls_manifest_url": null,
|
||||
"dash_manifest_url": null
|
||||
"dash_manifest_url": null,
|
||||
"preview_frames": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2119,5 +2119,6 @@
|
|||
"expires_in_seconds": 21540,
|
||||
"hls_manifest_url": null,
|
||||
"dash_manifest_url": null,
|
||||
"preview_frames": [],
|
||||
"visitor_data": "CgtGWDFCUllrcTdxayjo1_OiBg%3D%3D"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use reqwest::Client;
|
||||
use rstest::{fixture, rstest};
|
||||
use rustypipe::model::paginator::ContinuationEndpoint;
|
||||
use rustypipe::param::{ChannelOrder, ChannelVideoTab, Language};
|
||||
|
|
@ -15,8 +16,8 @@ use rustypipe::model::{
|
|||
paginator::Paginator,
|
||||
richtext::ToPlaintext,
|
||||
traits::{FromYtItem, YtStream},
|
||||
AlbumType, AudioCodec, AudioFormat, AudioTrackType, Channel, MusicGenre, MusicItemType,
|
||||
UrlTarget, Verification, VideoCodec, VideoFormat, YouTubeItem,
|
||||
AlbumType, AudioCodec, AudioFormat, AudioTrackType, Channel, Frameset, MusicGenre,
|
||||
MusicItemType, UrlTarget, Verification, VideoCodec, VideoFormat, YouTubeItem,
|
||||
};
|
||||
use rustypipe::param::{
|
||||
search_filter::{self, SearchFilter},
|
||||
|
|
@ -287,6 +288,11 @@ fn get_player(
|
|||
};
|
||||
|
||||
assert_gte(player_data.expires_in_seconds, 10_000, "expiry time");
|
||||
|
||||
if !is_live {
|
||||
assert_gte(player_data.preview_frames.len(), 3, "preview framesets");
|
||||
player_data.preview_frames.iter().for_each(assert_frameset);
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
|
@ -2375,3 +2381,37 @@ fn assert_album_id(id: &str) {
|
|||
fn assert_playlist_id(id: &str) {
|
||||
assert!(validate::playlist_id(id), "invalid playlist id: `{id}`");
|
||||
}
|
||||
|
||||
// fn assert_image(client: &Client, url: &str) {
|
||||
// let resp = tokio_test::block_on(client.get(url).send())
|
||||
// .unwrap()
|
||||
// .error_for_status()
|
||||
// .unwrap();
|
||||
// let ctype = resp
|
||||
// .headers()
|
||||
// .get(reqwest::header::CONTENT_TYPE)
|
||||
// .unwrap()
|
||||
// .to_str()
|
||||
// .unwrap();
|
||||
|
||||
// assert!(ctype.starts_with("image/"), "content type: {ctype}");
|
||||
// }
|
||||
|
||||
fn assert_frameset(frameset: &Frameset) {
|
||||
assert_gte(frameset.frame_height, 20, "frame height");
|
||||
assert_gte(frameset.frame_height, 20, "frame width");
|
||||
assert_gte(frameset.page_count, 1, "page count");
|
||||
assert_gte(frameset.total_count, 50, "total count");
|
||||
assert_gte(frameset.frames_per_page_x, 5, "frames per page x");
|
||||
assert_gte(frameset.frames_per_page_y, 5, "frames per page y");
|
||||
|
||||
// let client = Client::new();
|
||||
|
||||
let n = frameset
|
||||
.urls()
|
||||
// .map(|url| {
|
||||
// assert_image(&client, &url);
|
||||
// })
|
||||
.count() as u32;
|
||||
assert_eq!(n, frameset.page_count);
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue