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:
+4
-2
@@ -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: {
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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')"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user