mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-13 09:32:12 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4862c35cee | ||
|
|
2eed1726d5 | ||
|
|
8b69587787 | ||
|
|
ed7be2a675 |
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## [6.1.0](https://github.com/LuanRT/YouTube.js/compare/v6.0.2...v6.1.0) (2023-08-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **parser:** Add `AlertWithButton` ([#486](https://github.com/LuanRT/YouTube.js/issues/486)) ([8b69587](https://github.com/LuanRT/YouTube.js/commit/8b6958778721ba274283f641779fb60bc6f42cd2))
|
||||
* **parser:** Add `ChannelHeaderLinksView` ([#484](https://github.com/LuanRT/YouTube.js/issues/484)) ([ed7be2a](https://github.com/LuanRT/YouTube.js/commit/ed7be2a675cf1ec663e743e90db6260c97546739))
|
||||
* **parser:** Add `CompactMovie` ([#487](https://github.com/LuanRT/YouTube.js/issues/487)) ([2eed172](https://github.com/LuanRT/YouTube.js/commit/2eed1726d5bde7648af09273cc14ab4a315cb23e))
|
||||
|
||||
## [6.0.2](https://github.com/LuanRT/YouTube.js/compare/v6.0.1...v6.0.2) (2023-08-24)
|
||||
|
||||
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "6.0.2",
|
||||
"version": "6.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "youtubei.js",
|
||||
"version": "6.0.2",
|
||||
"version": "6.1.0",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "6.0.2",
|
||||
"version": "6.1.0",
|
||||
"description": "A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).",
|
||||
"type": "module",
|
||||
"types": "./dist/src/platform/lib.d.ts",
|
||||
|
||||
19
src/parser/classes/AlertWithButton.ts
Normal file
19
src/parser/classes/AlertWithButton.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import Button from './Button.js';
|
||||
import Text from './misc/Text.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import { Parser, type RawNode } from '../index.js';
|
||||
|
||||
export default class AlertWithButton extends YTNode {
|
||||
static type = 'AlertWithButton';
|
||||
|
||||
text: Text;
|
||||
alert_type: string;
|
||||
dismiss_button: Button | null;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.text = new Text(data.text);
|
||||
this.alert_type = data.type;
|
||||
this.dismiss_button = Parser.parseItem(data.dismissButton, Button);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { YTNode } from '../helpers.js';
|
||||
import Parser, { type RawNode } from '../index.js';
|
||||
import Button from './Button.js';
|
||||
import ChannelHeaderLinks from './ChannelHeaderLinks.js';
|
||||
import ChannelHeaderLinksView from './ChannelHeaderLinksView.js';
|
||||
import SubscribeButton from './SubscribeButton.js';
|
||||
import Author from './misc/Author.js';
|
||||
import Text from './misc/Text.js';
|
||||
@@ -18,7 +19,7 @@ export default class C4TabbedHeader extends YTNode {
|
||||
videos_count?: Text;
|
||||
sponsor_button?: Button | null;
|
||||
subscribe_button?: SubscribeButton | Button | null;
|
||||
header_links?: ChannelHeaderLinks | null;
|
||||
header_links?: ChannelHeaderLinks | ChannelHeaderLinksView | null;
|
||||
channel_handle?: Text;
|
||||
channel_id?: string;
|
||||
|
||||
@@ -58,7 +59,7 @@ export default class C4TabbedHeader extends YTNode {
|
||||
}
|
||||
|
||||
if (Reflect.has(data, 'headerLinks')) {
|
||||
this.header_links = Parser.parseItem(data.headerLinks, ChannelHeaderLinks);
|
||||
this.header_links = Parser.parseItem(data.headerLinks, [ ChannelHeaderLinks, ChannelHeaderLinksView ]);
|
||||
}
|
||||
|
||||
if (Reflect.has(data, 'channelHandleText')) {
|
||||
|
||||
22
src/parser/classes/ChannelHeaderLinksView.ts
Normal file
22
src/parser/classes/ChannelHeaderLinksView.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
|
||||
export default class ChannelHeaderLinksView extends YTNode {
|
||||
static type = 'ChannelHeaderLinksView';
|
||||
|
||||
first_link?: Text;
|
||||
more?: Text;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
if (Reflect.has(data, 'firstLink')) {
|
||||
this.first_link = Text.fromAttributed(data.firstLink);
|
||||
}
|
||||
|
||||
if (Reflect.has(data, 'more')) {
|
||||
this.more = Text.fromAttributed(data.more);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/parser/classes/CompactMovie.ts
Normal file
57
src/parser/classes/CompactMovie.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { YTNode, type ObservedArray } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import Parser from '../index.js';
|
||||
import Author from './misc/Author.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
import { timeToSeconds } from '../../utils/Utils.js';
|
||||
|
||||
export default class CompactMovie extends YTNode {
|
||||
static type = 'CompactMovie';
|
||||
|
||||
id: string;
|
||||
title: Text;
|
||||
top_metadata_items: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
thumbnail_overlays: ObservedArray<YTNode>;
|
||||
author: Author;
|
||||
|
||||
duration: {
|
||||
text: string;
|
||||
seconds: number;
|
||||
};
|
||||
|
||||
endpoint: NavigationEndpoint;
|
||||
badges: ObservedArray<YTNode>;
|
||||
use_vertical_poster: boolean;
|
||||
menu: Menu | null;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
console.log(data);
|
||||
const overlay_time_status = data.thumbnailOverlays
|
||||
.find((overlay: RawNode) => overlay.thumbnailOverlayTimeStatusRenderer)
|
||||
?.thumbnailOverlayTimeStatusRenderer.text || 'N/A';
|
||||
|
||||
this.id = data.videoId;
|
||||
this.title = new Text(data.title);
|
||||
|
||||
this.top_metadata_items = new Text(data.topMetadataItems);
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
this.author = new Author(data.shortBylineText);
|
||||
|
||||
const durationText = data.lengthText ? new Text(data.lengthText).toString() : new Text(overlay_time_status).toString();
|
||||
|
||||
this.duration = {
|
||||
text: durationText,
|
||||
seconds: timeToSeconds(durationText)
|
||||
};
|
||||
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.badges = Parser.parseArray(data.badges);
|
||||
this.use_vertical_poster = data.useVerticalPoster;
|
||||
this.menu = Parser.parseItem(data.menu, Menu);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export default class VideoSecondaryInfo extends YTNode {
|
||||
this.description = new Text(data.description);
|
||||
|
||||
if (Reflect.has(data, 'attributedDescription')) {
|
||||
this.description = new Text(this.#convertAttributedDescriptionToRuns(data.attributedDescription));
|
||||
this.description = Text.fromAttributed(data.attributedDescription);
|
||||
}
|
||||
|
||||
this.subscribe_button = Parser.parseItem(data.subscribeButton, [ SubscribeButton, Button ]);
|
||||
@@ -34,72 +34,4 @@ export default class VideoSecondaryInfo extends YTNode {
|
||||
this.default_expanded = data.defaultExpanded;
|
||||
this.description_collapsed_lines = data.descriptionCollapsedLines;
|
||||
}
|
||||
|
||||
#convertAttributedDescriptionToRuns(description: RawNode) {
|
||||
const runs: {
|
||||
text: string,
|
||||
navigationEndpoint?: RawNode,
|
||||
attachment?: RawNode
|
||||
}[] = [];
|
||||
|
||||
const content = description.content;
|
||||
const command_runs = description.commandRuns;
|
||||
|
||||
let last_end_index = 0;
|
||||
|
||||
if (command_runs) {
|
||||
for (const item of command_runs) {
|
||||
const length: number = item.length;
|
||||
const start_index: number = item.startIndex;
|
||||
|
||||
if (start_index > last_end_index) {
|
||||
runs.push({
|
||||
text: content.slice(last_end_index, start_index)
|
||||
});
|
||||
}
|
||||
|
||||
if (Reflect.has(item, 'onTap')) {
|
||||
let attachment = null;
|
||||
|
||||
if (Reflect.has(description, 'attachmentRuns')) {
|
||||
const attachment_runs = description.attachmentRuns;
|
||||
|
||||
for (const attatchment_run of attachment_runs) {
|
||||
if ((attatchment_run.startIndex - 2) == start_index) {
|
||||
attachment = attatchment_run;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment) {
|
||||
runs.push({
|
||||
text: content.slice(start_index, start_index + length),
|
||||
navigationEndpoint: item.onTap,
|
||||
attachment
|
||||
});
|
||||
} else {
|
||||
runs.push({
|
||||
text: content.slice(start_index, start_index + length),
|
||||
navigationEndpoint: item.onTap
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
last_end_index = start_index + length;
|
||||
}
|
||||
|
||||
if (last_end_index < content.length) {
|
||||
runs.push({
|
||||
text: content.slice(last_end_index)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
runs.push({
|
||||
text: content
|
||||
});
|
||||
}
|
||||
|
||||
return { runs };
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,74 @@ export default class Text {
|
||||
}
|
||||
}
|
||||
|
||||
static fromAttributed(data: RawNode): Text {
|
||||
const runs: {
|
||||
text: string,
|
||||
navigationEndpoint?: RawNode,
|
||||
attachment?: RawNode
|
||||
}[] = [];
|
||||
|
||||
const content = data.content;
|
||||
const command_runs = data.commandRuns;
|
||||
|
||||
let last_end_index = 0;
|
||||
|
||||
if (command_runs) {
|
||||
for (const item of command_runs) {
|
||||
const length: number = item.length;
|
||||
const start_index: number = item.startIndex;
|
||||
|
||||
if (start_index > last_end_index) {
|
||||
runs.push({
|
||||
text: content.slice(last_end_index, start_index)
|
||||
});
|
||||
}
|
||||
|
||||
if (Reflect.has(item, 'onTap')) {
|
||||
let attachment = null;
|
||||
|
||||
if (Reflect.has(data, 'attachmentRuns')) {
|
||||
const attachment_runs = data.attachmentRuns;
|
||||
|
||||
for (const attatchment_run of attachment_runs) {
|
||||
if ((attatchment_run.startIndex - 2) == start_index) {
|
||||
attachment = attatchment_run;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment) {
|
||||
runs.push({
|
||||
text: content.slice(start_index, start_index + length),
|
||||
navigationEndpoint: item.onTap,
|
||||
attachment
|
||||
});
|
||||
} else {
|
||||
runs.push({
|
||||
text: content.slice(start_index, start_index + length),
|
||||
navigationEndpoint: item.onTap
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
last_end_index = start_index + length;
|
||||
}
|
||||
|
||||
if (last_end_index < content.length) {
|
||||
runs.push({
|
||||
text: content.slice(last_end_index)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
runs.push({
|
||||
text: content
|
||||
});
|
||||
}
|
||||
|
||||
return new Text({ runs });
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the text to HTML.
|
||||
* @returns The HTML.
|
||||
|
||||
@@ -8,6 +8,7 @@ export { default as AccountSectionList } from './classes/AccountSectionList.js';
|
||||
export { default as AppendContinuationItemsAction } from './classes/actions/AppendContinuationItemsAction.js';
|
||||
export { default as OpenPopupAction } from './classes/actions/OpenPopupAction.js';
|
||||
export { default as Alert } from './classes/Alert.js';
|
||||
export { default as AlertWithButton } from './classes/AlertWithButton.js';
|
||||
export { default as AnalyticsMainAppKeyMetrics } from './classes/analytics/AnalyticsMainAppKeyMetrics.js';
|
||||
export { default as AnalyticsRoot } from './classes/analytics/AnalyticsRoot.js';
|
||||
export { default as AnalyticsShortsCarouselCard } from './classes/analytics/AnalyticsShortsCarouselCard.js';
|
||||
@@ -36,6 +37,7 @@ export { default as ChannelAboutFullMetadata } from './classes/ChannelAboutFullM
|
||||
export { default as ChannelAgeGate } from './classes/ChannelAgeGate.js';
|
||||
export { default as ChannelFeaturedContent } from './classes/ChannelFeaturedContent.js';
|
||||
export { default as ChannelHeaderLinks } from './classes/ChannelHeaderLinks.js';
|
||||
export { default as ChannelHeaderLinksView } from './classes/ChannelHeaderLinksView.js';
|
||||
export { default as ChannelMetadata } from './classes/ChannelMetadata.js';
|
||||
export { default as ChannelMobileHeader } from './classes/ChannelMobileHeader.js';
|
||||
export { default as ChannelOptions } from './classes/ChannelOptions.js';
|
||||
@@ -67,6 +69,7 @@ export { default as SponsorCommentBadge } from './classes/comments/SponsorCommen
|
||||
export { default as CompactChannel } from './classes/CompactChannel.js';
|
||||
export { default as CompactLink } from './classes/CompactLink.js';
|
||||
export { default as CompactMix } from './classes/CompactMix.js';
|
||||
export { default as CompactMovie } from './classes/CompactMovie.js';
|
||||
export { default as CompactPlaylist } from './classes/CompactPlaylist.js';
|
||||
export { default as CompactStation } from './classes/CompactStation.js';
|
||||
export { default as CompactVideo } from './classes/CompactVideo.js';
|
||||
|
||||
@@ -6,6 +6,7 @@ import PlayerCaptionsTracklist from './classes/PlayerCaptionsTracklist.js';
|
||||
import PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js';
|
||||
import PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js';
|
||||
import Alert from './classes/Alert.js';
|
||||
import AlertWithButton from './classes/AlertWithButton.js';
|
||||
|
||||
import type { IParsedResponse, IRawResponse, RawData, RawNode } from './types/index.js';
|
||||
|
||||
@@ -310,7 +311,7 @@ export function parseResponse<T extends IParsedResponse = IParsedResponse>(data:
|
||||
parsed_data.overlay = overlay;
|
||||
}
|
||||
|
||||
const alerts = parseArray(data.alerts, Alert);
|
||||
const alerts = parseArray(data.alerts, [ Alert, AlertWithButton ]);
|
||||
if (alerts.length) {
|
||||
parsed_data.alerts = alerts;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import type PlayerLiveStoryboardSpec from '../classes/PlayerLiveStoryboardSpec.j
|
||||
import type PlayerStoryboardSpec from '../classes/PlayerStoryboardSpec.js';
|
||||
import type VideoDetails from '../classes/misc/VideoDetails.js';
|
||||
import type Alert from '../classes/Alert.js';
|
||||
import type AlertWithButton from '../classes/AlertWithButton.js';
|
||||
import type NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import type PlayerAnnotationsExpanded from '../classes/PlayerAnnotationsExpanded.js';
|
||||
import type EngagementPanelSectionList from '../classes/EngagementPanelSectionList.js';
|
||||
@@ -44,7 +45,7 @@ export interface IParsedResponse {
|
||||
metadata?: SuperParsedResult<YTNode>;
|
||||
microformat?: YTNode;
|
||||
overlay?: YTNode;
|
||||
alerts?: ObservedArray<Alert>;
|
||||
alerts?: ObservedArray<Alert | AlertWithButton>;
|
||||
refinements?: string[];
|
||||
estimated_results?: number;
|
||||
player_overlays?: SuperParsedResult<YTNode>;
|
||||
@@ -122,7 +123,7 @@ export interface IBrowseResponse {
|
||||
header_memo?: Memo;
|
||||
metadata?: SuperParsedResult<YTNode>;
|
||||
microformat?: YTNode;
|
||||
alerts?: ObservedArray<Alert>;
|
||||
alerts?: ObservedArray<Alert | AlertWithButton>;
|
||||
sidebar?: YTNode;
|
||||
sidebar_memo?: Memo;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user