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 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_rescan (playlist); 00229 playlist_select_all (playlist, FALSE); 00230 00231 for (count = 0; count < entries; count ++) 00232 { 00233 if (! playlist_entry_get_decoder (playlist, count, FALSE)) 00234 playlist_entry_set_selected (playlist, count, TRUE); 00235 } 00236 00237 playlist_delete_selected (playlist); 00238 } 00239 00240 void playlist_select_by_patterns (gint playlist, const Tuple * patterns) 00241 { 00242 const gint fields[] = {FIELD_TITLE, FIELD_ALBUM, FIELD_ARTIST, 00243 FIELD_FILE_NAME}; 00244 00245 gint entries = playlist_entry_count (playlist); 00246 gint field, entry; 00247 00248 playlist_select_all (playlist, TRUE); 00249 00250 for (field = 0; field < G_N_ELEMENTS (fields); field ++) 00251 { 00252 const gchar * pattern = tuple_get_string ((Tuple *) patterns, 00253 fields[field], NULL); 00254 regex_t regex; 00255 00256 if (pattern == NULL || pattern[0] == 0) 00257 continue; 00258 00259 if (regcomp (& regex, pattern, REG_ICASE) != 0) 00260 continue; 00261 00262 for (entry = 0; entry < entries; entry ++) 00263 { 00264 const Tuple * tuple; 00265 const gchar * string; 00266 00267 if (! playlist_entry_get_selected (playlist, entry)) 00268 continue; 00269 00270 tuple = playlist_entry_get_tuple (playlist, entry, FALSE); 00271 00272 if (tuple == NULL) 00273 goto NO_MATCH; 00274 00275 string = tuple_get_string ((Tuple *) tuple, fields[field], NULL); 00276 00277 if (string == NULL) 00278 goto NO_MATCH; 00279 00280 if (regexec (& regex, string, 0, NULL, 0) == 0) 00281 continue; 00282 00283 NO_MATCH: 00284 playlist_entry_set_selected (playlist, entry, FALSE); 00285 } 00286 00287 regfree (& regex); 00288 } 00289 } 00290 00291 /* The algorithm is a bit quirky for historical reasons. -jlindgren */ 00292 static gchar * make_playlist_path (gint playlist) 00293 { 00294 if (! playlist) 00295 return g_strdup (get_path (AUD_PATH_PLAYLIST_FILE)); 00296 00297 return g_strdup_printf ("%s/playlist_%02d.xspf", 00298 get_path (AUD_PATH_PLAYLISTS_DIR), 1 + playlist); 00299 } 00300 00301 void load_playlists (void) 00302 { 00303 gboolean done = FALSE; 00304 gint count; 00305 00306 for (count = 0; ! done; count ++) 00307 { 00308 gchar * path = make_playlist_path (count); 00309 00310 if (g_file_test (path, G_FILE_TEST_EXISTS)) 00311 { 00312 gchar * uri = filename_to_uri (path); 00313 00314 if (count) 00315 playlist_insert (count); 00316 00317 playlist_insert_playlist (count, 0, uri); 00318 g_free (uri); 00319 } 00320 else 00321 done = TRUE; 00322 00323 g_free (path); 00324 } 00325 00326 playlist_load_state (); 00327 } 00328 00329 void save_playlists (void) 00330 { 00331 gint playlists = playlist_count (); 00332 gboolean done = FALSE; 00333 gint count; 00334 00335 for (count = 0; ! done; count ++) 00336 { 00337 gchar * path = make_playlist_path (count); 00338 00339 if (count < playlists) 00340 { 00341 gchar * uri = filename_to_uri (path); 00342 00343 playlist_save (count, uri); 00344 g_free (uri); 00345 } 00346 else if (g_file_test (path, G_FILE_TEST_EXISTS)) 00347 remove (path); 00348 else 00349 done = TRUE; 00350 00351 g_free (path); 00352 } 00353 00354 playlist_save_state (); 00355 }