00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <glib.h>
00023 #include <regex.h>
00024 #include <string.h>
00025
00026 #include <libaudcore/audstrings.h>
00027
00028 #include "audconfig.h"
00029 #include "main.h"
00030 #include "misc.h"
00031 #include "playlist.h"
00032 #include "playlist_container.h"
00033 #include "playlist-utils.h"
00034
00035 static const gchar * aud_titlestring_presets[] =
00036 {
00037 "${title}",
00038 "${?artist:${artist} - }${title}",
00039 "${?artist:${artist} - }${?album:${album} - }${title}",
00040 "${?artist:${artist} - }${?album:${album} - }"
00041 "${?track-number:${track-number}. }${title}",
00042 "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }"
00043 "${?track-number:${track-number}. }${title}",
00044 "${?album:${album} - }${title}",
00045 };
00046
00047 const gint n_titlestring_presets = G_N_ELEMENTS (aud_titlestring_presets);
00048
00049 static const gchar * get_basename (const gchar * filename)
00050 {
00051 const gchar * slash = strrchr (filename, '/');
00052
00053 return (slash == NULL) ? filename : slash + 1;
00054 }
00055
00056 static gint filename_compare_basename (const gchar * a, const gchar * b)
00057 {
00058 return string_compare_encoded (get_basename (a), get_basename (b));
00059 }
00060
00061 static gint tuple_compare_string (const Tuple * a, const Tuple * b, gint field)
00062 {
00063 const gchar * string_a = tuple_get_string (a, field, NULL);
00064 const gchar * string_b = tuple_get_string (b, field, NULL);
00065
00066 if (string_a == NULL)
00067 return (string_b == NULL) ? 0 : -1;
00068 if (string_b == NULL)
00069 return 1;
00070
00071 return string_compare (string_a, string_b);
00072 }
00073
00074 static gint tuple_compare_int (const Tuple * a, const Tuple * b, gint field)
00075 {
00076 if (tuple_get_value_type (a, field, NULL) != TUPLE_INT)
00077 return (tuple_get_value_type (b, field, NULL) != TUPLE_INT) ? 0 : -1;
00078 if (tuple_get_value_type (b, field, NULL) != TUPLE_INT)
00079 return 1;
00080
00081 gint int_a = tuple_get_int (a, field, NULL);
00082 gint int_b = tuple_get_int (b, field, NULL);
00083
00084 return (int_a < int_b) ? -1 : (int_a > int_b);
00085 }
00086
00087 static gint tuple_compare_title (const Tuple * a, const Tuple * b)
00088 {
00089 return tuple_compare_string (a, b, FIELD_TITLE);
00090 }
00091
00092 static gint tuple_compare_album (const Tuple * a, const Tuple * b)
00093 {
00094 return tuple_compare_string (a, b, FIELD_ALBUM);
00095 }
00096
00097 static gint tuple_compare_artist (const Tuple * a, const Tuple * b)
00098 {
00099 return tuple_compare_string (a, b, FIELD_ARTIST);
00100 }
00101
00102 static gint tuple_compare_date (const Tuple * a, const Tuple * b)
00103 {
00104 return tuple_compare_int (a, b, FIELD_YEAR);
00105 }
00106
00107 static gint tuple_compare_track (const Tuple * a, const Tuple * b)
00108 {
00109 return tuple_compare_int (a, b, FIELD_TRACK_NUMBER);
00110 }
00111
00112 static const PlaylistFilenameCompareFunc filename_comparisons[] = {
00113 [PLAYLIST_SORT_PATH] = string_compare_encoded,
00114 [PLAYLIST_SORT_FILENAME] = filename_compare_basename,
00115 [PLAYLIST_SORT_TITLE] = NULL,
00116 [PLAYLIST_SORT_ALBUM] = NULL,
00117 [PLAYLIST_SORT_ARTIST] = NULL,
00118 [PLAYLIST_SORT_DATE] = NULL,
00119 [PLAYLIST_SORT_TRACK] = NULL};
00120
00121 static const PlaylistTupleCompareFunc tuple_comparisons[] = {
00122 [PLAYLIST_SORT_PATH] = NULL,
00123 [PLAYLIST_SORT_FILENAME] = NULL,
00124 [PLAYLIST_SORT_TITLE] = tuple_compare_title,
00125 [PLAYLIST_SORT_ALBUM] = tuple_compare_album,
00126 [PLAYLIST_SORT_ARTIST] = tuple_compare_artist,
00127 [PLAYLIST_SORT_DATE] = tuple_compare_date,
00128 [PLAYLIST_SORT_TRACK] = tuple_compare_track};
00129
00130 const gchar * get_gentitle_format (void)
00131 {
00132 if (cfg.titlestring_preset >= 0 && cfg.titlestring_preset <
00133 n_titlestring_presets)
00134 return aud_titlestring_presets[cfg.titlestring_preset];
00135
00136 return cfg.gentitle_format;
00137 }
00138
00139 void playlist_sort_by_scheme (gint playlist, gint scheme)
00140 {
00141 if (filename_comparisons[scheme] != NULL)
00142 playlist_sort_by_filename (playlist, filename_comparisons[scheme]);
00143 else if (tuple_comparisons[scheme] != NULL)
00144 playlist_sort_by_tuple (playlist, tuple_comparisons[scheme]);
00145 }
00146
00147 void playlist_sort_selected_by_scheme (gint playlist, gint scheme)
00148 {
00149 if (filename_comparisons[scheme] != NULL)
00150 playlist_sort_selected_by_filename (playlist,
00151 filename_comparisons[scheme]);
00152 else if (tuple_comparisons[scheme] != NULL)
00153 playlist_sort_selected_by_tuple (playlist, tuple_comparisons[scheme]);
00154 }
00155
00156
00157 void playlist_remove_duplicates_by_scheme (gint playlist, gint scheme)
00158 {
00159 gint entries = playlist_entry_count (playlist);
00160 gint count;
00161
00162 if (entries < 1)
00163 return;
00164
00165 playlist_select_all (playlist, FALSE);
00166
00167 if (filename_comparisons[scheme] != NULL)
00168 {
00169 gint (* compare) (const gchar * a, const gchar * b) =
00170 filename_comparisons[scheme];
00171 const gchar * last, * current;
00172
00173 playlist_sort_by_filename (playlist, compare);
00174 last = playlist_entry_get_filename (playlist, 0);
00175
00176 for (count = 1; count < entries; count ++)
00177 {
00178 current = playlist_entry_get_filename (playlist, count);
00179
00180 if (compare (last, current) == 0)
00181 playlist_entry_set_selected (playlist, count, TRUE);
00182
00183 last = current;
00184 }
00185 }
00186 else if (tuple_comparisons[scheme] != NULL)
00187 {
00188 gint (* compare) (const Tuple * a, const Tuple * b) =
00189 tuple_comparisons[scheme];
00190 const Tuple * last, * current;
00191
00192 playlist_sort_by_tuple (playlist, compare);
00193 last = playlist_entry_get_tuple (playlist, 0, FALSE);
00194
00195 for (count = 1; count < entries; count ++)
00196 {
00197 current = playlist_entry_get_tuple (playlist, count, FALSE);
00198
00199 if (last != NULL && current != NULL && compare (last, current) == 0)
00200 playlist_entry_set_selected (playlist, count, TRUE);
00201
00202 last = current;
00203 }
00204 }
00205
00206 playlist_delete_selected (playlist);
00207 }
00208
00209 void playlist_remove_failed (gint playlist)
00210 {
00211 gint entries = playlist_entry_count (playlist);
00212 gint count;
00213
00214 playlist_rescan (playlist);
00215 playlist_select_all (playlist, FALSE);
00216
00217 for (count = 0; count < entries; count ++)
00218 {
00219 if (playlist_entry_get_decoder (playlist, count) == NULL ||
00220 playlist_entry_get_tuple (playlist, count, FALSE) == NULL)
00221 playlist_entry_set_selected (playlist, count, TRUE);
00222 }
00223
00224 playlist_delete_selected (playlist);
00225 }
00226
00227 void playlist_select_by_patterns (gint playlist, const Tuple * patterns)
00228 {
00229 const gint fields[] = {FIELD_TITLE, FIELD_ALBUM, FIELD_ARTIST,
00230 FIELD_FILE_NAME};
00231
00232 gint entries = playlist_entry_count (playlist);
00233 gint field, entry;
00234
00235 playlist_select_all (playlist, TRUE);
00236
00237 for (field = 0; field < G_N_ELEMENTS (fields); field ++)
00238 {
00239 const gchar * pattern = tuple_get_string ((Tuple *) patterns,
00240 fields[field], NULL);
00241 regex_t regex;
00242
00243 if (pattern == NULL || pattern[0] == 0)
00244 continue;
00245
00246 if (regcomp (& regex, pattern, REG_ICASE) != 0)
00247 continue;
00248
00249 for (entry = 0; entry < entries; entry ++)
00250 {
00251 const Tuple * tuple;
00252 const gchar * string;
00253
00254 if (! playlist_entry_get_selected (playlist, entry))
00255 continue;
00256
00257 tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
00258
00259 if (tuple == NULL)
00260 goto NO_MATCH;
00261
00262 string = tuple_get_string ((Tuple *) tuple, fields[field], NULL);
00263
00264 if (string == NULL)
00265 goto NO_MATCH;
00266
00267 if (regexec (& regex, string, 0, NULL, 0) == 0)
00268 continue;
00269
00270 NO_MATCH:
00271 playlist_entry_set_selected (playlist, entry, FALSE);
00272 }
00273
00274 regfree (& regex);
00275 }
00276 }
00277
00278 gboolean filename_is_playlist (const gchar * filename)
00279 {
00280 const gchar * period = strrchr (filename, '.');
00281
00282 return (period != NULL && playlist_container_find ((gchar *) period + 1) !=
00283 NULL);
00284 }
00285
00286 gboolean playlist_insert_playlist (gint playlist, gint at, const gchar *
00287 filename)
00288 {
00289 const gchar * period = strrchr (filename, '.');
00290 PlaylistContainer * container;
00291 gint last;
00292
00293 if (period == NULL)
00294 return FALSE;
00295
00296 container = playlist_container_find ((gchar *) period + 1);
00297
00298 if (container == NULL || container->plc_read == NULL)
00299 return FALSE;
00300
00301 last = playlist_get_active ();
00302 playlist_set_active (playlist);
00303 container->plc_read (filename, at);
00304 playlist_set_active (last);
00305 return TRUE;
00306 }
00307
00308 gboolean playlist_save (gint playlist, const gchar * filename)
00309 {
00310 const gchar * period = strrchr (filename, '.');
00311 PlaylistContainer * container;
00312 gint last;
00313
00314 if (period == NULL)
00315 return FALSE;
00316
00317 container = playlist_container_find ((gchar *) period + 1);
00318
00319 if (container == NULL || container->plc_write == NULL)
00320 return FALSE;
00321
00322 last = playlist_get_active ();
00323 playlist_set_active (playlist);
00324 container->plc_write (filename, 0);
00325 playlist_set_active (last);
00326 return TRUE;
00327 }
00328
00329
00330 static gchar * make_playlist_path (gint playlist)
00331 {
00332 if (! playlist)
00333 return g_strdup (aud_paths[BMP_PATH_PLAYLIST_FILE]);
00334
00335 return g_strdup_printf ("%s/playlist_%02d.xspf",
00336 aud_paths[BMP_PATH_PLAYLISTS_DIR], 1 + playlist);
00337 }
00338
00339 void load_playlists (void)
00340 {
00341 gboolean done = FALSE;
00342 gint count;
00343
00344 for (count = 0; ! done; count ++)
00345 {
00346 gchar * path = make_playlist_path (count);
00347
00348 if (g_file_test (path, G_FILE_TEST_EXISTS))
00349 {
00350 gchar * uri = filename_to_uri (path);
00351
00352 if (count)
00353 playlist_insert (count);
00354
00355 playlist_insert_playlist (count, 0, uri);
00356 g_free (uri);
00357 }
00358 else
00359 done = TRUE;
00360
00361 g_free (path);
00362 }
00363
00364 playlist_load_state ();
00365 }
00366
00367 void save_playlists (void)
00368 {
00369 gint playlists = playlist_count ();
00370 gboolean done = FALSE;
00371 gint count;
00372
00373 for (count = 0; ! done; count ++)
00374 {
00375 gchar * path = make_playlist_path (count);
00376
00377 if (count < playlists)
00378 {
00379 gchar * uri = filename_to_uri (path);
00380
00381 playlist_save (count, uri);
00382 g_free (uri);
00383 }
00384 else if (g_file_test (path, G_FILE_TEST_EXISTS))
00385 remove (path);
00386 else
00387 done = TRUE;
00388
00389 g_free (path);
00390 }
00391
00392 playlist_save_state ();
00393 }