Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ const DBActions = {
HISTORY: {
UPDATE_WATCH_PROGRESS: 20,
UPDATE_PLAYLIST: 21,
UNSET_PLAYLIST_FOR_VIDEOS: 22,
UNSET_PLAYLISTS: 23
},

PROFILES: {
Expand Down
21 changes: 21 additions & 0 deletions src/datastores/handlers/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,27 @@ class History {
return db.history.updateAsync({ videoId }, { $set: { lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId } }, { upsert: true })
}

static unsetLastViewedPlaylistForVideos(videoIds, lastViewedPlaylistId) {
return db.history.updateAsync(
{
videoId: { $in: videoIds },
lastViewedPlaylistId: lastViewedPlaylistId
},
{ $unset: { lastViewedPlaylistId: '', lastViewedPlaylistType: '', lastViewedPlaylistItemId: '' } },
{ multi: true }
)
}

static unsetLastViewedPlaylists(lastViewedPlaylistIds) {
return db.history.updateAsync(
{
lastViewedPlaylistId: { $in: lastViewedPlaylistIds }
},
{ $unset: { lastViewedPlaylistId: '', lastViewedPlaylistType: '', lastViewedPlaylistItemId: '' } },
{ multi: true }
)
}

static delete(videoId) {
return db.history.removeAsync({ videoId })
}
Expand Down
8 changes: 8 additions & 0 deletions src/datastores/handlers/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ class History {
)
}

static unsetLastViewedPlaylistForVideos(videoIds, lastViewedPlaylistId) {
return window.ftElectron.dbHistory(DBActions.HISTORY.UNSET_PLAYLIST_FOR_VIDEOS, { videoIds, lastViewedPlaylistId })
}

static unsetLastViewedPlaylists(lastViewedPlaylistIds) {
return window.ftElectron.dbHistory(DBActions.HISTORY.UNSET_PLAYLISTS, lastViewedPlaylistIds)
}

static delete(videoId) {
return window.ftElectron.dbHistory(DBActions.GENERAL.DELETE, videoId)
}
Expand Down
18 changes: 18 additions & 0 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,24 @@ function runApp() {
)
return null

case DBActions.HISTORY.UNSET_PLAYLIST_FOR_VIDEOS:
await baseHandlers.history.unsetLastViewedPlaylistForVideos(data.videoIds, data.lastViewedPlaylistId)
syncOtherWindows(
IpcChannels.SYNC_HISTORY,
event,
{ event: SyncEvents.HISTORY.UNSET_PLAYLIST_FOR_VIDEOS, data }
)
return null

case DBActions.HISTORY.UNSET_PLAYLISTS:
await baseHandlers.history.unsetLastViewedPlaylists(data)
syncOtherWindows(
IpcChannels.SYNC_HISTORY,
event,
{ event: SyncEvents.HISTORY.UNSET_PLAYLISTS, data }
)
return null

case DBActions.GENERAL.DELETE:
await baseHandlers.history.delete(data)
syncOtherWindows(
Expand Down
47 changes: 47 additions & 0 deletions src/renderer/store/modules/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ const actions = {
console.error(errMessage)
}
},

async unsetLastViewedPlaylistForVideos({ commit }, { videoIds, lastViewedPlaylistId }) {
try {
await DBHistoryHandlers.unsetLastViewedPlaylistForVideos(videoIds, lastViewedPlaylistId)
commit('unsetRecordsLastViewedPlaylistIdInHistoryCache', { videoIds, lastViewedPlaylistId })
} catch (errMessage) {
console.error(errMessage)
}
},

async unsetLastViewedPlaylists({ commit }, lastViewedPlaylistIds) {
try {
await DBHistoryHandlers.unsetLastViewedPlaylists(lastViewedPlaylistIds)
commit('unsetRecordsLastViewedPlaylistIdsInHistoryCache', lastViewedPlaylistIds)
} catch (errMessage) {
console.error(errMessage)
}
},
}

const mutations = {
Expand Down Expand Up @@ -153,6 +171,35 @@ const mutations = {
}
},

unsetRecordsLastViewedPlaylistIdInHistoryCache(state, { videoIds, lastViewedPlaylistId }) {
for (const videoId of videoIds) {
// historyCacheById and historyCacheSorted reference the same object instances,
// so modifying an existing object in one of them will update both.

const record = state.historyCacheById[videoId]

// Don't unset if the item was removed from the watch history or if the last viewed playlist does not match
if (record && record.lastViewedPlaylistId === lastViewedPlaylistId) {
delete record.lastViewedPlaylistId
delete record.lastViewedPlaylistType
delete record.lastViewedPlaylistItemId
}
}
},

unsetRecordsLastViewedPlaylistIdsInHistoryCache(state, playlistIds) {
const playlistIdSet = new Set(playlistIds)

for (const record of state.historyCacheSorted) {
// Don't unset if the item was removed from the watch history or if the last viewed playlist does not match
if (record && playlistIdSet.has(record.lastViewedPlaylistId)) {
delete record.lastViewedPlaylistId
delete record.lastViewedPlaylistType
delete record.lastViewedPlaylistItemId
}
}
},

removeFromHistoryCacheById(state, videoId) {
for (let i = 0; i < state.historyCacheSorted.length; i++) {
if (state.historyCacheSorted[i].videoId === videoId) {
Expand Down
27 changes: 20 additions & 7 deletions src/renderer/store/modules/playlists.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,46 +369,57 @@ const actions = {
}
},

async removeAllPlaylists({ commit }) {
async removeAllPlaylists({ commit, dispatch, getters }) {
try {
const playlistIds = getters.getAllPlaylists.map(playlist => playlist._id)
await dispatch('unsetLastViewedPlaylists', playlistIds)

await DBPlaylistHandlers.deleteAll()
commit('removeAllPlaylists')
} catch (errMessage) {
console.error(errMessage)
}
},

async removeAllVideos({ commit }, _id) {
async removeAllVideos({ commit, dispatch }, _id) {
try {
await dispatch('unsetLastViewedPlaylists', [_id])

await DBPlaylistHandlers.deleteAllVideosByPlaylistId(_id)
commit('removeAllVideos', _id)
} catch (errMessage) {
console.error(errMessage)
}
},

async removePlaylist({ commit }, playlistId) {
async removePlaylist({ commit, dispatch }, playlistId) {
try {
await dispatch('unsetLastViewedPlaylists', [playlistId])

await DBPlaylistHandlers.delete(playlistId)
commit('removePlaylist', playlistId)
} catch (errMessage) {
console.error(errMessage)
}
},

async removePlaylists({ commit }, playlistIds) {
async removePlaylists({ commit, dispatch }, playlistIds) {
try {
await dispatch('unsetLastViewedPlaylists', playlistIds)

await DBPlaylistHandlers.deleteMultiple(playlistIds)
commit('removePlaylists', playlistIds)
} catch (errMessage) {
console.error(errMessage)
}
},

async removeVideo({ commit }, payload) {
async removeVideo({ commit, dispatch }, payload) {
try {
const { _id, videoId, playlistItemId } = payload

await dispatch('unsetLastViewedPlaylistForVideos', { videoIds: [videoId], lastViewedPlaylistId: _id })

const lastUpdatedAt = Date.now()

await DBPlaylistHandlers.deleteVideoIdByPlaylistId(_id, lastUpdatedAt, videoId, playlistItemId)
Expand All @@ -421,9 +432,11 @@ const actions = {
}
},

async removeVideos({ commit }, payload) {
async removeVideos({ commit, dispatch }, payload) {
try {
const { _id, playlistItemIds } = payload
const { _id, playlistItemIds, videoIds } = payload

await dispatch('unsetLastViewedPlaylistForVideos', { videoIds, lastViewedPlaylistId: _id })

const lastUpdatedAt = Date.now()

Expand Down
8 changes: 7 additions & 1 deletion src/renderer/views/Playlist/Playlist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ const videoSearchQuery = ref('')
const promptOpen = ref(false)
/** @type {import('vue').Ref<string[]>} */
const toBeDeletedPlaylistItemIds = ref([])
/** @type {import('vue').Ref<string[]>} */
const videosWithPlaylistToUnset = ref([])
/** @type {AbortController | null} */
let undoToastAbortController = null

Expand Down Expand Up @@ -852,6 +854,7 @@ function removeVideoFromPlaylist(videoId, playlistItemId) {

if (foundVideo) {
toBeDeletedPlaylistItemIds.value.push(playlistItemId)
videosWithPlaylistToUnset.value.push(videoId)

// Only show toast when no existing toast shown
if (undoToastAbortController == null) {
Expand All @@ -868,6 +871,7 @@ function removeVideoFromPlaylist(videoId, playlistItemId) {
() => {
clearTimeout(actualRemoveVideosTimeout)
toBeDeletedPlaylistItemIds.value = []
videosWithPlaylistToUnset.value = []
undoToastAbortController = null
},
undoToastAbortController.signal,
Expand All @@ -886,11 +890,13 @@ async function removeToBeDeletedVideosSometimes() {
if (toBeDeletedPlaylistItemIds.value.length > 0) {
await store.dispatch('removeVideos', {
_id: playlistId.value,
// Create a new non-reactive array to avoid Electron erroring about Proxy objects not being clonable
// Create new non-reactive arrays to avoid Electron erroring about Proxy objects not being clonable
playlistItemIds: [...toBeDeletedPlaylistItemIds.value],
videoIds: [...videosWithPlaylistToUnset.value],
})

toBeDeletedPlaylistItemIds.value = []
videosWithPlaylistToUnset.value = []
undoToastAbortController?.abort()
undoToastAbortController = null
}
Expand Down
10 changes: 10 additions & 0 deletions src/renderer/views/Watch/Watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,16 @@ export default defineComponent({

// `playlistId` present
if (this.selectedUserPlaylist != null) {
// If the page is accessed through navigation via router history, 'playlistId' is still specified
// but the video could have been removed from the playlist in the meantime
if (!this.selectedUserPlaylist.videos.some((video) => video.videoId === this.videoId)) {
this.playlistId = ''
this.playlistType = ''
this.playlistItemId = null
this.watchingPlaylist = false
return
}

// If playlist ID matches a user playlist, it must be user playlist
this.playlistType = 'user'
this.watchingPlaylist = true
Expand Down