1 /*****************************************************************************
2 * media_library.c: SQL-based media library: ML creators and destructors
3 *****************************************************************************
4 * Copyright (C) 2009-2010 the VideoLAN team and AUTHORS
7 * Authors: Srikanth Raju <srikiraju at gmail dot com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #if !defined( __LIBVLC__ )
25 #error You are not libvlc or one of its plugins. You cannot include this file
32 #if defined(MEDIA_LIBRARY)
35 #include <vlc_media_library.h>
36 #include "../libvlc.h"
39 * @brief Destroy the medialibrary object
40 * @param Parent object that holds the media library object
42 void ml_Destroy( vlc_object_t * p_this )
44 media_library_t* p_ml = ( media_library_t* )p_this;
45 module_unneed( p_ml, p_ml->p_module );
50 * Atomically set the reference count to 1.
51 * @param p_gc reference counted object
52 * @param pf_destruct destruction calback
55 static void *ml_gc_init (ml_gc_object_t *p_gc, void (*pf_destruct) (ml_gc_object_t *))
57 /* There is no point in using the GC if there is no destructor... */
59 p_gc->pf_destructor = pf_destruct;
63 /* Nobody else can possibly lock the spin - it's there as a barrier */
64 vlc_spin_init (&p_gc->spin);
65 vlc_spin_lock (&p_gc->spin);
66 vlc_spin_unlock (&p_gc->spin);
73 * @brief Create an instance of the media library
74 * @param p_this Parent object
75 * @param psz_name Name which is passed to module_need (not needed)
76 * @return p_ml created and attached, module loaded. NULL if
79 media_library_t *ml_Create( vlc_object_t *p_this, char *psz_name )
81 media_library_t *p_ml;
83 p_ml = ( media_library_t * ) vlc_custom_create(
84 p_this, sizeof( media_library_t ),
85 VLC_OBJECT_GENERIC, "media-library" );
88 msg_Err( p_this, "unable to create media library object" );
91 vlc_object_attach( p_ml, p_this );
93 p_ml->p_module = module_need( p_ml, "media-library", psz_name, false );
96 vlc_object_release( p_ml );
97 msg_Err( p_this, "Media Library provider not found" );
106 * @brief Acquire a reference to the media library singleton
107 * @param p_this Object that holds the reference
108 * @return media_library_t The ml object. NULL if not compiled with
109 * media library or if unable to load
111 media_library_t* ml_Hold( vlc_object_t* p_this )
113 media_library_t* p_ml;
114 p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
115 assert( VLC_OBJECT( p_ml ) != p_this );
117 var_GetBool( p_this->p_libvlc, "load-media-library-on-startup" ) == false )
119 libvlc_priv (p_this->p_libvlc)->p_ml
120 = ml_Create( VLC_OBJECT( p_this->p_libvlc ), NULL );
121 p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
124 vlc_object_hold( p_ml );
130 * @brief Release a reference to the media library singleton
131 * @param p_this Object that holds the reference
133 void ml_Release( vlc_object_t* p_this )
135 media_library_t* p_ml;
136 p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
139 msg_Warn( p_this->p_libvlc , "Spurious release ML called");
142 assert( VLC_OBJECT( p_ml ) != p_this );
143 vlc_object_release( p_ml );
147 * @brief Destructor for ml_media_t
149 static void media_Destroy( ml_gc_object_t *p_gc )
151 ml_media_t* p_media = ml_priv( p_gc, ml_media_t );
152 vlc_mutex_destroy( &p_media->lock );
153 ml_FreeMediaContent( p_media );
158 * @brief Object constructor for ml_media_t
159 * @param p_ml The media library object
160 * @param id If 0, this item isn't in database. If non zero, it is and
161 * it will be a singleton
162 * @param select Type of object
163 * @param reload Whether to reload from database
165 ml_media_t* media_New( media_library_t* p_ml, int id,
166 ml_select_e select, bool reload )
170 ml_media_t* p_media = NULL;
171 p_media = ( ml_media_t* )calloc( 1, sizeof( ml_media_t ) );
172 ml_gc_init( &p_media->ml_gc_data, media_Destroy );
173 vlc_mutex_init( &p_media->lock );
177 return p_ml->functions.pf_GetMedia( p_ml, id, select, reload );
180 #undef ml_UpdateSimple
182 * @brief Update a given table
183 * @param p_media_library The media library object
184 * @param selected_type The table to update
185 * @param psz_lvalue The role of the person if selected_type = ML_PEOPLE
186 * @param id The id of the row to update
187 * @param ... The update data. [SelectType [RoleType] Value] ... ML_END
189 int ml_UpdateSimple( media_library_t *p_media_library,
190 ml_select_e selected_type,
191 const char* psz_lvalue,
194 ml_element_t *update;
195 vlc_array_t *array = vlc_array_new();
196 int i_ret = VLC_SUCCESS;
199 va_start( args, id );
203 update = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
204 sel = ( ml_select_e ) va_arg( args, int );
205 update->criteria = sel;
206 if( sel == ML_PEOPLE )
208 update->lvalue.str = va_arg( args, char* );
209 update->value.str = va_arg( args, char* );
210 vlc_array_append( array, update );
212 else if( sel == ML_PEOPLE_ID )
214 update->lvalue.str = va_arg( args, char* );
215 update->value.i = va_arg( args, int );
216 vlc_array_append( array, update );
218 else if( sel == ML_PEOPLE_ROLE )
221 msg_Dbg( p_media_library,
222 "this argument is invalid for Update: %d",
228 switch( ml_AttributeIsString( sel ) )
234 msg_Dbg( p_media_library,
235 "this argument is invalid for Update: %d",
238 i_ret = VLC_EGENERIC;
240 else if( sel == ML_END )
241 vlc_array_append( array, update );
244 update->value.str = va_arg( args, char* );
245 vlc_array_append( array, update );
248 update->value.i = va_arg( args, int );
249 vlc_array_append( array, update );
253 } while( sel != ML_END );
257 ml_ftree_t* p_where = NULL;
258 ml_ftree_t* find = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
259 find->criteria = ML_ID;
261 find->comp = ML_COMP_EQUAL;
262 p_where = ml_FtreeFastAnd( p_where, find );
264 /* Let's update the database ! */
265 if( i_ret == VLC_SUCCESS )
266 i_ret = ml_Update( p_media_library, selected_type, psz_lvalue,
269 /* Destroying array */
270 for( int i = 0; i < vlc_array_count( array ); i++ )
272 free( vlc_array_item_at_index( array, i ) );
274 vlc_array_destroy( array );
275 ml_FreeFindTree( p_where );
281 * @brief Connect up a find tree
282 * @param op operator to connect with
283 * If op = ML_OP_NONE, then you are connecting to a tree consisting of
284 * only SPECIAL nodes.
285 * If op = ML_OP_NOT, then right MUST be NULL
286 * op must not be ML_OP_SPECIAL, @see ml_FtreeSpec
287 * @param left part of the tree
288 * @param right part of the tree
289 * @return Pointer to new tree
290 * @note Use the helpers!
292 ml_ftree_t* ml_OpConnectChilds( ml_op_e op, ml_ftree_t* left,
295 /* Use this Op for fresh trees (with only special nodes/none at all!) */
296 if( op == ML_OP_NONE )
298 assert( ml_FtreeHasOp( left ) == 0 );
301 /* Percolate down tree only for special nodes */
302 assert( left->op == ML_OP_SPECIAL );
303 if( left->left == NULL )
310 return ml_OpConnectChilds( ML_OP_NONE, left->left, right );
313 else if( op == ML_OP_NOT )
315 assert( right == NULL && left != NULL );
316 assert( ml_FtreeHasOp( left ) > 0 );
318 else if( op == ML_OP_SPECIAL )
324 assert( right != NULL && left != NULL );
325 assert( ml_FtreeHasOp( left ) > 0 );
326 assert( ml_FtreeHasOp( right ) > 0 );
328 ml_ftree_t* p_parent = (ml_ftree_t *) calloc( 1, sizeof( ml_ftree_t ) );
330 p_parent->left = left;
331 p_parent->right = right;
337 * @brief Attaches a special node to a tree
338 * @param tree Tree to attach special node to
339 * @param crit Criteria may be SORT_ASC, SORT_DESC, LIMIT or DISTINCT
340 * @param limit Limit used if LIMIT criteria used
341 * @param Sort string used if SORT criteria is used
342 * @return Pointer to new tree
343 * @note Use the helpers
345 ml_ftree_t* ml_FtreeSpec( ml_ftree_t* tree,
350 assert( crit == ML_SORT_ASC || crit == ML_LIMIT || crit == ML_SORT_DESC ||
351 crit == ML_DISTINCT );
352 ml_ftree_t* right = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
353 right->criteria = crit;
354 if( crit == ML_LIMIT )
355 right->value.i = limit;
356 else if( crit == ML_SORT_ASC || crit == ML_SORT_DESC )
357 right->value.str = strdup( sort );
358 right->op = ML_OP_NONE;
359 ml_ftree_t* p_parent = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
360 p_parent->right = right;
361 p_parent->op = ML_OP_SPECIAL;
362 p_parent->left = tree;
368 * @brief Creates and adds the playlist based on a given find tree
369 * @param p_ml Media library object
370 * @param p_tree Find tree to create SELECT
372 void ml_PlaySmartPlaylistBasedOn( media_library_t* p_ml,
376 vlc_array_t* p_results = vlc_array_new();
377 ml_FindAdv( p_ml, p_results, ML_ID, NULL, p_tree );
378 playlist_t* p_pl = pl_Get( p_ml );
379 playlist_Lock( p_pl );
380 playlist_Clear( p_pl, true );
381 for( int i = 0; i < vlc_array_count( p_results ); i++ )
383 ml_result_t* p_res = ( ml_result_t* ) vlc_array_item_at_index( p_results, i );
384 input_item_t* p_item;
387 p_item = ml_CreateInputItem( p_ml, p_res->value.i );
388 playlist_AddInput( p_pl, p_item, PLAYLIST_APPEND,
389 PLAYLIST_END, true, true );
392 playlist_Unlock( p_pl );
393 ml_DestroyResultArray( p_results );
394 vlc_array_destroy( p_results );
398 * @brief Returns a person list of given type
399 * @param p_ml The ML object
400 * @param p_media The Media object
401 * @param i_type The person type
402 * @note This function is thread safe
404 ml_person_t* ml_GetPersonsFromMedia( media_library_t* p_ml,
406 const char *psz_role )
409 assert( p_media != NULL );
410 ml_person_t* p_return = NULL;
411 ml_LockMedia( p_media );
412 ml_person_t* p_person = p_media->p_people;
415 if( strcmp( p_person->psz_role, psz_role ) == 0 )
417 int i_ret = ml_CreateAppendPerson( &p_return, p_person );
418 if( i_ret != VLC_SUCCESS )
420 ml_UnlockMedia( p_media );
421 ml_FreePeople( p_return );
425 p_person = p_person->p_next;
427 ml_UnlockMedia( p_media );
428 //TODO: Fill up empty names + clean up list
433 * @brief Delete a certain type of people from a media
434 * @param p_media Media to delete from
435 * @param i_type Type of person to delete
436 * @note This function is threadsafe
438 void ml_DeletePersonTypeFromMedia( ml_media_t* p_media, const char *psz_role )
441 ml_LockMedia( p_media );
442 ml_person_t* p_prev = NULL;
443 ml_person_t* p_person = p_media->p_people;
447 if( strcmp( p_person->psz_role, psz_role ) == 0 )
451 p_media->p_people = p_person->p_next;
452 p_person->p_next = NULL;
453 ml_FreePeople( p_person );
454 p_person = p_media->p_people;
458 p_prev->p_next = p_person->p_next;
459 p_person->p_next = NULL;
460 ml_FreePeople( p_person );
461 p_person = p_prev->p_next;
467 p_person = p_person->p_next;
470 ml_UnlockMedia( p_media );
473 #endif /* MEDIA_LIBRARY */