]> git.sesse.net Git - vlc/blob - src/playlist/art.c
fix album art caching for files without artist/album tags
[vlc] / src / playlist / art.c
1 /*****************************************************************************
2  * art.c : Art metadata handling
3  *****************************************************************************
4  * Copyright (C) 1998-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea@videolan.org>
8  *          ClĂ©ment Stenac <zorglub@videolan.org
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <assert.h>
30 #include <vlc_common.h>
31 #include <vlc_playlist.h>
32 #include <vlc_fs.h>
33 #include <vlc_strings.h>
34 #include <vlc_stream.h>
35 #include <vlc_url.h>
36 #include <vlc_md5.h>
37
38 #ifdef HAVE_SYS_STAT_H
39 #   include <sys/stat.h>
40 #endif
41
42 #include "../libvlc.h"
43 #include "playlist_internal.h"
44
45 static void ArtCacheCreateDir( const char *psz_dir )
46 {
47     char newdir[strlen( psz_dir ) + 1];
48     strcpy( newdir, psz_dir );
49     char * psz_newdir = newdir;
50     char * psz = psz_newdir;
51
52     while( *psz )
53     {
54         while( *psz && *psz != DIR_SEP_CHAR) psz++;
55         if( !*psz ) break;
56         *psz = 0;
57         if( !EMPTY_STR( psz_newdir ) )
58             vlc_mkdir( psz_newdir, 0700 );
59         *psz = DIR_SEP_CHAR;
60         psz++;
61     }
62     vlc_mkdir( psz_dir, 0700 );
63 }
64
65 static char* ArtCacheGetDirPath( const char *psz_arturl, const char *psz_artist,
66                                  const char *psz_album,  const char *psz_title )
67 {
68     char *psz_dir;
69     char *psz_cachedir = config_GetUserDir(VLC_CACHE_DIR);
70
71     if( !EMPTY_STR(psz_artist) && !EMPTY_STR(psz_album) )
72     {
73         char *psz_album_sanitized = strdup( psz_album );
74         filename_sanitize( psz_album_sanitized );
75         char *psz_artist_sanitized = strdup( psz_artist );
76         filename_sanitize( psz_artist_sanitized );
77         if( asprintf( &psz_dir, "%s" DIR_SEP "art" DIR_SEP "artistalbum"
78                       DIR_SEP "%s" DIR_SEP "%s", psz_cachedir,
79                       psz_artist_sanitized, psz_album_sanitized ) == -1 )
80             psz_dir = NULL;
81         free( psz_album_sanitized );
82         free( psz_artist_sanitized );
83     }
84     else
85     {
86         /* If artist or album are missing, cache by art download URL.
87          * If the URL is an attachment://, add the title to the cache name.
88          * It will be md5 hashed to form a valid cache filename.
89          * We assume that psz_arturl is always the download URL and not the
90          * already hashed filename.
91          * (We should never need to call this function if art has already been
92          * downloaded anyway).
93          */
94         struct md5_s md5;
95         InitMD5( &md5 );
96         AddMD5( &md5, psz_arturl, strlen( psz_arturl ) );
97         if( !strncmp( psz_arturl, "attachment://", 13 ) )
98             AddMD5( &md5, psz_title, strlen( psz_title ) );
99         EndMD5( &md5 );
100         char * psz_arturl_sanitized = psz_md5_hash( &md5 );
101         if( asprintf( &psz_dir, "%s" DIR_SEP "art" DIR_SEP "arturl" DIR_SEP
102                       "%s", psz_cachedir, psz_arturl_sanitized ) == -1 )
103             psz_dir = NULL;
104         free( psz_arturl_sanitized );
105     }
106     free( psz_cachedir );
107     return psz_dir;
108 }
109
110 static char *ArtCachePath( input_item_t *p_item )
111 {
112     char* psz_path = NULL;
113     const char *psz_artist;
114     const char *psz_album;
115     const char *psz_arturl;
116     const char *psz_title;
117
118     vlc_mutex_lock( &p_item->lock );
119
120     if( !p_item->p_meta )
121         p_item->p_meta = vlc_meta_New();
122     if( !p_item->p_meta )
123         goto end;
124
125     psz_artist = vlc_meta_Get( p_item->p_meta, vlc_meta_Artist );
126     psz_album = vlc_meta_Get( p_item->p_meta, vlc_meta_Album );
127     psz_arturl = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL );
128     psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title );
129     if( !psz_title )
130         psz_title = p_item->psz_name;
131
132
133     if( (EMPTY_STR(psz_artist) || EMPTY_STR(psz_album) ) && !psz_arturl )
134         goto end;
135
136     psz_path = ArtCacheGetDirPath( psz_arturl, psz_artist, psz_album, psz_title );
137
138 end:
139     vlc_mutex_unlock( &p_item->lock );
140     return psz_path;
141 }
142
143 static char *ArtCacheName( input_item_t *p_item, const char *psz_type )
144 {
145     char *psz_path = ArtCachePath( p_item );
146     if( !psz_path )
147         return NULL;
148
149     ArtCacheCreateDir( psz_path );
150
151     char *psz_ext = strdup( psz_type ? psz_type : "" );
152     filename_sanitize( psz_ext );
153     char *psz_filename;
154     if( asprintf( &psz_filename, "%s" DIR_SEP "art%s", psz_path, psz_ext ) < 0 )
155         psz_filename = NULL;
156
157     free( psz_ext );
158     free( psz_path );
159
160     return psz_filename;
161 }
162
163 /* */
164 int playlist_FindArtInCache( input_item_t *p_item )
165 {
166     char *psz_path = ArtCachePath( p_item );
167
168     if( !psz_path )
169         return VLC_EGENERIC;
170
171     /* Check if file exists */
172     DIR *p_dir = vlc_opendir( psz_path );
173     if( !p_dir )
174     {
175         free( psz_path );
176         return VLC_EGENERIC;
177     }
178
179     bool b_found = false;
180     char *psz_filename;
181     while( !b_found && (psz_filename = vlc_readdir( p_dir )) )
182     {
183         if( !strncmp( psz_filename, "art", 3 ) )
184         {
185             char *psz_file;
186             if( asprintf( &psz_file, "%s" DIR_SEP "%s",
187                           psz_path, psz_filename ) != -1 )
188             {
189                 char *psz_uri = make_URI( psz_file, "file" );
190                 if( psz_uri )
191                 {
192                     input_item_SetArtURL( p_item, psz_uri );
193                     free( psz_uri );
194                 }
195                 free( psz_file );
196             }
197
198             b_found = true;
199         }
200         free( psz_filename );
201     }
202
203     /* */
204     closedir( p_dir );
205     free( psz_path );
206     return b_found ? VLC_SUCCESS : VLC_EGENERIC;
207 }
208
209
210 /* */
211 int playlist_SaveArt( playlist_t *p_playlist, input_item_t *p_item,
212                       const uint8_t *p_buffer, int i_buffer, const char *psz_type )
213 {
214     char *psz_filename = ArtCacheName( p_item, psz_type );
215
216     if( !psz_filename )
217         return VLC_EGENERIC;
218
219     char *psz_uri = make_URI( psz_filename, "file" );
220     if( !psz_uri )
221     {
222         free( psz_filename );
223         return VLC_EGENERIC;
224     }
225
226     /* Check if we already dumped it */
227     struct stat s;
228     if( !vlc_stat( psz_filename, &s ) )
229     {
230         input_item_SetArtURL( p_item, psz_uri );
231         free( psz_filename );
232         free( psz_uri );
233         return VLC_SUCCESS;
234     }
235
236     /* Dump it otherwise */
237     FILE *f = vlc_fopen( psz_filename, "wb" );
238     if( f )
239     {
240         if( fwrite( p_buffer, i_buffer, 1, f ) != 1 )
241         {
242             msg_Err( p_playlist, "%s: %m", psz_filename );
243         }
244         else
245         {
246             msg_Dbg( p_playlist, "album art saved to %s", psz_filename );
247             input_item_SetArtURL( p_item, psz_uri );
248         }
249         fclose( f );
250     }
251     free( psz_filename );
252     free( psz_uri );
253     return VLC_SUCCESS;
254 }
255