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 "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 /* Fix me: This considers empty fields as duplicates. */ 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 /* The algorithm is a bit quirky for historical reasons. -jlindgren */ 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 }