mirror of
https://github.com/FreeTubeApp/FreeTube
synced 2025-01-26 03:39:57 +01:00
Update subscription view to be able to load videos from video cache per channel (#3668)
* Changes from PR #3673 * * Update subscription view to be able to load videos from video cache again * * Update subscription view to be able to load videos from video cache per channel * * Remove meaningless argument `allowUseOfChannelCache` * $ Remove unused imports --------- Co-authored-by: petaded <code@zikl.co.uk>
This commit is contained in:
parent
bcf21f6d44
commit
242c93e163
@ -48,13 +48,7 @@ export default defineComponent({
|
||||
},
|
||||
hideUpcomingPremieres: function () {
|
||||
return this.$store.getters.getHideUpcomingPremieres
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onVisibilityChanged: function (visible) {
|
||||
this.visible = visible
|
||||
},
|
||||
|
||||
/**
|
||||
* Show or Hide results in the list
|
||||
*
|
||||
@ -70,10 +64,9 @@ export default defineComponent({
|
||||
// hide livestreams
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.hideUpcomingPremieres &&
|
||||
// Observed for premieres in Local API Channels.
|
||||
(data.durationText === 'PREMIERE' ||
|
||||
(data.premiereDate != null ||
|
||||
// viewCount is our only method of detecting premieres in RSS
|
||||
// data without sending an additional request.
|
||||
// If we ever get a better flag, use it here instead.
|
||||
@ -98,6 +91,11 @@ export default defineComponent({
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onVisibilityChanged: function (visible) {
|
||||
this.visible = visible
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
@ -110,12 +110,7 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
this.updateAllSubscriptionsList([])
|
||||
this.updateProfileSubscriptions({
|
||||
activeProfile: MAIN_PROFILE_ID,
|
||||
videoList: [],
|
||||
errorChannels: []
|
||||
})
|
||||
this.clearSubscriptionsCache()
|
||||
}
|
||||
},
|
||||
|
||||
@ -129,8 +124,7 @@ export default defineComponent({
|
||||
'updateProfile',
|
||||
'removeProfile',
|
||||
'updateActiveProfile',
|
||||
'updateAllSubscriptionsList',
|
||||
'updateProfileSubscriptions'
|
||||
'clearSubscriptionsCache',
|
||||
])
|
||||
}
|
||||
})
|
||||
|
@ -1,39 +1,41 @@
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
const defaultCacheEntryValueForForOneChannel = {
|
||||
videos: null,
|
||||
}
|
||||
|
||||
function deepCopy(obj) {
|
||||
return JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
|
||||
const state = {
|
||||
allSubscriptionsList: [],
|
||||
profileSubscriptions: {
|
||||
activeProfile: MAIN_PROFILE_ID,
|
||||
videoList: [],
|
||||
errorChannels: []
|
||||
}
|
||||
subscriptionsCachePerChannel: {},
|
||||
}
|
||||
|
||||
const getters = {
|
||||
getAllSubscriptionsList: () => {
|
||||
return state.allSubscriptionsList
|
||||
getSubscriptionsCacheEntriesForOneChannel: (state) => (channelId) => {
|
||||
return state.subscriptionsCachePerChannel[channelId]
|
||||
},
|
||||
getProfileSubscriptions: () => {
|
||||
return state.profileSubscriptions
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
updateAllSubscriptionsList ({ commit }, subscriptions) {
|
||||
commit('setAllSubscriptionsList', subscriptions)
|
||||
clearSubscriptionsCache: ({ commit }) => {
|
||||
commit('clearSubscriptionsCachePerChannel')
|
||||
},
|
||||
|
||||
updateSubscriptionsCacheForOneChannel: ({ commit }, payload) => {
|
||||
commit('updateSubscriptionsCacheForOneChannel', payload)
|
||||
},
|
||||
updateProfileSubscriptions ({ commit }, subscriptions) {
|
||||
commit('setProfileSubscriptions', subscriptions)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
setAllSubscriptionsList (state, allSubscriptionsList) {
|
||||
state.allSubscriptionsList = allSubscriptionsList
|
||||
updateSubscriptionsCacheForOneChannel(state, { channelId, videos }) {
|
||||
const existingObject = state.subscriptionsCachePerChannel[channelId]
|
||||
const newObject = existingObject != null ? existingObject : deepCopy(defaultCacheEntryValueForForOneChannel)
|
||||
if (videos != null) { newObject.videos = videos }
|
||||
state.subscriptionsCachePerChannel[channelId] = newObject
|
||||
},
|
||||
clearSubscriptionsCachePerChannel(state) {
|
||||
state.subscriptionsCachePerChannel = {}
|
||||
},
|
||||
setProfileSubscriptions (state, profileSubscriptions) {
|
||||
state.profileSubscriptions = profileSubscriptions
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
|
@ -8,7 +8,6 @@ import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
||||
import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue'
|
||||
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
import { calculatePublishedDate, copyToClipboard, showToast } from '../../helpers/utils'
|
||||
import { invidiousAPICall } from '../../helpers/api/invidious'
|
||||
import { getLocalChannelVideos } from '../../helpers/api/local'
|
||||
@ -30,7 +29,7 @@ export default defineComponent({
|
||||
dataLimit: 100,
|
||||
videoList: [],
|
||||
errorChannels: [],
|
||||
attemptedFetch: false
|
||||
attemptedFetch: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -65,13 +64,27 @@ export default defineComponent({
|
||||
activeProfile: function () {
|
||||
return this.$store.getters.getActiveProfile
|
||||
},
|
||||
|
||||
profileSubscriptions: function () {
|
||||
return this.$store.getters.getProfileSubscriptions
|
||||
activeProfileId: function () {
|
||||
return this.activeProfile._id
|
||||
},
|
||||
|
||||
allSubscriptionsList: function () {
|
||||
return this.$store.getters.getAllSubscriptionsList
|
||||
cacheEntriesForAllActiveProfileChannels() {
|
||||
const entries = []
|
||||
this.activeSubscriptionList.forEach((channel) => {
|
||||
const cacheEntry = this.$store.getters.getSubscriptionsCacheEntriesForOneChannel(channel.id)
|
||||
if (cacheEntry == null) { return }
|
||||
|
||||
entries.push(cacheEntry)
|
||||
})
|
||||
return entries
|
||||
},
|
||||
videoCacheForAllActiveProfileChannelsPresent() {
|
||||
if (this.cacheEntriesForAllActiveProfileChannels.length === 0) { return false }
|
||||
if (this.cacheEntriesForAllActiveProfileChannels.length < this.activeSubscriptionList.length) { return false }
|
||||
|
||||
return this.cacheEntriesForAllActiveProfileChannels.every((cacheEntry) => {
|
||||
return cacheEntry.videos != null
|
||||
})
|
||||
},
|
||||
|
||||
historyCache: function () {
|
||||
@ -92,12 +105,13 @@ export default defineComponent({
|
||||
|
||||
fetchSubscriptionsAutomatically: function() {
|
||||
return this.$store.getters.getFetchSubscriptionsAutomatically
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
activeProfile: async function (_) {
|
||||
this.getProfileSubscriptions()
|
||||
}
|
||||
this.isLoading = true
|
||||
this.loadVideosFromCacheSometimes()
|
||||
},
|
||||
},
|
||||
mounted: async function () {
|
||||
document.addEventListener('keydown', this.keyboardShortcutHandler)
|
||||
@ -108,66 +122,63 @@ export default defineComponent({
|
||||
this.dataLimit = dataLimit
|
||||
}
|
||||
|
||||
if (this.profileSubscriptions.videoList.length !== 0) {
|
||||
if (this.profileSubscriptions.activeProfile === this.activeProfile._id) {
|
||||
const subscriptionList = JSON.parse(JSON.stringify(this.profileSubscriptions))
|
||||
if (this.hideWatchedSubs) {
|
||||
this.videoList = await Promise.all(subscriptionList.videoList.filter((video) => {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return historyIndex === -1
|
||||
}))
|
||||
} else {
|
||||
this.videoList = subscriptionList.videoList
|
||||
this.errorChannels = subscriptionList.errorChannels
|
||||
}
|
||||
} else {
|
||||
this.getProfileSubscriptions()
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
} else if (this.fetchSubscriptionsAutomatically) {
|
||||
setTimeout(async () => {
|
||||
this.getSubscriptions()
|
||||
}, 300)
|
||||
} else {
|
||||
this.isLoading = false
|
||||
}
|
||||
this.loadVideosFromCacheSometimes()
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
document.removeEventListener('keydown', this.keyboardShortcutHandler)
|
||||
},
|
||||
methods: {
|
||||
loadVideosFromCacheSometimes() {
|
||||
// This method is called on view visible
|
||||
if (this.videoCacheForAllActiveProfileChannelsPresent) {
|
||||
this.loadVideosFromCacheForAllActiveProfileChannels()
|
||||
return
|
||||
}
|
||||
|
||||
this.maybeLoadVideosForSubscriptionsFromRemote()
|
||||
},
|
||||
|
||||
async loadVideosFromCacheForAllActiveProfileChannels() {
|
||||
const videoList = []
|
||||
this.activeSubscriptionList.forEach((channel) => {
|
||||
const channelCacheEntry = this.$store.getters.getSubscriptionsCacheEntriesForOneChannel(channel.id)
|
||||
|
||||
videoList.push(...channelCacheEntry.videos)
|
||||
})
|
||||
this.updateVideoListAfterProcessing(videoList)
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
goToChannel: function (id) {
|
||||
this.$router.push({ path: `/channel/${id}` })
|
||||
},
|
||||
|
||||
getSubscriptions: function () {
|
||||
loadVideosForSubscriptionsFromRemote: async function () {
|
||||
if (this.activeSubscriptionList.length === 0) {
|
||||
this.isLoading = false
|
||||
this.videoList = []
|
||||
return
|
||||
}
|
||||
|
||||
const channelsToLoadFromRemote = this.activeSubscriptionList
|
||||
const videoList = []
|
||||
let channelCount = 0
|
||||
this.isLoading = true
|
||||
|
||||
let useRss = this.useRssFeeds
|
||||
if (this.activeSubscriptionList.length >= 125 && !useRss) {
|
||||
if (channelsToLoadFromRemote.length >= 125 && !useRss) {
|
||||
showToast(
|
||||
this.$t('Subscriptions["This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting"]'),
|
||||
10000
|
||||
)
|
||||
useRss = true
|
||||
}
|
||||
this.isLoading = true
|
||||
this.updateShowProgressBar(true)
|
||||
this.setProgressBarPercentage(0)
|
||||
this.attemptedFetch = true
|
||||
|
||||
let videoList = []
|
||||
let channelCount = 0
|
||||
this.errorChannels = []
|
||||
this.activeSubscriptionList.forEach(async (channel) => {
|
||||
const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
|
||||
let videos = []
|
||||
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
|
||||
if (useRss) {
|
||||
@ -183,86 +194,66 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
videoList = videoList.concat(videos)
|
||||
channelCount++
|
||||
const percentageComplete = (channelCount / this.activeSubscriptionList.length) * 100
|
||||
const percentageComplete = (channelCount / channelsToLoadFromRemote.length) * 100
|
||||
this.setProgressBarPercentage(percentageComplete)
|
||||
this.updateSubscriptionsCacheForOneChannel({
|
||||
channelId: channel.id,
|
||||
videos: videos,
|
||||
})
|
||||
return videos
|
||||
}))).flatMap((o) => o)
|
||||
videoList.push(...videoListFromRemote)
|
||||
|
||||
if (channelCount === this.activeSubscriptionList.length) {
|
||||
videoList = await Promise.all(videoList.sort((a, b) => {
|
||||
return b.publishedDate - a.publishedDate
|
||||
}))
|
||||
if (this.hideLiveStreams) {
|
||||
videoList = videoList.filter(item => {
|
||||
return (!item.liveNow && !item.isUpcoming)
|
||||
})
|
||||
}
|
||||
if (this.hideUpcomingPremieres) {
|
||||
videoList = videoList.filter(item => {
|
||||
if (item.isRSS) {
|
||||
// viewCount is our only method of detecting premieres in RSS
|
||||
// data without sending an additional request.
|
||||
// If we ever get a better flag, use it here instead.
|
||||
return item.viewCount !== '0'
|
||||
}
|
||||
// Observed for premieres in Local API Subscriptions.
|
||||
return item.durationText !== 'PREMIERE'
|
||||
})
|
||||
}
|
||||
const profileSubscriptions = {
|
||||
activeProfile: this.activeProfile._id,
|
||||
videoList: videoList,
|
||||
errorChannels: this.errorChannels
|
||||
}
|
||||
this.updateVideoListAfterProcessing(videoList)
|
||||
this.isLoading = false
|
||||
this.updateShowProgressBar(false)
|
||||
},
|
||||
|
||||
this.videoList = await Promise.all(videoList.filter((video) => {
|
||||
if (this.hideWatchedSubs) {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return historyIndex === -1
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}))
|
||||
this.updateProfileSubscriptions(profileSubscriptions)
|
||||
this.isLoading = false
|
||||
this.updateShowProgressBar(false)
|
||||
|
||||
if (this.activeProfile === MAIN_PROFILE_ID) {
|
||||
this.updateAllSubscriptionsList(profileSubscriptions.videoList)
|
||||
updateVideoListAfterProcessing(videoList) {
|
||||
// Filtering and sorting based in preference
|
||||
videoList.sort((a, b) => {
|
||||
return b.publishedDate - a.publishedDate
|
||||
})
|
||||
if (this.hideLiveStreams) {
|
||||
videoList = videoList.filter(item => {
|
||||
return (!item.liveNow && !item.isUpcoming)
|
||||
})
|
||||
}
|
||||
if (this.hideUpcomingPremieres) {
|
||||
videoList = videoList.filter(item => {
|
||||
if (item.isRSS) {
|
||||
// viewCount is our only method of detecting premieres in RSS
|
||||
// data without sending an additional request.
|
||||
// If we ever get a better flag, use it here instead.
|
||||
return item.viewCount !== '0'
|
||||
}
|
||||
// Observed for premieres in Local API Subscriptions.
|
||||
return item.premiereDate == null
|
||||
})
|
||||
}
|
||||
|
||||
this.videoList = videoList.filter((video) => {
|
||||
if (this.hideWatchedSubs) {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return historyIndex === -1
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getProfileSubscriptions: async function () {
|
||||
if (this.allSubscriptionsList.length !== 0) {
|
||||
this.isLoading = true
|
||||
this.videoList = await Promise.all(this.allSubscriptionsList.filter((video) => {
|
||||
const channelIndex = this.activeSubscriptionList.findIndex((x) => {
|
||||
return x.id === video.authorId
|
||||
})
|
||||
|
||||
if (this.hideWatchedSubs) {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return channelIndex !== -1 && historyIndex === -1
|
||||
} else {
|
||||
return channelIndex !== -1
|
||||
}
|
||||
}))
|
||||
this.isLoading = false
|
||||
} else if (this.fetchSubscriptionsAutomatically) {
|
||||
this.getSubscriptions()
|
||||
} else if (this.activeProfile._id === this.profileSubscriptions.activeProfile) {
|
||||
this.videoList = this.profileSubscriptions.videoList
|
||||
maybeLoadVideosForSubscriptionsFromRemote: async function () {
|
||||
if (this.fetchSubscriptionsAutomatically) {
|
||||
// `this.isLoading = false` is called inside `loadVideosForSubscriptionsFromRemote` when needed
|
||||
await this.loadVideosForSubscriptionsFromRemote()
|
||||
} else {
|
||||
this.videoList = []
|
||||
this.attemptedFetch = false
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
@ -483,7 +474,7 @@ export default defineComponent({
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (!this.isLoading) {
|
||||
this.getSubscriptions()
|
||||
this.loadVideosForSubscriptionsFromRemote()
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -491,8 +482,7 @@ export default defineComponent({
|
||||
|
||||
...mapActions([
|
||||
'updateShowProgressBar',
|
||||
'updateProfileSubscriptions',
|
||||
'updateAllSubscriptionsList'
|
||||
'updateSubscriptionsCacheForOneChannel',
|
||||
]),
|
||||
|
||||
...mapMutations([
|
||||
|
@ -68,7 +68,7 @@
|
||||
:title="$t('Subscriptions.Refresh Subscriptions')"
|
||||
:size="12"
|
||||
theme="primary"
|
||||
@click="getSubscriptions"
|
||||
@click="loadVideosForSubscriptionsFromRemote"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user