]> git.sesse.net Git - vlc/blob - src/playlist/fetcher.c
Clean up preparser/fetcher code.
[vlc] / src / playlist / fetcher.c
1 /*****************************************************************************
2  * preparse.c: Preparser thread.
3  *****************************************************************************
4  * Copyright © 1999-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_playlist.h>
30
31 #include "art.h"
32 #include "fetcher.h"
33 #include "playlist_internal.h"
34
35 static void *Thread( void * );
36
37 playlist_fetcher_t *playlist_fetcher_New( playlist_t *p_playlist )
38 {
39     // Secondary Preparse
40     playlist_fetcher_t *p_fetcher = malloc( sizeof(*p_fetcher) );
41     if( !p_fetcher )
42         return NULL;
43
44     p_fetcher->p_playlist = p_playlist;
45     vlc_mutex_init( &p_fetcher->lock );
46     vlc_cond_init( &p_fetcher->wait );
47     p_fetcher->i_waiting = 0;
48     p_fetcher->pp_waiting = NULL;
49     p_fetcher->i_art_policy = var_CreateGetInteger( p_playlist, "album-art" );
50     ARRAY_INIT( p_fetcher->albums );
51
52     if( vlc_clone( &p_fetcher->thread, Thread, p_fetcher,
53                    VLC_THREAD_PRIORITY_LOW ) )
54     {
55         msg_Err( p_playlist, "cannot spawn secondary preparse thread" );
56         free( p_fetcher );
57         return NULL;
58     }
59
60     return p_fetcher;
61 }
62
63 void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
64 {
65     vlc_gc_incref( p_item );
66
67     vlc_mutex_lock( &p_fetcher->lock );
68     INSERT_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting,
69                  p_fetcher->i_waiting, p_item );
70     vlc_cond_signal( &p_fetcher->wait );
71     vlc_mutex_unlock( &p_fetcher->lock );
72 }
73
74 void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher )
75 {
76     /* Destroy the item meta-infos fetcher */
77     vlc_cancel( p_fetcher->thread );
78     vlc_join( p_fetcher->thread, NULL );
79
80     while( p_fetcher->i_waiting > 0 )
81     {   /* Any left-over unparsed item? */
82         vlc_gc_decref( p_fetcher->pp_waiting[0] );
83         REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
84     }
85     vlc_cond_destroy( &p_fetcher->wait );
86     vlc_mutex_destroy( &p_fetcher->lock );
87     free( p_fetcher );
88 }
89
90 /**
91  * This function locates the art associated to an input item.
92  * Return codes:
93  *   0 : Art is in cache or is a local file
94  *   1 : Art found, need to download
95  *  -X : Error/not found
96  */
97 static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
98 {
99     playlist_t *p_playlist = p_fetcher->p_playlist;
100     int i_ret = VLC_EGENERIC;
101     module_t *p_module;
102     char *psz_title, *psz_artist, *psz_album;
103
104     psz_artist = input_item_GetArtist( p_item );
105     psz_album = input_item_GetAlbum( p_item );
106     psz_title = input_item_GetTitle( p_item );
107     if( !psz_title )
108         psz_title = input_item_GetName( p_item );
109
110     if( !psz_title && !psz_artist && !psz_album )
111         return VLC_EGENERIC;
112
113     free( psz_title );
114
115     /* If we already checked this album in this session, skip */
116     if( psz_artist && psz_album )
117     {
118         FOREACH_ARRAY( playlist_album_t album, p_fetcher->albums )
119             if( !strcmp( album.psz_artist, psz_artist ) &&
120                 !strcmp( album.psz_album, psz_album ) )
121             {
122                 msg_Dbg( p_playlist, " %s - %s has already been searched",
123                          psz_artist, psz_album );
124                 /* TODO-fenrir if we cache art filename too, we can go faster */
125                 free( psz_artist );
126                 free( psz_album );
127                 if( album.b_found )
128                 {
129                     if( !strncmp( album.psz_arturl, "file://", 7 ) )
130                         input_item_SetArtURL( p_item, album.psz_arturl );
131                     else /* Actually get URL from cache */
132                         playlist_FindArtInCache( p_item );
133                     return 0;
134                 }
135                 else
136                 {
137                     return VLC_EGENERIC;
138                 }
139             }
140         FOREACH_END();
141     }
142     free( psz_artist );
143     free( psz_album );
144
145     playlist_FindArtInCache( p_item );
146
147     char *psz_arturl = input_item_GetArtURL( p_item );
148     if( psz_arturl )
149     {
150         /* We already have an URL */
151         if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) )
152         {
153             free( psz_arturl );
154             return 0; /* Art is in cache, no need to go further */
155         }
156
157         free( psz_arturl );
158
159         /* Art need to be put in cache */
160         return 1;
161     }
162
163     PL_LOCK;
164     p_playlist->p_private = p_item;
165     psz_album = input_item_GetAlbum( p_item );
166     psz_artist = input_item_GetArtist( p_item );
167     psz_title = input_item_GetTitle( p_item );
168     if( !psz_title )
169         psz_title = input_item_GetName( p_item );
170
171     if( psz_album && psz_artist )
172     {
173         msg_Dbg( p_playlist, "searching art for %s - %s",
174              psz_artist, psz_album );
175     }
176     else
177     {
178         msg_Dbg( p_playlist, "searching art for %s",
179              psz_title );
180     }
181     free( psz_title );
182
183     p_module = module_need( p_playlist, "art finder", NULL, false );
184
185     if( p_module )
186         i_ret = 1;
187     else
188         msg_Dbg( p_playlist, "unable to find art" );
189
190     /* Record this album */
191     if( psz_artist && psz_album )
192     {
193         playlist_album_t a;
194         a.psz_artist = psz_artist;
195         a.psz_album = psz_album;
196         a.psz_arturl = input_item_GetArtURL( p_item );
197         a.b_found = (i_ret == VLC_EGENERIC ? false : true );
198         ARRAY_APPEND( p_fetcher->albums, a );
199     }
200     else
201     {
202         free( psz_artist );
203         free( psz_album );
204     }
205
206     if( p_module )
207         module_unneed( p_playlist, p_module );
208     p_playlist->p_private = NULL;
209     PL_UNLOCK;
210
211     return i_ret;
212 }
213
214
215 static void *Thread( void *p_data )
216 {
217     playlist_fetcher_t *p_fetcher = p_data;
218     playlist_t *p_playlist = p_fetcher->p_playlist;
219     playlist_private_t *p_sys = pl_priv( p_playlist );
220
221     for( ;; )
222     {
223         input_item_t *p_item;
224
225         /* */
226         vlc_mutex_lock( &p_fetcher->lock );
227         mutex_cleanup_push( &p_fetcher->lock );
228
229         while( p_fetcher->i_waiting == 0 )
230             vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock );
231
232         p_item = p_fetcher->pp_waiting[0];
233         REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
234         vlc_cleanup_run( );
235
236         if( !p_item )
237             continue;
238
239         /* */
240         int canc = vlc_savecancel();
241         {
242             int i_ret;
243
244             /* Check if it is not yet preparsed and if so wait for it
245              * (at most 0.5s)
246              * (This can happen if we fetch art on play)
247              * FIXME this doesn't work if we need to fetch meta before art...
248              */
249             for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
250             {
251                 bool b_break;
252                 PL_LOCK;
253                 b_break = ( !p_sys->p_input || input_GetItem(p_sys->p_input) != p_item  ||
254                             p_sys->p_input->b_die || p_sys->p_input->b_eof || p_sys->p_input->b_error );
255                 PL_UNLOCK;
256                 if( b_break )
257                     break;
258                 msleep( 50000 );
259             }
260
261             i_ret = FindArt( p_fetcher, p_item );
262             if( i_ret == 1 )
263             {
264                 PL_DEBUG( "downloading art for %s", p_item->psz_name );
265                 if( playlist_DownloadArt( p_playlist, p_item ) )
266                     input_item_SetArtNotFound( p_item, true );
267                 else {
268                     input_item_SetArtFetched( p_item, true );
269                     var_SetInteger( p_playlist, "item-change",
270                                     p_item->i_id );
271                 }
272             }
273             else if( i_ret == 0 ) /* Was in cache */
274             {
275                 PL_DEBUG( "found art for %s in cache", p_item->psz_name );
276                 input_item_SetArtFetched( p_item, true );
277                 var_SetInteger( p_playlist, "item-change", p_item->i_id );
278             }
279             else
280             {
281                 PL_DEBUG( "art not found for %s", p_item->psz_name );
282                 input_item_SetArtNotFound( p_item, true );
283             }
284             vlc_gc_decref( p_item );
285         }
286         vlc_restorecancel( canc );
287
288         int i_activity = var_GetInteger( p_playlist, "activity" );
289         if( i_activity < 0 ) i_activity = 0;
290         /* Sleep at least 1ms */
291         msleep( (i_activity+1) * 1000 );
292     }
293     return NULL;
294 }
295
296