Update channel names and thumbnails when refreshing subscriptions (#4688)

This commit is contained in:
absidue 2024-03-07 14:49:54 +01:00 committed by GitHub
parent fdd675c693
commit 393f889d9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 272 additions and 58 deletions

View File

@ -121,6 +121,8 @@ export default defineComponent({
this.attemptedFetch = true this.attemptedFetch = true
this.errorChannels = [] this.errorChannels = []
const subscriptionUpdates = []
const postListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => { const postListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
let posts = [] let posts = []
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') { if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
@ -137,6 +139,32 @@ export default defineComponent({
channelId: channel.id, channelId: channel.id,
posts: posts, posts: posts,
}) })
if (posts.length > 0) {
const post = posts.find(post => post.authorId === channel.id)
if (post) {
const name = post.author
let thumbnailUrl = post.authorThumbnails?.[0]?.url
if (name || thumbnailUrl) {
if (thumbnailUrl) {
if (thumbnailUrl.startsWith('//')) {
thumbnailUrl = 'https:' + thumbnailUrl
} else if (thumbnailUrl.startsWith(`${this.currentInvidiousInstance}/ggpht`)) {
thumbnailUrl = thumbnailUrl.replace(`${this.currentInvidiousInstance}/ggpht`, 'https://yt3.googleusercontent.com')
}
}
subscriptionUpdates.push({
channelId: channel.id,
channelName: name,
channelThumbnailUrl: thumbnailUrl
})
}
}
}
return posts return posts
}))).flatMap((o) => o) }))).flatMap((o) => o)
postList.push(...postListFromRemote) postList.push(...postListFromRemote)
@ -147,6 +175,8 @@ export default defineComponent({
this.postList = postList this.postList = postList
this.isLoading = false this.isLoading = false
this.updateShowProgressBar(false) this.updateShowProgressBar(false)
this.batchUpdateSubscriptionDetails(subscriptionUpdates)
}, },
maybeLoadPostsForSubscriptionsFromRemote: async function () { maybeLoadPostsForSubscriptionsFromRemote: async function () {
@ -211,6 +241,7 @@ export default defineComponent({
...mapActions([ ...mapActions([
'updateShowProgressBar', 'updateShowProgressBar',
'batchUpdateSubscriptionDetails',
'updateSubscriptionPostsCacheByChannel', 'updateSubscriptionPostsCacheByChannel',
]), ]),

View File

@ -129,19 +129,23 @@ export default defineComponent({
this.attemptedFetch = true this.attemptedFetch = true
this.errorChannels = [] this.errorChannels = []
const subscriptionUpdates = []
const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => { const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
let videos = [] let videos = []
let name, thumbnailUrl
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') { if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
if (useRss) { if (useRss) {
videos = await this.getChannelLiveInvidiousRSS(channel) ({ videos, name, thumbnailUrl } = await this.getChannelLiveInvidiousRSS(channel))
} else { } else {
videos = await this.getChannelLiveInvidious(channel) ({ videos, name, thumbnailUrl } = await this.getChannelLiveInvidious(channel))
} }
} else { } else {
if (useRss) { if (useRss) {
videos = await this.getChannelLiveLocalRSS(channel) ({ videos, name, thumbnailUrl } = await this.getChannelLiveLocalRSS(channel))
} else { } else {
videos = await this.getChannelLiveLocal(channel) ({ videos, name, thumbnailUrl } = await this.getChannelLiveLocal(channel))
} }
} }
@ -152,6 +156,15 @@ export default defineComponent({
channelId: channel.id, channelId: channel.id,
videos: videos, videos: videos,
}) })
if (name || thumbnailUrl) {
subscriptionUpdates.push({
channelId: channel.id,
channelName: name,
channelThumbnailUrl: thumbnailUrl
})
}
return videos return videos
}))).flatMap((o) => o) }))).flatMap((o) => o)
videoList.push(...videoListFromRemote) videoList.push(...videoListFromRemote)
@ -159,6 +172,8 @@ export default defineComponent({
this.videoList = updateVideoListAfterProcessing(videoList) this.videoList = updateVideoListAfterProcessing(videoList)
this.isLoading = false this.isLoading = false
this.updateShowProgressBar(false) this.updateShowProgressBar(false)
this.batchUpdateSubscriptionDetails(subscriptionUpdates)
}, },
maybeLoadVideosForSubscriptionsFromRemote: async function () { maybeLoadVideosForSubscriptionsFromRemote: async function () {
@ -174,16 +189,18 @@ export default defineComponent({
getChannelLiveLocal: async function (channel, failedAttempts = 0) { getChannelLiveLocal: async function (channel, failedAttempts = 0) {
try { try {
const entries = await getLocalChannelLiveStreams(channel.id) const result = await getLocalChannelLiveStreams(channel.id)
if (entries === null) { if (result === null) {
this.errorChannels.push(channel) this.errorChannels.push(channel)
return [] return {
videos: []
}
} }
addPublishedDatesLocal(entries) addPublishedDatesLocal(result.videos)
return entries return result
} catch (err) { } catch (err) {
console.error(err) console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)') const errorMessage = this.$t('Local API Error (Click to copy)')
@ -198,12 +215,16 @@ export default defineComponent({
showToast(this.$t('Falling back to Invidious API')) showToast(this.$t('Falling back to Invidious API'))
return await this.getChannelLiveInvidious(channel, failedAttempts + 1) return await this.getChannelLiveInvidious(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
case 2: case 2:
return await this.getChannelLiveLocalRSS(channel, failedAttempts + 1) return await this.getChannelLiveLocalRSS(channel, failedAttempts + 1)
default: default:
return [] return {
videos: []
}
} }
} }
}, },
@ -227,7 +248,9 @@ export default defineComponent({
this.errorChannels.push(channel) this.errorChannels.push(channel)
} }
return [] return {
videos: []
}
} }
return await parseYouTubeRSSFeed(await response.text(), channel.id) return await parseYouTubeRSSFeed(await response.text(), channel.id)
@ -245,12 +268,16 @@ export default defineComponent({
showToast(this.$t('Falling back to Invidious API')) showToast(this.$t('Falling back to Invidious API'))
return this.getChannelLiveInvidiousRSS(channel, failedAttempts + 1) return this.getChannelLiveInvidiousRSS(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
case 2: case 2:
return this.getChannelLiveLocal(channel, failedAttempts + 1) return this.getChannelLiveLocal(channel, failedAttempts + 1)
default: default:
return [] return {
videos: []
}
} }
} }
}, },
@ -269,7 +296,16 @@ export default defineComponent({
addPublishedDatesInvidious(videos) addPublishedDatesInvidious(videos)
resolve(videos) let name
if (videos.length > 0) {
name = videos.find(video => video.author).author
}
resolve({
name,
videos
})
}).catch((err) => { }).catch((err) => {
console.error(err) console.error(err)
const errorMessage = this.$t('Invidious API Error (Click to copy)') const errorMessage = this.$t('Invidious API Error (Click to copy)')
@ -285,14 +321,18 @@ export default defineComponent({
showToast(this.$t('Falling back to Local API')) showToast(this.$t('Falling back to Local API'))
resolve(this.getChannelLiveLocal(channel, failedAttempts + 1)) resolve(this.getChannelLiveLocal(channel, failedAttempts + 1))
} else { } else {
resolve([]) resolve({
videos: []
})
} }
break break
case 2: case 2:
resolve(this.getChannelLiveInvidiousRSS(channel, failedAttempts + 1)) resolve(this.getChannelLiveInvidiousRSS(channel, failedAttempts + 1))
break break
default: default:
resolve([]) resolve({
videos: []
})
} }
}) })
}) })
@ -306,7 +346,9 @@ export default defineComponent({
const response = await fetch(feedUrl) const response = await fetch(feedUrl)
if (response.status === 500 || response.status === 404) { if (response.status === 500 || response.status === 404) {
return [] return {
videos: []
}
} }
return await parseYouTubeRSSFeed(await response.text(), channel.id) return await parseYouTubeRSSFeed(await response.text(), channel.id)
@ -324,17 +366,22 @@ export default defineComponent({
showToast(this.$t('Falling back to Local API')) showToast(this.$t('Falling back to Local API'))
return this.getChannelLiveLocalRSS(channel, failedAttempts + 1) return this.getChannelLiveLocalRSS(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
case 2: case 2:
return this.getChannelLiveInvidious(channel, failedAttempts + 1) return this.getChannelLiveInvidious(channel, failedAttempts + 1)
default: default:
return [] return {
videos: []
}
} }
} }
}, },
...mapActions([ ...mapActions([
'batchUpdateSubscriptionDetails',
'updateShowProgressBar', 'updateShowProgressBar',
'updateSubscriptionLiveCacheByChannel', 'updateSubscriptionLiveCacheByChannel',
]), ]),

View File

@ -114,12 +114,16 @@ export default defineComponent({
this.attemptedFetch = true this.attemptedFetch = true
this.errorChannels = [] this.errorChannels = []
const subscriptionUpdates = []
const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => { const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
let videos = [] let videos = []
let name
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') { if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
videos = await this.getChannelShortsInvidious(channel) ({ videos, name } = await this.getChannelShortsInvidious(channel))
} else { } else {
videos = await this.getChannelShortsLocal(channel) ({ videos, name } = await this.getChannelShortsLocal(channel))
} }
channelCount++ channelCount++
@ -129,6 +133,14 @@ export default defineComponent({
channelId: channel.id, channelId: channel.id,
videos: videos, videos: videos,
}) })
if (name) {
subscriptionUpdates.push({
channelId: channel.id,
channelName: name
})
}
return videos return videos
}))).flatMap((o) => o) }))).flatMap((o) => o)
videoList.push(...videoListFromRemote) videoList.push(...videoListFromRemote)
@ -136,6 +148,8 @@ export default defineComponent({
this.videoList = updateVideoListAfterProcessing(videoList) this.videoList = updateVideoListAfterProcessing(videoList)
this.isLoading = false this.isLoading = false
this.updateShowProgressBar(false) this.updateShowProgressBar(false)
this.batchUpdateSubscriptionDetails(subscriptionUpdates)
}, },
maybeLoadVideosForSubscriptionsFromRemote: async function () { maybeLoadVideosForSubscriptionsFromRemote: async function () {
@ -168,7 +182,9 @@ export default defineComponent({
this.errorChannels.push(channel) this.errorChannels.push(channel)
} }
return [] return {
videos: []
}
} }
return await parseYouTubeRSSFeed(await response.text(), channel.id) return await parseYouTubeRSSFeed(await response.text(), channel.id)
@ -184,10 +200,14 @@ export default defineComponent({
showToast(this.$t('Falling back to Invidious API')) showToast(this.$t('Falling back to Invidious API'))
return this.getChannelShortsInvidious(channel, failedAttempts + 1) return this.getChannelShortsInvidious(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
default: default:
return [] return {
videos: []
}
} }
} }
}, },
@ -200,7 +220,9 @@ export default defineComponent({
const response = await fetch(feedUrl) const response = await fetch(feedUrl)
if (response.status === 500 || response.status === 404) { if (response.status === 500 || response.status === 404) {
return [] return {
videos: []
}
} }
return await parseYouTubeRSSFeed(await response.text(), channel.id) return await parseYouTubeRSSFeed(await response.text(), channel.id)
@ -216,15 +238,20 @@ export default defineComponent({
showToast(this.$t('Falling back to Local API')) showToast(this.$t('Falling back to Local API'))
return this.getChannelShortsLocal(channel, failedAttempts + 1) return this.getChannelShortsLocal(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
default: default:
return [] return {
videos: []
}
} }
} }
}, },
...mapActions([ ...mapActions([
'batchUpdateSubscriptionDetails',
'updateShowProgressBar', 'updateShowProgressBar',
'updateSubscriptionShortsCacheByChannel', 'updateSubscriptionShortsCacheByChannel',
]), ]),

View File

@ -129,19 +129,23 @@ export default defineComponent({
this.attemptedFetch = true this.attemptedFetch = true
this.errorChannels = [] this.errorChannels = []
const subscriptionUpdates = []
const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => { const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
let videos = [] let videos = []
let name, thumbnailUrl
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') { if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
if (useRss) { if (useRss) {
videos = await this.getChannelVideosInvidiousRSS(channel) ({ videos, name, thumbnailUrl } = await this.getChannelVideosInvidiousRSS(channel))
} else { } else {
videos = await this.getChannelVideosInvidiousScraper(channel) ({ videos, name, thumbnailUrl } = await this.getChannelVideosInvidiousScraper(channel))
} }
} else { } else {
if (useRss) { if (useRss) {
videos = await this.getChannelVideosLocalRSS(channel) ({ videos, name, thumbnailUrl } = await this.getChannelVideosLocalRSS(channel))
} else { } else {
videos = await this.getChannelVideosLocalScraper(channel) ({ videos, name, thumbnailUrl } = await this.getChannelVideosLocalScraper(channel))
} }
} }
@ -152,6 +156,15 @@ export default defineComponent({
channelId: channel.id, channelId: channel.id,
videos: videos, videos: videos,
}) })
if (name || thumbnailUrl) {
subscriptionUpdates.push({
channelId: channel.id,
channelName: name,
channelThumbnailUrl: thumbnailUrl
})
}
return videos return videos
}))).flatMap((o) => o) }))).flatMap((o) => o)
videoList.push(...videoListFromRemote) videoList.push(...videoListFromRemote)
@ -159,6 +172,8 @@ export default defineComponent({
this.videoList = updateVideoListAfterProcessing(videoList) this.videoList = updateVideoListAfterProcessing(videoList)
this.isLoading = false this.isLoading = false
this.updateShowProgressBar(false) this.updateShowProgressBar(false)
this.batchUpdateSubscriptionDetails(subscriptionUpdates)
}, },
maybeLoadVideosForSubscriptionsFromRemote: async function () { maybeLoadVideosForSubscriptionsFromRemote: async function () {
@ -174,16 +189,18 @@ export default defineComponent({
getChannelVideosLocalScraper: async function (channel, failedAttempts = 0) { getChannelVideosLocalScraper: async function (channel, failedAttempts = 0) {
try { try {
const videos = await getLocalChannelVideos(channel.id) const result = await getLocalChannelVideos(channel.id)
if (videos === null) { if (result === null) {
this.errorChannels.push(channel) this.errorChannels.push(channel)
return [] return {
videos: []
}
} }
addPublishedDatesLocal(videos) addPublishedDatesLocal(result.videos)
return videos return result
} catch (err) { } catch (err) {
console.error(err) console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)') const errorMessage = this.$t('Local API Error (Click to copy)')
@ -198,12 +215,16 @@ export default defineComponent({
showToast(this.$t('Falling back to Invidious API')) showToast(this.$t('Falling back to Invidious API'))
return await this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1) return await this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
case 2: case 2:
return await this.getChannelVideosLocalRSS(channel, failedAttempts + 1) return await this.getChannelVideosLocalRSS(channel, failedAttempts + 1)
default: default:
return [] return {
videos: []
}
} }
} }
}, },
@ -227,7 +248,9 @@ export default defineComponent({
this.errorChannels.push(channel) this.errorChannels.push(channel)
} }
return [] return {
videos: []
}
} }
return await parseYouTubeRSSFeed(await response.text(), channel.id) return await parseYouTubeRSSFeed(await response.text(), channel.id)
@ -245,12 +268,16 @@ export default defineComponent({
showToast(this.$t('Falling back to Invidious API')) showToast(this.$t('Falling back to Invidious API'))
return this.getChannelVideosInvidiousRSS(channel, failedAttempts + 1) return this.getChannelVideosInvidiousRSS(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
case 2: case 2:
return this.getChannelVideosLocalScraper(channel, failedAttempts + 1) return this.getChannelVideosLocalScraper(channel, failedAttempts + 1)
default: default:
return [] return {
videos: []
}
} }
} }
}, },
@ -266,7 +293,16 @@ export default defineComponent({
invidiousAPICall(subscriptionsPayload).then((result) => { invidiousAPICall(subscriptionsPayload).then((result) => {
addPublishedDatesInvidious(result.videos) addPublishedDatesInvidious(result.videos)
resolve(result.videos) let name
if (result.videos.length > 0) {
name = result.videos.find(video => video.type === 'video' && video.author).author
}
resolve({
name,
videos: result.videos
})
}).catch((err) => { }).catch((err) => {
console.error(err) console.error(err)
const errorMessage = this.$t('Invidious API Error (Click to copy)') const errorMessage = this.$t('Invidious API Error (Click to copy)')
@ -282,14 +318,18 @@ export default defineComponent({
showToast(this.$t('Falling back to Local API')) showToast(this.$t('Falling back to Local API'))
resolve(this.getChannelVideosLocalScraper(channel, failedAttempts + 1)) resolve(this.getChannelVideosLocalScraper(channel, failedAttempts + 1))
} else { } else {
resolve([]) resolve({
videos: []
})
} }
break break
case 2: case 2:
resolve(this.getChannelVideosInvidiousRSS(channel, failedAttempts + 1)) resolve(this.getChannelVideosInvidiousRSS(channel, failedAttempts + 1))
break break
default: default:
resolve([]) resolve({
videos: []
})
} }
}) })
}) })
@ -304,7 +344,9 @@ export default defineComponent({
if (response.status === 500 || response.status === 404) { if (response.status === 500 || response.status === 404) {
this.errorChannels.push(channel) this.errorChannels.push(channel)
return [] return {
videos: []
}
} }
return await parseYouTubeRSSFeed(await response.text(), channel.id) return await parseYouTubeRSSFeed(await response.text(), channel.id)
@ -322,17 +364,22 @@ export default defineComponent({
showToast(this.$t('Falling back to Local API')) showToast(this.$t('Falling back to Local API'))
return this.getChannelVideosLocalRSS(channel, failedAttempts + 1) return this.getChannelVideosLocalRSS(channel, failedAttempts + 1)
} else { } else {
return [] return {
videos: []
}
} }
case 2: case 2:
return this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1) return this.getChannelVideosInvidiousScraper(channel, failedAttempts + 1)
default: default:
return [] return {
videos: []
}
} }
} }
}, },
...mapActions([ ...mapActions([
'batchUpdateSubscriptionDetails',
'updateShowProgressBar', 'updateShowProgressBar',
'updateSubscriptionVideosCacheByChannel', 'updateSubscriptionVideosCacheByChannel',
]), ]),

View File

@ -274,6 +274,9 @@ export async function getLocalChannel(id) {
return result return result
} }
/**
* @param {string} id
*/
export async function getLocalChannelVideos(id) { export async function getLocalChannelVideos(id) {
const innertube = await createInnertube() const innertube = await createInnertube()
@ -286,15 +289,22 @@ export async function getLocalChannelVideos(id) {
})) }))
const videosTab = new YT.Channel(null, response) const videosTab = new YT.Channel(null, response)
const { id: channelId = id, name, thumbnailUrl } = parseLocalChannelHeader(videosTab)
let videos
// 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')) {
const { id: channelId = id, name } = parseLocalChannelHeader(videosTab) videos = parseLocalChannelVideos(videosTab.videos, channelId, name)
return parseLocalChannelVideos(videosTab.videos, channelId, name)
} else { } else {
return [] videos = []
}
return {
name,
thumbnailUrl,
videos
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
@ -306,6 +316,9 @@ export async function getLocalChannelVideos(id) {
} }
} }
/**
* @param {string} id
*/
export async function getLocalChannelLiveStreams(id) { export async function getLocalChannelLiveStreams(id) {
const innertube = await createInnertube() const innertube = await createInnertube()
@ -318,15 +331,22 @@ export async function getLocalChannelLiveStreams(id) {
})) }))
const liveStreamsTab = new YT.Channel(null, response) const liveStreamsTab = new YT.Channel(null, response)
const { id: channelId = id, name, thumbnailUrl } = parseLocalChannelHeader(liveStreamsTab)
let videos
// 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')) {
const { id: channelId = id, name } = parseLocalChannelHeader(liveStreamsTab) videos = parseLocalChannelVideos(liveStreamsTab.videos, channelId, name)
return parseLocalChannelVideos(liveStreamsTab.videos, channelId, name)
} else { } else {
return [] videos = []
}
return {
name,
thumbnailUrl,
videos
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)

View File

@ -80,9 +80,14 @@ export async function parseYouTubeRSSFeed(rssString, channelId) {
promises.push(parseRSSEntry(entry, channelId, channelName)) promises.push(parseRSSEntry(entry, channelId, channelName))
} }
return await Promise.all(promises) return {
name: channelName,
videos: await Promise.all(promises)
}
} catch (e) { } catch (e) {
return [] return {
videos: []
}
} }
} }

View File

@ -91,6 +91,43 @@ const actions = {
commit('setProfileList', profiles) commit('setProfileList', profiles)
}, },
async batchUpdateSubscriptionDetails({ getters, dispatch }, channels) {
if (channels.length === 0) { return }
const profileList = getters.getProfileList
for (const profile of profileList) {
const currentProfileCopy = deepCopy(profile)
let profileUpdated = false
for (const { channelThumbnailUrl, channelName, channelId } of channels) {
const channel = currentProfileCopy.subscriptions.find((channel) => {
return channel.id === channelId
}) ?? null
if (channel === null) { continue }
if (channel.name !== channelName && channelName != null) {
channel.name = channelName
profileUpdated = true
}
if (channelThumbnailUrl) {
const thumbnail = channelThumbnailUrl.replace(/=s\d*/, '=s176') // change thumbnail size if different
if (channel.thumbnail !== thumbnail) {
channel.thumbnail = thumbnail
profileUpdated = true
}
}
}
if (profileUpdated) {
await dispatch('updateProfile', currentProfileCopy)
}
}
},
async updateSubscriptionDetails({ getters, dispatch }, { channelThumbnailUrl, channelName, channelId }) { async updateSubscriptionDetails({ getters, dispatch }, { channelThumbnailUrl, channelName, channelId }) {
const thumbnail = channelThumbnailUrl?.replace(/=s\d*/, '=s176') ?? null // change thumbnail size if different const thumbnail = channelThumbnailUrl?.replace(/=s\d*/, '=s176') ?? null // change thumbnail size if different
const profileList = getters.getProfileList const profileList = getters.getProfileList