From: Srikanth Raju Date: Mon, 21 Jun 2010 16:56:52 +0000 (+0530) Subject: ML: Media Library Core X-Git-Tag: 1.2.0-pre1~6057 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=6bff1cc92a66ab1a73ba1fbf22523671bf5c8ce3;p=vlc ML: Media Library Core Core functions and singleton media library object --- diff --git a/src/Makefile.am b/src/Makefile.am index 638c7e4ed1..1ce58d5635 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -77,6 +77,7 @@ pluginsinclude_HEADERS = \ ../include/vlc_md5.h \ ../include/vlc_messages.h \ ../include/vlc_meta.h \ + ../include/vlc_media_library.h \ ../include/vlc_modules.h \ ../include/vlc_mouse.h \ ../include/vlc_mtime.h \ @@ -452,6 +453,7 @@ SOURCES_libvlc_common = \ misc/update.c \ misc/update_crypto.c \ misc/xml.c \ + misc/media_library.c \ extras/libc.c \ extras/tdestroy.c \ misc/filter.c \ diff --git a/src/libvlc-module.c b/src/libvlc-module.c index 80962bee56..435fd65b18 100644 --- a/src/libvlc-module.c +++ b/src/libvlc-module.c @@ -1259,6 +1259,10 @@ static const char *const ppsz_albumart_descriptions[] = "The media library is automatically saved and reloaded each time you " \ "start VLC." ) +#define LOAD_ML_TEXT N_( "Load Media Library" ) +#define LOAD_ML_LONGTEXT N_( \ + "Enable this option to load the SQL-based Media Library at VLC startup" ) + #define PLTREE_TEXT N_("Display playlist tree") #define PLTREE_LONGTEXT N_( \ "The playlist can use a tree to categorize some items, like the " \ @@ -2123,6 +2127,10 @@ vlc_module_begin () add_bool( "play-and-pause", 0, NULL, PAP_TEXT, PAP_LONGTEXT, true ) change_safe() add_bool( "media-library", 1, NULL, ML_TEXT, ML_LONGTEXT, false ) +#if defined( MEDIA_LIBRARY ) + add_bool( "load-media-library-on-startup", 1, NULL, LOAD_ML_TEXT, + LOAD_ML_LONGTEXT, false ) +#endif add_bool( "playlist-tree", 0, NULL, PLTREE_TEXT, PLTREE_LONGTEXT, false ) add_string( "open", "", NULL, OPEN_TEXT, OPEN_LONGTEXT, false ) diff --git a/src/libvlc.c b/src/libvlc.c index efd4952445..9ec04a6b55 100644 --- a/src/libvlc.c +++ b/src/libvlc.c @@ -68,6 +68,8 @@ # include #endif + +#include #include #include @@ -251,6 +253,7 @@ libvlc_int_t * libvlc_InternalCreate( void ) priv = libvlc_priv (p_libvlc); priv->p_playlist = NULL; + priv->p_ml = NULL; priv->p_dialog_provider = NULL; priv->p_vlm = NULL; @@ -819,6 +822,23 @@ int libvlc_InternalInit( libvlc_int_t *p_libvlc, int i_argc, /* System specific configuration */ system_Configure( p_libvlc, i_argc - vlc_optind, ppsz_argv + vlc_optind ); +#if defined(MEDIA_LIBRARY) + /* Get the ML */ + if( var_GetBool( p_libvlc, "load-media-library-on-startup" ) == true ) + { + priv->p_ml = __ml_Create( VLC_OBJECT( p_libvlc ), NULL ); + if( !priv->p_ml ) + { + msg_Err( p_libvlc, "ML initialization failed" ); + return VLC_EGENERIC; + } + } + else + { + priv->p_ml = NULL; + } +#endif + /* Add service discovery modules */ psz_modules = var_InheritString( p_libvlc, "services-discovery" ); if( psz_modules ) @@ -1014,6 +1034,22 @@ void libvlc_InternalCleanup( libvlc_int_t *p_libvlc ) /* Free playlist now, all threads are gone */ playlist_Destroy( p_playlist ); + /* Free playlist now */ +#if defined(MEDIA_LIBRARY) + media_library_t* p_ml = priv->p_ml; + if( p_ml ) + { + __ml_Destroy( VLC_OBJECT( p_ml ) ); + vlc_object_release( p_ml ); + libvlc_priv(p_playlist->p_libvlc)->p_ml = NULL; + } +#endif + + /* Free playlist */ + /* Any thread still running must not assume pl_Hold() succeeds. */ + msg_Dbg( p_libvlc, "removing playlist" ); + vlc_object_release( p_playlist ); + stats_TimersDumpAll( p_libvlc ); stats_TimersCleanAll( p_libvlc ); diff --git a/src/libvlc.h b/src/libvlc.h index 873b73cd9d..48205b1471 100644 --- a/src/libvlc.h +++ b/src/libvlc.h @@ -25,6 +25,8 @@ #ifndef LIBVLC_LIBVLC_H # define LIBVLC_LIBVLC_H 1 +#include + typedef struct variable_t variable_t; /* Actions (hot keys) */ @@ -211,7 +213,8 @@ typedef struct libvlc_priv_t /* Singleton objects */ module_t *p_memcpy_module; ///< Fast memcpy plugin used - playlist_t *p_playlist; //< the playlist singleton + playlist_t *p_playlist; ///< the playlist singleton + media_library_t *p_ml; ///< the ML singleton vlm_t *p_vlm; ///< the VLM singleton (or NULL) vlc_object_t *p_dialog_provider; ///< dialog provider httpd_t *p_httpd; ///< HTTP daemon (src/network/httpd.c) diff --git a/src/libvlccore.sym b/src/libvlccore.sym index 49942250e9..02100b0b47 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -241,6 +241,20 @@ LocaleFree make_URI make_path mdate +__ml_Create +__ml_Destroy +__ml_Hold +__ml_Release +media_Destroy +media_New +ml_OpConnectChilds +__ml_FtreeSpec +__ml_UpdateSimple +ml_GetPersonsFromMedia +ml_DeletePersonTypeFromMedia +ml_PlaySmartPlaylistBasedOn +ml_gc_decref +ml_gc_incref module_config_free module_config_get module_exists diff --git a/src/misc/media_library.c b/src/misc/media_library.c new file mode 100644 index 0000000000..1105b4af62 --- /dev/null +++ b/src/misc/media_library.c @@ -0,0 +1,469 @@ +/***************************************************************************** + * media_library.c: SQL-based media library: ML creators and destructors + ***************************************************************************** + * Copyright (C) 2009-2010 the VideoLAN team and AUTHORS + * $Id$ + * + * Authors: Srikanth Raju + * + * 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. + *****************************************************************************/ + +#if !defined( __LIBVLC__ ) + #error You are not libvlc or one of its plugins. You cannot include this file +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(MEDIA_LIBRARY) + +#include +#include +#include "../libvlc.h" + +/** + * @brief Destroy the medialibrary object + * @param Parent object that holds the media library object + */ +void __ml_Destroy( vlc_object_t * p_this ) +{ + media_library_t* p_ml = ( media_library_t* )p_this; + module_unneed( p_ml, p_ml->p_module ); +} + + +/** + * Atomically set the reference count to 1. + * @param p_gc reference counted object + * @param pf_destruct destruction calback + * @return p_gc. + */ +static void *ml_gc_init (ml_gc_object_t *p_gc, void (*pf_destruct) (ml_gc_object_t *)) +{ + /* There is no point in using the GC if there is no destructor... */ + assert (pf_destruct); + p_gc->pf_destructor = pf_destruct; + + p_gc->pool = false; + p_gc->refs = 1; + /* Nobody else can possibly lock the spin - it's there as a barrier */ + vlc_spin_init (&p_gc->spin); + vlc_spin_lock (&p_gc->spin); + vlc_spin_unlock (&p_gc->spin); + return p_gc; +} + + + +/** + * @brief Create an instance of the media library + * @param p_this Parent object + * @param psz_name Name which is passed to module_need (not needed) + * @return p_ml created and attached, module loaded. NULL if + * not able to load + */ +media_library_t *__ml_Create( vlc_object_t *p_this, char *psz_name ) +{ + media_library_t *p_ml = NULL; + + p_ml = ( media_library_t * ) vlc_custom_create( + p_this, sizeof( media_library_t ), + VLC_OBJECT_GENERIC, "media-library" ); + if( !p_ml ) + { + msg_Err( p_this, "unable to create media library object" ); + return NULL; + } + vlc_object_attach( p_ml, p_this ); + + p_ml->p_module = module_need( p_ml, "media-library", psz_name, false ); + if( !p_ml->p_module ) + { + vlc_object_release( p_ml ); + msg_Err( p_this, "Media Library provider not found" ); + return NULL; + } + + return p_ml; +} + +/** + * @brief Acquire a reference to the media library singleton + * @param p_this Object that holds the reference + * @return media_library_t The ml object. NULL if not compiled with + * media library or if unable to load + */ +media_library_t* __ml_Hold( vlc_object_t* p_this ) +{ + media_library_t* p_ml = NULL; + p_ml = libvlc_priv (p_this->p_libvlc)->p_ml; + assert( VLC_OBJECT( p_ml ) != p_this ); + if( p_ml == NULL && + var_GetBool( p_this->p_libvlc, "load-media-library-on-startup" ) == false ) + { + libvlc_priv (p_this->p_libvlc)->p_ml + = __ml_Create( VLC_OBJECT( p_this->p_libvlc ), NULL ); + p_ml = libvlc_priv (p_this->p_libvlc)->p_ml; + } + if( p_ml ) + vlc_object_hold( p_ml ); + return p_ml; +} + +/** + * @brief Release a reference to the media library singleton + * @param p_this Object that holds the reference + */ +void __ml_Release( vlc_object_t* p_this ) +{ + media_library_t* p_ml; + p_ml = libvlc_priv (p_this->p_libvlc)->p_ml; + if( p_ml == NULL ) + { + msg_Warn( p_this->p_libvlc , "Spurious release ML called"); + return; + } + assert( VLC_OBJECT( p_ml ) != p_this ); + vlc_object_release( p_ml ); +} + +/** + * @brief Destructor for ml_media_t + */ +static void media_Destroy( ml_gc_object_t *p_gc ) +{ + ml_media_t* p_media = ml_priv( p_gc, ml_media_t ); + vlc_mutex_destroy( &p_media->lock ); + ml_FreeMediaContent( p_media ); + free( p_media ); +} + +/** + * @brief Object constructor for ml_media_t + * @param p_ml The media library object + * @param id If 0, this item isn't in database. If non zero, it is and + * it will be a singleton + * @param select Type of object + * @param reload Whether to reload from database + */ +ml_media_t* media_New( media_library_t* p_ml, int id, + ml_select_e select, bool reload ) +{ + if( id == 0 ) + { + ml_media_t* p_media = NULL; + p_media = ( ml_media_t* )calloc( 1, sizeof( ml_media_t ) ); + ml_gc_init( &p_media->ml_gc_data, media_Destroy ); + vlc_mutex_init( &p_media->lock ); + return p_media; + } + else + return p_ml->functions.pf_GetMedia( p_ml, id, select, reload ); +} + +/** + * @brief Update a given table + * @param p_media_library The media library object + * @param selected_type The table to update + * @param psz_lvalue The role of the person if selected_type = ML_PEOPLE + * @param id The id of the row to update + * @param ... The update data. [SelectType [RoleType] Value] ... ML_END + */ +int __ml_UpdateSimple( media_library_t *p_media_library, + ml_select_e selected_type, + const char* psz_lvalue, + int id, ... ) +{ + ml_element_t *update = NULL; + vlc_array_t *array = vlc_array_new(); + int i_ret = VLC_SUCCESS; + + va_list args; + va_start( args, id ); + + ml_select_e sel; + do { + update = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); + sel = ( ml_select_e ) va_arg( args, int ); + update->criteria = sel; + if( sel == ML_PEOPLE ) + { + update->lvalue.str = va_arg( args, char* ); + update->value.str = va_arg( args, char* ); + vlc_array_append( array, update ); + } + else if( sel == ML_PEOPLE_ID ) + { + update->lvalue.str = va_arg( args, char* ); + update->value.i = va_arg( args, int ); + vlc_array_append( array, update ); + } + else if( sel == ML_PEOPLE_ROLE ) + { +#ifndef NDEBUG + msg_Dbg( p_media_library, + "this argument is invalid for Update: %d", + (int)sel ); +#endif + } + else + { + switch( ml_AttributeIsString( sel ) ) + { + case -1: + if( sel != ML_END ) + { +#ifndef NDEBUG + msg_Dbg( p_media_library, + "this argument is invalid for Update: %d", + (int)sel ); +#endif + i_ret = VLC_EGENERIC; + } + else if( sel == ML_END ) + vlc_array_append( array, update ); + break; + case 0: + update->value.str = va_arg( args, char* ); + vlc_array_append( array, update ); + break; + case 1: + update->value.i = va_arg( args, int ); + vlc_array_append( array, update ); + break; + } + } + } while( sel != ML_END ); + + va_end( args ); + + ml_ftree_t* p_where = NULL; + ml_ftree_t* find = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) ); + find->criteria = ML_ID; + find->value.i = id ; + find->comp = ML_COMP_EQUAL; + p_where = ml_FtreeFastAnd( p_where, find ); + + /* Let's update the database ! */ + if( i_ret == VLC_SUCCESS ) + i_ret = ml_Update( p_media_library, selected_type, psz_lvalue, + p_where, array ); + + /* Destroying array */ + for( int i = 0; i < vlc_array_count( array ); i++ ) + { + free( vlc_array_item_at_index( array, i ) ); + } + vlc_array_destroy( array ); + ml_FreeFindTree( p_where ); + + return i_ret; +} + +/** + * @brief Connect up a find tree + * @param op operator to connect with + * If op = ML_OP_NONE, then you are connecting to a tree consisting of + * only SPECIAL nodes. + * If op = ML_OP_NOT, then right MUST be NULL + * op must not be ML_OP_SPECIAL, @see __ml_FtreeSpec + * @param left part of the tree + * @param right part of the tree + * @return Pointer to new tree + * @note Use the helpers! + */ +ml_ftree_t* ml_OpConnectChilds( ml_op_e op, ml_ftree_t* left, + ml_ftree_t* right ) +{ + /* Use this Op for fresh trees (with only special nodes/none at all!) */ + if( op == ML_OP_NONE ) + { + assert( ml_FtreeHasOp( left ) == 0 ); + if( left == NULL ) + return right; + /* Percolate down tree only for special nodes */ + assert( left->op == ML_OP_SPECIAL ); + if( left->left == NULL ) + { + left->left = right; + return left; + } + else + { + return ml_OpConnectChilds( ML_OP_NONE, left->left, right ); + } + } + else if( op == ML_OP_NOT ) + { + assert( right == NULL && left != NULL ); + assert( ml_FtreeHasOp( left ) > 0 ); + } + else if( op == ML_OP_SPECIAL ) + { + assert( 0 ); + } + else + { + assert( right != NULL && left != NULL ); + assert( ml_FtreeHasOp( left ) > 0 ); + assert( ml_FtreeHasOp( right ) > 0 ); + } + ml_ftree_t* p_parent = (ml_ftree_t *) calloc( 1, sizeof( ml_ftree_t ) ); + p_parent->op = op; + p_parent->left = left; + p_parent->right = right; + return p_parent; +} + +/** + * @brief Attaches a special node to a tree + * @param tree Tree to attach special node to + * @param crit Criteria may be SORT_ASC, SORT_DESC, LIMIT or DISTINCT + * @param limit Limit used if LIMIT criteria used + * @param Sort string used if SORT criteria is used + * @return Pointer to new tree + * @note Use the helpers + */ +ml_ftree_t* __ml_FtreeSpec( ml_ftree_t* tree, + ml_select_e crit, + int limit, + char* sort ) +{ + assert( crit == ML_SORT_ASC || crit == ML_LIMIT || crit == ML_SORT_DESC || + crit == ML_DISTINCT ); + ml_ftree_t* right = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) ); + right->criteria = crit; + if( crit == ML_LIMIT ) + right->value.i = limit; + else if( crit == ML_SORT_ASC || crit == ML_SORT_DESC ) + right->value.str = strdup( sort ); + right->op = ML_OP_NONE; + ml_ftree_t* p_parent = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) ); + p_parent->right = right; + p_parent->op = ML_OP_SPECIAL; + p_parent->left = tree; + return p_parent; +} + + +/** + * @brief Creates and adds the playlist based on a given find tree + * @param p_ml Media library object + * @param p_tree Find tree to create SELECT + */ +void ml_PlaySmartPlaylistBasedOn( media_library_t* p_ml, + ml_ftree_t* p_tree ) +{ + assert( p_tree ); + vlc_array_t* p_results = vlc_array_new(); + ml_FindAdv( p_ml, p_results, ML_ID, NULL, p_tree ); + playlist_t* p_pl = pl_Hold( p_ml ); + playlist_Lock( p_pl ); + playlist_Clear( p_pl, true ); + for( int i = 0; i < vlc_array_count( p_results ); i++ ) + { + ml_result_t* p_res = ( ml_result_t* ) vlc_array_item_at_index( p_results, i ); + input_item_t* p_item; + if( p_res ) + { + p_item = ml_CreateInputItem( p_ml, p_res->value.i ); + playlist_AddInput( p_pl, p_item, PLAYLIST_APPEND, + PLAYLIST_END, true, true ); + } + } + playlist_Unlock( p_pl ); + ml_DestroyResultArray( p_results ); + vlc_array_destroy( p_results ); +} + +/** + * @brief Returns a person list of given type + * @param p_ml The ML object + * @param p_media The Media object + * @param i_type The person type + * @note This function is thread safe + */ +ml_person_t* ml_GetPersonsFromMedia( media_library_t* p_ml, + ml_media_t* p_media, + const char *psz_role ) +{ + VLC_UNUSED( p_ml ); + assert( p_media != NULL ); + ml_person_t* p_return = NULL; + ml_LockMedia( p_media ); + ml_person_t* p_person = p_media->p_people; + while( p_person ) + { + if( strcmp( p_person->psz_role, psz_role ) == 0 ) + { + int i_ret = ml_CreateAppendPerson( &p_return, p_person ); + if( i_ret != VLC_SUCCESS ) + { + ml_UnlockMedia( p_media ); + ml_FreePeople( p_return ); + return NULL; + } + } + p_person = p_person->p_next; + } + ml_UnlockMedia( p_media ); + //TODO: Fill up empty names + clean up list + return p_return; +} + +/** + * @brief Delete a certain type of people from a media + * @param p_media Media to delete from + * @param i_type Type of person to delete + * @note This function is threadsafe + */ +void ml_DeletePersonTypeFromMedia( ml_media_t* p_media, const char *psz_role ) +{ + assert( p_media ); + ml_LockMedia( p_media ); + ml_person_t* p_prev = NULL; + ml_person_t* p_person = p_media->p_people; + + while( p_person ) + { + if( strcmp( p_person->psz_role, psz_role ) == 0 ) + { + if( p_prev == NULL ) + { + p_media->p_people = p_person->p_next; + p_person->p_next = NULL; + ml_FreePeople( p_person ); + p_person = p_media->p_people; + } + else + { + p_prev->p_next = p_person->p_next; + p_person->p_next = NULL; + ml_FreePeople( p_person ); + p_person = p_prev->p_next; + } + } + else + { + p_prev = p_person; + p_person = p_person->p_next; + } + } + ml_UnlockMedia( p_media ); +} + +#endif /* MEDIA_LIBRARY */ diff --git a/src/missing.c b/src/missing.c index b96f911b7d..46e125946a 100644 --- a/src/missing.c +++ b/src/missing.c @@ -395,3 +395,60 @@ vlm_t *vlm_New (vlc_object_t *obj) return NULL; } #endif /* !ENABLE_VLM */ + +#ifndef MEDIA_LIBRARY +#include + +media_library_t* __ml_Hold ( vlc_object_t* p_this ) +{ + return NULL; +} + +void __ml_Release ( vlc_object_t* p_this ) +{ + assert( 0 ); +} + +media_library_t* __ml_Create ( vlc_object_t *p_this, char* psz_name ) +{ + return NULL; +} + +void __ml_Destroy( vlc_object_t * p_this ) +{ + assert( 0 ); +} + +ml_media_t* media_New( media_library_t* p_ml, int id, ml_select_e select, bool reload ) +{ + assert( 0 ); +} + +int __ml_UpdateSimple( media_library_t *p_media_library, ml_select_e selected_type, + const char* psz_lvalue, int id, ... ) +{ + assert( 0 ); +} + +ml_ftree_t* ml_OpConnectChilds( ml_op_e op, ml_ftree_t* left, ml_ftree_t* right ) +{ + assert( 0 ); +} + +ml_ftree_t* __ml_FtreeSpec( ml_ftree_t* tree, ml_select_e crit, int limit, + char* sort ) +{ + assert( 0 ); +} + +void ml_PlaySmartPlaylistBasedOn( media_library_t* p_ml, + ml_ftree_t* p_tree ) +{ + assert( 0 ); +} + +void ml_DeletePersonTypeFromMedia( ml_media_t* p_media, const char *psz_role ) +{ + assert( 0 ); +} +#endif /* !MEDIA_LIBRARY */