Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 <pthread.h> 00024 #include <string.h> 00025 #include <sys/stat.h> 00026 00027 #include <gtk/gtk.h> 00028 00029 #include <libaudcore/audstrings.h> 00030 #include <libaudcore/hook.h> 00031 00032 #include "config.h" 00033 #include "i18n.h" 00034 #include "playback.h" 00035 #include "playlist.h" 00036 #include "plugins.h" 00037 #include "main.h" 00038 #include "misc.h" 00039 00040 typedef struct { 00041 int playlist_id, at; 00042 bool_t play; 00043 Index * filenames, * tuples; 00044 PlaylistFilterFunc filter; 00045 void * user; 00046 } AddTask; 00047 00048 typedef struct { 00049 int playlist_id, at; 00050 bool_t play; 00051 Index * filenames, * tuples, * decoders; 00052 } AddResult; 00053 00054 static GList * add_tasks = NULL; 00055 static GList * add_results = NULL; 00056 static int current_playlist_id = -1; 00057 00058 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 00059 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 00060 static bool_t add_quit; 00061 static pthread_t add_thread; 00062 static int add_source = 0; 00063 00064 static int status_source = 0; 00065 static char status_path[512]; 00066 static int status_count; 00067 static GtkWidget * status_window = NULL, * status_path_label, 00068 * status_count_label; 00069 00070 static bool_t status_cb (void * unused) 00071 { 00072 if (! headless && ! status_window) 00073 { 00074 status_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 00075 gtk_window_set_type_hint ((GtkWindow *) status_window, 00076 GDK_WINDOW_TYPE_HINT_DIALOG); 00077 gtk_window_set_title ((GtkWindow *) status_window, _("Searching ...")); 00078 gtk_window_set_resizable ((GtkWindow *) status_window, FALSE); 00079 gtk_container_set_border_width ((GtkContainer *) status_window, 6); 00080 00081 GtkWidget * vbox = gtk_vbox_new (FALSE, 6); 00082 gtk_container_add ((GtkContainer *) status_window, vbox); 00083 00084 status_path_label = gtk_label_new (NULL); 00085 #if GTK_CHECK_VERSION (3, 0, 0) 00086 gtk_label_set_width_chars ((GtkLabel *) status_path_label, 40); 00087 gtk_label_set_max_width_chars ((GtkLabel *) status_path_label, 40); 00088 #else 00089 gtk_widget_set_size_request (status_path_label, 320, -1); 00090 #endif 00091 gtk_label_set_ellipsize ((GtkLabel *) status_path_label, 00092 PANGO_ELLIPSIZE_MIDDLE); 00093 gtk_box_pack_start ((GtkBox *) vbox, status_path_label, FALSE, FALSE, 0); 00094 00095 status_count_label = gtk_label_new (NULL); 00096 #if GTK_CHECK_VERSION (3, 0, 0) 00097 gtk_label_set_width_chars ((GtkLabel *) status_count_label, 40); 00098 gtk_label_set_max_width_chars ((GtkLabel *) status_count_label, 40); 00099 #else 00100 gtk_widget_set_size_request (status_count_label, 320, -1); 00101 #endif 00102 gtk_box_pack_start ((GtkBox *) vbox, status_count_label, FALSE, FALSE, 0); 00103 00104 gtk_widget_show_all (status_window); 00105 00106 g_signal_connect (status_window, "destroy", (GCallback) 00107 gtk_widget_destroyed, & status_window); 00108 } 00109 00110 pthread_mutex_lock (& mutex); 00111 00112 char scratch[128]; 00113 snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found", 00114 "%d files found", status_count), status_count); 00115 00116 if (headless) 00117 { 00118 printf ("Searching, %s ...\r", scratch); 00119 fflush (stdout); 00120 } 00121 else 00122 { 00123 gtk_label_set_text ((GtkLabel *) status_path_label, status_path); 00124 gtk_label_set_text ((GtkLabel *) status_count_label, scratch); 00125 } 00126 00127 pthread_mutex_unlock (& mutex); 00128 return TRUE; 00129 } 00130 00131 static void status_update (const char * filename, int found) 00132 { 00133 pthread_mutex_lock (& mutex); 00134 00135 snprintf (status_path, sizeof status_path, "%s", filename); 00136 status_count = found; 00137 00138 if (! status_source) 00139 status_source = g_timeout_add (250, status_cb, NULL); 00140 00141 pthread_mutex_unlock (& mutex); 00142 } 00143 00144 static void status_done_locked (void) 00145 { 00146 if (status_source) 00147 { 00148 g_source_remove (status_source); 00149 status_source = 0; 00150 } 00151 00152 if (headless) 00153 printf ("\n"); 00154 else if (status_window) 00155 gtk_widget_destroy (status_window); 00156 } 00157 00158 static void index_free_filenames (Index * filenames) 00159 { 00160 int count = index_count (filenames); 00161 for (int i = 0; i < count; i ++) 00162 str_unref (index_get (filenames, i)); 00163 00164 index_free (filenames); 00165 } 00166 00167 static void index_free_tuples (Index * tuples) 00168 { 00169 int count = index_count (tuples); 00170 for (int i = 0; i < count; i ++) 00171 { 00172 Tuple * tuple = index_get (tuples, i); 00173 if (tuple) 00174 tuple_unref (tuple); 00175 } 00176 00177 index_free (tuples); 00178 } 00179 00180 static AddTask * add_task_new (int playlist_id, int at, bool_t play, 00181 Index * filenames, Index * tuples, PlaylistFilterFunc filter, 00182 void * user) 00183 { 00184 AddTask * task = g_slice_new (AddTask); 00185 task->playlist_id = playlist_id; 00186 task->at = at; 00187 task->play = play; 00188 task->filenames = filenames; 00189 task->tuples = tuples; 00190 task->filter = filter; 00191 task->user = user; 00192 return task; 00193 } 00194 00195 static void add_task_free (AddTask * task) 00196 { 00197 if (task->filenames) 00198 index_free_filenames (task->filenames); 00199 if (task->tuples) 00200 index_free_tuples (task->tuples); 00201 00202 g_slice_free (AddTask, task); 00203 } 00204 00205 static AddResult * add_result_new (int playlist_id, int at, bool_t play) 00206 { 00207 AddResult * result = g_slice_new (AddResult); 00208 result->playlist_id = playlist_id; 00209 result->at = at; 00210 result->play = play; 00211 result->filenames = index_new (); 00212 result->tuples = index_new (); 00213 result->decoders = index_new (); 00214 return result; 00215 } 00216 00217 static void add_result_free (AddResult * result) 00218 { 00219 if (result->filenames) 00220 index_free_filenames (result->filenames); 00221 if (result->tuples) 00222 index_free_tuples (result->tuples); 00223 if (result->decoders) 00224 index_free (result->decoders); 00225 00226 g_slice_free (AddResult, result); 00227 } 00228 00229 static void add_file (char * filename, Tuple * tuple, PluginHandle * decoder, 00230 PlaylistFilterFunc filter, void * user, AddResult * result, bool_t validate) 00231 { 00232 g_return_if_fail (filename); 00233 if (filter && ! filter (filename, user)) 00234 { 00235 str_unref (filename); 00236 return; 00237 } 00238 00239 status_update (filename, index_count (result->filenames)); 00240 00241 if (! tuple && ! decoder) 00242 { 00243 decoder = file_find_decoder (filename, TRUE); 00244 if (validate && ! decoder) 00245 { 00246 str_unref (filename); 00247 return; 00248 } 00249 } 00250 00251 if (! tuple && decoder && input_plugin_has_subtunes (decoder) && ! strchr 00252 (filename, '?')) 00253 tuple = file_read_tuple (filename, decoder); 00254 00255 int n_subtunes = tuple ? tuple_get_n_subtunes (tuple) : 0; 00256 00257 if (n_subtunes) 00258 { 00259 for (int sub = 0; sub < n_subtunes; sub ++) 00260 { 00261 char * subname = str_printf ("%s?%d", filename, 00262 tuple_get_nth_subtune (tuple, sub)); 00263 add_file (subname, NULL, decoder, filter, user, result, FALSE); 00264 } 00265 00266 str_unref (filename); 00267 tuple_unref (tuple); 00268 return; 00269 } 00270 00271 index_append (result->filenames, filename); 00272 index_append (result->tuples, tuple); 00273 index_append (result->decoders, decoder); 00274 } 00275 00276 static void add_folder (char * filename, PlaylistFilterFunc filter, 00277 void * user, AddResult * result) 00278 { 00279 g_return_if_fail (filename); 00280 if (filter && ! filter (filename, user)) 00281 { 00282 str_unref (filename); 00283 return; 00284 } 00285 00286 status_update (filename, index_count (result->filenames)); 00287 00288 char * unix_name = uri_to_filename (filename); 00289 if (! unix_name) 00290 { 00291 str_unref (filename); 00292 return; 00293 } 00294 00295 if (unix_name[strlen (unix_name) - 1] == '/') 00296 unix_name[strlen (unix_name) - 1] = 0; 00297 00298 GList * files = NULL; 00299 DIR * folder = opendir (unix_name); 00300 if (! folder) 00301 goto FREE; 00302 00303 struct dirent * entry; 00304 while ((entry = readdir (folder))) 00305 { 00306 if (entry->d_name[0] != '.') 00307 files = g_list_prepend (files, g_strdup_printf ("%s" 00308 G_DIR_SEPARATOR_S "%s", unix_name, entry->d_name)); 00309 } 00310 00311 closedir (folder); 00312 files = g_list_sort (files, (GCompareFunc) string_compare); 00313 00314 while (files) 00315 { 00316 struct stat info; 00317 if (stat (files->data, & info) < 0) 00318 goto NEXT; 00319 00320 if (S_ISREG (info.st_mode)) 00321 { 00322 char * item_name = filename_to_uri (files->data); 00323 if (item_name) 00324 { 00325 add_file (str_get (item_name), NULL, NULL, filter, user, result, TRUE); 00326 g_free (item_name); 00327 } 00328 } 00329 else if (S_ISDIR (info.st_mode)) 00330 { 00331 char * item_name = filename_to_uri (files->data); 00332 if (item_name) 00333 { 00334 add_folder (str_get (item_name), filter, user, result); 00335 g_free (item_name); 00336 } 00337 } 00338 00339 NEXT: 00340 g_free (files->data); 00341 files = g_list_delete_link (files, files); 00342 } 00343 00344 FREE: 00345 str_unref (filename); 00346 g_free (unix_name); 00347 } 00348 00349 static void add_playlist (char * filename, PlaylistFilterFunc filter, 00350 void * user, AddResult * result) 00351 { 00352 g_return_if_fail (filename); 00353 if (filter && ! filter (filename, user)) 00354 { 00355 str_unref (filename); 00356 return; 00357 } 00358 00359 status_update (filename, index_count (result->filenames)); 00360 00361 char * title = NULL; 00362 Index * filenames, * tuples; 00363 if (! playlist_load (filename, & title, & filenames, & tuples)) 00364 { 00365 str_unref (filename); 00366 return; 00367 } 00368 00369 int count = index_count (filenames); 00370 for (int i = 0; i < count; i ++) 00371 add_file (index_get (filenames, i), tuples ? index_get (tuples, i) : 00372 NULL, NULL, filter, user, result, FALSE); 00373 00374 str_unref (filename); 00375 str_unref (title); 00376 index_free (filenames); 00377 if (tuples) 00378 index_free (tuples); 00379 } 00380 00381 static void add_generic (char * filename, Tuple * tuple, 00382 PlaylistFilterFunc filter, void * user, AddResult * result) 00383 { 00384 g_return_if_fail (filename); 00385 00386 if (tuple) 00387 add_file (filename, tuple, NULL, filter, user, result, FALSE); 00388 else if (vfs_file_test (filename, G_FILE_TEST_IS_DIR)) 00389 add_folder (filename, filter, user, result); 00390 else if (filename_is_playlist (filename)) 00391 add_playlist (filename, filter, user, result); 00392 else 00393 add_file (filename, NULL, NULL, filter, user, result, FALSE); 00394 } 00395 00396 static bool_t add_finish (void * unused) 00397 { 00398 pthread_mutex_lock (& mutex); 00399 00400 while (add_results) 00401 { 00402 AddResult * result = add_results->data; 00403 add_results = g_list_delete_link (add_results, add_results); 00404 00405 int playlist = playlist_by_unique_id (result->playlist_id); 00406 if (playlist < 0) /* playlist deleted */ 00407 goto FREE; 00408 00409 int count = playlist_entry_count (playlist); 00410 if (result->at < 0 || result->at > count) 00411 result->at = count; 00412 00413 playlist_entry_insert_batch_raw (playlist, result->at, 00414 result->filenames, result->tuples, result->decoders); 00415 result->filenames = NULL; 00416 result->tuples = NULL; 00417 result->decoders = NULL; 00418 00419 if (result->play && playlist_entry_count (playlist) > count) 00420 { 00421 playlist_set_playing (playlist); 00422 if (! get_bool (NULL, "shuffle")) 00423 playlist_set_position (playlist, result->at); 00424 00425 playback_play (0, FALSE); 00426 } 00427 00428 FREE: 00429 add_result_free (result); 00430 } 00431 00432 if (add_source) 00433 { 00434 g_source_remove (add_source); 00435 add_source = 0; 00436 } 00437 00438 if (! add_tasks) 00439 status_done_locked (); 00440 00441 pthread_mutex_unlock (& mutex); 00442 00443 hook_call ("playlist add complete", NULL); 00444 return FALSE; 00445 } 00446 00447 static void * add_worker (void * unused) 00448 { 00449 pthread_mutex_lock (& mutex); 00450 00451 while (! add_quit) 00452 { 00453 if (! add_tasks) 00454 { 00455 pthread_cond_wait (& cond, & mutex); 00456 continue; 00457 } 00458 00459 AddTask * task = add_tasks->data; 00460 add_tasks = g_list_delete_link (add_tasks, add_tasks); 00461 00462 current_playlist_id = task->playlist_id; 00463 pthread_mutex_unlock (& mutex); 00464 00465 AddResult * result = add_result_new (task->playlist_id, task->at, 00466 task->play); 00467 00468 int count = index_count (task->filenames); 00469 if (task->tuples) 00470 count = MIN (count, index_count (task->tuples)); 00471 00472 for (int i = 0; i < count; i ++) 00473 { 00474 add_generic (index_get (task->filenames, i), task->tuples ? 00475 index_get (task->tuples, i) : NULL, task->filter, task->user, 00476 result); 00477 00478 index_set (task->filenames, i, NULL); 00479 if (task->tuples) 00480 index_set (task->tuples, i, NULL); 00481 } 00482 00483 add_task_free (task); 00484 00485 pthread_mutex_lock (& mutex); 00486 current_playlist_id = -1; 00487 00488 add_results = g_list_append (add_results, result); 00489 00490 if (! add_source) 00491 add_source = g_timeout_add (0, add_finish, NULL); 00492 } 00493 00494 pthread_mutex_unlock (& mutex); 00495 return NULL; 00496 } 00497 00498 void adder_init (void) 00499 { 00500 pthread_mutex_lock (& mutex); 00501 add_quit = FALSE; 00502 pthread_create (& add_thread, NULL, add_worker, NULL); 00503 pthread_mutex_unlock (& mutex); 00504 } 00505 00506 void adder_cleanup (void) 00507 { 00508 pthread_mutex_lock (& mutex); 00509 add_quit = TRUE; 00510 pthread_cond_broadcast (& cond); 00511 pthread_mutex_unlock (& mutex); 00512 pthread_join (add_thread, NULL); 00513 00514 if (add_source) 00515 { 00516 g_source_remove (add_source); 00517 add_source = 0; 00518 } 00519 00520 status_done_locked (); 00521 } 00522 00523 void playlist_entry_insert (int playlist, int at, const char * filename, 00524 Tuple * tuple, bool_t play) 00525 { 00526 Index * filenames = index_new (); 00527 Index * tuples = index_new (); 00528 index_append (filenames, str_get (filename)); 00529 index_append (tuples, tuple); 00530 00531 playlist_entry_insert_batch (playlist, at, filenames, tuples, play); 00532 } 00533 00534 void playlist_entry_insert_batch (int playlist, int at, 00535 Index * filenames, Index * tuples, bool_t play) 00536 { 00537 playlist_entry_insert_filtered (playlist, at, filenames, tuples, NULL, NULL, play); 00538 } 00539 00540 void playlist_entry_insert_filtered (int playlist, int at, 00541 Index * filenames, Index * tuples, PlaylistFilterFunc filter, 00542 void * user, bool_t play) 00543 { 00544 int playlist_id = playlist_get_unique_id (playlist); 00545 g_return_if_fail (playlist_id >= 0); 00546 00547 AddTask * task = add_task_new (playlist_id, at, play, filenames, tuples, filter, user); 00548 00549 pthread_mutex_lock (& mutex); 00550 add_tasks = g_list_append (add_tasks, task); 00551 pthread_cond_broadcast (& cond); 00552 pthread_mutex_unlock (& mutex); 00553 } 00554 00555 bool_t playlist_add_in_progress (int playlist) 00556 { 00557 int playlist_id = playlist_get_unique_id (playlist); 00558 g_return_val_if_fail (playlist_id >= 0, FALSE); 00559 00560 pthread_mutex_lock (& mutex); 00561 00562 for (GList * node = add_tasks; node; node = node->next) 00563 { 00564 if (((AddTask *) node->data)->playlist_id == playlist_id) 00565 goto YES; 00566 } 00567 00568 if (current_playlist_id == playlist_id) 00569 goto YES; 00570 00571 for (GList * node = add_results; node; node = node->next) 00572 { 00573 if (((AddResult *) node->data)->playlist_id == playlist_id) 00574 goto YES; 00575 } 00576 00577 pthread_mutex_unlock (& mutex); 00578 return FALSE; 00579 00580 YES: 00581 pthread_mutex_unlock (& mutex); 00582 return TRUE; 00583 }