1 /*
2 * Copyright (C) 2007 Felipe Weckx <felipe.weckx@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (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 GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 #include "gbemol-utils.h"
21 #include "gbemol-mpd.h"
22 #include "gbemol-net.h"
23 #include "gbemol-amazon.h"
24
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30
31 #ifdef ENABLE_ID3_APIC
32 /* ID3 Picture Tag */
33 #include <id3.h>
34 #endif
35
36 #include <gtk/gtk.h>
37 #include <glib/gstdio.h>
38
39 /* Got from brasero (http://perso.wanadoo.fr/bonfire) */
40 GtkWidget *
gbemol_utils_make_button(char * text,char * stock)41 gbemol_utils_make_button (char *text, char *stock)
42 {
43 GtkWidget *button;
44 GtkWidget *box, *lbl;
45
46 box = gtk_hbox_new (FALSE, 6);
47 gtk_box_pack_start (GTK_BOX (box),
48 gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON),
49 FALSE,
50 FALSE,
51 0);
52
53 button = gtk_button_new ();
54 if (text)
55 {
56 lbl = gtk_label_new_with_mnemonic (text);
57 gtk_box_pack_start (GTK_BOX (box),
58 lbl,
59 FALSE,
60 FALSE,
61 0);
62 gtk_label_set_mnemonic_widget (GTK_LABEL (lbl), button);
63 }
64
65 gtk_container_add (GTK_CONTAINER (button), box);
66 gtk_button_set_alignment (GTK_BUTTON (button), 0.5, 0.5);
67
68 return button;
69 }
70
71 /* Same as above, but returns the label as well */
72 GtkWidget *
gbemol_utils_make_button_and_label(char * text,char * stock,GtkWidget ** lbl)73 gbemol_utils_make_button_and_label (char *text, char *stock, GtkWidget** lbl)
74 {
75 GtkWidget *button;
76 GtkWidget *box;
77
78 box = gtk_hbox_new (FALSE, 6);
79 gtk_box_pack_start (GTK_BOX (box),
80 gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON),
81 FALSE,
82 FALSE,
83 0);
84
85 button = gtk_button_new ();
86 if (text)
87 {
88 *lbl = gtk_label_new_with_mnemonic (text);
89 gtk_box_pack_start (GTK_BOX (box),
90 *lbl,
91 FALSE,
92 FALSE,
93 0);
94 gtk_label_set_mnemonic_widget (GTK_LABEL (*lbl), button);
95 }
96
97 gtk_container_add (GTK_CONTAINER (button), box);
98 gtk_button_set_alignment (GTK_BUTTON (button), 0.5, 0.5);
99
100 return button;
101 }
102
gbemol_utils_char_list_free(GList * l)103 void gbemol_utils_char_list_free (GList *l)
104 {
105 if (l)
106 {
107 g_list_foreach (l, (GFunc) g_free, NULL);
108 g_list_free (l);
109 }
110 }
111
gbemol_utils_song_list_free(GList * l)112 void gbemol_utils_song_list_free (GList *l)
113 {
114 if (l)
115 {
116 g_list_foreach (l, (GFunc) gbemol_mpd_free_song, NULL);
117 g_list_free (l);
118 }
119 }
120
121 GList*
gbemol_utils_get_selected(GtkTreeView * tvw)122 gbemol_utils_get_selected (GtkTreeView* tvw)
123 {
124 GtkTreeModel *model;
125 GtkTreeSelection *select;
126 GList *rows = NULL;
127
128 select = gtk_tree_view_get_selection (tvw);
129 if (gtk_tree_selection_count_selected_rows (select) > 0)
130 {
131 rows = gtk_tree_selection_get_selected_rows (select, &model);
132 return rows;
133 }
134 else
135 return NULL;
136 }
137
138 void
gbemol_utils_selected_list_free(GList * l)139 gbemol_utils_selected_list_free (GList* l)
140 {
141 if (l)
142 {
143 g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
144 g_list_free (l);
145 }
146 }
147
148 /*
149 * Config file functions
150 */
gbemol_cfg_create_file()151 gboolean gbemol_cfg_create_file ()
152 {
153 gchar* path;
154 gint fd;
155
156 path = g_strdup_printf ("%s/.gbemol.cfg", g_get_home_dir());
157
158 if ((fd = open (path, O_CREAT)) != -1)
159 {
160 chmod (path, 0644);
161 close (fd);
162 /* Initial configs */
163 gbemol_cfg_set_int ("win_main", "visible", 1);
164 return TRUE;
165 }
166
167 return FALSE;
168 }
169
170 /*
171 * Opens and reads the key file
172 */
gbemol_cfg_load_file()173 GKeyFile* gbemol_cfg_load_file ()
174 {
175 gchar* path, *path2;
176 GKeyFile* file;
177 GError* error = NULL;
178
179 path = g_strdup_printf ("%s/gbemol/gbemol.cfg", g_get_user_config_dir());
180 file = g_key_file_new ();
181
182 if (g_file_test (path, G_FILE_TEST_EXISTS))
183 /* File exists, load it */
184 g_key_file_load_from_file (file, path, G_KEY_FILE_NONE, &error);
185 else
186 {
187 /* File doesn't exist, check if the directory exists */
188 path2 = g_strdup_printf ("%s/gbemol", g_get_user_config_dir());
189 if (!(g_file_test (path2, G_FILE_TEST_EXISTS) && g_file_test (path2, G_FILE_TEST_IS_DIR)))
190 g_mkdir (path2, 0755);
191 g_free (path2);
192 }
193
194 g_free (path);
195
196 return file;
197 }
198
gbemol_cfg_save_file(GKeyFile * file)199 gboolean gbemol_cfg_save_file (GKeyFile* file)
200 {
201 gchar* path, *data;
202 gsize len;
203 FILE* cfg;
204
205 path = g_strdup_printf ("%s/gbemol/gbemol.cfg", g_get_user_config_dir ());
206 data = g_key_file_to_data (file, &len, NULL);
207
208 cfg = fopen (path, "w");
209
210 if (cfg)
211 {
212 fprintf (cfg, data);
213 fclose (cfg);
214 g_free (path);
215 g_free (data);
216 return TRUE;
217 }
218
219 g_free (path);
220 g_free (data);
221 g_key_file_free (file);
222
223 return FALSE;
224 }
225
gbemol_cfg_set_string(gchar * group,gchar * key,const gchar * value)226 void gbemol_cfg_set_string (gchar* group, gchar* key, const gchar* value)
227 {
228 GKeyFile *file;
229
230 file = gbemol_cfg_load_file ();
231 g_key_file_set_string (file, group, key, value);
232 gbemol_cfg_save_file (file);
233 }
234
gbemol_cfg_get_string(gchar * group,gchar * key)235 gchar* gbemol_cfg_get_string (gchar* group, gchar* key)
236 {
237 GKeyFile *file;
238 gchar* str;
239
240 file = gbemol_cfg_load_file ();
241 str = g_key_file_get_string (file, group, key, NULL);
242 g_key_file_free (file);
243
244 return str;
245 }
246
gbemol_cfg_set_int(gchar * group,gchar * key,gint value)247 void gbemol_cfg_set_int (gchar* group, gchar* key, gint value)
248 {
249 GKeyFile *file;
250
251 file = gbemol_cfg_load_file ();
252 g_key_file_set_integer (file, group, key, value);
253 gbemol_cfg_save_file (file);
254 g_key_file_free (file);
255
256 }
257
gbemol_cfg_get_int(gchar * group,gchar * key)258 gint gbemol_cfg_get_int (gchar* group, gchar* key)
259 {
260 GKeyFile *file;
261 gint value;
262
263 file = gbemol_cfg_load_file ();
264 value = g_key_file_get_integer (file, group, key, NULL);
265 g_key_file_free (file);
266
267 return value;
268 }
269
gbemol_cfg_set_bool(gchar * group,gchar * key,gboolean value)270 void gbemol_cfg_set_bool(gchar* group, gchar* key, gboolean value)
271 {
272 GKeyFile *file;
273
274 file = gbemol_cfg_load_file ();
275 g_key_file_set_boolean(file, group, key, value);
276 gbemol_cfg_save_file (file);
277 g_key_file_free (file);
278
279 }
280
gbemol_cfg_get_bool(gchar * group,gchar * key)281 gboolean gbemol_cfg_get_bool(gchar* group, gchar* key)
282 {
283 GKeyFile *file;
284 gboolean value;
285
286 file = gbemol_cfg_load_file ();
287 value = g_key_file_get_boolean(file, group, key, NULL);
288 g_key_file_free (file);
289
290 return value;
291 }
292
gbemol_cfg_get_bool_with_default(gchar * group,gchar * key,gboolean def)293 gboolean gbemol_cfg_get_bool_with_default(gchar* group, gchar* key, gboolean def)
294 {
295 GKeyFile *file;
296 gboolean value;
297 GError* e = NULL;
298
299 file = gbemol_cfg_load_file();
300 value = g_key_file_get_boolean(file, group, key, &e);
301 if ((e != NULL) && (e->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND))
302 value = def;
303 g_key_file_free (file);
304
305 return value;
306 }
307
gbemol_cfg_set_double(gchar * group,gchar * key,gdouble value)308 void gbemol_cfg_set_double (gchar* group, gchar* key, gdouble value)
309 {
310 GKeyFile *file;
311
312 file = gbemol_cfg_load_file ();
313 g_key_file_set_double (file, group, key, value);
314 gbemol_cfg_save_file (file);
315
316 }
317
gbemol_cfg_get_double(gchar * group,gchar * key)318 gdouble gbemol_cfg_get_double (gchar* group, gchar* key)
319 {
320 GKeyFile *file;
321 gdouble value;
322
323 file = gbemol_cfg_load_file ();
324 value = g_key_file_get_double (file, group, key, NULL);
325 g_key_file_free (file);
326
327 return value;
328 }
329
330 #ifdef ENABLE_ID3_APIC
331 /*
332 * Got from EasyTag (http://easytag.sf.net)
333 */
ID3Tag_Link_1(ID3Tag * id3tag,const char * filename)334 ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename)
335 {
336 size_t offset;
337
338 # if (0) // Link the file with the both tags may cause damage to unicode strings
339 //# if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) && (ID3LIB_PATCH >= 1) ) // Same test used in Id3tag_Read_File_Tag to use ID3Tag_HasTagType
340 /* No problem of priority, so we link the file with the both tags
341 * to manage => ID3Tag_HasTagType works correctly */
342 offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1 | ID3TT_ID3V2);
343 # elif ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) )
344 /* Version 3.8.0pre2 gives priority to tag id3v1 instead of id3v2, so we
345 * try to fix it by linking the file with the id3v2 tag first. This bug
346 * was fixed in the final version of 3.8.0 but we can't know it... */
347 /* First, try to get the ID3v2 tags */
348 offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V2);
349 if (offset == 0)
350 {
351 /* No ID3v2 tags available => try to get the ID3v1 tags */
352 offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1);
353 }
354 # else
355 /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */
356 offset = ID3Tag_Link(id3tag,filename);
357 # endif
358 //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_path_get_basename(filename));
359 return offset;
360 }
361
362 #endif
363
gbemol_get_song_image_from_id3(GbemolMpdSong * song)364 GdkPixbuf* gbemol_get_song_image_from_id3 (GbemolMpdSong* song)
365 {
366 GdkPixbuf *pix = NULL;
367 #ifdef ENABLE_ID3_APIC
368 size_t offset;
369 ID3Tag* id3_tag = NULL;
370 ID3Frame* id3_frame;
371 ID3Field* id3_field;
372 gchar *path, *songs_path;
373 GdkPixbufLoader *load;
374 gulong size = 0;
375 guchar *data = NULL;
376
377 if (!(songs_path = gbemol_cfg_get_string ("MPD", "music_dir")))
378 return NULL;
379
380 path = g_strdup_printf ("%s/%s", songs_path, song->file);
381
382 id3_tag = ID3Tag_New();
383 offset = ID3Tag_LinkWithFlags (id3_tag, path, ID3TT_ID3V1 | ID3TT_ID3V2);
384
385 g_free (path);
386 g_free (songs_path);
387
388 if (!offset)
389 return NULL;
390
391 id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_PICTURE);
392 if (!id3_frame)
393 return NULL;
394
395 if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_DATA)) )
396 {
397 size = ID3Field_Size(id3_field);
398 data = g_malloc(size);
399 ID3Field_GetBINARY(id3_field, data, size);
400 }
401
402 load = gdk_pixbuf_loader_new ();
403 if (load)
404 if (gdk_pixbuf_loader_write (load, data, size, 0))
405 pix = gdk_pixbuf_loader_get_pixbuf (load);
406
407 g_free (data);
408 g_free (id3_tag);
409 g_free (id3_frame);
410 g_free (id3_field);
411 #endif
412 return pix;
413 }
414
415
416 /* Returns the cover filename and creates ~/.cover if it doesn't exists */
gbemol_utils_generate_cover_filename(GbemolMpdSong * song)417 gchar* gbemol_utils_generate_cover_filename(GbemolMpdSong* song)
418 {
419 gchar* str_dir, *str;
420 GDir* dir_covers;
421
422 str_dir = g_strdup_printf ("%s/.covers/", g_get_home_dir());
423 dir_covers = g_dir_open(str_dir, 0, NULL);
424 if (dir_covers == NULL) {
425 g_mkdir(str_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
426 } else {
427 g_dir_close(dir_covers);
428 }
429 g_free(str_dir);
430 str = g_strdup_printf ("%s/.covers/%s-%s.jpg", g_get_home_dir(), song->artist, song->album);
431 return str;
432 }
433
434 /**
435 * Try to load image from file in ~/.covers
436 */
gbemol_utils_get_cover_image_from_file(GbemolMpdSong * song)437 GdkPixbuf* gbemol_utils_get_cover_image_from_file(GbemolMpdSong* song)
438 {
439 gchar *str;
440 GdkPixbuf* img = NULL;
441
442 str = gbemol_utils_generate_cover_filename(song);
443
444 if (g_file_test (str, G_FILE_TEST_EXISTS))
445 {
446 GError *e = NULL;
447 img = gdk_pixbuf_new_from_file (str, &e);
448 if (e)
449 {
450 g_message (e->message);
451 img = NULL;
452 }
453 }
454 g_free(str);
455
456 return img;
457 }
458
459 /**
460 * Try to fetch image from Amazon.com
461 */
gbemol_utils_get_cover_image_from_amazon(GbemolMpdSong * song)462 GdkPixbuf* gbemol_utils_get_cover_image_from_amazon(GbemolMpdSong* song)
463 {
464 GdkPixbuf* img = NULL;;
465 gchar *result = NULL;
466 GSList* results;
467
468 results = gbemol_amazon_search_cover(song);
469 if (results == NULL)
470 {
471 return NULL;
472 }
473 else
474 {
475 GError* err = NULL;
476 gchar* str = gbemol_utils_generate_cover_filename(song);
477 /* There's a cover, save it */
478 result = results->data;
479 gbemol_net_download_file(result, str, &err);
480 if (err != NULL)
481 {
482 g_message(err->message);
483 }
484 else
485 {
486 img = gdk_pixbuf_new_from_file (str, NULL);
487 }
488 g_free (str);
489 }
490
491 /* Free everything */
492 g_slist_foreach(results, (GFunc) g_free, NULL);
493
494 return img;
495 }
496
gbemol_utils_get_song_image(GbemolMpdSong * song)497 GdkPixbuf* gbemol_utils_get_song_image (GbemolMpdSong* song)
498 {
499 GdkPixbuf *img = NULL;
500
501 if (gbemol_cfg_get_bool("cover_art", "file"))
502 img = gbemol_utils_get_cover_image_from_file(song);
503
504 /* No picture in ~/.covers, try amazon */
505 if (!img && gbemol_cfg_get_bool("cover_art", "amazon"))
506 {
507 img = gbemol_utils_get_cover_image_from_amazon(song);
508 }
509
510 return img;
511 }
512
gbemol_utils_label_new_with_markup(gchar * text)513 GtkWidget* gbemol_utils_label_new_with_markup (gchar* text)
514 {
515 GtkWidget* lbl;
516
517 lbl = gtk_label_new (text);
518 gtk_label_set_use_markup (GTK_LABEL (lbl), TRUE);
519 gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5);
520
521 return lbl;
522 }
523
gbemol_debug_msg(char * msg,...)524 void gbemol_debug_msg (char *msg, ...)
525 {
526 #ifdef DEBUG
527 va_list args;
528 char *str;
529
530 va_start (args);
531
532 str = g_strdup_vprintf ("DEBUG: %s\n", args);
533 g_print (str);
534 g_free (str);
535 #endif
536 }
537
538