feat: add parsers for TimeWatched

This commit is contained in:
LuanRT
2022-08-24 06:13:19 -03:00
parent c000bd8d5f
commit 541cdc455f
11 changed files with 159 additions and 32 deletions

View File

@@ -1,8 +1,10 @@
import { throwIfMissing, findNode } from '../utils/Utils';
import Constants from '../utils/Constants';
import Analytics from '../parser/youtube/Analytics';
import Proto from '../proto/index';
import Actions from './Actions';
import Constants from '../utils/Constants';
import { throwIfMissing, findNode } from '../utils/Utils';
import Analytics from '../parser/youtube/Analytics';
import TimeWatched from '../parser/youtube/TimeWatched';
class AccountManager {
#actions;
@@ -129,20 +131,12 @@ class AccountManager {
* Retrieves time watched statistics.
*/
async getTimeWatched() {
const response = await this.#actions.browse('SPtime_watched', { client: 'ANDROID' });
const rows: any[] = findNode(response.data, 'contents', 'statRowRenderer', 11, false);
const response = await this.#actions.execute('/browse', {
browseId: 'SPtime_watched',
client: 'ANDROID'
});
const stats = rows.map((row: any) => {
const renderer = row.statRowRenderer;
if (renderer) {
return {
title: renderer.title.runs.map((run: any) => run.text).join(''),
time: renderer.contents.runs.map((run: any) => run.text).join('')
};
}
}).filter((stat: any) => stat);
return stats;
return new TimeWatched(response);
}
/**

View File

@@ -689,6 +689,11 @@ class Actions {
if (!args.protobuf) {
data = { ...args };
if (Reflect.has(data, 'browseId')) {
if (this.#needsLogin(data.browseId) && !this.#session.logged_in)
throw new InnertubeError('You are not signed in');
}
if (Reflect.has(data, 'parse'))
delete data.parse;

View File

@@ -1,15 +1,22 @@
import Parser from '../index';
import ChildElement from './misc/ChildElement';
import { YTNode } from '../helpers';
class Element extends YTNode {
static type = 'Element';
model;
child_elements?: ChildElement[];
constructor(data: any) {
super();
const type = data.newElement.type.componentType;
this.model = Parser.parse(type.model);
this.model = Parser.parse(type?.model);
if (data.newElement?.childElements) {
this.child_elements = data.newElement?.childElements?.map((el: any) => new ChildElement(el)) || null;
}
}
}

View File

@@ -0,0 +1,38 @@
import Text from './misc/Text';
import NavigationEndpoint from './NavigationEndpoint';
import { YTNode } from '../helpers';
class SettingBoolean extends YTNode {
static type = 'SettingBoolean';
title?: Text;
summary?: Text;
enable_endpoint?: NavigationEndpoint;
disable_endpoint?: NavigationEndpoint;
item_id: string;
constructor(data: any) {
super();
if (data.title) {
this.title = new Text(data.title);
}
if (data.summary) {
this.summary = new Text(data.summary);
}
if (data.enableServiceEndpoint) {
this.enable_endpoint = new NavigationEndpoint(data.enableServiceEndpoint);
}
if (data.disableServiceEndpoint) {
this.disable_endpoint = new NavigationEndpoint(data.disableServiceEndpoint);
}
this.item_id = data.itemId;
}
}
export default SettingBoolean;

View File

@@ -0,0 +1,18 @@
import Text from './misc/Text';
import { YTNode } from '../helpers';
class SimpleTextSection extends YTNode {
static type = 'SimpleTextSection';
lines: Text[];
style: string;
constructor(data: any) {
super();
this.lines = data.lines.map((line: any) => new Text(line));
this.style = data.layoutStyle;
}
}
export default SimpleTextSection;

View File

@@ -5,12 +5,18 @@ class AnalyticsVodCarouselCard extends YTNode {
static type = 'AnalyticsVodCarouselCard';
title: string;
videos: Video[];
videos: Video[] | null;
no_data_message?: string;
constructor(data: any) {
super();
this.title = data.title;
this.videos = data.videoCarouselData.videos.map((video: any) => new Video(video));
if (data.noDataMessage) {
this.no_data_message = data.noDataMessage;
}
this.videos = data.videoCarouselData?.videos.map((video: any) => new Video(video)) || null;
}
}

View File

@@ -0,0 +1,18 @@
import Text from '../misc/Text';
import { YTNode } from '../../helpers';
class StatRow extends YTNode {
static type = 'StatRow';
title: Text;
contents: Text;
constructor(data: any) {
super();
this.title = new Text(data.title);
this.contents = new Text(data.contents);
}
}
export default StatRow;

View File

@@ -0,0 +1,18 @@
class ChildElement {
static type = 'ChildElement';
text: string | null;
properties;
child_elements?: ChildElement[];
constructor(data: any) {
this.text = data.type.textType?.text?.content || null;
this.properties = data.properties;
if (data.childElements) {
this.child_elements = data.childElements.map((el: any) => new ChildElement(el));
}
}
}
export default ChildElement;

View File

@@ -11,6 +11,7 @@ import { default as AnalyticsVideo } from './classes/analytics/AnalyticsVideo';
import { default as AnalyticsVodCarouselCard } from './classes/analytics/AnalyticsVodCarouselCard';
import { default as CtaGoToCreatorStudio } from './classes/analytics/CtaGoToCreatorStudio';
import { default as DataModelSection } from './classes/analytics/DataModelSection';
import { default as StatRow } from './classes/analytics/StatRow';
import { default as AutomixPreviewVideo } from './classes/AutomixPreviewVideo';
import { default as BackstageImage } from './classes/BackstageImage';
import { default as BackstagePost } from './classes/BackstagePost';
@@ -192,10 +193,12 @@ import { default as SearchSuggestion } from './classes/SearchSuggestion';
import { default as SearchSuggestionsSection } from './classes/SearchSuggestionsSection';
import { default as SecondarySearchContainer } from './classes/SecondarySearchContainer';
import { default as SectionList } from './classes/SectionList';
import { default as SettingBoolean } from './classes/SettingBoolean';
import { default as Shelf } from './classes/Shelf';
import { default as ShowingResultsFor } from './classes/ShowingResultsFor';
import { default as SimpleCardContent } from './classes/SimpleCardContent';
import { default as SimpleCardTeaser } from './classes/SimpleCardTeaser';
import { default as SimpleTextSection } from './classes/SimpleTextSection';
import { default as SingleActionEmergencySupport } from './classes/SingleActionEmergencySupport';
import { default as SingleColumnBrowseResults } from './classes/SingleColumnBrowseResults';
import { default as SingleColumnMusicWatchNextResults } from './classes/SingleColumnMusicWatchNextResults';
@@ -252,6 +255,7 @@ const map: Record<string, YTNodeConstructor> = {
AnalyticsVodCarouselCard,
CtaGoToCreatorStudio,
DataModelSection,
StatRow,
AutomixPreviewVideo,
BackstageImage,
BackstagePost,
@@ -433,10 +437,12 @@ const map: Record<string, YTNodeConstructor> = {
SearchSuggestionsSection,
SecondarySearchContainer,
SectionList,
SettingBoolean,
Shelf,
ShowingResultsFor,
SimpleCardContent,
SimpleCardTeaser,
SimpleTextSection,
SingleActionEmergencySupport,
SingleColumnBrowseResults,
SingleColumnMusicWatchNextResults,

View File

@@ -0,0 +1,30 @@
import Parser, { ParsedResponse } from '..';
import { AxioslikeResponse } from '../../core/Actions';
import ItemSection from '../classes/ItemSection';
import SingleColumnBrowseResults from '../classes/SingleColumnBrowseResults';
import SectionList from '../classes/SectionList';
import { InnertubeError } from '../../utils/Utils';
class TimeWatched {
#page;
contents;
constructor(response: AxioslikeResponse) {
this.#page = Parser.parseResponse(response.data);
const tab = this.#page.contents.item().as(SingleColumnBrowseResults).tabs.get({ selected: true });
if (!tab)
throw new InnertubeError('Could not find target tab.');
this.contents = tab.content?.as(SectionList).contents.array().as(ItemSection);
}
get page(): ParsedResponse {
return this.#page;
}
}
export default TimeWatched;

View File

@@ -205,19 +205,6 @@ export function hasKeys<T extends object, R extends (keyof T)[]>(params: T, ...k
return true;
}
/**
* Turns the ntoken transform data into a valid json array
*/
export function refineNTokenData(data: string) {
return data
.replace(/function\(d,e\)/g, '"function(d,e)').replace(/function\(d\)/g, '"function(d)')
.replace(/function\(\)/g, '"function()').replace(/function\(d,e,f\)/g, '"function(d,e,f)')
.replace(/\[function\(d,e,f\)/g, '["function(d,e,f)').replace(/,b,/g, ',"b",')
.replace(/,b/g, ',"b"').replace(/b,/g, '"b",').replace(/b]/g, '"b"]')
.replace(/\[b/g, '["b"').replace(/}]/g, '"]').replace(/},/g, '}",')
.replace(/""/g, '').replace(/length]\)}"/g, 'length])}');
}
export function uuidv4() {
if (getRuntime() === 'node') {
return Reflect.get(module, 'require')('crypto').webcrypto.randomUUID();