Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
playlist-utils.c
Go to the documentation of this file.
1 /*
2  * playlist-utils.c
3  * Copyright 2009-2011 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <dirent.h>
21 #include <glib.h>
22 #include <regex.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <libaudcore/audstrings.h>
28 #include <libaudcore/hook.h>
29 
30 #include "misc.h"
31 #include "playlist.h"
32 
33 static const char * get_basename (const char * filename)
34 {
35  const char * slash = strrchr (filename, '/');
36 
37  return (slash == NULL) ? filename : slash + 1;
38 }
39 
40 static int filename_compare_basename (const char * a, const char * b)
41 {
43 }
44 
45 static int tuple_compare_string (const Tuple * a, const Tuple * b, int field)
46 {
47  char * string_a = tuple_get_str (a, field, NULL);
48  char * string_b = tuple_get_str (b, field, NULL);
49  int ret;
50 
51  if (string_a == NULL)
52  ret = (string_b == NULL) ? 0 : -1;
53  else if (string_b == NULL)
54  ret = 1;
55  else
56  ret = string_compare (string_a, string_b);
57 
58  str_unref (string_a);
59  str_unref (string_b);
60  return ret;
61 }
62 
63 static int tuple_compare_int (const Tuple * a, const Tuple * b, int field)
64 {
65  if (tuple_get_value_type (a, field, NULL) != TUPLE_INT)
66  return (tuple_get_value_type (b, field, NULL) != TUPLE_INT) ? 0 : -1;
67  if (tuple_get_value_type (b, field, NULL) != TUPLE_INT)
68  return 1;
69 
70  int int_a = tuple_get_int (a, field, NULL);
71  int int_b = tuple_get_int (b, field, NULL);
72 
73  return (int_a < int_b) ? -1 : (int_a > int_b);
74 }
75 
76 static int tuple_compare_title (const Tuple * a, const Tuple * b)
77 {
78  return tuple_compare_string (a, b, FIELD_TITLE);
79 }
80 
81 static int tuple_compare_album (const Tuple * a, const Tuple * b)
82 {
83  return tuple_compare_string (a, b, FIELD_ALBUM);
84 }
85 
86 static int tuple_compare_artist (const Tuple * a, const Tuple * b)
87 {
88  return tuple_compare_string (a, b, FIELD_ARTIST);
89 }
90 
91 static int tuple_compare_date (const Tuple * a, const Tuple * b)
92 {
93  return tuple_compare_int (a, b, FIELD_YEAR);
94 }
95 
96 static int tuple_compare_track (const Tuple * a, const Tuple * b)
97 {
99 }
100 
101 static int tuple_compare_length (const Tuple * a, const Tuple * b)
102 {
103  return tuple_compare_int (a, b, FIELD_LENGTH);
104 }
105 
116 
127 
138 
139 void playlist_sort_by_scheme (int playlist, int scheme)
140 {
141  if (filename_comparisons[scheme] != NULL)
143  else if (tuple_comparisons[scheme] != NULL)
144  playlist_sort_by_tuple (playlist, tuple_comparisons[scheme]);
145  else if (title_comparisons[scheme] != NULL)
146  playlist_sort_by_title (playlist, title_comparisons[scheme]);
147 }
148 
150 {
151  if (filename_comparisons[scheme] != NULL)
153  filename_comparisons[scheme]);
154  else if (tuple_comparisons[scheme] != NULL)
156  else if (title_comparisons[scheme] != NULL)
158 }
159 
160 /* Fix me: This considers empty fields as duplicates. */
162 {
163  int entries = playlist_entry_count (playlist);
164  int count;
165 
166  if (entries < 1)
167  return;
168 
169  playlist_select_all (playlist, FALSE);
170 
171  if (filename_comparisons[scheme] != NULL)
172  {
173  int (* compare) (const char * a, const char * b) =
174  filename_comparisons[scheme];
175 
176  playlist_sort_by_filename (playlist, compare);
177  char * last = playlist_entry_get_filename (playlist, 0);
178 
179  for (count = 1; count < entries; count ++)
180  {
181  char * current = playlist_entry_get_filename (playlist, count);
182 
183  if (compare (last, current) == 0)
184  playlist_entry_set_selected (playlist, count, TRUE);
185 
186  str_unref (last);
187  last = current;
188  }
189 
190  str_unref (last);
191  }
192  else if (tuple_comparisons[scheme] != NULL)
193  {
194  int (* compare) (const Tuple * a, const Tuple * b) =
195  tuple_comparisons[scheme];
196 
197  playlist_sort_by_tuple (playlist, compare);
198  Tuple * last = playlist_entry_get_tuple (playlist, 0, FALSE);
199 
200  for (count = 1; count < entries; count ++)
201  {
202  Tuple * current = playlist_entry_get_tuple (playlist, count, FALSE);
203 
204  if (last != NULL && current != NULL && compare (last, current) == 0)
205  playlist_entry_set_selected (playlist, count, TRUE);
206 
207  if (last)
208  tuple_unref (last);
209  last = current;
210  }
211 
212  if (last)
213  tuple_unref (last);
214  }
215 
216  playlist_delete_selected (playlist);
217 }
218 
220 {
221  int entries = playlist_entry_count (playlist);
222  int count;
223 
224  playlist_select_all (playlist, FALSE);
225 
226  for (count = 0; count < entries; count ++)
227  {
228  char * filename = playlist_entry_get_filename (playlist, count);
229 
230  /* vfs_file_test() only works for file:// URIs currently */
231  if (! strncmp (filename, "file://", 7) && ! vfs_file_test (filename,
232  G_FILE_TEST_EXISTS))
233  playlist_entry_set_selected (playlist, count, TRUE);
234 
235  str_unref (filename);
236  }
237 
238  playlist_delete_selected (playlist);
239 }
240 
241 void playlist_select_by_patterns (int playlist, const Tuple * patterns)
242 {
243  const int fields[] = {FIELD_TITLE, FIELD_ALBUM, FIELD_ARTIST,
245 
246  int entries = playlist_entry_count (playlist);
247  int field, entry;
248 
249  playlist_select_all (playlist, TRUE);
250 
251  for (field = 0; field < G_N_ELEMENTS (fields); field ++)
252  {
253  char * pattern = tuple_get_str (patterns, fields[field], NULL);
254  regex_t regex;
255 
256  if (! pattern || ! pattern[0] || regcomp (& regex, pattern, REG_ICASE))
257  {
258  str_unref (pattern);
259  continue;
260  }
261 
262  for (entry = 0; entry < entries; entry ++)
263  {
264  if (! playlist_entry_get_selected (playlist, entry))
265  continue;
266 
267  Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
268  char * string = tuple ? tuple_get_str (tuple, fields[field], NULL) : NULL;
269 
270  if (! string || regexec (& regex, string, 0, NULL, 0))
271  playlist_entry_set_selected (playlist, entry, FALSE);
272 
273  str_unref (string);
274  if (tuple)
275  tuple_unref (tuple);
276  }
277 
278  regfree (& regex);
279  str_unref (pattern);
280  }
281 }
282 
283 static char * make_playlist_path (int playlist)
284 {
285  if (! playlist)
286  return g_strdup_printf ("%s/playlist.xspf", get_path (AUD_PATH_USER_DIR));
287 
288  return g_strdup_printf ("%s/playlist_%02d.xspf",
289  get_path (AUD_PATH_PLAYLISTS_DIR), 1 + playlist);
290 }
291 
292 static void load_playlists_real (void)
293 {
294  /* old (v3.1 and earlier) naming scheme */
295 
296  int count;
297  for (count = 0; ; count ++)
298  {
299  char * path = make_playlist_path (count);
300 
301  if (! g_file_test (path, G_FILE_TEST_EXISTS))
302  {
303  g_free (path);
304  break;
305  }
306 
307  char * uri = filename_to_uri (path);
308 
309  playlist_insert (count);
310  playlist_insert_playlist_raw (count, 0, uri);
311  playlist_set_modified (count, TRUE);
312 
313  g_free (path);
314  g_free (uri);
315  }
316 
317  /* unique ID-based naming scheme */
318 
319  char * order_path = g_strdup_printf ("%s/order", get_path (AUD_PATH_PLAYLISTS_DIR));
320  char * order_string;
321  g_file_get_contents (order_path, & order_string, NULL, NULL);
322  g_free (order_path);
323 
324  if (! order_string)
325  goto DONE;
326 
327  char * * order = g_strsplit (order_string, " ", -1);
328  g_free (order_string);
329 
330  for (int i = 0; order[i]; i ++)
331  {
332  char * path = g_strdup_printf ("%s/%s.audpl", get_path (AUD_PATH_PLAYLISTS_DIR), order[i]);
333 
334  if (! g_file_test (path, G_FILE_TEST_EXISTS))
335  {
336  g_free (path);
337  path = g_strdup_printf ("%s/%s.xspf", get_path (AUD_PATH_PLAYLISTS_DIR), order[i]);
338  }
339 
340  char * uri = filename_to_uri (path);
341 
342  playlist_insert_with_id (count + i, atoi (order[i]));
343  playlist_insert_playlist_raw (count + i, 0, uri);
344  playlist_set_modified (count + i, FALSE);
345 
346  if (g_str_has_suffix (path, ".xspf"))
347  playlist_set_modified (count + i, TRUE);
348 
349  g_free (path);
350  g_free (uri);
351  }
352 
353  g_strfreev (order);
354 
355 DONE:
356  if (! playlist_count ())
357  playlist_insert (0);
358 
360 }
361 
362 static void save_playlists_real (void)
363 {
364  int lists = playlist_count ();
365  const char * folder = get_path (AUD_PATH_PLAYLISTS_DIR);
366 
367  /* save playlists */
368 
369  char * * order = g_malloc (sizeof (char *) * (lists + 1));
370  GHashTable * saved = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
371 
372  for (int i = 0; i < lists; i ++)
373  {
374  int id = playlist_get_unique_id (i);
375  order[i] = g_strdup_printf ("%d", id);
376 
377  if (playlist_get_modified (i))
378  {
379  char * path = g_strdup_printf ("%s/%d.audpl", folder, id);
380  char * uri = filename_to_uri (path);
381 
382  playlist_save (i, uri);
384 
385  g_free (path);
386  g_free (uri);
387  }
388 
389  g_hash_table_insert (saved, g_strdup_printf ("%d.audpl", id), NULL);
390  }
391 
392  order[lists] = NULL;
393  char * order_string = g_strjoinv (" ", order);
394  g_strfreev (order);
395 
396  GError * error = NULL;
397  char * order_path = g_strdup_printf ("%s/order", get_path (AUD_PATH_PLAYLISTS_DIR));
398 
399  char * old_order_string;
400  g_file_get_contents (order_path, & old_order_string, NULL, NULL);
401 
402  if (! old_order_string || strcmp (old_order_string, order_string))
403  {
404  if (! g_file_set_contents (order_path, order_string, -1, & error))
405  {
406  fprintf (stderr, "Cannot write to %s: %s\n", order_path, error->message);
407  g_error_free (error);
408  }
409  }
410 
411  g_free (order_string);
412  g_free (order_path);
413  g_free (old_order_string);
414 
415  /* clean up deleted playlists and files from old naming scheme */
416 
417  char * path = make_playlist_path (0);
418  remove (path);
419  g_free (path);
420 
421  DIR * dir = opendir (folder);
422  if (! dir)
423  goto DONE;
424 
425  struct dirent * entry;
426  while ((entry = readdir (dir)))
427  {
428  if (! g_str_has_suffix (entry->d_name, ".audpl")
429  && ! g_str_has_suffix (entry->d_name, ".xspf"))
430  continue;
431 
432  if (! g_hash_table_lookup_extended (saved, entry->d_name, NULL, NULL))
433  {
434  char * path = g_strdup_printf ("%s/%s", folder, entry->d_name);
435  remove (path);
436  g_free (path);
437  }
438  }
439 
440  closedir (dir);
441 
442 DONE:
443  g_hash_table_destroy (saved);
444 }
445 
447 
448 static void update_cb (void * data, void * user)
449 {
450  if (GPOINTER_TO_INT (data) < PLAYLIST_UPDATE_METADATA)
451  return;
452 
454 }
455 
456 static void state_cb (void * data, void * user)
457 {
459 }
460 
461 void load_playlists (void)
462 {
465 
467 
468  if (! hooks_added)
469  {
470  hook_associate ("playlist update", update_cb, NULL);
471  hook_associate ("playlist activate", state_cb, NULL);
472  hook_associate ("playlist position", state_cb, NULL);
473 
474  hooks_added = TRUE;
475  }
476 }
477 
478 void save_playlists (bool_t exiting)
479 {
481 
482  /* on exit, save resume states */
483  if (state_changed || exiting)
484  {
487  }
488 
489  if (exiting && hooks_added)
490  {
491  hook_dissociate ("playlist update", update_cb);
492  hook_dissociate ("playlist activate", state_cb);
493  hook_dissociate ("playlist position", state_cb);
494 
495  hooks_added = FALSE;
496  }
497 }
static char * make_playlist_path(int playlist)
void playlist_insert_with_id(int at, int id)
Definition: playlist-new.c:695
void playlist_select_by_patterns(int playlist, const Tuple *patterns)
int playlist_get_unique_id(int playlist_num)
Definition: playlist-new.c:774
void playlist_sort_by_scheme(int playlist, int scheme)
void playlist_sort_selected_by_filename(int playlist_num, int(*compare)(const char *a, const char *b))
int(* PlaylistTupleCompareFunc)(const Tuple *a, const Tuple *b)
Definition: playlist.h:63
static float a[EQ_BANDS][2]
Definition: equalizer.c:55
void load_playlists(void)
static void load_playlists_real(void)
const char filename
Definition: misc-api.h:85
static void state_cb(void *data, void *user)
playlist
Definition: playlist-api.h:122
void playlist_sort_selected_by_title(int playlist_num, int(*compare)(const char *a, const char *b))
const char PluginHandle decoder const char PluginHandle decoder const char PluginHandle decoder void const PreferencesWidget int
Definition: misc-api.h:103
bool_t playlist_entry_get_selected(int playlist_num, int entry_num)
static float b[EQ_BANDS][2]
Definition: equalizer.c:56
int playlist_entry_count(int playlist_num)
Definition: playlist-new.c:986
void playlist_entry_set_selected(int playlist_num, int entry_num, bool_t selected)
int(* PlaylistStringCompareFunc)(const char *a, const char *b)
Definition: playlist.h:62
void playlist_remove_failed(int playlist)
File name part of the location URI.
Definition: tuple.h:48
EXPORT void hook_associate(const char *name, HookFunction func, void *user)
Definition: hook.c:36
#define FALSE
Definition: core.h:35
EXPORT int string_compare(const char *ap, const char *bp)
Definition: audstrings.c:282
static int tuple_compare_artist(const Tuple *a, const Tuple *b)
void playlist_sort_by_title(int playlist_num, int(*compare)(const char *a, const char *b))
Index Index bool_t
Definition: playlist-api.h:122
#define hook_dissociate(n, f)
Definition: hook.h:34
static const PlaylistStringCompareFunc filename_comparisons[]
bool_t playlist_insert_playlist_raw(int list, int at, const char *filename)
static bool_t hooks_added
void playlist_load_state(void)
Tuple * playlist_entry_get_tuple(int playlist_num, int entry_num, bool_t fast)
#define NULL
Definition: core.h:27
int playlist_count(void)
Definition: playlist-new.c:688
static int tuple_compare_length(const Tuple *a, const Tuple *b)
void playlist_select_all(int playlist_num, bool_t selected)
void playlist_set_active(int playlist_num)
Definition: playlist-new.c:843
EXPORT void tuple_unref(Tuple *tuple)
Definition: tuple.c:284
static GError * error
Definition: audctrl.c:30
EXPORT int string_compare_encoded(const char *ap, const char *bp)
Definition: audstrings.c:326
static int tuple_compare_album(const Tuple *a, const Tuple *b)
void save_playlists(bool_t exiting)
#define TRUE
Definition: core.h:37
static const PlaylistTupleCompareFunc tuple_comparisons[]
void playlist_save_state(void)
char * playlist_entry_get_filename(int playlist_num, int entry_num)
bool_t playlist_get_modified(int playlist_num)
Definition: playlist-new.c:836
void playlist_sort_selected_by_scheme(int playlist, int scheme)
void str_unref(char *str)
Definition: strpool.c:89
static int tuple_compare_track(const Tuple *a, const Tuple *b)
void playlist_sort_by_tuple(int playlist_num, int(*compare)(const Tuple *a, const Tuple *b))
const char * get_path(int id)
Definition: main.c:225
static bool_t state_changed
EXPORT char * tuple_get_str(const Tuple *tuple, int nfield, const char *field)
Definition: tuple.c:478
void playlist_set_modified(int playlist_num, bool_t modified)
Definition: playlist-new.c:829
static int filename_compare_basename(const char *a, const char *b)
static const char * get_basename(const char *filename)
EXPORT TupleValueType tuple_get_value_type(const Tuple *tuple, int nfield, const char *field)
Returns TupleValueType of given #Tuple field.
Definition: tuple.c:459
static const PlaylistStringCompareFunc title_comparisons[]
static int tuple_compare_string(const Tuple *a, const Tuple *b, int field)
static void save_playlists_real(void)
static void update_cb(void *data, void *user)
Song title.
Definition: tuple.h:36
Track length in milliseconds.
Definition: tuple.h:42
EXPORT char * filename_to_uri(const char *name)
Definition: audstrings.c:143
void playlist_remove_duplicates_by_scheme(int playlist, int scheme)
static int tuple_compare_int(const Tuple *a, const Tuple *b, int field)
void playlist_delete_selected(int playlist_num)
void playlist_insert(int at)
Definition: playlist-new.c:709
static int tuple_compare_date(const Tuple *a, const Tuple *b)
void playlist_sort_selected_by_tuple(int playlist_num, int(*compare)(const Tuple *a, const Tuple *b))
bool_t playlist_save(int list, const char *filename)
Album name.
Definition: tuple.h:37
void playlist_sort_by_filename(int playlist_num, int(*compare)(const char *a, const char *b))
Year of production/performance/etc.
Definition: tuple.h:43
Index Index play entry
Definition: playlist-api.h:144
EXPORT bool_t vfs_file_test(const char *path, int test)
Wrapper for g_file_test().
Definition: vfs.c:406
static int tuple_compare_title(const Tuple *a, const Tuple *b)
EXPORT int tuple_get_int(const Tuple *tuple, int nfield, const char *field)
Returns integer associated to #Tuple field.
Definition: tuple.c:509