Compare commits
No commits in common. "449fc0128e04c55637e5fd2258913f80ca5660e9" and "79c504954ef856332d7e7ff05e712818f010588d" have entirely different histories.
449fc0128e
...
79c504954e
15 changed files with 138 additions and 18242 deletions
|
@ -1,8 +1,5 @@
|
||||||
name: CI
|
name: CI
|
||||||
on:
|
on: [push, pull_request]
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Test:
|
Test:
|
||||||
|
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -2,16 +2,6 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [v0.1.3](https://code.thetadev.de/ThetaDev/rustypipe/compare/rustypipe/v0.1.2..v0.1.3) - 2024-04-01
|
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
|
||||||
|
|
||||||
- Parse new comment model (A/B#14 frameworkUpdates) - ([b0331f7](https://code.thetadev.de/ThetaDev/rustypipe/commit/b0331f7250f5d7d61a45209150739d2cb08b4280))
|
|
||||||
|
|
||||||
### ◀️ Revert
|
|
||||||
|
|
||||||
- "fix: improve VecLogErr messages" (leads to infinite loop) - ([348c852](https://code.thetadev.de/ThetaDev/rustypipe/commit/348c8523fe847f2f6ce98317375a7ab65e778ed2))
|
|
||||||
|
|
||||||
## [v0.1.2](https://code.thetadev.de/ThetaDev/rustypipe/compare/rustypipe/v0.1.1..v0.1.2) - 2024-03-26
|
## [v0.1.2](https://code.thetadev.de/ThetaDev/rustypipe/compare/rustypipe/v0.1.1..v0.1.2) - 2024-03-26
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "rustypipe"
|
name = "rustypipe"
|
||||||
version = "0.1.3"
|
version = "0.1.2"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
|
@ -36,7 +36,7 @@ Client for the public YouTube / YouTube Music API (Innertube), inspired by
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustypipe = "0.1.3"
|
rustypipe = "0.1.2"
|
||||||
tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,6 @@ use rustypipe::param::ChannelVideoTab;
|
||||||
use serde::de::IgnoredAny;
|
use serde::de::IgnoredAny;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::model::QCont;
|
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive, Serialize, Deserialize,
|
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive, Serialize, Deserialize,
|
||||||
)]
|
)]
|
||||||
|
@ -33,15 +31,10 @@ pub enum ABTest {
|
||||||
LikeButtonViewmodel = 11,
|
LikeButtonViewmodel = 11,
|
||||||
ChannelPageHeader = 12,
|
ChannelPageHeader = 12,
|
||||||
MusicPlaylistTwoColumn = 13,
|
MusicPlaylistTwoColumn = 13,
|
||||||
CommentsFrameworkUpdate = 14,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List of active A/B tests that are run when none is manually specified
|
/// List of active A/B tests that are run when none is manually specified
|
||||||
const TESTS_TO_RUN: [ABTest; 3] = [
|
const TESTS_TO_RUN: [ABTest; 2] = [ABTest::ChannelPageHeader, ABTest::MusicPlaylistTwoColumn];
|
||||||
ABTest::ChannelPageHeader,
|
|
||||||
ABTest::MusicPlaylistTwoColumn,
|
|
||||||
ABTest::CommentsFrameworkUpdate,
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ABTestRes {
|
pub struct ABTestRes {
|
||||||
|
@ -111,7 +104,6 @@ pub async fn run_test(
|
||||||
ABTest::LikeButtonViewmodel => like_button_viewmodel(&query).await,
|
ABTest::LikeButtonViewmodel => like_button_viewmodel(&query).await,
|
||||||
ABTest::ChannelPageHeader => channel_page_header(&query).await,
|
ABTest::ChannelPageHeader => channel_page_header(&query).await,
|
||||||
ABTest::MusicPlaylistTwoColumn => music_playlist_two_column(&query).await,
|
ABTest::MusicPlaylistTwoColumn => music_playlist_two_column(&query).await,
|
||||||
ABTest::CommentsFrameworkUpdate => comments_framework_update(&query).await,
|
|
||||||
}
|
}
|
||||||
.unwrap();
|
.unwrap();
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
|
@ -364,20 +356,3 @@ pub async fn music_playlist_two_column(rp: &RustyPipeQuery) -> Result<bool> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(res.contains("\"musicResponsiveHeaderRenderer\""))
|
Ok(res.contains("\"musicResponsiveHeaderRenderer\""))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn comments_framework_update(rp: &RustyPipeQuery) -> Result<bool> {
|
|
||||||
let continuation =
|
|
||||||
"Eg0SC3dMZHBSN2d1S3k4GAYyJSIRIgt3TGRwUjdndUt5ODAAeAJCEGNvbW1lbnRzLXNlY3Rpb24%3D";
|
|
||||||
let res = rp
|
|
||||||
.raw(
|
|
||||||
ClientType::Desktop,
|
|
||||||
"next",
|
|
||||||
&QCont {
|
|
||||||
context: rp.get_context(ClientType::Desktop, true, None).await,
|
|
||||||
continuation,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
Ok(res.contains("\"frameworkUpdates\""))
|
|
||||||
}
|
|
||||||
|
|
|
@ -592,6 +592,7 @@ be accomodated. There are also no mobile/TV header images available any more.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## [13] Music album/playlist 2-column layout
|
## [13] Music album/playlist 2-column layout
|
||||||
|
|
||||||
- **Encountered on:** 29.02.2024
|
- **Encountered on:** 29.02.2024
|
||||||
|
@ -601,149 +602,5 @@ be accomodated. There are also no mobile/TV header images available any more.
|
||||||
|
|
||||||
![A/B test 13 screenshot](./_img/ab_13.png)
|
![A/B test 13 screenshot](./_img/ab_13.png)
|
||||||
|
|
||||||
YouTube Music updated the layout of album and playlist pages. The new layout shows the
|
YouTube Music updated the layout of album and playlist pages. The new layout shows
|
||||||
cover on the left side of the playlist content.
|
the cover on the left side of the playlist content.
|
||||||
|
|
||||||
## [14] Comments Framework update
|
|
||||||
|
|
||||||
- **Encountered on:** 31.01.2024
|
|
||||||
- **Impact:** 🟢 Low
|
|
||||||
- **Endpoint:** next
|
|
||||||
- **Status:** Common (50%)
|
|
||||||
|
|
||||||
YouTube changed the data model for YouTube comments, now putting the content into a
|
|
||||||
seperate framework update object
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"frameworkUpdates": {
|
|
||||||
"onResponseReceivedEndpoints": [
|
|
||||||
{
|
|
||||||
"clickTrackingParams": "CAAQg2ciEwi64q3dmKGFAxWvy0IFHc14BKM=",
|
|
||||||
"reloadContinuationItemsCommand": {
|
|
||||||
"targetId": "comments-section",
|
|
||||||
"continuationItems": [
|
|
||||||
{
|
|
||||||
"commentThreadRenderer": {
|
|
||||||
"replies": {
|
|
||||||
"commentRepliesRenderer": {
|
|
||||||
"contents": [
|
|
||||||
{
|
|
||||||
"continuationItemRenderer": {
|
|
||||||
"trigger": "CONTINUATION_TRIGGER_ON_ITEM_SHOWN",
|
|
||||||
"continuationEndpoint": {
|
|
||||||
"clickTrackingParams": "CHgQvnUiEwi64q3dmKGFAxWvy0IFHc14BKM=",
|
|
||||||
"commandMetadata": {
|
|
||||||
"webCommandMetadata": {
|
|
||||||
"sendPost": true,
|
|
||||||
"apiUrl": "/youtubei/v1/next"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"continuationCommand": {
|
|
||||||
"token": "Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5TlRUOHV4REVqZ1lxeWJJRjRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3lOVFQ4dXhERWpnWXF5YklGNEFhQUJBZw%3D%3D",
|
|
||||||
"request": "CONTINUATION_REQUEST_TYPE_WATCH_NEXT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"trackingParams": "CHgQvnUiEwi64q3dmKGFAxWvy0IFHc14BKM=",
|
|
||||||
"viewReplies": {
|
|
||||||
"buttonRenderer": {
|
|
||||||
"text": { "runs": [{ "text": "220 replies" }] },
|
|
||||||
"icon": { "iconType": "ARROW_DROP_DOWN" },
|
|
||||||
"trackingParams": "CHoQosAEIhMIuuKt3ZihhQMVr8tCBR3NeASj",
|
|
||||||
"iconPosition": "BUTTON_ICON_POSITION_TYPE_LEFT_OF_TEXT"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hideReplies": {
|
|
||||||
"buttonRenderer": {
|
|
||||||
"text": { "runs": [{ "text": "220 replies" }] },
|
|
||||||
"icon": { "iconType": "ARROW_DROP_UP" },
|
|
||||||
"trackingParams": "CHkQ280EIhMIuuKt3ZihhQMVr8tCBR3NeASj",
|
|
||||||
"iconPosition": "BUTTON_ICON_POSITION_TYPE_LEFT_OF_TEXT"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"targetId": "comment-replies-item-UgyNTT8uxDEjgYqybIF4AaABAg"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"trackingParams": "CHYQwnUYywEiEwi64q3dmKGFAxWvy0IFHc14BKM=",
|
|
||||||
"renderingPriority": "RENDERING_PRIORITY_PINNED_COMMENT",
|
|
||||||
"isModeratedElqComment": false,
|
|
||||||
"commentViewModel": {
|
|
||||||
"commentViewModel": {
|
|
||||||
"commentId": "UgyNTT8uxDEjgYqybIF4AaABAg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"entityBatchUpdate": {
|
|
||||||
"mutations": [
|
|
||||||
{
|
|
||||||
"entityKey": "EhpVZ3lOVFQ4dXhERWpnWXF5YklGNEFhQUJBZyAoKAE%3D",
|
|
||||||
"type": "ENTITY_MUTATION_TYPE_REPLACE",
|
|
||||||
"payload": {
|
|
||||||
"commentEntityPayload": {
|
|
||||||
"key": "EhpVZ3lOVFQ4dXhERWpnWXF5YklGNEFhQUJBZyAoKAE%3D",
|
|
||||||
"properties": {
|
|
||||||
"commentId": "UgyNTT8uxDEjgYqybIF4AaABAg",
|
|
||||||
"content": {
|
|
||||||
"content": "⚠️ Important notice: if you put any symbol immediately after markup, it will not work: *here is the comma*, without space.\n\nYou should leave space before and after , to make it work.\n\nSame for _underscore_, and -hyphen-.\n\nLeave space before opening and after closing underscore and hyphen. Put all dots and commas inside markup.",
|
|
||||||
"styleRuns": [
|
|
||||||
{
|
|
||||||
"startIndex": 135,
|
|
||||||
"length": 28,
|
|
||||||
"weightLabel": "FONT_WEIGHT_MEDIUM"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startIndex": 267,
|
|
||||||
"length": 10,
|
|
||||||
"weightLabel": "FONT_WEIGHT_NORMAL",
|
|
||||||
"italic": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startIndex": 282,
|
|
||||||
"length": 7,
|
|
||||||
"weightLabel": "FONT_WEIGHT_NORMAL",
|
|
||||||
"strikethrough": "LINE_STYLE_SINGLE"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"publishedTime": "2 years ago (edited)",
|
|
||||||
"replyLevel": 0,
|
|
||||||
"authorButtonA11y": "@kibizoid",
|
|
||||||
"toolbarStateKey": "EhpVZ3lOVFQ4dXhERWpnWXF5YklGNEFhQUJBZyAsKAE%3D",
|
|
||||||
"translateButtonEntityKey": "EhpVZ3lOVFQ4dXhERWpnWXF5YklGNEFhQUJBZyD_ASgB"
|
|
||||||
},
|
|
||||||
"author": {
|
|
||||||
"channelId": "UCUJfyiofeHQTmxKwZ6cCwIg",
|
|
||||||
"displayName": "@kibizoid",
|
|
||||||
"avatarThumbnailUrl": "https://yt3.ggpht.com/ytc/AIdro_nY2PkIyojDqs9Bk5RY6J90-U7wePswTYl799DNJQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
"isVerified": false,
|
|
||||||
"isCurrentUser": false,
|
|
||||||
"isCreator": false,
|
|
||||||
"isArtist": false
|
|
||||||
},
|
|
||||||
"avatar": {
|
|
||||||
"image": {
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"url": "https://yt3.ggpht.com/ytc/AIdro_nY2PkIyojDqs9Bk5RY6J90-U7wePswTYl799DNJQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
"width": 88,
|
|
||||||
"height": 88
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkip
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
video_item::YouTubeListRenderer, Alert, ChannelBadge, ContentRenderer, ContentsRenderer,
|
video_item::YouTubeListRenderer, Alert, ChannelBadge, ContentRenderer, ContentsRenderer,
|
||||||
ContinuationActionWrap, ImageView, ResponseContext, Thumbnails, TwoColumnBrowseResults,
|
ContinuationActionWrap, ResponseContext, Thumbnails, TwoColumnBrowseResults,
|
||||||
};
|
};
|
||||||
use crate::serializer::text::{AttributedText, Text, TextComponent};
|
use crate::serializer::text::{AttributedText, Text, TextComponent};
|
||||||
|
|
||||||
|
@ -224,6 +224,12 @@ pub(crate) struct PhAvatarView3 {
|
||||||
pub avatar_view_model: ImageView,
|
pub avatar_view_model: ImageView,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) struct ImageView {
|
||||||
|
pub image: Thumbnails,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct PhMetadataView {
|
pub(crate) struct PhMetadataView {
|
||||||
|
|
|
@ -48,7 +48,6 @@ pub(crate) mod channel_rss;
|
||||||
pub(crate) use channel_rss::ChannelRss;
|
pub(crate) use channel_rss::ChannelRss;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
|
@ -107,12 +106,6 @@ pub(crate) struct ThumbnailsWrap {
|
||||||
pub thumbnail: Thumbnails,
|
pub thumbnail: Thumbnails,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct ImageView {
|
|
||||||
pub image: Thumbnails,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List of images in different resolutions.
|
/// List of images in different resolutions.
|
||||||
/// Not only used for thumbnails, but also for avatars and banners.
|
/// Not only used for thumbnails, but also for avatars and banners.
|
||||||
#[derive(Default, Debug, Deserialize)]
|
#[derive(Default, Debug, Deserialize)]
|
||||||
|
@ -381,87 +374,3 @@ pub(crate) fn alerts_to_err(id: &str, alerts: Option<Vec<Alert>>) -> ExtractionE
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FRAMEWORK UPDATES
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct FrameworkUpdates<T> {
|
|
||||||
pub entity_batch_update: EntityBatchUpdate<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct EntityBatchUpdate<T> {
|
|
||||||
pub mutations: FrameworkUpdateMutations<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List of update mutations that deserializes into a HashMap (entity_key => payload)
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct FrameworkUpdateMutations<T> {
|
|
||||||
pub items: HashMap<String, T>,
|
|
||||||
pub warnings: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T> Deserialize<'de> for FrameworkUpdateMutations<T>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct SeqVisitor<T>(PhantomData<T>);
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum MutationOrError<T> {
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
Good {
|
|
||||||
entity_key: String,
|
|
||||||
payload: T,
|
|
||||||
},
|
|
||||||
Error(serde_json::Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for SeqVisitor<T>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
type Value = FrameworkUpdateMutations<T>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("sequence of entity mutations")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
|
||||||
where
|
|
||||||
A: serde::de::SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut items = HashMap::with_capacity(seq.size_hint().unwrap_or_default());
|
|
||||||
let mut warnings = Vec::new();
|
|
||||||
|
|
||||||
while let Some(value) = seq.next_element::<MutationOrError<T>>()? {
|
|
||||||
match value {
|
|
||||||
MutationOrError::Good {
|
|
||||||
entity_key,
|
|
||||||
payload,
|
|
||||||
} => {
|
|
||||||
items.insert(entity_key, payload);
|
|
||||||
}
|
|
||||||
MutationOrError::Error(value) => {
|
|
||||||
warnings.push(format!(
|
|
||||||
"error deserializing item: {}",
|
|
||||||
serde_json::to_string(&value).unwrap_or_default()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(FrameworkUpdateMutations { items, warnings })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_seq(SeqVisitor(PhantomData::<T>))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError};
|
use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError};
|
||||||
|
|
||||||
|
use crate::serializer::text::TextComponent;
|
||||||
use crate::serializer::{
|
use crate::serializer::{
|
||||||
text::{AccessibilityText, AttributedText, Text, TextComponent, TextComponents},
|
text::{AccessibilityText, AttributedText, Text, TextComponents},
|
||||||
MapResult,
|
MapResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,10 +13,7 @@ use super::{
|
||||||
url_endpoint::BrowseEndpointWrap, ContinuationEndpoint, ContinuationItemRenderer, Icon,
|
url_endpoint::BrowseEndpointWrap, ContinuationEndpoint, ContinuationItemRenderer, Icon,
|
||||||
MusicContinuationData, Thumbnails,
|
MusicContinuationData, Thumbnails,
|
||||||
};
|
};
|
||||||
use super::{
|
use super::{ChannelBadge, ContentsRendererLogged, ResponseContext, YouTubeListItem};
|
||||||
ChannelBadge, ContentsRendererLogged, FrameworkUpdates, ImageView, ResponseContext,
|
|
||||||
YouTubeListItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#VIDEO DETAILS
|
#VIDEO DETAILS
|
||||||
|
@ -478,7 +476,6 @@ pub(crate) struct VideoComments {
|
||||||
/// - n*commentRenderer, continuationItemRenderer:
|
/// - n*commentRenderer, continuationItemRenderer:
|
||||||
/// replies + continuation
|
/// replies + continuation
|
||||||
pub on_response_received_endpoints: MapResult<Vec<CommentsContItem>>,
|
pub on_response_received_endpoints: MapResult<Vec<CommentsContItem>>,
|
||||||
pub framework_updates: Option<FrameworkUpdates<Payload>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Video comments continuation
|
/// Video comments continuation
|
||||||
|
@ -501,13 +498,23 @@ pub(crate) struct AppendComments {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) enum CommentListItem {
|
pub(crate) enum CommentListItem {
|
||||||
/// Top-level comment
|
/// Top-level comment
|
||||||
CommentThreadRenderer(CommentThreadRenderer),
|
#[serde(rename_all = "camelCase")]
|
||||||
|
CommentThreadRenderer {
|
||||||
|
comment: Comment,
|
||||||
|
/// Continuation token to fetch replies
|
||||||
|
#[serde(default)]
|
||||||
|
replies: Replies,
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde_as(deserialize_as = "DefaultOnError")]
|
||||||
|
rendering_priority: CommentPriority,
|
||||||
|
},
|
||||||
/// Reply comment
|
/// Reply comment
|
||||||
CommentRenderer(CommentRenderer),
|
CommentRenderer(CommentRenderer),
|
||||||
/// Reply comment (A/B #14)
|
|
||||||
CommentViewModel(CommentViewModel),
|
|
||||||
/// Continuation token to fetch more comments
|
/// Continuation token to fetch more comments
|
||||||
ContinuationItemRenderer(ContinuationItemVariants),
|
#[serde(rename_all = "camelCase")]
|
||||||
|
ContinuationItemRenderer {
|
||||||
|
continuation_endpoint: ContinuationEndpoint,
|
||||||
|
},
|
||||||
/// Header of the comment section (contains number of comments)
|
/// Header of the comment section (contains number of comments)
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
CommentsHeaderRenderer {
|
CommentsHeaderRenderer {
|
||||||
|
@ -517,46 +524,6 @@ pub(crate) enum CommentListItem {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub(crate) enum ContinuationItemVariants {
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
Ep {
|
|
||||||
continuation_endpoint: ContinuationEndpoint,
|
|
||||||
},
|
|
||||||
Btn {
|
|
||||||
button: ContinuationButton,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContinuationItemVariants {
|
|
||||||
pub fn token(self) -> String {
|
|
||||||
match self {
|
|
||||||
ContinuationItemVariants::Ep {
|
|
||||||
continuation_endpoint,
|
|
||||||
} => continuation_endpoint,
|
|
||||||
ContinuationItemVariants::Btn { button } => button.button_renderer.command,
|
|
||||||
}
|
|
||||||
.continuation_command
|
|
||||||
.token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentThreadRenderer {
|
|
||||||
/// Missing on the FrameworkUpdate data model (A/B #14)
|
|
||||||
pub comment: Option<Comment>,
|
|
||||||
pub comment_view_model: Option<CommentViewModelWrap>,
|
|
||||||
/// Continuation token to fetch replies
|
|
||||||
#[serde(default)]
|
|
||||||
pub replies: Replies,
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde_as(deserialize_as = "DefaultOnError")]
|
|
||||||
pub rendering_priority: CommentPriority,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct Comment {
|
pub(crate) struct Comment {
|
||||||
|
@ -597,7 +564,7 @@ pub(crate) struct CommentRenderer {
|
||||||
pub action_buttons: CommentActionButtons,
|
pub action_buttons: CommentActionButtons,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Debug, Deserialize)]
|
#[derive(Default, Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub(crate) enum CommentPriority {
|
pub(crate) enum CommentPriority {
|
||||||
/// Default rendering priority
|
/// Default rendering priority
|
||||||
|
@ -607,26 +574,6 @@ pub(crate) enum CommentPriority {
|
||||||
RenderingPriorityPinnedComment,
|
RenderingPriorityPinnedComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CommentPriority> for bool {
|
|
||||||
fn from(value: CommentPriority) -> Self {
|
|
||||||
matches!(value, CommentPriority::RenderingPriorityPinnedComment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentViewModelWrap {
|
|
||||||
pub comment_view_model: CommentViewModel,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentViewModel {
|
|
||||||
pub comment_id: String,
|
|
||||||
pub comment_key: String,
|
|
||||||
pub toolbar_state_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Does not contain replies directly but a continuation token
|
/// Does not contain replies directly but a continuation token
|
||||||
/// for fetching them.
|
/// for fetching them.
|
||||||
#[derive(Default, Debug, Deserialize)]
|
#[derive(Default, Debug, Deserialize)]
|
||||||
|
@ -690,85 +637,3 @@ pub(crate) struct AuthorCommentBadgeRenderer {
|
||||||
/// Artist: `OFFICIAL_ARTIST_BADGE`
|
/// Artist: `OFFICIAL_ARTIST_BADGE`
|
||||||
pub icon: Icon,
|
pub icon: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) enum Payload {
|
|
||||||
CommentEntityPayload(CommentEntityPayload),
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
EngagementToolbarStateEntityPayload {
|
|
||||||
heart_state: HeartState,
|
|
||||||
},
|
|
||||||
#[serde(other, deserialize_with = "deserialize_ignore_any")]
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentEntityPayload {
|
|
||||||
pub properties: CommentProperties,
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde_as(as = "DefaultOnError")]
|
|
||||||
pub author: Option<CommentAuthor>,
|
|
||||||
pub toolbar: CommentToolbar,
|
|
||||||
#[serde(default)]
|
|
||||||
pub avatar: ImageView,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentProperties {
|
|
||||||
#[serde_as(as = "AttributedText")]
|
|
||||||
pub content: TextComponents,
|
|
||||||
pub published_time: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentAuthor {
|
|
||||||
pub channel_id: String,
|
|
||||||
pub display_name: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub is_verified: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub is_artist: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub is_creator: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct CommentToolbar {
|
|
||||||
pub like_count_notliked: String,
|
|
||||||
pub reply_count: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Deserialize)]
|
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
|
||||||
pub(crate) enum HeartState {
|
|
||||||
ToolbarHeartStateUnhearted,
|
|
||||||
ToolbarHeartStateHearted,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HeartState> for bool {
|
|
||||||
fn from(value: HeartState) -> Self {
|
|
||||||
match value {
|
|
||||||
HeartState::ToolbarHeartStateUnhearted => false,
|
|
||||||
HeartState::ToolbarHeartStateHearted => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct ContinuationButton {
|
|
||||||
pub button_renderer: ContinuationButtonRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub(crate) struct ContinuationButtonRenderer {
|
|
||||||
pub command: ContinuationEndpoint,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,691 +0,0 @@
|
||||||
---
|
|
||||||
source: src/client/video_details.rs
|
|
||||||
expression: map_res.c
|
|
||||||
---
|
|
||||||
Paginator(
|
|
||||||
count: Some(20617),
|
|
||||||
items: [
|
|
||||||
Comment(
|
|
||||||
id: "UgyNTT8uxDEjgYqybIF4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "⚠\u{fe0f} Important notice: if you put any symbol immediately after markup, it will not work: *here is the comma*, without space.\n\nYou should leave space before and after , to make it work.\n\nSame for _underscore_, and -hyphen-.\n\nLeave space before opening and after closing underscore and hyphen. Put all dots and commas inside markup.",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCUJfyiofeHQTmxKwZ6cCwIg",
|
|
||||||
name: "@kibizoid",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_nY2PkIyojDqs9Bk5RY6J90-U7wePswTYl799DNJQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago (edited)",
|
|
||||||
like_count: Some(293),
|
|
||||||
reply_count: 220,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(220),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5TlRUOHV4REVqZ1lxeWJJRjRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3lOVFQ4dXhERWpnWXF5YklGNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: true,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgycWgNOoon0A4EV9LZ4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Me: tests out fonts\nFriend: Why are you doing this?\nMe: my goals are beyond your understanding",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCr0PeEY_am9P-GobbfvKECw",
|
|
||||||
name: "@userfjdrg",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/Zr2PmQsrD4obL2n5HS18X3jKXGJ-HFjIJS_OcZv4I5VAk5HuLRCpzFprY5Hh7n23-FCURVJi=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago",
|
|
||||||
like_count: Some(80),
|
|
||||||
reply_count: 34,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(34),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5Y1dnTk9vb24wQTRFVjlMWjRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3ljV2dOT29vbjBBNEVWOUxaNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugy5iq4M1c3WS3lGmih4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "To-do list\n• be dumb\n• get kicked out when i can legally live alone\n• spend money on pointless things",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCDB5XvpUB8cEvjbWewlp28w",
|
|
||||||
name: "@T0r0xFan",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/PPL5vj6-pXFpaLa41yet34OHGcEYt06WPQLmruaiFJSM0eLmn9ZQW0QgTtdafDBO-kNy2oukVA=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago (edited)",
|
|
||||||
like_count: Some(48),
|
|
||||||
reply_count: 22,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(22),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5NWlxNE0xYzNXUzNsR21paDRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3k1aXE0TTFjM1dTM2xHbWloNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgxqDIVVcoigjtx4Dtl4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "omg thank you! Ive been looking for this tutorial for a year forever",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCxa4xER0-cFbcIYp0ZIeVaw",
|
|
||||||
name: "@LunasVibe",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/P5Io1kQb7GRwe4pgNsaYFEm30hDl_T7Tp5rZo7aYWFkqbV6Yp_lCYVuaaK7O3SEsnIX_5iC1Hw=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "1 month ago",
|
|
||||||
like_count: Some(0),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgxDQfVQdYaWR-VUM-94AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "tysm\ni finally learned it\nother channel never go straight to the point",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UC8cojSRuyZT74Bs_b5AecTA",
|
|
||||||
name: "@Bp_bts_skz_for_life",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/0zFBRYv8wo2JqzkyMk29xgC8zD1nKYNSSoD3Zo9XP8t9rHrbTYEEt0gdu0O3XS7Scpza3JJKog=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "1 month ago (edited)",
|
|
||||||
like_count: Some(1),
|
|
||||||
reply_count: 1,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(1),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd4RFFmVlFkWWFXUi1WVU0tOTRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3hEUWZWUWRZYVdSLVZVTS05NEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgxFvrmwec-jmfQyGRR4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "I like how this was straight to the point. Unlike other channels lol Thank you!",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCCyIVS_s1-jA48pPft8AifA",
|
|
||||||
name: "@ishouldbesleepingalready",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/N90y_XdoDP6Rd1v6Z5OpDH8jMtvqpU1qnF6DJoIL6qcLiWfZK7ok8u_IxqSxJazaQH6oqhEbqA=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago",
|
|
||||||
like_count: Some(241),
|
|
||||||
reply_count: 65,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(65),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd4RnZybXdlYy1qbWZReUdSUjRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3hGdnJtd2VjLWptZlF5R1JSNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugy-3OYEcwxkvyrrCqN4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "To the person who is reading this: You\'re intelligent and smart, stay safe",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCQklgcA8quxZm5pgNAsVJAQ",
|
|
||||||
name: "@blocking948",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_nAg9bEjW4otWlryJwqAgiDRLzy8ZX-ROqkDY1ksQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "3 years ago (edited)",
|
|
||||||
like_count: Some(711),
|
|
||||||
reply_count: 250,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(250),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5LTNPWUVjd3hrdnlyckNxTjRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3ktM09ZRWN3eGt2eXJyQ3FONEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugylw3ss_xv9svWbRud4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "‘ life could be a dream, life could be a dream ‘",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCSyjdP7Duhns4Ybncy6ObZA",
|
|
||||||
name: "@malarchee0899",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/dSwRzOfoKrN4yc58uhFlIyqBXmbi6B14-On-wEEM_S6Nr6aDHTkG-xVkI1-u-uBwqKqodEgrMro=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "13 days ago",
|
|
||||||
like_count: Some(2),
|
|
||||||
reply_count: 1,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(1),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5bHczc3NfeHY5c3ZXYlJ1ZDRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3lsdzNzc194djlzdldiUnVkNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgydXobRB0F5dW1KVsF4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Woah! thank you for showing me this I really needed it!",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UC9f9uJgwsCBBHA4CioIzdkA",
|
|
||||||
name: "@fatimagarcia3162",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/cKmBVKqq9mkW4F355y3UtUw4POwTWKi-0LUYLDx85vffRd7pU-LECXvudUrHH_9qobo6A1kM=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "1 month ago",
|
|
||||||
like_count: Some(0),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgwmFn6ejKltcZ_BZvl4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "The fitness gram pacer test is a multistage aerobic capacity test that progressively gets more difficult as it continues.",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCIymYi-_AJ10pYrh8sqTBTg",
|
|
||||||
name: "@No-du9is",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_lPWhGGvIIA08s4u_-Lwyx88rGSRksOFeYHipE=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago",
|
|
||||||
like_count: Some(22),
|
|
||||||
reply_count: 12,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(12),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd3bUZuNmVqS2x0Y1pfQlp2bDRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3dtRm42ZWpLbHRjWl9CWnZsNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgxtXH6bWRWm8ahavfR4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "YouTube got a new update(or probably it\'s a bug) and for that it\'s not showing bold/strikethrough/italic on the app but it\'s showing on other places.",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCyaflzzOH0GC683DlQ-fzwg",
|
|
||||||
name: "@HaruXen",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/YMmDD0zp5wT6l5ozqVMEMuqm5W07QFqmMHzOJ9QKGnSf9xpgEQ0rznstfXlBDxlFpLIrltQxRg=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "3 years ago",
|
|
||||||
like_count: Some(167),
|
|
||||||
reply_count: 337,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(337),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd4dFhINmJXUldtOGFoYXZmUjRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3h0WEg2YldSV204YWhhdmZSNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: true,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgyHg3XnjBV935da_Lh4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "omg it works i actuallly cant believe this ive been wanting to do this for ages thankyou so much!",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCFL5d8rMCfbxppODSbRLOgQ",
|
|
||||||
name: "@Auf-dem-weg-zum-sieg",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/oi4vbSV3Cx9G97QcHkSMWL98LksC6rnTLoq93T5sOO8MNuZPXWEXq9Nqkp8XYF93L2WklHADmNY=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "4 days ago",
|
|
||||||
like_count: Some(0),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgxIL5emXyn42htlfZZ4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "I did know how to do that writing where the text is highlighted before, and now after I watched this video I knew even how to write those styles of text.",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UChkVaXCYN_QcaE50zETAMOg",
|
|
||||||
name: "@CasamTheAnimator",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ctUbv8QpWPZRZumEBTVhlSSxg0JfiyvJ40nrWj_0ivOy5s6OoPK7iNp01diskRLs1Hig4ZE82w=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "3 weeks ago",
|
|
||||||
like_count: Some(0),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgwMKY-89XCdCVB9bXp4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Nobody asked for, but everyone needed",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCDezbPSXn3awzhxVm7qhGtg",
|
|
||||||
name: "@0_Ed",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/4DWdgRIJ0lEV-e4GZFrdf8MGxQBtML2aix2orKBt3iM6QBrh7Kg1ur1FZlyRmqWpWnRPRIex9w=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "3 years ago",
|
|
||||||
like_count: Some(12),
|
|
||||||
reply_count: 5,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(5),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd3TUtZLTg5WENkQ1ZCOWJYcDRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3dNS1ktODlYQ2RDVkI5YlhwNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgyfuG2sCDvgnRUYHJp4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "me: types bold\n\nHaruTutorial: your bald",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCge96FdHXkARBjzPhdYl8Sg",
|
|
||||||
name: "@stargazeu",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/JoeW_qNuf9b6BusB3E6JShizqRLB4jR3NaTnsnzvpUQ1KW88OcS74_Sx1h6vjZiXK2uOxnrUNeY=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "3 years ago",
|
|
||||||
like_count: Some(42),
|
|
||||||
reply_count: 15,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(15),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd5ZnVHMnNDRHZnblJVWUhKcDRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3lmdUcyc0NEdmduUlVZSEpwNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgwAXndNNEa1h-VVIC94AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "the McDonald’s don’t feel like turning the Icecream machine on",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCkmY4kQ8e8gDRllV485Rd9g",
|
|
||||||
name: "@Flowershowrise",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/c2iMsq-wFjKRjTDqPy14UpMI1B9hNms4moW9H7xtPjOMI0vjaHwN94me23upYar-8CE3s6QkFw=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "1 month ago",
|
|
||||||
like_count: Some(2),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgwwEBqareQ0tpsW7RR4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "YOOO THIS IS SICK! THANK YOU MAN!",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UChIbg4dGguUwzg7O-xmi57g",
|
|
||||||
name: "@ziaaaaa.",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/HEAdH93oAQu2ScXNmiKIISapv5O9dKSVLuT3gD1zJhSgHqTaptL7JPun6A5GZqg58_C75_OPkQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "1 month ago",
|
|
||||||
like_count: Some(0),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgxnFMLrpvbCWzHidml4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Someone must honor him , this man is the best , no , he is a LEGEND . We must all thank him for his video and for getting to the point immediately.",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCeGJuvHZqqebHTE_Kz2zyug",
|
|
||||||
name: "@Dahackabarade",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/csf-cEr2z4uWg8ZpVEPqgS2D2ZUHKBAJWnIbnzQCRtAlioSlUbtQZAyx76tnyfpXpixrsKke6DE=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago (edited)",
|
|
||||||
like_count: Some(11),
|
|
||||||
reply_count: 5,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(5),
|
|
||||||
items: [],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYygwEaUBIaVWd4bkZNTHJwdmJDV3pIaWRtbDRBYUFCQWciAggAKhhVQ3lhZmx6ek9IMEdDNjgzRGxRLWZ6d2cyC1FpcDFWa1R1TTcwQAFICoIBAggBQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3huRk1McnB2YkNXekhpZG1sNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "UgwCIwmF6synP7UF_wV4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Never gonna give you up. Im gonna let u down",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCyrDrBrWvXwIhf2s2F1dq-Q",
|
|
||||||
name: "@imnotjust...2326",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/Clzb1GD_KnRm9u7mOAN165HZO_H0jhXQlRG8YvEjqkDuBUNibGkclRyRZIdhi-yJhC4hHorGLQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 years ago",
|
|
||||||
like_count: Some(14),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: true,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugyb5Wy91Yon69o3wLh4AaABAg",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Thank you for being A Legend No, The Goat Lets go dude",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCPCgaC_EJlS5RpRRWPHWvKA",
|
|
||||||
name: "@gfghdgfghd6391",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_n0lpwvjOP9HO_XHxzInwQoqQ7qIXeR0SqZVbCE=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "1 month ago",
|
|
||||||
like_count: Some(0),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
ctoken: Some("Eg0SC1FpcDFWa1R1TTcwGAYy4gIKuAJnZXRfcmFua2VkX3N0cmVhbXMtLUNxWUJDSUFFRlJlMzBUZ2Ftd0VLbGdFSTJGOFFnQVFZQnlLTEFSdUNtdFZ5a3dZMFFzVVVvM3I0LUY0OWU2d3RGSGFjbDIxS0Nsd3M4ZFZNaGdDdm9VWFhac2NZNXVncURIaUNiQVpveUczUEh6MTRPQ0tJV1BZTm9PTnN6dlFPVDZkaFZXMGRiSlZNelJXSW5QTm5QY0pyTmhQbzAyT1ZuamlVcHJTTHc1UEZxVHFBRkxlYXEtSHQtdU5uZkp1SzItMXVhQkp2aWE3S183QzgzOURiekJhY2tFeVRzUUFRRkJJRkNJZ2dHQUFTQndpSElCQUJHQUFTQlFpb0lCZ0FFZ1VJaVNBWUFCSUhDSVVnRUFrWUFSSUhDSVFnRUFzWUFSZ0EiESILUWlwMVZrVHVNNzAwAHgBKBRCEGNvbW1lbnRzLXNlY3Rpb24%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
)
|
|
|
@ -1,351 +0,0 @@
|
||||||
---
|
|
||||||
source: src/client/video_details.rs
|
|
||||||
expression: map_res.c
|
|
||||||
---
|
|
||||||
Paginator(
|
|
||||||
count: None,
|
|
||||||
items: [
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-GzThFMUcw",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Fact🙌🏻",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UC4I0-MXGyTRsc1tsJrDMh2A",
|
|
||||||
name: "@Sadaf788",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/uIXOBEBIhYn6Y8cQHlhaGGnCqOqS8PI5YR_Cx28qR_Y_p1qkjHC8V68iwxfeJ20eQ3zp81owJ64=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(800),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-H295I5iMZ",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Facts",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCdJ0CAWWa1rRjRbVrQUrU_w",
|
|
||||||
name: "@Biggest_Onceu",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/dHe_Zmr-7EueUz0R-BbuFoGwRyNMXwa3gb_GJMgAie9yU5PM6LbgTlNJ1zivRxnjiFg2nrlF1Es=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(530),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-H73oLoHkI",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Faacttts",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCPPhfcNhQ768F0Hhk3-25hA",
|
|
||||||
name: "@neni996",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/t__fCiOIhKCg2BwaxaRog9F_a5uemd8rTEvwzWYl6WeLn-nN9xEW0FvxUtM0fQrh2Dj_6ENsGQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(412),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-H7nBbPD5z",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "REAL",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCQyomFJDEQtC2lbQ6E7QUGA",
|
|
||||||
name: "@momolvs",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/nt6GjcwAs8NPgaF29pn-cHTPmPAwQC_e_lXQHGDjZJGSRKzsH4s3le8Wpg0ByAUvPwTSHWe0OA=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(312),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-H9s3LvStZ",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "FR!!",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UC9HOPOf3gD3aw6Ej9WZ-rYg",
|
|
||||||
name: "@user-vv9yp1fh8w",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_nqyUCWW7jqWrg_39XNQ18-acPouL6wyHeQnZOMbmlSa9x2YGWINkfU1DLcvaXw=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(187),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-HB52Dv3SL",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "fato.",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCW6ua0VDEFz7SyVMX01fTCA",
|
|
||||||
name: "@millenatwice",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/rInaNzUF3JLL_pCNfZtZlf2cHipf1yM4grr8VGJRHocwOQiuq1x7kUVi24q3ydtDC0j8bqbw2vA=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(165),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-HDLrM1OPD",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "For sure!! TWICE is always TWICE!! They always give GOOD MUSIC",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCqDSps4SV0v8Dzf8esr6ScQ",
|
|
||||||
name: "@Its_me_hi_good",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_nfh9rWK7_gae1YkUgKuq13G9OUpxQCqrXAAi1hfPkCvHeHORbq3DUTYm7b5eoy=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(341),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-HIAbm3Him",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Fr",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCFA4BaLyvM1DDNsFyE_BHqQ",
|
|
||||||
name: "@amanpreetbrar7836",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_n1M-xxgSLIqe17kDv-i-tPn23FT1ywabpRAQ=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(84),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-HIrpOKIi7",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "Presave I got you and with youth on spotify",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCxMDESp088wGItVM4xXACgw",
|
|
||||||
name: "@RitaOnce9",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/hVTumbLCpmtJw0c2mq1B-ES5W3kdYPqnNrtzEcUhxCoUN6dAutXc6exaPRnBMLM6Jw1ILPoBDg=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(112),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
id: "Ugzu-t48vV9SjdeWIMh4AaABAg.A-Grr7qN9uaA-HKMOSBnLK",
|
|
||||||
text: RichText([
|
|
||||||
Text(
|
|
||||||
text: "real",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
author: Some(ChannelTag(
|
|
||||||
id: "UCqeOr9ddrs_d6OgboKjk6zw",
|
|
||||||
name: "@twiceupremacy",
|
|
||||||
avatar: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/ytc/AIdro_lpKYjxtRm1HSjv3tFvGwrvnRILmJoQrPTBBOFG=s88-c-k-c0x00ffffff-no-rj",
|
|
||||||
width: 88,
|
|
||||||
height: 88,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
verification: None,
|
|
||||||
subscriber_count: None,
|
|
||||||
)),
|
|
||||||
publish_date: "[date]",
|
|
||||||
publish_date_txt: "2 months ago",
|
|
||||||
like_count: Some(75),
|
|
||||||
reply_count: 0,
|
|
||||||
replies: Paginator(
|
|
||||||
count: Some(0),
|
|
||||||
items: [],
|
|
||||||
ctoken: None,
|
|
||||||
endpoint: browse,
|
|
||||||
),
|
|
||||||
by_owner: false,
|
|
||||||
pinned: false,
|
|
||||||
hearted: false,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
ctoken: Some("Eg0SC2hhZjY3ZUtGMHVvGAYy1wEKUGdldF9jb21tZW50X3dpdGhfcmVwbGllc19zdHJlYW0tLUNnZ0lnQVFWRjdmUk9CSUZDS0FnR0FFWUFDSU9DZ3dJeXUzdXJRWVE2Tkt2NGdFGlASGlVnenUtdDQ4dlY5U2pkZVdJTWg0QWFBQkFnIgIIACoYVUNhTzZUWXRsQzhVNXR0ejYyaFRyWmdnMgtoYWY2N2VLRjB1b0AASDKCAQIIASgKQi9jb21tZW50LXJlcGxpZXMtaXRlbS1VZ3p1LXQ0OHZWOVNqZGVXSU1oNEFhQUJBZw%3D%3D"),
|
|
||||||
endpoint: browse,
|
|
||||||
)
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, fmt::Debug};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
error::{Error, ExtractionError},
|
error::{Error, ExtractionError},
|
||||||
model::{
|
model::{
|
||||||
paginator::{ContinuationEndpoint, Paginator},
|
paginator::{ContinuationEndpoint, Paginator},
|
||||||
ChannelTag, Chapter, Comment, Verification, VideoDetails, VideoItem,
|
ChannelTag, Chapter, Comment, VideoDetails, VideoItem,
|
||||||
},
|
},
|
||||||
param::Language,
|
param::Language,
|
||||||
serializer::MapResult,
|
serializer::MapResult,
|
||||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
response::{self, video_details::Payload, IconType},
|
response::{self, IconType},
|
||||||
ClientType, MapResponse, QContinuation, RustyPipeQuery, YTContext,
|
ClientType, MapResponse, QContinuation, RustyPipeQuery, YTContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -391,73 +391,44 @@ impl MapResponse<Paginator<Comment>> for response::VideoComments {
|
||||||
_vdata: Option<&str>,
|
_vdata: Option<&str>,
|
||||||
) -> Result<MapResult<Paginator<Comment>>, ExtractionError> {
|
) -> Result<MapResult<Paginator<Comment>>, ExtractionError> {
|
||||||
let received_endpoints = self.on_response_received_endpoints;
|
let received_endpoints = self.on_response_received_endpoints;
|
||||||
let mut warnings = Vec::new();
|
let mut warnings = received_endpoints.warnings;
|
||||||
|
|
||||||
let mut comments = Vec::new();
|
let mut comments = Vec::new();
|
||||||
let mut comment_count = None;
|
let mut comment_count = None;
|
||||||
let mut ctoken = None;
|
let mut ctoken = None;
|
||||||
|
|
||||||
let mut mutations = if let Some(upd) = self.framework_updates {
|
|
||||||
let mut m = upd.entity_batch_update.mutations;
|
|
||||||
warnings.append(&mut m.warnings);
|
|
||||||
m.items
|
|
||||||
} else {
|
|
||||||
HashMap::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
received_endpoints.c.into_iter().for_each(|citem| {
|
received_endpoints.c.into_iter().for_each(|citem| {
|
||||||
let mut items = citem.append_continuation_items_action.continuation_items;
|
let mut items = citem.append_continuation_items_action.continuation_items;
|
||||||
warnings.append(&mut items.warnings);
|
warnings.append(&mut items.warnings);
|
||||||
items.c.into_iter().for_each(|item| match item {
|
items.c.into_iter().for_each(|item| match item {
|
||||||
response::video_details::CommentListItem::CommentThreadRenderer(thread) => {
|
response::video_details::CommentListItem::CommentThreadRenderer {
|
||||||
if let Some(comment) = thread.comment {
|
comment,
|
||||||
comments.push(map_comment(
|
replies,
|
||||||
|
rendering_priority,
|
||||||
|
} => {
|
||||||
|
let mut res = map_comment(
|
||||||
comment.comment_renderer,
|
comment.comment_renderer,
|
||||||
Some(thread.replies),
|
Some(replies),
|
||||||
thread.rendering_priority,
|
rendering_priority,
|
||||||
lang,
|
lang,
|
||||||
&mut warnings,
|
|
||||||
));
|
|
||||||
} else if let Some(vm) = thread.comment_view_model {
|
|
||||||
if let Some(c) = map_comment_vm(
|
|
||||||
vm.comment_view_model,
|
|
||||||
&mut mutations,
|
|
||||||
Some(thread.replies),
|
|
||||||
thread.rendering_priority,
|
|
||||||
lang,
|
|
||||||
&mut warnings,
|
|
||||||
) {
|
|
||||||
comments.push(c);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warnings.push(
|
|
||||||
"comment does not contain comment or commentViewModel field".to_owned(),
|
|
||||||
);
|
);
|
||||||
}
|
comments.push(res.c);
|
||||||
|
warnings.append(&mut res.warnings);
|
||||||
}
|
}
|
||||||
response::video_details::CommentListItem::CommentRenderer(comment) => {
|
response::video_details::CommentListItem::CommentRenderer(comment) => {
|
||||||
comments.push(map_comment(
|
let mut res = map_comment(
|
||||||
comment,
|
comment,
|
||||||
None,
|
None,
|
||||||
response::video_details::CommentPriority::RenderingPriorityUnknown,
|
response::video_details::CommentPriority::RenderingPriorityUnknown,
|
||||||
lang,
|
lang,
|
||||||
&mut warnings,
|
);
|
||||||
));
|
comments.push(res.c);
|
||||||
|
warnings.append(&mut res.warnings);
|
||||||
}
|
}
|
||||||
response::video_details::CommentListItem::CommentViewModel(vm) => {
|
response::video_details::CommentListItem::ContinuationItemRenderer {
|
||||||
if let Some(c) = map_comment_vm(
|
continuation_endpoint,
|
||||||
vm,
|
} => {
|
||||||
&mut mutations,
|
ctoken = Some(continuation_endpoint.continuation_command.token);
|
||||||
None,
|
|
||||||
response::video_details::CommentPriority::RenderingPriorityUnknown,
|
|
||||||
lang,
|
|
||||||
&mut warnings,
|
|
||||||
) {
|
|
||||||
comments.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response::video_details::CommentListItem::ContinuationItemRenderer(cont) => {
|
|
||||||
ctoken = Some(cont.token());
|
|
||||||
}
|
}
|
||||||
response::video_details::CommentListItem::CommentsHeaderRenderer { count_text } => {
|
response::video_details::CommentListItem::CommentsHeaderRenderer { count_text } => {
|
||||||
comment_count = count_text
|
comment_count = count_text
|
||||||
|
@ -500,50 +471,44 @@ fn map_recommendations(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_replies(
|
fn map_comment(
|
||||||
|
c: response::video_details::CommentRenderer,
|
||||||
replies: Option<response::video_details::Replies>,
|
replies: Option<response::video_details::Replies>,
|
||||||
|
priority: response::video_details::CommentPriority,
|
||||||
lang: Language,
|
lang: Language,
|
||||||
warnings: &mut Vec<String>,
|
) -> MapResult<Comment> {
|
||||||
) -> (Vec<Comment>, Option<String>) {
|
let mut warnings = Vec::new();
|
||||||
|
|
||||||
let mut reply_ctoken = None;
|
let mut reply_ctoken = None;
|
||||||
let replies = replies
|
let replies = replies.map(|replies| {
|
||||||
.map(|replies| {
|
|
||||||
replies
|
replies
|
||||||
.comment_replies_renderer
|
.comment_replies_renderer
|
||||||
.contents
|
.contents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|item| match item {
|
.filter_map(|item| match item {
|
||||||
response::video_details::CommentListItem::CommentRenderer(comment) => {
|
response::video_details::CommentListItem::CommentRenderer(comment) => {
|
||||||
Some(map_comment(
|
let mut res = map_comment(
|
||||||
comment,
|
comment,
|
||||||
None,
|
None,
|
||||||
response::video_details::CommentPriority::default(),
|
response::video_details::CommentPriority::default(),
|
||||||
lang,
|
lang,
|
||||||
warnings,
|
);
|
||||||
))
|
warnings.append(&mut res.warnings);
|
||||||
|
Some(res.c)
|
||||||
}
|
}
|
||||||
response::video_details::CommentListItem::ContinuationItemRenderer(cont) => {
|
response::video_details::CommentListItem::ContinuationItemRenderer {
|
||||||
reply_ctoken = Some(cont.token());
|
continuation_endpoint,
|
||||||
|
} => {
|
||||||
|
reply_ctoken = Some(continuation_endpoint.continuation_command.token);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
});
|
||||||
.unwrap_or_default();
|
|
||||||
(replies, reply_ctoken)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_comment(
|
MapResult {
|
||||||
c: response::video_details::CommentRenderer,
|
c: Comment {
|
||||||
replies: Option<response::video_details::Replies>,
|
|
||||||
priority: response::video_details::CommentPriority,
|
|
||||||
lang: Language,
|
|
||||||
warnings: &mut Vec<String>,
|
|
||||||
) -> Comment {
|
|
||||||
let (replies, reply_ctoken) = map_replies(replies, lang, warnings);
|
|
||||||
|
|
||||||
Comment {
|
|
||||||
id: c.comment_id,
|
id: c.comment_id,
|
||||||
text: c.content_text.into(),
|
text: c.content_text.into(),
|
||||||
author: match (c.author_endpoint, c.author_text) {
|
author: match (c.author_endpoint, c.author_text) {
|
||||||
|
@ -559,91 +524,32 @@ fn map_comment(
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
publish_date: timeago::parse_timeago_dt_or_warn(lang, &c.published_time_text, warnings),
|
publish_date: timeago::parse_timeago_dt_or_warn(
|
||||||
|
lang,
|
||||||
|
&c.published_time_text,
|
||||||
|
&mut warnings,
|
||||||
|
),
|
||||||
publish_date_txt: c.published_time_text,
|
publish_date_txt: c.published_time_text,
|
||||||
like_count: match c.vote_count {
|
like_count: match c.vote_count {
|
||||||
Some(txt) => util::parse_numeric_or_warn(&txt, warnings),
|
Some(txt) => util::parse_numeric_or_warn(&txt, &mut warnings),
|
||||||
None => Some(0),
|
None => Some(0),
|
||||||
},
|
},
|
||||||
reply_count: c.reply_count as u32,
|
reply_count: c.reply_count as u32,
|
||||||
replies: Paginator::new(Some(c.reply_count), replies, reply_ctoken),
|
replies: replies
|
||||||
|
.map(|items| Paginator::new(Some(c.reply_count), items, reply_ctoken))
|
||||||
|
.unwrap_or_default(),
|
||||||
by_owner: c.author_is_channel_owner,
|
by_owner: c.author_is_channel_owner,
|
||||||
pinned: priority.into(),
|
pinned: priority
|
||||||
|
== response::video_details::CommentPriority::RenderingPriorityPinnedComment,
|
||||||
hearted: c
|
hearted: c
|
||||||
.action_buttons
|
.action_buttons
|
||||||
.comment_action_buttons_renderer
|
.comment_action_buttons_renderer
|
||||||
.creator_heart
|
.creator_heart
|
||||||
.map(|h| h.creator_heart_renderer.is_hearted)
|
.map(|h| h.creator_heart_renderer.is_hearted)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_comment_vm(
|
|
||||||
vm: response::video_details::CommentViewModel,
|
|
||||||
mutations: &mut HashMap<String, response::video_details::Payload>,
|
|
||||||
replies: Option<response::video_details::Replies>,
|
|
||||||
priority: response::video_details::CommentPriority,
|
|
||||||
lang: Language,
|
|
||||||
warnings: &mut Vec<String>,
|
|
||||||
) -> Option<Comment> {
|
|
||||||
let (replies, reply_ctoken) = map_replies(replies, lang, warnings);
|
|
||||||
|
|
||||||
let ce = if let Some(Payload::CommentEntityPayload(ce)) = mutations.remove(&vm.comment_key) {
|
|
||||||
ce
|
|
||||||
} else {
|
|
||||||
warnings.push(format!(
|
|
||||||
"comment `{}` does not have entity payload (key: `{}`)",
|
|
||||||
vm.comment_id, vm.comment_key
|
|
||||||
));
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let hearted = if let Some(Payload::EngagementToolbarStateEntityPayload { heart_state }) =
|
|
||||||
mutations.get(&vm.toolbar_state_key)
|
|
||||||
{
|
|
||||||
(*heart_state).into()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut parse_num = |s: &str| -> Option<u32> {
|
|
||||||
if s.is_empty() || s == " " {
|
|
||||||
Some(0)
|
|
||||||
} else {
|
|
||||||
util::parse_large_numstr_or_warn(s, lang, warnings)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let reply_count = parse_num(&ce.toolbar.reply_count).unwrap_or_default();
|
|
||||||
|
|
||||||
Some(Comment {
|
|
||||||
id: vm.comment_id,
|
|
||||||
text: ce.properties.content.into(),
|
|
||||||
by_owner: ce.author.as_ref().map(|a| a.is_creator).unwrap_or_default(),
|
|
||||||
author: ce.author.map(|a| ChannelTag {
|
|
||||||
id: a.channel_id,
|
|
||||||
name: a.display_name,
|
|
||||||
avatar: ce.avatar.image.into(),
|
|
||||||
verification: if a.is_artist {
|
|
||||||
Verification::Artist
|
|
||||||
} else if a.is_verified {
|
|
||||||
Verification::Verified
|
|
||||||
} else {
|
|
||||||
Verification::None
|
|
||||||
},
|
},
|
||||||
subscriber_count: None,
|
|
||||||
}),
|
|
||||||
like_count: parse_num(&ce.toolbar.like_count_notliked),
|
|
||||||
reply_count,
|
|
||||||
replies: Paginator::new(Some(reply_count.into()), replies, reply_ctoken),
|
|
||||||
publish_date: timeago::parse_timeago_dt_or_warn(
|
|
||||||
lang,
|
|
||||||
&ce.properties.published_time,
|
|
||||||
warnings,
|
warnings,
|
||||||
),
|
}
|
||||||
publish_date_txt: ce.properties.published_time,
|
|
||||||
pinned: priority.into(),
|
|
||||||
hearted,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -708,8 +614,6 @@ mod tests {
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case::top("top")]
|
#[case::top("top")]
|
||||||
#[case::latest("latest")]
|
#[case::latest("latest")]
|
||||||
#[case::frameworkupd("20240401_frameworkupd")]
|
|
||||||
#[case::frameworkupd_reply("20240401_frameworkupd_reply")]
|
|
||||||
fn map_comments(#[case] name: &str) {
|
fn map_comments(#[case] name: &str) {
|
||||||
let json_path = path!(*TESTFILES / "video_details" / format!("comments_{name}.json"));
|
let json_path = path!(*TESTFILES / "video_details" / format!("comments_{name}.json"));
|
||||||
let json_file = File::open(json_path).unwrap();
|
let json_file = File::open(json_path).unwrap();
|
||||||
|
|
|
@ -20,13 +20,6 @@ where
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum GoodOrError<T> {
|
|
||||||
Good(T),
|
|
||||||
Error(serde_json::Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SeqVisitor<T>(PhantomData<T>);
|
struct SeqVisitor<T>(PhantomData<T>);
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for SeqVisitor<T>
|
impl<'de, T> Visitor<'de> for SeqVisitor<T>
|
||||||
|
@ -46,16 +39,14 @@ where
|
||||||
let mut values = Vec::with_capacity(seq.size_hint().unwrap_or_default());
|
let mut values = Vec::with_capacity(seq.size_hint().unwrap_or_default());
|
||||||
let mut warnings = Vec::new();
|
let mut warnings = Vec::new();
|
||||||
|
|
||||||
while let Some(value) = seq.next_element()? {
|
loop {
|
||||||
match value {
|
match seq.next_element::<T>() {
|
||||||
GoodOrError::<T>::Good(value) => {
|
Ok(val) => match val {
|
||||||
values.push(value);
|
Some(val) => values.push(val),
|
||||||
}
|
None => break,
|
||||||
GoodOrError::<T>::Error(value) => {
|
},
|
||||||
warnings.push(format!(
|
Err(e) => {
|
||||||
"error deserializing item: {}",
|
warnings.push(format!("error deserializing item: {e}"));
|
||||||
serde_json::to_string(&value).unwrap_or_default()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,8 +177,8 @@ mod tests {
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(res.items.warnings, @r###"
|
insta::assert_debug_snapshot!(res.items.warnings, @r###"
|
||||||
[
|
[
|
||||||
"error deserializing item: {\"xyz\":\"i2\"}",
|
"error deserializing item: missing field `name` at line 1 column 40",
|
||||||
"error deserializing item: {\"namra\":\"i4\"}",
|
"error deserializing item: missing field `name` at line 1 column 73",
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue