Commit 1237aadc authored by Srikanth Raju's avatar Srikanth Raju

Media library features sql_monitor - directory scanner ml_watch - playlist...

Media library features sql_monitor - directory scanner ml_watch - playlist watcher item_list - items being watched by ml_watch media_pool - list of active media in vlc
parent 98d2036a
/*****************************************************************************
* item_list.c: An input_item_t+media_id couple list for the media library
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN Team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
#include "item_list.h"
/**
* @short item hash list for the media library monitoring system
*/
/**
* @brief Add an item to the head of the list
* @param p_ml
* @param p_media media object. ID must be non zero and valid
* @param p_item input item to add, MUST NOT be NULL
* @param locked flag set if the list is locked. do not use
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int __item_list_add( watch_thread_t *p_wt, ml_media_t* p_media, input_item_t *p_item,
bool locked )
{
if( !locked )
vlc_mutex_lock( &p_wt->list_mutex );
ml_LockMedia( p_media );
assert( p_media->i_id );
/* Ensure duplication does not occur */
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item->i_id == p_item->i_id )
{
ml_UnlockMedia( p_media );
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_EGENERIC;
}
}
item_list_t *p_new = ( item_list_t* ) calloc( 1, sizeof( item_list_t ) );
if( !p_new )
{
ml_UnlockMedia( p_media );
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_ENOMEM;
}
p_new->p_next = p_wt->p_hlist[ item_hash( p_item ) ];
p_new->i_refs = 1;
p_new->i_media_id = p_media->i_id;
p_new->p_media = p_media;
p_new->p_item = p_item;
p_wt->p_hlist[ item_hash( p_item ) ] = p_new;
ml_UnlockMedia( p_media );
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_SUCCESS;
}
/**
* @brief Delete an item from the list
* @param p_ml this media library
* @param i_media_id media library's media ID
*/
item_list_t* item_list_delMedia( watch_thread_t *p_wt, int i_media_id )
{
vlc_mutex_lock( &p_wt->list_mutex );
item_list_t *p_prev = NULL;
il_foreachhashlist( p_wt->p_hlist, p_elt, ixx )
{
if( p_elt->i_media_id == i_media_id )
{
if( p_prev )
p_prev->p_next = p_elt->p_next;
else
p_wt->p_hlist[ixx] = p_elt->p_next;
p_elt->p_next = NULL;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt;
}
else
{
p_prev = p_elt;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Delete an item from the list and return the single element
* @param p_ml this media library
* @param p_item item to delete
* @return The element from the list containing p_item
*/
item_list_t* item_list_delItem( watch_thread_t *p_wt, input_item_t *p_item, bool locked )
{
if( !locked )
vlc_mutex_lock( &p_wt->list_mutex );
item_list_t *p_prev = NULL;
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
if( p_prev )
p_prev->p_next = p_elt->p_next;
else
p_wt->p_hlist[ item_hash( p_item ) ] = p_elt->p_next;
p_elt->p_next = NULL;
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt;
}
else
{
p_prev = p_elt;
}
}
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Find an input item
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return input item if found, NULL if not found
*/
input_item_t* item_list_itemOfMediaId( watch_thread_t *p_wt, int i_media_id )
{
item_list_t* p_tmp = item_list_listitemOfMediaId( p_wt, i_media_id );
if( p_tmp )
return p_tmp->p_item;
else
return NULL;
}
/**
* @brief Find an item list item
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return input item if found, NULL if not found
*/
item_list_t* item_list_listitemOfMediaId( watch_thread_t *p_wt, int i_media_id )
{
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachhashlist( p_wt->p_hlist, p_elt, ixx )
{
if( p_elt->i_media_id == i_media_id )
{
p_elt->i_age = 0;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Find a media
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return media if found. NULL otherwise
*/
ml_media_t* item_list_mediaOfMediaId( watch_thread_t *p_wt, int i_media_id )
{
item_list_t* p_tmp = item_list_listitemOfMediaId( p_wt, i_media_id );
if( p_tmp )
return p_tmp->p_media;
else
return NULL;
}
/**
* @brief Find a media ID by its input_item
* @param p_ml this media library
* @param p_item item to find
* @return media_id found, or VLC_EGENERIC
*/
int item_list_mediaIdOfItem( watch_thread_t *p_wt, input_item_t *p_item )
{
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
if( p_elt->i_media_id <= 0 )
/* TODO! */
p_elt->i_age = 0;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt->i_media_id;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_EGENERIC;
}
/**
* @brief Find a media by its input_item
* @param p_ml this media library
* @param p_item item to find
* @return media found, or VLC_EGENERIC
*/
ml_media_t* item_list_mediaOfItem( watch_thread_t *p_wt, input_item_t* p_item,
bool locked )
{
if( !locked )
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
p_wt->p_hlist[ item_hash( p_item ) ] = p_elt->p_next;
p_elt->p_next = NULL;
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt->p_media;
}
}
if( !locked )
vlc_mutex_unlock( &p_wt->list_mutex );
return NULL;
}
/**
* @brief Flag an item as updated
* @param p_ml this media library
* @param p_item item to find and flag
* @param b_played raise play count or not, update last play
* @return media_id found, or VLC_EGENERIC
*/
int item_list_updateInput( watch_thread_t *p_wt, input_item_t *p_item,
bool b_played )
{
vlc_mutex_lock( &p_wt->list_mutex );
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item == p_item )
{
/* Item found, flag and return */
p_elt->i_age = 0;
p_elt->i_update |= b_played ? 3 : 1;
vlc_mutex_unlock( &p_wt->list_mutex );
return p_elt->i_media_id;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
return VLC_EGENERIC;
}
/**
* @brief Free every item in the item list.
* @param p_wt Watch thread
* @note All decref of objects must be handled by watch system
*/
void item_list_destroy( watch_thread_t* p_wt )
{
vlc_mutex_lock( &p_wt->list_mutex );
for( int i = 0; i < ML_ITEMLIST_HASH_LENGTH ; i++ )
{
for( item_list_t* p_elt = p_wt->p_hlist[i] ; p_elt; p_elt = p_wt->p_hlist[i] )
{
p_wt->p_hlist[i] = p_elt->p_next;
free( p_elt );
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
}
/*****************************************************************************
* item_list.h : Item list data structure for Watching system
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef ML_ITEM_LIST_H
#define ML_ITEM_LIST_H
#include <vlc_input.h>
#include <vlc_arrays.h>
struct watch_thread_t;
typedef struct watch_thread_t watch_thread_t;
typedef struct item_list_t item_list_t;
/**
* Definition of item_list_t
*/
struct item_list_t {
input_item_t *p_item; /**< Input item */
ml_media_t *p_media; /**< Media item */
item_list_t *p_next; /**< Next element in the list */
int i_media_id; /**< Media id */
int i_age; /**< Time spent in this list without activity */
int i_refs; /**< Number of important refs */
int i_update; /**< Flag set when the input item is updated:
0: no update,
1: meta update,
2: increment play count,
3: both */
};
#define ML_ITEMLIST_HASH_LENGTH 40
#define il_foreachhashlist( a, b, c ) \
for( int c = 0 ; c < ML_ITEMLIST_HASH_LENGTH ; c++ ) \
for( item_list_t* b = a[c]; b; b = b->p_next )
#define il_foreachlist( a, b ) for( item_list_t* b = a ; b; b = b->p_next )
#define item_list_add( a, b, c ) __item_list_add( a, b, c, false )
int __item_list_add( watch_thread_t *p_wt, ml_media_t* p_media,
input_item_t *p_item, bool );
item_list_t* item_list_delMedia( watch_thread_t *p_wt, int i_media_id );
item_list_t* item_list_delItem( watch_thread_t *p_wt, input_item_t *p_item, bool );
item_list_t* item_list_listitemOfMediaId( watch_thread_t *p_wt, int i_media_id );
input_item_t* item_list_itemOfMediaId( watch_thread_t *p_wt, int i_media_id );
ml_media_t* item_list_mediaOfMediaId( watch_thread_t *p_wt, int i_media_id );
ml_media_t* item_list_mediaOfItem( watch_thread_t *p_wt, input_item_t* p_item, bool );
int item_list_mediaIdOfItem( watch_thread_t *p_wt, input_item_t *p_item );
int item_list_updateInput( watch_thread_t *p_wt, input_item_t *p_item,
bool b_play_count );
void item_list_destroy( watch_thread_t* p_wt );
/**
* @brief Simple hash function
* @param item_id Hash Key
* @return Hash index
*/
static inline int item_hash( input_item_t* p_item )
{
return DictHash( p_item->psz_uri, ML_ITEMLIST_HASH_LENGTH );
}
#endif /* ML_ITEM_LIST_H */
/*****************************************************************************
* media_pool.c : Media pool for watching system
*****************************************************************************
* Copyright (C) 2009-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
#define mp_foreachlist( a, b ) for( ml_poolobject_t* b = a; b; b = b->p_next )
static inline int mediapool_hash( int media_id )
{
return media_id % ML_MEDIAPOOL_HASH_LENGTH;
}
/**
* @brief Get a media from the pool
* @param p_ml ML object
* @param media_id The media id of the object to get
* @return the found media or NULL if not found
*/
ml_media_t* pool_GetMedia( media_library_t* p_ml, int media_id )
{
vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
ml_media_t* p_media = NULL;
mp_foreachlist( p_ml->p_sys->p_mediapool[ mediapool_hash( media_id ) ], p_item )
{
if( p_item->p_media->i_id == media_id )
{
p_media = p_item->p_media;
break;
}
}
if( p_media )
ml_gc_incref( p_media );
vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
return p_media;
}
/**
* @brief Insert a media into the media pool
* @param p_ml ML object
* @param p_media Media object to insert
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int pool_InsertMedia( media_library_t* p_ml, ml_media_t* p_media, bool locked )
{
if( !locked )
ml_LockMedia( p_media );
assert( p_media );
assert( p_media->i_id > 0 );
vlc_spin_lock( &p_media->ml_gc_data.spin );
if( p_media->ml_gc_data.pool )
{
msg_Dbg( p_ml, "Already in pool! %s %d", p_media->psz_uri, p_media->i_id );
ml_UnlockMedia( p_media );
return VLC_EGENERIC;
}
p_media->ml_gc_data.pool = true;
vlc_spin_unlock( &p_media->ml_gc_data.spin );
int i_ret = VLC_SUCCESS;
vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
mp_foreachlist( p_ml->p_sys->p_mediapool[ (mediapool_hash(p_media->i_id)) ], p_item )
{
if( p_media == p_item->p_media )
{
i_ret = VLC_EGENERIC;
break;
}
else if( p_media->i_id == p_item->p_media->i_id )
{
i_ret = VLC_EGENERIC;
msg_Warn( p_ml, "A media of the same id was found, but in different objects!" );
break;
}
}
if( i_ret == VLC_SUCCESS )
{
ml_poolobject_t* p_new = ( ml_poolobject_t * ) calloc( 1, sizeof( ml_poolobject_t* ) );
if( !p_new )
i_ret = VLC_EGENERIC;
else
{
ml_gc_incref( p_media );
p_new->p_media = p_media;
p_new->p_next = p_ml->p_sys->p_mediapool[ ( mediapool_hash( p_media->i_id ) ) ];
p_ml->p_sys->p_mediapool[ ( mediapool_hash( p_media->i_id ) ) ] = p_new;
}
}
vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
if( !locked )
ml_UnlockMedia( p_media );
return i_ret;
}
/**
* @brief Perform a single garbage collection scan on the media pool
* @param p_ml The ML object
* @note Scans all media and removes any medias not held by any other objects.
*/
void pool_GC( media_library_t* p_ml )
{
vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
ml_poolobject_t* p_prev = NULL;
ml_media_t* p_media = NULL;
for( int i_idx = 0; i_idx < ML_MEDIAPOOL_HASH_LENGTH; i_idx++ )
{
p_prev = NULL;
for( ml_poolobject_t* p_item = p_ml->p_sys->p_mediapool[ i_idx ];
p_item != NULL; p_item = p_item->p_next )
{
p_media = p_item->p_media;
int refs;
vlc_spin_lock( &p_media->ml_gc_data.spin );
refs = p_media->ml_gc_data.refs;
vlc_spin_unlock( &p_media->ml_gc_data.spin );
if( refs == 1 )
{
if( p_prev == NULL )
p_ml->p_sys->p_mediapool[i_idx] = p_item->p_next;
else
p_prev->p_next = p_item->p_next;
vlc_spin_lock( &p_media->ml_gc_data.spin );
p_media->ml_gc_data.pool = false;
vlc_spin_unlock( &p_media->ml_gc_data.spin );
ml_gc_decref( p_item->p_media );//This should destroy the object
free( p_item );
}
p_prev = p_item;
}
}
vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
}
/*****************************************************************************
* ml_watch.c: SQL-based media library: Medias watching system
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
#include "item_list.h"
#include <vlc_events.h>
static void* watch_Thread( vlc_object_t *p_this );
static void watch_ItemChange( const vlc_event_t *, void * );
static int watch_PlaylistItemCurrent( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *data );
static int watch_PlaylistItemAppend( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *data );
static int watch_PlaylistItemDeleted( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *data );
static void watch_loop( media_library_t *p_ml, bool b_force );
static void watch_Thread_Cleanup( void* p_object );
static int watch_update_Item( media_library_t *p_ml, int i_media_id,
input_item_t *p_item, bool b_raise_count, bool locked );
static void watch_ProcessAppendQueue( media_library_t* p_ml );
/**
* @brief Watching thread
*/
static void* watch_Thread( vlc_object_t *p_this )
{
watch_thread_t *p_watch = ( watch_thread_t* ) p_this;
media_library_t *p_ml = p_watch->p_ml;
int i_ret = 0;
vlc_mutex_lock( &p_watch->lock );
vlc_cleanup_push( watch_Thread_Cleanup, p_ml );
while( vlc_object_alive( p_watch ) )
{
watch_loop( p_ml, !i_ret );
if( !vlc_object_alive( p_watch ) )
break;
i_ret = vlc_cond_timedwait( &p_watch->cond, &p_watch->lock,
mdate() + 1000000 * THREAD_SLEEP_DELAY );
}
vlc_cleanup_run();
return NULL;
}
/**
* @brief Callback for thread exit
*/
static void watch_Thread_Cleanup( void* p_object )
{
media_library_t* p_ml = ( media_library_t* )p_object;
watch_loop( p_ml, true );
vlc_mutex_unlock( &p_ml->p_sys->p_watch->lock );
}
/**
* @brief Init watching system
* @return Error if the object or the thread could not be created
*/
int watch_Init( media_library_t *p_ml )
{
/* init and launch watching thread */
p_ml->p_sys->p_watch = vlc_object_create( p_ml, sizeof(watch_thread_t) );
if( !p_ml->p_sys->p_watch )
return VLC_ENOMEM;
watch_thread_t* p_wt = p_ml->p_sys->p_watch;
vlc_mutex_init( &p_wt->list_mutex );
p_wt->p_ml = p_ml;
vlc_object_attach( p_wt, p_ml );
vlc_cond_init( &p_wt->cond );
vlc_mutex_init( &p_wt->lock );
if( vlc_thread_create( p_wt, "Media Library Auto-Update",
watch_Thread, VLC_THREAD_PRIORITY_LOW ) )
{
msg_Dbg( p_ml, "unable to launch the auto-updating thread" );
vlc_object_release( p_wt );
return VLC_EGENERIC;
}
/* Wait on playlist events
* playlist-item-append -> entry to playlist
* item-current -> to ensure that we catch played item only!
* playlist-item-deleted -> exit from playlist
* item-change -> Currently not required, as we monitor input_item events
*/
playlist_t *p_pl = pl_Get( p_ml );
var_AddCallback( p_pl, "item-current", watch_PlaylistItemCurrent, p_ml );
var_AddCallback( p_pl, "playlist-item-append", watch_PlaylistItemAppend, p_ml );
var_AddCallback( p_pl, "playlist-item-deleted", watch_PlaylistItemDeleted, p_ml );
/* Initialise item append queue */
vlc_mutex_init( &p_wt->item_append_queue_lock );
p_wt->item_append_queue = NULL;
p_wt->item_append_queue_count = 0;
return VLC_SUCCESS;
}
/**
* @brief Add the input to the watch system
* @param p_ml The Media Library Object
* @param p_item Item to be watched
* @param p_media Corresponding media item to sync with
* @param locked Status of item list lock
* @return VLC_SUCCESS or error code
*/
int __watch_add_Item( media_library_t *p_ml, input_item_t *p_item,
ml_media_t* p_media, bool locked )
{
vlc_gc_incref( p_item );
ml_gc_incref( p_media );
int i_ret = __item_list_add( p_ml->p_sys->p_watch, p_media, p_item, locked );
if( i_ret != VLC_SUCCESS )
return i_ret;
vlc_event_manager_t *p_em = &p_item->event_manager;
vlc_event_attach( p_em, vlc_InputItemMetaChanged, watch_ItemChange, p_ml );
vlc_event_attach( p_em, vlc_InputItemNameChanged, watch_ItemChange, p_ml );
vlc_event_attach( p_em, vlc_InputItemInfoChanged, watch_ItemChange, p_ml );
/*
Note: vlc_InputItemDurationChanged is disabled because
it is triggered too often, even without consequent changes
*/
return VLC_SUCCESS;
}
/**
* @brief Detach event manager
* @param p_ml The Media Library Object
*/
static void detachItemEvents( media_library_t *p_ml, input_item_t *p_item )
{
vlc_event_manager_t *p_em = &p_item->event_manager;
vlc_event_detach( p_em, vlc_InputItemMetaChanged, watch_ItemChange, p_ml );
vlc_event_detach( p_em, vlc_InputItemNameChanged, watch_ItemChange, p_ml );
vlc_event_detach( p_em, vlc_InputItemInfoChanged, watch_ItemChange, p_ml );
}
/**
* @brief Close the watching system
* @param p_ml The Media Library Object
*/
void watch_Close( media_library_t *p_ml )
{
playlist_t *p_pl = pl_Get( p_ml );
var_DelCallback( p_pl, "item-current", watch_PlaylistItemCurrent, p_ml );
var_DelCallback( p_pl, "playlist-item-append", watch_PlaylistItemAppend, p_ml );
var_DelCallback( p_pl, "playlist-item-deleted", watch_PlaylistItemDeleted, p_ml );
/* Stop the watch thread and join in */
vlc_object_kill( p_ml->p_sys->p_watch );
vlc_thread_join( p_ml->p_sys->p_watch );
/* Flush item list */
il_foreachhashlist( p_ml->p_sys->p_watch->p_hlist, p_elt, ixx )
{
detachItemEvents( p_ml, p_elt->p_item );
ml_gc_decref( p_elt->p_media );
vlc_gc_decref( p_elt->p_item );
}
item_list_destroy( p_ml->p_sys->p_watch );
/* Clear up other stuff */
vlc_mutex_destroy( &p_ml->p_sys->p_watch->lock );
vlc_cond_destroy( &p_ml->p_sys->p_watch->cond );
vlc_mutex_destroy( &p_ml->p_sys->p_watch->list_mutex );
vlc_object_release( p_ml->p_sys->p_watch );
free( p_ml->p_sys->p_watch->item_append_queue );
vlc_mutex_destroy( &p_ml->p_sys->p_watch->item_append_queue_lock );
p_ml->p_sys->p_watch = NULL;
}
/**
* @brief Del item that is currently being watched
* @param p_ml The Media Library Object
* @param p_item Item to stop watching
* @param locked Lock state of item list
*/
int __watch_del_Item( media_library_t* p_ml, input_item_t* p_item, bool locked )
{
assert( p_item );
item_list_t* p_tmp = item_list_delItem( p_ml->p_sys->p_watch, p_item, locked );
if( p_tmp == NULL )
return VLC_EGENERIC;
detachItemEvents( p_ml, p_tmp->p_item );
vlc_gc_decref( p_tmp->p_item );
ml_gc_decref( p_tmp->p_media );
free( p_tmp );
return VLC_SUCCESS;
}
/**
* @brief Del media from watching by ID
* @param p_ml The Media Library Object
* @param i_media_id Media ID
*/
int watch_del_MediaById( media_library_t* p_ml, int i_media_id )
{
assert( i_media_id > 0 );
item_list_t* p_elt = item_list_delMedia( p_ml->p_sys->p_watch, i_media_id );
if( p_elt == NULL )
return VLC_EGENERIC;
detachItemEvents( p_ml, p_elt->p_item );
vlc_gc_decref( p_elt->p_item );
ml_gc_decref( p_elt->p_media );
free( p_elt );
return VLC_SUCCESS;
}
/**
* @brief Get item using media id, if exists in item list
* @param p_ml The Media Library Object
* @param i_media_id Media ID
*/
input_item_t* watch_get_itemOfMediaId( media_library_t *p_ml, int i_media_id )
{
input_item_t* p_tmp = item_list_itemOfMediaId( p_ml->p_sys->p_watch, i_media_id );
if( p_tmp == NULL )
return NULL;
vlc_gc_incref( p_tmp );
return p_tmp;
}
/**
* @brief Get media using media id, if exists in item list
* @param p_ml The Media Library Object
* @param i_media_id Media ID
*/
ml_media_t* watch_get_mediaOfMediaId( media_library_t* p_ml, int i_media_id )
{
ml_media_t* p_tmp = item_list_mediaOfMediaId( p_ml->p_sys->p_watch, i_media_id );
if( p_tmp == NULL )
return NULL;
ml_gc_incref( p_tmp );
return p_tmp;
}
/**
* @brief Get mediaid of existing item
* @param p_ml The Media Library Object
* @param p_item Pointer to input item
*/
int watch_get_mediaIdOfItem( media_library_t *p_ml, input_item_t *p_item )
{
return item_list_mediaIdOfItem( p_ml->p_sys->p_watch, p_item );
}
/**
* @brief Updates a media each time it is changed (name, info or meta)
*/
static void watch_ItemChange( const vlc_event_t *event, void *data )
{
input_item_t *p_item = ( input_item_t* ) event->p_obj;
media_library_t *p_ml = ( media_library_t* ) data;
/* Note: we don't add items to the item_list, but normally there should
not be any item at this point that is not in the list. */
if( item_list_updateInput( p_ml->p_sys->p_watch, p_item, false ) <= 0 )
{
#ifndef NDEBUG
msg_Dbg( p_ml, "Couldn't update in watch_ItemChange(): (%s:%d)",
__FILE__, __LINE__ );
#endif
}
/*
if( event->type == vlc_InputItemMetaChanged )
{
int id = item_list_mediaIdOfItem( p_ml->p_sys->p_watch, p_item );
if( !id ) return;
* Tell the world what happened *
var_SetInteger( p_ml, "media-meta-change", id );
}
*/
}
/**
* @brief Callback when item is added to playlist
*/
static int watch_PlaylistItemAppend( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *data )
{
VLC_UNUSED( oldval );
VLC_UNUSED( p_this );
VLC_UNUSED( psz_var );
media_library_t* p_ml = ( media_library_t* ) data;
playlist_t* p_playlist = pl_Get( p_ml );
playlist_add_t* p_add;
p_add = ( playlist_add_t* ) newval.p_address;
playlist_item_t* p_pitem = playlist_ItemGetById( p_playlist, p_add->i_item );
input_item_t* p_item = p_pitem->p_input;
watch_thread_t* p_wt = p_ml->p_sys->p_watch;
vlc_mutex_lock( &p_wt->list_mutex );
/* Check if we are already watching this item */
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item->i_id == p_item->i_id )
{
p_elt->i_refs++;
vlc_mutex_unlock( &p_wt->list_mutex );
goto quit_playlistitemappend;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
/* Add the the append queue */
vlc_mutex_lock( &p_wt->item_append_queue_lock );
p_wt->item_append_queue_count++;
p_wt->item_append_queue = realloc( p_wt->item_append_queue,
sizeof( input_item_t* ) * p_wt->item_append_queue_count );
vlc_gc_incref( p_item );
p_wt->item_append_queue[ p_wt->item_append_queue_count - 1 ] = p_item;
vlc_mutex_unlock( &p_wt->item_append_queue_lock );
quit_playlistitemappend:
return VLC_SUCCESS;
}
/**
* @brief Callback when item is deleted from playlist
*/
static int watch_PlaylistItemDeleted( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *data )
{
VLC_UNUSED( oldval );
VLC_UNUSED( p_this );
VLC_UNUSED( psz_var );
media_library_t* p_ml = ( media_library_t* ) data;
playlist_t* p_playlist = pl_Get( p_ml );
/* Luckily this works, because the item isn't deleted from PL, yet */
playlist_item_t* p_pitem = playlist_ItemGetById( p_playlist, newval.i_int );
input_item_t* p_item = p_pitem->p_input;
/* Find the new item and decrement its ref */
il_foreachlist( p_ml->p_sys->p_watch->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item->i_id == p_item->i_id )
{
p_elt->i_refs--;
break;
}
}
return VLC_SUCCESS;
}
/**
* @brief Callback when watched input item starts playing
* @note This will update playcount mainly
* TODO: Increment playcount on playing 50%(configurable)
*/
static int watch_PlaylistItemCurrent( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *data )
{
(void)p_this;
(void)oldval;
(void)newval;
media_library_t *p_ml = ( media_library_t* ) data;
input_item_t *p_item = NULL;
if( strcmp( psz_var, "item-current" ) != 0 )
/* This case should not happen... */
return VLC_EGENERIC;
/* Get current input */
input_thread_t *p_input = pl_CurrentInput( p_ml );
p_item = p_input ? input_GetItem( p_input ) : NULL;
if( p_input )
vlc_object_release( p_input );
if( !p_item )
return VLC_EGENERIC;
if( item_list_updateInput( p_ml->p_sys->p_watch, p_item, true ) == 0 )
{
#ifndef NDEBUG
msg_Dbg( p_ml, "couldn't in watch_PlaylistItemCurrent(): (%s:%d)",
__FILE__, __LINE__ );
#endif
}
return VLC_SUCCESS;
}
/**
* @brief Update informations in the DB for an input item
*
* @param p_ml this media library instance
* @param i_media_id may be 0 (but not recommended)
* @param p_item input item that was updated
* @param b_raise_count increment the played count
* @return result of UpdateMedia()
*/
static int watch_update_Item( media_library_t *p_ml,
int i_media_id, input_item_t *p_item,
bool b_raise_count, bool locked )
{
#ifndef NDEBUG
msg_Dbg( p_ml, "automatically updating media %d", i_media_id );
#endif
ml_media_t* p_media = item_list_mediaOfItem( p_ml->p_sys->p_watch, p_item, locked );
CopyInputItemToMedia( p_media, p_item );
ml_LockMedia( p_media );
p_media->i_played_count += b_raise_count ? 1 : 0;
ml_UnlockMedia( p_media );
int i_ret = UpdateMedia( p_ml, p_media );
/* Add the poster to the album */
ml_LockMedia( p_media );
if( p_media->i_album_id && p_media->psz_cover )
{
SetArtCover( p_ml, p_media->i_album_id, p_media->psz_cover );
}
ml_UnlockMedia( p_media );
return i_ret;
}
/**
* @brief Signals the watch system to update all medias
*/
void watch_Force_Update( media_library_t* p_ml )
{
vlc_mutex_lock( &p_ml->p_sys->p_watch->lock );
vlc_cond_signal( &p_ml->p_sys->p_watch->cond );
vlc_mutex_unlock( &p_ml->p_sys->p_watch->lock );
}
/**
* @brief Loop on the item_list: old objects collector and automatic updater
*
* This function is *not* a garbage collector. It actually decrefs items
* when they are too old. ITEM_GC_MAX_AGE is the maximum 'time' an item
* can stay in the list. After that, it is gc_decref'ed but not removed
* from this list. If you try to get it after that, either the input item
* is still alive, then you get it, or you'll have
*
* The update of an item is done when its age is >= ITEM_LOOP_UPDATE
* (0 could lead to a too early update)
*
* A thread should call this function every N seconds
*
* @param p_ml the media library instance
*/
static void watch_loop( media_library_t *p_ml, bool b_force )
{
/* Do the garbage collection */
pool_GC( p_ml );
/* Process the append queue */
watch_ProcessAppendQueue( p_ml );
/* Do the item update if necessary */
vlc_mutex_lock( &p_ml->p_sys->p_watch->list_mutex );
item_list_t *p_prev = NULL;
il_foreachhashlist( p_ml->p_sys->p_watch->p_hlist, p_elt, ixx )
{
if( ( p_elt->i_update && p_elt->i_age >= ITEM_LOOP_UPDATE )
|| b_force )
{
/* This is the automatic delayed update */
watch_update_Item( p_ml, p_elt->i_media_id, p_elt->p_item,
( p_elt->i_update & 2 ) ? true : false, true );
/* The item gets older */
p_prev = p_elt;
p_elt->i_age++;
p_elt->i_update = false;
}
else if( p_elt->i_refs == 0 )
{
if( p_elt->i_update )
watch_update_Item( p_ml, p_elt->i_media_id, p_elt->p_item,
( p_elt->i_update & 2 ) ? true : false, true );
__watch_del_Item( p_ml, p_elt->p_item, true );
/* TODO: Do something about below crazy hack */
if( p_prev != NULL )
p_elt = p_prev;
else
{
ixx--;
break;
}
}
else
{
p_prev = p_elt;
p_elt->i_age++;
}
}
vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
}
/**
* This function goes through a queue of input_items and checks
* if they are present in ML. All the items we wish to add in the
* watch Queue
*/
static void watch_ProcessAppendQueue( media_library_t* p_ml )
{
watch_thread_t* p_wt = p_ml->p_sys->p_watch;
vlc_mutex_lock( &p_wt->item_append_queue_lock );
bool b_add = var_CreateGetBool( p_ml, "ml-auto-add" );
for( int i = 0; i < p_wt->item_append_queue_count; i++ )
{
input_item_t* p_item = p_wt->item_append_queue[i];
ml_media_t* p_media = NULL;
/* Is this item in ML? */
int i_media_id = GetMediaIdOfURI( p_ml, p_item->psz_uri );
int i_ret = 0;
if( i_media_id <= 0 )
{
if( b_add )
{
i_ret = AddInputItem( p_ml, p_item );
/* FIXME: Need to skip? */
if( i_ret != VLC_SUCCESS )
continue;
i_media_id = GetMediaIdOfURI( p_ml, p_item->psz_uri );
}
else
continue;
}
vlc_mutex_lock( &p_wt->list_mutex );
p_media = media_New( p_ml, i_media_id, ML_MEDIA, true );
if( p_media == NULL )
continue;
/* If duplicate, then it just continues */
i_ret = __watch_add_Item( p_ml, p_item, p_media, true );
if( i_ret != VLC_SUCCESS )
{
ml_gc_decref( p_media );
continue;
}
/* Find the new item and increment its ref */
il_foreachlist( p_wt->p_hlist[ item_hash( p_item ) ], p_elt )
{
if( p_elt->p_item->i_id == p_item->i_id )
{
p_elt->i_refs++;
break;
}
}
vlc_mutex_unlock( &p_wt->list_mutex );
ml_gc_decref( p_media );
}
p_wt->item_append_queue_count = 0;
FREENULL( p_wt->item_append_queue );
vlc_mutex_unlock( &p_wt->item_append_queue_lock );
}
/*****************************************************************************
* sql_monitor.c: SQL-based media library: directory scanning and monitoring
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/** **************************************************************************
* MONITORING AND DIRECTORY SCANNING FUNCTIONS
*****************************************************************************/
#include "sql_media_library.h"
#include "vlc_playlist.h"
#include "vlc_url.h"
#include "vlc_fs.h"
static const char* ppsz_MediaExtensions[] =
{ EXTENSIONS_AUDIO_CSV, EXTENSIONS_VIDEO_CSV, NULL };
/* Monitoring and directory scanning private functions */
typedef struct stat_list_t stat_list_t;
typedef struct preparsed_item_t preparsed_item_t;
static void UpdateLibrary( monitoring_thread_t *p_mon );
static void ScanFiles( monitoring_thread_t *, int, bool, stat_list_t *stparent );
static int Sort( const char **, const char ** );
/* Struct used to verify there are no recursive directory */
struct stat_list_t
{
stat_list_t *parent;
struct stat st;
};
struct preparsed_item_t
{
monitoring_thread_t *p_mon;
char* psz_uri;
int i_dir_id;
int i_mtime;
int i_update_id;
bool b_update;
};
/**
* @brief Remove a directory to monitor
* @param p_ml A media library object
* @param psz_dir the directory to remove
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int RemoveDirToMonitor( media_library_t *p_ml, const char *psz_dir )
{
assert( p_ml );
char **pp_results = NULL;
int i_cols = 0, i_rows = 0, i_ret = VLC_SUCCESS;
int i;
bool b_recursive = var_CreateGetBool( p_ml, "ml-recursive-scan" );
if( b_recursive )
{
i_ret = Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT media.id FROM media JOIN directories ON "
"(media.directory_id = directories.id) WHERE "
"directories.uri LIKE '%q%%'",
psz_dir );
if( i_ret != VLC_SUCCESS )
{
msg_Err( p_ml, "Error occured while making a query to the database" );
return i_ret;
}
QuerySimple( p_ml, "DELETE FROM directories WHERE uri LIKE '%q%%'",
psz_dir );
}
else
{
i_ret = Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT media.id FROM media JOIN directories ON "
"(media.directory_id = directories.id) WHERE "
"directories.uri = %Q",
psz_dir );
if( i_ret != VLC_SUCCESS )
{
msg_Err( p_ml, "Error occured while making a query to the database" );
return i_ret;
}
QuerySimple( p_ml, "DELETE FROM directories WHERE uri = %Q",
psz_dir );
}
vlc_array_t *p_where = vlc_array_new();
for( i = 1; i <= i_rows; i++ )
{
int id = atoi( pp_results[i*i_cols] );
ml_element_t* p_find = ( ml_element_t * ) calloc( 1, sizeof( ml_element_t ) );
p_find->criteria = ML_ID;
p_find->value.i = id;
vlc_array_append( p_where, p_find );
}
Delete( p_ml, p_where );
FreeSQLResult( p_ml, pp_results );
for( i = 0; i < vlc_array_count( p_where ); i++ )
{
free( vlc_array_item_at_index( p_where, i ) );
}
vlc_array_destroy( p_where );
return VLC_SUCCESS;
}
/**
* @brief Get the list of the monitored directories
* @param p_ml A media library object
* @param p_array An initialized array where the list will be put in
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int ListMonitoredDirs( media_library_t *p_ml, vlc_array_t *p_array )
{
char **pp_results;
int i_cols, i_rows;
int i;
if( Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT uri AS directory_uri FROM directories WHERE recursive=0" )
!= VLC_SUCCESS )
return VLC_EGENERIC;
for( i = 1; i <= i_rows; i++ )
{
vlc_array_append( p_array, strdup( pp_results[i] ) );
}
FreeSQLResult( p_ml, pp_results );
return VLC_SUCCESS;
}
/**
* @brief Add a directory to monitor
* @param p_ml This media_library_t object
* @param psz_dir the directory to add
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int AddDirToMonitor( media_library_t *p_ml, const char *psz_dir )
{
assert( p_ml );
/* Verify if we can open the directory */
DIR *dir = vlc_opendir( psz_dir );
if( !dir )
{
int err = errno;
if( err != ENOTDIR )
msg_Err( p_ml, "%s: %m", psz_dir );
else
msg_Dbg( p_ml, "`%s' is not a directory", psz_dir );
errno = err;
return VLC_EGENERIC;
}
closedir( dir );
msg_Dbg( p_ml, "Adding directory `%s' to be monitored", psz_dir );
QuerySimple( p_ml, "INSERT INTO directories ( uri, timestamp, "
"recursive ) VALUES( %Q, 0, 0 )", psz_dir );
vlc_cond_signal( &p_ml->p_sys->p_mon->wait );
return VLC_SUCCESS;
}
static int Sort( const char **a, const char **b )
{
return strcoll( *a, *b );
}
/**
* @brief Directory Monitoring thread loop
*/
void *RunMonitoringThread( void *p_this )
{
monitoring_thread_t *p_mon = (monitoring_thread_t*) p_this;
vlc_cond_init( &p_mon->wait );
vlc_mutex_init( &p_mon->lock );
var_Create( p_mon, "ml-recursive-scan", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
while( vlc_object_alive( p_mon ) )
{
vlc_mutex_lock( &p_mon->lock );
/* Update */
UpdateLibrary( p_mon );
/* We wait MONITORING_DELAY seconds or wait that the media library
signals us to do something */
vlc_cond_timedwait( &p_mon->wait, &p_mon->lock,
mdate() + 1000000*MONITORING_DELAY );
vlc_mutex_unlock( &p_mon->lock );
}
vlc_cond_destroy( &p_mon->wait );
vlc_mutex_destroy( &p_mon->lock );
return NULL;
}
/**
* @brief Update library if new files found or updated
*/
static void UpdateLibrary( monitoring_thread_t *p_mon )
{
int i_rows, i_cols, i;
char **pp_results;
media_library_t *p_ml = p_mon->p_ml;
struct stat s_stat;
bool b_recursive = var_GetBool( p_mon, "ml-recursive-scan" );
msg_Dbg( p_mon, "Scanning directories" );
Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT id AS directory_id, uri AS directory_uri, "
"timestamp AS directory_ts FROM directories" );
msg_Dbg( p_mon, "%d directories to scan", i_rows );
for( i = 1; i <= i_rows; i++ )
{
int id = atoi( pp_results[i*i_cols] );
char *psz_dir = pp_results[i*i_cols+1];
int timestamp = atoi( pp_results[i*i_cols+2] );
if( vlc_stat( psz_dir, &s_stat ) == -1 )
{
int err = errno;
if( err == ENOTDIR || err == ENOENT )
{
msg_Dbg( p_mon, "Removing `%s'", psz_dir );
RemoveDirToMonitor( p_ml, psz_dir );
}
else
{
msg_Err( p_mon, "%s: %m", psz_dir );
FreeSQLResult( p_ml, pp_results );
return;
}
errno = err;
}
if( !S_ISDIR( s_stat.st_mode ) )
{
msg_Dbg( p_mon, "Removing `%s'", psz_dir );
RemoveDirToMonitor( p_ml, psz_dir );
}
if( timestamp < s_stat.st_mtime )
{
msg_Dbg( p_mon, "Adding `%s'", psz_dir );
ScanFiles( p_mon, id, b_recursive, NULL );
}
}
FreeSQLResult( p_ml, pp_results );
}
/**
* @brief Callback for input item preparser to directory monitor
*/
static void PreparseComplete( const vlc_event_t * p_event, void *p_data )
{
int i_ret = VLC_SUCCESS;
preparsed_item_t* p_itemobject = (preparsed_item_t*) p_data;
monitoring_thread_t *p_mon = p_itemobject->p_mon;
media_library_t *p_ml = (media_library_t *)p_mon->p_ml;
input_item_t *p_input = (input_item_t*) p_event->p_obj;
if( input_item_IsPreparsed( p_input ) )
{
if( p_itemobject->b_update )
{
//TODO: Perhaps we don't have to load everything?
ml_media_t* p_media = GetMedia( p_ml, p_itemobject->i_update_id,
ML_MEDIA_SPARSE, true );
CopyInputItemToMedia( p_media, p_input );
i_ret = UpdateMedia( p_ml, p_media );
ml_gc_decref( p_media );
}
else
i_ret = AddInputItem( p_ml, p_input );
}
if( i_ret != VLC_SUCCESS )
msg_Dbg( p_mon, "Item could not be correctly added"
" or updated during scan: %s", p_input->psz_uri );
QuerySimple( p_ml, "UPDATE media SET directory_id=%d, timestamp=%d "
"WHERE id=%d",
p_itemobject->i_dir_id, p_itemobject->i_mtime,
GetMediaIdOfURI( p_ml, p_input->psz_uri ) );
vlc_event_detach( &p_input->event_manager, vlc_InputItemPreparsedChanged,
PreparseComplete, p_itemobject );
vlc_gc_decref( p_input );
free( p_itemobject->psz_uri );
}
/**
* @brief Scan files in a particular directory
*/
static void ScanFiles( monitoring_thread_t *p_mon, int i_dir_id,
bool b_recursive, stat_list_t *stparent )
{
int i_rows, i_cols, i_dir_content, i, i_mon_rows, i_mon_cols;
char **ppsz_monitored_files;
char **pp_results, *psz_dir;
char **pp_dir_content;
bool *pb_processed;
input_item_t *p_input;
struct stat s_stat;
media_library_t *p_ml = (media_library_t *)p_mon->p_ml;
Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT uri AS directory_uri FROM directories WHERE id = '%d'",
i_dir_id );
if( i_rows < 1 )
{
msg_Dbg( p_mon, "query returned no directory for dir_id: %d (%s:%d)",
i_dir_id, __FILE__, __LINE__ );
return;
}
psz_dir = strdup( pp_results[1] );
FreeSQLResult( p_ml, pp_results );
struct stat_list_t stself;
if( vlc_stat( psz_dir, &stself.st ) == -1 )
{
msg_Err( p_ml, "Cannot stat `%s': %m", psz_dir );
free( psz_dir );
return;
}
#ifndef WIN32
for( stat_list_t *stats = stparent; stats != NULL; stats = stats->parent )
{
if( ( stself.st.st_ino == stats->st.st_ino ) &&
( stself.st.st_dev == stats->st.st_dev ) )
{
msg_Warn( p_ml, "Ignoring infinitely recursive directory `%s'",
psz_dir );
free( psz_dir );
return;
}
}
#else
/* Windows has st_dev (driver letter - 'A'), but it zeroes st_ino,
* so that the test above will always incorrectly succeed.
* Besides, Windows does not have dirfd(). */
#endif
stself.parent = stparent;
QuerySimple( p_ml, "UPDATE directories SET timestamp=%d WHERE id = %d",
stself.st.st_mtime, i_dir_id );
Query( p_ml, &ppsz_monitored_files, &i_mon_rows, &i_mon_cols,
"SELECT id AS media_id, timestamp AS media_ts, uri AS media_uri "
"FROM media WHERE directory_id = %d",
i_dir_id );
pb_processed = malloc(sizeof(bool) * i_mon_rows);
for( i = 0; i < i_mon_rows ; i++)
pb_processed[i] = false;
i_dir_content = vlc_scandir( psz_dir, &pp_dir_content, NULL, Sort );
if( i_dir_content == -1 )
{
msg_Err( p_mon, "Cannot read `%s': %m", psz_dir );
free( psz_dir );
return;
}
else if( i_dir_content == 0 )
{
msg_Dbg( p_mon, "Nothing in directory `%s'", psz_dir );
free( psz_dir );
return;
}
for( i = 0; i < i_dir_content; i++ )
{
const char *psz_entry = pp_dir_content[i];
if( psz_entry[0] != '.' )
{
/* 7 is the size of "file://" */
char psz_uri[strlen(psz_dir) + strlen(psz_entry) + 2 + 7];
sprintf( psz_uri, "%s/%s", psz_dir, psz_entry );
if( vlc_stat( psz_uri, &s_stat ) == -1 )
{
msg_Err( p_mon, "%s: %m", psz_uri );
free( psz_dir );
return;
}
if( S_ISREG( s_stat.st_mode ) )
{
const char *psz_dot = strrchr( psz_uri, '.' );
if( psz_dot++ && *psz_dot )
{
int i_is_media = 0;
for( int a = 0; ppsz_MediaExtensions[a]; a++ )
{
if( !strcasecmp( psz_dot, ppsz_MediaExtensions[a] ) )
{
i_is_media = 1;
break;
}
}
if( !i_is_media )
{
msg_Dbg( p_mon, "ignoring file %s", psz_uri );
continue;
}
}
char * psz_tmp = encode_URI_component( psz_uri );
char * psz_encoded_uri = ( char * )calloc( strlen( psz_tmp ) + 9, 1 );
strcpy( psz_encoded_uri, "file:///" );
strcat( psz_encoded_uri, psz_tmp );
free( psz_tmp );
/* Check if given media is already in DB and it has been updated */
bool b_skip = false;
bool b_update = false;
int j = 1;
for( j = 1; j <= i_mon_rows; j++ )
{
if( strcasecmp( ppsz_monitored_files[ j * i_mon_cols + 2 ],
psz_encoded_uri ) != 0 )
continue;
b_update = true;
pb_processed[ j - 1 ] = true;
if( atoi( ppsz_monitored_files[ j * i_mon_cols + 1 ] )
< s_stat.st_mtime )
{
b_skip = false;
break;
}
else
{
b_skip = true;
break;
}
}
msg_Dbg( p_ml , "Checking if %s is in DB. Found: %d", psz_encoded_uri,
b_skip? 1 : 0 );
if( b_skip )
continue;
p_input = input_item_New( VLC_OBJECT(p_mon), psz_encoded_uri,
psz_entry );
playlist_t* p_pl = pl_Get( p_mon );
preparsed_item_t* p_itemobject;
p_itemobject = malloc( sizeof( preparsed_item_t ) );
p_itemobject->i_dir_id = i_dir_id;
p_itemobject->psz_uri = psz_encoded_uri;
p_itemobject->i_mtime = s_stat.st_mtime;
p_itemobject->p_mon = p_mon;
p_itemobject->b_update = b_update;
p_itemobject->i_update_id = b_update ?
atoi( ppsz_monitored_files[ j * i_mon_cols + 0 ] ) : 0 ;
vlc_event_manager_t *p_em = &p_input->event_manager;
vlc_event_attach( p_em, vlc_InputItemPreparsedChanged,
PreparseComplete, p_itemobject );
playlist_PreparseEnqueue( p_pl, p_input );
}
else if( S_ISDIR( s_stat.st_mode ) && b_recursive )
{
Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT id AS directory_id FROM directories "
"WHERE uri=%Q", psz_uri );
FreeSQLResult( p_ml, pp_results );
if( i_rows <= 0 )
{
msg_Dbg( p_mon, "New directory `%s' in dir of id %d",
psz_uri, i_dir_id );
QuerySimple( p_ml,
"INSERT INTO directories (uri, timestamp, "
"recursive) VALUES(%Q, 0, 1)", psz_uri );
// We get the id of the directory we've just added
Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT id AS directory_id FROM directories WHERE uri=%Q",
psz_uri );
if( i_rows <= 0 )
{
msg_Err( p_mon, "Directory `%s' was not sucessfully"
" added to the database", psz_uri );
FreeSQLResult( p_ml, pp_results );
continue;
}
ScanFiles( p_mon, atoi( pp_results[1] ), b_recursive,
&stself );
FreeSQLResult( p_ml, pp_results );
}
}
}
}
vlc_array_t* delete_ids = vlc_array_new();
for( i = 0; i < i_mon_rows; i++ )
{
if( pb_processed[i] == false )
{
/* This file doesn't exist anymore. Let's...urm...delete it. */
ml_element_t* find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
find->criteria = ML_ID;
find->value.i = atoi( ppsz_monitored_files[ (i + 1) * i_mon_cols ] );
vlc_array_append( delete_ids, find );
}
}
/* Delete the unfound media */
if( Delete( p_ml, delete_ids ) != VLC_SUCCESS )
msg_Dbg( p_ml, "Something went wrong in multi delete" );
for( i = 0; i < vlc_array_count( delete_ids ); i++ )
{
free( vlc_array_item_at_index( delete_ids, i ) );
}
vlc_array_destroy( delete_ids );
FreeSQLResult( p_ml, ppsz_monitored_files );
for( i = 0; i < i_dir_content; i++ )
free( pp_dir_content[i] );
free( pp_dir_content );
free( psz_dir );
free( pb_processed );
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment