Skip to content

Commit

Permalink
Playlist performance improvements (#4597)
Browse files Browse the repository at this point in the history
* Implement pagination for user playlists on the Playlist page

* Fix the load more loading icon being displayed off-screen

* Caching user playlists is unnecessary as they are already in the store

* ft-list-video: Only render description element if there is a description

* Minor speed ups in the playlists store

* ft-list-video: Remove superfluous template around view count

* ft-list-video: Replace watched property with historyEntryExists computed property

* playlist-info: Fix copying partially loaded user playlists

* Fix the playlist number not getting lazy loaded

* Reduce nesting levels

Co-authored-by: PikachuEXE <git@pikachuexe.net>

---------

Co-authored-by: PikachuEXE <git@pikachuexe.net>
  • Loading branch information
absidue and PikachuEXE authored Feb 1, 2024
1 parent 5a6d4d5 commit 351fdb9
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 117 deletions.
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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
}
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<div
v-show="show"
v-observe-visibility="!initialVisibleState ? {
callback: onVisibilityChanged,
once: true,
} : null"
:class="{ placeholder: !visible }"
>
<template
v-if="visible"
>
<p
class="videoIndex"
>
<font-awesome-icon
v-if="isCurrentVideo"
class="videoIndexIcon"
:icon="['fas', 'play']"
/>
<template
v-else
>
{{ videoIndex + 1 }}
</template>
</p>
<ft-list-video
:data="data"
:playlist-id="playlistId"
:playlist-type="playlistType"
:playlist-index="playlistIndex"
:playlist-reverse="playlistReverse"
:playlist-shuffle="playlistShuffle"
:playlist-loop="playlistLoop"
:playlist-item-id="playlistItemId"
force-list-type="list"
:appearance="appearance"
:always-show-add-to-playlist-button="alwaysShowAddToPlaylistButton"
:quick-bookmark-button-enabled="quickBookmarkButtonEnabled"
:can-move-video-up="canMoveVideoUp"
:can-move-video-down="canMoveVideoDown"
:can-remove-from-playlist="canRemoveFromPlaylist"
@pause-player="$emit('pause-player')"
@move-video-up="$emit('move-video-up')"
@move-video-down="$emit('move-video-down')"
@remove-from-playlist="$emit('remove-from-playlist')"
/>
</template>
</div>
</template>

<script src="./ft-list-video-numbered.js" />
<style scoped src="./ft-list-video-numbered.css" />
15 changes: 4 additions & 11 deletions src/renderer/components/ft-list-video/ft-list-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ export default defineComponent({
lengthSeconds: 0,
duration: '',
description: '',
watched: false,
watchProgress: 0,
publishedText: '',
isLive: false,
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -343,7 +342,7 @@ export default defineComponent({
},

addWatchedStyle: function () {
return this.watched && !this.inHistory
return this.historyEntryExists && !this.inHistory
},

externalPlayer: function () {
Expand Down Expand Up @@ -576,15 +575,15 @@ export default defineComponent({
}
this.openInExternalPlayer(payload)

if (this.saveWatchedProgress && !this.watched) {
if (this.saveWatchedProgress && !this.historyEntryExists) {
this.markAsWatched()
}
},

handleOptionsClick: function (option) {
switch (option) {
case 'history':
if (this.watched) {
if (this.historyEntryExists) {
this.removeFromWatched()
} else {
this.markAsWatched()
Expand Down Expand Up @@ -727,8 +726,6 @@ export default defineComponent({

checkIfWatched: function () {
if (this.historyEntryExists) {
this.watched = true

const historyEntry = this.historyEntry

if (this.saveWatchedProgress) {
Expand All @@ -744,7 +741,6 @@ export default defineComponent({
this.publishedText = ''
}
} else {
this.watched = false
this.watchProgress = 0
}
},
Expand All @@ -766,16 +762,13 @@ export default defineComponent({
}
this.updateHistory(videoData)
showToast(this.$t('Video.Video has been marked as watched'))

this.watched = true
},

removeFromWatched: function () {
this.removeFromHistory(this.id)

showToast(this.$t('Video.Video has been removed from your history'))

this.watched = false
this.watchProgress = 0
},

Expand Down
17 changes: 9 additions & 8 deletions src/renderer/components/ft-list-video/ft-list-video.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
{{ $t("Video.Watched") }}
</div>
<div
v-if="watched"
v-if="historyEntryExists"
class="watchedProgressBar"
:style="{inlineSize: progressPercentage + '%'}"
/>
Expand All @@ -129,12 +129,13 @@
<span v-else-if="channelName !== null">
{{ channelName }}
</span>
<template v-if="!isLive && !isUpcoming && !isPremium && !hideViews && viewCount != null">
<span class="viewCount">
<template v-if="channelId !== null"> • </template>
{{ $tc('Global.Counts.View Count', viewCount, {count: parsedViewCount}) }}
</span>
</template>
<span
v-if="!isLive && !isUpcoming && !isPremium && !hideViews && viewCount != null"
class="viewCount"
>
<template v-if="channelId !== null || channelName !== null"> • </template>
{{ $tc('Global.Counts.View Count', viewCount, {count: parsedViewCount}) }}
</span>
<span
v-if="uploadedTime !== '' && !isLive && !inHistory"
class="uploadedTime"
Expand All @@ -160,7 +161,7 @@
@click="handleOptionsClick"
/>
<p
v-if="((listType === 'list' || forceListType === 'list') && forceListType !== 'grid') &&
v-if="description && ((listType === 'list' || forceListType === 'list') && forceListType !== 'grid') &&
appearance === 'result'"
class="description"
v-html="description"
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/playlist-info/playlist-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,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)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,6 @@
transition: background 0.2s ease-in;
}

.videoIndexContainer {
text-align: center;
}

.videoIndex {
color: var(--tertiary-text-color);
}

.videoIndexIcon {
font-size: 14px;
color: var(--tertiary-text-color);
}

.videoInfo {
margin-inline-start: 30px;
position: relative;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineComponent, nextTick } from 'vue'
import { mapMutations } from 'vuex'
import FtLoader from '../ft-loader/ft-loader.vue'
import FtCard from '../ft-card/ft-card.vue'
import FtListVideoLazy from '../ft-list-video-lazy/ft-list-video-lazy.vue'
import FtListVideoNumbered from '../ft-list-video-numbered/ft-list-video-numbered.vue'
import { copyToClipboard, showToast } from '../../helpers/utils'
import {
getLocalPlaylist,
Expand All @@ -16,7 +16,7 @@ export default defineComponent({
components: {
'ft-loader': FtLoader,
'ft-card': FtCard,
'ft-list-video-lazy': FtListVideoLazy,
'ft-list-video-numbered': FtListVideoNumbered
},
props: {
playlistId: {
Expand Down Expand Up @@ -473,14 +473,13 @@ export default defineComponent({
parseUserPlaylist: function (playlist, { allowPlayingVideoRemoval = true } = {}) {
this.playlistTitle = playlist.playlistName
this.channelName = ''
this.channelThumbnail = ''
this.channelId = ''

if (this.playlistItems.length === 0 || allowPlayingVideoRemoval) {
this.playlistItems = playlist.videos
} else {
// `this.currentVideo` relies on `playlistItems`
const latestPlaylistContainsCurrentVideo = playlist.videos.find(v => v.playlistItemId === this.playlistItemId) != null
const latestPlaylistContainsCurrentVideo = playlist.videos.some(v => v.playlistItemId === this.playlistItemId)
// Only update list of videos if latest video list still contains currently playing video
if (latestPlaylistContainsCurrentVideo) {
this.playlistItems = playlist.videos
Expand Down Expand Up @@ -513,7 +512,7 @@ export default defineComponent({
const currentVideoItem = (this.$refs.currentVideoItem || [])[0]
if (container != null && currentVideoItem != null) {
// Watch view can be ready sooner than this component
container.scrollTop = currentVideoItem.offsetTop - container.offsetTop
container.scrollTop = currentVideoItem.$el.offsetTop - container.offsetTop
}
},

Expand Down
Loading

0 comments on commit 351fdb9

Please sign in to comment.