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