youtubei.js
Advanced tools
Comparing version 1.2.4 to 1.2.5
@@ -39,3 +39,3 @@ 'use strict'; | ||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/${engagement_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: args.video_id, data })).catch((error) => error); | ||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/${engagement_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, id: args.video_id, data })).catch((error) => error); | ||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; | ||
@@ -60,4 +60,4 @@ return { | ||
} | ||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/browse${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session })).catch((error) => error); | ||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/browse${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error); | ||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; | ||
@@ -96,3 +96,3 @@ return { | ||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/notification/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session })).catch((error) => error); | ||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/notification/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error); | ||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; | ||
@@ -110,3 +110,3 @@ if (action_type === 'modify_channel_preference') return { success: true, status_code: response.status }; | ||
switch (action_type) { | ||
case 'live_chat/send_message': | ||
case 'live_chat/send_message': | ||
data = { | ||
@@ -134,4 +134,4 @@ context: session.context, | ||
} | ||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, params: args.params })).catch((error) => error); | ||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, params: args.params })).catch((error) => error); | ||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; | ||
@@ -161,3 +161,3 @@ return { | ||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/next${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session })).catch((error) => error); | ||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/next${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error); | ||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; | ||
@@ -164,0 +164,0 @@ return { |
@@ -5,326 +5,262 @@ 'use strict'; | ||
const urls = { | ||
YT_BASE_URL: 'https://www.youtube.com', | ||
YT_MOBILE_URL: 'https://m.youtube.com', | ||
YT_WATCH_PAGE: 'https://m.youtube.com/watch', | ||
}; | ||
const oauth = { | ||
scope: 'http://gdata.youtube.com https://www.googleapis.com/auth/youtube-paid-content', | ||
grant_type: 'http://oauth.net/grant_type/device/1.0', | ||
model_name: 'ytlr::' | ||
}; | ||
const oauth_reqopts = { | ||
headers: { | ||
'accept': '*/*', | ||
'origin': urls.YT_BASE_URL, | ||
'user-agent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version', | ||
'content-type': 'application/json', | ||
'referer': `${urls.YT_BASE_URL}/tv`, | ||
'accept-language': 'en-US' | ||
} | ||
}; | ||
const default_headers = (session) => { | ||
return { | ||
headers: { | ||
'Cookie': session.cookie, | ||
'user-agent': Utils.getRandomUserAgent('desktop').userAgent, | ||
'Referer': 'https://www.google.com/', | ||
'Accept': 'text/html', | ||
'Accept-Language': 'en-US,en', | ||
'Accept-Encoding': 'gzip', | ||
'Upgrade-Insecure-Requests': 1 | ||
module.exports = { | ||
URLS: { | ||
YT_BASE_URL: 'https://www.youtube.com', | ||
YT_MOBILE_URL: 'https://m.youtube.com', | ||
YT_WATCH_PAGE: 'https://m.youtube.com/watch' | ||
}, | ||
OAUTH: { | ||
SCOPE: 'http://gdata.youtube.com https://www.googleapis.com/auth/youtube-paid-content', | ||
GRANT_TYPE: 'http://oauth.net/grant_type/device/1.0', | ||
MODEL_NAME: 'ytlr::', | ||
HEADERS: { | ||
headers: { | ||
'accept': '*/*', | ||
'origin': 'https://www.youtube.com', | ||
'user-agent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version', | ||
'content-type': 'application/json', | ||
'referer': `https://www.youtube.com/tv`, | ||
'accept-language': 'en-US' | ||
} | ||
} | ||
}; | ||
}; | ||
const innertube_request_opts = (info) => { | ||
info.desktop === undefined && (info.desktop = true); | ||
let req_opts = { | ||
params: info.params || {}, | ||
headers: { | ||
'accept': '*/*', | ||
'user-agent': Utils.getRandomUserAgent(info.desktop ? 'desktop' : 'mobile').userAgent, | ||
'content-type': 'application/json', | ||
'accept-language': 'en-US,en;q=0.9', | ||
'x-goog-authuser': 0, | ||
'x-goog-visitor-id': info.session.context.client.visitorData, | ||
'x-youtube-client-name': info.desktop ? 1 : 2, | ||
'x-youtube-client-version': info.session.context.client.clientVersion, | ||
'x-youtube-chrome-connected': 'source=Chrome,mode=0,enable_account_consistency=true,supervised=false,consistency_enabled_by_default=false', | ||
'x-origin': info.desktop ? urls.YT_BASE_URL : urls.YT_MOBILE_URL, | ||
'origin': info.desktop ? urls.YT_BASE_URL : urls.YT_MOBILE_URL, | ||
} | ||
}; | ||
info.id && (req_opts.headers.referer = (info.desktop ? urls.YT_BASE_URL : urls.YT_MOBILE_URL) + '/watch?v=' + info.id); | ||
if (info.session.logged_in && info.desktop) { | ||
req_opts.headers.Cookie = info.session.cookie; | ||
req_opts.headers.authorization = info.session.cookie.length < 1 ? `Bearer ${info.session.access_token}` : info.session.auth_apisid; | ||
} | ||
return req_opts; | ||
}; | ||
const video_details_reqbody = (id, sts, context) => { | ||
return { | ||
playbackContext: { | ||
contentPlaybackContext: { | ||
'currentUrl': '/watch?v=' + id, | ||
'vis': 0, | ||
'splay': false, | ||
'autoCaptionsDefaultOn': false, | ||
'autonavState': 'STATE_OFF', | ||
'html5Preference': 'HTML5_PREF_WANTS', | ||
'signatureTimestamp': sts, | ||
'referer': urls.YT_BASE_URL, | ||
'lactMilliseconds': '-1' | ||
}, | ||
DEFAULT_HEADERS: (session) => { | ||
return { | ||
headers: { | ||
'Cookie': session.cookie, | ||
'user-agent': Utils.getRandomUserAgent('desktop').userAgent, | ||
'Referer': 'https://www.google.com/', | ||
'Accept': 'text/html', | ||
'Accept-Language': 'en-US,en', | ||
'Accept-Encoding': 'gzip', | ||
'Upgrade-Insecure-Requests': 1 | ||
} | ||
}, | ||
context: context, | ||
videoId: id | ||
}; | ||
}; | ||
const stream_headers = (range) => { | ||
let headers = { | ||
}; | ||
}, | ||
STREAM_HEADERS: { | ||
'Accept': '*/*', | ||
'User-Agent': Utils.getRandomUserAgent('desktop').userAgent, | ||
'Connection': 'keep-alive', | ||
'Origin': urls.YT_BASE_URL, | ||
'Referer': urls.YT_BASE_URL, | ||
'Origin': 'https://www.youtube.com', | ||
'Referer': 'https://www.youtube.com', | ||
'DNT': '?1' | ||
}; | ||
range && (headers.Range = range); | ||
return headers; | ||
}; | ||
}, | ||
INNERTUBE_REQOPTS: (info) => { | ||
info.desktop === undefined && (info.desktop = true); | ||
let req_opts = { | ||
params: info.params || {}, | ||
headers: { | ||
'accept': '*/*', | ||
'user-agent': Utils.getRandomUserAgent(info.desktop ? 'desktop' : 'mobile').userAgent, | ||
'content-type': 'application/json', | ||
'accept-language': 'en-US,en;q=0.9', | ||
'x-goog-authuser': 0, | ||
'x-goog-visitor-id': info.session.context.client.visitorData, | ||
'x-youtube-client-name': info.desktop ? 1 : 2, | ||
'x-youtube-client-version': info.session.context.client.clientVersion, | ||
'x-youtube-chrome-connected': 'source=Chrome,mode=0,enable_account_consistency=true,supervised=false,consistency_enabled_by_default=false', | ||
'x-origin': info.desktop ? 'https://www.youtube.com' : 'https://m.youtube.com', | ||
'origin': info.desktop ? 'https://www.youtube.com' : 'https://m.youtube.com', | ||
} | ||
}; | ||
const formatVideoData = (data, context, desktop) => { | ||
let video_details = {}; | ||
let metadata = {}; | ||
info.id && (req_opts.headers.referer = (info.desktop ? 'https://www.youtube.com' : 'https://m.youtube.com') + '/watch?v=' + info.id); | ||
if (desktop) { | ||
metadata.embed = data.microformat.playerMicroformatRenderer.embed; | ||
metadata.view_count = parseInt(data.videoDetails.viewCount); | ||
metadata.average_rating = data.videoDetails.averageRating; | ||
metadata.length_seconds = data.microformat.playerMicroformatRenderer.lengthSeconds; | ||
metadata.channel_id = data.videoDetails.channelId; | ||
metadata.channel_url = data.microformat.playerMicroformatRenderer.ownerProfileUrl; | ||
metadata.external_channel_id = data.microformat.playerMicroformatRenderer.externalChannelId; | ||
metadata.is_live_content = data.videoDetails.isLiveContent; | ||
metadata.is_family_safe = data.microformat.playerMicroformatRenderer.isFamilySafe; | ||
metadata.is_unlisted = data.microformat.playerMicroformatRenderer.isUnlisted; | ||
metadata.is_private = data.videoDetails.isPrivate; | ||
metadata.has_ypc_metadata = data.microformat.playerMicroformatRenderer.hasYpcMetadata; | ||
metadata.category = data.microformat.playerMicroformatRenderer.category; | ||
metadata.channel_name = data.microformat.playerMicroformatRenderer.ownerChannelName; | ||
metadata.publish_date = data.microformat.playerMicroformatRenderer.publishDate || 'N/A'; | ||
metadata.upload_date = data.microformat.playerMicroformatRenderer.uploadDate || 'N/A'; | ||
metadata.keywords = data.videoDetails.keywords || []; | ||
if (info.session.logged_in && info.desktop) { | ||
req_opts.headers.Cookie = info.session.cookie; | ||
req_opts.headers.authorization = info.session.cookie.length < 1 ? `Bearer ${info.session.access_token}` : info.session.auth_apisid; | ||
} | ||
video_details.title = data.videoDetails.title; | ||
video_details.description = data.videoDetails.shortDescription; | ||
video_details.thumbnail = data.videoDetails.thumbnail.thumbnails.slice(-1)[0]; | ||
video_details.metadata = metadata; | ||
} else { | ||
metadata.embed = data[2].playerResponse.microformat.playerMicroformatRenderer.embed; | ||
metadata.likes = parseInt(data[3].response.contents.singleColumnWatchNextResults.results.results.contents[1].slimVideoMetadataSectionRenderer.contents[1].slimVideoActionBarRenderer.buttons[0].slimMetadataToggleButtonRenderer.button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/\D/g, '')); | ||
metadata.dislikes = parseInt(data[3].response.contents.singleColumnWatchNextResults.results.results.contents[1].slimVideoMetadataSectionRenderer.contents[1].slimVideoActionBarRenderer.buttons[1].slimMetadataToggleButtonRenderer.button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/\D/g, '')); | ||
metadata.view_count = parseInt(data[2].playerResponse.videoDetails.viewCount); | ||
metadata.average_rating = data[2].playerResponse.videoDetails.averageRating; | ||
metadata.length_seconds = data[2].playerResponse.microformat.playerMicroformatRenderer.lengthSeconds; | ||
metadata.channel_id = data[2].playerResponse.videoDetails.channelId; | ||
metadata.channel_url = data[2].playerResponse.microformat.playerMicroformatRenderer.ownerProfileUrl; | ||
metadata.external_channel_id = data[2].playerResponse.microformat.playerMicroformatRenderer.externalChannelId; | ||
metadata.is_live_content = data[2].playerResponse.videoDetails.isLiveContent; | ||
metadata.is_family_safe = data[2].playerResponse.microformat.playerMicroformatRenderer.isFamilySafe; | ||
metadata.is_unlisted = data[2].playerResponse.microformat.playerMicroformatRenderer.isUnlisted; | ||
metadata.is_private = data[2].playerResponse.videoDetails.isPrivate; | ||
metadata.has_ypc_metadata = data[2].playerResponse.microformat.playerMicroformatRenderer.hasYpcMetadata; | ||
metadata.category = data[2].playerResponse.microformat.playerMicroformatRenderer.category; | ||
metadata.channel_name = data[2].playerResponse.microformat.playerMicroformatRenderer.ownerChannelName; | ||
metadata.publish_date = data[2].playerResponse.microformat.playerMicroformatRenderer.publishDate; | ||
metadata.upload_date = data[2].playerResponse.microformat.playerMicroformatRenderer.uploadDate; | ||
metadata.keywords = data[2].playerResponse.videoDetails.keywords; | ||
return req_opts; | ||
}, | ||
VIDEO_INFO_REQBODY: (id, sts, context) => { | ||
return { | ||
playbackContext: { | ||
contentPlaybackContext: { | ||
'currentUrl': '/watch?v=' + id, | ||
'vis': 0, | ||
'splay': false, | ||
'autoCaptionsDefaultOn': false, | ||
'autonavState': 'STATE_OFF', | ||
'html5Preference': 'HTML5_PREF_WANTS', | ||
'signatureTimestamp': sts, | ||
'referer': 'https://www.youtube.com', | ||
'lactMilliseconds': '-1' | ||
} | ||
}, | ||
context: context, | ||
videoId: id | ||
}; | ||
}, | ||
BASE64_DIALECT: { | ||
NORMAL: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split(''), | ||
REVERSE: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'.split('') | ||
}, | ||
FUNCS_REGEX: /d\.push\(e\)|d\.reverse\(\)|d\[0\]\)\[0\]\)|f=d\[0];d\[0\]|d\.length;d\.splice\(e,1\)|function\(\){for\(var|function\(d,e,f\){var h=f|function\(d\){for\(var|reverse\(\)\.forEach|unshift\(d\.pop\(\)\)|function\(d,e\){for\(var f/, | ||
FUNCS: { | ||
PUSH: 'd.push(e)', | ||
REVERSE_1: 'd.reverse()', | ||
REVERSE_2: 'function(d){for(var', | ||
SPLICE: 'd.length;d.splice(e,1)', | ||
SWAP0_1: 'd[0])[0])', | ||
SWAP0_2: 'f=d[0];d[0]', | ||
ROTATE_1: 'reverse().forEach', | ||
ROTATE_2: 'unshift(d.pop())', | ||
BASE64_DIA: 'function(){for(var', | ||
TRANSLATE_1: 'function(d,e){for(var f', | ||
TRANSLATE_2: 'function(d,e,f){var h=f' | ||
}, | ||
// Helper functions, felt like Utils.js wasn't the right place for them: | ||
formatNTransformData: (data) => { | ||
return data | ||
.replace(/function\(d,e\)/g, '"function(d,e)').replace(/function\(d\)/g, '"function(d)') | ||
.replace(/function\(\)/, '"function()').replace(/function\(d,e,f\)/g, '"function(d,e,f)') | ||
.replace(/,b,/g, ',"b",').replace(/,b/g, ',"b"').replace(/b,/g, '"b",').replace(/b]/g, '"b"]') | ||
.replace(/\[b/g, '["b"').replace(/}]/g, '"]').replace(/},/g, '}",').replace(/""/g, '') | ||
.replace(/length]\)}"/g, 'length])}'); | ||
}, | ||
formatVideoData: (data, context, is_desktop) => { | ||
let video_details = {}; | ||
let metadata = {}; | ||
video_details.title = data[2].playerResponse.videoDetails.title; | ||
video_details.description = data[2].playerResponse.videoDetails.shortDescription; | ||
video_details.thumbnail = data[2].playerResponse.videoDetails.thumbnail.thumbnails.slice(-1)[0]; | ||
if (is_desktop) { | ||
metadata.embed = data.microformat.playerMicroformatRenderer.embed; | ||
metadata.view_count = parseInt(data.videoDetails.viewCount); | ||
metadata.average_rating = data.videoDetails.averageRating; | ||
metadata.length_seconds = data.microformat.playerMicroformatRenderer.lengthSeconds; | ||
metadata.channel_id = data.videoDetails.channelId; | ||
metadata.channel_url = data.microformat.playerMicroformatRenderer.ownerProfileUrl; | ||
metadata.external_channel_id = data.microformat.playerMicroformatRenderer.externalChannelId; | ||
metadata.is_live_content = data.videoDetails.isLiveContent; | ||
metadata.is_family_safe = data.microformat.playerMicroformatRenderer.isFamilySafe; | ||
metadata.is_unlisted = data.microformat.playerMicroformatRenderer.isUnlisted; | ||
metadata.is_private = data.videoDetails.isPrivate; | ||
metadata.has_ypc_metadata = data.microformat.playerMicroformatRenderer.hasYpcMetadata; | ||
metadata.category = data.microformat.playerMicroformatRenderer.category; | ||
metadata.channel_name = data.microformat.playerMicroformatRenderer.ownerChannelName; | ||
metadata.publish_date = data.microformat.playerMicroformatRenderer.publishDate || 'N/A'; | ||
metadata.upload_date = data.microformat.playerMicroformatRenderer.uploadDate || 'N/A'; | ||
metadata.keywords = data.videoDetails.keywords || []; | ||
// Functions | ||
video_details.like = () => {}; | ||
video_details.dislike = () => {}; | ||
video_details.removeLike = () => {}; | ||
video_details.subscribe = () => {}; | ||
video_details.unsubscribe = () => {}; | ||
video_details.comment = () => {}; | ||
video_details.getComments = () => {}; | ||
video_details.setNotificationPref = () => {}; | ||
video_details.getLivechat = () => {}; | ||
video_details.title = data.videoDetails.title; | ||
video_details.description = data.videoDetails.shortDescription; | ||
video_details.thumbnail = data.videoDetails.thumbnail.thumbnails.slice(-1)[0]; | ||
video_details.metadata = metadata; | ||
} else { | ||
metadata.embed = data[2].playerResponse.microformat.playerMicroformatRenderer.embed; | ||
metadata.likes = parseInt(data[3].response.contents.singleColumnWatchNextResults.results.results.contents[1].slimVideoMetadataSectionRenderer.contents[1].slimVideoActionBarRenderer.buttons[0].slimMetadataToggleButtonRenderer.button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/\D/g, '')); | ||
metadata.dislikes = parseInt(data[3].response.contents.singleColumnWatchNextResults.results.results.contents[1].slimVideoMetadataSectionRenderer.contents[1].slimVideoActionBarRenderer.buttons[1].slimMetadataToggleButtonRenderer.button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/\D/g, '')); | ||
metadata.view_count = parseInt(data[2].playerResponse.videoDetails.viewCount); | ||
metadata.average_rating = data[2].playerResponse.videoDetails.averageRating; | ||
metadata.length_seconds = data[2].playerResponse.microformat.playerMicroformatRenderer.lengthSeconds; | ||
metadata.channel_id = data[2].playerResponse.videoDetails.channelId; | ||
metadata.channel_url = data[2].playerResponse.microformat.playerMicroformatRenderer.ownerProfileUrl; | ||
metadata.external_channel_id = data[2].playerResponse.microformat.playerMicroformatRenderer.externalChannelId; | ||
metadata.is_live_content = data[2].playerResponse.videoDetails.isLiveContent; | ||
metadata.is_family_safe = data[2].playerResponse.microformat.playerMicroformatRenderer.isFamilySafe; | ||
metadata.is_unlisted = data[2].playerResponse.microformat.playerMicroformatRenderer.isUnlisted; | ||
metadata.is_private = data[2].playerResponse.videoDetails.isPrivate; | ||
metadata.has_ypc_metadata = data[2].playerResponse.microformat.playerMicroformatRenderer.hasYpcMetadata; | ||
metadata.category = data[2].playerResponse.microformat.playerMicroformatRenderer.category; | ||
metadata.channel_name = data[2].playerResponse.microformat.playerMicroformatRenderer.ownerChannelName; | ||
metadata.publish_date = data[2].playerResponse.microformat.playerMicroformatRenderer.publishDate; | ||
metadata.upload_date = data[2].playerResponse.microformat.playerMicroformatRenderer.uploadDate; | ||
metadata.keywords = data[2].playerResponse.videoDetails.keywords; | ||
// Additional metadata | ||
video_details.metadata = metadata; | ||
} | ||
return video_details; | ||
}; | ||
video_details.title = data[2].playerResponse.videoDetails.title; | ||
video_details.description = data[2].playerResponse.videoDetails.shortDescription; | ||
video_details.thumbnail = data[2].playerResponse.videoDetails.thumbnail.thumbnails.slice(-1)[0]; | ||
const base64_alphabet = { | ||
normal: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split(''), | ||
reverse: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'.split('') | ||
}; | ||
// Functions | ||
video_details.like = () => {}; | ||
video_details.dislike = () => {}; | ||
video_details.removeLike = () => {}; | ||
video_details.subscribe = () => {}; | ||
video_details.unsubscribe = () => {}; | ||
video_details.comment = () => {}; | ||
video_details.getComments = () => {}; | ||
video_details.setNotificationPref = () => {}; | ||
video_details.getLivechat = () => {}; | ||
const filters = (order) => { | ||
// It seems like all of these are just proto buffers, so I think it'll be easy to refactor | ||
switch (order) { | ||
case 'any,any,relevance': | ||
return 'EgIQAQ%3D%3D'; | ||
case 'hour,any,relevance': | ||
return 'EgIIAQ%3D%3D'; | ||
case 'day,any,relevance': | ||
return 'EgQIAhAB'; | ||
case 'week,any,relevance': | ||
return 'EgQIAxAB'; | ||
case 'month,any,relevance': | ||
return 'EgQIBBAB'; | ||
case 'year,any,relevance': | ||
return 'EgQIBRAB'; | ||
case 'any,short,relevance': | ||
return 'EgQQARgB'; | ||
case 'hour,short,relevance': | ||
return 'EgYIARABGAE%3D'; | ||
case 'day,short,relevance': | ||
return 'EgYIAhABGAE%3D'; | ||
case 'week,short,relevance': | ||
return 'EgYIAxABGAE%3D'; | ||
case 'month,short,relevance': | ||
return 'EgYIBBABGAE%3D'; | ||
case 'year,short,relevance': | ||
return 'EgYIBRABGAE%3D'; | ||
case 'any,long,relevance': | ||
return 'EgQQARgC'; | ||
case 'hour,long,relevance': | ||
return 'EgYIARABGAI%3D'; | ||
case 'day,long,relevance': | ||
return 'EgYIAhABGAI%3D'; | ||
case 'week,long,relevance': | ||
return 'EgYIAxABGAI%3D'; | ||
case 'month,long,relevance': | ||
return 'EgYIBBABGAI%3D'; | ||
case 'year,long,relevance': | ||
return 'EgYIBRABGAI%3D'; | ||
case 'any,any,age': | ||
return 'CAISAhAB'; | ||
case 'hour,any,age': | ||
return 'CAISBAgBEAE%3D'; | ||
case 'day,any,age': | ||
return 'CAISBAgCEAE%3D'; | ||
case 'week,any,age': | ||
return 'CAISBAgDEAE%3D'; | ||
case 'month,any,age': | ||
return 'CAISBAgEEAE%3D'; | ||
case 'year,any,age': | ||
return 'CAISBAgFEAE%3D'; | ||
case 'any,short,age': | ||
return 'CAISBBABGAE%3D'; | ||
case 'hour,short,age': | ||
return 'CAISBggBEAEYAQ%3D%3D'; | ||
case 'day,short,age': | ||
return 'CAISBggCEAEYAQ%3D%3D'; | ||
case 'week,short,age': | ||
return 'CAISBggDEAEYAQ%3D%3D'; | ||
case 'month,short,age': | ||
return 'CAISBggEEAEYAQ%3D%3D'; | ||
case 'year,short,age': | ||
return 'CAISBggFEAEYAQ%3D%3D'; | ||
case 'any,long,age': | ||
return 'CAISBBABGAI%3D'; | ||
case 'hour,long,age': | ||
return 'CAISBggBEAEYAg%3D%3D'; | ||
case 'day,long,age': | ||
return 'CAISBggCEAEYAg%3D%3D'; | ||
case 'week,long,age': | ||
return 'CAISBggDEAEYAg%3D%3D'; | ||
case 'month,long,age': | ||
return 'CAISBggEEAEYAg%3D%3D'; | ||
case 'year,long,age': | ||
return 'CAISBggFEAEYAg%3D%3D'; | ||
case 'any,any,views': | ||
return 'CAMSAhAB'; | ||
case 'hour,any,views': | ||
return 'CAMSBAgBEAE%3D'; | ||
case 'day,any,views': | ||
return 'CAMSBAgCEAE%3D'; | ||
case 'week,any,views': | ||
return 'CAMSBAgDEAE%3D'; | ||
case 'month,any,views': | ||
return 'CAMSBAgEEAE%3D'; | ||
case 'year,any,views': | ||
return 'CAMSBAgFEAE%3D'; | ||
case 'any,short,views': | ||
return 'CAMSBBABGAE%3D'; | ||
case 'hour,short,views': | ||
return 'CAMSBggBEAEYAQ%3D%3D'; | ||
case 'day,short,views': | ||
return 'CAMSBggCEAEYAQ%3D%3D'; | ||
case 'week,short,views': | ||
return 'CAMSBggDEAEYAQ%3D%3D'; | ||
case 'month,short,views': | ||
return 'CAMSBggEEAEYAQ%3D%3D'; | ||
case 'year,short,views': | ||
return 'CAMSBggFEAEYAQ%3D%3D'; | ||
case 'any,long,views': | ||
return 'CAMSBBABGAI%3D'; | ||
case 'hour,long,views': | ||
return 'CAMSBggBEAEYAg%3D%3D'; | ||
case 'day,long,views': | ||
return 'CAMSBggCEAEYAg%3D%3D'; | ||
case 'week,long,views': | ||
return 'CAMSBggDEAEYAg%3D%3D'; | ||
case 'month,long,views': | ||
return 'CAMSBggEEAEYAg%3D%3D'; | ||
case 'year,long,views': | ||
return 'CAMSBggFEAEYAg%3D%3D'; | ||
case 'any,any,rating': | ||
return 'CAESAhAB'; | ||
case 'hour,any,rating': | ||
return 'CAESBAgBEAE%3D'; | ||
case 'day,any,rating': | ||
return 'CAESBAgCEAE%3D'; | ||
case 'week,any,rating': | ||
return 'CAESBAgDEAE%3D'; | ||
case 'month,any,rating': | ||
return 'CAESBAgEEAE%3D'; | ||
case 'year,any,rating': | ||
return 'CAESBAgFEAE%3D'; | ||
case 'any,short,rating': | ||
return 'CAESBBABGAE%3D'; | ||
case 'hour,short,rating': | ||
return 'CAESBggBEAEYAQ%3D%3D'; | ||
case 'day,short,rating': | ||
return 'CAESBggCEAEYAQ%3D%3D'; | ||
case 'week,short,rating': | ||
return 'CAESBggDEAEYAQ%3D%3D'; | ||
case 'month,short,rating': | ||
return 'CAESBggEEAEYAQ%3D%3D'; | ||
case 'year,short,rating': | ||
return 'CAESBggFEAEYAQ%3D%3D'; | ||
case 'any,long,rating': | ||
return 'CAESBBABGAI%3D'; | ||
case 'hour,long,rating': | ||
return 'CAESBggBEAEYAg%3D%3D'; | ||
case 'day,long,rating': | ||
return 'CAESBggCEAEYAg%3D%3D'; | ||
case 'week,long,rating': | ||
return 'CAESBggDEAEYAg%3D%3D'; | ||
case 'month,long,rating': | ||
return 'CAESBggEEAEYAg%3D%3D'; | ||
case 'year,long,rating': | ||
return 'CAESBggFEAEYAg%3D%3D'; | ||
default: | ||
// Additional metadata | ||
video_details.metadata = metadata; | ||
} | ||
return video_details; | ||
}, | ||
filters: (order) => { | ||
return (({ | ||
'any,any,relevance': 'EgIQAQ%3D%3D', | ||
'hour,any,relevance': 'EgIIAQ%3D%3D', | ||
'day,any,relevance': 'EgQIAhAB', | ||
'week,any,relevance': 'EgQIAxAB', | ||
'month,any,relevance': 'EgQIBBAB', | ||
'year,any,relevance': 'EgQIBRAB', | ||
'any,short,relevance': 'EgQQARgB', | ||
'hour,short,relevance': 'EgYIARABGAE%3D', | ||
'day,short,relevance': 'EgYIAhABGAE%3D', | ||
'week,short,relevance': 'EgYIAxABGAE%3D', | ||
'month,short,relevance': 'EgYIBBABGAE%3D', | ||
'year,short,relevance': 'EgYIBRABGAE%3D', | ||
'any,long,relevance': 'EgQQARgC', | ||
'hour,long,relevance': 'EgYIARABGAI%3D', | ||
'day,long,relevance': 'EgYIAhABGAI%3D', | ||
'week,long,relevance': 'EgYIAxABGAI%3D', | ||
'month,long,relevance': 'EgYIBBABGAI%3D', | ||
'year,long,relevance': 'EgYIBRABGAI%3D', | ||
'any,any,age': 'CAISAhAB', | ||
'hour,any,age': 'CAISBAgBEAE%3D', | ||
'day,any,age': 'CAISBAgCEAE%3D', | ||
'week,any,age': 'CAISBAgDEAE%3D', | ||
'month,any,age': 'CAISBAgEEAE%3D', | ||
'year,any,age': 'CAISBAgFEAE%3D', | ||
'any,short,age': 'CAISBBABGAE%3D', | ||
'hour,short,age': 'CAISBggBEAEYAQ%3D%3D', | ||
'day,short,age': 'CAISBggCEAEYAQ%3D%3D', | ||
'week,short,age': 'CAISBggDEAEYAQ%3D%3D', | ||
'month,short,age': 'CAISBggEEAEYAQ%3D%3D', | ||
'year,short,age': 'CAISBggFEAEYAQ%3D%3D', | ||
'any,long,age': 'CAISBBABGAI%3D', | ||
'hour,long,age': 'CAISBggBEAEYAg%3D%3D', | ||
'day,long,age': 'CAISBggCEAEYAg%3D%3D', | ||
'week,long,age': 'CAISBggDEAEYAg%3D%3D', | ||
'month,long,age': 'CAISBggEEAEYAg%3D%3D', | ||
'year,long,age': 'CAISBggFEAEYAg%3D%3D', | ||
'any,any,views': 'CAMSAhAB', | ||
'hour,any,views': 'CAMSBAgBEAE%3D', | ||
'day,any,views': 'CAMSBAgCEAE%3D', | ||
'week,any,views': 'CAMSBAgDEAE%3D', | ||
'month,any,views': 'CAMSBAgEEAE%3D', | ||
'year,any,views': 'CAMSBAgFEAE%3D', | ||
'any,short,views': 'CAMSBBABGAE%3D', | ||
'hour,short,views': 'CAMSBggBEAEYAQ%3D%3D', | ||
'day,short,views': 'CAMSBggCEAEYAQ%3D%3D', | ||
'week,short,views': 'CAMSBggDEAEYAQ%3D%3D', | ||
'month,short,views': 'CAMSBggEEAEYAQ%3D%3D', | ||
'year,short,views': 'CAMSBggFEAEYAQ%3D%3D', | ||
'any,long,views': 'CAMSBBABGAI%3D', | ||
'hour,long,views': 'CAMSBggBEAEYAg%3D%3D', | ||
'day,long,views': 'CAMSBggCEAEYAg%3D%3D', | ||
'week,long,views': 'CAMSBggDEAEYAg%3D%3D', | ||
'month,long,views': 'CAMSBggEEAEYAg%3D%3D', | ||
'year,long,views': 'CAMSBggFEAEYAg%3D%3D', | ||
'any,any,rating': 'CAESAhAB', | ||
'hour,any,rating': 'CAESBAgBEAE%3D', | ||
'day,any,rating': 'CAESBAgCEAE%3D', | ||
'week,any,rating': 'CAESBAgDEAE%3D', | ||
'month,any,rating': 'CAESBAgEEAE%3D', | ||
'year,any,rating': 'CAESBAgFEAE%3D', | ||
'any,short,rating': 'CAESBBABGAE%3D', | ||
'hour,short,rating': 'CAESBggBEAEYAQ%3D%3D', | ||
'day,short,rating': 'CAESBggCEAEYAQ%3D%3D', | ||
'week,short,rating': 'CAESBggDEAEYAQ%3D%3D', | ||
'month,short,rating': 'CAESBggEEAEYAQ%3D%3D', | ||
'year,short,rating': 'CAESBggFEAEYAQ%3D%3D', | ||
'any,long,rating': 'CAESBBABGAI%3D', | ||
'hour,long,rating': 'CAESBggBEAEYAg%3D%3D', | ||
'day,long,rating': 'CAESBggCEAEYAg%3D%3D', | ||
'week,long,rating': 'CAESBggDEAEYAg%3D%3D', | ||
'month,long,rating': 'CAESBggEEAEYAg%3D%3D', | ||
'year,long,rating': 'CAESBggFEAEYAg%3D%3D' | ||
})[order] || 'EgIQAQ%3D%3D'); | ||
} | ||
}; | ||
module.exports = { urls, oauth, oauth_reqopts, default_headers, innertube_request_opts, video_details_reqbody, stream_headers, formatVideoData, base64_alphabet, filters }; | ||
}; |
@@ -12,3 +12,3 @@ 'use strict'; | ||
const Constants = require('./Constants'); | ||
const SigDecipher = require('./SigDecipher'); | ||
const SigDecipher = require('./Sig'); | ||
const EventEmitter = require('events'); | ||
@@ -22,2 +22,3 @@ const TimeToSeconds = require('time-to-seconds'); | ||
this.cookie = cookie || ''; | ||
this.retry_count = 0; | ||
return this.init(); | ||
@@ -27,5 +28,5 @@ } | ||
async init() { | ||
const response = await Axios.get(Constants.urls.YT_BASE_URL, Constants.default_headers(this)).catch((error) => error); | ||
const response = await Axios.get(Constants.URLS.YT_BASE_URL, Constants.DEFAULT_HEADERS(this)).catch((error) => error); | ||
if (response instanceof Error) throw new Error(`Could not extract Innertube data: ${response.message}`); | ||
try { | ||
@@ -52,5 +53,9 @@ const innertube_data = JSON.parse(`{${Utils.getStringBetweenStrings(response.data, 'ytcfg.set({', '});')}}`); | ||
} else { | ||
this.retry_count += 1; | ||
if (this.retry_count >= 10) throw new Error('Could not retrieve Innertube data'); | ||
return this.init(); | ||
} | ||
} catch (err) { | ||
this.retry_count += 1; | ||
if (this.retry_count >= 10) throw new Error('Could not retrieve Innertube data'); | ||
return this.init(); | ||
@@ -103,4 +108,4 @@ } | ||
if (!query) throw new Error('No query was provided'); | ||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/search${this.logged_in && this.cookie.length < 1 ? '' : `?key=${this.key}`}`, JSON.stringify({ context: this.context, params: Constants.filters(options.period + ',' + options.duration + ',' + options.order), query }), Constants.innertube_request_opts({ session: this })).catch((error) => error); | ||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/search${this.logged_in && this.cookie.length < 1 ? '' : `?key=${this.key}`}`, JSON.stringify({ context: this.context, params: Constants.filters(options.period + ',' + options.duration + ',' + options.order), query }), Constants.INNERTUBE_REQOPTS({ session: this })).catch((error) => error); | ||
if (response instanceof Error) throw new Error(`Could not search on YouTube: ${response.message}`); | ||
@@ -124,3 +129,3 @@ | ||
url: `https://youtu.be/${video.videoId}`, | ||
channel_url: `${Constants.urls.YT_BASE_URL}${video.ownerText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url}`, | ||
channel_url: `${Constants.URLS.YT_BASE_URL}${video.ownerText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url}`, | ||
metadata: { | ||
@@ -160,3 +165,3 @@ view_count: video.viewCountText && video.viewCountText.simpleText || 'N/A', | ||
} | ||
video_data.like = () => Actions.engage(this, 'like/like', { video_id: id }); | ||
@@ -173,6 +178,6 @@ video_data.dislike = () => Actions.engage(this, 'like/dislike', { video_id: id }); | ||
} | ||
async getComments(video_id, token) { | ||
let comment_section_token; | ||
if (!token) { | ||
@@ -183,22 +188,21 @@ const data_continuation = await Actions.getContinuation(this, { video_id }); | ||
} | ||
const response = await Actions.getContinuation(this, { continuation_token: comment_section_token || token }); | ||
if (!response.success) throw new Error('Could not fetch comments section'); | ||
const comments_section = { comments: [] }; | ||
!token && (comments_section.comment_count = response.data.onResponseReceivedEndpoints[0].reloadContinuationItemsCommand.continuationItems && response.data.onResponseReceivedEndpoints[0].reloadContinuationItemsCommand.continuationItems[0].commentsHeaderRenderer.countText.runs[0].text || 'N/A'); | ||
let continuation_token; | ||
!token && (continuation_token = response.data.onResponseReceivedEndpoints[1].reloadContinuationItemsCommand.continuationItems.find((item) => item.continuationItemRenderer).continuationItemRenderer.continuationEndpoint.continuationCommand.token) | ||
|| (continuation_token = response.data.onResponseReceivedEndpoints[0].appendContinuationItemsAction.continuationItems.find((item) => item.continuationItemRenderer).continuationItemRenderer.continuationEndpoint.continuationCommand.token); | ||
!token && (continuation_token = response.data.onResponseReceivedEndpoints[1].reloadContinuationItemsCommand.continuationItems.find((item) => item.continuationItemRenderer).continuationItemRenderer.continuationEndpoint.continuationCommand.token) || | ||
(continuation_token = response.data.onResponseReceivedEndpoints[0].appendContinuationItemsAction.continuationItems.find((item) => item.continuationItemRenderer).continuationItemRenderer.continuationEndpoint.continuationCommand.token); | ||
comments_section.getContinuation = () => this.getComments(video_id, continuation_token); | ||
let contents; | ||
!token && (contents = response.data.onResponseReceivedEndpoints[1].reloadContinuationItemsCommand.continuationItems) | ||
|| (contents = response.data.onResponseReceivedEndpoints[0].appendContinuationItemsAction.continuationItems); | ||
!token && (contents = response.data.onResponseReceivedEndpoints[1].reloadContinuationItemsCommand.continuationItems) || | ||
(contents = response.data.onResponseReceivedEndpoints[0].appendContinuationItemsAction.continuationItems); | ||
contents.forEach((thread) => { | ||
if (!thread.commentThreadRenderer) return; | ||
console.log(thread.commentThreadRenderer.comment); | ||
const comment = { | ||
@@ -222,22 +226,22 @@ text: thread.commentThreadRenderer.comment.commentRenderer.contentText.runs.map((t) => t.text).join(' '), | ||
}); | ||
return comments_section; | ||
} | ||
async getSubscriptionsFeed() { | ||
const response = await Actions.browse(this, 'subscriptions_feed'); | ||
if (!response.success) throw new Error('Could not fetch subscriptions feed'); | ||
const contents = response.data.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents; | ||
const subscriptions_feed = {}; | ||
contents.forEach((section) => { | ||
if (!section.itemSectionRenderer) return; | ||
const section_contents = section.itemSectionRenderer.contents[0]; | ||
const section_items = section_contents.shelfRenderer.content.gridRenderer.items; | ||
const key = section_contents.shelfRenderer.title.runs[0].text; | ||
subscriptions_feed[key.toLowerCase().replace(/ +/g, '_')] = []; | ||
section_items.forEach((item) => { | ||
@@ -256,7 +260,7 @@ const content = { | ||
}; | ||
subscriptions_feed[key.toLowerCase().replace(/ +/g, '_')].push(content); | ||
}); | ||
}); | ||
return subscriptions_feed; | ||
@@ -268,3 +272,3 @@ } | ||
if (!response.success) throw new Error('Could not fetch notifications'); | ||
const contents = response.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[0]; | ||
@@ -296,4 +300,4 @@ if (!contents.multiPageMenuNotificationSectionRenderer) return { error: 'You don\'t have any notification.' }; | ||
let response; | ||
!desktop && (response = await Axios.get(`${Constants.urls.YT_WATCH_PAGE}?v=${id}&t=8s&pbj=1`, Constants.innertube_request_opts({ session: this, id, desktop: false })).catch((error) => error)) | ||
|| (response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/player${this.logged_in && this.cookie.length < 1 ? '' : `?key=${this.key}`}`, JSON.stringify(Constants.video_details_reqbody(id, this.sts, this.context)), Constants.innertube_request_opts({ session: this, id, desktop: true })).catch((error) => error)); | ||
!desktop && (response = await Axios.get(`${Constants.URLS.YT_WATCH_PAGE}?v=${id}&t=8s&pbj=1`, Constants.INNERTUBE_REQOPTS({ session: this, id, desktop: false })).catch((error) => error)) || | ||
(response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/player${this.logged_in && this.cookie.length < 1 ? '' : `?key=${this.key}`}`, JSON.stringify(Constants.VIDEO_INFO_REQBODY(id, this.sts, this.context)), Constants.INNERTUBE_REQOPTS({ session: this, id, desktop: true })).catch((error) => error)); | ||
if (response instanceof Error) throw new Error('Could not retrieve watch page info: ' + response.message); | ||
@@ -367,6 +371,6 @@ return response.data; | ||
let streams; | ||
options.type != 'audio' && (streams = filtered_streams.filter((format) => format.mimeType.includes(options.format || 'mp4') && format.qualityLabel == options.quality)) | ||
|| (streams = filtered_streams.filter((format) => format.mimeType.includes(options.format || 'mp4'))); | ||
options.type != 'audio' && (streams = filtered_streams.filter((format) => format.mimeType.includes(options.format || 'mp4') && format.qualityLabel == options.quality)) || | ||
(streams = filtered_streams.filter((format) => format.mimeType.includes(options.format || 'mp4'))); | ||
streams == undefined || streams.length == 0 && (streams = filtered_streams.filter((format) => format.quality == 'medium')); | ||
@@ -385,7 +389,7 @@ | ||
if (options.type == 'videoandaudio') { | ||
if (options.type == 'videoandaudio' && !options.range) { | ||
const response = await Axios.get(selected_format.url, { | ||
responseType: 'stream', | ||
cancelToken: new CancelToken(function executor(c) { cancel = c; }), | ||
headers: Constants.stream_headers() | ||
headers: Constants.STREAM_HEADERS | ||
}).catch((error) => error); | ||
@@ -419,7 +423,7 @@ | ||
const chunk_size = 1048576 * 10; // 10MB | ||
let chunk_start = 0; | ||
let chunk_end = chunk_size; | ||
let chunk_start = (options.range && options.range.start || 0); | ||
let chunk_end = (options.range && options.range.end || chunk_size); | ||
let downloaded_size = 0; | ||
let end = false; | ||
let must_end = false; | ||
@@ -429,8 +433,9 @@ stream.emit('start'); | ||
const downloadChunk = async () => { | ||
if (chunk_end >= selected_format.contentLength) end = true; | ||
(chunk_end >= selected_format.contentLength || options.range) && (must_end = true); | ||
options.range && (selected_format.contentLength = options.range.end); | ||
const response = await Axios.get(`${selected_format.url}&range=${chunk_start}-${chunk_end || ''}`, { | ||
responseType: 'stream', | ||
cancelToken: new CancelToken(function executor(c) { cancel = c; }), | ||
headers: Constants.stream_headers() | ||
headers: Constants.STREAM_HEADERS | ||
}).catch((error) => error); | ||
@@ -459,3 +464,3 @@ | ||
response.data.on('end', () => { | ||
if (!end) { | ||
if (!must_end && !options.range) { | ||
chunk_start = chunk_end + 1; | ||
@@ -467,3 +472,3 @@ chunk_end += chunk_size; | ||
response.data.pipe(stream, { end }); | ||
response.data.pipe(stream, { end: must_end }); | ||
}; | ||
@@ -470,0 +475,0 @@ downloadChunk(); |
@@ -24,3 +24,3 @@ 'use strict'; | ||
} | ||
enqueueActionGroup(group) { | ||
@@ -42,3 +42,3 @@ group.forEach((action) => { | ||
}; | ||
this.message_queue.push(message); | ||
@@ -54,3 +54,3 @@ }); | ||
data = { context: this.session.context, continuation: this.ctoken }; | ||
const livechat = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/live_chat/get_live_chat${this.session.logged_in && this.session.cookie.length < 1 ? '' : `?key=${this.session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session: this.session, data, desktop: true })); | ||
const livechat = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/live_chat/get_live_chat${this.session.logged_in && this.session.cookie.length < 1 ? '' : `?key=${this.session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session: this.session, data, desktop: true })); | ||
if (livechat instanceof Error) throw new Error(`Error polling livechat: ${livechat.message}`); | ||
@@ -68,9 +68,9 @@ | ||
}); | ||
this.message_queue = []; | ||
data = { context: this.session.context, videoId: this.video_id }; | ||
if (this.metadata_ctoken) data.continuation = this.metadata_ctoken; | ||
const updated_metadata = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/updated_metadata${this.session.logged_in && this.session.cookie.length < 1 ? '' : `?key=${this.session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session: this.session, data, desktop: true })); | ||
const updated_metadata = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/updated_metadata${this.session.logged_in && this.session.cookie.length < 1 ? '' : `?key=${this.session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session: this.session, data, desktop: true })); | ||
if (updated_metadata instanceof Error) throw new Error(`Error polling updated metadata: ${updated_metadata.message}`); | ||
@@ -88,19 +88,19 @@ this.metadata_ctoken = updated_metadata.data.continuation.timedContinuationData.continuation; | ||
}); | ||
this.livechat_poller = setTimeout(async () => await this.poll(), this.poll_intervals_ms); | ||
} | ||
async sendMessage(text) { | ||
const message = await Actions.livechat(this.session, 'live_chat/send_message', { text, channel_id: this.channel_id, video_id: this.video_id }); | ||
if (!message.success) return message; | ||
const deleteMessage = async () => { | ||
const menu = await Actions.livechat(this.session, 'live_chat/get_item_context_menu', { params: { params: message.data.actions[0].addChatItemAction.item.liveChatTextMessageRenderer.contextMenuEndpoint.liveChatItemContextMenuEndpoint.params, pbj: 1 } }); | ||
if (!menu.success) return menu; | ||
const chat_item_menu = menu.data.liveChatItemContextMenuSupportedRenderers.menuRenderer.items[0]; | ||
const cmd = await Actions.livechat(this.session, 'live_chat/moderate', { cmd_params: chat_item_menu.menuServiceItemRenderer.serviceEndpoint.moderateLiveChatEndpoint.params }); | ||
if (!cmd.success) return cmd; | ||
return { success: true, status_code: cmd.status_code }; | ||
@@ -125,5 +125,5 @@ }; | ||
} | ||
async blockUser(msg_params) { | ||
/* TODO: Implement this */ | ||
/* TODO: Implement this */ | ||
throw new Error('Not implemented'); | ||
@@ -130,0 +130,0 @@ } |
@@ -17,3 +17,2 @@ 'use strict'; | ||
try { | ||
// Identifies the necessary transformation data and emulates them accordingly. | ||
let transformations = this.getTransformationData(this.raw_code); | ||
@@ -23,23 +22,15 @@ transformations = transformations.map((el) => { | ||
const is_reverse_base64 = el.includes('case 65:'); | ||
if (el.includes('function(d){for(var')) { | ||
el = (arr) => this.pushSplice(arr); | ||
} else if (el.includes('d.push(e)')) { | ||
el = (arr, item) => this.push(arr, item); | ||
} else if (el.includes('d.reverse()')) { | ||
el = (arr) => this.reverse(arr); | ||
} else if (el.includes('d.length;d.splice(e,1)')) { | ||
el = (arr, index) => this.spliceOnce(arr, index); | ||
} else if (el.includes('d[0])[0])')) { | ||
el = (arr, index) => this.spliceTwice(arr, index); | ||
} else if (el.includes('reverse().forEach')) { | ||
el = (arr, index) => this.spliceReverseUnshift(arr, index); | ||
} else if (el.includes('f=d[0];d[0]')) { | ||
el = (arr, index) => this.swapFirstItem(arr, index); | ||
} else if (el.includes('unshift(d.pop())')) { | ||
el = (arr, index) => this.unshiftPop(arr, index); | ||
} else if (el.includes('switch')) { | ||
el = (arr, e) => this.translateAB(arr, e, is_reverse_base64); | ||
} else if (el === 'b') { | ||
el = n_token; | ||
} | ||
(({ // Identifies the transformation functions and emulates them accordingly. | ||
[Constants.FUNCS.PUSH]: () => el = (arr, i) => this.push(arr, i), | ||
[Constants.FUNCS.SPLICE]: () => el = (arr, i) => this.splice(arr, i), | ||
[Constants.FUNCS.SWAP0_1]: () => el = (arr, i) => this.swap0(arr, i), | ||
[Constants.FUNCS.SWAP0_2]: () => el = (arr, i) => this.swap0(arr, i), | ||
[Constants.FUNCS.ROTATE_1]: () => el = (arr, i) => this.rotate(arr, i), | ||
[Constants.FUNCS.ROTATE_2]: () => el = (arr, i) => this.rotate(arr, i), | ||
[Constants.FUNCS.REVERSE_1]: () => el = (arr) => this.reverse(arr), | ||
[Constants.FUNCS.REVERSE_2]: () => el = (arr) => this.reverse(arr), | ||
[Constants.FUNCS.BASE64_DIA]: () => el = () => this.getBase64Dia(is_reverse_base64), | ||
[Constants.FUNCS.TRANSLATE_1]: () => el = (arr, token) => this.translate1(arr, token, is_reverse_base64), | ||
[Constants.FUNCS.TRANSLATE_2]: () => el = (arr, token, base64_dic) => this.translate2(arr, token, base64_dic) | ||
})[this.getFunc(el)] || (() => el === 'b' && (el = n_token)))(); | ||
} | ||
@@ -56,7 +47,8 @@ return el; | ||
transformation_calls.forEach((data) => { | ||
const index = data.index; | ||
const param_index = data.params.split(',').map((param) => param.match(/c\[(.*?)\]/)[1]); | ||
transformations[index](transformations[param_index[0]], transformations[param_index[1]]); | ||
const base64_dia = (param_index[2] && transformations[param_index[2]]()); | ||
transformations[data.index](transformations[param_index[0]], transformations[param_index[1]], base64_dia); | ||
}); | ||
} catch (err) { | ||
console.error('Could not transform n-token, download may be throttled:', err); | ||
return n; | ||
@@ -68,48 +60,43 @@ } | ||
getFunc(el) { | ||
return el.match(Constants.FUNCS_REGEX); | ||
} | ||
getTransformationData() { | ||
// These variable names have always been the same since earlier player versions, so it should not be a problem for now. | ||
let transformation_data = `[${Utils.getStringBetweenStrings(this.raw_code.replace(/\n/g, ''), 'c=[', '];c')}]`; | ||
transformation_data = transformation_data | ||
.replace(/function\(d,e\)/g, '"function(d,e)') | ||
.replace(/function\(d\)/g, '"function(d)') | ||
.replace(/,b,/g, ',"b",') | ||
.replace(/,b/g, ',"b"') | ||
.replace(/b,/g, '"b",') | ||
.replace(/b]/g, '"b"]') | ||
.replace(/\[b/g, '["b"') | ||
.replace(/}]/g, '"]') | ||
.replace(/},/g, '}",') | ||
.replace(/""/g, '') | ||
.replace(/length]\)}"/g, 'length])}'); | ||
return JSON.parse(transformation_data); | ||
const data = `[${Utils.getStringBetweenStrings(this.raw_code.replace(/\n/g, ''), 'c=[', '];c')}]`; | ||
return JSON.parse(Constants.formatNTransformData(data)); | ||
} | ||
translateAB(arr, index, is_reverse_base64) { | ||
let characters = is_reverse_base64 && Constants.base64_alphabet.reverse || Constants.base64_alphabet.normal; | ||
translate1(arr, token, is_reverse_base64) { | ||
const characters = is_reverse_base64 && Constants.BASE64_DIALECT.REVERSE || Constants.BASE64_DIALECT.NORMAL; | ||
arr.forEach(function(char, index, loc) { | ||
this.push(loc[index] = characters[(characters.indexOf(char) - characters.indexOf(this[index]) + 64) % characters.length]); | ||
}, index.split('')); | ||
}, token.split('')); | ||
} | ||
unshiftPop(arr, index) { | ||
index = (index % arr.length + arr.length) % arr.length; | ||
for (; index--;) { | ||
arr.unshift(arr.pop()); | ||
} | ||
translate2(arr, token, characters) { | ||
let chars_length = characters.length; | ||
arr.forEach(function(char, index, loc) { | ||
this.push(loc[index] = characters[(characters.indexOf(char) - characters.indexOf(this[index]) + index + chars_length--) % characters.length]); | ||
}, token.split('')); | ||
} | ||
swapFirstItem(arr, index) { | ||
let oldValue = arr[0]; | ||
getBase64Dia(is_reverse_base64) { | ||
const characters = is_reverse_base64 && Constants.BASE64_DIALECT.REVERSE || Constants.BASE64_DIALECT.NORMAL; | ||
return characters; | ||
} | ||
swap0(arr, index) { | ||
const old_value = arr[0]; | ||
index = (index % arr.length + arr.length) % arr.length; | ||
arr[0] = arr[index]; | ||
arr[index] = oldValue; | ||
arr[index] = old_value; | ||
} | ||
spliceReverseUnshift(arr, index) { | ||
rotate(arr, index) { | ||
index = (index % arr.length + arr.length) % arr.length; | ||
arr.splice(-index).reverse().forEach((f) => arr.unshift(f)); | ||
arr.splice(-index).reverse().forEach((el) => arr.unshift(el)); | ||
} | ||
spliceOnce(arr, index) { | ||
splice(arr, index) { | ||
index = (index % arr.length + arr.length) % arr.length; | ||
@@ -119,21 +106,11 @@ arr.splice(index, 1); | ||
spliceTwice(arr, index) { | ||
index = (index % arr.length + arr.length) % arr.length; | ||
arr.splice(0, 1, arr.splice(index, 1, arr[0])[0]); | ||
reverse(arr) { | ||
arr.reverse(); | ||
} | ||
pushSplice(arr) { | ||
for (let index = arr.length; index;) | ||
arr.push(arr.splice(--index, 1)[0]); | ||
} | ||
push(arr, item) { | ||
arr.push(item); | ||
} | ||
reverse(arr) { | ||
arr.reverse(); | ||
} | ||
} | ||
module.exports = NToken; |
114
lib/OAuth.js
@@ -16,12 +16,12 @@ 'use strict'; | ||
// OAuth URLs: | ||
this.oauth_code_url = `${Constants.urls.YT_BASE_URL}/o/oauth2/device/code`; | ||
this.oauth_token_url = `${Constants.urls.YT_BASE_URL}/o/oauth2/token`; | ||
this.oauth_code_url = `${Constants.URLS.YT_BASE_URL}/o/oauth2/device/code`; | ||
this.oauth_token_url = `${Constants.URLS.YT_BASE_URL}/o/oauth2/token`; | ||
// Used to check whether an access token is valid or not. | ||
this.guide_url = `${Constants.urls.YT_BASE_URL}/youtubei/v1/guide`; | ||
this.guide_url = `${Constants.URLS.YT_BASE_URL}/youtubei/v1/guide`; | ||
// These are always the same, so we shouldn't have any problems for now. | ||
this.model_name = Constants.oauth.model_name; | ||
this.grant_type = Constants.oauth.grant_type; | ||
this.scope = Constants.oauth.scope; | ||
this.model_name = Constants.OAUTH.MODEL_NAME; | ||
this.grant_type = Constants.OAUTH.GRANT_TYPE; | ||
this.scope = Constants.OAUTH.SCOPE; | ||
@@ -38,2 +38,50 @@ // Script that contains important information such as client id and client secret. | ||
async requestAuthCode() { | ||
const identity = await this.getClientIdentity(); | ||
this.client_id = identity.id; | ||
this.client_secret = identity.secret; | ||
const data = { | ||
client_id: this.client_id, | ||
scope: this.scope, | ||
device_id: Uuid.v4(), | ||
model_name: this.model_name | ||
}; | ||
const response = await Axios.post(this.oauth_code_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); | ||
if (response instanceof Error) | ||
return this.emit('auth', { | ||
error: 'Could not get auth code.', | ||
status: 'FAILED' | ||
}); | ||
this.emit('auth', { | ||
code: response.data.user_code, | ||
status: 'AUTHORIZATION_PENDING', | ||
expires_in: response.data.expires_in, | ||
verification_url: response.data.verification_url | ||
}); | ||
this.refresh_interval = response.data.interval; | ||
// Keeps requesting at a specific rate until the authorization is granted or denied. | ||
this.waitForAuth(response.data.device_code); | ||
} | ||
async getClientIdentity() { | ||
// The first request is made to get the auth script url, hard-coding it isn't viable as it changes overtime. | ||
const yttv_response = await Axios.get(`${Constants.URLS.YT_BASE_URL}/tv`, Constants.OAUTH.HEADERS).catch((error) => error); | ||
if (yttv_response instanceof Error) throw new Error(`Could not extract client identify: ${yttv_response.message}`); | ||
// Here we get the script and extract the necessary data to proceed with the auth flow. | ||
const url_body = this.auth_script_regex.exec(yttv_response.data)[1]; | ||
const script_url = `${Constants.URLS.YT_BASE_URL}/${url_body}`; | ||
const response = await Axios.get(script_url, Constants.DEFAULT_HEADERS).catch((error) => error); | ||
if (response instanceof Error) throw new Error(`Could not extract client identify: ${response.message}`); | ||
const identity_function = Utils.getStringBetweenStrings(response.data, 'YTLR_STORAGE_NAMESPACE",', 'reloadAppFlushLogsMaxTimeoutMs:'); | ||
const client_identity = identity_function.replace(/\n/g, '').match(this.identity_regex); | ||
return client_identity.groups; | ||
} | ||
waitForAuth(device_code) { | ||
@@ -48,3 +96,3 @@ const data = { | ||
setTimeout(async () => { | ||
const response = await Axios.post(this.oauth_token_url, JSON.stringify(data), Constants.oauth_reqopts).catch((error) => error); | ||
const response = await Axios.post(this.oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); | ||
if (response instanceof Error) | ||
@@ -90,50 +138,2 @@ return this.emit('auth', { | ||
async requestAuthCode() { | ||
const identity = await this.getClientIdentity(); | ||
this.client_id = identity.id; | ||
this.client_secret = identity.secret; | ||
const data = { | ||
client_id: this.client_id, | ||
scope: this.scope, | ||
device_id: Uuid.v4(), | ||
model_name: this.model_name | ||
}; | ||
const response = await Axios.post(this.oauth_code_url, JSON.stringify(data), Constants.oauth_reqopts).catch((error) => error); | ||
if (response instanceof Error) | ||
return this.emit('auth', { | ||
error: 'Could not get auth code.', | ||
status: 'FAILED' | ||
}); | ||
this.emit('auth', { | ||
code: response.data.user_code, | ||
status: 'AUTHORIZATION_PENDING', | ||
expires_in: response.data.expires_in, | ||
verification_url: response.data.verification_url | ||
}); | ||
this.refresh_interval = response.data.interval; | ||
// Keeps requesting at a specific rate until the authorization is granted or denied. | ||
this.waitForAuth(response.data.device_code); | ||
} | ||
async getClientIdentity() { | ||
// The first request is made to get the auth script url, hard-coding it isn't viable as it changes overtime. | ||
const yttv_response = await Axios.get(`${Constants.urls.YT_BASE_URL}/tv`, Constants.oauth_reqopts).catch((error) => error); | ||
if (yttv_response instanceof Error) throw new Error(`Could not extract client identify: ${yttv_response.message}`); | ||
// Here we get the script and extract the necessary data to proceed with the auth flow. | ||
const url_body = this.auth_script_regex.exec(yttv_response.data)[1]; | ||
const script_url = `${Constants.urls.YT_BASE_URL}/${url_body}`; | ||
const response = await Axios.get(script_url, Constants.default_headers).catch((error) => error); | ||
if (response instanceof Error) throw new Error(`Could not extract client identify: ${response.message}`); | ||
const identity_function = Utils.getStringBetweenStrings(response.data, 'YTLR_STORAGE_NAMESPACE",', 'reloadAppFlushLogsMaxTimeoutMs:'); | ||
const client_identity = identity_function.replace(/\n/g, '').match(this.identity_regex); | ||
return client_identity.groups; | ||
} | ||
async refreshAccessToken(refresh_token) { | ||
@@ -149,3 +149,3 @@ const identity = await this.getClientIdentity(); | ||
const response = await Axios.post(this.oauth_token_url, JSON.stringify(data), Constants.oauth_reqopts).catch((error) => error); | ||
const response = await Axios.post(this.oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); | ||
if (response instanceof Error) | ||
@@ -167,3 +167,3 @@ return this.emit('refresh-token', { | ||
async checkTokenValidity(access_token, session) { | ||
let headers = Constants.innertube_request_opts({ session }).headers; | ||
let headers = Constants.INNERTUBE_REQOPTS({ session }).headers; | ||
headers.authorization = `Bearer ${access_token}`; | ||
@@ -170,0 +170,0 @@ |
@@ -21,5 +21,5 @@ 'use strict'; | ||
} else { | ||
const response = await Axios.get(`${Constants.urls.YT_BASE_URL}${this.session.player_url}`, { path: this.session.playerUrl, headers: { 'content-type': 'text/javascript', 'user-agent': Utils.getRandomUserAgent('desktop').userAgent } }).catch((error) => error); | ||
const response = await Axios.get(`${Constants.URLS.YT_BASE_URL}${this.session.player_url}`, { path: this.session.playerUrl, headers: { 'content-type': 'text/javascript', 'user-agent': Utils.getRandomUserAgent('desktop').userAgent } }).catch((error) => error); | ||
if (response instanceof Error) throw new Error('Could not download player script: ' + response.message); | ||
try { | ||
@@ -30,3 +30,3 @@ // Caches the current player so we don't have to download it all the time | ||
} catch (err) {} | ||
this.getSigDecipherCode(response.data); | ||
@@ -33,0 +33,0 @@ this.getNEncoder(response.data); |
@@ -42,3 +42,3 @@ 'use strict'; | ||
const youtube_proto = Proto(Fs.readFileSync(`${__dirname}/proto/youtube.proto`)); | ||
const buf = youtube_proto.NotificationPreferences.encode({ | ||
@@ -52,3 +52,3 @@ channel_id, | ||
}); | ||
return encodeURIComponent(Buffer.from(buf).toString('base64')); | ||
@@ -59,8 +59,8 @@ } | ||
const youtube_proto = Proto(Fs.readFileSync(`${__dirname}/proto/youtube.proto`)); | ||
const buf = youtube_proto.LiveMessageParams.encode({ | ||
params: { | ||
ids: { | ||
channel_id, | ||
video_id | ||
channel_id, | ||
video_id | ||
} | ||
@@ -71,3 +71,3 @@ }, | ||
}); | ||
return Buffer.from(encodeURIComponent(Buffer.from(buf).toString('base64'))).toString('base64'); | ||
@@ -74,0 +74,0 @@ } |
{ | ||
"name": "youtubei.js", | ||
"version": "1.2.4", | ||
"version": "1.2.5", | ||
"description": "An object-oriented library that allows you to search, get detailed info about videos, subscribe, unsubscribe, like, dislike, comment, download videos and much more!", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "node test" | ||
}, | ||
@@ -9,0 +9,0 @@ "author": "LuanRT", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
96433
15
1393
0
3