Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
art.c
Go to the documentation of this file.
1 /*
2  * art.c
3  * Copyright 2011 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <errno.h>
21 #include <glib.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <libaudcore/audstrings.h>
28 #include <libaudcore/hook.h>
29 
30 #include "main.h"
31 #include "misc.h"
32 #include "playlist.h"
33 #include "util.h"
34 
35 typedef struct {
36  char * song_file; /* pooled */
37  int refcount;
38 
39  /* album art as JPEG or PNG data */
40  void * data;
41  int64_t len;
42 
43  /* album art as (possibly a temporary) file */
44  char * art_file;
46 } ArtItem;
47 
48 static GHashTable * art_items;
49 static char * current_file; /* pooled */
50 
51 static void art_item_free (ArtItem * item)
52 {
53  /* delete temporary file */
54  if (item->art_file && item->is_temp)
55  {
56  char * unixname = uri_to_filename (item->art_file);
57  if (unixname)
58  {
59  unlink (unixname);
60  g_free (unixname);
61  }
62  }
63 
64  str_unref (item->song_file);
65  g_free (item->data);
66  g_free (item->art_file);
67  g_slice_free (ArtItem, item);
68 }
69 
70 static ArtItem * art_item_new (const char * file)
71 {
72  /* local files only */
73  if (strncmp (file, "file://", 7))
74  return NULL;
75 
76  ArtItem * item = g_slice_new0 (ArtItem);
77  item->song_file = str_get (file);
78 
79  /* try to load embedded album art */
80  PluginHandle * decoder = file_find_decoder (file, FALSE);
81  if (decoder)
82  file_read_image (file, decoder, & item->data, & item->len);
83 
84  if (item->data)
85  return item;
86 
87  /* try to find external image file */
88  char * unixname = get_associated_image_file (file);
89  if (unixname)
90  {
91  item->art_file = filename_to_uri (unixname);
92  g_free (unixname);
93  }
94 
95  if (item->art_file)
96  return item;
97 
98  /* failed */
99  art_item_free (item);
100  return NULL;
101 }
102 
103 static ArtItem * art_item_get (const char * file)
104 {
105  if (! art_items)
106  art_items = g_hash_table_new_full (g_str_hash, g_str_equal,
107  NULL, (GDestroyNotify) art_item_free);
108 
109  ArtItem * item = g_hash_table_lookup (art_items, file);
110  if (item)
111  {
112  item->refcount ++;
113  return item;
114  }
115 
116  item = art_item_new (file);
117  if (! item)
118  return NULL;
119 
120  g_hash_table_insert (art_items, item->song_file, item);
121  item->refcount = 1;
122  return item;
123 }
124 
125 static void art_item_unref (ArtItem * item)
126 {
127  if (! -- item->refcount)
128  {
129  /* keep album art for current entry */
130  if (current_file && ! strcmp (current_file, item->song_file))
131  return;
132 
133  g_hash_table_remove (art_items, item->song_file);
134  }
135 }
136 
137 static void release_current (void)
138 {
139  if (! art_items || ! current_file)
140  return;
141 
142  /* free album art for previous entry */
143  ArtItem * item = g_hash_table_lookup (art_items, current_file);
144  if (item && ! item->refcount)
145  g_hash_table_remove (art_items, current_file);
146 }
147 
148 static void position_hook (void * data, void * user)
149 {
150  release_current ();
152 
153  int list = playlist_get_playing ();
154  int entry = (list >= 0) ? playlist_get_position (list) : -1;
155  current_file = (entry >= 0) ? playlist_entry_get_filename (list, entry) : NULL;
156 }
157 
158 void art_init (void)
159 {
160  hook_associate ("playlist position", position_hook, NULL);
161  hook_associate ("playlist set playing", position_hook, NULL);
162 }
163 
164 void art_cleanup (void)
165 {
166  hook_dissociate ("playlist position", position_hook);
167  hook_dissociate ("playlist set playing", position_hook);
168 
169  release_current ();
171  current_file = NULL;
172 
173  if (art_items && g_hash_table_size (art_items))
174  {
175  fprintf (stderr, "Album art not freed\n");
176  abort ();
177  }
178 
179  if (art_items)
180  {
181  g_hash_table_destroy (art_items);
182  art_items = NULL;
183  }
184 }
185 
186 void art_get_data (const char * file, const void * * data, int64_t * len)
187 {
188  * data = NULL;
189  * len = 0;
190 
191  ArtItem * item = art_item_get (file);
192  if (! item)
193  return;
194 
195  /* load data from external image file */
196  if (! item->data && item->art_file)
197  vfs_file_get_contents (item->art_file, & item->data, & item->len);
198 
199  if (! item->data)
200  {
201  art_item_unref (item);
202  return;
203  }
204 
205  * data = item->data;
206  * len = item->len;
207 }
208 
209 const char * art_get_file (const char * file)
210 {
211  ArtItem * item = art_item_get (file);
212  if (! item)
213  return NULL;
214 
215  /* save data to temporary file */
216  if (item->data && ! item->art_file)
217  {
218  char * unixname = write_temp_file (item->data, item->len);
219  if (unixname)
220  {
221  item->art_file = filename_to_uri (unixname);
222  item->is_temp = TRUE;
223  g_free (unixname);
224  }
225  }
226 
227  if (! item->art_file)
228  {
229  art_item_unref (item);
230  return NULL;
231  }
232 
233  return item->art_file;
234 }
235 
236 void art_unref (const char * file)
237 {
238  ArtItem * item = art_items ? g_hash_table_lookup (art_items, file) : NULL;
239  if (item)
240  art_item_unref (item);
241 }