Skip to content

Commit f03f4b6

Browse files
Add Playlist Sort By Video Duration (#5627)
1 parent 6c6ea23 commit f03f4b6

File tree

4 files changed

+76
-2
lines changed

4 files changed

+76
-2
lines changed

src/renderer/helpers/playlists.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export const SORT_BY_VALUES = {
55
AuthorDescending: 'author_descending',
66
VideoTitleAscending: 'video_title_ascending',
77
VideoTitleDescending: 'video_title_descending',
8+
VideoDurationAscending: 'video_duration_ascending',
9+
VideoDurationDescending: 'video_duration_descending',
810
Custom: 'custom'
911
}
1012

@@ -19,7 +21,9 @@ export function getSortedPlaylistItems(playlistItems, sortOrder, locale, reverse
1921
sortOrder === SORT_BY_VALUES.VideoTitleAscending ||
2022
sortOrder === SORT_BY_VALUES.VideoTitleDescending ||
2123
sortOrder === SORT_BY_VALUES.AuthorAscending ||
22-
sortOrder === SORT_BY_VALUES.AuthorDescending
24+
sortOrder === SORT_BY_VALUES.AuthorDescending ||
25+
sortOrder === SORT_BY_VALUES.VideoDurationAscending ||
26+
sortOrder === SORT_BY_VALUES.VideoDurationDescending
2327
) {
2428
collator = new Intl.Collator([locale, 'en'])
2529
}
@@ -31,6 +35,19 @@ export function getSortedPlaylistItems(playlistItems, sortOrder, locale, reverse
3135
})
3236
}
3337

38+
export function videoDurationPresent(video) {
39+
if (typeof video.lengthSeconds !== 'number') { return false }
40+
41+
return !(isNaN(video.lengthSeconds) || video.lengthSeconds === 0)
42+
}
43+
44+
export function videoDurationWithFallback(video) {
45+
if (videoDurationPresent(video)) { return video.lengthSeconds }
46+
47+
// Fallback
48+
return 0
49+
}
50+
3451
function compareTwoPlaylistItems(a, b, sortOrder, collator) {
3552
switch (sortOrder) {
3653
case SORT_BY_VALUES.DateAddedNewest:
@@ -45,6 +62,12 @@ function compareTwoPlaylistItems(a, b, sortOrder, collator) {
4562
return collator.compare(a.author, b.author)
4663
case SORT_BY_VALUES.AuthorDescending:
4764
return collator.compare(b.author, a.author)
65+
case SORT_BY_VALUES.VideoDurationAscending: {
66+
return videoDurationWithFallback(a) - videoDurationWithFallback(b)
67+
}
68+
case SORT_BY_VALUES.VideoDurationDescending: {
69+
return videoDurationWithFallback(b) - videoDurationWithFallback(a)
70+
}
4871
default:
4972
console.error(`Unknown sortOrder: ${sortOrder}`)
5073
return 0

src/renderer/views/Playlist/Playlist.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import {
2020
getIconForSortPreference,
2121
setPublishedTimestampsInvidious,
2222
showToast,
23+
deepCopy,
2324
} from '../../helpers/utils'
2425
import { invidiousGetPlaylistInfo, youtubeImageUrlToInvidious } from '../../helpers/api/invidious'
25-
import { getSortedPlaylistItems, SORT_BY_VALUES } from '../../helpers/playlists'
26+
import { getSortedPlaylistItems, videoDurationPresent, videoDurationWithFallback, SORT_BY_VALUES } from '../../helpers/playlists'
2627
import packageDetails from '../../../../package.json'
2728
import { MOBILE_WIDTH_THRESHOLD, PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD } from '../../../constants'
2829

@@ -74,6 +75,7 @@ export default defineComponent({
7475
getPlaylistInfoDebounce: function() {},
7576
playlistInEditMode: false,
7677
forceListView: false,
78+
alreadyShownNotice: false,
7779

7880
videoSearchQuery: '',
7981

@@ -180,6 +182,13 @@ export default defineComponent({
180182
return this.sortOrder === SORT_BY_VALUES.Custom
181183
},
182184
sortedPlaylistItems: function () {
185+
if (
186+
this.sortOrder === SORT_BY_VALUES.VideoDurationAscending ||
187+
this.sortOrder === SORT_BY_VALUES.VideoDurationDescending
188+
) {
189+
const playlistItems = this.getPlaylistItemsWithDuration()
190+
return getSortedPlaylistItems(playlistItems, this.sortOrder, this.currentLocale)
191+
}
183192
return getSortedPlaylistItems(this.playlistItems, this.sortOrder, this.currentLocale)
184193
},
185194
visiblePlaylistItems: function () {
@@ -214,6 +223,10 @@ export default defineComponent({
214223
return this.$t('Playlist.Sort By.AuthorAscending')
215224
case SORT_BY_VALUES.AuthorDescending:
216225
return this.$t('Playlist.Sort By.AuthorDescending')
226+
case SORT_BY_VALUES.VideoDurationAscending:
227+
return this.$t('Playlist.Sort By.VideoDurationAscending')
228+
case SORT_BY_VALUES.VideoDurationDescending:
229+
return this.$t('Playlist.Sort By.VideoDurationDescending')
217230
default:
218231
console.error(`Unknown sort: ${k}`)
219232
return k
@@ -416,10 +429,40 @@ export default defineComponent({
416429

417430
this.isLoading = false
418431
},
432+
419433
showUserPlaylistNotFound() {
420434
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.This playlist does not exist'))
421435
},
422436

437+
getPlaylistItemsWithDuration() {
438+
const modifiedPlaylistItems = deepCopy(this.playlistItems)
439+
let anyVideoMissingDuration = false
440+
modifiedPlaylistItems.forEach(video => {
441+
if (videoDurationPresent(video)) { return }
442+
443+
const videoHistory = this.$store.getters.getHistoryCacheById[video.videoId]
444+
if (typeof videoHistory !== 'undefined') {
445+
const fetchedLengthSeconds = videoDurationWithFallback(videoHistory)
446+
video.lengthSeconds = fetchedLengthSeconds
447+
// if the video duration is 0, it will be the fallback value, so mark it as missing a duration
448+
if (fetchedLengthSeconds === 0) { anyVideoMissingDuration = true }
449+
} else {
450+
// Mark at least one video have no duration, show notice later
451+
// Also assign fallback duration here
452+
anyVideoMissingDuration = true
453+
video.lengthSeconds = 0
454+
}
455+
})
456+
457+
// Show notice if not already shown before returning playlist items
458+
if (anyVideoMissingDuration && !this.alreadyShownNotice) {
459+
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.This playlist has a video with a duration error'), 5000)
460+
this.alreadyShownNotice = true
461+
}
462+
463+
return modifiedPlaylistItems
464+
},
465+
423466
getNextPage: function () {
424467
switch (this.infoSource) {
425468
case 'local':

static/locales/en-GB.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ User Playlists:
174174
This playlist is protected and cannot be removed.: This playlist is protected
175175
and cannot be removed.
176176
This playlist does not exist: This playlist does not exist
177+
178+
This playlist has a video with a duration error: This playlist contains at least one video that doesn't have a duration, it will be sorted as if their duration is zero.
177179
Playlist {playlistName} has been deleted.: Playlist {playlistName} has been
178180
deleted.
179181
Video has been removed: Video has been removed
@@ -1006,6 +1008,8 @@ Playlist:
10061008
AuthorAscending: Author (A-Z)
10071009
AuthorDescending: Author (Z-A)
10081010
VideoTitleAscending: Title (A-Z)
1011+
VideoDurationAscending: Duration (Shortest first)
1012+
VideoDurationDescending: Duration (Longest first)
10091013
Toggle Theatre Mode: 'Toggle Theatre Mode'
10101014
Change Format:
10111015
Change Media Formats: 'Change Media Formats'

static/locales/en-US.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ User Playlists:
241241
Playlist {playlistName} is the new quick bookmark playlist.: Playlist {playlistName} is the new quick bookmark playlist.
242242

243243
This playlist does not exist: This playlist does not exist
244+
245+
This playlist has a video with a duration error: This playlist contains at least one video that doesn't have a duration, it will be sorted as if their duration is zero.
244246
AddVideoPrompt:
245247
Select a playlist to add your N videos to: 'Select a playlist to add your video to | Select a playlist to add your {videoCount} videos to'
246248
N playlists selected: '{playlistCount} Selected'
@@ -934,6 +936,8 @@ Playlist:
934936
AuthorDescending: Author (Z-A)
935937
VideoTitleAscending: Title (A-Z)
936938
VideoTitleDescending: Title (Z-A)
939+
VideoDurationAscending: Duration (Shortest first)
940+
VideoDurationDescending: Duration (Longest first)
937941
Custom: Custom
938942

939943
# On Video Watch Page

0 commit comments

Comments
 (0)