Upgrade YouTube.js from 4.3 to 5.0.2 (fixes throttling) (#3474)

* Upgrade YouTube.js from 4.3 to 5.0.1 (fixes throttling)

* Fix typo

* Upgrade YouTube.js from 5.0.1 to 5.0.2 (fixes watch page views)
This commit is contained in:
absidue 2023-05-01 15:12:23 +02:00 committed by GitHub
parent d3dcaaac53
commit 170df2a054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 67 additions and 96 deletions

View File

@ -75,7 +75,7 @@
"vue-router": "^3.6.5",
"vue-tiny-slider": "^0.1.39",
"vuex": "^3.6.2",
"youtubei.js": "^4.3.0"
"youtubei.js": "^5.0.2"
},
"devDependencies": {
"@babel/core": "^7.21.5",

View File

@ -170,7 +170,7 @@ export default defineComponent({
getCommentDataLocal: async function (more) {
try {
/** @type {import('youtubei.js/dist/src/parser/youtube/Comments').default} */
/** @type {import('youtubei.js').YT.Comments} */
let comments
if (more) {
comments = await this.nextPageToken.getContinuation()
@ -210,7 +210,7 @@ export default defineComponent({
try {
const comment = this.commentData[index]
/** @type {import('youtubei.js/dist/src/parser/classes/comments/CommentThread').default} */
/** @type {import('youtubei.js').YTNodes.CommentThread} */
const commentThread = comment.replyToken
if (comment.replies.length > 0) {

View File

@ -200,12 +200,12 @@ export default defineComponent({
},
/**
* @param {import('youtubei.js/dist/src/parser/classes/livechat/items/LiveChatTextMessage').default} comment
* @param {import('youtubei.js').YTNodes.LiveChatTextMessage} comment
*/
parseLiveChatComment: function (comment) {
/**
* can also be undefined if there is no badge
* @type {import('youtubei.js/dist/src/parser/classes/LiveChatAuthorBadge').default}
* @type {import('youtubei.js').YTNodes.LiveChatAuthorBadge}
*/
const badge = comment.author.badges.find(badge => badge.type === 'LiveChatAuthorBadge' && badge.custom_thumbnail)
@ -231,7 +231,7 @@ export default defineComponent({
},
/**
* @param {import('youtubei.js/dist/src/parser/classes/livechat/items/LiveChatPaidMessage').default} superChat
* @param {import('youtubei.js').YTNodes.LiveChatPaidMessage} superChat
*/
parseLiveChatSuperChat: function (superChat) {
const parsedComment = {

View File

@ -1,4 +1,4 @@
import { Innertube, ClientType, Misc, Utils, YT } from 'youtubei.js'
import { ClientType, Endpoints, Innertube, Misc, Utils, YT } from 'youtubei.js'
import Autolinker from 'autolinker'
import { join } from 'path'
@ -65,14 +65,10 @@ export async function getLocalPlaylist(id) {
return await innertube.getPlaylist(id)
}
/**
* @typedef {import('youtubei.js/dist/src/core/TabbedFeed').default} TabbedFeed
*/
/**
* @param {string} location
* @param {string} tab
* @param {TabbedFeed|null} instance
* @param {import('youtubei.js').Mixins.TabbedFeed|null} instance
*/
export async function getLocalTrending(location, tab, instance) {
if (instance === null) {
@ -107,11 +103,7 @@ export async function getLocalSearchResults(query, filters, safetyMode) {
}
/**
* @typedef {import('youtubei.js/dist/src/parser/youtube/Search').default} Search
*/
/**
* @param {Search} continuationData
* @param {YT.Search} continuationData
*/
export async function getLocalSearchContinuation(continuationData) {
const response = await continuationData.getContinuation()
@ -151,8 +143,8 @@ export async function getLocalComments(id, sortByNewest = false) {
}
/**
* @param {import('youtubei.js/dist/src/parser/classes/misc/Format').default[]} formats
* @param {import('youtubei.js/dist/index').Player} player
* @param {Misc.Format[]} formats
* @param {import('youtubei.js').Player} player
*/
function decipherFormats(formats, player) {
for (const format of formats) {
@ -206,12 +198,12 @@ export async function getLocalChannelVideos(id) {
const innertube = await createInnertube()
try {
const response = await innertube.actions.execute('/browse', {
browseId: id,
const response = await innertube.actions.execute(Endpoints.BrowseEndpoint.PATH, Endpoints.BrowseEndpoint.build({
browse_id: id,
params: 'EgZ2aWRlb3PyBgQKAjoA'
// protobuf for the videos tab (this is the one that YouTube uses,
// it has some empty fields in the protobuf but it doesn't work if you remove them)
})
}))
const videosTab = new YT.Channel(null, response)
@ -233,8 +225,8 @@ export async function getLocalChannelVideos(id) {
}
/**
* @param {import('youtubei.js/dist/src/parser/classes/Video').default[]} videos
* @param {import('youtubei.js/dist/src/parser/classes/misc/Author').default} author
* @param {import('youtubei.js').YTNodes.Video[]} videos
* @param {Misc.Author} author
*/
export function parseLocalChannelVideos(videos, author) {
const parsedVideos = videos.map(parseLocalListVideo)
@ -249,13 +241,13 @@ export function parseLocalChannelVideos(videos, author) {
}
/**
* @typedef {import('youtubei.js/dist/src/parser/classes/Playlist').default} Playlist
* @typedef {import('youtubei.js/dist/src/parser/classes/GridPlaylist').default} GridPlaylist
* @typedef {import('youtubei.js').YTNodes.Playlist} Playlist
* @typedef {import('youtubei.js').YTNodes.GridPlaylist} GridPlaylist
*/
/**
* @param {Playlist|GridPlaylist} playlist
* @param {import('youtubei.js/dist/src/parser/classes/misc/Author').default} author
* @param {Misc.Author} author
*/
export function parseLocalListPlaylist(playlist, author = undefined) {
let channelName
@ -290,7 +282,7 @@ export function parseLocalListPlaylist(playlist, author = undefined) {
}
/**
* @param {Search} response
* @param {YT.Search} response
*/
function handleSearchResponse(response) {
if (!response.results) {
@ -313,11 +305,7 @@ function handleSearchResponse(response) {
}
/**
* @typedef {import('youtubei.js/dist/src/parser/classes/PlaylistVideo').default} PlaylistVideo
*/
/**
* @param {PlaylistVideo} video
* @param {import('youtubei.js').YTNodes.PlaylistVideo} video
*/
export function parseLocalPlaylistVideo(video) {
return {
@ -333,7 +321,7 @@ export function parseLocalPlaylistVideo(video) {
}
/**
* @param {import('youtubei.js/dist/src/parser/classes/Video').default} video
* @param {import('youtubei.js').YTNodes.Video} video
*/
export function parseLocalListVideo(video) {
return {
@ -344,7 +332,7 @@ export function parseLocalListVideo(video) {
authorId: video.author.id,
description: video.description,
viewCount: extractNumberFromString(video.view_count.text),
publishedText: video.published.text !== 'N/A' ? video.published.text : null,
publishedText: video.published.isEmpty() ? null : video.published.text,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live,
isUpcoming: video.is_upcoming || video.is_premiere,
@ -353,14 +341,14 @@ export function parseLocalListVideo(video) {
}
/**
* @param {import('youtubei.js/dist/src/parser/helpers').YTNode} item
* @param {import('youtubei.js').Helpers.YTNode} item
*/
function parseListItem(item) {
switch (item.type) {
case 'Video':
return parseLocalListVideo(item)
case 'Channel': {
/** @type {import('youtubei.js/dist/src/parser/classes/Channel').default} */
/** @type {import('youtubei.js').YTNodes.Channel} */
const channel = item
// see upstream TODO: https://github.com/LuanRT/YouTube.js/blob/main/src/parser/classes/Channel.ts#L33
@ -370,17 +358,17 @@ function parseListItem(item) {
let subscribers = null
let videos = null
let handle = null
if (channel.subscribers.text?.startsWith('@')) {
handle = channel.subscribers.text
if (channel.subscriber_count.text?.startsWith('@')) {
handle = channel.subscriber_count.text
if (channel.videos.text !== 'N/A') {
subscribers = channel.videos.text
if (!channel.video_count.isEmpty()) {
subscribers = channel.video_count.text
}
} else {
videos = extractNumberFromString(channel.videos.text)
videos = extractNumberFromString(channel.video_count.text)
if (channel.subscribers.text !== 'N/A') {
subscribers = channel.subscribers.text
if (!channel.subscriber_count.isEmpty()) {
subscribers = channel.subscriber_count.text
}
}
@ -403,11 +391,7 @@ function parseListItem(item) {
}
/**
* @typedef {import('youtubei.js/dist/src/parser/classes/CompactVideo').default} CompactVideo
*/
/**
* @param {CompactVideo} video
* @param {import('youtubei.js').YTNodes.CompactVideo} video
*/
export function parseLocalWatchNextVideo(video) {
return {
@ -417,7 +401,7 @@ export function parseLocalWatchNextVideo(video) {
author: video.author.name,
authorId: video.author.id,
viewCount: extractNumberFromString(video.view_count.text),
publishedText: video.published.text === 'N/A' ? null : video.published.text,
publishedText: video.published.isEmpty() ? null : video.published.text,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live,
isUpcoming: video.is_premiere
@ -452,12 +436,7 @@ function convertSearchFilters(filters) {
}
/**
* @typedef {import('youtubei.js/dist/src/parser/classes/misc/TextRun').default} TextRun
* @typedef {import('youtubei.js/dist/src/parser/classes/misc/EmojiRun').default} EmojiRun
*/
/**
* @param {(TextRun|EmojiRun)[]} runs
* @param {(Misc.TextRun|Misc.EmojiRun)[]} runs
* @param {number} emojiSize
* @param {{looseChannelNameDetection: boolean}} options
*/
@ -527,7 +506,7 @@ export function parseLocalTextRuns(runs, emojiSize = 16, options = { looseChanne
break
case 'WEB_PAGE_TYPE_UNKNOWN':
default: {
const url = new URL(endpoint.payload?.content?.confirmDialogRenderer?.confirmButton?.buttonRenderer?.command?.urlEndpoint?.url || endpoint.payload.url)
const url = new URL((endpoint.dialog?.type === 'ConfirmDialog' && endpoint.dialog.confirm_button.endpoint.payload.url) || endpoint.payload.url)
if (url.hostname === 'www.youtube.com' && url.pathname === '/redirect' && url.searchParams.has('q')) {
// remove utm tracking parameters
const realURL = new URL(url.searchParams.get('q'))
@ -569,11 +548,7 @@ export function parseLocalTextRuns(runs, emojiSize = 16, options = { looseChanne
}
/**
* @typedef {import('youtubei.js/dist/src/parser/classes/misc/Format').default} Format
*/
/**
* @param {Format} format
* @param {Misc.Format} format
*/
export function mapLocalFormat(format) {
return {
@ -589,8 +564,8 @@ export function mapLocalFormat(format) {
}
/**
* @param {import('youtubei.js/dist/src/parser/classes/comments/Comment').default} comment
* @param {import('youtubei.js/dist/src/parser/classes/comments/CommentThread').default} commentThread
* @param {import('youtubei.js').YTNodes.Comment} comment
* @param {import('youtubei.js').YTNodes.CommentThread} commentThread
*/
export function parseLocalComment(comment, commentThread = undefined) {
let hasOwnerReplied = false
@ -625,7 +600,7 @@ export function parseLocalComment(comment, commentThread = undefined) {
/**
* video.js only supports MP4 DASH not WebM DASH
* so we filter out the WebM DASH formats
* @param {Format[]} formats
* @param {Misc.Format[]} formats
* @param {boolean} allowAv1 Use the AV1 formats if they are available
*/
export function filterLocalFormats(formats, allowAv1 = false) {
@ -683,7 +658,7 @@ export function parseLocalSubscriberCount(text) {
/**
* Parse community posts
* @param {import('youtubei.js/dist/src/parser/classes/BackstagePost').default} post
* @param {import('youtubei.js').YTNodes.BackstagePost} post
*/
export function parseLocalCommunityPost(post) {
let replyCount = post.action_buttons?.reply_button?.text ?? null

View File

@ -412,7 +412,7 @@ export default defineComponent({
this.setErrorMessage(channel.alert)
return
} else if (channel.memo.has('ChannelAgeGate')) {
/** @type {import('youtubei.js/dist/src/parser/classes/ChannelAgeGate').default} */
/** @type {import('youtubei.js').YTNodes.ChannelAgeGate} */
const ageGate = channel.memo.get('ChannelAgeGate')[0]
channelName = ageGate.channel_title
@ -444,7 +444,7 @@ export default defineComponent({
// https://www.youtube.com/channel/UCXuqSBlHAE6Xw-yeJA0Tunw
/**
* @type {import('youtubei.js/dist/src/parser/classes/C4TabbedHeader').default}
* @type {import('youtubei.js').YTNodes.C4TabbedHeader}
*/
const header = channel.header
@ -460,12 +460,12 @@ export default defineComponent({
// https://www.youtube.com/channel/UCOpNcN46UbXVtpKMrmU4Abg
/**
* @type {import('youtubei.js/dist/src/parser/classes/CarouselHeader').default}
* @type {import('youtubei.js').YTNodes.CarouselHeader}
*/
const header = channel.header
/**
* @type {import('youtubei.js/dist/src/parser/classes/TopicChannelDetails').default}
* @type {import('youtubei.js').YTNodes.TopicChannelDetails}
*/
const topicChannelDetails = header.contents.find(node => node.type === 'TopicChannelDetails')
channelName = topicChannelDetails.title.text
@ -484,7 +484,7 @@ export default defineComponent({
// https://www.youtube.com/channel/UCQvWX73GQygcwXOTSf_VDVg
/**
* @type {import('youtubei.js/dist/src/parser/classes/InteractiveTabbedHeader').default}
* @type {import('youtubei.js').YTNodes.InteractiveTabbedHeader}
*/
const header = channel.header
channelName = header.title.text
@ -597,19 +597,19 @@ export default defineComponent({
getChannelAboutLocal: async function () {
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').default}
* @type {import('youtubei.js').YT.Channel}
*/
const channel = this.channelInstance
const about = await channel.getAbout()
this.description = about.description.text !== 'N/A' ? autolinker.link(about.description.text) : ''
this.description = about.description.isEmpty() ? '' : autolinker.link(about.description.text)
const views = extractNumberFromString(about.views.text)
const views = extractNumberFromString(about.view_count.text)
this.views = isNaN(views) ? null : views
this.joined = about.joined.text !== 'N/A' ? new Date(about.joined.text.replace('Joined').trim()) : 0
this.joined = about.joined_date.isEmpty() ? 0 : new Date(about.joined_date.text.replace('Joined').trim())
this.location = about.country.text !== 'N/A' ? about.country.text : null
this.location = about.country.isEmpty() ? null : about.country.text
} catch (err) {
console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)')
@ -631,7 +631,7 @@ export default defineComponent({
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').default}
* @type {import('youtubei.js').YT.Channel}
*/
const channel = this.channelInstance
let videosTab = await channel.getVideos()
@ -668,7 +668,7 @@ export default defineComponent({
channelLocalNextPage: async function () {
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').ChannelListContinuation|import('youtubei.js/dist/src/parser/youtube/Channel').FilteredChannelList}
* @type {import('youtubei.js').YT.ChannelListContinuation|import('youtubei.js').YT.FilteredChannelList}
*/
const continuation = await this.videoContinuationData.getContinuation()
@ -689,7 +689,7 @@ export default defineComponent({
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').default}
* @type {import('youtubei.js').YT.Channel}
*/
const channel = this.channelInstance
let liveTab = await channel.getLiveStreams()
@ -726,7 +726,7 @@ export default defineComponent({
getChannelLiveLocalMore: async function () {
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').ChannelListContinuation|import('youtubei.js/dist/src/parser/youtube/Channel').FilteredChannelList}
* @type {import('youtubei.js').YT.ChannelListContinuation|import('youtubei.js').YT.FilteredChannelList}
*/
const continuation = await this.liveContinuationData.getContinuation()
@ -909,7 +909,7 @@ export default defineComponent({
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').default}
* @type {import('youtubei.js').YT.Channel}
*/
const channel = this.channelInstance
let playlistsTab = await channel.getPlaylists()
@ -919,7 +919,7 @@ export default defineComponent({
if (playlistsTab.content_type_filters.length > 1) {
/**
* @type {import('youtubei.js/dist/src/parser/classes/ChannelSubMenu').default}
* @type {import('youtubei.js').YTNodes.ChannelSubMenu}
*/
const menu = playlistsTab.current_tab.content.sub_menu
const createdPlaylistsFilter = menu.content_type_sub_menu_items.find(contentType => {
@ -964,7 +964,7 @@ export default defineComponent({
getChannelPlaylistsLocalMore: async function () {
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').ChannelListContinuation}
* @type {import('youtubei.js').YT.ChannelListContinuation}
*/
const continuation = await this.playlistContinuationData.getContinuation()
@ -1054,12 +1054,12 @@ export default defineComponent({
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').default}
* @type {import('youtubei.js').YT.Channel}
*/
const channel = this.channelInstance
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').default|import('youtubei.js/dist/src/parser/youtube/Channel').ChannelListContinuation}
* @type {import('youtubei.js').YT.Channel|import('youtubei.js').YT.ChannelListContinuation}
*/
let communityTab = await channel.getCommunity()
if (expectedId !== this.id) {
@ -1095,7 +1095,7 @@ export default defineComponent({
getCommunityPostsLocalMore: async function () {
try {
/**
* @type {import('youtubei.js/dist/src/parser/youtube/Channel').ChannelListContinuation}
* @type {import('youtubei.js').YT.ChannelListContinuation}
*/
let continuation = await this.communityContinuationData.getContinuation()

View File

@ -268,8 +268,7 @@ export default defineComponent({
if (playabilityStatus.status === 'UNPLAYABLE') {
/**
* @typedef {import('youtubei.js/dist/src/parser/classes/PlayerErrorMessage').default} PlayerErrorMessage
* @type {PlayerErrorMessage}
* @type {import ('youtubei.js').YTNodes.PlayerErrorMessage}
*/
const errorScreen = playabilityStatus.error_screen
throw new Error(`[${playabilityStatus.status}] ${errorScreen.reason.text}: ${errorScreen.subreason.text}`)
@ -1217,10 +1216,7 @@ export default defineComponent({
},
/**
* @typedef {import('youtubei.js/dist/src/parser/youtube/VideoInfo').default} VideoInfo
*/
/**
* @param {VideoInfo} videoInfo
* @param {import('youtubei.js').YT.VideoInfo} videoInfo
*/
createLocalDashManifest: async function (videoInfo) {
const xmlData = await videoInfo.toDash()

View File

@ -8446,10 +8446,10 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
youtubei.js@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-4.3.0.tgz#2872ba878c3ee32c77152015267a47275da49459"
integrity sha512-HdU6Awdr1nUWy0Ph7WdmoYPWL0ovx+S4w40eeTzAENr5xiUENsLuXcvULRc2fRCIxi+n7Q6142VVhmM4yK/g5g==
youtubei.js@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-5.0.2.tgz#2ce14dcb041fc18ff3ceb86c53abd78ce763f4f1"
integrity sha512-Fw2W0TaK50Vqkln3kr7DQ/b5Ve/f9UPh7DizPa4AWtabEyVJ68LNRaEbJp+5oSshbutHs8Q86zMtz+hxy2oQdw==
dependencies:
jintr "^1.0.0"
linkedom "^0.14.12"