Skip to content

Commit 08a8a01

Browse files
committed
Merge branch 'feature/playlist-search-videos-in-one-user-playlist-2' into custom-builds/tmp
* feature/playlist-search-videos-in-one-user-playlist-2: Fix local API search erroring, because the default parameter values were not getting applied (FreeTubeApp#4704) * Show message when search returns no result ! Fix load more button appears when searching & visible items under pagination limit * Update single playlist view for user playlists to add search video function
2 parents cb29229 + 0bb522c commit 08a8a01

File tree

7 files changed

+148
-50
lines changed

7 files changed

+148
-50
lines changed

src/renderer/components/playlist-info/playlist-info.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
55
import FtIconButton from '../ft-icon-button/ft-icon-button.vue'
66
import FtInput from '../ft-input/ft-input.vue'
77
import FtPrompt from '../ft-prompt/ft-prompt.vue'
8+
import FtButton from '../ft-button/ft-button.vue'
89
import {
910
formatNumber,
1011
showToast,
1112
} from '../../helpers/utils'
13+
import debounce from 'lodash.debounce'
1214

1315
export default defineComponent({
1416
name: 'PlaylistInfo',
@@ -18,6 +20,7 @@ export default defineComponent({
1820
'ft-icon-button': FtIconButton,
1921
'ft-input': FtInput,
2022
'ft-prompt': FtPrompt,
23+
'ft-button': FtButton,
2124
},
2225
props: {
2326
id: {
@@ -83,6 +86,9 @@ export default defineComponent({
8386
},
8487
data: function () {
8588
return {
89+
searchVideoMode: false,
90+
query: '',
91+
updateQueryDebounce: function() {},
8692
editMode: false,
8793
showDeletePlaylistPrompt: false,
8894
showRemoveVideosOnWatchPrompt: false,
@@ -232,6 +238,8 @@ export default defineComponent({
232238
created: function () {
233239
this.newTitle = this.title
234240
this.newDescription = this.description
241+
242+
this.updateQueryDebounce = debounce(this.updateQuery, 500)
235243
},
236244
methods: {
237245
toggleCopyVideosPrompt: function (force = false) {
@@ -373,6 +381,25 @@ export default defineComponent({
373381
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.Quick bookmark disabled'))
374382
},
375383

384+
updateQuery(query) {
385+
this.query = query
386+
this.$emit('search-video-query-change', query)
387+
},
388+
enableVideoSearchMode() {
389+
this.searchVideoMode = true
390+
this.$emit('search-video-mode-on')
391+
392+
nextTick(() => {
393+
// Some elements only present after rendering update
394+
this.$refs.searchInput.focus()
395+
})
396+
},
397+
disableVideoSearchMode() {
398+
this.searchVideoMode = false
399+
this.updateQuery('')
400+
this.$emit('search-video-mode-off')
401+
},
402+
376403
...mapActions([
377404
'showAddToPlaylistPromptForManyVideos',
378405
'updatePlaylist',

src/renderer/components/playlist-info/playlist-info.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@
7373
justify-content: flex-end;
7474
}
7575

76+
.searchInputsRow {
77+
margin-block-start: 8px;
78+
79+
display: grid;
80+
81+
/* 2 columns */
82+
grid-template-columns: 1fr auto;
83+
column-gap: 8px;
84+
}
85+
7686
@media only screen and (max-width: 1250px) {
7787
:deep(.sharePlaylistIcon .iconDropdown) {
7888
inset-inline-start: auto;

src/renderer/components/playlist-info/playlist-info.vue

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<hr>
7777

7878
<div
79+
v-if="!searchVideoMode"
7980
class="channelShareWrapper"
8081
>
8182
<router-link
@@ -106,6 +107,13 @@
106107
</div>
107108

108109
<div class="playlistOptions">
110+
<ft-icon-button
111+
v-if="isUserPlaylist && !editMode"
112+
:title="$t('User Playlists.SinglePlaylistView.Search for Videos')"
113+
:icon="['fas', 'search']"
114+
theme="secondary"
115+
@click="enableVideoSearchMode"
116+
/>
109117
<ft-icon-button
110118
v-if="editMode"
111119
:title="$t('User Playlists.Save Changes')"
@@ -187,6 +195,28 @@
187195
@click="handleRemoveVideosOnWatchPromptAnswer"
188196
/>
189197
</div>
198+
199+
<div
200+
v-if="isUserPlaylist && searchVideoMode"
201+
class="searchInputsRow"
202+
>
203+
<ft-input
204+
ref="searchInput"
205+
class="searchInput"
206+
:placeholder="$t('User Playlists.SinglePlaylistView.Search for Videos')"
207+
:show-clear-text-button="true"
208+
:show-action-button="false"
209+
@input="(input) => updateQueryDebounce(input)"
210+
@clear="updateQueryDebounce('')"
211+
/>
212+
<ft-icon-button
213+
v-if="isUserPlaylist && searchVideoMode"
214+
:title="$t('User Playlists.Cancel')"
215+
:icon="['fas', 'times']"
216+
theme="secondary"
217+
@click="disableVideoSearchMode"
218+
/>
219+
</div>
190220
</div>
191221
</template>
192222

src/renderer/helpers/api/local.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,23 @@ const TRACKING_PARAM_NAMES = [
3535
* @param {boolean} options.generateSessionLocally generate the session locally or let YouTube generate it (local is faster, remote is more accurate)
3636
* @returns the Innertube instance
3737
*/
38-
async function createInnertube(options = { withPlayer: false, location: undefined, safetyMode: false, clientType: undefined, generateSessionLocally: true }) {
38+
async function createInnertube({ withPlayer = false, location = undefined, safetyMode = false, clientType = undefined, generateSessionLocally = true } = {}) {
3939
let cache
40-
if (options.withPlayer) {
40+
if (withPlayer) {
4141
const userData = await getUserDataPath()
4242
cache = new PlayerCache(join(userData, 'player_cache'))
4343
}
4444

4545
return await Innertube.create({
46-
retrieve_player: !!options.withPlayer,
47-
location: options.location,
48-
enable_safety_mode: !!options.safetyMode,
49-
client_type: options.clientType,
46+
retrieve_player: !!withPlayer,
47+
location: location,
48+
enable_safety_mode: !!safetyMode,
49+
client_type: clientType,
5050

5151
// use browser fetch
5252
fetch: (input, init) => fetch(input, init),
5353
cache,
54-
generate_session_locally: !!options.generateSessionLocally
54+
generate_session_locally: !!generateSessionLocally
5555
})
5656
}
5757

src/renderer/views/Playlist/Playlist.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export default defineComponent({
6060
getPlaylistInfoDebounce: function() {},
6161
playlistInEditMode: false,
6262

63+
playlistInVideoSearchMode: false,
64+
videoSearchQuery: '',
65+
6366
promptOpen: false,
6467
}
6568
},
@@ -104,7 +107,7 @@ export default defineComponent({
104107

105108
moreVideoDataAvailable() {
106109
if (this.isUserPlaylistRequested) {
107-
return this.userPlaylistVisibleLimit < this.videoCount
110+
return this.userPlaylistVisibleLimit < this.sometimesFilteredUserPlaylistItems.length
108111
} else {
109112
return this.continuationData !== null
110113
}
@@ -123,17 +126,29 @@ export default defineComponent({
123126
return this.selectedUserPlaylist?._id !== this.quickBookmarkPlaylistId
124127
},
125128

129+
sometimesFilteredUserPlaylistItems() {
130+
if (!this.isUserPlaylistRequested) { return this.playlistItems }
131+
if (this.processedVideoSearchQuery === '') { return this.playlistItems }
132+
133+
return this.playlistItems.filter((v) => {
134+
return v.title.toLowerCase().includes(this.processedVideoSearchQuery)
135+
})
136+
},
126137
visiblePlaylistItems: function () {
127138
if (!this.isUserPlaylistRequested) {
139+
// No filtering for non user playlists yet
128140
return this.playlistItems
129141
}
130142

131-
if (this.userPlaylistVisibleLimit < this.videoCount) {
132-
return this.playlistItems.slice(0, this.userPlaylistVisibleLimit)
143+
if (this.userPlaylistVisibleLimit < this.sometimesFilteredUserPlaylistItems.length) {
144+
return this.sometimesFilteredUserPlaylistItems.slice(0, this.userPlaylistVisibleLimit)
133145
} else {
134-
return this.playlistItems
146+
return this.sometimesFilteredUserPlaylistItems
135147
}
136-
}
148+
},
149+
processedVideoSearchQuery() {
150+
return this.videoSearchQuery.trim().toLowerCase()
151+
},
137152
},
138153
watch: {
139154
$route () {

src/renderer/views/Playlist/Playlist.vue

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
}"
2929
@enter-edit-mode="playlistInEditMode = true"
3030
@exit-edit-mode="playlistInEditMode = false"
31+
@search-video-mode-on="playlistInVideoSearchMode = true"
32+
@search-video-mode-off="playlistInVideoSearchMode = false"
33+
@search-video-query-change="(v) => videoSearchQuery = v"
3134
@prompt-open="promptOpen = true"
3235
@prompt-close="promptOpen = false"
3336
/>
@@ -39,48 +42,59 @@
3942
<template
4043
v-if="playlistItems.length > 0"
4144
>
42-
<transition-group
43-
name="playlistItem"
44-
tag="span"
45+
<template
46+
v-if="visiblePlaylistItems.length > 0"
4547
>
46-
<ft-list-video-numbered
47-
v-for="(item, index) in visiblePlaylistItems"
48-
:key="`${item.videoId}-${item.playlistItemId || index}`"
49-
class="playlistItem"
50-
:data="item"
51-
:playlist-id="playlistId"
52-
:playlist-type="infoSource"
53-
:playlist-index="index"
54-
:playlist-item-id="item.playlistItemId"
55-
appearance="result"
56-
:always-show-add-to-playlist-button="true"
57-
:quick-bookmark-button-enabled="quickBookmarkButtonEnabled"
58-
:can-move-video-up="index > 0"
59-
:can-move-video-down="index < visiblePlaylistItems.length - 1"
60-
:can-remove-from-playlist="true"
61-
:video-index="index"
62-
:initial-visible-state="index < 10"
63-
@move-video-up="moveVideoUp(item.videoId, item.playlistItemId)"
64-
@move-video-down="moveVideoDown(item.videoId, item.playlistItemId)"
65-
@remove-from-playlist="removeVideoFromPlaylist(item.videoId, item.playlistItemId)"
66-
/>
67-
</transition-group>
48+
<transition-group
49+
name="playlistItem"
50+
tag="span"
51+
>
52+
<ft-list-video-numbered
53+
v-for="(item, index) in visiblePlaylistItems"
54+
:key="`${item.videoId}-${item.playlistItemId || index}`"
55+
class="playlistItem"
56+
:data="item"
57+
:playlist-id="playlistId"
58+
:playlist-type="infoSource"
59+
:playlist-index="index"
60+
:playlist-item-id="item.playlistItemId"
61+
appearance="result"
62+
:always-show-add-to-playlist-button="true"
63+
:quick-bookmark-button-enabled="quickBookmarkButtonEnabled"
64+
:can-move-video-up="index > 0 && !playlistInVideoSearchMode"
65+
:can-move-video-down="index < playlistItems.length - 1 && !playlistInVideoSearchMode"
66+
:can-remove-from-playlist="true"
67+
:video-index="index"
68+
:initial-visible-state="index < 10"
69+
@move-video-up="moveVideoUp(item.videoId, item.playlistItemId)"
70+
@move-video-down="moveVideoDown(item.videoId, item.playlistItemId)"
71+
@remove-from-playlist="removeVideoFromPlaylist(item.videoId, item.playlistItemId)"
72+
/>
73+
</transition-group>
74+
<ft-flex-box
75+
v-if="moreVideoDataAvailable && !isLoadingMore"
76+
>
77+
<ft-button
78+
:label="$t('Subscriptions.Load More Videos')"
79+
background-color="var(--primary-color)"
80+
text-color="var(--text-with-main-color)"
81+
@click="getNextPage"
82+
/>
83+
</ft-flex-box>
84+
<div
85+
v-if="isLoadingMore"
86+
class="loadNextPageWrapper"
87+
>
88+
<ft-loader />
89+
</div>
90+
</template>
6891
<ft-flex-box
69-
v-if="moreVideoDataAvailable && !isLoadingMore"
92+
v-else
7093
>
71-
<ft-button
72-
:label="$t('Subscriptions.Load More Videos')"
73-
background-color="var(--primary-color)"
74-
text-color="var(--text-with-main-color)"
75-
@click="getNextPage"
76-
/>
94+
<p class="message">
95+
{{ $t("User Playlists['Empty Search Message']") }}
96+
</p>
7797
</ft-flex-box>
78-
<div
79-
v-if="isLoadingMore"
80-
class="loadNextPageWrapper"
81-
>
82-
<ft-loader />
83-
</div>
8498
</template>
8599
<ft-flex-box
86100
v-else

static/locales/en-US.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ User Playlists:
187187
EarliestPlayedFirst: 'Earliest Played'
188188

189189
SinglePlaylistView:
190+
Search for Videos: Search for Videos
191+
190192
Toast:
191193
This video cannot be moved up.: This video cannot be moved up.
192194
This video cannot be moved down.: This video cannot be moved down.

0 commit comments

Comments
 (0)