diff --git a/src/renderer/store/modules/subscriptions.js b/src/renderer/store/modules/subscriptions.js index 76f34bcb9..c85168423 100644 --- a/src/renderer/store/modules/subscriptions.js +++ b/src/renderer/store/modules/subscriptions.js @@ -54,6 +54,10 @@ const actions = { commit('updateShortsCacheByChannel', payload) }, + updateSubscriptionShortsCacheWithChannelPageShorts: ({ commit }, payload) => { + commit('updateShortsCacheWithChannelPageShorts', payload) + }, + updateSubscriptionLiveCacheByChannel: ({ commit }, payload) => { commit('updateLiveCacheByChannel', payload) }, @@ -86,6 +90,31 @@ const mutations = { if (videos != null) { newObject.videos = videos } state.shortsCache[channelId] = newObject }, + updateShortsCacheWithChannelPageShorts(state, { channelId, videos }) { + const cachedObject = state.shortsCache[channelId] + + if (cachedObject && cachedObject.videos.length > 0) { + cachedObject.videos.forEach(cachedVideo => { + const channelVideo = videos.find(short => cachedVideo.videoId === short.videoId) + + if (channelVideo) { + // authorId probably never changes, so we don't need to update that + + cachedVideo.title = channelVideo.title + cachedVideo.author = channelVideo.author + + // as the channel shorts page only has compact view counts for numbers above 1000 e.g. 12k + // and the RSS feeds include an exact value, we only want to overwrite it when the number is larger than the cached value + // 12345 vs 12000 => 12345 + // 12345 vs 15000 => 15000 + + if (channelVideo.viewCount > cachedVideo.viewCount) { + cachedVideo.viewCount = channelVideo.viewCount + } + } + }) + } + }, clearShortsCache(state) { state.shortsCache = {} }, diff --git a/src/renderer/views/Channel/Channel.js b/src/renderer/views/Channel/Channel.js index b1b795720..90dfb5bd5 100644 --- a/src/renderer/views/Channel/Channel.js +++ b/src/renderer/views/Channel/Channel.js @@ -33,6 +33,10 @@ import { parseLocalListVideo, parseLocalSubscriberCount } from '../../helpers/api/local' +import { + addPublishedDatesInvidious, + addPublishedDatesLocal +} from '../../helpers/subscriptions' export default defineComponent({ name: 'Channel', @@ -171,6 +175,13 @@ export default defineComponent({ return this.subscriptionInfo !== null }, + isSubscribedInAnyProfile: function () { + const profileList = this.$store.getters.getProfileList + + // check the all channels profile + return profileList[0].subscriptions.some((channel) => channel.id === this.id) + }, + videoLiveSelectNames: function () { return [ this.$t('Channel.Videos.Sort Types.Newest'), @@ -767,6 +778,17 @@ export default defineComponent({ this.latestVideos = parseLocalChannelVideos(videosTab.videos, this.id, this.channelName) this.videoContinuationData = videosTab.has_continuation ? videosTab : null this.isElementListLoading = false + + if (this.isSubscribedInAnyProfile && this.latestVideos.length > 0 && this.videoSortBy === 'newest') { + addPublishedDatesLocal(this.latestVideos) + this.updateSubscriptionVideosCacheByChannel({ + channelId: this.id, + // create a copy so that we only cache the first page + // if we use the same array, the store will get angry at us for modifying it outside of the store, + // when the user clicks load more + videos: [...this.latestVideos] + }) + } } catch (err) { console.error(err) const errorMessage = this.$t('Local API Error (Click to copy)') @@ -825,6 +847,16 @@ export default defineComponent({ this.latestShorts = parseLocalChannelShorts(shortsTab.videos, this.id, this.channelName) this.shortContinuationData = shortsTab.has_continuation ? shortsTab : null this.isElementListLoading = false + + if (this.isSubscribedInAnyProfile && this.latestShorts.length > 0 && this.shortSortBy === 'newest') { + // As the shorts tab API response doesn't include the published dates, + // we can't just write the results to the subscriptions cache like we do with videos and live (can't sort chronologically without the date). + // However we can still update the metadata in the cache such as the view count and title that might have changed since it was cached + this.updateSubscriptionShortsCacheWithChannelPageShorts({ + channelId: this.id, + videos: this.latestShorts + }) + } } catch (err) { console.error(err) const errorMessage = this.$t('Local API Error (Click to copy)') @@ -883,6 +915,17 @@ export default defineComponent({ this.latestLive = parseLocalChannelVideos(liveTab.videos, this.id, this.channelName) this.liveContinuationData = liveTab.has_continuation ? liveTab : null this.isElementListLoading = false + + if (this.isSubscribedInAnyProfile && this.latestLive.length > 0 && this.liveSortBy === 'newest') { + addPublishedDatesLocal(this.latestLive) + this.updateSubscriptionLiveCacheByChannel({ + channelId: this.id, + // create a copy so that we only cache the first page + // if we use the same array, the store will get angry at us for modifying it outside of the store, + // when the user clicks load more + videos: [...this.latestLive] + }) + } } catch (err) { console.error(err) const errorMessage = this.$t('Local API Error (Click to copy)') @@ -1052,6 +1095,16 @@ export default defineComponent({ } this.videoContinuationData = response.continuation || null this.isElementListLoading = false + + if (this.isSubscribedInAnyProfile && !more && this.latestVideos.length > 0 && this.videoSortBy === 'newest') { + addPublishedDatesInvidious(this.latestVideos) + this.updateSubscriptionVideosCacheByChannel({ + channelId: this.id, + // create a copy so that we only cache the first page + // if we use the same array, it will also contain all the next pages + videos: [...this.latestVideos] + }) + } }).catch((err) => { console.error(err) const errorMessage = this.$t('Invidious API Error (Click to copy)') @@ -1101,6 +1154,17 @@ export default defineComponent({ } this.shortContinuationData = response.continuation || null this.isElementListLoading = false + + if (this.isSubscribedInAnyProfile && !more && this.latestShorts.length > 0 && this.shortSortBy === 'newest') { + // As the shorts tab API response doesn't include the published dates, + // we can't just write the results to the subscriptions cache like we do with videos and live (can't sort chronologically without the date). + // However we can still update the metadata in the cache e.g. adding the duration, as that isn't included in the RSS feeds + // and updating the view count and title that might have changed since it was cached + this.updateSubscriptionShortsCacheWithChannelPageShorts({ + channelId: this.id, + videos: this.latestShorts + }) + } }).catch((err) => { console.error(err) const errorMessage = this.$t('Invidious API Error (Click to copy)') @@ -1142,6 +1206,17 @@ export default defineComponent({ } this.liveContinuationData = response.continuation || null this.isElementListLoading = false + + if (this.isSubscribedInAnyProfile && !more && this.latestLive.length > 0 && this.liveSortBy === 'newest') { + addPublishedDatesInvidious(this.latestLive) + this.updateSubscriptionLiveCacheByChannel({ + channelId: this.id, + // create a copy so that we only cache the first page + // if we use the same array, the store will get angry at us for modifying it outside of the store, + // when the user clicks load more + videos: [...this.latestLive] + }) + } }).catch((err) => { console.error(err) const errorMessage = this.$t('Invidious API Error (Click to copy)') @@ -1559,6 +1634,19 @@ export default defineComponent({ this.latestCommunityPosts = parseLocalCommunityPosts(posts) this.communityContinuationData = communityTab.has_continuation ? communityTab : null + + if (this.latestCommunityPosts.length > 0) { + this.latestCommunityPosts.forEach(post => { + post.authorId = this.id + }) + this.updateSubscriptionPostsCacheByChannel({ + channelId: this.id, + // create a copy so that we only cache the first page + // if we use the same array, the store will get angry at us for modifying it outside of the store, + // when the user clicks load more + posts: [...this.latestCommunityPosts] + }) + } } catch (err) { console.error(err) const errorMessage = this.$t('Local API Error (Click to copy)') @@ -1610,6 +1698,19 @@ export default defineComponent({ this.latestCommunityPosts = posts } this.communityContinuationData = continuation + + if (this.isSubscribedInAnyProfile && !more && this.latestCommunityPosts.length > 0) { + this.latestCommunityPosts.forEach(post => { + post.authorId = this.id + }) + this.updateSubscriptionPostsCacheByChannel({ + channelId: this.id, + // create a copy so that we only cache the first page + // if we use the same array, the store will get angry at us for modifying it outside of the store, + // when the user clicks load more + posts: [...this.latestCommunityPosts] + }) + } }).catch(async (err) => { console.error(err) const errorMessage = this.$t('Invidious API Error (Click to copy)') @@ -1849,7 +1950,11 @@ export default defineComponent({ ...mapActions([ 'showOutlines', - 'updateSubscriptionDetails' + 'updateSubscriptionDetails', + 'updateSubscriptionVideosCacheByChannel', + 'updateSubscriptionLiveCacheByChannel', + 'updateSubscriptionShortsCacheWithChannelPageShorts', + 'updateSubscriptionPostsCacheByChannel' ]) } })