diff --git a/README.md b/README.md
index a3c77b05..b30944ff 100644
--- a/README.md
+++ b/README.md
@@ -248,7 +248,7 @@ const yt = await Innertube.create({
Methods
- * [.getInfo(video_id, client?)](#getinfo)
+ * [.getInfo(target, client?)](#getinfo)
* [.getBasicInfo(video_id, client?)](#getbasicinfo)
* [.search(query, filters?)](#search)
* [.getSearchSuggestions(query)](#getsearchsuggestions)
@@ -273,7 +273,7 @@ const yt = await Innertube.create({
-### getInfo(video_id, client?)
+### getInfo(target, client?)
Retrieves video info, including playback data and even layout elements such as menus, buttons, etc — all nicely parsed.
@@ -281,7 +281,7 @@ Retrieves video info, including playback data and even layout elements such as m
| Param | Type | Description |
| --- | --- | --- |
-| video_id | `string` | The id of the video |
+| target | `string` \| `NavigationEndpoint` | If `string`, the id of the video. If `NavigationEndpoint`, the endpoint of watchable elements such as `Video`, `Mix` and `Playlist`. To clarify, valid endpoints have payloads containing at least `videoId` and optionally `playlistId`, `params` and `index`. |
| client? | `InnerTubeClient` | `WEB`, `ANDROID`, `YTMUSIC`, `YTMUSIC_ANDROID` or `TV_EMBEDDED` |
@@ -321,6 +321,9 @@ Retrieves video info, including playback data and even layout elements such as m
- `#addToWatchHistory()`
- Adds the video to the watch history.
+- `#autoplay_video_endpoint`
+ - Returns the endpoint of the video for Autoplay.
+
- `#page`
- Returns original InnerTube response (sanitized).
diff --git a/deno/package.json b/deno/package.json
index 9d626129..d9d9b6b9 100644
--- a/deno/package.json
+++ b/deno/package.json
@@ -1,6 +1,6 @@
{
"name": "youtubei.js",
- "version": "3.2.0",
+ "version": "3.3.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",
diff --git a/deno/src/Innertube.ts b/deno/src/Innertube.ts
index 2e1c07c3..01f1b500 100644
--- a/deno/src/Innertube.ts
+++ b/deno/src/Innertube.ts
@@ -31,7 +31,7 @@ import type Format from './parser/classes/misc/Format.ts';
import type { ApiResponse } from './core/Actions.ts';
import type { IBrowseResponse, IParsedResponse } from './parser/types/index.ts';
import type { DownloadOptions, FormatOptions } from './utils/FormatUtils.ts';
-import { generateRandomString, throwIfMissing } from './utils/Utils.ts';
+import { generateRandomString, InnertubeError, throwIfMissing } from './utils/Utils.ts';
export type InnertubeConfig = SessionOptions;
@@ -72,16 +72,48 @@ class Innertube {
/**
* Retrieves video info.
- * @param video_id - The video id.
+ * @param target - The video id or `NavigationEndpoint`.
* @param client - The client to use.
*/
- async getInfo(video_id: string, client?: InnerTubeClient): Promise {
- throwIfMissing({ video_id });
+ async getInfo(target: string | NavigationEndpoint, client?: InnerTubeClient): Promise {
+ throwIfMissing({ target });
+
+ let payload: {
+ videoId: string,
+ playlistId?: string,
+ params?: string,
+ playlistIndex?: number
+ };
+
+ if (target instanceof NavigationEndpoint) {
+ const video_id = target.payload?.videoId;
+ if (!video_id) {
+ throw new InnertubeError('Missing video id in endpoint payload.', target);
+ }
+ payload = {
+ videoId: video_id
+ };
+ if (target.payload.playlistId) {
+ payload.playlistId = target.payload.playlistId;
+ }
+ if (target.payload.params) {
+ payload.params = target.payload.params;
+ }
+ if (target.payload.index) {
+ payload.playlistIndex = target.payload.index;
+ }
+ } else if (typeof target === 'string') {
+ payload = {
+ videoId: target
+ };
+ } else {
+ throw new InnertubeError('Invalid target, expected either a video id or a valid NavigationEndpoint', target);
+ }
const cpn = generateRandomString(16);
- const initial_info = this.actions.getVideoInfo(video_id, cpn, client);
- const continuation = this.actions.execute('/next', { videoId: video_id });
+ const initial_info = this.actions.getVideoInfo(payload.videoId, cpn, client);
+ const continuation = this.actions.execute('/next', payload);
const response = await Promise.all([ initial_info, continuation ]);
return new VideoInfo(response, this.actions, this.session.player, cpn);
diff --git a/deno/src/core/Session.ts b/deno/src/core/Session.ts
index bae0066a..9b1356f3 100644
--- a/deno/src/core/Session.ts
+++ b/deno/src/core/Session.ts
@@ -4,7 +4,7 @@ import Actions from './Actions.ts';
import Player from './Player.ts';
import HTTPClient from '../utils/HTTPClient.ts';
-import { Platform, DeviceCategory, generateRandomString, getRandomUserAgent, InnertubeError, SessionError } from '../utils/Utils.ts';
+import { Platform, DeviceCategory, getRandomUserAgent, InnertubeError, SessionError } from '../utils/Utils.ts';
import OAuth, { Credentials, OAuthAuthErrorEventHandler, OAuthAuthEventHandler, OAuthAuthPendingEventHandler } from './OAuth.ts';
import Proto from '../proto/index.ts';
import { ICache } from '../types/Cache.ts';
@@ -232,7 +232,7 @@ export default class Session extends EventEmitterLike {
'user-agent': getRandomUserAgent('desktop'),
'accept': '*/*',
'referer': 'https://www.youtube.com/sw.js',
- 'cookie': `PREF=tz=${options.time_zone.replace('/', '.')}`
+ 'cookie': `PREF=tz=${options.time_zone.replace('/', '.')};VISITOR_INFO1_LIVE=${Constants.CLIENTS.WEB.STATIC_VISITOR_ID};`
}
});
@@ -294,7 +294,7 @@ export default class Session extends EventEmitterLike {
client_name: string;
enable_safety_mode: boolean
}): SessionData {
- const id = generateRandomString(11);
+ const id = Constants.CLIENTS.WEB.STATIC_VISITOR_ID;
const timestamp = Math.floor(Date.now() / 1000);
const context: Context = {
diff --git a/deno/src/parser/classes/ConversationBar.ts b/deno/src/parser/classes/ConversationBar.ts
new file mode 100644
index 00000000..11889cd8
--- /dev/null
+++ b/deno/src/parser/classes/ConversationBar.ts
@@ -0,0 +1,16 @@
+import { YTNode } from '../helpers.ts';
+import Parser, { RawNode } from '../index.ts';
+import Message from './Message.ts';
+
+class ConversationBar extends YTNode {
+ static type = 'ConversationBar';
+
+ availability_message: Message | null;
+
+ constructor(data: RawNode) {
+ super();
+ this.availability_message = Parser.parseItem(data.availabilityMessage, Message);
+ }
+}
+
+export default ConversationBar;
\ No newline at end of file
diff --git a/deno/src/parser/classes/DecoratedPlayerBar.ts b/deno/src/parser/classes/DecoratedPlayerBar.ts
index 2b22b678..d6f60795 100644
--- a/deno/src/parser/classes/DecoratedPlayerBar.ts
+++ b/deno/src/parser/classes/DecoratedPlayerBar.ts
@@ -2,6 +2,7 @@ import Parser from '../index.ts';
import { YTNode } from '../helpers.ts';
import type Button from './Button.ts';
import type MultiMarkersPlayerBar from './MultiMarkersPlayerBar.ts';
+import type { RawNode } from '../index.ts';
class DecoratedPlayerBar extends YTNode {
static type = 'DecoratedPlayerBar';
@@ -9,7 +10,7 @@ class DecoratedPlayerBar extends YTNode {
player_bar: MultiMarkersPlayerBar | null;
player_bar_action_button: Button | null;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
this.player_bar = Parser.parseItem(data.playerBar);
this.player_bar_action_button = Parser.parseItem