1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2018 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include <vlc_common.h>
33 * Struct containing a copy of (parsed) media metadata, used for sorting
34 * without locking all the items.
36 struct vlc_playlist_item_meta
{
37 vlc_playlist_item_t
*item
;
38 const char *title_or_name
;
42 const char *album_artist
;
50 bool has_track_number
;
56 vlc_playlist_item_meta_CopyString(const char **to
, const char *from
)
70 vlc_playlist_item_meta_InitField(struct vlc_playlist_item_meta
*meta
,
71 enum vlc_playlist_sort_key key
)
73 input_item_t
*media
= meta
->item
->media
;
76 case VLC_PLAYLIST_SORT_KEY_TITLE
:
78 const char *value
= input_item_GetMetaLocked(media
, vlc_meta_Title
);
80 value
= media
->psz_name
;
81 return vlc_playlist_item_meta_CopyString(&meta
->title_or_name
,
84 case VLC_PLAYLIST_SORT_KEY_DURATION
:
86 if (media
->i_duration
== INPUT_DURATION_INDEFINITE
87 || media
->i_duration
== INPUT_DURATION_UNSET
)
90 meta
->duration
= media
->i_duration
;
93 case VLC_PLAYLIST_SORT_KEY_ARTIST
:
95 const char *value
= input_item_GetMetaLocked(media
,
97 return vlc_playlist_item_meta_CopyString(&meta
->artist
, value
);
99 case VLC_PLAYLIST_SORT_KEY_ALBUM
:
101 const char *value
= input_item_GetMetaLocked(media
, vlc_meta_Album
);
102 return vlc_playlist_item_meta_CopyString(&meta
->album
, value
);
104 case VLC_PLAYLIST_SORT_KEY_ALBUM_ARTIST
:
106 const char *value
= input_item_GetMetaLocked(media
,
107 vlc_meta_AlbumArtist
);
108 return vlc_playlist_item_meta_CopyString(&meta
->album_artist
,
111 case VLC_PLAYLIST_SORT_KEY_GENRE
:
113 const char *value
= input_item_GetMetaLocked(media
, vlc_meta_Genre
);
114 return vlc_playlist_item_meta_CopyString(&meta
->genre
, value
);
116 case VLC_PLAYLIST_SORT_KEY_DATE
:
118 const char *str
= input_item_GetMetaLocked(media
, vlc_meta_Date
);
119 meta
->has_date
= !EMPTY_STR(str
);
121 meta
->date
= atoll(str
);
124 case VLC_PLAYLIST_SORT_KEY_TRACK_NUMBER
:
126 const char *str
= input_item_GetMetaLocked(media
,
127 vlc_meta_TrackNumber
);
128 meta
->has_track_number
= !EMPTY_STR(str
);
129 if (meta
->has_track_number
)
130 meta
->track_number
= atoll(str
);
133 case VLC_PLAYLIST_SORT_KEY_DISC_NUMBER
:
135 const char *str
= input_item_GetMetaLocked(media
,
136 vlc_meta_DiscNumber
);
137 meta
->has_disc_number
= !EMPTY_STR(str
);
138 if (meta
->has_disc_number
)
139 meta
->disc_number
= atoll(str
);
142 case VLC_PLAYLIST_SORT_KEY_URL
:
144 const char *value
= input_item_GetMetaLocked(media
, vlc_meta_URL
);
145 return vlc_playlist_item_meta_CopyString(&meta
->url
, value
);
147 case VLC_PLAYLIST_SORT_KEY_RATING
:
149 const char *str
= input_item_GetMetaLocked(media
, vlc_meta_Rating
);
150 meta
->has_rating
= !EMPTY_STR(str
);
151 if (meta
->has_rating
)
152 meta
->rating
= atoll(str
);
156 assert(!"Unknown sort key");
157 vlc_assert_unreachable();
162 vlc_playlist_item_meta_DestroyFields(struct vlc_playlist_item_meta
*meta
)
164 free((void *) meta
->title_or_name
);
165 free((void *) meta
->artist
);
166 free((void *) meta
->album
);
167 free((void *) meta
->album_artist
);
168 free((void *) meta
->genre
);
169 free((void *) meta
->url
);
173 vlc_playlist_item_meta_InitFields(struct vlc_playlist_item_meta
*meta
,
174 const struct vlc_playlist_sort_criterion criteria
[], size_t count
)
176 for (size_t i
= 0; i
< count
; ++i
)
178 const struct vlc_playlist_sort_criterion
*criterion
= &criteria
[i
];
179 int ret
= vlc_playlist_item_meta_InitField(meta
, criterion
->key
);
180 if (unlikely(ret
!= VLC_SUCCESS
))
182 vlc_playlist_item_meta_DestroyFields(meta
);
189 static struct vlc_playlist_item_meta
*
190 vlc_playlist_item_meta_New(vlc_playlist_item_t
*item
,
191 const struct vlc_playlist_sort_criterion criteria
[],
194 /* assume that NULL representation is all-zeros */
195 struct vlc_playlist_item_meta
*meta
= calloc(1, sizeof(*meta
));
201 vlc_mutex_lock(&item
->media
->lock
);
202 int ret
= vlc_playlist_item_meta_InitFields(meta
, criteria
, count
);
203 vlc_mutex_unlock(&item
->media
->lock
);
205 if (unlikely(ret
!= VLC_SUCCESS
))
215 vlc_playlist_item_meta_Delete(struct vlc_playlist_item_meta
*meta
)
217 vlc_playlist_item_meta_DestroyFields(meta
);
222 CompareStrings(const char *a
, const char *b
)
225 return strcasecmp(a
, b
);
232 CompareIntegers(int64_t a
, int64_t b
)
242 CompareOptionalIntegers(bool has_a
, int64_t a
, bool has_b
, int64_t b
)
245 return CompareIntegers(a
, b
);
247 if (!has_a
&& !has_b
)
254 CompareMetaByKey(const struct vlc_playlist_item_meta
*a
,
255 const struct vlc_playlist_item_meta
*b
,
256 enum vlc_playlist_sort_key key
)
260 case VLC_PLAYLIST_SORT_KEY_TITLE
:
261 return CompareStrings(a
->title_or_name
, b
->title_or_name
);
262 case VLC_PLAYLIST_SORT_KEY_DURATION
:
263 return CompareIntegers(a
->duration
, b
->duration
);
264 case VLC_PLAYLIST_SORT_KEY_ARTIST
:
265 return CompareStrings(a
->artist
, b
->artist
);
266 case VLC_PLAYLIST_SORT_KEY_ALBUM
:
267 return CompareStrings(a
->album
, b
->album
);
268 case VLC_PLAYLIST_SORT_KEY_ALBUM_ARTIST
:
269 return CompareStrings(a
->album_artist
, b
->album_artist
);
270 case VLC_PLAYLIST_SORT_KEY_GENRE
:
271 return CompareStrings(a
->genre
, b
->genre
);
272 case VLC_PLAYLIST_SORT_KEY_DATE
:
273 return CompareOptionalIntegers(a
->has_date
, a
->date
,
274 b
->has_date
, b
->date
);
275 case VLC_PLAYLIST_SORT_KEY_TRACK_NUMBER
:
276 return CompareOptionalIntegers(a
->has_track_number
, a
->track_number
,
277 b
->has_track_number
, b
->track_number
);
278 case VLC_PLAYLIST_SORT_KEY_DISC_NUMBER
:
279 return CompareOptionalIntegers(a
->has_disc_number
, a
->disc_number
,
280 b
->has_disc_number
, b
->disc_number
);
281 case VLC_PLAYLIST_SORT_KEY_URL
:
282 return CompareStrings(a
->url
, b
->url
);
283 case VLC_PLAYLIST_SORT_KEY_RATING
:
284 return CompareOptionalIntegers(a
->has_rating
, a
->rating
,
285 b
->has_rating
, b
->rating
);
287 assert(!"Unknown sort key");
288 vlc_assert_unreachable();
292 /* context for qsort_r() */
295 const struct vlc_playlist_sort_criterion
*criteria
;
300 compare_meta(const void *lhs
, const void *rhs
, void *userdata
)
302 struct sort_request
*req
= userdata
;
303 const struct vlc_playlist_item_meta
*a
=
304 *(const struct vlc_playlist_item_meta
**) lhs
;
305 const struct vlc_playlist_item_meta
*b
=
306 *(const struct vlc_playlist_item_meta
**) rhs
;
308 for (size_t i
= 0; i
< req
->count
; ++i
)
310 const struct vlc_playlist_sort_criterion
*criterion
= &req
->criteria
[i
];
311 int ret
= CompareMetaByKey(a
, b
, criterion
->key
);
314 if (criterion
->order
== VLC_PLAYLIST_SORT_ORDER_DESCENDING
)
315 /* do not return -ret, it's undefined if ret == INT_MIN */
316 return ret
> 0 ? -1 : 1;
324 vlc_playlist_DeleteMetaArray(struct vlc_playlist_item_meta
*array
[],
327 for (size_t i
= 0; i
< count
; ++i
)
328 vlc_playlist_item_meta_Delete(array
[i
]);
332 static struct vlc_playlist_item_meta
**
333 vlc_playlist_NewMetaArray(vlc_playlist_t
*playlist
,
334 const struct vlc_playlist_sort_criterion criteria
[], size_t count
)
336 struct vlc_playlist_item_meta
**array
=
337 vlc_alloc(playlist
->items
.size
, sizeof(*array
));
339 if (unlikely(!array
))
343 for (i
= 0; i
< playlist
->items
.size
; ++i
)
345 array
[i
] = vlc_playlist_item_meta_New(playlist
->items
.data
[i
],
347 if (unlikely(!array
[i
]))
351 if (i
< playlist
->items
.size
)
353 /* allocation failure */
354 vlc_playlist_DeleteMetaArray(array
, i
);
362 vlc_playlist_Sort(vlc_playlist_t
*playlist
,
363 const struct vlc_playlist_sort_criterion criteria
[],
367 vlc_playlist_AssertLocked(playlist
);
369 vlc_playlist_item_t
*current
= playlist
->current
!= -1
370 ? playlist
->items
.data
[playlist
->current
]
373 struct vlc_playlist_item_meta
**array
=
374 vlc_playlist_NewMetaArray(playlist
, criteria
, count
);
375 if (unlikely(!array
))
378 struct sort_request req
= { criteria
, count
};
380 qsort_r(array
, playlist
->items
.size
, sizeof(*array
), compare_meta
, &req
);
382 /* apply the sorting result to the playlist */
383 for (size_t i
= 0; i
< playlist
->items
.size
; ++i
)
384 playlist
->items
.data
[i
] = array
[i
]->item
;
386 vlc_playlist_DeleteMetaArray(array
, playlist
->items
.size
);
388 struct vlc_playlist_state state
;
391 /* the current position have changed after the shuffle */
392 vlc_playlist_state_Save(playlist
, &state
);
393 playlist
->current
= vlc_playlist_IndexOf(playlist
, current
);
394 playlist
->has_prev
= vlc_playlist_ComputeHasPrev(playlist
);
395 playlist
->has_next
= vlc_playlist_ComputeHasNext(playlist
);
398 vlc_playlist_Notify(playlist
, on_items_reset
, playlist
->items
.data
,
399 playlist
->items
.size
);
401 vlc_playlist_state_NotifyChanges(playlist
, &state
);