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