Skip to content

Commit 3da9059

Browse files
committed
add: webParentTools client and update Android SDK version
1 parent 17a4616 commit 3da9059

3 files changed

Lines changed: 124 additions & 3 deletions

File tree

config.default.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,8 @@ export default {
273273
gl: 'US',
274274
clients: {
275275
search: ['Android'], // Clients used for searching tracks
276-
playback: ['AndroidVR', 'TV', 'WebEmbedded', 'IOS'], // Clients used for playback/streaming
277-
resolve: ['AndroidVR', 'TV', 'WebEmbedded', 'IOS', 'Web'], // Clients used for resolving detailed track information (channel, external links, etc.)
276+
playback: ['AndroidVR', 'TV', 'WebEmbedded', 'WebParentTools', 'IOS'], // Clients used for playback/streaming
277+
resolve: ['AndroidVR', 'TV', 'WebEmbedded', 'WebParentTools', 'IOS', 'Web'], // Clients used for resolving detailed track information (channel, external links, etc.)
278278
settings: {
279279
TV: {
280280
refreshToken: [""] // You can use a string "token" or an array ["token1", "token2"] for rotation/fallback

src/sources/youtube/clients/Android.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default class Android extends BaseClient {
2222
deviceModel: 'Pixel 6',
2323
osName: 'Android',
2424
osVersion: '14',
25-
androidSdkVersion: '30',
25+
androidSdkVersion: '34',
2626
hl: context.client.hl,
2727
gl: context.client.gl,
2828
visitorData: context.client.visitorData
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { makeRequest } from '../../../utils.js'
2+
import { BaseClient, checkURLType, YOUTUBE_CONSTANTS } from '../common.js'
3+
4+
export default class WebParentTools extends BaseClient {
5+
constructor(nodelink, oauth) {
6+
super(nodelink, 'WEB_PARENT_TOOLS', oauth)
7+
}
8+
9+
getClient(context) {
10+
return {
11+
client: {
12+
clientName: 'WEB_PARENT_TOOLS',
13+
clientVersion: '1.20220918',
14+
hl: context.client.hl,
15+
gl: context.client.gl,
16+
visitorData: context.client.visitorData,
17+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36,gzip(gfe)'
18+
},
19+
thirdParty: {
20+
embedUrl: 'https://www.youtube.com/'
21+
}
22+
}
23+
}
24+
25+
async resolve(url, _type, context, cipherManager) {
26+
const sourceName = 'youtube'
27+
const urlType = checkURLType(url, 'youtube')
28+
29+
switch (urlType) {
30+
case YOUTUBE_CONSTANTS.VIDEO:
31+
case YOUTUBE_CONSTANTS.SHORTS: {
32+
const idPattern = /(?:v=|\/shorts\/|youtu\.be\/)([^&?]+)/
33+
const videoIdMatch = url.match(idPattern)
34+
if (!videoIdMatch || !videoIdMatch[1]) {
35+
return {
36+
exception: {
37+
message: 'Invalid video URL.',
38+
severity: 'common',
39+
cause: 'Input'
40+
}
41+
}
42+
}
43+
const videoId = videoIdMatch[1]
44+
45+
const { body: playerResponse, statusCode } =
46+
await this._makePlayerRequest(videoId, context, {}, cipherManager)
47+
48+
if (statusCode !== 200) {
49+
return {
50+
exception: { message: `Failed to load video data. Status: ${statusCode}`, severity: 'common', cause: 'Upstream' }
51+
}
52+
}
53+
54+
return await this._handlePlayerResponse(
55+
playerResponse,
56+
sourceName,
57+
videoId
58+
)
59+
}
60+
61+
default:
62+
return { loadType: 'empty', data: {} }
63+
}
64+
}
65+
66+
async getTrackUrl(decodedTrack, context, cipherManager, itag) {
67+
const { body: playerResponse, statusCode } = await this._makePlayerRequest(
68+
decodedTrack.identifier,
69+
context,
70+
{},
71+
cipherManager
72+
)
73+
74+
if (statusCode !== 200) {
75+
return { exception: { message: `Failed to get player data. Status: ${statusCode}`, severity: 'common', cause: 'Upstream' } }
76+
}
77+
78+
return await this._extractStreamData(
79+
playerResponse,
80+
decodedTrack,
81+
context,
82+
cipherManager,
83+
itag
84+
)
85+
}
86+
87+
async _makePlayerRequest(videoId, context, headers, cipherManager) {
88+
const requestBody = {
89+
context: this.getClient(context),
90+
videoId: videoId,
91+
contentCheckOk: true,
92+
racyCheckOk: true,
93+
playbackContext: {
94+
contentPlaybackContext: {
95+
signatureTimestamp: 19700
96+
}
97+
}
98+
}
99+
100+
const response = await makeRequest(
101+
'https://www.youtube.com/youtubei/v1/player',
102+
{
103+
method: 'POST',
104+
headers: {
105+
'Content-Type': 'application/json',
106+
'User-Agent': this.getClient(context).client.userAgent,
107+
'X-YouTube-Client-Name': '88',
108+
'X-YouTube-Client-Version': '1.20220918',
109+
'X-Goog-Visitor-Id': context.client.visitorData,
110+
'Origin': 'https://www.youtube.com',
111+
'Referer': 'https://www.youtube.com/',
112+
...headers
113+
},
114+
body: requestBody,
115+
disableBodyCompression: true
116+
}
117+
)
118+
119+
return response
120+
}
121+
}

0 commit comments

Comments
 (0)