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