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 <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 }