• Main Page
  • Related Pages
  • Classes
  • Files
  • File List
  • File Members

playlist-utils.c

Go to the documentation of this file.
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 }

Generated on Wed Apr 6 2011 for Audacious by  doxygen 1.7.1