Removed unused variable (avformat)
[vlc.git] / modules / media_library / ml_watch.c
blob9ad575ccab35dfdb1f72107abe1bd55e1792a5d7
1 /*****************************************************************************
2 * ml_watch.c: SQL-based media library: Medias watching system
3 *****************************************************************************
4 * Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
5 * $Id$
7 * Authors: Antoine Lejeune <phytos@videolan.org>
8 * Jean-Philippe André <jpeg@videolan.org>
9 * Rémi Duraffort <ivoire@videolan.org>
10 * Adrien Maglo <magsoft@videolan.org>
11 * Srikanth Raju <srikiraju at gmail dot com>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
28 #include "sql_media_library.h"
29 #include "item_list.h"
30 #include <vlc_events.h>
32 static void watch_ItemChange( const vlc_event_t *, void * );
33 static int watch_PlaylistItemCurrent( vlc_object_t *p_this, char const *psz_var,
34 vlc_value_t oldval, vlc_value_t newval,
35 void *data );
36 static int watch_PlaylistItemAppend( vlc_object_t *p_this, char const *psz_var,
37 vlc_value_t oldval, vlc_value_t newval,
38 void *data );
39 static int watch_PlaylistItemDeleted( vlc_object_t *p_this, char const *psz_var,
40 vlc_value_t oldval, vlc_value_t newval,
41 void *data );
42 static void watch_loop( media_library_t *p_ml, bool b_force );
43 static void watch_Thread_Cleanup( void* p_object );
44 static int watch_update_Item( media_library_t *p_ml, int i_media_id,
45 input_item_t *p_item, bool b_raise_count, bool locked );
46 static void watch_ProcessAppendQueue( media_library_t* p_ml );
48 /**
49 * @brief Watching thread
51 static void* watch_Thread( void *obj )
53 watch_thread_t *p_watch = ( watch_thread_t* )obj;
54 media_library_t *p_ml = p_watch->p_ml;
55 int i_ret = 0;
57 vlc_mutex_lock( &p_watch->lock );
58 vlc_cleanup_push( watch_Thread_Cleanup, p_ml );
59 for( ;; )
61 watch_loop( p_ml, !i_ret );
62 i_ret = vlc_cond_timedwait( &p_watch->cond, &p_watch->lock,
63 mdate() + 1000000 * THREAD_SLEEP_DELAY );
65 vlc_cleanup_run();
66 return NULL;
69 /**
70 * @brief Callback for thread exit
72 static void watch_Thread_Cleanup( void* p_object )
74 media_library_t* p_ml = ( media_library_t* )p_object;
75 watch_loop( p_ml, true );
76 vlc_mutex_unlock( &p_ml->p_sys->p_watch->lock );
78 /**
79 * @brief Init watching system
80 * @return Error if the object or the thread could not be created
82 int watch_Init( media_library_t *p_ml )
84 /* init and launch watching thread */
85 p_ml->p_sys->p_watch = calloc( 1, sizeof(*p_ml->p_sys->p_watch) );
86 if( !p_ml->p_sys->p_watch )
87 return VLC_ENOMEM;
89 watch_thread_t* p_wt = p_ml->p_sys->p_watch;
90 vlc_mutex_init( &p_wt->list_mutex );
91 p_wt->p_ml = p_ml;
93 vlc_cond_init( &p_wt->cond );
94 vlc_mutex_init( &p_wt->lock );
96 if( vlc_clone( &p_wt->thread, watch_Thread, p_wt, VLC_THREAD_PRIORITY_LOW ) )
98 msg_Dbg( p_ml, "unable to launch the auto-updating thread" );
99 free( p_wt );
100 return VLC_EGENERIC;
103 /* Wait on playlist events
104 * playlist-item-append -> entry to playlist
105 * item-current -> to ensure that we catch played item only!
106 * playlist-item-deleted -> exit from playlist
107 * item-change -> Currently not required, as we monitor input_item events
109 playlist_t *p_pl = pl_Get( p_ml );
110 var_AddCallback( p_pl, "item-current", watch_PlaylistItemCurrent, p_ml );
111 var_AddCallback( p_pl, "playlist-item-append", watch_PlaylistItemAppend, p_ml );
112 var_AddCallback( p_pl, "playlist-item-deleted", watch_PlaylistItemDeleted, p_ml );
114 /* Initialise item append queue */
115 vlc_mutex_init( &p_wt->item_append_queue_lock );
116 p_wt->item_append_queue = NULL;
117 p_wt->item_append_queue_count = 0;
119 return VLC_SUCCESS;
123 * @brief Add the input to the watch system
124 * @param p_ml The Media Library Object
125 * @param p_item Item to be watched
126 * @param p_media Corresponding media item to sync with
127 * @param locked Status of item list lock
128 * @return VLC_SUCCESS or error code
130 int __watch_add_Item( media_library_t *p_ml, input_item_t *p_item,
131 ml_media_t* p_media, bool locked )
133 vlc_gc_incref( p_item );
134 ml_gc_incref( p_media );
135 int i_ret = __item_list_add( p_ml->p_sys->p_watch, p_media, p_item, locked );
136 if( i_ret != VLC_SUCCESS )
137 return i_ret;
138 vlc_event_manager_t *p_em = &p_item->event_manager;
139 vlc_event_attach( p_em, vlc_InputItemMetaChanged, watch_ItemChange, p_ml );
140 vlc_event_attach( p_em, vlc_InputItemNameChanged, watch_ItemChange, p_ml );
141 vlc_event_attach( p_em, vlc_InputItemInfoChanged, watch_ItemChange, p_ml );
143 Note: vlc_InputItemDurationChanged is disabled because
144 it is triggered too often, even without consequent changes
146 return VLC_SUCCESS;
151 * @brief Detach event manager
152 * @param p_ml The Media Library Object
154 static void detachItemEvents( media_library_t *p_ml, input_item_t *p_item )
156 vlc_event_manager_t *p_em = &p_item->event_manager;
157 vlc_event_detach( p_em, vlc_InputItemMetaChanged, watch_ItemChange, p_ml );
158 vlc_event_detach( p_em, vlc_InputItemNameChanged, watch_ItemChange, p_ml );
159 vlc_event_detach( p_em, vlc_InputItemInfoChanged, watch_ItemChange, p_ml );
164 * @brief Close the watching system
165 * @param p_ml The Media Library Object
167 void watch_Close( media_library_t *p_ml )
169 playlist_t *p_pl = pl_Get( p_ml );
170 var_DelCallback( p_pl, "playlist-item-deleted", watch_PlaylistItemDeleted, p_ml );
171 var_DelCallback( p_pl, "playlist-item-append", watch_PlaylistItemAppend, p_ml );
172 var_DelCallback( p_pl, "item-current", watch_PlaylistItemCurrent, p_ml );
174 /* Flush item list */
175 il_foreachhashlist( p_ml->p_sys->p_watch->p_hlist, p_elt, ixx )
177 detachItemEvents( p_ml, p_elt->p_item );
178 ml_gc_decref( p_elt->p_media );
179 vlc_gc_decref( p_elt->p_item );
181 item_list_destroy( p_ml->p_sys->p_watch );
183 /* Stop the watch thread and join in */
184 vlc_cancel( p_ml->p_sys->p_watch->thread );
185 vlc_join( p_ml->p_sys->p_watch->thread, NULL );
187 /* Clear up other stuff */
188 vlc_mutex_destroy( &p_ml->p_sys->p_watch->lock );
189 vlc_cond_destroy( &p_ml->p_sys->p_watch->cond );
190 vlc_mutex_destroy( &p_ml->p_sys->p_watch->list_mutex );
191 free( p_ml->p_sys->p_watch );
193 free( p_ml->p_sys->p_watch->item_append_queue );
194 vlc_mutex_destroy( &p_ml->p_sys->p_watch->item_append_queue_lock );
195 p_ml->p_sys->p_watch = NULL;
199 * @brief Del item that is currently being watched
200 * @param p_ml The Media Library Object
201 * @param p_item Item to stop watching
202 * @param locked Lock state of item list
204 int __watch_del_Item( media_library_t* p_ml, input_item_t* p_item, bool locked )
206 assert( p_item );
207 item_list_t* p_tmp = item_list_delItem( p_ml->p_sys->p_watch, p_item, locked );
208 if( p_tmp == NULL )
209 return VLC_EGENERIC;
210 detachItemEvents( p_ml, p_tmp->p_item );
211 vlc_gc_decref( p_tmp->p_item );
212 ml_gc_decref( p_tmp->p_media );
213 free( p_tmp );
214 return VLC_SUCCESS;
218 * @brief Del media from watching by ID
219 * @param p_ml The Media Library Object
220 * @param i_media_id Media ID
222 int watch_del_MediaById( media_library_t* p_ml, int i_media_id )
224 assert( i_media_id > 0 );
225 item_list_t* p_elt = item_list_delMedia( p_ml->p_sys->p_watch, i_media_id );
226 if( p_elt == NULL )
227 return VLC_EGENERIC;
228 detachItemEvents( p_ml, p_elt->p_item );
229 vlc_gc_decref( p_elt->p_item );
230 ml_gc_decref( p_elt->p_media );
231 free( p_elt );
232 return VLC_SUCCESS;
236 * @brief Get item using media id, if exists in item list
237 * @param p_ml The Media Library Object
238 * @param i_media_id Media ID
240 input_item_t* watch_get_itemOfMediaId( media_library_t *p_ml, int i_media_id )
242 input_item_t* p_tmp = item_list_itemOfMediaId( p_ml->p_sys->p_watch, i_media_id );
243 if( p_tmp == NULL )
244 return NULL;
245 vlc_gc_incref( p_tmp );
246 return p_tmp;
250 * @brief Get media using media id, if exists in item list
251 * @param p_ml The Media Library Object
252 * @param i_media_id Media ID
254 ml_media_t* watch_get_mediaOfMediaId( media_library_t* p_ml, int i_media_id )
256 ml_media_t* p_tmp = item_list_mediaOfMediaId( p_ml->p_sys->p_watch, i_media_id );
257 if( p_tmp == NULL )
258 return NULL;
259 ml_gc_incref( p_tmp );
260 return p_tmp;
264 * @brief Get mediaid of existing item
265 * @param p_ml The Media Library Object
266 * @param p_item Pointer to input item
268 int watch_get_mediaIdOfItem( media_library_t *p_ml, input_item_t *p_item )
270 return item_list_mediaIdOfItem( p_ml->p_sys->p_watch, p_item );
274 * @brief Updates a media each time it is changed (name, info or meta)
276 static void watch_ItemChange( const vlc_event_t *event, void *data )
278 input_item_t *p_item = ( input_item_t* ) event->p_obj;
279 media_library_t *p_ml = ( media_library_t* ) data;
280 /* Note: we don't add items to the item_list, but normally there should
281 not be any item at this point that is not in the list. */
282 if( item_list_updateInput( p_ml->p_sys->p_watch, p_item, false ) <= 0 )
284 #ifndef NDEBUG
285 msg_Dbg( p_ml, "Couldn't update in watch_ItemChange(): (%s:%d)",
286 __FILE__, __LINE__ );
287 #endif
291 if( event->type == vlc_InputItemMetaChanged )
293 int id = item_list_mediaIdOfItem( p_ml->p_sys->p_watch, p_item );
294 if( !id ) return;
296 * Tell the world what happened *
297 var_SetInteger( p_ml, "media-meta-change", id );
303 * @brief Callback when item is added to playlist
305 static int watch_PlaylistItemAppend( vlc_object_t *p_this, char const *psz_var,
306 vlc_value_t oldval, vlc_value_t newval,
307 void *data )
309 VLC_UNUSED( oldval );
310 VLC_UNUSED( p_this );
311 VLC_UNUSED( psz_var );
312 media_library_t* p_ml = ( media_library_t* ) data;
313 playlist_t* p_playlist = pl_Get( p_ml );
314 playlist_add_t* p_add;
315 p_add = ( playlist_add_t* ) newval.p_address;
316 playlist_item_t* p_pitem = playlist_ItemGetById( p_playlist, p_add->i_item );
317 input_item_t* p_item = p_pitem->p_input;
318 watch_thread_t* p_wt = p_ml->p_sys->p_watch;
320 vlc_mutex_lock( &p_wt->list_mutex );
321 /* Check if we are already watching this item */
322 il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
324 if( p_elt->p_item->i_id == p_item->i_id )
326 p_elt->i_refs++;
327 vlc_mutex_unlock( &p_wt->list_mutex );
328 goto quit_playlistitemappend;
331 vlc_mutex_unlock( &p_wt->list_mutex );
333 /* Add the the append queue */
334 vlc_mutex_lock( &p_wt->item_append_queue_lock );
335 p_wt->item_append_queue_count++;
336 p_wt->item_append_queue = realloc( p_wt->item_append_queue,
337 sizeof( input_item_t* ) * p_wt->item_append_queue_count );
338 vlc_gc_incref( p_item );
339 p_wt->item_append_queue[ p_wt->item_append_queue_count - 1 ] = p_item;
340 vlc_mutex_unlock( &p_wt->item_append_queue_lock );
341 quit_playlistitemappend:
342 return VLC_SUCCESS;
346 * @brief Callback when item is deleted from playlist
348 static int watch_PlaylistItemDeleted( vlc_object_t *p_this, char const *psz_var,
349 vlc_value_t oldval, vlc_value_t newval,
350 void *data )
352 VLC_UNUSED( oldval );
353 VLC_UNUSED( p_this );
354 VLC_UNUSED( psz_var );
355 media_library_t* p_ml = ( media_library_t* ) data;
356 playlist_t* p_playlist = pl_Get( p_ml );
358 /* Luckily this works, because the item isn't deleted from PL, yet */
359 playlist_item_t* p_pitem = playlist_ItemGetById( p_playlist, newval.i_int );
360 input_item_t* p_item = p_pitem->p_input;
362 /* Find the new item and decrement its ref */
363 il_foreachlist( p_ml->p_sys->p_watch->p_hlist[ item_hash( p_item ) ], p_elt )
365 if( p_elt->p_item->i_id == p_item->i_id )
367 p_elt->i_refs--;
368 break;
372 return VLC_SUCCESS;
375 * @brief Callback when watched input item starts playing
376 * @note This will update playcount mainly
377 * TODO: Increment playcount on playing 50%(configurable)
379 static int watch_PlaylistItemCurrent( vlc_object_t *p_this, char const *psz_var,
380 vlc_value_t oldval, vlc_value_t newval,
381 void *data )
383 (void)p_this;
384 (void)oldval;
385 (void)newval;
386 media_library_t *p_ml = ( media_library_t* ) data;
387 input_item_t *p_item = NULL;
389 if( strcmp( psz_var, "item-current" ) != 0 )
390 /* This case should not happen... */
391 return VLC_EGENERIC;
393 /* Get current input */
394 input_thread_t *p_input = pl_CurrentInput( p_ml );
395 p_item = p_input ? input_GetItem( p_input ) : NULL;
397 if( p_input )
398 vlc_object_release( p_input );
400 if( !p_item )
401 return VLC_EGENERIC;
403 if( item_list_updateInput( p_ml->p_sys->p_watch, p_item, true ) == 0 )
405 #ifndef NDEBUG
406 msg_Dbg( p_ml, "couldn't in watch_PlaylistItemCurrent(): (%s:%d)",
407 __FILE__, __LINE__ );
408 #endif
411 return VLC_SUCCESS;
415 * @brief Update informations in the DB for an input item
417 * @param p_ml this media library instance
418 * @param i_media_id may be 0 (but not recommended)
419 * @param p_item input item that was updated
420 * @param b_raise_count increment the played count
421 * @return result of UpdateMedia()
423 static int watch_update_Item( media_library_t *p_ml,
424 int i_media_id, input_item_t *p_item,
425 bool b_raise_count, bool locked )
427 #ifndef NDEBUG
428 msg_Dbg( p_ml, "automatically updating media %d", i_media_id );
429 #endif
430 ml_media_t* p_media = item_list_mediaOfItem( p_ml->p_sys->p_watch, p_item, locked );
431 CopyInputItemToMedia( p_media, p_item );
432 ml_LockMedia( p_media );
433 p_media->i_played_count += b_raise_count ? 1 : 0;
434 ml_UnlockMedia( p_media );
435 int i_ret = UpdateMedia( p_ml, p_media );
437 /* Add the poster to the album */
438 ml_LockMedia( p_media );
439 if( p_media->i_album_id && p_media->psz_cover )
441 SetArtCover( p_ml, p_media->i_album_id, p_media->psz_cover );
443 ml_UnlockMedia( p_media );
445 return i_ret;
449 * @brief Signals the watch system to update all medias
451 void watch_Force_Update( media_library_t* p_ml )
453 vlc_mutex_lock( &p_ml->p_sys->p_watch->lock );
454 vlc_cond_signal( &p_ml->p_sys->p_watch->cond );
455 vlc_mutex_unlock( &p_ml->p_sys->p_watch->lock );
459 * @brief Loop on the item_list: old objects collector and automatic updater
461 * This function is *not* a garbage collector. It actually decrefs items
462 * when they are too old. ITEM_GC_MAX_AGE is the maximum 'time' an item
463 * can stay in the list. After that, it is gc_decref'ed but not removed
464 * from this list. If you try to get it after that, either the input item
465 * is still alive, then you get it, or you'll have
467 * The update of an item is done when its age is >= ITEM_LOOP_UPDATE
468 * (0 could lead to a too early update)
470 * A thread should call this function every N seconds
472 * @param p_ml the media library instance
474 static void watch_loop( media_library_t *p_ml, bool b_force )
476 /* Do the garbage collection */
477 pool_GC( p_ml );
479 /* Process the append queue */
480 watch_ProcessAppendQueue( p_ml );
482 /* Do the item update if necessary */
483 vlc_mutex_lock( &p_ml->p_sys->p_watch->list_mutex );
484 item_list_t *p_prev = NULL;
485 il_foreachhashlist( p_ml->p_sys->p_watch->p_hlist, p_elt, ixx )
487 if( ( p_elt->i_update && p_elt->i_age >= ITEM_LOOP_UPDATE )
488 || b_force )
490 /* This is the automatic delayed update */
491 watch_update_Item( p_ml, p_elt->i_media_id, p_elt->p_item,
492 ( p_elt->i_update & 2 ) ? true : false, true );
493 /* The item gets older */
494 p_prev = p_elt;
495 p_elt->i_age++;
496 p_elt->i_update = false;
498 else if( p_elt->i_refs == 0 )
500 if( p_elt->i_update )
501 watch_update_Item( p_ml, p_elt->i_media_id, p_elt->p_item,
502 ( p_elt->i_update & 2 ) ? true : false, true );
503 __watch_del_Item( p_ml, p_elt->p_item, true );
504 /* TODO: Do something about below crazy hack */
505 if( p_prev != NULL )
506 p_elt = p_prev;
507 else
509 ixx--;
510 break;
513 else
515 p_prev = p_elt;
516 p_elt->i_age++;
519 vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
523 * This function goes through a queue of input_items and checks
524 * if they are present in ML. All the items we wish to add in the
525 * watch Queue
527 static void watch_ProcessAppendQueue( media_library_t* p_ml )
529 watch_thread_t* p_wt = p_ml->p_sys->p_watch;
530 vlc_mutex_lock( &p_wt->item_append_queue_lock );
531 bool b_add = var_CreateGetBool( p_ml, "ml-auto-add" );
532 for( int i = 0; i < p_wt->item_append_queue_count; i++ )
534 input_item_t* p_item = p_wt->item_append_queue[i];
535 ml_media_t* p_media = NULL;
536 /* Is this item in ML? */
537 int i_media_id = GetMediaIdOfURI( p_ml, p_item->psz_uri );
538 int i_ret = 0;
539 if( i_media_id <= 0 )
541 if( b_add )
543 i_ret = AddInputItem( p_ml, p_item );
544 /* FIXME: Need to skip? */
545 if( i_ret != VLC_SUCCESS )
546 continue;
547 i_media_id = GetMediaIdOfURI( p_ml, p_item->psz_uri );
549 else
550 continue;
552 vlc_mutex_lock( &p_wt->list_mutex );
553 p_media = media_New( p_ml, i_media_id, ML_MEDIA, true );
554 if( p_media == NULL )
556 vlc_mutex_unlock( &p_wt->list_mutex );
557 continue;
559 /* If duplicate, then it just continues */
560 i_ret = __watch_add_Item( p_ml, p_item, p_media, true );
561 if( i_ret != VLC_SUCCESS )
563 ml_gc_decref( p_media );
564 vlc_mutex_unlock( &p_wt->list_mutex );
565 continue;
568 /* Find the new item and increment its ref */
569 il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
571 if( p_elt->p_item->i_id == p_item->i_id )
573 p_elt->i_refs++;
574 break;
577 vlc_mutex_unlock( &p_wt->list_mutex );
578 ml_gc_decref( p_media );
580 p_wt->item_append_queue_count = 0;
581 FREENULL( p_wt->item_append_queue );
582 vlc_mutex_unlock( &p_wt->item_append_queue_lock );