00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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)
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 }