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 const gchar * last, * current;
00186
00187 playlist_sort_by_filename (playlist, compare);
00188 last = playlist_entry_get_filename (playlist, 0);
00189
00190 for (count = 1; count < entries; count ++)
00191 {
00192 current = playlist_entry_get_filename (playlist, count);
00193
00194 if (compare (last, current) == 0)
00195 playlist_entry_set_selected (playlist, count, TRUE);
00196
00197 last = current;
00198 }
00199 }
00200 else if (tuple_comparisons[scheme] != NULL)
00201 {
00202 gint (* compare) (const Tuple * a, const Tuple * b) =
00203 tuple_comparisons[scheme];
00204 const Tuple * last, * current;
00205
00206 playlist_sort_by_tuple (playlist, compare);
00207 last = playlist_entry_get_tuple (playlist, 0, FALSE);
00208
00209 for (count = 1; count < entries; count ++)
00210 {
00211 current = playlist_entry_get_tuple (playlist, count, FALSE);
00212
00213 if (last != NULL && current != NULL && compare (last, current) == 0)
00214 playlist_entry_set_selected (playlist, count, TRUE);
00215
00216 last = current;
00217 }
00218 }
00219
00220 playlist_delete_selected (playlist);
00221 }
00222
00223 void playlist_remove_failed (gint playlist)
00224 {
00225 gint entries = playlist_entry_count (playlist);
00226 gint count;
00227
00228 playlist_select_all (playlist, FALSE);
00229
00230 for (count = 0; count < entries; count ++)
00231 {
00232 const gchar * filename = playlist_entry_get_filename (playlist, count);
00233
00234
00235 if (! strncmp (filename, "file://", 7) && ! vfs_file_test (filename,
00236 G_FILE_TEST_EXISTS))
00237 playlist_entry_set_selected (playlist, count, TRUE);
00238 }
00239
00240 playlist_delete_selected (playlist);
00241 }
00242
00243 void playlist_select_by_patterns (gint playlist, const Tuple * patterns)
00244 {
00245 const gint fields[] = {FIELD_TITLE, FIELD_ALBUM, FIELD_ARTIST,
00246 FIELD_FILE_NAME};
00247
00248 gint entries = playlist_entry_count (playlist);
00249 gint field, entry;
00250
00251 playlist_select_all (playlist, TRUE);
00252
00253 for (field = 0; field < G_N_ELEMENTS (fields); field ++)
00254 {
00255 const gchar * pattern = tuple_get_string ((Tuple *) patterns,
00256 fields[field], NULL);
00257 regex_t regex;
00258
00259 if (pattern == NULL || pattern[0] == 0)
00260 continue;
00261
00262 if (regcomp (& regex, pattern, REG_ICASE) != 0)
00263 continue;
00264
00265 for (entry = 0; entry < entries; entry ++)
00266 {
00267 const Tuple * tuple;
00268 const gchar * string;
00269
00270 if (! playlist_entry_get_selected (playlist, entry))
00271 continue;
00272
00273 tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
00274
00275 if (tuple == NULL)
00276 goto NO_MATCH;
00277
00278 string = tuple_get_string ((Tuple *) tuple, fields[field], NULL);
00279
00280 if (string == NULL)
00281 goto NO_MATCH;
00282
00283 if (regexec (& regex, string, 0, NULL, 0) == 0)
00284 continue;
00285
00286 NO_MATCH:
00287 playlist_entry_set_selected (playlist, entry, FALSE);
00288 }
00289
00290 regfree (& regex);
00291 }
00292 }
00293
00294
00295 static gchar * make_playlist_path (gint playlist)
00296 {
00297 if (! playlist)
00298 return g_strdup (get_path (AUD_PATH_PLAYLIST_FILE));
00299
00300 return g_strdup_printf ("%s/playlist_%02d.xspf",
00301 get_path (AUD_PATH_PLAYLISTS_DIR), 1 + playlist);
00302 }
00303
00304 void load_playlists (void)
00305 {
00306 gboolean done = FALSE;
00307 gint count;
00308
00309 for (count = 0; ! done; count ++)
00310 {
00311 gchar * path = make_playlist_path (count);
00312
00313 if (g_file_test (path, G_FILE_TEST_EXISTS))
00314 {
00315 gchar * uri = filename_to_uri (path);
00316
00317 if (count)
00318 playlist_insert (count);
00319
00320 playlist_insert_playlist (count, 0, uri);
00321 g_free (uri);
00322 }
00323 else
00324 done = TRUE;
00325
00326 g_free (path);
00327 }
00328
00329 playlist_load_state ();
00330 }
00331
00332 void save_playlists (void)
00333 {
00334 gint playlists = playlist_count ();
00335 gboolean done = FALSE;
00336 gint count;
00337
00338 for (count = 0; ! done; count ++)
00339 {
00340 gchar * path = make_playlist_path (count);
00341
00342 if (count < playlists)
00343 {
00344 gchar * uri = filename_to_uri (path);
00345
00346 playlist_save (count, uri);
00347 g_free (uri);
00348 }
00349 else if (g_file_test (path, G_FILE_TEST_EXISTS))
00350 remove (path);
00351 else
00352 done = TRUE;
00353
00354 g_free (path);
00355 }
00356
00357 playlist_save_state ();
00358 }