1 /*
2 * Copyright (C) 2004,2005 Marc Pavot <marc.pavot@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 */
19
20 #include "shell/ario-shell-coverdownloader.h"
21 #include <gtk/gtk.h>
22 #include <string.h>
23 #include <glib/gi18n.h>
24
25 #include "ario-debug.h"
26 #include "covers/ario-cover.h"
27 #include "covers/ario-cover-handler.h"
28 #include "covers/ario-cover-manager.h"
29 #include "lib/gtk-builder-helpers.h"
30 #include "servers/ario-server.h"
31
32 static void ario_shell_coverdownloader_finalize (GObject *object);
33 static GObject * ario_shell_coverdownloader_constructor (GType type, guint n_construct_properties,
34 GObjectConstructParam *construct_properties);
35 static gboolean ario_shell_coverdownloader_window_delete_cb (GtkWidget *window,
36 GdkEventAny *event,
37 ArioShellCoverdownloader *ario_shell_coverdownloader);
38 static void ario_shell_coverdownloader_get_cover (ArioShellCoverdownloader *ario_shell_coverdownloader,
39 const char *artist,
40 const char *album,
41 const char *path);
42 static void ario_shell_coverdownloader_close_cb (GtkButton *button,
43 ArioShellCoverdownloader *ario_shell_coverdownloader);
44 static void ario_shell_coverdownloader_cancel_cb (GtkButton *button,
45 ArioShellCoverdownloader *ario_shell_coverdownloader);
46 static void ario_shell_coverdownloader_get_cover_from_album (ArioShellCoverdownloader *ario_shell_coverdownloader,
47 const ArioServerAlbum *server_album,
48 const ArioShellCoverdownloaderOperation operation);
49
50 struct ArioShellCoverdownloaderPrivate
51 {
52 int nb_covers;
53 int nb_covers_already_exist;
54 int nb_covers_found;
55 int nb_covers_not_found;
56
57 gboolean cancelled;
58
59 GtkWidget *progress_artist_label;
60 GtkWidget *progress_album_label;
61 GtkWidget *progress_hbox;
62 GtkWidget *progress_const_artist_label;
63 GtkWidget *progressbar;
64 GtkWidget *cancel_button;
65 GtkWidget *close_button;
66
67 GSList *albums;
68 ArioShellCoverdownloaderOperation operation;
69
70 GThread *thread;
71 };
72
73 static gboolean is_instantiated = FALSE;
74
75 #define ARIO_SHELL_COVERDOWNLOADER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ARIO_SHELL_COVERDOWNLOADER, ArioShellCoverdownloaderPrivate))
G_DEFINE_TYPE(ArioShellCoverdownloader,ario_shell_coverdownloader,GTK_TYPE_WINDOW)76 G_DEFINE_TYPE (ArioShellCoverdownloader, ario_shell_coverdownloader, GTK_TYPE_WINDOW)
77
78 static void
79 ario_shell_coverdownloader_class_init (ArioShellCoverdownloaderClass *klass)
80 {
81 ARIO_LOG_FUNCTION_START;
82 GObjectClass *object_class = G_OBJECT_CLASS (klass);
83
84 /* Virtual methods */
85 object_class->finalize = ario_shell_coverdownloader_finalize;
86 object_class->constructor = ario_shell_coverdownloader_constructor;
87
88 /* Private attributes */
89 g_type_class_add_private (klass, sizeof (ArioShellCoverdownloaderPrivate));
90 }
91
92 static void
ario_shell_coverdownloader_init(ArioShellCoverdownloader * ario_shell_coverdownloader)93 ario_shell_coverdownloader_init (ArioShellCoverdownloader *ario_shell_coverdownloader)
94 {
95 ARIO_LOG_FUNCTION_START;
96 ario_shell_coverdownloader->priv = ARIO_SHELL_COVERDOWNLOADER_GET_PRIVATE (ario_shell_coverdownloader);
97 ario_shell_coverdownloader->priv->cancelled = FALSE;
98 }
99
100 static void
ario_shell_coverdownloader_finalize(GObject * object)101 ario_shell_coverdownloader_finalize (GObject *object)
102 {
103 ARIO_LOG_FUNCTION_START;
104 ArioShellCoverdownloader *ario_shell_coverdownloader;
105
106 g_return_if_fail (object != NULL);
107 g_return_if_fail (IS_ARIO_SHELL_COVERDOWNLOADER (object));
108
109 ario_shell_coverdownloader = ARIO_SHELL_COVERDOWNLOADER (object);
110
111 g_return_if_fail (ario_shell_coverdownloader->priv != NULL);
112
113 /* Wait for thread to finish */
114 if (ario_shell_coverdownloader->priv->thread)
115 g_thread_join (ario_shell_coverdownloader->priv->thread);
116
117 /* We free the list */
118 g_slist_foreach (ario_shell_coverdownloader->priv->albums, (GFunc) ario_server_free_album, NULL);
119 g_slist_free (ario_shell_coverdownloader->priv->albums);
120
121 is_instantiated = FALSE;
122
123 G_OBJECT_CLASS (ario_shell_coverdownloader_parent_class)->finalize (object);
124 }
125
126 GtkWidget *
ario_shell_coverdownloader_new(void)127 ario_shell_coverdownloader_new (void)
128 {
129 ARIO_LOG_FUNCTION_START;
130 ArioShellCoverdownloader *ario_shell_coverdownloader;
131
132 /* Ensure that only one coverdownloader is instantiated at the same time */
133 if (is_instantiated)
134 return NULL;
135 else
136 is_instantiated = TRUE;
137
138 ario_shell_coverdownloader = g_object_new (TYPE_ARIO_SHELL_COVERDOWNLOADER,
139 NULL);
140
141 g_return_val_if_fail (ario_shell_coverdownloader->priv != NULL, NULL);
142
143 return GTK_WIDGET (ario_shell_coverdownloader);
144 }
145
146 static GObject *
ario_shell_coverdownloader_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)147 ario_shell_coverdownloader_constructor (GType type, guint n_construct_properties,
148 GObjectConstructParam *construct_properties)
149 {
150 ARIO_LOG_FUNCTION_START;
151 ArioShellCoverdownloader *ario_shell_coverdownloader;
152 ArioShellCoverdownloaderClass *klass;
153 GObjectClass *parent_class;
154 GtkBuilder *builder;
155 GtkWidget *vbox;
156
157 /* Call parent constructor */
158 klass = ARIO_SHELL_COVERDOWNLOADER_CLASS (g_type_class_peek (TYPE_ARIO_SHELL_COVERDOWNLOADER));
159 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
160 ario_shell_coverdownloader = ARIO_SHELL_COVERDOWNLOADER (parent_class->constructor (type, n_construct_properties,
161 construct_properties));
162
163 /* Create UI using GtkBuilder */
164 builder = gtk_builder_helpers_new (UI_PATH "cover-progress.ui",
165 NULL);
166
167 /* Get pointers to various widgets */
168 vbox = GTK_WIDGET (gtk_builder_get_object (builder, "vbox"));
169 ario_shell_coverdownloader->priv->progress_artist_label =
170 GTK_WIDGET (gtk_builder_get_object (builder, "artist_label"));
171 ario_shell_coverdownloader->priv->progress_album_label =
172 GTK_WIDGET (gtk_builder_get_object (builder, "album_label"));
173 ario_shell_coverdownloader->priv->progressbar =
174 GTK_WIDGET (gtk_builder_get_object (builder, "progressbar"));
175 ario_shell_coverdownloader->priv->progress_hbox =
176 GTK_WIDGET (gtk_builder_get_object (builder, "hbox2"));
177 ario_shell_coverdownloader->priv->progress_const_artist_label =
178 GTK_WIDGET (gtk_builder_get_object (builder, "const_artist_label"));
179 ario_shell_coverdownloader->priv->cancel_button =
180 GTK_WIDGET (gtk_builder_get_object (builder, "cancel_button"));
181 ario_shell_coverdownloader->priv->close_button =
182 GTK_WIDGET (gtk_builder_get_object (builder, "close_button"));
183
184 gtk_builder_helpers_boldify_label (builder, "operation_label");
185
186 /* Set window properties */
187 gtk_window_set_title (GTK_WINDOW (ario_shell_coverdownloader), _("Music Player Cover Download"));
188 gtk_container_add (GTK_CONTAINER (ario_shell_coverdownloader),
189 vbox);
190 gtk_window_set_position (GTK_WINDOW (ario_shell_coverdownloader),
191 GTK_WIN_POS_CENTER);
192
193 /* Connect signals for user actions */
194 g_signal_connect (ario_shell_coverdownloader->priv->cancel_button,
195 "clicked",
196 G_CALLBACK (ario_shell_coverdownloader_cancel_cb),
197 ario_shell_coverdownloader);
198
199 g_signal_connect (ario_shell_coverdownloader->priv->close_button,
200 "clicked",
201 G_CALLBACK (ario_shell_coverdownloader_close_cb),
202 ario_shell_coverdownloader);
203
204 g_signal_connect (ario_shell_coverdownloader,
205 "delete_event",
206 G_CALLBACK (ario_shell_coverdownloader_window_delete_cb),
207 ario_shell_coverdownloader);
208
209 g_object_unref (builder);
210
211 return G_OBJECT (ario_shell_coverdownloader);
212 }
213
214 static void
ario_shell_coverdownloader_close_cb(GtkButton * button,ArioShellCoverdownloader * ario_shell_coverdownloader)215 ario_shell_coverdownloader_close_cb (GtkButton *button,
216 ArioShellCoverdownloader *ario_shell_coverdownloader)
217 {
218 ARIO_LOG_FUNCTION_START;
219 /* Close button pressed : we close and destroy the window */
220 ario_shell_coverdownloader->priv->cancelled = TRUE;
221 gtk_widget_hide (GTK_WIDGET (ario_shell_coverdownloader));
222 gtk_widget_destroy (GTK_WIDGET (ario_shell_coverdownloader));
223 }
224
225 static void
ario_shell_coverdownloader_cancel_cb(GtkButton * button,ArioShellCoverdownloader * ario_shell_coverdownloader)226 ario_shell_coverdownloader_cancel_cb (GtkButton *button,
227 ArioShellCoverdownloader *ario_shell_coverdownloader)
228 {
229 ARIO_LOG_FUNCTION_START;
230 /* Cancel button pressed : we wait until the end of the current download and we stop the search */
231 ario_shell_coverdownloader->priv->cancelled = TRUE;
232 }
233
234 static gboolean
ario_shell_coverdownloader_window_delete_cb(GtkWidget * window,GdkEventAny * event,ArioShellCoverdownloader * ario_shell_coverdownloader)235 ario_shell_coverdownloader_window_delete_cb (GtkWidget *window,
236 GdkEventAny *event,
237 ArioShellCoverdownloader *ario_shell_coverdownloader)
238 {
239 ARIO_LOG_FUNCTION_START;
240 if (!ario_shell_coverdownloader->priv->cancelled) {
241 /* Window destroyed for the first time : we wait until the end of the current download and we stop the search */
242 ario_shell_coverdownloader->priv->cancelled = TRUE;
243 } else {
244 /* Window destroyed for the second time : we close and destroy the window */
245 gtk_widget_hide (GTK_WIDGET (ario_shell_coverdownloader));
246 gtk_widget_destroy (GTK_WIDGET (ario_shell_coverdownloader));
247 }
248
249 return TRUE;
250 }
251
252 static gboolean
ario_shell_coverdownloader_refresh(gpointer data)253 ario_shell_coverdownloader_refresh (gpointer data)
254 {
255 /* Wait for UI refresh */
256 while (gtk_events_pending ())
257 gtk_main_iteration ();
258
259 return FALSE;
260 }
261
262 static gboolean
ario_shell_coverdownloader_progress_start(ArioShellCoverdownloader * ario_shell_coverdownloader)263 ario_shell_coverdownloader_progress_start (ArioShellCoverdownloader *ario_shell_coverdownloader)
264 {
265 ARIO_LOG_FUNCTION_START;
266 gtk_window_resize (GTK_WINDOW (ario_shell_coverdownloader), 350, 150);
267
268 gtk_window_set_resizable (GTK_WINDOW (ario_shell_coverdownloader),
269 FALSE);
270
271 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (ario_shell_coverdownloader->priv->progressbar), 0);
272 gtk_widget_show_all (GTK_WIDGET (ario_shell_coverdownloader));
273
274 /* We only want the cancel button at beginning, not the close button */
275 gtk_widget_hide (ario_shell_coverdownloader->priv->close_button);
276
277 /* We refresh the window */
278 ario_shell_coverdownloader_refresh (NULL);
279
280 return FALSE;
281 }
282
283 typedef struct
284 {
285 ArioShellCoverdownloader *ario_shell_coverdownloader;
286 gchar *artist;
287 gchar *album;
288 } ArioShellCoverdownloaderIdleData;
289
290 static gboolean
ario_shell_coverdownloader_progress_update(ArioShellCoverdownloaderIdleData * data)291 ario_shell_coverdownloader_progress_update (ArioShellCoverdownloaderIdleData *data)
292 {
293 ARIO_LOG_FUNCTION_START;
294 /* We have already searched for nb_covers_done covers */
295 gdouble nb_covers_done = (data->ario_shell_coverdownloader->priv->nb_covers_found
296 + data->ario_shell_coverdownloader->priv->nb_covers_not_found
297 + data->ario_shell_coverdownloader->priv->nb_covers_already_exist);
298
299 /* We update the progress bar */
300 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->ario_shell_coverdownloader->priv->progressbar),
301 nb_covers_done / data->ario_shell_coverdownloader->priv->nb_covers);
302
303 /* We update the artist and the album label */
304 gtk_label_set_text (GTK_LABEL (data->ario_shell_coverdownloader->priv->progress_artist_label), data->artist);
305 gtk_label_set_text (GTK_LABEL (data->ario_shell_coverdownloader->priv->progress_album_label), data->album);
306
307 /* We refresh the window */
308 ario_shell_coverdownloader_refresh (NULL);
309
310 g_free (data->artist);
311 g_free (data->album);
312 g_free (data);
313
314 return FALSE;
315 }
316
317 static gboolean
ario_shell_coverdownloader_progress_end(ArioShellCoverdownloader * ario_shell_coverdownloader)318 ario_shell_coverdownloader_progress_end (ArioShellCoverdownloader *ario_shell_coverdownloader)
319 {
320 ARIO_LOG_FUNCTION_START;
321 char *label_text;
322
323 /* We only want the close button at the end, not the cancel button */
324 gtk_widget_hide (ario_shell_coverdownloader->priv->cancel_button);
325 gtk_widget_show (ario_shell_coverdownloader->priv->close_button);
326
327 gtk_label_set_text (GTK_LABEL (ario_shell_coverdownloader->priv->progress_artist_label), "");
328 gtk_label_set_text (GTK_LABEL (ario_shell_coverdownloader->priv->progress_album_label), "");
329 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (ario_shell_coverdownloader->priv->progressbar), 1);
330
331 gtk_label_set_text (GTK_LABEL (ario_shell_coverdownloader->priv->progress_const_artist_label),
332 _("Download Finished!"));
333
334 /* We show the numbers of covers found and not found */
335 label_text = g_strdup_printf (_("%i covers found\n%i covers not found\n%i covers already exist"),
336 ario_shell_coverdownloader->priv->nb_covers_found,
337 ario_shell_coverdownloader->priv->nb_covers_not_found,
338 ario_shell_coverdownloader->priv->nb_covers_already_exist);
339
340 gtk_label_set_text (GTK_LABEL (ario_shell_coverdownloader->priv->progress_const_artist_label),
341 label_text);
342 g_free (label_text);
343
344 gtk_widget_destroy (ario_shell_coverdownloader->priv->progress_hbox);
345 gtk_widget_destroy (ario_shell_coverdownloader->priv->progress_artist_label);
346
347 return FALSE;
348 }
349
350 void
ario_shell_coverdownloader_get_covers(ArioShellCoverdownloader * ario_shell_coverdownloader,const ArioShellCoverdownloaderOperation operation)351 ario_shell_coverdownloader_get_covers (ArioShellCoverdownloader *ario_shell_coverdownloader,
352 const ArioShellCoverdownloaderOperation operation)
353 {
354 ARIO_LOG_FUNCTION_START;
355 GSList *albums;
356
357 /* Get list of all albums of server */
358 albums = ario_server_get_albums (NULL);
359
360 /* Get covers of albums */
361 ario_shell_coverdownloader_get_covers_from_albums (ario_shell_coverdownloader,
362 albums,
363 operation);
364
365 g_slist_foreach (albums, (GFunc) ario_server_free_album, NULL);
366 g_slist_free (albums);
367 }
368
369 static gboolean
ario_shell_coverdownloader_force_reload(gpointer data)370 ario_shell_coverdownloader_force_reload (gpointer data)
371 {
372 ARIO_LOG_FUNCTION_START;
373 ario_cover_handler_force_reload();
374
375 return FALSE;
376 }
377
378 static gpointer
ario_shell_coverdownloader_get_covers_from_albums_thread(ArioShellCoverdownloader * ario_shell_coverdownloader)379 ario_shell_coverdownloader_get_covers_from_albums_thread (ArioShellCoverdownloader *ario_shell_coverdownloader)
380 {
381 ARIO_LOG_FUNCTION_START;
382 GSList *tmp;
383
384 if (!ario_shell_coverdownloader->priv->albums)
385 return NULL;
386
387 /* We show the window with the progress bar */
388 if (ario_shell_coverdownloader->priv->operation == GET_COVERS)
389 g_idle_add ((GSourceFunc) ario_shell_coverdownloader_progress_start, ario_shell_coverdownloader);
390
391 ario_shell_coverdownloader->priv->nb_covers = g_slist_length (ario_shell_coverdownloader->priv->albums);
392
393 /* While there are still covers to search */
394 for (tmp = ario_shell_coverdownloader->priv->albums; tmp; tmp = g_slist_next (tmp)) {
395 /* The user has pressed the "cancel button" or has closed the window : we stop the search */
396 if (ario_shell_coverdownloader->priv->cancelled)
397 break;
398
399 /* We search for a new cover */
400 ario_shell_coverdownloader_get_cover_from_album (ario_shell_coverdownloader,
401 tmp->data,
402 ario_shell_coverdownloader->priv->operation);
403 }
404 /* We change the window to show a close button and infos about the search */
405 if (ario_shell_coverdownloader->priv->operation == GET_COVERS)
406 g_idle_add ((GSourceFunc) ario_shell_coverdownloader_progress_end, ario_shell_coverdownloader);
407 else
408 g_idle_add ((GSourceFunc) gtk_widget_destroy, ario_shell_coverdownloader);
409
410 g_idle_add ((GSourceFunc) ario_shell_coverdownloader_force_reload, NULL);
411
412 return NULL;
413 }
414
415 void
ario_shell_coverdownloader_get_covers_from_albums(ArioShellCoverdownloader * ario_shell_coverdownloader,const GSList * albums,const ArioShellCoverdownloaderOperation operation)416 ario_shell_coverdownloader_get_covers_from_albums (ArioShellCoverdownloader *ario_shell_coverdownloader,
417 const GSList *albums,
418 const ArioShellCoverdownloaderOperation operation)
419 {
420 ARIO_LOG_FUNCTION_START;
421 const GSList *tmp;
422
423 if (!albums)
424 return;
425
426 /* Copy the list of albums */
427 ario_shell_coverdownloader->priv->albums = NULL;
428 for (tmp = albums; tmp; tmp = g_slist_next (tmp)) {
429 ario_shell_coverdownloader->priv->albums = g_slist_append (ario_shell_coverdownloader->priv->albums, ario_server_copy_album (tmp->data));
430 }
431
432 ario_shell_coverdownloader->priv->operation = operation;
433
434 /* Launch thread for cover download */
435 ario_shell_coverdownloader->priv->thread = g_thread_new ("coverdl",
436 (GThreadFunc) ario_shell_coverdownloader_get_covers_from_albums_thread,
437 ario_shell_coverdownloader);
438 }
439
440 static void
ario_shell_coverdownloader_get_cover_from_album(ArioShellCoverdownloader * ario_shell_coverdownloader,const ArioServerAlbum * server_album,const ArioShellCoverdownloaderOperation operation)441 ario_shell_coverdownloader_get_cover_from_album (ArioShellCoverdownloader *ario_shell_coverdownloader,
442 const ArioServerAlbum *server_album,
443 const ArioShellCoverdownloaderOperation operation)
444 {
445 ARIO_LOG_FUNCTION_START;
446 const gchar *artist;
447 const gchar *album;
448 const gchar *path;
449 ArioShellCoverdownloaderIdleData *data;
450
451 if (!server_album)
452 return;
453
454 artist = server_album->artist;
455 album = server_album->album;
456 path = server_album->path;
457
458 if (!album || !artist)
459 return;
460
461 switch (operation) {
462 case GET_COVERS:
463 /* We update the progress bar */
464 data = (ArioShellCoverdownloaderIdleData *) g_malloc0 (sizeof (ArioShellCoverdownloaderIdleData));
465
466 data->ario_shell_coverdownloader = ario_shell_coverdownloader;
467 data->artist = g_strdup (artist);
468 data->album = g_strdup (album);
469 g_idle_add ((GSourceFunc) ario_shell_coverdownloader_progress_update, data);
470
471 if (ario_cover_cover_exists (artist, album))
472 /* The cover already exists, we do nothing */
473 ++ario_shell_coverdownloader->priv->nb_covers_already_exist;
474 else
475 /* We search for the cover */
476 ario_shell_coverdownloader_get_cover (ario_shell_coverdownloader, artist, album, path);
477 break;
478
479 case REMOVE_COVERS:
480 /* We remove the cover from the ~/.config/ario/covers/ directory */
481 ario_cover_remove_cover (artist, album);
482 break;
483
484 default:
485 break;
486 }
487 }
488
489 static void
ario_shell_coverdownloader_get_cover(ArioShellCoverdownloader * ario_shell_coverdownloader,const char * artist,const char * album,const char * path)490 ario_shell_coverdownloader_get_cover (ArioShellCoverdownloader *ario_shell_coverdownloader,
491 const char *artist,
492 const char *album,
493 const char *path)
494 {
495 ARIO_LOG_FUNCTION_START;
496 GArray *size;
497 GSList *data = NULL;
498 gboolean ret;
499
500 size = g_array_new (TRUE, TRUE, sizeof (int));
501
502 /* If a cover is found, it is loaded in data(0) */
503 ret = ario_cover_manager_get_covers (ario_cover_manager_get_instance (),
504 artist,
505 album,
506 path,
507 &size,
508 &data,
509 GET_FIRST_COVER);
510
511 /* If the cover is not too big and not too small (blank image), we save it */
512 if (ret && ario_cover_size_is_valid (g_array_index (size, int, 0))) {
513 ret = ario_cover_save_cover (artist,
514 album,
515 g_slist_nth_data (data, 0),
516 g_array_index (size, int, 0),
517 OVERWRITE_MODE_SKIP);
518
519 if (ret)
520 ++ario_shell_coverdownloader->priv->nb_covers_found;
521 else
522 ++ario_shell_coverdownloader->priv->nb_covers_not_found;
523 } else {
524 ++ario_shell_coverdownloader->priv->nb_covers_not_found;
525 }
526
527 g_array_free (size, TRUE);
528 g_slist_foreach (data, (GFunc) g_free, NULL);
529 g_slist_free (data);
530 }
531