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_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 /* vfs_file_test() only works for file:// URIs currently */ 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 /* The algorithm is a bit quirky for historical reasons. -jlindgren */ 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 }