Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * playlist-utils.c 00003 * Copyright 2009-2010 John Lindgren 00004 * 00005 * This file is part of Audacious. 00006 * 00007 * Audacious is free software: you can redistribute it and/or modify it under 00008 * the terms of the GNU General Public License as published by the Free Software 00009 * Foundation, version 2 or version 3 of the License. 00010 * 00011 * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY 00012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 00013 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along with 00016 * Audacious. If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 * The Audacious team does not consider modular code linking to Audacious or 00019 * using our public API to be a derived work. 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 /* Fix me: This considers empty fields as duplicates. */ 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 /* vfs_file_test() only works for file:// URIs currently */ 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 /* The algorithm is a bit quirky for historical reasons. -jlindgren */ 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 }