mirror of https://github.com/FreeTubeApp/FreeTube
Support YouTube using PageHeader on user channels not just auto-generated ones (#4543)
* Support YouTube using PageHeader on user channels not just auto-generated ones * Bump YouTube.js to 9.0.2 as requested
This commit is contained in:
parent
3bce022616
commit
30248d6bbc
|
@ -1,5 +1,8 @@
|
||||||
{
|
{
|
||||||
"vueCompilerOptions": {
|
"vueCompilerOptions": {
|
||||||
"target": 2.7
|
"target": 2.7
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"strictNullChecks": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
"vue-observe-visibility": "^1.0.0",
|
"vue-observe-visibility": "^1.0.0",
|
||||||
"vue-router": "^3.6.5",
|
"vue-router": "^3.6.5",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
"youtubei.js": "^8.2.0"
|
"youtubei.js": "^9.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.23.9",
|
"@babel/core": "^7.23.9",
|
||||||
|
|
|
@ -290,7 +290,9 @@ export async function getLocalChannelVideos(id) {
|
||||||
// if the channel doesn't have a videos tab, YouTube returns the home tab instead
|
// if the channel doesn't have a videos tab, YouTube returns the home tab instead
|
||||||
// so we need to check that we got the right tab
|
// so we need to check that we got the right tab
|
||||||
if (videosTab.current_tab?.endpoint.metadata.url?.endsWith('/videos')) {
|
if (videosTab.current_tab?.endpoint.metadata.url?.endsWith('/videos')) {
|
||||||
return parseLocalChannelVideos(videosTab.videos, videosTab.header.author)
|
const { id: channelId = id, name } = parseLocalChannelHeader(videosTab)
|
||||||
|
|
||||||
|
return parseLocalChannelVideos(videosTab.videos, channelId, name)
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -320,7 +322,9 @@ export async function getLocalChannelLiveStreams(id) {
|
||||||
// if the channel doesn't have a live tab, YouTube returns the home tab instead
|
// if the channel doesn't have a live tab, YouTube returns the home tab instead
|
||||||
// so we need to check that we got the right tab
|
// so we need to check that we got the right tab
|
||||||
if (liveStreamsTab.current_tab?.endpoint.metadata.url?.endsWith('/streams')) {
|
if (liveStreamsTab.current_tab?.endpoint.metadata.url?.endsWith('/streams')) {
|
||||||
return parseLocalChannelVideos(liveStreamsTab.videos, liveStreamsTab.header.author)
|
const { id: channelId = id, name } = parseLocalChannelHeader(liveStreamsTab)
|
||||||
|
|
||||||
|
return parseLocalChannelVideos(liveStreamsTab.videos, channelId, name)
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -365,16 +369,158 @@ export async function getLocalChannelCommunity(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('youtubei.js').YTNodes.Video[]} videos
|
* @param {YT.Channel} channel
|
||||||
* @param {Misc.Author} author
|
|
||||||
*/
|
*/
|
||||||
export function parseLocalChannelVideos(videos, author) {
|
export function parseLocalChannelHeader(channel) {
|
||||||
|
/** @type {string=} */
|
||||||
|
let id
|
||||||
|
/** @type {string} */
|
||||||
|
let name
|
||||||
|
/** @type {string=} */
|
||||||
|
let thumbnailUrl
|
||||||
|
/** @type {string=} */
|
||||||
|
let bannerUrl
|
||||||
|
/** @type {string=} */
|
||||||
|
let subscriberText
|
||||||
|
/** @type {string[]} */
|
||||||
|
const tags = []
|
||||||
|
|
||||||
|
switch (channel.header.type) {
|
||||||
|
case 'C4TabbedHeader': {
|
||||||
|
// example: Linus Tech Tips
|
||||||
|
// https://www.youtube.com/channel/UCXuqSBlHAE6Xw-yeJA0Tunw
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('youtubei.js').YTNodes.C4TabbedHeader}
|
||||||
|
*/
|
||||||
|
const header = channel.header
|
||||||
|
|
||||||
|
id = header.author.id
|
||||||
|
name = header.author.name
|
||||||
|
thumbnailUrl = header.author.best_thumbnail.url
|
||||||
|
bannerUrl = header.banner?.[0]?.url
|
||||||
|
subscriberText = header.subscribers?.text
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'CarouselHeader': {
|
||||||
|
// examples: Music and YouTube Gaming
|
||||||
|
// https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ
|
||||||
|
// https://www.youtube.com/channel/UCOpNcN46UbXVtpKMrmU4Abg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('youtubei.js').YTNodes.CarouselHeader}
|
||||||
|
*/
|
||||||
|
const header = channel.header
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('youtubei.js').YTNodes.TopicChannelDetails}
|
||||||
|
*/
|
||||||
|
const topicChannelDetails = header.contents.find(node => node.type === 'TopicChannelDetails')
|
||||||
|
name = topicChannelDetails.title.text
|
||||||
|
subscriberText = topicChannelDetails.subtitle.text
|
||||||
|
thumbnailUrl = topicChannelDetails.avatar[0].url
|
||||||
|
|
||||||
|
if (channel.metadata.external_id) {
|
||||||
|
id = channel.metadata.external_id
|
||||||
|
} else {
|
||||||
|
id = topicChannelDetails.subscribe_button.channel_id
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'InteractiveTabbedHeader': {
|
||||||
|
// example: Minecraft - Topic
|
||||||
|
// https://www.youtube.com/channel/UCQvWX73GQygcwXOTSf_VDVg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('youtubei.js').YTNodes.InteractiveTabbedHeader}
|
||||||
|
*/
|
||||||
|
const header = channel.header
|
||||||
|
name = header.title.text
|
||||||
|
thumbnailUrl = header.box_art.at(-1).url
|
||||||
|
bannerUrl = header.banner[0]?.url
|
||||||
|
|
||||||
|
const badges = header.badges.map(badge => badge.label).filter(tag => tag)
|
||||||
|
tags.push(...badges)
|
||||||
|
|
||||||
|
id = channel.current_tab?.endpoint.payload.browseId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'PageHeader': {
|
||||||
|
// example: YouTube Gaming
|
||||||
|
// https://www.youtube.com/channel/UCOpNcN46UbXVtpKMrmU4Abg
|
||||||
|
|
||||||
|
// User channels (an A/B test at the time of writing)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('youtubei.js').YTNodes.PageHeader}
|
||||||
|
*/
|
||||||
|
const header = channel.header
|
||||||
|
|
||||||
|
name = header.content.title.text.text
|
||||||
|
if (header.content.image) {
|
||||||
|
if (header.content.image.type === 'ContentPreviewImageView') {
|
||||||
|
/** @type {import('youtubei.js').YTNodes.ContentPreviewImageView} */
|
||||||
|
const image = header.content.image
|
||||||
|
|
||||||
|
thumbnailUrl = image.image[0].url
|
||||||
|
} else {
|
||||||
|
/** @type {import('youtubei.js').YTNodes.DecoratedAvatarView} */
|
||||||
|
const image = header.content.image
|
||||||
|
thumbnailUrl = image.avatar?.image[0].url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!thumbnailUrl && channel.metadata.thumbnail) {
|
||||||
|
thumbnailUrl = channel.metadata.thumbnail[0].url
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.content.banner) {
|
||||||
|
bannerUrl = header.content.banner.image[0]?.url
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.content.actions) {
|
||||||
|
const modal = header.content.actions.actions_rows[0].actions[0].on_tap.modal
|
||||||
|
|
||||||
|
if (modal && modal.type === 'ModalWithTitleAndButton') {
|
||||||
|
/** @type {import('youtubei.js').YTNodes.ModalWithTitleAndButton} */
|
||||||
|
const typedModal = modal
|
||||||
|
|
||||||
|
id = typedModal.button.endpoint.next_endpoint?.payload.browseId
|
||||||
|
}
|
||||||
|
} else if (channel.metadata.external_id) {
|
||||||
|
id = channel.metadata.external_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.content.metadata) {
|
||||||
|
subscriberText = header.content.metadata.metadata_rows[0].metadata_parts[1].text.text
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
thumbnailUrl,
|
||||||
|
bannerUrl,
|
||||||
|
subscriberText,
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('youtubei.js').YTNodes.Video[]} videos
|
||||||
|
* @param {string} channelId
|
||||||
|
* @param {string} channelName
|
||||||
|
*/
|
||||||
|
export function parseLocalChannelVideos(videos, channelId, channelName) {
|
||||||
const parsedVideos = videos.map(parseLocalListVideo)
|
const parsedVideos = videos.map(parseLocalListVideo)
|
||||||
|
|
||||||
// fix empty author info
|
// fix empty author info
|
||||||
parsedVideos.forEach(video => {
|
parsedVideos.forEach(video => {
|
||||||
video.author = author.name
|
video.author = channelName
|
||||||
video.authorId = author.id
|
video.authorId = channelId
|
||||||
})
|
})
|
||||||
|
|
||||||
return parsedVideos
|
return parsedVideos
|
||||||
|
@ -382,16 +528,17 @@ export function parseLocalChannelVideos(videos, author) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('youtubei.js').YTNodes.ReelItem[]} shorts
|
* @param {import('youtubei.js').YTNodes.ReelItem[]} shorts
|
||||||
* @param {Misc.Author} author
|
* @param {string} channelId
|
||||||
|
* @param {string} channelName
|
||||||
*/
|
*/
|
||||||
export function parseLocalChannelShorts(shorts, author) {
|
export function parseLocalChannelShorts(shorts, channelId, channelName) {
|
||||||
return shorts.map(short => {
|
return shorts.map(short => {
|
||||||
return {
|
return {
|
||||||
type: 'video',
|
type: 'video',
|
||||||
videoId: short.id,
|
videoId: short.id,
|
||||||
title: short.title.text,
|
title: short.title.text,
|
||||||
author: author.name,
|
author: channelName,
|
||||||
authorId: author.id,
|
authorId: channelId,
|
||||||
viewCount: parseLocalSubscriberCount(short.views.text),
|
viewCount: parseLocalSubscriberCount(short.views.text),
|
||||||
lengthSeconds: ''
|
lengthSeconds: ''
|
||||||
}
|
}
|
||||||
|
@ -405,40 +552,43 @@ export function parseLocalChannelShorts(shorts, author) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Playlist|GridPlaylist} playlist
|
* @param {Playlist|GridPlaylist} playlist
|
||||||
* @param {Misc.Author} author
|
* @param {string} channelId
|
||||||
|
* @param {string} chanelName
|
||||||
*/
|
*/
|
||||||
export function parseLocalListPlaylist(playlist, author = undefined) {
|
export function parseLocalListPlaylist(playlist, channelId = undefined, channelName = undefined) {
|
||||||
let channelName
|
let internalChannelName
|
||||||
let channelId = null
|
let internalChannelId = null
|
||||||
/** @type {import('youtubei.js').YTNodes.PlaylistVideoThumbnail} */
|
|
||||||
const thumbnailRenderer = playlist.thumbnail_renderer
|
|
||||||
if (playlist.author && playlist.author.id !== 'N/A') {
|
if (playlist.author && playlist.author.id !== 'N/A') {
|
||||||
if (playlist.author instanceof Misc.Text) {
|
if (playlist.author instanceof Misc.Text) {
|
||||||
channelName = playlist.author.text
|
internalChannelName = playlist.author.text
|
||||||
|
|
||||||
if (author) {
|
if (channelId) {
|
||||||
channelId = author.id
|
internalChannelId = channelId
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
channelName = playlist.author.name
|
internalChannelName = playlist.author.name
|
||||||
channelId = playlist.author.id
|
internalChannelId = playlist.author.id
|
||||||
}
|
}
|
||||||
} else if (author) {
|
} else if (channelId || channelName) {
|
||||||
channelName = author.name
|
internalChannelName = channelName
|
||||||
channelId = author.id
|
internalChannelId = channelId
|
||||||
} else if (playlist.author?.name) {
|
} else if (playlist.author?.name) {
|
||||||
// auto-generated album playlists don't have an author
|
// auto-generated album playlists don't have an author
|
||||||
// so in search results, the author text is "Playlist" and doesn't have a link or channel ID
|
// so in search results, the author text is "Playlist" and doesn't have a link or channel ID
|
||||||
channelName = playlist.author.name
|
internalChannelName = playlist.author.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {import('youtubei.js').YTNodes.PlaylistVideoThumbnail} */
|
||||||
|
const thumbnailRenderer = playlist.thumbnail_renderer
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'playlist',
|
type: 'playlist',
|
||||||
dataSource: 'local',
|
dataSource: 'local',
|
||||||
title: playlist.title.text,
|
title: playlist.title.text,
|
||||||
thumbnail: thumbnailRenderer ? thumbnailRenderer.thumbnail[0].url : playlist.thumbnails[0].url,
|
thumbnail: thumbnailRenderer ? thumbnailRenderer.thumbnail[0].url : playlist.thumbnails[0].url,
|
||||||
channelName,
|
channelName: internalChannelName,
|
||||||
channelId,
|
channelId: internalChannelId,
|
||||||
playlistId: playlist.id,
|
playlistId: playlist.id,
|
||||||
videoCount: extractNumberFromString(playlist.video_count.text)
|
videoCount: extractNumberFromString(playlist.video_count.text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
import {
|
import {
|
||||||
getLocalChannel,
|
getLocalChannel,
|
||||||
getLocalChannelId,
|
getLocalChannelId,
|
||||||
|
parseLocalChannelHeader,
|
||||||
parseLocalChannelShorts,
|
parseLocalChannelShorts,
|
||||||
parseLocalChannelVideos,
|
parseLocalChannelVideos,
|
||||||
parseLocalCommunityPosts,
|
parseLocalCommunityPosts,
|
||||||
|
@ -532,90 +533,22 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let channelId
|
const parsedHeader = parseLocalChannelHeader(channel)
|
||||||
let subscriberText = null
|
|
||||||
let tags = []
|
|
||||||
|
|
||||||
switch (channel.header.type) {
|
const channelId = parsedHeader.id ?? this.id
|
||||||
case 'C4TabbedHeader': {
|
const subscriberText = parsedHeader.subscriberText ?? null
|
||||||
// example: Linus Tech Tips
|
let tags = parsedHeader.tags
|
||||||
// https://www.youtube.com/channel/UCXuqSBlHAE6Xw-yeJA0Tunw
|
|
||||||
|
|
||||||
/**
|
channelThumbnailUrl = parsedHeader.thumbnailUrl ?? this.subscriptionInfo?.thumbnail
|
||||||
* @type {import('youtubei.js').YTNodes.C4TabbedHeader}
|
channelName = parsedHeader.name ?? this.subscriptionInfo?.name
|
||||||
*/
|
|
||||||
const header = channel.header
|
|
||||||
|
|
||||||
channelId = header.author.id
|
if (channelThumbnailUrl?.startsWith('//')) {
|
||||||
channelName = header.author.name
|
|
||||||
channelThumbnailUrl = header.author.best_thumbnail.url
|
|
||||||
subscriberText = header.subscribers?.text
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'CarouselHeader': {
|
|
||||||
// examples: Music and YouTube Gaming
|
|
||||||
// https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ
|
|
||||||
// https://www.youtube.com/channel/UCOpNcN46UbXVtpKMrmU4Abg
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('youtubei.js').YTNodes.CarouselHeader}
|
|
||||||
*/
|
|
||||||
const header = channel.header
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('youtubei.js').YTNodes.TopicChannelDetails}
|
|
||||||
*/
|
|
||||||
const topicChannelDetails = header.contents.find(node => node.type === 'TopicChannelDetails')
|
|
||||||
channelName = topicChannelDetails.title.text
|
|
||||||
subscriberText = topicChannelDetails.subtitle.text
|
|
||||||
channelThumbnailUrl = topicChannelDetails.avatar[0].url
|
|
||||||
|
|
||||||
if (channel.metadata.external_id) {
|
|
||||||
channelId = channel.metadata.external_id
|
|
||||||
} else {
|
|
||||||
channelId = topicChannelDetails.subscribe_button.channel_id
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'InteractiveTabbedHeader': {
|
|
||||||
// example: Minecraft - Topic
|
|
||||||
// https://www.youtube.com/channel/UCQvWX73GQygcwXOTSf_VDVg
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('youtubei.js').YTNodes.InteractiveTabbedHeader}
|
|
||||||
*/
|
|
||||||
const header = channel.header
|
|
||||||
channelName = header.title.text
|
|
||||||
channelId = this.id
|
|
||||||
channelThumbnailUrl = header.box_art.at(-1).url
|
|
||||||
|
|
||||||
const badges = header.badges.map(badge => badge.label).filter(tag => tag)
|
|
||||||
tags.push(...badges)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'PageHeader': {
|
|
||||||
// example: YouTube Gaming (an A/B test at the time of writing)
|
|
||||||
// https://www.youtube.com/channel/UCOpNcN46UbXVtpKMrmU4Abg
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('youtubei.js').YTNodes.PageHeader}
|
|
||||||
*/
|
|
||||||
const header = channel.header
|
|
||||||
|
|
||||||
channelName = header.content.title.text
|
|
||||||
channelThumbnailUrl = header.content.image.image[0].url
|
|
||||||
channelId = this.id
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channelThumbnailUrl.startsWith('//')) {
|
|
||||||
channelThumbnailUrl = `https:${channelThumbnailUrl}`
|
channelThumbnailUrl = `https:${channelThumbnailUrl}`
|
||||||
}
|
}
|
||||||
|
|
||||||
this.channelName = channelName
|
this.channelName = channelName
|
||||||
this.thumbnailUrl = channelThumbnailUrl
|
this.thumbnailUrl = channelThumbnailUrl
|
||||||
|
this.bannerUrl = parsedHeader.bannerUrl ?? null
|
||||||
this.isFamilyFriendly = !!channel.metadata.is_family_safe
|
this.isFamilyFriendly = !!channel.metadata.is_family_safe
|
||||||
|
|
||||||
if (channel.metadata.tags) {
|
if (channel.metadata.tags) {
|
||||||
|
@ -646,12 +579,6 @@ export default defineComponent({
|
||||||
|
|
||||||
this.updateSubscriptionDetails({ channelThumbnailUrl, channelName, channelId })
|
this.updateSubscriptionDetails({ channelThumbnailUrl, channelName, channelId })
|
||||||
|
|
||||||
if (channel.header.banner?.length > 0) {
|
|
||||||
this.bannerUrl = channel.header.banner[0].url
|
|
||||||
} else {
|
|
||||||
this.bannerUrl = null
|
|
||||||
}
|
|
||||||
|
|
||||||
let relatedChannels = channel.channels.map(({ author }) => ({
|
let relatedChannels = channel.channels.map(({ author }) => ({
|
||||||
name: author.name,
|
name: author.name,
|
||||||
id: author.id,
|
id: author.id,
|
||||||
|
@ -837,7 +764,7 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.latestVideos = parseLocalChannelVideos(videosTab.videos, channel.header.author)
|
this.latestVideos = parseLocalChannelVideos(videosTab.videos, this.id, this.channelName)
|
||||||
this.videoContinuationData = videosTab.has_continuation ? videosTab : null
|
this.videoContinuationData = videosTab.has_continuation ? videosTab : null
|
||||||
this.isElementListLoading = false
|
this.isElementListLoading = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -862,7 +789,7 @@ export default defineComponent({
|
||||||
*/
|
*/
|
||||||
const continuation = await this.videoContinuationData.getContinuation()
|
const continuation = await this.videoContinuationData.getContinuation()
|
||||||
|
|
||||||
this.latestVideos = this.latestVideos.concat(parseLocalChannelVideos(continuation.videos, this.channelInstance.header.author))
|
this.latestVideos = this.latestVideos.concat(parseLocalChannelVideos(continuation.videos, this.id, this.channelName))
|
||||||
this.videoContinuationData = continuation.has_continuation ? continuation : null
|
this.videoContinuationData = continuation.has_continuation ? continuation : null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -895,7 +822,7 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.latestShorts = parseLocalChannelShorts(shortsTab.videos, channel.header.author)
|
this.latestShorts = parseLocalChannelShorts(shortsTab.videos, this.id, this.channelName)
|
||||||
this.shortContinuationData = shortsTab.has_continuation ? shortsTab : null
|
this.shortContinuationData = shortsTab.has_continuation ? shortsTab : null
|
||||||
this.isElementListLoading = false
|
this.isElementListLoading = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -920,7 +847,7 @@ export default defineComponent({
|
||||||
*/
|
*/
|
||||||
const continuation = await this.shortContinuationData.getContinuation()
|
const continuation = await this.shortContinuationData.getContinuation()
|
||||||
|
|
||||||
this.latestShorts.push(...parseLocalChannelShorts(continuation.videos, this.channelInstance.header.author))
|
this.latestShorts.push(...parseLocalChannelShorts(continuation.videos, this.id, this.channelName))
|
||||||
this.shortContinuationData = continuation.has_continuation ? continuation : null
|
this.shortContinuationData = continuation.has_continuation ? continuation : null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -953,7 +880,7 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.latestLive = parseLocalChannelVideos(liveTab.videos, channel.header.author)
|
this.latestLive = parseLocalChannelVideos(liveTab.videos, this.id, this.channelName)
|
||||||
this.liveContinuationData = liveTab.has_continuation ? liveTab : null
|
this.liveContinuationData = liveTab.has_continuation ? liveTab : null
|
||||||
this.isElementListLoading = false
|
this.isElementListLoading = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -978,7 +905,7 @@ export default defineComponent({
|
||||||
*/
|
*/
|
||||||
const continuation = await this.liveContinuationData.getContinuation()
|
const continuation = await this.liveContinuationData.getContinuation()
|
||||||
|
|
||||||
this.latestLive.push(...parseLocalChannelVideos(continuation.videos, this.channelInstance.header.author))
|
this.latestLive.push(...parseLocalChannelVideos(continuation.videos, this.id, this.channelName))
|
||||||
this.liveContinuationData = continuation.has_continuation ? continuation : null
|
this.liveContinuationData = continuation.has_continuation ? continuation : null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -1270,7 +1197,7 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.latestPlaylists = playlistsTab.playlists.map(playlist => parseLocalListPlaylist(playlist, channel.header.author))
|
this.latestPlaylists = playlistsTab.playlists.map(playlist => parseLocalListPlaylist(playlist, this.id, this.channelName))
|
||||||
this.playlistContinuationData = playlistsTab.has_continuation ? playlistsTab : null
|
this.playlistContinuationData = playlistsTab.has_continuation ? playlistsTab : null
|
||||||
this.isElementListLoading = false
|
this.isElementListLoading = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1295,7 +1222,7 @@ export default defineComponent({
|
||||||
*/
|
*/
|
||||||
const continuation = await this.playlistContinuationData.getContinuation()
|
const continuation = await this.playlistContinuationData.getContinuation()
|
||||||
|
|
||||||
const parsedPlaylists = continuation.playlists.map(playlist => parseLocalListPlaylist(playlist, this.channelInstance.header.author))
|
const parsedPlaylists = continuation.playlists.map(playlist => parseLocalListPlaylist(playlist, this.id, this.channelName))
|
||||||
this.latestPlaylists = this.latestPlaylists.concat(parsedPlaylists)
|
this.latestPlaylists = this.latestPlaylists.concat(parsedPlaylists)
|
||||||
this.playlistContinuationData = continuation.has_continuation ? continuation : null
|
this.playlistContinuationData = continuation.has_continuation ? continuation : null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1393,7 +1320,7 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.latestReleases = releaseTab.playlists.map(playlist => parseLocalListPlaylist(playlist, channel.header.author))
|
this.latestReleases = releaseTab.playlists.map(playlist => parseLocalListPlaylist(playlist, this.id, this.channelName))
|
||||||
this.releaseContinuationData = releaseTab.has_continuation ? releaseTab : null
|
this.releaseContinuationData = releaseTab.has_continuation ? releaseTab : null
|
||||||
this.isElementListLoading = false
|
this.isElementListLoading = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1418,7 +1345,7 @@ export default defineComponent({
|
||||||
*/
|
*/
|
||||||
const continuation = await this.releaseContinuationData.getContinuation()
|
const continuation = await this.releaseContinuationData.getContinuation()
|
||||||
|
|
||||||
const parsedReleases = continuation.playlists.map(playlist => parseLocalListPlaylist(playlist, this.channelInstance.header.author))
|
const parsedReleases = continuation.playlists.map(playlist => parseLocalListPlaylist(playlist, this.id, this.channelName))
|
||||||
this.latestReleases = this.latestReleases.concat(parsedReleases)
|
this.latestReleases = this.latestReleases.concat(parsedReleases)
|
||||||
this.releaseContinuationData = continuation.has_continuation ? continuation : null
|
this.releaseContinuationData = continuation.has_continuation ? continuation : null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1506,7 +1433,7 @@ export default defineComponent({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.latestPodcasts = podcastTab.playlists.map(playlist => parseLocalListPlaylist(playlist, channel.header.author))
|
this.latestPodcasts = podcastTab.playlists.map(playlist => parseLocalListPlaylist(playlist, this.id, this.channelName))
|
||||||
this.podcastContinuationData = podcastTab.has_continuation ? podcastTab : null
|
this.podcastContinuationData = podcastTab.has_continuation ? podcastTab : null
|
||||||
this.isElementListLoading = false
|
this.isElementListLoading = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1531,7 +1458,7 @@ export default defineComponent({
|
||||||
*/
|
*/
|
||||||
const continuation = await this.podcastContinuationData.getContinuation()
|
const continuation = await this.podcastContinuationData.getContinuation()
|
||||||
|
|
||||||
const parsedPodcasts = continuation.playlists.map(playlist => parseLocalListPlaylist(playlist, this.channelInstance.header.author))
|
const parsedPodcasts = continuation.playlists.map(playlist => parseLocalListPlaylist(playlist, this.id, this.channelName))
|
||||||
this.latestPodcasts = this.latestPodcasts.concat(parsedPodcasts)
|
this.latestPodcasts = this.latestPodcasts.concat(parsedPodcasts)
|
||||||
this.releaseContinuationData = continuation.has_continuation ? continuation : null
|
this.releaseContinuationData = continuation.has_continuation ? continuation : null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1857,7 +1784,7 @@ export default defineComponent({
|
||||||
if (item.type === 'Video') {
|
if (item.type === 'Video') {
|
||||||
return parseLocalListVideo(item)
|
return parseLocalListVideo(item)
|
||||||
} else {
|
} else {
|
||||||
return parseLocalListPlaylist(item, this.channelInstance.header.author)
|
return parseLocalListPlaylist(item, this.id, this.channelName)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -8850,10 +8850,10 @@ yocto-queue@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
||||||
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||||
|
|
||||||
youtubei.js@^8.2.0:
|
youtubei.js@^9.0.2:
|
||||||
version "8.2.0"
|
version "9.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-8.2.0.tgz#5b173f41fbe6240bb44cb733ce2c1f24e0b072ca"
|
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-9.0.2.tgz#77592a1144cdd51bb4258472265f5031b3966162"
|
||||||
integrity sha512-i/F4PEURSQmSYCQCo4dWKxOCZXhqkgAuGzNG2RUCtGSmlMX8TvwNewVD/JBjH/czdNmh9SJ00onNZMMxHbt+YA==
|
integrity sha512-D7GoJmupYaJxTNQyHRWYw8MUdQTxRaa3c7nzM9etWQjaexepFGVlVtwl3CybLx7GopBNtBvr7RxSUUIUyNnYIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
jintr "^1.1.0"
|
jintr "^1.1.0"
|
||||||
tslib "^2.5.0"
|
tslib "^2.5.0"
|
||||||
|
|
Loading…
Reference in New Issue