]> git.sesse.net Git - vlc/blob - src/playlist/engine.c
Beginning of implementation of art fetch when played
[vlc] / src / playlist / engine.c
1 /*****************************************************************************
2  * engine.c : Run the playlist and handle its control
3  *****************************************************************************
4  * Copyright (C) 1999-2004 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
25 #include <vlc/vlc.h>
26 #include <vlc/vout.h>
27 #include <vlc/sout.h>
28 #include <vlc/input.h>
29 #include "vlc_playlist.h"
30 #include "vlc_interaction.h"
31 #include "playlist_internal.h"
32
33 /*****************************************************************************
34  * Local prototypes
35  *****************************************************************************/
36 static void VariablesInit( playlist_t *p_playlist );
37
38 static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd,
39                            vlc_value_t oldval, vlc_value_t newval, void *a )
40 {
41     ((playlist_t*)p_this)->b_reset_currently_playing = VLC_TRUE;
42     playlist_Signal( ((playlist_t*)p_this) );
43     return VLC_SUCCESS;
44 }
45
46 /**
47  * Create playlist
48  *
49  * Create a playlist structure.
50  * \param p_parent the vlc object that is to be the parent of this playlist
51  * \return a pointer to the created playlist, or NULL on error
52  */
53 playlist_t * playlist_Create( vlc_object_t *p_parent )
54 {
55     playlist_t *p_playlist;
56     vlc_bool_t b_save;
57     int i_tree;
58
59     /* Allocate structure */
60     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
61     if( !p_playlist )
62     {
63         msg_Err( p_parent, "out of memory" );
64         return NULL;
65     }
66     p_parent->p_libvlc->p_playlist = p_playlist;
67
68     VariablesInit( p_playlist );
69
70     /* Initialise data structures */
71     vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
72     p_playlist->i_last_playlist_id = 0;
73     p_playlist->i_last_input_id = 0;
74     p_playlist->p_input = NULL;
75
76     p_playlist->gc_date = 0;
77     p_playlist->b_cant_sleep = VLC_FALSE;
78
79     ARRAY_INIT( p_playlist->items );
80     ARRAY_INIT( p_playlist->all_items );
81     ARRAY_INIT( p_playlist->input_items );
82     ARRAY_INIT( p_playlist->current );
83
84     p_playlist->i_current_index = 0;
85     p_playlist->b_reset_currently_playing = VLC_TRUE;
86     p_playlist->last_rebuild_date = 0;
87
88     i_tree = var_CreateGetBool( p_playlist, "playlist-tree" );
89     p_playlist->b_always_tree = (i_tree == 1);
90     p_playlist->b_never_tree = (i_tree == 2);
91
92     p_playlist->b_doing_ml = VLC_FALSE;
93
94     p_playlist->b_auto_preparse =
95                         var_CreateGetBool( p_playlist, "auto-preparse") ;
96
97     p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL);
98     p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL);
99
100     if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
101         return NULL;
102
103     /* Create playlist and media library */
104     p_playlist->p_local_category = playlist_NodeCreate( p_playlist,
105                                  _( "Playlist" ),p_playlist->p_root_category );
106     p_playlist->p_local_onelevel =  playlist_NodeCreate( p_playlist,
107                                 _( "Playlist" ), p_playlist->p_root_onelevel );
108     p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
109     p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;
110
111     if( !p_playlist->p_local_category || !p_playlist->p_local_onelevel ||
112         !p_playlist->p_local_category->p_input ||
113         !p_playlist->p_local_onelevel->p_input )
114         return NULL;
115
116     /* Link the nodes together. Todo: actually create them from the same input*/
117     p_playlist->p_local_onelevel->p_input->i_id =
118         p_playlist->p_local_category->p_input->i_id;
119
120     if( config_GetInt( p_playlist, "media-library") )
121     {
122         p_playlist->p_ml_category =   playlist_NodeCreate( p_playlist,
123                            _( "Media Library" ), p_playlist->p_root_category );
124         p_playlist->p_ml_onelevel =  playlist_NodeCreate( p_playlist,
125                            _( "Media Library" ), p_playlist->p_root_onelevel );
126
127         if(!p_playlist->p_ml_category || !p_playlist->p_ml_onelevel)
128             return NULL;
129
130         p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
131         p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
132         p_playlist->p_ml_onelevel->p_input->i_id =
133              p_playlist->p_ml_category->p_input->i_id;
134
135     }
136     else
137     {
138         p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL;
139     }
140
141     /* Initial status */
142     p_playlist->status.p_item = NULL;
143     p_playlist->status.p_node = p_playlist->p_local_onelevel;
144     p_playlist->request.b_request = VLC_FALSE;
145     p_playlist->status.i_status = PLAYLIST_STOPPED;
146
147     p_playlist->i_sort = SORT_ID;
148     p_playlist->i_order = ORDER_NORMAL;
149
150     vlc_object_attach( p_playlist, p_parent );
151     b_save = p_playlist->b_auto_preparse;
152     p_playlist->b_auto_preparse = VLC_FALSE;
153     playlist_MLLoad( p_playlist );
154     p_playlist->b_auto_preparse = VLC_TRUE;
155     return p_playlist;
156 }
157
158 void playlist_Destroy( playlist_t *p_playlist )
159 {
160     while( p_playlist->i_sds )
161     {
162         playlist_ServicesDiscoveryRemove( p_playlist,
163                                           p_playlist->pp_sds[0]->psz_module );
164     }
165
166     playlist_MLDump( p_playlist );
167
168     vlc_thread_join( p_playlist->p_preparse );
169     vlc_thread_join( p_playlist->p_fetcher );
170     vlc_thread_join( p_playlist );
171
172     vlc_object_detach( p_playlist->p_preparse );
173     vlc_object_detach( p_playlist->p_fetcher );
174
175     var_Destroy( p_playlist, "intf-change" );
176     var_Destroy( p_playlist, "item-change" );
177     var_Destroy( p_playlist, "playlist-current" );
178     var_Destroy( p_playlist, "intf-popmenu" );
179     var_Destroy( p_playlist, "intf-show" );
180     var_Destroy( p_playlist, "play-and-stop" );
181     var_Destroy( p_playlist, "play-and-exit" );
182     var_Destroy( p_playlist, "random" );
183     var_Destroy( p_playlist, "repeat" );
184     var_Destroy( p_playlist, "loop" );
185     var_Destroy( p_playlist, "activity" );
186
187     PL_LOCK;
188     /* Go through all items, and simply free everything without caring
189      * about the tree structure. Do not decref, it will be done by doing
190      * the same thing on the input items array */
191     FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->all_items )
192         free( p_del->pp_children );
193         free( p_del );
194     FOREACH_END();
195     ARRAY_RESET( p_playlist->all_items );
196
197     FOREACH_ARRAY( input_item_t *p_del, p_playlist->input_items )
198         input_ItemClean( p_del );
199         free( p_del );
200     FOREACH_END();
201     ARRAY_RESET( p_playlist->input_items );
202
203     ARRAY_RESET( p_playlist->items );
204     ARRAY_RESET( p_playlist->current );
205
206     PL_UNLOCK;
207
208     if( p_playlist->p_stats )
209         free( p_playlist->p_stats );
210
211     vlc_mutex_destroy( &p_playlist->gc_lock );
212     vlc_object_destroy( p_playlist->p_preparse );
213     vlc_object_destroy( p_playlist->p_fetcher );
214     vlc_object_detach( p_playlist );
215     vlc_object_destroy( p_playlist );
216 }
217
218 /* Destroy remaining objects */
219 static void ObjectGarbageCollector( playlist_t *p_playlist )
220 {
221     vlc_object_t *p_obj;
222
223     if( mdate() - p_playlist->gc_date < 1000000 )
224     {
225         p_playlist->b_cant_sleep = VLC_TRUE;
226         return;
227     }
228     else if( p_playlist->gc_date == 0 )
229         return;
230
231     vlc_mutex_lock( &p_playlist->gc_lock );
232     while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_VOUT,
233                                                   FIND_CHILD ) ) )
234     {
235         if( p_obj->p_parent != (vlc_object_t*)p_playlist )
236         {
237             vlc_object_release( p_obj );
238             break;
239         }
240         msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
241         vlc_object_detach( p_obj );
242         vlc_object_release( p_obj );
243         vout_Destroy( (vout_thread_t *)p_obj );
244     }
245     while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_SOUT,
246                                                   FIND_CHILD ) ) )
247     {
248         if( p_obj->p_parent != (vlc_object_t*)p_playlist )
249         {
250             vlc_object_release( p_obj );
251             break;
252         }
253         vlc_object_release( p_obj );
254         sout_DeleteInstance( (sout_instance_t*)p_obj );
255     }
256     p_playlist->b_cant_sleep = VLC_FALSE;
257     vlc_mutex_unlock( &p_playlist->gc_lock );
258 }
259
260 /** Main loop for the playlist */
261 void playlist_MainLoop( playlist_t *p_playlist )
262 {
263     playlist_item_t *p_item = NULL;
264     vlc_bool_t b_playexit = var_GetBool( p_playlist, "play-and-exit" );
265     PL_LOCK;
266
267     if( p_playlist->b_reset_currently_playing &&
268         mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
269     {
270         ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random"),
271                              p_playlist->status.p_item );
272         p_playlist->last_rebuild_date = mdate();
273     }
274
275 check_input:
276     /* If there is an input, check that it doesn't need to die. */
277     if( p_playlist->p_input )
278     {
279         if( p_playlist->request.b_request && !p_playlist->p_input->b_die )
280         {
281             PL_DEBUG( "incoming request - stopping current input" );
282             input_StopThread( p_playlist->p_input );
283         }
284
285         /* This input is dead. Remove it ! */
286         if( p_playlist->p_input->b_dead )
287         {
288             int i_activity;
289             input_thread_t *p_input;
290             PL_DEBUG( "dead input" );
291
292             p_input = p_playlist->p_input;
293             p_playlist->p_input = NULL;
294
295             /* Release the playlist lock, because we may get stuck
296              * in input_DestroyThread() for some time. */
297             PL_UNLOCK
298
299             /* Destroy input */
300             input_DestroyThread( p_input );
301
302             /* Unlink current input
303              * (_after_ input_DestroyThread for vout garbage collector) */
304             vlc_object_detach( p_input );
305
306             /* Destroy object */
307             vlc_object_destroy( p_input );
308
309             PL_LOCK;
310
311             p_playlist->gc_date = mdate();
312             p_playlist->b_cant_sleep = VLC_TRUE;
313
314             if( p_playlist->status.p_item->i_flags
315                 & PLAYLIST_REMOVE_FLAG )
316             {
317                  PL_DEBUG( "%s was marked for deletion, deleting",
318                                  PLI_NAME( p_playlist->status.p_item  ) );
319                  playlist_ItemDelete( p_playlist->status.p_item );
320                  if( p_playlist->request.p_item == p_playlist->status.p_item )
321                      p_playlist->request.p_item = NULL;
322                  p_playlist->status.p_item = NULL;
323             }
324
325             i_activity= var_GetInteger( p_playlist, "activity") ;
326             var_SetInteger( p_playlist, "activity", i_activity -
327                             DEFAULT_INPUT_ACTIVITY );
328             goto check_input;
329         }
330         /* This input is dying, let it do */
331         else if( p_playlist->p_input->b_die )
332         {
333             PL_DEBUG( "dying input" );
334             msleep( 25000 ); // 25 ms
335             goto check_input;
336         }
337         /* This input has finished, ask it to die ! */
338         else if( p_playlist->p_input->b_error
339                   || p_playlist->p_input->b_eof )
340         {
341             PL_DEBUG( "finished input" );
342             input_StopThread( p_playlist->p_input );
343             /* No need to wait here, we'll wait in the p_input->b_die case */
344             goto check_input;
345         }
346         else if( p_playlist->p_input->i_state != INIT_S )
347         {
348             PL_UNLOCK;
349             ObjectGarbageCollector( p_playlist );
350             PL_LOCK;
351         }
352     }
353     else
354     {
355         /* No input. Several cases
356          *  - No request, running status -> start new item
357          *  - No request, stopped status -> collect garbage
358          *  - Request, running requested -> start new item
359          *  - Request, stopped requested -> collect garbage
360          */
361          if( (!p_playlist->request.b_request &&
362               p_playlist->status.i_status != PLAYLIST_STOPPED) ||
363               ( p_playlist->request.b_request &&
364                 p_playlist->request.i_status != PLAYLIST_STOPPED ) )
365          {
366              msg_Dbg( p_playlist, "starting new item" );
367              p_item = playlist_NextItem( p_playlist );
368
369              if( p_item == NULL )
370              {
371                 msg_Dbg( p_playlist, "nothing to play" );
372                 if( b_playexit == VLC_TRUE )
373                 {
374                     msg_Info( p_playlist, "end of playlist, exiting" );
375                     p_playlist->p_libvlc->b_die = VLC_TRUE;
376                 }
377                 p_playlist->status.i_status = PLAYLIST_STOPPED;
378                 PL_UNLOCK
379                 return;
380              }
381              playlist_PlayItem( p_playlist, p_item );
382          }
383          else
384          {
385             p_playlist->status.i_status = PLAYLIST_STOPPED;
386             if( p_playlist->status.p_item &&
387                 p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
388             {
389                 PL_DEBUG( "deleting item marked for deletion" );
390                 playlist_ItemDelete( p_playlist->status.p_item );
391                 p_playlist->status.p_item = NULL;
392             }
393
394             /* Collect garbage */
395             PL_UNLOCK;
396             ObjectGarbageCollector( p_playlist );
397             PL_LOCK;
398         }
399     }
400     PL_UNLOCK
401 }
402
403 /** Playlist dying last loop */
404 void playlist_LastLoop( playlist_t *p_playlist )
405 {
406     vlc_object_t *p_obj;
407
408     /* If there is an input, kill it */
409     while( 1 )
410     {
411         PL_LOCK
412
413         if( p_playlist->p_input == NULL )
414         {
415             PL_UNLOCK
416             break;
417         }
418
419         if( p_playlist->p_input->b_dead )
420         {
421             input_thread_t *p_input;
422
423             /* Unlink current input */
424             p_input = p_playlist->p_input;
425             p_playlist->p_input = NULL;
426             PL_UNLOCK
427
428             /* Destroy input */
429             input_DestroyThread( p_input );
430             /* Unlink current input (_after_ input_DestroyThread for vout
431              * garbage collector)*/
432             vlc_object_detach( p_input );
433
434             /* Destroy object */
435             vlc_object_destroy( p_input );
436             continue;
437         }
438         else if( p_playlist->p_input->b_die )
439         {
440             /* This input is dying, leave it alone */
441             ;
442         }
443         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
444         {
445             input_StopThread( p_playlist->p_input );
446             PL_UNLOCK
447             continue;
448         }
449         else
450         {
451             p_playlist->p_input->b_eof = 1;
452         }
453
454         PL_UNLOCK
455
456         msleep( INTF_IDLE_SLEEP );
457     }
458
459     /* close all remaining sout */
460     while( ( p_obj = vlc_object_find( p_playlist,
461                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
462     {
463         vlc_object_release( p_obj );
464         sout_DeleteInstance( (sout_instance_t*)p_obj );
465     }
466
467     /* close all remaining vout */
468     while( ( p_obj = vlc_object_find( p_playlist,
469                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
470     {
471         vlc_object_detach( p_obj );
472         vlc_object_release( p_obj );
473         vout_Destroy( (vout_thread_t *)p_obj );
474     }
475 }
476
477 /** Main loop for preparser queue */
478 void playlist_PreparseLoop( playlist_preparse_t *p_obj )
479 {
480     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
481     input_item_t *p_current;
482     int i_activity;
483     uint32_t i_m, i_o;
484
485     while( !p_playlist->b_die )
486     {
487         vlc_mutex_lock( &p_obj->object_lock );
488         while( p_obj->i_waiting == 0 )
489         {
490             vlc_cond_wait( &p_obj->object_wait, &p_obj->object_lock );
491             if( p_playlist->b_die )
492             {
493                 vlc_mutex_unlock( &p_obj->object_lock );
494                 return;
495             }
496         }
497
498         p_current = p_obj->pp_waiting[0];
499         REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
500         vlc_mutex_unlock( &p_obj->object_lock );
501
502         PL_LOCK;
503         if( p_current )
504         {
505             vlc_bool_t b_preparsed = VLC_FALSE;
506             if( strncmp( p_current->psz_uri, "http:", 5 ) &&
507                 strncmp( p_current->psz_uri, "rtsp:", 5 ) &&
508                 strncmp( p_current->psz_uri, "udp:", 4 ) &&
509                 strncmp( p_current->psz_uri, "mms:", 4 ) &&
510                 strncmp( p_current->psz_uri, "cdda:", 4 ) &&
511                 strncmp( p_current->psz_uri, "dvd:", 4 ) &&
512                 strncmp( p_current->psz_uri, "v4l:", 4 ) &&
513                 strncmp( p_current->psz_uri, "dshow:", 6 ) )
514             {
515                 b_preparsed = VLC_TRUE;
516                 stats_TimerStart( p_playlist, "Preparse run",
517                                   STATS_TIMER_PREPARSE );
518                 PL_UNLOCK;
519                 input_Preparse( p_playlist, p_current );
520                 PL_LOCK;
521                 stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
522             }
523             PL_UNLOCK;
524             if( b_preparsed )
525             {
526                 p_current->p_meta->i_status |= ITEM_PREPARSED;
527                 var_SetInteger( p_playlist, "item-change", p_current->i_id );
528             }
529             PL_LOCK;
530
531             /* If we haven't retrieved enough meta, add to secondary queue
532              * which will run the "meta fetchers".
533              * This only checks for meta, not for art
534              * \todo don't do this for things we won't get meta for, like vids
535              */
536             if( p_current->p_meta &&
537                 !input_MetaSatisfied( p_playlist, p_current, &i_m, &i_o ) )
538             {
539                 preparse_item_t p;
540                 PL_DEBUG("need to fetch meta for %s", p_current->psz_name );
541                 p.p_item = p_current;
542                 p.b_fetch_art = VLC_FALSE;
543                 vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
544                 INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
545                              p_playlist->p_fetcher->i_waiting,
546                              p_playlist->p_fetcher->i_waiting, p);
547                 vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
548                 vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
549             }
550             /* We already have all needed meta, but we need art right now */
551             else if( p_current->p_meta &&
552                      p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL &&
553                      EMPTY_STR( p_current->p_meta->psz_arturl ) )
554             {
555                 preparse_item_t p;
556                 PL_DEBUG("meta ok for %s, need to fetch art",
557                                                          p_current->psz_name );
558                 p.p_item = p_current;
559                 p.b_fetch_art = VLC_TRUE;
560                 vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
561                 INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
562                              p_playlist->p_fetcher->i_waiting,
563                              p_playlist->p_fetcher->i_waiting, p);
564                 vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
565                 vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
566             }
567             else
568             {
569                 PL_DEBUG( "no fetch required for %s (art currently %s)",
570                           p_current->psz_name,
571                           p_current->p_meta ? p_current->p_meta->psz_arturl:
572                                               "null" );
573                 vlc_gc_decref( p_current );
574             }
575             PL_UNLOCK;
576         }
577         else
578             PL_UNLOCK;
579
580         vlc_mutex_lock( &p_obj->object_lock );
581         i_activity = var_GetInteger( p_playlist, "activity" );
582         if( i_activity < 0 ) i_activity = 0;
583         vlc_mutex_unlock( &p_obj->object_lock );
584         /* Sleep at least 1ms */
585         msleep( (i_activity+1) * 1000 );
586     }
587 }
588
589 /** Main loop for secondary preparser queue */
590 void playlist_FetcherLoop( playlist_fetcher_t *p_obj )
591 {
592     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
593     vlc_bool_t b_fetch_art;
594     input_item_t *p_item;
595     int i_activity;
596
597     while( !p_playlist->b_die )
598     {
599         vlc_mutex_lock( &p_obj->object_lock );
600         while( p_obj->i_waiting == 0 )
601         {
602             vlc_cond_wait( &p_obj->object_wait, &p_obj->object_lock );
603             if( p_playlist->b_die )
604             {
605                 vlc_mutex_unlock( &p_obj->object_lock );
606                 return;
607             }
608         }
609
610         b_fetch_art = p_obj->p_waiting->b_fetch_art;
611         p_item = p_obj->p_waiting->p_item;
612         REMOVE_ELEM( p_obj->p_waiting, p_obj->i_waiting, 0 );
613         vlc_mutex_unlock( &p_obj->object_lock );
614         if( p_item )
615         {
616             assert( p_item->p_meta );
617             if( !b_fetch_art )
618             {
619                 input_MetaFetch( p_playlist, p_item );
620                 p_item->p_meta->i_status |= ITEM_META_FETCHED;
621                 var_SetInteger( p_playlist, "item-change", p_item->i_id );
622                 /*  Fetch right now */
623                 if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL )
624                 {
625                     vlc_mutex_lock( &p_obj->object_lock );
626                     preparse_item_t p;
627                     p.p_item = p_item;
628                     p.b_fetch_art = VLC_TRUE;
629                     INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
630                                  p_playlist->p_fetcher->i_waiting,
631                                  0, p );
632                     PL_DEBUG("meta fetched for %s, get art", p_item->psz_name);
633                     vlc_mutex_unlock( &p_obj->object_lock );
634                     continue;
635                 }
636                 else
637                     vlc_gc_decref( p_item );
638             }
639             else
640             {
641                 int i_ret = input_ArtFind( p_playlist, p_item );
642                 if( i_ret == 1 )
643                 {
644                     PL_DEBUG("downloading art for %s", p_item->psz_name );
645                     if( input_DownloadAndCacheArt( p_playlist, p_item ) )
646                         p_item->p_meta->i_status |= ITEM_ART_NOTFOUND;
647                     else {
648                         p_item->p_meta->i_status |= ITEM_ART_FETCHED;
649                         var_SetInteger( p_playlist, "item-change",
650                                         p_item->i_id );
651                     }
652                 }
653                 else if( i_ret == 0 ) /* Was in cache */
654                 {
655                     PL_DEBUG("found art for %s in cache", p_item->psz_name );
656                     p_item->p_meta->i_status |= ITEM_ART_FETCHED;
657                     var_SetInteger( p_playlist, "item-change", p_item->i_id );
658                 }
659                 else
660                 {
661                     PL_DEBUG("art not found for %s", p_item->psz_name );
662                     p_item->p_meta->i_status |= ITEM_ART_NOTFOUND;
663                 }
664                 vlc_gc_decref( p_item );
665            }
666         }
667         vlc_mutex_lock( &p_obj->object_lock );
668         i_activity = var_GetInteger( p_playlist, "activity" );
669         if( i_activity < 0 ) i_activity = 0;
670         vlc_mutex_unlock( &p_obj->object_lock );
671         /* Sleep at least 1ms */
672         msleep( (i_activity+1) * 1000 );
673     }
674 }
675
676 static void VariablesInit( playlist_t *p_playlist )
677 {
678     vlc_value_t val;
679     /* These variables control updates */
680     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
681     val.b_bool = VLC_TRUE;
682     var_Set( p_playlist, "intf-change", val );
683
684     var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
685     val.i_int = -1;
686     var_Set( p_playlist, "item-change", val );
687
688     var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
689     val.i_int = -1;
690     var_Set( p_playlist, "item-deleted", val );
691
692     var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
693
694     var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
695     val.i_int = -1;
696     var_Set( p_playlist, "playlist-current", val );
697
698     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
699
700     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
701     val.b_bool = VLC_TRUE;
702     var_Set( p_playlist, "intf-show", val );
703
704     var_Create( p_playlist, "activity", VLC_VAR_INTEGER );
705     var_SetInteger( p_playlist, "activity", 0 );
706
707     /* Variables to control playback */
708     var_CreateGetBool( p_playlist, "play-and-stop" );
709     var_CreateGetBool( p_playlist, "play-and-exit" );
710     var_CreateGetBool( p_playlist, "random" );
711     var_CreateGetBool( p_playlist, "repeat" );
712     var_CreateGetBool( p_playlist, "loop" );
713
714     var_AddCallback( p_playlist, "random", RandomCallback, NULL );
715 }