1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "SortUtils.h"
10
11 #include "LangInfo.h"
12 #include "URL.h"
13 #include "Util.h"
14 #include "utils/CharsetConverter.h"
15 #include "utils/StringUtils.h"
16 #include "utils/Variant.h"
17
18 #include <algorithm>
19 #include <inttypes.h>
20
ArrayToString(SortAttribute attributes,const CVariant & variant,const std::string & separator=" / ")21 std::string ArrayToString(SortAttribute attributes, const CVariant &variant, const std::string &separator = " / ")
22 {
23 std::vector<std::string> strArray;
24 if (variant.isArray())
25 {
26 for (CVariant::const_iterator_array it = variant.begin_array(); it != variant.end_array(); it++)
27 {
28 if (attributes & SortAttributeIgnoreArticle)
29 strArray.push_back(SortUtils::RemoveArticles(it->asString()));
30 else
31 strArray.push_back(it->asString());
32 }
33
34 return StringUtils::Join(strArray, separator);
35 }
36 else if (variant.isString())
37 {
38 if (attributes & SortAttributeIgnoreArticle)
39 return SortUtils::RemoveArticles(variant.asString());
40 else
41 return variant.asString();
42 }
43
44 return "";
45 }
46
ByLabel(SortAttribute attributes,const SortItem & values)47 std::string ByLabel(SortAttribute attributes, const SortItem &values)
48 {
49 if (attributes & SortAttributeIgnoreArticle)
50 return SortUtils::RemoveArticles(values.at(FieldLabel).asString());
51
52 return values.at(FieldLabel).asString();
53 }
54
ByFile(SortAttribute attributes,const SortItem & values)55 std::string ByFile(SortAttribute attributes, const SortItem &values)
56 {
57 CURL url(values.at(FieldPath).asString());
58
59 return StringUtils::Format("%s %" PRId64, url.GetFileNameWithoutPath().c_str(), values.at(FieldStartOffset).asInteger());
60 }
61
ByPath(SortAttribute attributes,const SortItem & values)62 std::string ByPath(SortAttribute attributes, const SortItem &values)
63 {
64 return StringUtils::Format("%s %" PRId64, values.at(FieldPath).asString().c_str(), values.at(FieldStartOffset).asInteger());
65 }
66
ByLastPlayed(SortAttribute attributes,const SortItem & values)67 std::string ByLastPlayed(SortAttribute attributes, const SortItem &values)
68 {
69 if (attributes & SortAttributeIgnoreLabel)
70 return values.at(FieldLastPlayed).asString();
71
72 return StringUtils::Format("%s %s", values.at(FieldLastPlayed).asString().c_str(), ByLabel(attributes, values).c_str());
73 }
74
ByPlaycount(SortAttribute attributes,const SortItem & values)75 std::string ByPlaycount(SortAttribute attributes, const SortItem &values)
76 {
77 return StringUtils::Format("%i %s", (int)values.at(FieldPlaycount).asInteger(), ByLabel(attributes, values).c_str());
78 }
79
ByDate(SortAttribute attributes,const SortItem & values)80 std::string ByDate(SortAttribute attributes, const SortItem &values)
81 {
82 return values.at(FieldDate).asString() + " " + ByLabel(attributes, values);
83 }
84
ByDateAdded(SortAttribute attributes,const SortItem & values)85 std::string ByDateAdded(SortAttribute attributes, const SortItem &values)
86 {
87 return StringUtils::Format("%s %d", values.at(FieldDateAdded).asString().c_str(), (int)values.at(FieldId).asInteger());
88 }
89
BySize(SortAttribute attributes,const SortItem & values)90 std::string BySize(SortAttribute attributes, const SortItem &values)
91 {
92 return StringUtils::Format("%" PRId64, values.at(FieldSize).asInteger());
93 }
94
ByDriveType(SortAttribute attributes,const SortItem & values)95 std::string ByDriveType(SortAttribute attributes, const SortItem &values)
96 {
97 return StringUtils::Format("%d %s", (int)values.at(FieldDriveType).asInteger(), ByLabel(attributes, values).c_str());
98 }
99
ByTitle(SortAttribute attributes,const SortItem & values)100 std::string ByTitle(SortAttribute attributes, const SortItem &values)
101 {
102 if (attributes & SortAttributeIgnoreArticle)
103 return SortUtils::RemoveArticles(values.at(FieldTitle).asString());
104
105 return values.at(FieldTitle).asString();
106 }
107
ByAlbum(SortAttribute attributes,const SortItem & values)108 std::string ByAlbum(SortAttribute attributes, const SortItem &values)
109 {
110 std::string album = values.at(FieldAlbum).asString();
111 if (attributes & SortAttributeIgnoreArticle)
112 album = SortUtils::RemoveArticles(album);
113
114 std::string label = StringUtils::Format("%s %s", album.c_str(), ArrayToString(attributes, values.at(FieldArtist)).c_str());
115
116 const CVariant &track = values.at(FieldTrackNumber);
117 if (!track.isNull())
118 label += StringUtils::Format(" %i", (int)track.asInteger());
119
120 return label;
121 }
122
ByAlbumType(SortAttribute attributes,const SortItem & values)123 std::string ByAlbumType(SortAttribute attributes, const SortItem &values)
124 {
125 return values.at(FieldAlbumType).asString() + " " + ByLabel(attributes, values);
126 }
127
ByArtist(SortAttribute attributes,const SortItem & values)128 std::string ByArtist(SortAttribute attributes, const SortItem &values)
129 {
130 std::string label;
131 if (attributes & SortAttributeUseArtistSortName)
132 {
133 const CVariant &artistsort = values.at(FieldArtistSort);
134 if (!artistsort.isNull())
135 label = artistsort.asString();
136 }
137 if (label.empty())
138 label = ArrayToString(attributes, values.at(FieldArtist));
139
140 const CVariant &album = values.at(FieldAlbum);
141 if (!album.isNull())
142 label += " " + SortUtils::RemoveArticles(album.asString());
143
144 const CVariant &track = values.at(FieldTrackNumber);
145 if (!track.isNull())
146 label += StringUtils::Format(" %i", (int)track.asInteger());
147
148 return label;
149 }
150
ByArtistThenYear(SortAttribute attributes,const SortItem & values)151 std::string ByArtistThenYear(SortAttribute attributes, const SortItem &values)
152 {
153 std::string label;
154 if (attributes & SortAttributeUseArtistSortName)
155 {
156 const CVariant &artistsort = values.at(FieldArtistSort);
157 if (!artistsort.isNull())
158 label = artistsort.asString();
159 }
160 if (label.empty())
161 label = ArrayToString(attributes, values.at(FieldArtist));
162
163 const CVariant &year = values.at(FieldYear);
164 if (!year.isNull())
165 label += StringUtils::Format(" %i", static_cast<int>(year.asInteger()));
166
167 const CVariant &album = values.at(FieldAlbum);
168 if (!album.isNull())
169 label += " " + SortUtils::RemoveArticles(album.asString());
170
171 const CVariant &track = values.at(FieldTrackNumber);
172 if (!track.isNull())
173 label += StringUtils::Format(" %i", (int)track.asInteger());
174
175 return label;
176 }
177
ByTrackNumber(SortAttribute attributes,const SortItem & values)178 std::string ByTrackNumber(SortAttribute attributes, const SortItem &values)
179 {
180 return StringUtils::Format("%i", (int)values.at(FieldTrackNumber).asInteger());
181 }
182
ByTotalDiscs(SortAttribute attributes,const SortItem & values)183 std::string ByTotalDiscs(SortAttribute attributes, const SortItem& values)
184 {
185 return StringUtils::Format("%d %s", static_cast<int>(values.at(FieldTotalDiscs).asInteger()),
186 ByLabel(attributes, values));
187 }
ByTime(SortAttribute attributes,const SortItem & values)188 std::string ByTime(SortAttribute attributes, const SortItem &values)
189 {
190 std::string label;
191 const CVariant &time = values.at(FieldTime);
192 if (time.isInteger())
193 label = StringUtils::Format("%i", (int)time.asInteger());
194 else
195 label = StringUtils::Format("%s", time.asString().c_str());
196 return label;
197 }
198
ByProgramCount(SortAttribute attributes,const SortItem & values)199 std::string ByProgramCount(SortAttribute attributes, const SortItem &values)
200 {
201 return StringUtils::Format("%i", (int)values.at(FieldProgramCount).asInteger());
202 }
203
ByPlaylistOrder(SortAttribute attributes,const SortItem & values)204 std::string ByPlaylistOrder(SortAttribute attributes, const SortItem &values)
205 {
206 //! @todo Playlist order is hacked into program count variable (not nice, but ok until 2.0)
207 return ByProgramCount(attributes, values);
208 }
209
ByGenre(SortAttribute attributes,const SortItem & values)210 std::string ByGenre(SortAttribute attributes, const SortItem &values)
211 {
212 return ArrayToString(attributes, values.at(FieldGenre));
213 }
214
ByCountry(SortAttribute attributes,const SortItem & values)215 std::string ByCountry(SortAttribute attributes, const SortItem &values)
216 {
217 return ArrayToString(attributes, values.at(FieldCountry));
218 }
219
ByYear(SortAttribute attributes,const SortItem & values)220 std::string ByYear(SortAttribute attributes, const SortItem &values)
221 {
222 std::string label;
223 const CVariant &airDate = values.at(FieldAirDate);
224 if (!airDate.isNull() && !airDate.asString().empty())
225 label = airDate.asString() + " ";
226
227 label += StringUtils::Format("%i", (int)values.at(FieldYear).asInteger());
228
229 const CVariant &album = values.at(FieldAlbum);
230 if (!album.isNull())
231 label += " " + SortUtils::RemoveArticles(album.asString());
232
233 const CVariant &track = values.at(FieldTrackNumber);
234 if (!track.isNull())
235 label += StringUtils::Format(" %i", (int)track.asInteger());
236
237 label += " " + ByLabel(attributes, values);
238
239 return label;
240 }
241
ByOrigDate(SortAttribute attributes,const SortItem & values)242 std::string ByOrigDate(SortAttribute attributes, const SortItem& values)
243 {
244 std::string label;
245 label = values.at(FieldOrigDate).asString();
246
247 const CVariant &album = values.at(FieldAlbum);
248 if (!album.isNull())
249 label += " " + SortUtils::RemoveArticles(album.asString());
250
251 const CVariant &track = values.at(FieldTrackNumber);
252 if (!track.isNull())
253 label += StringUtils::Format(" %i", static_cast<int>(track.asInteger()));
254
255 label += " " + ByLabel(attributes, values);
256
257 return label;
258 }
259
BySortTitle(SortAttribute attributes,const SortItem & values)260 std::string BySortTitle(SortAttribute attributes, const SortItem &values)
261 {
262 std::string title = values.at(FieldSortTitle).asString();
263 if (title.empty())
264 title = values.at(FieldTitle).asString();
265
266 if (attributes & SortAttributeIgnoreArticle)
267 title = SortUtils::RemoveArticles(title);
268
269 return title;
270 }
271
ByRating(SortAttribute attributes,const SortItem & values)272 std::string ByRating(SortAttribute attributes, const SortItem &values)
273 {
274 return StringUtils::Format("%f %s", values.at(FieldRating).asFloat(), ByLabel(attributes, values).c_str());
275 }
276
ByUserRating(SortAttribute attributes,const SortItem & values)277 std::string ByUserRating(SortAttribute attributes, const SortItem &values)
278 {
279 return StringUtils::Format("%d %s", static_cast<int>(values.at(FieldUserRating).asInteger()), ByLabel(attributes, values).c_str());
280 }
281
ByVotes(SortAttribute attributes,const SortItem & values)282 std::string ByVotes(SortAttribute attributes, const SortItem &values)
283 {
284 return StringUtils::Format("%d %s", (int)values.at(FieldVotes).asInteger(), ByLabel(attributes, values).c_str());
285 }
286
ByTop250(SortAttribute attributes,const SortItem & values)287 std::string ByTop250(SortAttribute attributes, const SortItem &values)
288 {
289 return StringUtils::Format("%d %s", (int)values.at(FieldTop250).asInteger(), ByLabel(attributes, values).c_str());
290 }
291
ByMPAA(SortAttribute attributes,const SortItem & values)292 std::string ByMPAA(SortAttribute attributes, const SortItem &values)
293 {
294 return values.at(FieldMPAA).asString() + " " + ByLabel(attributes, values);
295 }
296
ByStudio(SortAttribute attributes,const SortItem & values)297 std::string ByStudio(SortAttribute attributes, const SortItem &values)
298 {
299 return ArrayToString(attributes, values.at(FieldStudio));
300 }
301
ByEpisodeNumber(SortAttribute attributes,const SortItem & values)302 std::string ByEpisodeNumber(SortAttribute attributes, const SortItem &values)
303 {
304 // we calculate an offset number based on the episode's
305 // sort season and episode values. in addition
306 // we include specials 'episode' numbers to get proper
307 // sorting of multiple specials in a row. each
308 // of these are given their particular ranges to semi-ensure uniqueness.
309 // theoretical problem: if a show has > 2^15 specials and two of these are placed
310 // after each other they will sort backwards. if a show has > 2^32-1 seasons
311 // or if a season has > 2^16-1 episodes strange things will happen (overflow)
312 uint64_t num;
313 const CVariant &episodeSpecial = values.at(FieldEpisodeNumberSpecialSort);
314 const CVariant &seasonSpecial = values.at(FieldSeasonSpecialSort);
315 if (!episodeSpecial.isNull() && !seasonSpecial.isNull() &&
316 (episodeSpecial.asInteger() > 0 || seasonSpecial.asInteger() > 0))
317 num = ((uint64_t)seasonSpecial.asInteger() << 32) + (episodeSpecial.asInteger() << 16) - ((2 << 15) - values.at(FieldEpisodeNumber).asInteger());
318 else
319 num = ((uint64_t)values.at(FieldSeason).asInteger() << 32) + (values.at(FieldEpisodeNumber).asInteger() << 16);
320
321 std::string title;
322 if (values.find(FieldMediaType) != values.end() && values.at(FieldMediaType).asString() == MediaTypeMovie)
323 title = BySortTitle(attributes, values);
324 if (title.empty())
325 title = ByLabel(attributes, values);
326
327 return StringUtils::Format("%" PRIu64" %s", num, title.c_str());
328 }
329
BySeason(SortAttribute attributes,const SortItem & values)330 std::string BySeason(SortAttribute attributes, const SortItem &values)
331 {
332 int season = (int)values.at(FieldSeason).asInteger();
333 const CVariant &specialSeason = values.at(FieldSeasonSpecialSort);
334 if (!specialSeason.isNull())
335 season = (int)specialSeason.asInteger();
336
337 return StringUtils::Format("%i %s", season, ByLabel(attributes, values).c_str());
338 }
339
ByNumberOfEpisodes(SortAttribute attributes,const SortItem & values)340 std::string ByNumberOfEpisodes(SortAttribute attributes, const SortItem &values)
341 {
342 return StringUtils::Format("%i %s", (int)values.at(FieldNumberOfEpisodes).asInteger(), ByLabel(attributes, values).c_str());
343 }
344
ByNumberOfWatchedEpisodes(SortAttribute attributes,const SortItem & values)345 std::string ByNumberOfWatchedEpisodes(SortAttribute attributes, const SortItem &values)
346 {
347 return StringUtils::Format("%i %s", (int)values.at(FieldNumberOfWatchedEpisodes).asInteger(), ByLabel(attributes, values).c_str());
348 }
349
ByTvShowStatus(SortAttribute attributes,const SortItem & values)350 std::string ByTvShowStatus(SortAttribute attributes, const SortItem &values)
351 {
352 return values.at(FieldTvShowStatus).asString() + " " + ByLabel(attributes, values);
353 }
354
ByTvShowTitle(SortAttribute attributes,const SortItem & values)355 std::string ByTvShowTitle(SortAttribute attributes, const SortItem &values)
356 {
357 return values.at(FieldTvShowTitle).asString() + " " + ByLabel(attributes, values);
358 }
359
ByProductionCode(SortAttribute attributes,const SortItem & values)360 std::string ByProductionCode(SortAttribute attributes, const SortItem &values)
361 {
362 return values.at(FieldProductionCode).asString();
363 }
364
ByVideoResolution(SortAttribute attributes,const SortItem & values)365 std::string ByVideoResolution(SortAttribute attributes, const SortItem &values)
366 {
367 return StringUtils::Format("%i %s", (int)values.at(FieldVideoResolution).asInteger(), ByLabel(attributes, values).c_str());
368 }
369
ByVideoCodec(SortAttribute attributes,const SortItem & values)370 std::string ByVideoCodec(SortAttribute attributes, const SortItem &values)
371 {
372 return StringUtils::Format("%s %s", values.at(FieldVideoCodec).asString().c_str(), ByLabel(attributes, values).c_str());
373 }
374
ByVideoAspectRatio(SortAttribute attributes,const SortItem & values)375 std::string ByVideoAspectRatio(SortAttribute attributes, const SortItem &values)
376 {
377 return StringUtils::Format("%.3f %s", values.at(FieldVideoAspectRatio).asFloat(), ByLabel(attributes, values).c_str());
378 }
379
ByAudioChannels(SortAttribute attributes,const SortItem & values)380 std::string ByAudioChannels(SortAttribute attributes, const SortItem &values)
381 {
382 return StringUtils::Format("%i %s", (int)values.at(FieldAudioChannels).asInteger(), ByLabel(attributes, values).c_str());
383 }
384
ByAudioCodec(SortAttribute attributes,const SortItem & values)385 std::string ByAudioCodec(SortAttribute attributes, const SortItem &values)
386 {
387 return StringUtils::Format("%s %s", values.at(FieldAudioCodec).asString().c_str(), ByLabel(attributes, values).c_str());
388 }
389
ByAudioLanguage(SortAttribute attributes,const SortItem & values)390 std::string ByAudioLanguage(SortAttribute attributes, const SortItem &values)
391 {
392 return StringUtils::Format("%s %s", values.at(FieldAudioLanguage).asString().c_str(), ByLabel(attributes, values).c_str());
393 }
394
BySubtitleLanguage(SortAttribute attributes,const SortItem & values)395 std::string BySubtitleLanguage(SortAttribute attributes, const SortItem &values)
396 {
397 return StringUtils::Format("%s %s", values.at(FieldSubtitleLanguage).asString().c_str(), ByLabel(attributes, values).c_str());
398 }
399
ByBitrate(SortAttribute attributes,const SortItem & values)400 std::string ByBitrate(SortAttribute attributes, const SortItem &values)
401 {
402 return StringUtils::Format("%" PRId64, values.at(FieldBitrate).asInteger());
403 }
404
ByListeners(SortAttribute attributes,const SortItem & values)405 std::string ByListeners(SortAttribute attributes, const SortItem &values)
406 {
407 return StringUtils::Format("%" PRId64, values.at(FieldListeners).asInteger());
408 }
409
ByRandom(SortAttribute attributes,const SortItem & values)410 std::string ByRandom(SortAttribute attributes, const SortItem &values)
411 {
412 return StringUtils::Format("%i", CUtil::GetRandomNumber());
413 }
414
ByChannel(SortAttribute attributes,const SortItem & values)415 std::string ByChannel(SortAttribute attributes, const SortItem &values)
416 {
417 return values.at(FieldChannelName).asString();
418 }
419
ByChannelNumber(SortAttribute attributes,const SortItem & values)420 std::string ByChannelNumber(SortAttribute attributes, const SortItem &values)
421 {
422 return values.at(FieldChannelNumber).asString();
423 }
424
ByClientChannelOrder(SortAttribute attributes,const SortItem & values)425 std::string ByClientChannelOrder(SortAttribute attributes, const SortItem& values)
426 {
427 return values.at(FieldClientChannelOrder).asString();
428 }
429
ByDateTaken(SortAttribute attributes,const SortItem & values)430 std::string ByDateTaken(SortAttribute attributes, const SortItem &values)
431 {
432 return values.at(FieldDateTaken).asString();
433 }
434
ByRelevance(SortAttribute attributes,const SortItem & values)435 std::string ByRelevance(SortAttribute attributes, const SortItem &values)
436 {
437 return StringUtils::Format("%i", (int)values.at(FieldRelevance).asInteger());
438 }
439
ByInstallDate(SortAttribute attributes,const SortItem & values)440 std::string ByInstallDate(SortAttribute attributes, const SortItem &values)
441 {
442 return values.at(FieldInstallDate).asString();
443 }
444
ByLastUpdated(SortAttribute attributes,const SortItem & values)445 std::string ByLastUpdated(SortAttribute attributes, const SortItem &values)
446 {
447 return values.at(FieldLastUpdated).asString();
448 }
449
ByLastUsed(SortAttribute attributes,const SortItem & values)450 std::string ByLastUsed(SortAttribute attributes, const SortItem &values)
451 {
452 return values.at(FieldLastUsed).asString();
453 }
454
ByBPM(SortAttribute attributes,const SortItem & values)455 std::string ByBPM(SortAttribute attributes, const SortItem& values)
456 {
457 return StringUtils::Format("%d %s", static_cast<int>(values.at(FieldBPM).asInteger()),
458 ByLabel(attributes, values));
459 }
460
preliminarySort(const SortItem & left,const SortItem & right,bool handleFolder,bool & result,std::wstring & labelLeft,std::wstring & labelRight)461 bool preliminarySort(const SortItem &left, const SortItem &right, bool handleFolder, bool &result, std::wstring &labelLeft, std::wstring &labelRight)
462 {
463 // make sure both items have the necessary data to do the sorting
464 SortItem::const_iterator itLeftSort, itRightSort;
465 if ((itLeftSort = left.find(FieldSort)) == left.end())
466 {
467 result = false;
468 return true;
469 }
470 if ((itRightSort = right.find(FieldSort)) == right.end())
471 {
472 result = true;
473 return true;
474 }
475
476 // look at special sorting behaviour
477 SortItem::const_iterator itLeft, itRight;
478 SortSpecial leftSortSpecial = SortSpecialNone;
479 SortSpecial rightSortSpecial = SortSpecialNone;
480 if ((itLeft = left.find(FieldSortSpecial)) != left.end() && itLeft->second.asInteger() <= (int64_t)SortSpecialOnBottom)
481 leftSortSpecial = (SortSpecial)itLeft->second.asInteger();
482 if ((itRight = right.find(FieldSortSpecial)) != right.end() && itRight->second.asInteger() <= (int64_t)SortSpecialOnBottom)
483 rightSortSpecial = (SortSpecial)itRight->second.asInteger();
484
485 // one has a special sort
486 if (leftSortSpecial != rightSortSpecial)
487 {
488 // left should be sorted on top
489 // or right should be sorted on bottom
490 // => left is sorted above right
491 if (leftSortSpecial == SortSpecialOnTop ||
492 rightSortSpecial == SortSpecialOnBottom)
493 {
494 result = true;
495 return true;
496 }
497
498 // otherwise right is sorted above left
499 result = false;
500 return true;
501 }
502 // both have either sort on top or sort on bottom -> leave as-is
503 else if (leftSortSpecial != SortSpecialNone && leftSortSpecial == rightSortSpecial)
504 {
505 result = false;
506 return true;
507 }
508
509 if (handleFolder)
510 {
511 itLeft = left.find(FieldFolder);
512 itRight = right.find(FieldFolder);
513 if (itLeft != left.end() && itRight != right.end() &&
514 itLeft->second.asBoolean() != itRight->second.asBoolean())
515 {
516 result = itLeft->second.asBoolean();
517 return true;
518 }
519 }
520
521 labelLeft = itLeftSort->second.asWideString();
522 labelRight = itRightSort->second.asWideString();
523
524 return false;
525 }
526
SorterAscending(const SortItem & left,const SortItem & right)527 bool SorterAscending(const SortItem &left, const SortItem &right)
528 {
529 bool result;
530 std::wstring labelLeft, labelRight;
531 if (preliminarySort(left, right, true, result, labelLeft, labelRight))
532 return result;
533
534 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
535 }
536
SorterDescending(const SortItem & left,const SortItem & right)537 bool SorterDescending(const SortItem &left, const SortItem &right)
538 {
539 bool result;
540 std::wstring labelLeft, labelRight;
541 if (preliminarySort(left, right, true, result, labelLeft, labelRight))
542 return result;
543
544 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
545 }
546
SorterIgnoreFoldersAscending(const SortItem & left,const SortItem & right)547 bool SorterIgnoreFoldersAscending(const SortItem &left, const SortItem &right)
548 {
549 bool result;
550 std::wstring labelLeft, labelRight;
551 if (preliminarySort(left, right, false, result, labelLeft, labelRight))
552 return result;
553
554 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
555 }
556
SorterIgnoreFoldersDescending(const SortItem & left,const SortItem & right)557 bool SorterIgnoreFoldersDescending(const SortItem &left, const SortItem &right)
558 {
559 bool result;
560 std::wstring labelLeft, labelRight;
561 if (preliminarySort(left, right, false, result, labelLeft, labelRight))
562 return result;
563
564 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
565 }
566
SorterIndirectAscending(const SortItemPtr & left,const SortItemPtr & right)567 bool SorterIndirectAscending(const SortItemPtr &left, const SortItemPtr &right)
568 {
569 return SorterAscending(*left, *right);
570 }
571
SorterIndirectDescending(const SortItemPtr & left,const SortItemPtr & right)572 bool SorterIndirectDescending(const SortItemPtr &left, const SortItemPtr &right)
573 {
574 return SorterDescending(*left, *right);
575 }
576
SorterIndirectIgnoreFoldersAscending(const SortItemPtr & left,const SortItemPtr & right)577 bool SorterIndirectIgnoreFoldersAscending(const SortItemPtr &left, const SortItemPtr &right)
578 {
579 return SorterIgnoreFoldersAscending(*left, *right);
580 }
581
SorterIndirectIgnoreFoldersDescending(const SortItemPtr & left,const SortItemPtr & right)582 bool SorterIndirectIgnoreFoldersDescending(const SortItemPtr &left, const SortItemPtr &right)
583 {
584 return SorterIgnoreFoldersDescending(*left, *right);
585 }
586
587 // clang-format off
fillPreparators()588 std::map<SortBy, SortUtils::SortPreparator> fillPreparators()
589 {
590 std::map<SortBy, SortUtils::SortPreparator> preparators;
591
592 preparators[SortByNone] = NULL;
593 preparators[SortByLabel] = ByLabel;
594 preparators[SortByDate] = ByDate;
595 preparators[SortBySize] = BySize;
596 preparators[SortByFile] = ByFile;
597 preparators[SortByPath] = ByPath;
598 preparators[SortByDriveType] = ByDriveType;
599 preparators[SortByTitle] = ByTitle;
600 preparators[SortByTrackNumber] = ByTrackNumber;
601 preparators[SortByTime] = ByTime;
602 preparators[SortByArtist] = ByArtist;
603 preparators[SortByArtistThenYear] = ByArtistThenYear;
604 preparators[SortByAlbum] = ByAlbum;
605 preparators[SortByAlbumType] = ByAlbumType;
606 preparators[SortByGenre] = ByGenre;
607 preparators[SortByCountry] = ByCountry;
608 preparators[SortByYear] = ByYear;
609 preparators[SortByRating] = ByRating;
610 preparators[SortByUserRating] = ByUserRating;
611 preparators[SortByVotes] = ByVotes;
612 preparators[SortByTop250] = ByTop250;
613 preparators[SortByProgramCount] = ByProgramCount;
614 preparators[SortByPlaylistOrder] = ByPlaylistOrder;
615 preparators[SortByEpisodeNumber] = ByEpisodeNumber;
616 preparators[SortBySeason] = BySeason;
617 preparators[SortByNumberOfEpisodes] = ByNumberOfEpisodes;
618 preparators[SortByNumberOfWatchedEpisodes] = ByNumberOfWatchedEpisodes;
619 preparators[SortByTvShowStatus] = ByTvShowStatus;
620 preparators[SortByTvShowTitle] = ByTvShowTitle;
621 preparators[SortBySortTitle] = BySortTitle;
622 preparators[SortByProductionCode] = ByProductionCode;
623 preparators[SortByMPAA] = ByMPAA;
624 preparators[SortByVideoResolution] = ByVideoResolution;
625 preparators[SortByVideoCodec] = ByVideoCodec;
626 preparators[SortByVideoAspectRatio] = ByVideoAspectRatio;
627 preparators[SortByAudioChannels] = ByAudioChannels;
628 preparators[SortByAudioCodec] = ByAudioCodec;
629 preparators[SortByAudioLanguage] = ByAudioLanguage;
630 preparators[SortBySubtitleLanguage] = BySubtitleLanguage;
631 preparators[SortByStudio] = ByStudio;
632 preparators[SortByDateAdded] = ByDateAdded;
633 preparators[SortByLastPlayed] = ByLastPlayed;
634 preparators[SortByPlaycount] = ByPlaycount;
635 preparators[SortByListeners] = ByListeners;
636 preparators[SortByBitrate] = ByBitrate;
637 preparators[SortByRandom] = ByRandom;
638 preparators[SortByChannel] = ByChannel;
639 preparators[SortByChannelNumber] = ByChannelNumber;
640 preparators[SortByClientChannelOrder] = ByClientChannelOrder;
641 preparators[SortByDateTaken] = ByDateTaken;
642 preparators[SortByRelevance] = ByRelevance;
643 preparators[SortByInstallDate] = ByInstallDate;
644 preparators[SortByLastUpdated] = ByLastUpdated;
645 preparators[SortByLastUsed] = ByLastUsed;
646 preparators[SortByTotalDiscs] = ByTotalDiscs;
647 preparators[SortByOrigDate] = ByOrigDate;
648 preparators[SortByBPM] = ByBPM;
649
650 return preparators;
651 }
652 // clang-format on
653
fillSortingFields()654 std::map<SortBy, Fields> fillSortingFields()
655 {
656 std::map<SortBy, Fields> sortingFields;
657
658 sortingFields.insert(std::pair<SortBy, Fields>(SortByNone, Fields()));
659
660 sortingFields[SortByLabel].insert(FieldLabel);
661 sortingFields[SortByDate].insert(FieldDate);
662 sortingFields[SortBySize].insert(FieldSize);
663 sortingFields[SortByFile].insert(FieldPath);
664 sortingFields[SortByFile].insert(FieldStartOffset);
665 sortingFields[SortByPath].insert(FieldPath);
666 sortingFields[SortByPath].insert(FieldStartOffset);
667 sortingFields[SortByDriveType].insert(FieldDriveType);
668 sortingFields[SortByTitle].insert(FieldTitle);
669 sortingFields[SortByTrackNumber].insert(FieldTrackNumber);
670 sortingFields[SortByTime].insert(FieldTime);
671 sortingFields[SortByArtist].insert(FieldArtist);
672 sortingFields[SortByArtist].insert(FieldArtistSort);
673 sortingFields[SortByArtist].insert(FieldYear);
674 sortingFields[SortByArtist].insert(FieldAlbum);
675 sortingFields[SortByArtist].insert(FieldTrackNumber);
676 sortingFields[SortByArtistThenYear].insert(FieldArtist);
677 sortingFields[SortByArtistThenYear].insert(FieldArtistSort);
678 sortingFields[SortByArtistThenYear].insert(FieldYear);
679 sortingFields[SortByArtistThenYear].insert(FieldOrigDate);
680 sortingFields[SortByArtistThenYear].insert(FieldAlbum);
681 sortingFields[SortByArtistThenYear].insert(FieldTrackNumber);
682 sortingFields[SortByAlbum].insert(FieldAlbum);
683 sortingFields[SortByAlbum].insert(FieldArtist);
684 sortingFields[SortByAlbum].insert(FieldArtistSort);
685 sortingFields[SortByAlbum].insert(FieldTrackNumber);
686 sortingFields[SortByAlbumType].insert(FieldAlbumType);
687 sortingFields[SortByGenre].insert(FieldGenre);
688 sortingFields[SortByCountry].insert(FieldCountry);
689 sortingFields[SortByYear].insert(FieldYear);
690 sortingFields[SortByYear].insert(FieldAirDate);
691 sortingFields[SortByYear].insert(FieldAlbum);
692 sortingFields[SortByYear].insert(FieldTrackNumber);
693 sortingFields[SortByYear].insert(FieldOrigDate);
694 sortingFields[SortByRating].insert(FieldRating);
695 sortingFields[SortByUserRating].insert(FieldUserRating);
696 sortingFields[SortByVotes].insert(FieldVotes);
697 sortingFields[SortByTop250].insert(FieldTop250);
698 sortingFields[SortByProgramCount].insert(FieldProgramCount);
699 sortingFields[SortByPlaylistOrder].insert(FieldProgramCount);
700 sortingFields[SortByEpisodeNumber].insert(FieldEpisodeNumber);
701 sortingFields[SortByEpisodeNumber].insert(FieldSeason);
702 sortingFields[SortByEpisodeNumber].insert(FieldEpisodeNumberSpecialSort);
703 sortingFields[SortByEpisodeNumber].insert(FieldSeasonSpecialSort);
704 sortingFields[SortByEpisodeNumber].insert(FieldTitle);
705 sortingFields[SortByEpisodeNumber].insert(FieldSortTitle);
706 sortingFields[SortBySeason].insert(FieldSeason);
707 sortingFields[SortBySeason].insert(FieldSeasonSpecialSort);
708 sortingFields[SortByNumberOfEpisodes].insert(FieldNumberOfEpisodes);
709 sortingFields[SortByNumberOfWatchedEpisodes].insert(FieldNumberOfWatchedEpisodes);
710 sortingFields[SortByTvShowStatus].insert(FieldTvShowStatus);
711 sortingFields[SortByTvShowTitle].insert(FieldTvShowTitle);
712 sortingFields[SortBySortTitle].insert(FieldSortTitle);
713 sortingFields[SortBySortTitle].insert(FieldTitle);
714 sortingFields[SortByProductionCode].insert(FieldProductionCode);
715 sortingFields[SortByMPAA].insert(FieldMPAA);
716 sortingFields[SortByVideoResolution].insert(FieldVideoResolution);
717 sortingFields[SortByVideoCodec].insert(FieldVideoCodec);
718 sortingFields[SortByVideoAspectRatio].insert(FieldVideoAspectRatio);
719 sortingFields[SortByAudioChannels].insert(FieldAudioChannels);
720 sortingFields[SortByAudioCodec].insert(FieldAudioCodec);
721 sortingFields[SortByAudioLanguage].insert(FieldAudioLanguage);
722 sortingFields[SortBySubtitleLanguage].insert(FieldSubtitleLanguage);
723 sortingFields[SortByStudio].insert(FieldStudio);
724 sortingFields[SortByDateAdded].insert(FieldDateAdded);
725 sortingFields[SortByDateAdded].insert(FieldId);
726 sortingFields[SortByLastPlayed].insert(FieldLastPlayed);
727 sortingFields[SortByPlaycount].insert(FieldPlaycount);
728 sortingFields[SortByListeners].insert(FieldListeners);
729 sortingFields[SortByBitrate].insert(FieldBitrate);
730 sortingFields[SortByChannel].insert(FieldChannelName);
731 sortingFields[SortByChannelNumber].insert(FieldChannelNumber);
732 sortingFields[SortByClientChannelOrder].insert(FieldClientChannelOrder);
733 sortingFields[SortByDateTaken].insert(FieldDateTaken);
734 sortingFields[SortByRelevance].insert(FieldRelevance);
735 sortingFields[SortByInstallDate].insert(FieldInstallDate);
736 sortingFields[SortByLastUpdated].insert(FieldLastUpdated);
737 sortingFields[SortByLastUsed].insert(FieldLastUsed);
738 sortingFields[SortByTotalDiscs].insert(FieldTotalDiscs);
739 sortingFields[SortByOrigDate].insert(FieldOrigDate);
740 sortingFields[SortByOrigDate].insert(FieldAlbum);
741 sortingFields[SortByOrigDate].insert(FieldTrackNumber);
742 sortingFields[SortByBPM].insert(FieldBPM);
743 sortingFields.insert(std::pair<SortBy, Fields>(SortByRandom, Fields()));
744
745 return sortingFields;
746 }
747
748 std::map<SortBy, SortUtils::SortPreparator> SortUtils::m_preparators = fillPreparators();
749 std::map<SortBy, Fields> SortUtils::m_sortingFields = fillSortingFields();
750
GetFieldsForSQLSort(const MediaType & mediaType,SortBy sortMethod,FieldList & fields)751 void SortUtils::GetFieldsForSQLSort(const MediaType& mediaType,
752 SortBy sortMethod,
753 FieldList& fields)
754 {
755 fields.clear();
756 if (mediaType == MediaTypeNone)
757 return;
758
759 if (mediaType == MediaTypeAlbum)
760 {
761 if (sortMethod == SortByLabel || sortMethod == SortByAlbum || sortMethod == SortByTitle)
762 {
763 fields.emplace_back(FieldAlbum);
764 fields.emplace_back(FieldArtist);
765 }
766 else if (sortMethod == SortByAlbumType)
767 {
768 fields.emplace_back(FieldAlbumType);
769 fields.emplace_back(FieldAlbum);
770 fields.emplace_back(FieldArtist);
771 }
772 else if (sortMethod == SortByArtist)
773 {
774 fields.emplace_back(FieldArtist);
775 fields.emplace_back(FieldAlbum);
776 }
777 else if (sortMethod == SortByArtistThenYear)
778 {
779 fields.emplace_back(FieldArtist);
780 fields.emplace_back(FieldYear);
781 fields.emplace_back(FieldAlbum);
782 }
783 else if (sortMethod == SortByYear)
784 {
785 fields.emplace_back(FieldYear);
786 fields.emplace_back(FieldAlbum);
787 }
788 else if (sortMethod == SortByGenre)
789 {
790 fields.emplace_back(FieldGenre);
791 fields.emplace_back(FieldAlbum);
792 }
793 else if (sortMethod == SortByDateAdded)
794 fields.emplace_back(FieldDateAdded);
795 else if (sortMethod == SortByPlaycount)
796 {
797 fields.emplace_back(FieldPlaycount);
798 fields.emplace_back(FieldAlbum);
799 }
800 else if (sortMethod == SortByLastPlayed)
801 {
802 fields.emplace_back(FieldLastPlayed);
803 fields.emplace_back(FieldAlbum);
804 }
805 else if (sortMethod == SortByRating)
806 {
807 fields.emplace_back(FieldRating);
808 fields.emplace_back(FieldAlbum);
809 }
810 else if (sortMethod == SortByVotes)
811 {
812 fields.emplace_back(FieldVotes);
813 fields.emplace_back(FieldAlbum);
814 }
815 else if (sortMethod == SortByUserRating)
816 {
817 fields.emplace_back(FieldUserRating);
818 fields.emplace_back(FieldAlbum);
819 }
820 else if (sortMethod == SortByTotalDiscs)
821 {
822 fields.emplace_back(FieldTotalDiscs);
823 fields.emplace_back(FieldAlbum);
824 }
825 else if (sortMethod == SortByOrigDate)
826 {
827 fields.emplace_back(FieldOrigDate);
828 fields.emplace_back(FieldAlbum);
829 }
830 }
831 else if (mediaType == MediaTypeSong)
832 {
833 if (sortMethod == SortByLabel || sortMethod == SortByTrackNumber)
834 fields.emplace_back(FieldTrackNumber);
835 else if (sortMethod == SortByTitle)
836 fields.emplace_back(FieldTitle);
837 else if (sortMethod == SortByAlbum)
838 {
839 fields.emplace_back(FieldAlbum);
840 fields.emplace_back(FieldAlbumArtist);
841 fields.emplace_back(FieldTrackNumber);
842 }
843 else if (sortMethod == SortByArtist)
844 {
845 fields.emplace_back(FieldArtist);
846 fields.emplace_back(FieldAlbum);
847 fields.emplace_back(FieldTrackNumber);
848 }
849 else if (sortMethod == SortByArtistThenYear)
850 {
851 fields.emplace_back(FieldArtist);
852 fields.emplace_back(FieldYear);
853 fields.emplace_back(FieldAlbum);
854 fields.emplace_back(FieldTrackNumber);
855 }
856 else if (sortMethod == SortByYear)
857 {
858 fields.emplace_back(FieldYear);
859 fields.emplace_back(FieldAlbum);
860 fields.emplace_back(FieldTrackNumber);
861 }
862 else if (sortMethod == SortByGenre)
863 {
864 fields.emplace_back(FieldGenre);
865 fields.emplace_back(FieldAlbum);
866 }
867 else if (sortMethod == SortByDateAdded)
868 fields.emplace_back(FieldDateAdded);
869 else if (sortMethod == SortByPlaycount)
870 {
871 fields.emplace_back(FieldPlaycount);
872 fields.emplace_back(FieldTrackNumber);
873 }
874 else if (sortMethod == SortByLastPlayed)
875 {
876 fields.emplace_back(FieldLastPlayed);
877 fields.emplace_back(FieldTrackNumber);
878 }
879 else if (sortMethod == SortByRating)
880 {
881 fields.emplace_back(FieldRating);
882 fields.emplace_back(FieldTrackNumber);
883 }
884 else if (sortMethod == SortByVotes)
885 {
886 fields.emplace_back(FieldVotes);
887 fields.emplace_back(FieldTrackNumber);
888 }
889 else if (sortMethod == SortByUserRating)
890 {
891 fields.emplace_back(FieldUserRating);
892 fields.emplace_back(FieldTrackNumber);
893 }
894 else if (sortMethod == SortByFile)
895 {
896 fields.emplace_back(FieldPath);
897 fields.emplace_back(FieldFilename);
898 fields.emplace_back(FieldStartOffset);
899 }
900 else if (sortMethod == SortByTime)
901 fields.emplace_back(FieldTime);
902 else if (sortMethod == SortByAlbumType)
903 {
904 fields.emplace_back(FieldAlbumType);
905 fields.emplace_back(FieldAlbum);
906 fields.emplace_back(FieldTrackNumber);
907 }
908 else if (sortMethod == SortByOrigDate)
909 {
910 fields.emplace_back(FieldOrigDate);
911 fields.emplace_back(FieldAlbum);
912 fields.emplace_back(FieldTrackNumber);
913 }
914 else if (sortMethod == SortByBPM)
915 fields.emplace_back(FieldBPM);
916 }
917 else if (mediaType == MediaTypeArtist)
918 {
919 if (sortMethod == SortByLabel || sortMethod == SortByTitle || sortMethod == SortByArtist)
920 fields.emplace_back(FieldArtist);
921 else if (sortMethod == SortByGenre)
922 fields.emplace_back(FieldGenre);
923 else if (sortMethod == SortByDateAdded)
924 fields.emplace_back(FieldDateAdded);
925 }
926
927 // Add sort by id to define order when other fields same or sort none
928 fields.emplace_back(FieldId);
929 return;
930 }
931
932
Sort(SortBy sortBy,SortOrder sortOrder,SortAttribute attributes,DatabaseResults & items,int limitEnd,int limitStart)933 void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, DatabaseResults& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
934 {
935 if (sortBy != SortByNone)
936 {
937 // get the matching SortPreparator
938 SortPreparator preparator = getPreparator(sortBy);
939 if (preparator != NULL)
940 {
941 Fields sortingFields = GetFieldsForSorting(sortBy);
942
943 // Prepare the string used for sorting and store it under FieldSort
944 for (DatabaseResults::iterator item = items.begin(); item != items.end(); ++item)
945 {
946 // add all fields to the item that are required for sorting if they are currently missing
947 for (Fields::const_iterator field = sortingFields.begin(); field != sortingFields.end(); ++field)
948 {
949 if (item->find(*field) == item->end())
950 item->insert(std::pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
951 }
952
953 std::wstring sortLabel;
954 g_charsetConverter.utf8ToW(preparator(attributes, *item), sortLabel, false);
955 item->insert(std::pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
956 }
957
958 // Do the sorting
959 std::stable_sort(items.begin(), items.end(), getSorter(sortOrder, attributes));
960 }
961 }
962
963 if (limitStart > 0 && (size_t)limitStart < items.size())
964 {
965 items.erase(items.begin(), items.begin() + limitStart);
966 limitEnd -= limitStart;
967 }
968 if (limitEnd > 0 && (size_t)limitEnd < items.size())
969 items.erase(items.begin() + limitEnd, items.end());
970 }
971
Sort(SortBy sortBy,SortOrder sortOrder,SortAttribute attributes,SortItems & items,int limitEnd,int limitStart)972 void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, SortItems& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
973 {
974 if (sortBy != SortByNone)
975 {
976 // get the matching SortPreparator
977 SortPreparator preparator = getPreparator(sortBy);
978 if (preparator != NULL)
979 {
980 Fields sortingFields = GetFieldsForSorting(sortBy);
981
982 // Prepare the string used for sorting and store it under FieldSort
983 for (SortItems::iterator item = items.begin(); item != items.end(); ++item)
984 {
985 // add all fields to the item that are required for sorting if they are currently missing
986 for (Fields::const_iterator field = sortingFields.begin(); field != sortingFields.end(); ++field)
987 {
988 if ((*item)->find(*field) == (*item)->end())
989 (*item)->insert(std::pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
990 }
991
992 std::wstring sortLabel;
993 g_charsetConverter.utf8ToW(preparator(attributes, **item), sortLabel, false);
994 (*item)->insert(std::pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
995 }
996
997 // Do the sorting
998 std::stable_sort(items.begin(), items.end(), getSorterIndirect(sortOrder, attributes));
999 }
1000 }
1001
1002 if (limitStart > 0 && (size_t)limitStart < items.size())
1003 {
1004 items.erase(items.begin(), items.begin() + limitStart);
1005 limitEnd -= limitStart;
1006 }
1007 if (limitEnd > 0 && (size_t)limitEnd < items.size())
1008 items.erase(items.begin() + limitEnd, items.end());
1009 }
1010
Sort(const SortDescription & sortDescription,DatabaseResults & items)1011 void SortUtils::Sort(const SortDescription &sortDescription, DatabaseResults& items)
1012 {
1013 Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
1014 }
1015
Sort(const SortDescription & sortDescription,SortItems & items)1016 void SortUtils::Sort(const SortDescription &sortDescription, SortItems& items)
1017 {
1018 Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
1019 }
1020
SortFromDataset(const SortDescription & sortDescription,const MediaType & mediaType,const std::unique_ptr<dbiplus::Dataset> & dataset,DatabaseResults & results)1021 bool SortUtils::SortFromDataset(const SortDescription &sortDescription, const MediaType &mediaType, const std::unique_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results)
1022 {
1023 FieldList fields;
1024 if (!DatabaseUtils::GetSelectFields(SortUtils::GetFieldsForSorting(sortDescription.sortBy), mediaType, fields))
1025 fields.clear();
1026
1027 if (!DatabaseUtils::GetDatabaseResults(mediaType, fields, dataset, results))
1028 return false;
1029
1030 SortDescription sorting = sortDescription;
1031 if (sortDescription.sortBy == SortByNone)
1032 {
1033 sorting.limitStart = 0;
1034 sorting.limitEnd = -1;
1035 }
1036
1037 Sort(sorting, results);
1038
1039 return true;
1040 }
1041
getPreparator(SortBy sortBy)1042 const SortUtils::SortPreparator& SortUtils::getPreparator(SortBy sortBy)
1043 {
1044 std::map<SortBy, SortPreparator>::const_iterator it = m_preparators.find(sortBy);
1045 if (it != m_preparators.end())
1046 return it->second;
1047
1048 return m_preparators[SortByNone];
1049 }
1050
getSorter(SortOrder sortOrder,SortAttribute attributes)1051 SortUtils::Sorter SortUtils::getSorter(SortOrder sortOrder, SortAttribute attributes)
1052 {
1053 if (attributes & SortAttributeIgnoreFolders)
1054 return sortOrder == SortOrderDescending ? SorterIgnoreFoldersDescending : SorterIgnoreFoldersAscending;
1055
1056 return sortOrder == SortOrderDescending ? SorterDescending : SorterAscending;
1057 }
1058
getSorterIndirect(SortOrder sortOrder,SortAttribute attributes)1059 SortUtils::SorterIndirect SortUtils::getSorterIndirect(SortOrder sortOrder, SortAttribute attributes)
1060 {
1061 if (attributes & SortAttributeIgnoreFolders)
1062 return sortOrder == SortOrderDescending ? SorterIndirectIgnoreFoldersDescending : SorterIndirectIgnoreFoldersAscending;
1063
1064 return sortOrder == SortOrderDescending ? SorterIndirectDescending : SorterIndirectAscending;
1065 }
1066
GetFieldsForSorting(SortBy sortBy)1067 const Fields& SortUtils::GetFieldsForSorting(SortBy sortBy)
1068 {
1069 std::map<SortBy, Fields>::const_iterator it = m_sortingFields.find(sortBy);
1070 if (it != m_sortingFields.end())
1071 return it->second;
1072
1073 return m_sortingFields[SortByNone];
1074 }
1075
RemoveArticles(const std::string & label)1076 std::string SortUtils::RemoveArticles(const std::string &label)
1077 {
1078 std::set<std::string> sortTokens = g_langInfo.GetSortTokens();
1079 for (std::set<std::string>::const_iterator token = sortTokens.begin(); token != sortTokens.end(); ++token)
1080 {
1081 if (token->size() < label.size() && StringUtils::StartsWithNoCase(label, *token))
1082 return label.substr(token->size());
1083 }
1084
1085 return label;
1086 }
1087
1088 typedef struct
1089 {
1090 SortBy sort;
1091 SORT_METHOD old;
1092 SortAttribute flags;
1093 int label;
1094 } sort_map;
1095
1096 // clang-format off
1097 const sort_map table[] = {
1098 { SortByLabel, SORT_METHOD_LABEL, SortAttributeNone, 551 },
1099 { SortByLabel, SORT_METHOD_LABEL_IGNORE_THE, SortAttributeIgnoreArticle, 551 },
1100 { SortByLabel, SORT_METHOD_LABEL_IGNORE_FOLDERS, SortAttributeIgnoreFolders, 551 },
1101 { SortByDate, SORT_METHOD_DATE, SortAttributeNone, 552 },
1102 { SortBySize, SORT_METHOD_SIZE, SortAttributeNone, 553 },
1103 { SortByBitrate, SORT_METHOD_BITRATE, SortAttributeNone, 623 },
1104 { SortByDriveType, SORT_METHOD_DRIVE_TYPE, SortAttributeNone, 564 },
1105 { SortByTrackNumber, SORT_METHOD_TRACKNUM, SortAttributeNone, 554 },
1106 { SortByEpisodeNumber, SORT_METHOD_EPISODE, SortAttributeNone, 20359 },// 20360 "Episodes" used for SORT_METHOD_EPISODE for sorting tvshows by episode count
1107 { SortByTime, SORT_METHOD_DURATION, SortAttributeNone, 180 },
1108 { SortByTime, SORT_METHOD_VIDEO_RUNTIME, SortAttributeNone, 180 },
1109 { SortByTitle, SORT_METHOD_TITLE, SortAttributeNone, 556 },
1110 { SortByTitle, SORT_METHOD_TITLE_IGNORE_THE, SortAttributeIgnoreArticle, 556 },
1111 { SortByTitle, SORT_METHOD_VIDEO_TITLE, SortAttributeNone, 556 },
1112 { SortByArtist, SORT_METHOD_ARTIST, SortAttributeNone, 557 },
1113 { SortByArtistThenYear, SORT_METHOD_ARTIST_AND_YEAR, SortAttributeNone, 578 },
1114 { SortByArtist, SORT_METHOD_ARTIST_IGNORE_THE, SortAttributeIgnoreArticle, 557 },
1115 { SortByAlbum, SORT_METHOD_ALBUM, SortAttributeNone, 558 },
1116 { SortByAlbum, SORT_METHOD_ALBUM_IGNORE_THE, SortAttributeIgnoreArticle, 558 },
1117 { SortByGenre, SORT_METHOD_GENRE, SortAttributeNone, 515 },
1118 { SortByCountry, SORT_METHOD_COUNTRY, SortAttributeNone, 574 },
1119 { SortByDateAdded, SORT_METHOD_DATEADDED, SortAttributeIgnoreFolders, 570 },
1120 { SortByFile, SORT_METHOD_FILE, SortAttributeIgnoreFolders, 561 },
1121 { SortByRating, SORT_METHOD_SONG_RATING, SortAttributeNone, 563 },
1122 { SortByRating, SORT_METHOD_VIDEO_RATING, SortAttributeIgnoreFolders, 563 },
1123 { SortByUserRating, SORT_METHOD_SONG_USER_RATING, SortAttributeIgnoreFolders, 38018 },
1124 { SortByUserRating, SORT_METHOD_VIDEO_USER_RATING, SortAttributeIgnoreFolders, 38018 },
1125 { SortBySortTitle, SORT_METHOD_VIDEO_SORT_TITLE, SortAttributeIgnoreFolders, 171 },
1126 { SortBySortTitle, SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE, (SortAttribute)(SortAttributeIgnoreFolders | SortAttributeIgnoreArticle), 171 },
1127 { SortByYear, SORT_METHOD_YEAR, SortAttributeIgnoreFolders, 562 },
1128 { SortByProductionCode, SORT_METHOD_PRODUCTIONCODE, SortAttributeNone, 20368 },
1129 { SortByProgramCount, SORT_METHOD_PROGRAM_COUNT, SortAttributeNone, 567 }, // label is "play count"
1130 { SortByPlaylistOrder, SORT_METHOD_PLAYLIST_ORDER, SortAttributeIgnoreFolders, 559 },
1131 { SortByMPAA, SORT_METHOD_MPAA_RATING, SortAttributeNone, 20074 },
1132 { SortByStudio, SORT_METHOD_STUDIO, SortAttributeNone, 572 },
1133 { SortByStudio, SORT_METHOD_STUDIO_IGNORE_THE, SortAttributeIgnoreArticle, 572 },
1134 { SortByPath, SORT_METHOD_FULLPATH, SortAttributeNone, 573 },
1135 { SortByLastPlayed, SORT_METHOD_LASTPLAYED, SortAttributeIgnoreFolders, 568 },
1136 { SortByPlaycount, SORT_METHOD_PLAYCOUNT, SortAttributeIgnoreFolders, 567 },
1137 { SortByListeners, SORT_METHOD_LISTENERS, SortAttributeNone, 20455 },
1138 { SortByChannel, SORT_METHOD_CHANNEL, SortAttributeNone, 19029 },
1139 { SortByChannel, SORT_METHOD_CHANNEL_NUMBER, SortAttributeNone, 549 },
1140 { SortByChannel, SORT_METHOD_CLIENT_CHANNEL_ORDER, SortAttributeNone, 19315 },
1141 { SortByDateTaken, SORT_METHOD_DATE_TAKEN, SortAttributeIgnoreFolders, 577 },
1142 { SortByNone, SORT_METHOD_NONE, SortAttributeNone, 16018 },
1143 { SortByTotalDiscs, SORT_METHOD_TOTAL_DISCS, SortAttributeNone, 38077 },
1144 { SortByOrigDate, SORT_METHOD_ORIG_DATE, SortAttributeNone, 38079 },
1145 { SortByBPM, SORT_METHOD_BPM, SortAttributeNone, 38080 },
1146
1147 // the following have no corresponding SORT_METHOD_*
1148 { SortByAlbumType, SORT_METHOD_NONE, SortAttributeNone, 564 },
1149 { SortByVotes, SORT_METHOD_NONE, SortAttributeNone, 205 },
1150 { SortByTop250, SORT_METHOD_NONE, SortAttributeNone, 13409 },
1151 { SortByMPAA, SORT_METHOD_NONE, SortAttributeNone, 20074 },
1152 { SortByDateAdded, SORT_METHOD_NONE, SortAttributeNone, 570 },
1153 { SortByTvShowTitle, SORT_METHOD_NONE, SortAttributeNone, 20364 },
1154 { SortByTvShowStatus, SORT_METHOD_NONE, SortAttributeNone, 126 },
1155 { SortBySeason, SORT_METHOD_NONE, SortAttributeNone, 20373 },
1156 { SortByNumberOfEpisodes, SORT_METHOD_NONE, SortAttributeNone, 20360 },
1157 { SortByNumberOfWatchedEpisodes, SORT_METHOD_NONE, SortAttributeNone, 21441 },
1158 { SortByVideoResolution, SORT_METHOD_NONE, SortAttributeNone, 21443 },
1159 { SortByVideoCodec, SORT_METHOD_NONE, SortAttributeNone, 21445 },
1160 { SortByVideoAspectRatio, SORT_METHOD_NONE, SortAttributeNone, 21374 },
1161 { SortByAudioChannels, SORT_METHOD_NONE, SortAttributeNone, 21444 },
1162 { SortByAudioCodec, SORT_METHOD_NONE, SortAttributeNone, 21446 },
1163 { SortByAudioLanguage, SORT_METHOD_NONE, SortAttributeNone, 21447 },
1164 { SortBySubtitleLanguage, SORT_METHOD_NONE, SortAttributeNone, 21448 },
1165 { SortByRandom, SORT_METHOD_NONE, SortAttributeNone, 590 }
1166 };
1167 // clang-format on
1168
TranslateOldSortMethod(SortBy sortBy,bool ignoreArticle)1169 SORT_METHOD SortUtils::TranslateOldSortMethod(SortBy sortBy, bool ignoreArticle)
1170 {
1171 for (const sort_map& t : table)
1172 {
1173 if (t.sort == sortBy)
1174 {
1175 if (ignoreArticle == ((t.flags & SortAttributeIgnoreArticle) == SortAttributeIgnoreArticle))
1176 return t.old;
1177 }
1178 }
1179 for (const sort_map& t : table)
1180 {
1181 if (t.sort == sortBy)
1182 return t.old;
1183 }
1184 return SORT_METHOD_NONE;
1185 }
1186
TranslateOldSortMethod(SORT_METHOD sortBy)1187 SortDescription SortUtils::TranslateOldSortMethod(SORT_METHOD sortBy)
1188 {
1189 SortDescription description;
1190 for (const sort_map& t : table)
1191 {
1192 if (t.old == sortBy)
1193 {
1194 description.sortBy = t.sort;
1195 description.sortAttributes = t.flags;
1196 break;
1197 }
1198 }
1199 return description;
1200 }
1201
GetSortLabel(SortBy sortBy)1202 int SortUtils::GetSortLabel(SortBy sortBy)
1203 {
1204 for (const sort_map& t : table)
1205 {
1206 if (t.sort == sortBy)
1207 return t.label;
1208 }
1209 return 16018; // None
1210 }
1211
1212 template<typename T>
TypeFromString(const std::map<std::string,T> & typeMap,const std::string & name,const T & defaultType)1213 T TypeFromString(const std::map<std::string, T>& typeMap, const std::string& name, const T& defaultType)
1214 {
1215 auto it = typeMap.find(name);
1216 if (it == typeMap.end())
1217 return defaultType;
1218
1219 return it->second;
1220 }
1221
1222 template<typename T>
TypeToString(const std::map<std::string,T> & typeMap,const T & value)1223 const std::string& TypeToString(const std::map<std::string, T>& typeMap, const T& value)
1224 {
1225 auto it = std::find_if(typeMap.begin(), typeMap.end(),
1226 [&value](const std::pair<std::string, T>& pair)
1227 {
1228 return pair.second == value;
1229 });
1230
1231 if (it == typeMap.end())
1232 return StringUtils::Empty;
1233
1234 return it->first;
1235 }
1236
1237 /**
1238 * @brief Sort methods to translate string values to enum values.
1239 *
1240 * @warning On string changes, edit __SortBy__ enumerator to have strings right
1241 * for documentation!
1242 */
1243 const std::map<std::string, SortBy> sortMethods = {
1244 { "label", SortByLabel },
1245 { "date", SortByDate },
1246 { "size", SortBySize },
1247 { "file", SortByFile },
1248 { "path", SortByPath },
1249 { "drivetype", SortByDriveType },
1250 { "title", SortByTitle },
1251 { "track", SortByTrackNumber },
1252 { "time", SortByTime },
1253 { "artist", SortByArtist },
1254 { "artistyear", SortByArtistThenYear },
1255 { "album", SortByAlbum },
1256 { "albumtype", SortByAlbumType },
1257 { "genre", SortByGenre },
1258 { "country", SortByCountry },
1259 { "year", SortByYear },
1260 { "rating", SortByRating },
1261 { "votes", SortByVotes },
1262 { "top250", SortByTop250 },
1263 { "programcount", SortByProgramCount },
1264 { "playlist", SortByPlaylistOrder },
1265 { "episode", SortByEpisodeNumber },
1266 { "season", SortBySeason },
1267 { "totalepisodes", SortByNumberOfEpisodes },
1268 { "watchedepisodes", SortByNumberOfWatchedEpisodes },
1269 { "tvshowstatus", SortByTvShowStatus },
1270 { "tvshowtitle", SortByTvShowTitle },
1271 { "sorttitle", SortBySortTitle },
1272 { "productioncode", SortByProductionCode },
1273 { "mpaa", SortByMPAA },
1274 { "videoresolution", SortByVideoResolution },
1275 { "videocodec", SortByVideoCodec },
1276 { "videoaspectratio", SortByVideoAspectRatio },
1277 { "audiochannels", SortByAudioChannels },
1278 { "audiocodec", SortByAudioCodec },
1279 { "audiolanguage", SortByAudioLanguage },
1280 { "subtitlelanguage", SortBySubtitleLanguage },
1281 { "studio", SortByStudio },
1282 { "dateadded", SortByDateAdded },
1283 { "lastplayed", SortByLastPlayed },
1284 { "playcount", SortByPlaycount },
1285 { "listeners", SortByListeners },
1286 { "bitrate", SortByBitrate },
1287 { "random", SortByRandom },
1288 { "channel", SortByChannel },
1289 { "channelnumber", SortByChannelNumber },
1290 { "clientchannelorder", SortByClientChannelOrder },
1291 { "datetaken", SortByDateTaken },
1292 { "userrating", SortByUserRating },
1293 { "installdate", SortByInstallDate },
1294 { "lastupdated", SortByLastUpdated },
1295 { "lastused", SortByLastUsed },
1296 { "totaldiscs", SortByTotalDiscs },
1297 { "originaldate", SortByOrigDate },
1298 { "bpm", SortByBPM },
1299 };
1300
SortMethodFromString(const std::string & sortMethod)1301 SortBy SortUtils::SortMethodFromString(const std::string& sortMethod)
1302 {
1303 return TypeFromString<SortBy>(sortMethods, sortMethod, SortByNone);
1304 }
1305
SortMethodToString(SortBy sortMethod)1306 const std::string& SortUtils::SortMethodToString(SortBy sortMethod)
1307 {
1308 return TypeToString<SortBy>(sortMethods, sortMethod);
1309 }
1310
1311 const std::map<std::string, SortOrder> sortOrders = {
1312 { "ascending", SortOrderAscending },
1313 { "descending", SortOrderDescending }
1314 };
1315
SortOrderFromString(const std::string & sortOrder)1316 SortOrder SortUtils::SortOrderFromString(const std::string& sortOrder)
1317 {
1318 return TypeFromString<SortOrder>(sortOrders, sortOrder, SortOrderNone);
1319 }
1320
SortOrderToString(SortOrder sortOrder)1321 const std::string& SortUtils::SortOrderToString(SortOrder sortOrder)
1322 {
1323 return TypeToString<SortOrder>(sortOrders, sortOrder);
1324 }
1325