From b90c3bd3c6593230d1ee39b829d396297b4f1901 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:30:30 +0100 Subject: [PATCH 01/10] Implement pagination for user playlists on the Playlist page --- src/renderer/views/Playlist/Playlist.js | 39 ++++++++++++++++++++++-- src/renderer/views/Playlist/Playlist.vue | 4 +-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/renderer/views/Playlist/Playlist.js b/src/renderer/views/Playlist/Playlist.js index 826f034494ec3..c80d4d82ba754 100644 --- a/src/renderer/views/Playlist/Playlist.js +++ b/src/renderer/views/Playlist/Playlist.js @@ -54,6 +54,7 @@ export default defineComponent({ channelId: '', infoSource: 'local', playlistItems: [], + userPlaylistVisibleLimit: 100, continuationData: null, isLoadingMore: false, getPlaylistInfoDebounce: function() {}, @@ -102,7 +103,11 @@ export default defineComponent({ }, moreVideoDataAvailable() { - return this.continuationData !== null + if (this.isUserPlaylistRequested) { + return this.userPlaylistVisibleLimit < this.videoCount + } else { + return this.continuationData !== null + } }, isUserPlaylistRequested: function () { @@ -117,6 +122,18 @@ export default defineComponent({ return this.selectedUserPlaylist?._id !== this.quickBookmarkPlaylistId }, + + visiblePlaylistItems: function () { + if (this.isUserPlaylistRequested) { + if (this.userPlaylistVisibleLimit < this.videoCount) { + return this.playlistItems.slice(0, this.userPlaylistVisibleLimit) + } else { + return this.playlistItems + } + } else { + return this.playlistItems + } + } }, watch: { $route () { @@ -147,8 +164,10 @@ export default defineComponent({ this.getPlaylistInfoDebounce() }, }, - mounted: function () { + created: function () { this.getPlaylistInfoDebounce = debounce(this.getPlaylistInfo, 100) + }, + mounted: function () { this.getPlaylistInfoDebounce() }, methods: { @@ -250,7 +269,7 @@ export default defineComponent({ const dateString = new Date(result.updated * 1000) this.lastUpdated = dateString.toLocaleDateString(this.currentLocale, { year: 'numeric', month: 'short', day: 'numeric' }) - this.playlistItems = this.playlistItems.concat(result.videos) + this.allPlaylistItems = result.videos this.isLoading = false }).catch((err) => { @@ -298,6 +317,20 @@ export default defineComponent({ case 'local': this.getNextPageLocal() break + case 'user': + // Stop users from spamming the load more button, by replacing it with a loading symbol until the newly added items are renderered + this.isLoadingMore = true + + setTimeout(() => { + if (this.userPlaylistVisibleLimit + 100 < this.videoCount) { + this.userPlaylistVisibleLimit += 100 + } else { + this.userPlaylistVisibleLimit = this.videoCount + } + + this.isLoadingMore = false + }) + break case 'invidious': console.error('Playlist pagination is not currently supported when the Invidious backend is selected.') break diff --git a/src/renderer/views/Playlist/Playlist.vue b/src/renderer/views/Playlist/Playlist.vue index ef3efef0ce00e..86f1393102341 100644 --- a/src/renderer/views/Playlist/Playlist.vue +++ b/src/renderer/views/Playlist/Playlist.vue @@ -44,7 +44,7 @@ tag="span" >
@@ -64,7 +64,7 @@ :always-show-add-to-playlist-button="true" :quick-bookmark-button-enabled="quickBookmarkButtonEnabled" :can-move-video-up="index > 0" - :can-move-video-down="index < playlistItems.length - 1" + :can-move-video-down="index < visiblePlaylistItems.length - 1" :can-remove-from-playlist="true" :hide-forbidden-titles="false" @move-video-up="moveVideoUp(item.videoId, item.playlistItemId)" From e5d61d960215ceb4f40bf16ec26994c47400f5c0 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:31:31 +0100 Subject: [PATCH 02/10] Fix the load more loading icon being displayed off-screen --- src/renderer/views/Playlist/Playlist.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/views/Playlist/Playlist.vue b/src/renderer/views/Playlist/Playlist.vue index 86f1393102341..3176f1f70a69e 100644 --- a/src/renderer/views/Playlist/Playlist.vue +++ b/src/renderer/views/Playlist/Playlist.vue @@ -36,7 +36,7 @@ v-if="!isLoading" class="playlistItems" > -
- + From 93415caa3b9cbf206be6518f6cfa86d56353d3d5 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:35:29 +0100 Subject: [PATCH 03/10] Caching user playlists is unnecessary as they are already in the store --- src/renderer/views/Playlist/Playlist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/views/Playlist/Playlist.js b/src/renderer/views/Playlist/Playlist.js index c80d4d82ba754..33af64a26554b 100644 --- a/src/renderer/views/Playlist/Playlist.js +++ b/src/renderer/views/Playlist/Playlist.js @@ -26,7 +26,7 @@ export default defineComponent({ 'ft-button': FtButton }, beforeRouteLeave(to, from, next) { - if (!this.isLoading && to.path.startsWith('/watch') && to.query.playlistId === this.playlistId) { + if (!this.isLoading && !this.isUserPlaylistRequested && to.path.startsWith('/watch') && to.query.playlistId === this.playlistId) { this.setCachedPlaylist({ id: this.playlistId, title: this.playlistTitle, From 80aa5384e57e65e23f412f092dbd66d61b1dbc0e Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:47:13 +0100 Subject: [PATCH 04/10] ft-list-video: Only render description element if there is a description --- src/renderer/components/ft-list-video/ft-list-video.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/ft-list-video/ft-list-video.vue b/src/renderer/components/ft-list-video/ft-list-video.vue index 898364909ce1e..31c627bf8de15 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.vue +++ b/src/renderer/components/ft-list-video/ft-list-video.vue @@ -157,7 +157,7 @@ @click="handleOptionsClick" />

Date: Wed, 24 Jan 2024 22:24:55 +0100 Subject: [PATCH 05/10] Minor speed ups in the playlists store --- src/renderer/store/modules/playlists.js | 27 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/renderer/store/modules/playlists.js b/src/renderer/store/modules/playlists.js index 704d6ebb58887..2d24234a8bd6b 100644 --- a/src/renderer/store/modules/playlists.js +++ b/src/renderer/store/modules/playlists.js @@ -63,10 +63,13 @@ const actions = { payload.createdAt = Date.now() payload.lastUpdatedAt = Date.now() // Ensure all videos has required attributes + + const currentTime = new Date().getTime() + if (Array.isArray(payload.videos)) { payload.videos.forEach(videoData => { if (videoData.timeAdded == null) { - videoData.timeAdded = new Date().getTime() + videoData.timeAdded = currentTime } if (videoData.playlistItemId == null) { videoData.playlistItemId = generateRandomUniqueId() @@ -149,11 +152,14 @@ const actions = { // Since this action will ensure uniqueness of `playlistItemId` of added video entries try { const { _id, videos } = payload + + const currentTime = new Date().getTime() + const newVideoObjects = videos.map((video) => { // Create a new object to prevent changing existing values outside const videoData = Object.assign({}, video) if (videoData.timeAdded == null) { - videoData.timeAdded = new Date().getTime() + videoData.timeAdded = currentTime } videoData.playlistItemId = generateRandomUniqueId() // For backward compatibility @@ -188,6 +194,9 @@ const actions = { dispatch('addPlaylist', playlist) }) } else { + const dateNow = Date.now() + const currentTime = new Date().getTime() + payload.forEach((playlist) => { let anythingUpdated = false // Assign generated playlist ID in case DB data corrupted @@ -205,19 +214,19 @@ const actions = { // Assign current time as created time in case DB data corrupted if (playlist.createdAt == null) { // Time now in unix time, in ms - playlist.createdAt = Date.now() + playlist.createdAt = dateNow anythingUpdated = true } // Assign current time as last updated time in case DB data corrupted if (playlist.lastUpdatedAt == null) { // Time now in unix time, in ms - playlist.lastUpdatedAt = Date.now() + playlist.lastUpdatedAt = dateNow anythingUpdated = true } playlist.videos.forEach((v) => { // Ensure all videos has `timeAdded` property if (v.timeAdded == null) { - v.timeAdded = new Date().getTime() + v.timeAdded = currentTime anythingUpdated = true } @@ -257,8 +266,9 @@ const actions = { return playlist.playlistName === 'Watch Later' || playlist._id === 'watchLater' }) - const defaultFavoritesPlaylist = state.defaultPlaylists.find((e) => e._id === 'favorites') if (favoritesPlaylist != null) { + const defaultFavoritesPlaylist = state.defaultPlaylists.find((e) => e._id === 'favorites') + // Update existing matching playlist only if it exists if (favoritesPlaylist._id !== defaultFavoritesPlaylist._id || favoritesPlaylist.protected !== defaultFavoritesPlaylist.protected) { const oldId = favoritesPlaylist._id @@ -277,8 +287,9 @@ const actions = { } } - const defaultWatchLaterPlaylist = state.defaultPlaylists.find((e) => e._id === 'watchLater') if (watchLaterPlaylist != null) { + const defaultWatchLaterPlaylist = state.defaultPlaylists.find((e) => e._id === 'watchLater') + // Update existing matching playlist only if it exists if (watchLaterPlaylist._id !== defaultWatchLaterPlaylist._id || watchLaterPlaylist.protected !== defaultWatchLaterPlaylist.protected) { const oldId = watchLaterPlaylist._id @@ -394,7 +405,7 @@ const mutations = { addVideos(state, payload) { const playlist = state.playlists.find(playlist => playlist._id === payload._id) if (playlist) { - playlist.videos = [].concat(playlist.videos).concat(payload.videos) + playlist.videos = [].concat(playlist.videos, payload.videos) } }, From a7bb2188980153bd203e2c6fe786705f1a798083 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:59:02 +0100 Subject: [PATCH 06/10] ft-list-video: Remove superfluous template around view count --- .../components/ft-list-video/ft-list-video.vue | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/renderer/components/ft-list-video/ft-list-video.vue b/src/renderer/components/ft-list-video/ft-list-video.vue index 31c627bf8de15..3597329a628e5 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.vue +++ b/src/renderer/components/ft-list-video/ft-list-video.vue @@ -126,12 +126,13 @@ > {{ channelName }} - + + + {{ $tc('Global.Counts.View Count', viewCount, {count: parsedViewCount}) }} + Date: Wed, 24 Jan 2024 23:02:21 +0100 Subject: [PATCH 07/10] ft-list-video: Replace watched property with historyEntryExists computed property --- .../components/ft-list-video/ft-list-video.js | 15 ++++----------- .../components/ft-list-video/ft-list-video.vue | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/renderer/components/ft-list-video/ft-list-video.js b/src/renderer/components/ft-list-video/ft-list-video.js index 11c35f95475db..1befb4403e3d2 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.js +++ b/src/renderer/components/ft-list-video/ft-list-video.js @@ -97,7 +97,6 @@ export default defineComponent({ lengthSeconds: 0, duration: '', description: '', - watched: false, watchProgress: 0, publishedText: '', isLive: false, @@ -223,7 +222,7 @@ export default defineComponent({ dropdownOptions: function () { const options = [ { - label: this.watched + label: this.historyEntryExists ? this.$t('Video.Remove From History') : this.$t('Video.Mark As Watched'), value: 'history' @@ -343,7 +342,7 @@ export default defineComponent({ }, addWatchedStyle: function () { - return this.watched && !this.inHistory + return this.historyEntryExists && !this.inHistory }, externalPlayer: function () { @@ -576,7 +575,7 @@ export default defineComponent({ } this.openInExternalPlayer(payload) - if (this.saveWatchedProgress && !this.watched) { + if (this.saveWatchedProgress && !this.historyEntryExists) { this.markAsWatched() } }, @@ -584,7 +583,7 @@ export default defineComponent({ handleOptionsClick: function (option) { switch (option) { case 'history': - if (this.watched) { + if (this.historyEntryExists) { this.removeFromWatched() } else { this.markAsWatched() @@ -727,8 +726,6 @@ export default defineComponent({ checkIfWatched: function () { if (this.historyEntryExists) { - this.watched = true - const historyEntry = this.historyEntry if (this.saveWatchedProgress) { @@ -744,7 +741,6 @@ export default defineComponent({ this.publishedText = '' } } else { - this.watched = false this.watchProgress = 0 } }, @@ -766,8 +762,6 @@ export default defineComponent({ } this.updateHistory(videoData) showToast(this.$t('Video.Video has been marked as watched')) - - this.watched = true }, removeFromWatched: function () { @@ -775,7 +769,6 @@ export default defineComponent({ showToast(this.$t('Video.Video has been removed from your history')) - this.watched = false this.watchProgress = 0 }, diff --git a/src/renderer/components/ft-list-video/ft-list-video.vue b/src/renderer/components/ft-list-video/ft-list-video.vue index 3597329a628e5..9d6c57f21b124 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.vue +++ b/src/renderer/components/ft-list-video/ft-list-video.vue @@ -104,7 +104,7 @@ {{ $t("Video.Watched") }}

From f13ef1a43a2a1e2491507ff54c2b871fb346c470 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:02:49 +0100 Subject: [PATCH 08/10] playlist-info: Fix copying partially loaded user playlists --- src/renderer/components/playlist-info/playlist-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/playlist-info/playlist-info.js b/src/renderer/components/playlist-info/playlist-info.js index f5c43941322d5..7deaf2d387100 100644 --- a/src/renderer/components/playlist-info/playlist-info.js +++ b/src/renderer/components/playlist-info/playlist-info.js @@ -226,7 +226,7 @@ export default defineComponent({ }, methods: { toggleCopyVideosPrompt: function (force = false) { - if (this.moreVideoDataAvailable && !force) { + if (this.moreVideoDataAvailable && !this.isUserPlaylist && !force) { showToast(this.$t('User Playlists.SinglePlaylistView.Toast["Some videos in the playlist are not loaded yet. Click here to copy anyway."]'), 5000, () => { this.toggleCopyVideosPrompt(true) }) From 66756c162ac8da0ece69dc80ac2811c9c848c605 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:05:04 +0100 Subject: [PATCH 09/10] Fix the playlist number not getting lazy loaded --- .../ft-list-video-numbered.css | 17 +++ .../ft-list-video-numbered.js | 124 ++++++++++++++++++ .../ft-list-video-numbered.vue | 53 ++++++++ .../watch-video-playlist.css | 13 -- .../watch-video-playlist.js | 9 +- .../watch-video-playlist.vue | 46 +++---- src/renderer/views/Playlist/Playlist.js | 4 +- src/renderer/views/Playlist/Playlist.scss | 5 - src/renderer/views/Playlist/Playlist.vue | 44 +++---- 9 files changed, 233 insertions(+), 82 deletions(-) create mode 100644 src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.css create mode 100644 src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.js create mode 100644 src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.vue diff --git a/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.css b/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.css new file mode 100644 index 0000000000000..20e216423f167 --- /dev/null +++ b/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.css @@ -0,0 +1,17 @@ +/* + Set a height to invisible/unloaded elements, so that lazy loading actually works. + If we don't set a height, they all get a height of 0px (because they have no content), + so they all bunch up together and end up loading all of them in one go. + */ +.placeholder { + block-size: 40px; +} + +.videoIndex { + color: var(--tertiary-text-color); + text-align: center; +} + +.videoIndexIcon { + font-size: 14px; +} diff --git a/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.js b/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.js new file mode 100644 index 0000000000000..9ed32bb8e9a89 --- /dev/null +++ b/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.js @@ -0,0 +1,124 @@ +import { defineComponent } from 'vue' +import FtListVideo from '../ft-list-video/ft-list-video.vue' + +export default defineComponent({ + name: 'FtListVideoNumbered', + components: { + 'ft-list-video': FtListVideo + }, + props: { + data: { + type: Object, + required: true + }, + playlistId: { + type: String, + default: null + }, + playlistType: { + type: String, + default: null + }, + playlistIndex: { + type: Number, + default: null + }, + playlistReverse: { + type: Boolean, + default: false + }, + playlistShuffle: { + type: Boolean, + default: false + }, + playlistLoop: { + type: Boolean, + default: false + }, + playlistItemId: { + type: String, + default: null, + }, + appearance: { + type: String, + required: true + }, + initialVisibleState: { + type: Boolean, + default: false, + }, + alwaysShowAddToPlaylistButton: { + type: Boolean, + default: false, + }, + quickBookmarkButtonEnabled: { + type: Boolean, + default: true, + }, + canMoveVideoUp: { + type: Boolean, + default: false, + }, + canMoveVideoDown: { + type: Boolean, + default: false, + }, + canRemoveFromPlaylist: { + type: Boolean, + default: false, + }, + videoIndex: { + type: Number, + default: -1 + }, + isCurrentVideo: { + type: Boolean, + default: false + }, + useChannelsHiddenPreference: { + type: Boolean, + default: false, + } + }, + data: function () { + return { + visible: false, + show: true + } + }, + computed: { + channelsHidden() { + // Some component users like channel view will have this disabled + if (!this.useChannelsHiddenPreference) { return [] } + + return JSON.parse(this.$store.getters.getChannelsHidden).map((ch) => { + // Legacy support + if (typeof ch === 'string') { + return { name: ch, preferredName: '', icon: '' } + } + return ch + }) + }, + + // As we only use this component in Playlist and watch-video-playlist, + // where title filtering is never desired, we don't have any title filtering logic here, + // like we do in ft-list-video-lazy + + shouldBeVisible() { + return !(this.channelsHidden.some(ch => ch.name === this.data.authorId) || + this.channelsHidden.some(ch => ch.name === this.data.author)) + } + }, + created() { + this.visible = this.initialVisibleState + }, + methods: { + onVisibilityChanged: function (visible) { + if (visible && this.shouldBeVisible) { + this.visible = visible + } else if (visible) { + this.show = false + } + } + } +}) diff --git a/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.vue b/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.vue new file mode 100644 index 0000000000000..745a704fe84c5 --- /dev/null +++ b/src/renderer/components/ft-list-video-numbered/ft-list-video-numbered.vue @@ -0,0 +1,53 @@ + + +