diff --git a/src/core/AccountManager.ts b/src/core/AccountManager.ts index b4ee468d..d385e34e 100644 --- a/src/core/AccountManager.ts +++ b/src/core/AccountManager.ts @@ -154,7 +154,7 @@ class AccountManager { const params = Proto.encodeChannelAnalyticsParams(info.channel_id); const response = await this.#actions.browse('FEanalytics_screen', { params, client: 'ANDROID' }); - return new Analytics(response.data); + return new Analytics(response); } } diff --git a/src/parser/classes/AnalyticsVodCarouselCard.js b/src/parser/classes/AnalyticsVodCarouselCard.js deleted file mode 100644 index cc617935..00000000 --- a/src/parser/classes/AnalyticsVodCarouselCard.js +++ /dev/null @@ -1,14 +0,0 @@ -import Video from './AnalyticsVideo'; -import { YTNode } from '../helpers'; - -class AnalyticsVodCarouselCard extends YTNode { - static type = 'AnalyticsVodCarouselCard'; - - constructor(data) { - super(); - this.title = data.title; - this.videos = data.videoCarouselData.videos.map((video) => new Video(video)); - } -} - -export default AnalyticsVodCarouselCard; \ No newline at end of file diff --git a/src/parser/classes/DataModelSection.js b/src/parser/classes/DataModelSection.js deleted file mode 100644 index 971e00eb..00000000 --- a/src/parser/classes/DataModelSection.js +++ /dev/null @@ -1,23 +0,0 @@ -import { YTNode } from '../helpers'; - -class DataModelSection extends YTNode { - static type = 'DataModelSection'; - - constructor(data) { - super(); - this.title = data.title; - this.subtitle = data.subtitle; - this.metric_value = data.metricValue; - this.comparison_indicator = data.comparisonIndicator; - - this.series_configuration = { - line_series: { - lines_data: data.seriesConfiguration.lineSeries.linesData, - domain_axis: data.seriesConfiguration.lineSeries.domainAxis, - measure_axis: data.seriesConfiguration.lineSeries.measureAxis - } - }; - } -} - -export default DataModelSection; \ No newline at end of file diff --git a/src/parser/classes/Element.js b/src/parser/classes/Element.js index dad8713e..e0626cb5 100644 --- a/src/parser/classes/Element.js +++ b/src/parser/classes/Element.js @@ -3,11 +3,12 @@ import { YTNode } from '../helpers'; class Element extends YTNode { static type = 'Element'; + model; constructor(data) { super(); const type = data.newElement.type.componentType; - return Parser.parse(type.model); + this.model = Parser.parse(type.model); } } diff --git a/src/parser/classes/NavigationEndpoint.ts b/src/parser/classes/NavigationEndpoint.ts index 5b7f3d6b..3e11a9f9 100644 --- a/src/parser/classes/NavigationEndpoint.ts +++ b/src/parser/classes/NavigationEndpoint.ts @@ -36,6 +36,11 @@ class NavigationEndpoint extends YTNode { constructor(data: any) { super(); + + // This is only present in Android nav endpoints + if (Reflect.has(data || {}, 'innertubeCommand')) + data = data.innertubeCommand; + const name = Object.keys(data || {}) .find((item) => item.endsWith('Endpoint') || diff --git a/src/parser/classes/AnalyticsMainAppKeyMetrics.js b/src/parser/classes/analytics/AnalyticsMainAppKeyMetrics.ts similarity index 63% rename from src/parser/classes/AnalyticsMainAppKeyMetrics.js rename to src/parser/classes/analytics/AnalyticsMainAppKeyMetrics.ts index f381e01d..230a20b2 100644 --- a/src/parser/classes/AnalyticsMainAppKeyMetrics.js +++ b/src/parser/classes/analytics/AnalyticsMainAppKeyMetrics.ts @@ -1,14 +1,17 @@ import DataModelSection from './DataModelSection'; -import { YTNode } from '../helpers'; +import { YTNode } from '../../helpers'; class AnalyticsMainAppKeyMetrics extends YTNode { static type = 'AnalyticsMainAppKeyMetrics'; - constructor(data) { + period: string; + sections: DataModelSection[]; + + constructor(data: any) { super(); this.period = data.cardData.periodLabel; const metrics_data = data.cardData.sections[0].analyticsKeyMetricsData; - this.sections = metrics_data.dataModel.sections.map((section) => new DataModelSection(section)); + this.sections = metrics_data.dataModel.sections.map((section: any) => new DataModelSection(section)); } } diff --git a/src/parser/classes/analytics/AnalyticsRoot.ts b/src/parser/classes/analytics/AnalyticsRoot.ts new file mode 100644 index 00000000..3a16d568 --- /dev/null +++ b/src/parser/classes/analytics/AnalyticsRoot.ts @@ -0,0 +1,45 @@ +import { YTNode } from '../../helpers'; + +class AnalyticsRoot extends YTNode { + static type = 'AnalyticsRoot'; + + title: string; + selected_card_index_key: string; + use_main_app_specs: boolean; + + table_cards: { + title: string; + rows: { + label: string; + display_value: string; + display_value_a11y: string; + bar_ratio: number; + bar_color: number; + bar_opacity: number; + }[]; + }[]; + + constructor(data: any) { + super(); + const cards = data.analyticsTableCarouselData.data.tableCards; + + this.title = data.analyticsTableCarouselData.carouselTitle; + this.selected_card_index_key = data.analyticsTableCarouselData.selectedCardIndexKey; + + this.table_cards = cards.map((card: any) => ({ + title: card.cardData.title, + rows: card.cardData.rows.map((row: any) => ({ + label: row.label, + display_value: row.displayValue, + display_value_a11y: row.displayValueA11y, + bar_ratio: row.barRatio, + bar_color: row.barColor, + bar_opacity: row.barOpacity + })) + })); + + this.use_main_app_specs = data.analyticsTableCarouselData.useMainAppSpecs; + } +} + +export default AnalyticsRoot; \ No newline at end of file diff --git a/src/parser/classes/analytics/AnalyticsShortsCarouselCard.ts b/src/parser/classes/analytics/AnalyticsShortsCarouselCard.ts new file mode 100644 index 00000000..99002a2f --- /dev/null +++ b/src/parser/classes/analytics/AnalyticsShortsCarouselCard.ts @@ -0,0 +1,25 @@ +import { YTNode } from '../../helpers'; +import NavigationEndpoint from '../NavigationEndpoint'; + +class AnalyticsShortsCarouselCard extends YTNode { + static type = 'AnalyticsShortsCarouselCard'; + + title: string; + shorts: { + description: string; + thumbnail_url: string; + endpoint: NavigationEndpoint; + }[]; + + constructor(data: any) { + super(); + this.title = data.title; + this.shorts = data.shortsCarouselData.shorts.map((short: any) => ({ + description: short.shortsDescription, + thumbnail_url: short.thumbnailUrl, + endpoint: new NavigationEndpoint(short.videoEndpoint) + })); + } +} + +export default AnalyticsShortsCarouselCard; \ No newline at end of file diff --git a/src/parser/classes/AnalyticsVideo.js b/src/parser/classes/analytics/AnalyticsVideo.ts similarity index 58% rename from src/parser/classes/AnalyticsVideo.js rename to src/parser/classes/analytics/AnalyticsVideo.ts index d924cc04..71c492a5 100644 --- a/src/parser/classes/AnalyticsVideo.js +++ b/src/parser/classes/analytics/AnalyticsVideo.ts @@ -1,10 +1,19 @@ -import Thumbnail from './misc/Thumbnail'; -import { YTNode } from '../helpers'; +import Thumbnail from '../misc/Thumbnail'; +import { YTNode } from '../../helpers'; class AnalyticsVideo extends YTNode { static type = 'AnalyticsVideo'; - constructor(data) { + title: string; + metadata: { + views: string; + published: string; + thumbnails: Thumbnail[]; + duration: string; + is_short: boolean; + }; + + constructor(data: any) { super(); this.title = data.videoTitle; diff --git a/src/parser/classes/analytics/AnalyticsVodCarouselCard.ts b/src/parser/classes/analytics/AnalyticsVodCarouselCard.ts new file mode 100644 index 00000000..4ebb6d2b --- /dev/null +++ b/src/parser/classes/analytics/AnalyticsVodCarouselCard.ts @@ -0,0 +1,17 @@ +import Video from './AnalyticsVideo'; +import { YTNode } from '../../helpers'; + +class AnalyticsVodCarouselCard extends YTNode { + static type = 'AnalyticsVodCarouselCard'; + + title: string; + videos: Video[]; + + constructor(data: any) { + super(); + this.title = data.title; + this.videos = data.videoCarouselData.videos.map((video: any) => new Video(video)); + } +} + +export default AnalyticsVodCarouselCard; \ No newline at end of file diff --git a/src/parser/classes/CtaGoToCreatorStudio.js b/src/parser/classes/analytics/CtaGoToCreatorStudio.ts similarity index 59% rename from src/parser/classes/CtaGoToCreatorStudio.js rename to src/parser/classes/analytics/CtaGoToCreatorStudio.ts index 22a3140d..c43177cf 100644 --- a/src/parser/classes/CtaGoToCreatorStudio.js +++ b/src/parser/classes/analytics/CtaGoToCreatorStudio.ts @@ -1,9 +1,12 @@ -import { YTNode } from '../helpers'; +import { YTNode } from '../../helpers'; class CtaGoToCreatorStudio extends YTNode { static type = 'CtaGoToCreatorStudio'; - constructor(data) { + title: string; + use_new_specs: boolean; + + constructor(data: any) { super(); this.title = data.buttonLabel; this.use_new_specs = data.useNewSpecs; diff --git a/src/parser/classes/analytics/DataModelSection.ts b/src/parser/classes/analytics/DataModelSection.ts new file mode 100644 index 00000000..13ee55db --- /dev/null +++ b/src/parser/classes/analytics/DataModelSection.ts @@ -0,0 +1,72 @@ +import { YTNode } from '../../helpers'; + +class DataModelSection extends YTNode { + static type = 'DataModelSection'; + + title: string; + subtitle: string; + metric_value: string; + + comparison_indicator: { + trend: string; + }; + + series_configuration: { + line_series: { + lines_data: { + x: number[]; + y: number[]; + style: { + line_width: number; + line_color: number; + } + } + domain_axis: { + tick_values: number[]; + custom_formatter: { + labels: string[]; + } + } + measure_axis: { + tick_values: number[]; + custom_formatter: { + labels: string[]; + } + } + } + }; + + constructor(data: any) { + super(); + + this.title = data.title; + this.subtitle = data.subtitle; + this.metric_value = data.metricValue; + this.comparison_indicator = data.comparisonIndicator; + + const line_series = data.seriesConfiguration.lineSeries; + + this.series_configuration = { + line_series: { + lines_data: { + x: line_series.linesData[0].x, + y: line_series.linesData[0].y, + style: { + line_width: line_series.linesData[0].style.lineWidth, + line_color: line_series.linesData[0].style.lineColor + } + }, + domain_axis: { + tick_values: line_series.domainAxis.tickValues, + custom_formatter: line_series.domainAxis.customFormatter + }, + measure_axis: { + tick_values: line_series.measureAxis.tickValues, + custom_formatter: line_series.measureAxis.customFormatter + } + } + }; + } +} + +export default DataModelSection; \ No newline at end of file diff --git a/src/parser/map.ts b/src/parser/map.ts index 54202984..fc87ecd5 100644 --- a/src/parser/map.ts +++ b/src/parser/map.ts @@ -4,9 +4,13 @@ import { YTNodeConstructor } from './helpers'; import { default as AppendContinuationItemsAction } from './classes/actions/AppendContinuationItemsAction'; import { default as OpenPopupAction } from './classes/actions/OpenPopupAction'; -import { default as AnalyticsMainAppKeyMetrics } from './classes/AnalyticsMainAppKeyMetrics'; -import { default as AnalyticsVideo } from './classes/AnalyticsVideo'; -import { default as AnalyticsVodCarouselCard } from './classes/AnalyticsVodCarouselCard'; +import { default as AnalyticsMainAppKeyMetrics } from './classes/analytics/AnalyticsMainAppKeyMetrics'; +import { default as AnalyticsRoot } from './classes/analytics/AnalyticsRoot'; +import { default as AnalyticsShortsCarouselCard } from './classes/analytics/AnalyticsShortsCarouselCard'; +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 BackstageImage } from './classes/BackstageImage'; import { default as BackstagePost } from './classes/BackstagePost'; import { default as BackstagePostThread } from './classes/BackstagePostThread'; @@ -42,8 +46,6 @@ import { default as CompactMix } from './classes/CompactMix'; import { default as CompactPlaylist } from './classes/CompactPlaylist'; import { default as CompactVideo } from './classes/CompactVideo'; import { default as ContinuationItem } from './classes/ContinuationItem'; -import { default as CtaGoToCreatorStudio } from './classes/CtaGoToCreatorStudio'; -import { default as DataModelSection } from './classes/DataModelSection'; import { default as DidYouMean } from './classes/DidYouMean'; import { default as DownloadButton } from './classes/DownloadButton'; import { default as Element } from './classes/Element'; @@ -234,8 +236,12 @@ const map: Record = { AppendContinuationItemsAction, OpenPopupAction, AnalyticsMainAppKeyMetrics, + AnalyticsRoot, + AnalyticsShortsCarouselCard, AnalyticsVideo, AnalyticsVodCarouselCard, + CtaGoToCreatorStudio, + DataModelSection, BackstageImage, BackstagePost, BackstagePostThread, @@ -271,8 +277,6 @@ const map: Record = { CompactPlaylist, CompactVideo, ContinuationItem, - CtaGoToCreatorStudio, - DataModelSection, DidYouMean, DownloadButton, Element, diff --git a/src/parser/youtube/Analytics.js b/src/parser/youtube/Analytics.js deleted file mode 100644 index 48bb60de..00000000 --- a/src/parser/youtube/Analytics.js +++ /dev/null @@ -1,18 +0,0 @@ -import Parser from '../index'; - -class Analytics { - #page; - - /** - * @param {object} response - API response. - */ - constructor(response) { - this.#page = Parser.parseResponse(response); - this.sections = this.#page.contents_memo.get('Element'); - } - - get page() { - return this.#page; - } -} -export default Analytics; diff --git a/src/parser/youtube/Analytics.ts b/src/parser/youtube/Analytics.ts new file mode 100644 index 00000000..1ca1f838 --- /dev/null +++ b/src/parser/youtube/Analytics.ts @@ -0,0 +1,20 @@ +import Parser, { ParsedResponse } from '..'; +import { AxioslikeResponse } from '../../core/Actions'; +import Element from '../classes/Element'; + +class Analytics { + #page; + sections; + + constructor(response: AxioslikeResponse) { + this.#page = Parser.parseResponse(response.data); + this.sections = this.#page.contents_memo?.get('Element') + ?.map((el) => el.as(Element).model.item()); + } + + get page(): ParsedResponse { + return this.#page; + } +} + +export default Analytics;