Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
adder.c
Go to the documentation of this file.
00001 /*
00002  * adder.c
00003  * Copyright 2011 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 <dirent.h>
00023 #include <sys/stat.h>
00024 
00025 #include <gtk/gtk.h>
00026 
00027 #include <libaudcore/audstrings.h>
00028 
00029 #include "audconfig.h"
00030 #include "config.h"
00031 #include "i18n.h"
00032 #include "playback.h"
00033 #include "playlist.h"
00034 #include "plugins.h"
00035 #include "misc.h"
00036 
00037 typedef struct {
00038     gint playlist_id, at;
00039     gboolean play;
00040     struct index * filenames, * tuples;
00041 } AddTask;
00042 
00043 typedef struct {
00044     gint playlist_id, at;
00045     gboolean play;
00046     struct index * filenames, * tuples, * decoders;
00047 } AddResult;
00048 
00049 static GList * add_tasks = NULL;
00050 static GList * add_results = NULL;
00051 
00052 static GMutex * mutex;
00053 static GCond * cond;
00054 static gboolean add_quit;
00055 static GThread * add_thread;
00056 static gint add_source = 0;
00057 
00058 static gint status_source = 0;
00059 static gchar status_path[512];
00060 static gint status_count;
00061 static GtkWidget * status_window = NULL, * status_path_label,
00062  * status_count_label;
00063 
00064 static gboolean status_cb (void * unused)
00065 {
00066     if (! status_window)
00067     {
00068         status_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
00069         gtk_window_set_type_hint ((GtkWindow *) status_window,
00070          GDK_WINDOW_TYPE_HINT_DIALOG);
00071         gtk_window_set_title ((GtkWindow *) status_window, _("Searching ..."));
00072         gtk_window_set_resizable ((GtkWindow *) status_window, FALSE);
00073         gtk_container_set_border_width ((GtkContainer *) status_window, 6);
00074 
00075         GtkWidget * vbox = gtk_vbox_new (FALSE, 6);
00076         gtk_container_add ((GtkContainer *) status_window, vbox);
00077 
00078         status_path_label = gtk_label_new (NULL);
00079         gtk_widget_set_size_request (status_path_label, 320, -1);
00080         gtk_label_set_ellipsize ((GtkLabel *) status_path_label,
00081          PANGO_ELLIPSIZE_MIDDLE);
00082         gtk_box_pack_start ((GtkBox *) vbox, status_path_label, FALSE, FALSE, 0);
00083 
00084         status_count_label = gtk_label_new (NULL);
00085         gtk_widget_set_size_request (status_count_label, 320, -1);
00086         gtk_box_pack_start ((GtkBox *) vbox, status_count_label, FALSE, FALSE, 0);
00087 
00088         gtk_widget_show_all (status_window);
00089 
00090         g_signal_connect (status_window, "destroy", (GCallback)
00091          gtk_widget_destroyed, & status_window);
00092     }
00093 
00094     g_mutex_lock (mutex);
00095 
00096     gtk_label_set_text ((GtkLabel *) status_path_label, status_path);
00097 
00098     gchar scratch[128];
00099     snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found",
00100      "%d files found", status_count), status_count);
00101     gtk_label_set_text ((GtkLabel *) status_count_label, scratch);
00102 
00103     g_mutex_unlock (mutex);
00104     return TRUE;
00105 }
00106 
00107 static void status_update (const gchar * filename, gint found)
00108 {
00109     g_mutex_lock (mutex);
00110 
00111     snprintf (status_path, sizeof status_path, "%s", filename);
00112     status_count = found;
00113 
00114     if (! status_source)
00115         status_source = g_timeout_add (250, status_cb, NULL);
00116 
00117     g_mutex_unlock (mutex);
00118 }
00119 
00120 static void status_done_locked (void)
00121 {
00122     if (status_source)
00123     {
00124         g_source_remove (status_source);
00125         status_source = 0;
00126     }
00127 
00128     if (status_window)
00129         gtk_widget_destroy (status_window);
00130 }
00131 
00132 static void index_free_filenames (struct index * filenames)
00133 {
00134     gint count = index_count (filenames);
00135     for (gint i = 0; i < count; i ++)
00136     {
00137         gchar * filename = index_get (filenames, i);
00138         if (filename)
00139             g_free (filename);
00140     }
00141 
00142     index_free (filenames);
00143 }
00144 
00145 static void index_free_tuples (struct index * tuples)
00146 {
00147     gint count = index_count (tuples);
00148     for (gint i = 0; i < count; i ++)
00149     {
00150         Tuple * tuple = index_get (tuples, i);
00151         if (tuple)
00152             tuple_free (tuple);
00153     }
00154 
00155     index_free (tuples);
00156 }
00157 
00158 static AddTask * add_task_new (gint playlist_id, gint at, gboolean play,
00159  struct index * filenames, struct index * tuples)
00160 {
00161     AddTask * task = g_malloc (sizeof (AddTask));
00162     task->playlist_id = playlist_id;
00163     task->at = at;
00164     task->play = play;
00165     task->filenames = filenames;
00166     task->tuples = tuples;
00167     return task;
00168 }
00169 
00170 static void add_task_free (AddTask * task)
00171 {
00172     if (task->filenames)
00173         index_free_filenames (task->filenames);
00174     if (task->tuples)
00175         index_free_tuples (task->tuples);
00176 
00177     g_free (task);
00178 }
00179 
00180 static AddResult * add_result_new (gint playlist_id, gint at, gboolean play)
00181 {
00182     AddResult * result = g_malloc (sizeof (AddResult));
00183     result->playlist_id = playlist_id;
00184     result->at = at;
00185     result->play = play;
00186     result->filenames = index_new ();
00187     result->tuples = index_new ();
00188     result->decoders = index_new ();
00189     return result;
00190 }
00191 
00192 static void add_result_free (AddResult * result)
00193 {
00194     if (result->filenames)
00195         index_free_filenames (result->filenames);
00196     if (result->tuples)
00197         index_free_tuples (result->tuples);
00198     if (result->decoders)
00199         index_free (result->decoders);
00200 
00201     g_free (result);
00202 }
00203 
00204 static void add_file (gchar * filename, Tuple * tuple, PluginHandle * decoder,
00205  AddResult * result, gboolean filter)
00206 {
00207     g_return_if_fail (filename);
00208     status_update (filename, index_count (result->filenames));
00209 
00210     if (! tuple && ! decoder)
00211     {
00212         decoder = file_find_decoder (filename, TRUE);
00213         if (filter && ! decoder)
00214         {
00215             g_free (filename);
00216             return;
00217         }
00218     }
00219 
00220     if (! tuple && decoder && input_plugin_has_subtunes (decoder) && ! strchr
00221      (filename, '?'))
00222         tuple = file_read_tuple (filename, decoder);
00223 
00224     if (tuple && tuple->nsubtunes > 0)
00225     {
00226         for (gint sub = 0; sub < tuple->nsubtunes; sub ++)
00227         {
00228             gchar * subname = g_strdup_printf ("%s?%d", filename, tuple->subtunes ?
00229              tuple->subtunes[sub] : 1 + sub);
00230             add_file (subname, NULL, decoder, result, FALSE);
00231         }
00232 
00233         g_free (filename);
00234         tuple_free (tuple);
00235         return;
00236     }
00237 
00238     index_append (result->filenames, filename);
00239     index_append (result->tuples, tuple);
00240     index_append (result->decoders, decoder);
00241 }
00242 
00243 static void add_folder (gchar * filename, AddResult * result)
00244 {
00245     g_return_if_fail (filename);
00246     status_update (filename, index_count (result->filenames));
00247 
00248     gchar * unix_name = uri_to_filename (filename);
00249     g_return_if_fail (unix_name);
00250     if (unix_name[strlen (unix_name) - 1] == '/')
00251         unix_name[strlen (unix_name) - 1] = 0;
00252 
00253     GList * files = NULL;
00254     DIR * folder = opendir (unix_name);
00255     if (! folder)
00256         goto FREE;
00257 
00258     struct dirent * entry;
00259     while ((entry = readdir (folder)))
00260     {
00261         if (entry->d_name[0] != '.')
00262             files = g_list_prepend (files, g_strdup_printf ("%s"
00263              G_DIR_SEPARATOR_S "%s", unix_name, entry->d_name));
00264     }
00265 
00266     closedir (folder);
00267     files = g_list_sort (files, (GCompareFunc) string_compare);
00268 
00269     while (files)
00270     {
00271         struct stat info;
00272         if (stat (files->data, & info) < 0)
00273             goto NEXT;
00274 
00275         if (S_ISREG (info.st_mode))
00276         {
00277             gchar * item_name = filename_to_uri (files->data);
00278             add_file (item_name, NULL, NULL, result, TRUE);
00279         }
00280         else if (S_ISDIR (info.st_mode))
00281         {
00282             gchar * item_name = filename_to_uri (files->data);
00283             add_folder (item_name, result);
00284         }
00285 
00286     NEXT:
00287         g_free (files->data);
00288         files = g_list_delete_link (files, files);
00289     }
00290 
00291 FREE:
00292     g_free (filename);
00293     g_free (unix_name);
00294 }
00295 
00296 static void add_playlist (gchar * filename, AddResult * result)
00297 {
00298     g_return_if_fail (filename);
00299     status_update (filename, index_count (result->filenames));
00300 
00301     gchar * title = NULL;
00302     struct index * filenames, * tuples;
00303     if (! playlist_load (filename, & title, & filenames, & tuples))
00304         return;
00305 
00306     gint count = index_count (filenames);
00307     for (gint i = 0; i < count; i ++)
00308         add_file (index_get (filenames, i), tuples ? index_get (tuples, i) :
00309          NULL, NULL, result, FALSE);
00310 
00311     g_free (title);
00312     index_free (filenames);
00313     if (tuples)
00314         index_free (tuples);
00315 }
00316 
00317 static void add_generic (gchar * filename, Tuple * tuple, AddResult * result,
00318  gboolean filter)
00319 {
00320     g_return_if_fail (filename);
00321 
00322     if (tuple)
00323         add_file (filename, tuple, NULL, result, filter);
00324     else if (vfs_file_test (filename, G_FILE_TEST_IS_DIR))
00325         add_folder (filename, result);
00326     else if (filename_is_playlist (filename))
00327         add_playlist (filename, result);
00328     else
00329         add_file (filename, NULL, NULL, result, filter);
00330 }
00331 
00332 static gboolean add_finish (void * unused)
00333 {
00334     g_mutex_lock (mutex);
00335 
00336     while (add_results)
00337     {
00338         AddResult * result = add_results->data;
00339         add_results = g_list_delete_link (add_results, add_results);
00340 
00341         gint playlist = playlist_by_unique_id (result->playlist_id);
00342         if (playlist < 0) /* playlist deleted */
00343             goto FREE;
00344 
00345         gint count = playlist_entry_count (playlist);
00346         if (result->at < 0 || result->at > count)
00347             result->at = count;
00348 
00349         playlist_entry_insert_batch_raw (playlist, result->at,
00350          result->filenames, result->tuples, result->decoders);
00351         result->filenames = NULL;
00352         result->tuples = NULL;
00353         result->decoders = NULL;
00354 
00355         if (result->play && playlist_entry_count (playlist) > count)
00356         {
00357             playlist_set_playing (playlist);
00358             if (! cfg.shuffle)
00359                 playlist_set_position (playlist, result->at);
00360 
00361             playback_play (0, FALSE);
00362         }
00363 
00364     FREE:
00365         add_result_free (result);
00366     }
00367 
00368     if (add_source)
00369     {
00370         g_source_remove (add_source);
00371         add_source = 0;
00372     }
00373 
00374     if (! add_tasks)
00375         status_done_locked ();
00376 
00377     g_mutex_unlock (mutex);
00378     return FALSE;
00379 }
00380 
00381 static void * add_worker (void * unused)
00382 {
00383     g_mutex_lock (mutex);
00384     g_cond_broadcast (cond);
00385 
00386     while (! add_quit)
00387     {
00388         if (! add_tasks)
00389         {
00390             g_cond_wait (cond, mutex);
00391             continue;
00392         }
00393 
00394         AddTask * task = add_tasks->data;
00395         add_tasks = g_list_delete_link (add_tasks, add_tasks);
00396 
00397         g_mutex_unlock (mutex);
00398 
00399         AddResult * result = add_result_new (task->playlist_id, task->at,
00400          task->play);
00401 
00402         gint count = index_count (task->filenames);
00403         if (task->tuples)
00404             count = MIN (count, index_count (task->tuples));
00405 
00406         for (gint i = 0; i < count; i ++)
00407         {
00408             add_generic (index_get (task->filenames, i), task->tuples ?
00409              index_get (task->tuples, i) : NULL, result, FALSE);
00410             index_set (task->filenames, i, NULL);
00411             if (task->tuples)
00412                 index_set (task->tuples, i, NULL);
00413         }
00414 
00415         add_task_free (task);
00416 
00417         g_mutex_lock (mutex);
00418 
00419         add_results = g_list_append (add_results, result);
00420 
00421         if (! add_source)
00422             add_source = g_timeout_add (0, add_finish, NULL);
00423     }
00424 
00425     g_mutex_unlock (mutex);
00426     return NULL;
00427 }
00428 
00429 void adder_init (void)
00430 {
00431     mutex = g_mutex_new ();
00432     cond = g_cond_new ();
00433     g_mutex_lock (mutex);
00434     add_quit = FALSE;
00435     add_thread = g_thread_create (add_worker, NULL, TRUE, NULL);
00436     g_cond_wait (cond, mutex);
00437     g_mutex_unlock (mutex);
00438 }
00439 
00440 void adder_cleanup (void)
00441 {
00442     g_mutex_lock (mutex);
00443     add_quit = TRUE;
00444     g_cond_broadcast (cond);
00445     g_mutex_unlock (mutex);
00446     g_thread_join (add_thread);
00447     g_mutex_free (mutex);
00448     g_cond_free (cond);
00449 
00450     if (add_source)
00451     {
00452         g_source_remove (add_source);
00453         add_source = 0;
00454     }
00455 
00456     status_done_locked ();
00457 }
00458 
00459 void playlist_entry_insert (gint playlist, gint at, gchar * filename,
00460  Tuple * tuple, gboolean play)
00461 {
00462     struct index * filenames = index_new ();
00463     struct index * tuples = index_new ();
00464     index_append (filenames, filename);
00465     index_append (tuples, tuple);
00466 
00467     playlist_entry_insert_batch (playlist, at, filenames, tuples, play);
00468 }
00469 
00470 void playlist_entry_insert_batch (gint playlist, gint at,
00471  struct index * filenames, struct index * tuples, gboolean play)
00472 {
00473     gint playlist_id = playlist_get_unique_id (playlist);
00474     g_return_if_fail (playlist_id >= 0);
00475 
00476     AddTask * task = add_task_new (playlist_id, at, play, filenames, tuples);
00477 
00478     g_mutex_lock (mutex);
00479     add_tasks = g_list_append (add_tasks, task);
00480     g_cond_broadcast (cond);
00481     g_mutex_unlock (mutex);
00482 }