mirror of https://github.com/FreeTubeApp/FreeTube
Improve performance of the watch history handling (#4017)
* Improve performance of the watch history handling * Remove duplicate checks for whether history entries exist Co-authored-by: PikachuEXE <pikachuexe@gmail.com> * Remove leftover `typeof` --------- Co-authored-by: PikachuEXE <pikachuexe@gmail.com>
This commit is contained in:
parent
3db6f437c9
commit
d333990fbc
|
@ -54,8 +54,8 @@ export default defineComponent({
|
||||||
allPlaylists: function () {
|
allPlaylists: function () {
|
||||||
return this.$store.getters.getAllPlaylists
|
return this.$store.getters.getAllPlaylists
|
||||||
},
|
},
|
||||||
historyCache: function () {
|
historyCacheSorted: function () {
|
||||||
return this.$store.getters.getHistoryCache
|
return this.$store.getters.getHistoryCacheSorted
|
||||||
},
|
},
|
||||||
exportSubscriptionsPromptNames: function () {
|
exportSubscriptionsPromptNames: function () {
|
||||||
const exportFreeTube = this.$t('Settings.Data Settings.Export FreeTube')
|
const exportFreeTube = this.$t('Settings.Data Settings.Export FreeTube')
|
||||||
|
@ -825,7 +825,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
exportHistory: async function () {
|
exportHistory: async function () {
|
||||||
const historyDb = this.historyCache.map((historyEntry) => {
|
const historyDb = this.historyCacheSorted.map((historyEntry) => {
|
||||||
return JSON.stringify(historyEntry)
|
return JSON.stringify(historyEntry)
|
||||||
}).join('\n') + '\n'
|
}).join('\n') + '\n'
|
||||||
const dateStr = getTodayDateStrLocalTimezone()
|
const dateStr = getTodayDateStrLocalTimezone()
|
||||||
|
|
|
@ -76,8 +76,12 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
historyCache: function () {
|
historyEntry: function () {
|
||||||
return this.$store.getters.getHistoryCache
|
return this.$store.getters.getHistoryCacheById[this.id]
|
||||||
|
},
|
||||||
|
|
||||||
|
historyEntryExists: function () {
|
||||||
|
return typeof this.historyEntry !== 'undefined'
|
||||||
},
|
},
|
||||||
|
|
||||||
listType: function () {
|
listType: function () {
|
||||||
|
@ -313,12 +317,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
historyIndex: function() {
|
|
||||||
return this.historyCache.findIndex((video) => {
|
|
||||||
return video.videoId === this.id
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
playlistIdFinal: function () {
|
playlistIdFinal: function () {
|
||||||
if (this.playlistId) {
|
if (this.playlistId) {
|
||||||
return this.playlistId
|
return this.playlistId
|
||||||
|
@ -327,12 +325,8 @@ export default defineComponent({
|
||||||
// Get playlist ID from history ONLY if option enabled
|
// Get playlist ID from history ONLY if option enabled
|
||||||
if (!this.showVideoWithLastViewedPlaylist) { return }
|
if (!this.showVideoWithLastViewedPlaylist) { return }
|
||||||
if (!this.saveVideoHistoryWithLastViewedPlaylist) { return }
|
if (!this.saveVideoHistoryWithLastViewedPlaylist) { return }
|
||||||
const historyIndex = this.historyIndex
|
|
||||||
if (historyIndex === -1) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.historyCache[historyIndex].lastViewedPlaylistId
|
return this.historyEntry?.lastViewedPlaylistId
|
||||||
},
|
},
|
||||||
|
|
||||||
currentLocale: function () {
|
currentLocale: function () {
|
||||||
|
@ -348,7 +342,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
historyIndex() {
|
historyEntry() {
|
||||||
this.checkIfWatched()
|
this.checkIfWatched()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -451,8 +445,8 @@ export default defineComponent({
|
||||||
this.channelName = this.data.author ?? null
|
this.channelName = this.data.author ?? null
|
||||||
this.channelId = this.data.authorId ?? null
|
this.channelId = this.data.authorId ?? null
|
||||||
|
|
||||||
if (this.data.isRSS && this.historyIndex !== -1) {
|
if (this.data.isRSS && this.historyEntryExists) {
|
||||||
this.duration = formatDurationAsTimestamp(this.historyCache[this.historyIndex].lengthSeconds)
|
this.duration = formatDurationAsTimestamp(this.historyEntry.lengthSeconds)
|
||||||
} else {
|
} else {
|
||||||
this.duration = formatDurationAsTimestamp(this.data.lengthSeconds)
|
this.duration = formatDurationAsTimestamp(this.data.lengthSeconds)
|
||||||
}
|
}
|
||||||
|
@ -538,22 +532,26 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
checkIfWatched: function () {
|
checkIfWatched: function () {
|
||||||
const historyIndex = this.historyIndex
|
if (this.historyEntryExists) {
|
||||||
|
|
||||||
if (historyIndex !== -1) {
|
|
||||||
this.watched = true
|
this.watched = true
|
||||||
|
|
||||||
|
const historyEntry = this.historyEntry
|
||||||
|
|
||||||
if (this.saveWatchedProgress) {
|
if (this.saveWatchedProgress) {
|
||||||
// For UX consistency, no progress reading if writing disabled
|
// For UX consistency, no progress reading if writing disabled
|
||||||
this.watchProgress = this.historyCache[historyIndex].watchProgress
|
this.watchProgress = historyEntry.watchProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.historyCache[historyIndex].published !== '') {
|
if (historyEntry.published !== '') {
|
||||||
const videoPublished = this.historyCache[historyIndex].published
|
const videoPublished = historyEntry.published
|
||||||
const videoPublishedDate = new Date(videoPublished)
|
const videoPublishedDate = new Date(videoPublished)
|
||||||
this.publishedText = videoPublishedDate.toLocaleDateString()
|
this.publishedText = videoPublishedDate.toLocaleDateString()
|
||||||
} else {
|
} else {
|
||||||
this.publishedText = ''
|
this.publishedText = ''
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.watched = false
|
||||||
|
this.watchProgress = 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -38,14 +38,10 @@ export function updateVideoListAfterProcessing(videos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.getHideWatchedSubs) {
|
if (store.getters.getHideWatchedSubs) {
|
||||||
const historyCache = store.getters.getHistoryCache
|
const historyCacheById = store.getters.getHistoryCacheById
|
||||||
|
|
||||||
videoList = videoList.filter((video) => {
|
videoList = videoList.filter((video) => {
|
||||||
const historyIndex = historyCache.findIndex((x) => {
|
return !Object.hasOwn(historyCacheById, video.videoId)
|
||||||
return x.videoId === video.videoId
|
|
||||||
})
|
|
||||||
|
|
||||||
return historyIndex === -1
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
|
import { set as vueSet, del as vueDel } from 'vue'
|
||||||
import { DBHistoryHandlers } from '../../../datastores/handlers/index'
|
import { DBHistoryHandlers } from '../../../datastores/handlers/index'
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
historyCache: []
|
historyCacheSorted: [],
|
||||||
|
|
||||||
|
// Vuex doesn't support Maps, so we have to use an object here instead
|
||||||
|
// TODO: switch to a Map during the Pinia migration
|
||||||
|
historyCacheById: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
getHistoryCache: () => {
|
getHistoryCacheSorted: () => {
|
||||||
return state.historyCache
|
return state.historyCacheSorted
|
||||||
|
},
|
||||||
|
|
||||||
|
getHistoryCacheById: () => {
|
||||||
|
return state.historyCacheById
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +23,14 @@ const actions = {
|
||||||
async grabHistory({ commit }) {
|
async grabHistory({ commit }) {
|
||||||
try {
|
try {
|
||||||
const results = await DBHistoryHandlers.find()
|
const results = await DBHistoryHandlers.find()
|
||||||
commit('setHistoryCache', results)
|
|
||||||
|
const resultsById = {}
|
||||||
|
results.forEach(video => {
|
||||||
|
resultsById[video.videoId] = video
|
||||||
|
})
|
||||||
|
|
||||||
|
commit('setHistoryCacheSorted', results)
|
||||||
|
commit('setHistoryCacheById', resultsById)
|
||||||
} catch (errMessage) {
|
} catch (errMessage) {
|
||||||
console.error(errMessage)
|
console.error(errMessage)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +57,8 @@ const actions = {
|
||||||
async removeAllHistory({ commit }) {
|
async removeAllHistory({ commit }) {
|
||||||
try {
|
try {
|
||||||
await DBHistoryHandlers.deleteAll()
|
await DBHistoryHandlers.deleteAll()
|
||||||
commit('setHistoryCache', [])
|
commit('setHistoryCacheSorted', [])
|
||||||
|
commit('setHistoryCacheById', {})
|
||||||
} catch (errMessage) {
|
} catch (errMessage) {
|
||||||
console.error(errMessage)
|
console.error(errMessage)
|
||||||
}
|
}
|
||||||
|
@ -67,56 +84,60 @@ const actions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
setHistoryCache(state, historyCache) {
|
setHistoryCacheSorted(state, historyCacheSorted) {
|
||||||
state.historyCache = historyCache
|
state.historyCacheSorted = historyCacheSorted
|
||||||
},
|
},
|
||||||
|
|
||||||
hoistEntryToTopOfHistoryCache(state, { currentIndex, updatedEntry }) {
|
setHistoryCacheById(state, historyCacheById) {
|
||||||
state.historyCache.splice(currentIndex, 1)
|
state.historyCacheById = historyCacheById
|
||||||
state.historyCache.unshift(updatedEntry)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
upsertToHistoryCache(state, record) {
|
upsertToHistoryCache(state, record) {
|
||||||
const i = state.historyCache.findIndex((currentRecord) => {
|
const i = state.historyCacheSorted.findIndex((currentRecord) => {
|
||||||
return record.videoId === currentRecord.videoId
|
return record.videoId === currentRecord.videoId
|
||||||
})
|
})
|
||||||
|
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
// Already in cache
|
// Already in cache
|
||||||
// Must be hoisted to top, remove it and then unshift it
|
// Must be hoisted to top, remove it and then unshift it
|
||||||
state.historyCache.splice(i, 1)
|
state.historyCacheSorted.splice(i, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.historyCache.unshift(record)
|
state.historyCacheSorted.unshift(record)
|
||||||
|
vueSet(state.historyCacheById, record.videoId, record)
|
||||||
},
|
},
|
||||||
|
|
||||||
updateRecordWatchProgressInHistoryCache(state, { videoId, watchProgress }) {
|
updateRecordWatchProgressInHistoryCache(state, { videoId, watchProgress }) {
|
||||||
const i = state.historyCache.findIndex((currentRecord) => {
|
const i = state.historyCacheSorted.findIndex((currentRecord) => {
|
||||||
return currentRecord.videoId === videoId
|
return currentRecord.videoId === videoId
|
||||||
})
|
})
|
||||||
|
|
||||||
const targetRecord = Object.assign({}, state.historyCache[i])
|
const targetRecord = Object.assign({}, state.historyCacheSorted[i])
|
||||||
targetRecord.watchProgress = watchProgress
|
targetRecord.watchProgress = watchProgress
|
||||||
state.historyCache.splice(i, 1, targetRecord)
|
state.historyCacheSorted.splice(i, 1, targetRecord)
|
||||||
|
vueSet(state.historyCacheById, videoId, targetRecord)
|
||||||
},
|
},
|
||||||
|
|
||||||
updateRecordLastViewedPlaylistIdInHistoryCache(state, { videoId, lastViewedPlaylistId }) {
|
updateRecordLastViewedPlaylistIdInHistoryCache(state, { videoId, lastViewedPlaylistId }) {
|
||||||
const i = state.historyCache.findIndex((currentRecord) => {
|
const i = state.historyCacheSorted.findIndex((currentRecord) => {
|
||||||
return currentRecord.videoId === videoId
|
return currentRecord.videoId === videoId
|
||||||
})
|
})
|
||||||
|
|
||||||
const targetRecord = Object.assign({}, state.historyCache[i])
|
const targetRecord = Object.assign({}, state.historyCacheSorted[i])
|
||||||
targetRecord.lastViewedPlaylistId = lastViewedPlaylistId
|
targetRecord.lastViewedPlaylistId = lastViewedPlaylistId
|
||||||
state.historyCache.splice(i, 1, targetRecord)
|
state.historyCacheSorted.splice(i, 1, targetRecord)
|
||||||
|
vueSet(state.historyCacheById, videoId, targetRecord)
|
||||||
},
|
},
|
||||||
|
|
||||||
removeFromHistoryCacheById(state, videoId) {
|
removeFromHistoryCacheById(state, videoId) {
|
||||||
for (let i = 0; i < state.historyCache.length; i++) {
|
for (let i = 0; i < state.historyCacheSorted.length; i++) {
|
||||||
if (state.historyCache[i].videoId === videoId) {
|
if (state.historyCacheSorted[i].videoId === videoId) {
|
||||||
state.historyCache.splice(i, 1)
|
state.historyCacheSorted.splice(i, 1)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vueDel(state.historyCacheById, videoId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -469,7 +469,8 @@ const customActions = {
|
||||||
break
|
break
|
||||||
|
|
||||||
case SyncEvents.GENERAL.DELETE_ALL:
|
case SyncEvents.GENERAL.DELETE_ALL:
|
||||||
commit('setHistoryCache', [])
|
commit('setHistoryCacheSorted', [])
|
||||||
|
commit('setHistoryCacheById', {})
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -28,15 +28,15 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
historyCache: function () {
|
historyCacheSorted: function () {
|
||||||
return this.$store.getters.getHistoryCache
|
return this.$store.getters.getHistoryCacheSorted
|
||||||
},
|
},
|
||||||
|
|
||||||
fullData: function () {
|
fullData: function () {
|
||||||
if (this.historyCache.length < this.dataLimit) {
|
if (this.historyCacheSorted.length < this.dataLimit) {
|
||||||
return this.historyCache
|
return this.historyCacheSorted
|
||||||
} else {
|
} else {
|
||||||
return this.historyCache.slice(0, this.dataLimit)
|
return this.historyCacheSorted.slice(0, this.dataLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -59,7 +59,7 @@ export default defineComponent({
|
||||||
|
|
||||||
this.activeData = this.fullData
|
this.activeData = this.fullData
|
||||||
|
|
||||||
if (this.activeData.length < this.historyCache.length) {
|
if (this.activeData.length < this.historyCacheSorted.length) {
|
||||||
this.showLoadMoreButton = true
|
this.showLoadMoreButton = true
|
||||||
} else {
|
} else {
|
||||||
this.showLoadMoreButton = false
|
this.showLoadMoreButton = false
|
||||||
|
@ -85,14 +85,14 @@ export default defineComponent({
|
||||||
filterHistory: function() {
|
filterHistory: function() {
|
||||||
if (this.query === '') {
|
if (this.query === '') {
|
||||||
this.activeData = this.fullData
|
this.activeData = this.fullData
|
||||||
if (this.activeData.length < this.historyCache.length) {
|
if (this.activeData.length < this.historyCacheSorted.length) {
|
||||||
this.showLoadMoreButton = true
|
this.showLoadMoreButton = true
|
||||||
} else {
|
} else {
|
||||||
this.showLoadMoreButton = false
|
this.showLoadMoreButton = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const lowerCaseQuery = this.query.toLowerCase()
|
const lowerCaseQuery = this.query.toLowerCase()
|
||||||
const filteredQuery = this.historyCache.filter((video) => {
|
const filteredQuery = this.historyCacheSorted.filter((video) => {
|
||||||
if (typeof (video.title) !== 'string' || typeof (video.author) !== 'string') {
|
if (typeof (video.title) !== 'string' || typeof (video.author) !== 'string') {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -123,8 +123,11 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
historyCache: function () {
|
historyEntry: function () {
|
||||||
return this.$store.getters.getHistoryCache
|
return this.$store.getters.getHistoryCacheById[this.videoId]
|
||||||
|
},
|
||||||
|
historyEntryExists: function () {
|
||||||
|
return typeof this.historyEntry !== 'undefined'
|
||||||
},
|
},
|
||||||
rememberHistory: function () {
|
rememberHistory: function () {
|
||||||
return this.$store.getters.getRememberHistory
|
return this.$store.getters.getRememberHistory
|
||||||
|
@ -1040,10 +1043,6 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
checkIfWatched: function () {
|
checkIfWatched: function () {
|
||||||
const historyIndex = this.historyCache.findIndex((video) => {
|
|
||||||
return video.videoId === this.videoId
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!this.isLive) {
|
if (!this.isLive) {
|
||||||
if (this.timestamp) {
|
if (this.timestamp) {
|
||||||
if (this.timestamp < 0) {
|
if (this.timestamp < 0) {
|
||||||
|
@ -1053,9 +1052,9 @@ export default defineComponent({
|
||||||
} else {
|
} else {
|
||||||
this.$refs.videoPlayer.player.currentTime(this.timestamp)
|
this.$refs.videoPlayer.player.currentTime(this.timestamp)
|
||||||
}
|
}
|
||||||
} else if (this.saveWatchedProgress && historyIndex !== -1) {
|
} else if (this.saveWatchedProgress && this.historyEntryExists) {
|
||||||
// For UX consistency, no progress reading if writing disabled
|
// For UX consistency, no progress reading if writing disabled
|
||||||
const watchProgress = this.historyCache[historyIndex].watchProgress
|
const watchProgress = this.historyEntry.watchProgress
|
||||||
|
|
||||||
if (watchProgress < (this.videoLengthSeconds - 10)) {
|
if (watchProgress < (this.videoLengthSeconds - 10)) {
|
||||||
this.$refs.videoPlayer.player.currentTime(watchProgress)
|
this.$refs.videoPlayer.player.currentTime(watchProgress)
|
||||||
|
@ -1066,8 +1065,8 @@ export default defineComponent({
|
||||||
if (this.rememberHistory) {
|
if (this.rememberHistory) {
|
||||||
if (this.timestamp) {
|
if (this.timestamp) {
|
||||||
this.addToHistory(this.timestamp)
|
this.addToHistory(this.timestamp)
|
||||||
} else if (historyIndex !== -1) {
|
} else if (this.historyEntryExists) {
|
||||||
this.addToHistory(this.historyCache[historyIndex].watchProgress)
|
this.addToHistory(this.historyEntry.watchProgress)
|
||||||
} else {
|
} else {
|
||||||
this.addToHistory(0)
|
this.addToHistory(0)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue