Add feature to remember video history with last viewed playlist when enabled (#3006)

* + Add "infra" code for new feature

* * Update view/component to implement remembering last playlistID in history

* * Implement option to disable new feature

* ! Fix implementation for use case "Find a video (with playlist PL-A) to view, go to channel, view that same video, no playlist restored"
This commit is contained in:
PikachuEXE
2023-01-26 03:18:04 +01:00
committed by GitHub
parent 2c4e31bc85
commit 0daa422284
18 changed files with 143 additions and 17 deletions
+4 -2
View File
@@ -38,7 +38,8 @@ const DBActions = {
},
HISTORY: {
UPDATE_WATCH_PROGRESS: 'db-action-history-update-watch-progress'
UPDATE_WATCH_PROGRESS: 'db-action-history-update-watch-progress',
UPDATE_PLAYLIST: 'db-action-history-update-playlist',
},
PLAYLISTS: {
@@ -59,7 +60,8 @@ const SyncEvents = {
},
HISTORY: {
UPDATE_WATCH_PROGRESS: 'sync-history-update-watch-progress'
UPDATE_WATCH_PROGRESS: 'sync-history-update-watch-progress',
UPDATE_PLAYLIST: 'sync-history-update-playlist',
},
PLAYLISTS: {
+4
View File
@@ -58,6 +58,10 @@ class History {
return db.history.update({ videoId }, { $set: { watchProgress } }, { upsert: true })
}
static updateLastViewedPlaylist(videoId, playlistId) {
return db.history.update({ videoId }, { $set: { lastViewedPlaylistId: playlistId } }, { upsert: true })
}
static delete(videoId) {
return db.history.remove({ videoId })
}
+10
View File
@@ -42,6 +42,16 @@ class History {
)
}
static updateLastViewedPlaylist(videoId, playlistId) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{
action: DBActions.HISTORY.UPDATE_PLAYLIST,
data: { videoId, playlistId }
}
)
}
static delete(videoId) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
+4
View File
@@ -33,6 +33,10 @@ class History {
return baseHandlers.history.updateWatchProgress(videoId, watchProgress)
}
static updateLastViewedPlaylist(videoId, playlistId) {
return baseHandlers.history.updateLastViewedPlaylist(videoId, playlistId)
}
static delete(videoId) {
return baseHandlers.history.delete(videoId)
}
+9
View File
@@ -789,6 +789,15 @@ function runApp() {
)
return null
case DBActions.HISTORY.UPDATE_PLAYLIST:
await baseHandlers.history.updateLastViewedPlaylist(data.videoId, data.playlistId)
syncOtherWindows(
IpcChannels.SYNC_HISTORY,
event,
{ event: SyncEvents.HISTORY.UPDATE_PLAYLIST, data }
)
return null
case DBActions.GENERAL.DELETE:
await baseHandlers.history.delete(data)
syncOtherWindows(
@@ -12,7 +12,11 @@ export default defineComponent({
data: {
type: Array,
required: true
}
},
showVideoWithLastViewedPlaylist: {
type: Boolean,
default: false
},
},
computed: {
listType: function () {
@@ -9,6 +9,7 @@
:data="result"
:first-screen="index < 16"
:layout="listType"
:show-video-with-last-viewed-playlist="showVideoWithLastViewedPlaylist"
/>
</ft-auto-grid>
</template>
@@ -26,7 +26,11 @@ export default defineComponent({
layout: {
type: String,
default: 'grid'
}
},
showVideoWithLastViewedPlaylist: {
type: Boolean,
default: false
},
},
data: function () {
return {
@@ -19,6 +19,7 @@
v-if="(data.type === 'video' || data.type === 'shortVideo') && visible"
:appearance="appearance"
:data="data"
:show-video-with-last-viewed-playlist="showVideoWithLastViewedPlaylist"
/>
<ft-list-playlist
v-if="data.type === 'playlist' && visible"
@@ -48,7 +48,11 @@ export default defineComponent({
appearance: {
type: String,
required: true
}
},
showVideoWithLastViewedPlaylist: {
type: Boolean,
default: false
},
},
data: function () {
return {
@@ -101,9 +105,9 @@ export default defineComponent({
invidiousUrl: function () {
let videoUrl = `${this.currentInvidiousInstance}/watch?v=${this.id}`
// `playlistId` can be undefined
if (this.playlistId && this.playlistId.length !== 0) {
if (this.playlistIdFinal && this.playlistIdFinal.length !== 0) {
// `index` seems can be ignored
videoUrl += `&list=${this.playlistId}`
videoUrl += `&list=${this.playlistIdFinal}`
}
return videoUrl
},
@@ -115,18 +119,18 @@ export default defineComponent({
youtubeUrl: function () {
let videoUrl = `https://www.youtube.com/watch?v=${this.id}`
// `playlistId` can be undefined
if (this.playlistId && this.playlistId.length !== 0) {
if (this.playlistIdFinal && this.playlistIdFinal.length !== 0) {
// `index` seems can be ignored
videoUrl += `&list=${this.playlistId}`
videoUrl += `&list=${this.playlistIdFinal}`
}
return videoUrl
},
youtubeShareUrl: function () {
// `playlistId` can be undefined
if (this.playlistId && this.playlistId.length !== 0) {
if (this.playlistIdFinal && this.playlistIdFinal.length !== 0) {
// `index` seems can be ignored
return `https://youtu.be/${this.id}?list=${this.playlistId}`
return `https://youtu.be/${this.id}?list=${this.playlistIdFinal}`
}
return `https://youtu.be/${this.id}`
},
@@ -281,9 +285,14 @@ export default defineComponent({
return this.$store.getters.getSaveWatchedProgress
},
saveVideoHistoryWithLastViewedPlaylist: function () {
return this.$store.getters.getSaveVideoHistoryWithLastViewedPlaylist
},
showDistractionFreeTitles: function () {
return this.$store.getters.getShowDistractionFreeTitles
},
displayTitle: function () {
if (this.showDistractionFreeTitles) {
return toDistractionFreeTitle(this.data.title)
@@ -291,6 +300,28 @@ export default defineComponent({
return this.data.title
}
},
historyIndex: function() {
return this.historyCache.findIndex((video) => {
return video.videoId === this.id
})
},
playlistIdFinal: function () {
if (this.playlistId) {
return this.playlistId
}
// Get playlist ID from history ONLY if option enabled
if (!this.showVideoWithLastViewedPlaylist) { return }
if (!this.saveVideoHistoryWithLastViewedPlaylist) { return }
const historyIndex = this.historyIndex
if (historyIndex === -1) {
return undefined
}
return this.historyCache[historyIndex].lastViewedPlaylistId
},
},
mounted: function () {
this.parseVideoData()
@@ -305,7 +336,7 @@ export default defineComponent({
playbackRate: this.defaultPlayback,
videoId: this.id,
videoLength: this.data.lengthSeconds,
playlistId: this.playlistId,
playlistId: this.playlistIdFinal,
playlistIndex: this.playlistIndex,
playlistReverse: this.playlistReverse,
playlistShuffle: this.playlistShuffle,
@@ -411,9 +442,7 @@ export default defineComponent({
},
checkIfWatched: function () {
const historyIndex = this.historyCache.findIndex((video) => {
return video.videoId === this.id
})
const historyIndex = this.historyIndex
if (historyIndex !== -1) {
this.watched = true
@@ -16,7 +16,7 @@
tabindex="-1"
:to="{
path: `/watch/${id}`,
query: playlistId ? {playlistId} : {}
query: playlistIdFinal ? {playlistId: playlistIdFinal} : {}
}"
>
<img
@@ -82,7 +82,7 @@
class="title"
:to="{
path: `/watch/${id}`,
query: playlistId ? {playlistId} : {}
query: playlistIdFinal ? {playlistId: playlistIdFinal} : {}
}"
>
{{ displayTitle }}
@@ -35,6 +35,9 @@ export default defineComponent({
saveWatchedProgress: function () {
return this.$store.getters.getSaveWatchedProgress
},
saveVideoHistoryWithLastViewedPlaylist: function () {
return this.$store.getters.getSaveVideoHistoryWithLastViewedPlaylist
},
removeVideoMetaFiles: function () {
return this.$store.getters.getRemoveVideoMetaFiles
},
@@ -121,6 +124,7 @@ export default defineComponent({
'updateRemoveVideoMetaFiles',
'removeAllHistory',
'updateSaveWatchedProgress',
'updateSaveVideoHistoryWithLastViewedPlaylist',
'clearSessionSearchHistory',
'updateProfile',
'removeProfile',
@@ -20,6 +20,15 @@
@change="updateSaveWatchedProgress"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Privacy Settings.Save Watched Videos With Last Viewed Playlist')"
:compact="true"
:disabled="!rememberHistory"
:default-value="saveVideoHistoryWithLastViewedPlaylist"
@change="updateSaveVideoHistoryWithLastViewedPlaylist"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Privacy Settings.Automatically Remove Video Meta Files')"
+19
View File
@@ -56,6 +56,15 @@ const actions = {
}
},
async updateLastViewedPlaylist({ commit }, { videoId, playlistId }) {
try {
await DBHistoryHandlers.updateLastViewedPlaylist(videoId, playlistId)
commit('updateRecordLastViewedPlaylistIdInHistoryCache', { videoId, playlistId })
} catch (errMessage) {
console.error(errMessage)
}
},
compactHistory(_) {
DBHistoryHandlers.persist()
}
@@ -95,6 +104,16 @@ const mutations = {
state.historyCache.splice(i, 1, targetRecord)
},
updateRecordLastViewedPlaylistIdInHistoryCache(state, { videoId, playlistId }) {
const i = state.historyCache.findIndex((currentRecord) => {
return currentRecord.videoId === videoId
})
const targetRecord = Object.assign({}, state.historyCache[i])
targetRecord.lastViewedPlaylistId = playlistId
state.historyCache.splice(i, 1, targetRecord)
},
removeFromHistoryCacheById(state, videoId) {
for (let i = 0; i < state.historyCache.length; i++) {
if (state.historyCache[i].videoId === videoId) {
+5
View File
@@ -226,6 +226,7 @@ const state = {
rememberHistory: true,
removeVideoMetaFiles: true,
saveWatchedProgress: true,
saveVideoHistoryWithLastViewedPlaylist: true,
showFamilyFriendlyOnly: false,
sponsorBlockShowSkippedToast: true,
sponsorBlockUrl: 'https://sponsor.ajay.app',
@@ -444,6 +445,10 @@ const customActions = {
commit('updateRecordWatchProgressInHistoryCache', data)
break
case SyncEvents.HISTORY.UPDATE_PLAYLIST:
commit('updateRecordLastViewedPlaylistIdInHistoryCache', data)
break
case SyncEvents.GENERAL.DELETE:
commit('removeFromHistoryCacheById', data)
break
+1
View File
@@ -35,6 +35,7 @@
<ft-element-list
v-if="activeData.length > 0 && !isLoading"
:data="activeData"
:show-video-with-last-viewed-playlist="true"
/>
<ft-flex-box
v-if="showLoadMoreButton"
+19
View File
@@ -115,6 +115,9 @@ export default defineComponent({
saveWatchedProgress: function () {
return this.$store.getters.getSaveWatchedProgress
},
saveVideoHistoryWithLastViewedPlaylist: function () {
return this.$store.getters.getSaveVideoHistoryWithLastViewedPlaylist
},
backendPreference: function () {
return this.$store.getters.getBackendPreference
},
@@ -236,6 +239,7 @@ export default defineComponent({
this.useTheatreMode = this.defaultTheatreMode
this.checkIfPlaylist()
this.handlePlaylistPersisting()
this.checkIfTimestamp()
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
@@ -939,6 +943,19 @@ export default defineComponent({
}
},
handlePlaylistPersisting: function () {
// Only save playlist ID if enabled, and it's not special video types
if (!(this.rememberHistory && this.saveVideoHistoryWithLastViewedPlaylist)) { return }
if (this.isUpcoming || this.isLoading || this.isLive) { return }
const payload = {
videoId: this.videoId,
// Whether there is a playlist ID or not, save it
playlistId: this.$route.query?.playlistId,
}
this.updateLastViewedPlaylist(payload)
},
checkIfWatched: function () {
const historyIndex = this.historyCache.findIndex((video) => {
return video.videoId === this.videoId
@@ -1143,6 +1160,7 @@ export default defineComponent({
this.videoChapters = []
this.handleWatchProgress()
this.handlePlaylistPersisting()
if (!this.isUpcoming && !this.isLoading) {
const player = this.$refs.videoPlayer.player
@@ -1429,6 +1447,7 @@ export default defineComponent({
...mapActions([
'updateHistory',
'updateWatchProgress',
'updateLastViewedPlaylist',
'updateSubscriptionDetails'
])
}
+1
View File
@@ -291,6 +291,7 @@ Settings:
Privacy Settings: Privacy Settings
Remember History: Remember History
Save Watched Progress: Save Watched Progress
Save Watched Videos With Last Viewed Playlist: Save Watched Videos With Last Viewed Playlist
Automatically Remove Video Meta Files: Automatically Remove Video Meta Files
Clear Search Cache: Clear Search Cache
Are you sure you want to clear out your search cache?: Are you sure you want to