Skip to content

Commit 3c09033

Browse files
committed
add innerTube API, types and tests
1 parent 072324f commit 3c09033

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

src/types/innerTubeApi.model.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export interface innerTubeVideoDetails {
2+
"videoId": string,
3+
"title": string,
4+
"lengthSeconds": string, // yes, don't ask.
5+
"channelId": string,
6+
"isOwnerViewing": boolean,
7+
"shortDescription": string,
8+
"isCrawlable": boolean,
9+
"thumbnail": {
10+
"thumbnails": [{
11+
"url": string,
12+
"width": number,
13+
"height": number
14+
}
15+
]
16+
},
17+
"allowRatings": boolean,
18+
"viewCount": string, // yes, don't ask
19+
"author": string,
20+
"isPrivate": boolean,
21+
"isUnpluggedCorpus": boolean,
22+
"isLiveContent": boolean
23+
}

src/utils/innerTubeAPI.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import axios from "axios";
2+
import { innerTubeVideoDetails } from "../types/innerTubeApi.model";
3+
4+
export async function getPlayerData(videoID: string): Promise<innerTubeVideoDetails> {
5+
// start subrequest
6+
const url = "https://www.youtube.com/youtubei/v1/player";
7+
const data = {
8+
context: {
9+
client: {
10+
clientName: "WEB",
11+
clientVersion: "2.20211129.09.00"
12+
}
13+
},
14+
videoId: videoID
15+
};
16+
const result = await axios.post(url, data, {
17+
timeout: 3500
18+
});
19+
if (result.status === 200) {
20+
return result.data.videoDetails;
21+
} else {
22+
return Promise.reject(result.status);
23+
}
24+
}
25+
26+
export const getLength = (videoID: string): Promise<number> =>
27+
getPlayerData(videoID)
28+
.then(pData => Number(pData.lengthSeconds))
29+
.catch(err => err);

test/cases/innerTubeApi.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { config } from "../../src/config";
2+
import assert from "assert";
3+
// import { innerTubeVideoDetails } from "../../src/types/innerTubeApi.model";
4+
import { YouTubeAPI } from "../../src/utils/youtubeApi";
5+
import * as innerTube from "../../src/utils/innerTubeAPI";
6+
import { partialDeepEquals } from "../utils/partialDeepEquals";
7+
8+
9+
const videoID = "dQw4w9WgXcQ";
10+
const expected = { // partial type of innerTubeVideoDetails
11+
videoId: videoID,
12+
title: "Rick Astley - Never Gonna Give You Up (Official Music Video)",
13+
lengthSeconds: "212",
14+
channelId: "UCuAXFkgsw1L7xaCfnd5JJOw",
15+
isOwnerViewing: false,
16+
isCrawlable: true,
17+
allowRatings: true,
18+
author: "Rick Astley",
19+
isPrivate: false,
20+
isUnpluggedCorpus: false,
21+
isLiveContent: false
22+
};
23+
const currentViews = 1284257550;
24+
25+
describe("innertube API test", function() {
26+
it("should be able to get innerTube details", async () => {
27+
const result = await innerTube.getPlayerData(videoID);
28+
assert.ok(partialDeepEquals(result, expected));
29+
});
30+
it("Should have more views than current", async () => {
31+
const result = await innerTube.getPlayerData(videoID);
32+
assert.ok(Number(result.viewCount) >= currentViews);
33+
});
34+
it("Should have the same video duration from both endpoints", async () => {
35+
const playerData = await innerTube.getPlayerData(videoID);
36+
const length = await innerTube.getLength(videoID);
37+
assert.equal(Number(playerData.lengthSeconds), length);
38+
});
39+
it("Should have equivalent response from NewLeaf", async function () {
40+
if (!config.newLeafURLs || config.newLeafURLs.length <= 0 || config.newLeafURLs[0] == "placeholder") this.skip();
41+
const itResponse = await innerTube.getPlayerData(videoID);
42+
const newLeafResponse = await YouTubeAPI.listVideos(videoID, true);
43+
// validate videoID
44+
assert.strictEqual(itResponse.videoId, videoID);
45+
assert.strictEqual(newLeafResponse.data?.videoId, videoID);
46+
// validate description
47+
assert.strictEqual(itResponse.shortDescription, newLeafResponse.data?.description);
48+
// validate authorId
49+
assert.strictEqual(itResponse.channelId, newLeafResponse.data?.authorId);
50+
});
51+
});

0 commit comments

Comments
 (0)