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 <vlc_modules.h>
37 #include "../libvlc.h"
40 * @brief Destroy the medialibrary object
41 * @param Parent object that holds the media library object
43 void ml_Destroy( vlc_object_t
* p_this
)
45 media_library_t
* p_ml
= ( media_library_t
* )p_this
;
46 module_unneed( p_ml
, p_ml
->p_module
);
51 * Atomically set the reference count to 1.
52 * @param p_gc reference counted object
53 * @param pf_destruct destruction calback
56 static void *ml_gc_init (ml_gc_object_t
*p_gc
, void (*pf_destruct
) (ml_gc_object_t
*))
58 /* There is no point in using the GC if there is no destructor... */
60 p_gc
->pf_destructor
= pf_destruct
;
64 /* Nobody else can possibly lock the spin - it's there as a barrier */
65 vlc_spin_init (&p_gc
->spin
);
66 vlc_spin_lock (&p_gc
->spin
);
67 vlc_spin_unlock (&p_gc
->spin
);
74 * @brief Create an instance of the media library
75 * @param p_this Parent object
76 * @param psz_name Name which is passed to module_need (not needed)
77 * @return p_ml created and attached, module loaded. NULL if
80 media_library_t
*ml_Create( vlc_object_t
*p_this
, char *psz_name
)
82 media_library_t
*p_ml
;
84 p_ml
= ( media_library_t
* ) vlc_custom_create(
85 p_this
, sizeof( media_library_t
),
86 VLC_OBJECT_GENERIC
, "media-library" );
89 msg_Err( p_this
, "unable to create media library object" );
92 vlc_object_attach( p_ml
, p_this
);
94 p_ml
->p_module
= module_need( p_ml
, "media-library", psz_name
, false );
97 vlc_object_release( p_ml
);
98 msg_Err( p_this
, "Media Library provider not found" );
107 * @brief Acquire a reference to the media library singleton
108 * @param p_this Object that holds the reference
109 * @return media_library_t The ml object. NULL if not compiled with
110 * media library or if unable to load
112 media_library_t
* ml_Hold( vlc_object_t
* p_this
)
114 media_library_t
* p_ml
;
115 vlc_mutex_lock( &( libvlc_priv( p_this
->p_libvlc
)->ml_lock
) );
116 p_ml
= libvlc_priv (p_this
->p_libvlc
)->p_ml
;
117 assert( VLC_OBJECT( p_ml
) != p_this
);
119 var_GetBool( p_this
->p_libvlc
, "load-media-library-on-startup" ) == false )
121 libvlc_priv (p_this
->p_libvlc
)->p_ml
122 = ml_Create( VLC_OBJECT( p_this
->p_libvlc
), NULL
);
123 p_ml
= libvlc_priv (p_this
->p_libvlc
)->p_ml
;
126 vlc_object_hold( p_ml
);
127 vlc_mutex_unlock( &( libvlc_priv( p_this
->p_libvlc
)->ml_lock
) );
133 * @brief Release a reference to the media library singleton
134 * @param p_this Object that holds the reference
136 void ml_Release( vlc_object_t
* p_this
)
138 media_library_t
* p_ml
;
139 p_ml
= libvlc_priv (p_this
->p_libvlc
)->p_ml
;
142 msg_Warn( p_this
->p_libvlc
, "Spurious release ML called");
145 assert( VLC_OBJECT( p_ml
) != p_this
);
146 vlc_object_release( p_ml
);
150 * @brief Destructor for ml_media_t
152 static void media_Destroy( ml_gc_object_t
*p_gc
)
154 ml_media_t
* p_media
= ml_priv( p_gc
, ml_media_t
);
155 vlc_mutex_destroy( &p_media
->lock
);
156 ml_FreeMediaContent( p_media
);
161 * @brief Object constructor for ml_media_t
162 * @param p_ml The media library object
163 * @param id If 0, this item isn't in database. If non zero, it is and
164 * it will be a singleton
165 * @param select Type of object
166 * @param reload Whether to reload from database
168 ml_media_t
* media_New( media_library_t
* p_ml
, int id
,
169 ml_select_e select
, bool reload
)
173 ml_media_t
* p_media
= NULL
;
174 p_media
= ( ml_media_t
* )calloc( 1, sizeof( ml_media_t
) );
175 ml_gc_init( &p_media
->ml_gc_data
, media_Destroy
);
176 vlc_mutex_init( &p_media
->lock
);
180 return p_ml
->functions
.pf_GetMedia( p_ml
, id
, select
, reload
);
183 #undef ml_UpdateSimple
185 * @brief Update a given table
186 * @param p_media_library The media library object
187 * @param selected_type The table to update
188 * @param psz_lvalue The role of the person if selected_type = ML_PEOPLE
189 * @param id The id of the row to update
190 * @param ... The update data. [SelectType [RoleType] Value] ... ML_END
192 int ml_UpdateSimple( media_library_t
*p_media_library
,
193 ml_select_e selected_type
,
194 const char* psz_lvalue
,
197 ml_element_t
*update
;
198 vlc_array_t
*array
= vlc_array_new();
199 int i_ret
= VLC_SUCCESS
;
202 va_start( args
, id
);
206 update
= ( ml_element_t
* ) calloc( 1, sizeof( ml_element_t
) );
207 sel
= ( ml_select_e
) va_arg( args
, int );
208 update
->criteria
= sel
;
209 if( sel
== ML_PEOPLE
)
211 update
->lvalue
.str
= va_arg( args
, char* );
212 update
->value
.str
= va_arg( args
, char* );
213 vlc_array_append( array
, update
);
215 else if( sel
== ML_PEOPLE_ID
)
217 update
->lvalue
.str
= va_arg( args
, char* );
218 update
->value
.i
= va_arg( args
, int );
219 vlc_array_append( array
, update
);
221 else if( sel
== ML_PEOPLE_ROLE
)
224 msg_Dbg( p_media_library
,
225 "this argument is invalid for Update: %d",
231 switch( ml_AttributeIsString( sel
) )
237 msg_Dbg( p_media_library
,
238 "this argument is invalid for Update: %d",
241 i_ret
= VLC_EGENERIC
;
243 else if( sel
== ML_END
)
244 vlc_array_append( array
, update
);
247 update
->value
.str
= va_arg( args
, char* );
248 vlc_array_append( array
, update
);
251 update
->value
.i
= va_arg( args
, int );
252 vlc_array_append( array
, update
);
256 } while( sel
!= ML_END
);
260 ml_ftree_t
* p_where
= NULL
;
261 ml_ftree_t
* find
= ( ml_ftree_t
* ) calloc( 1, sizeof( ml_ftree_t
) );
262 find
->criteria
= ML_ID
;
264 find
->comp
= ML_COMP_EQUAL
;
265 p_where
= ml_FtreeFastAnd( p_where
, find
);
267 /* Let's update the database ! */
268 if( i_ret
== VLC_SUCCESS
)
269 i_ret
= ml_Update( p_media_library
, selected_type
, psz_lvalue
,
272 /* Destroying array */
273 for( int i
= 0; i
< vlc_array_count( array
); i
++ )
275 free( vlc_array_item_at_index( array
, i
) );
277 vlc_array_destroy( array
);
278 ml_FreeFindTree( p_where
);
284 * @brief Connect up a find tree
285 * @param op operator to connect with
286 * If op = ML_OP_NONE, then you are connecting to a tree consisting of
287 * only SPECIAL nodes.
288 * If op = ML_OP_NOT, then right MUST be NULL
289 * op must not be ML_OP_SPECIAL, @see ml_FtreeSpec
290 * @param left part of the tree
291 * @param right part of the tree
292 * @return Pointer to new tree
293 * @note Use the helpers!
295 ml_ftree_t
* ml_OpConnectChilds( ml_op_e op
, ml_ftree_t
* left
,
298 /* Use this Op for fresh trees (with only special nodes/none at all!) */
299 if( op
== ML_OP_NONE
)
301 assert( ml_FtreeHasOp( left
) == 0 );
304 /* Percolate down tree only for special nodes */
305 assert( left
->op
== ML_OP_SPECIAL
);
306 if( left
->left
== NULL
)
313 return ml_OpConnectChilds( ML_OP_NONE
, left
->left
, right
);
316 else if( op
== ML_OP_NOT
)
318 assert( right
== NULL
&& left
!= NULL
);
319 assert( ml_FtreeHasOp( left
) > 0 );
321 else if( op
== ML_OP_SPECIAL
)
327 assert( right
!= NULL
&& left
!= NULL
);
328 assert( ml_FtreeHasOp( left
) > 0 );
329 assert( ml_FtreeHasOp( right
) > 0 );
331 ml_ftree_t
* p_parent
= (ml_ftree_t
*) calloc( 1, sizeof( ml_ftree_t
) );
333 p_parent
->left
= left
;
334 p_parent
->right
= right
;
340 * @brief Attaches a special node to a tree
341 * @param tree Tree to attach special node to
342 * @param crit Criteria may be SORT_ASC, SORT_DESC, LIMIT or DISTINCT
343 * @param limit Limit used if LIMIT criteria used
344 * @param Sort string used if SORT criteria is used
345 * @return Pointer to new tree
346 * @note Use the helpers
348 ml_ftree_t
* ml_FtreeSpec( ml_ftree_t
* tree
,
353 assert( crit
== ML_SORT_ASC
|| crit
== ML_LIMIT
|| crit
== ML_SORT_DESC
||
354 crit
== ML_DISTINCT
);
355 ml_ftree_t
* right
= ( ml_ftree_t
* ) calloc( 1, sizeof( ml_ftree_t
) );
356 right
->criteria
= crit
;
357 if( crit
== ML_LIMIT
)
358 right
->value
.i
= limit
;
359 else if( crit
== ML_SORT_ASC
|| crit
== ML_SORT_DESC
)
360 right
->value
.str
= strdup( sort
);
361 right
->op
= ML_OP_NONE
;
362 ml_ftree_t
* p_parent
= ( ml_ftree_t
* ) calloc( 1, sizeof( ml_ftree_t
) );
363 p_parent
->right
= right
;
364 p_parent
->op
= ML_OP_SPECIAL
;
365 p_parent
->left
= tree
;
371 * @brief Creates and adds the playlist based on a given find tree
372 * @param p_ml Media library object
373 * @param p_tree Find tree to create SELECT
375 void ml_PlaySmartPlaylistBasedOn( media_library_t
* p_ml
,
379 vlc_array_t
* p_results
= vlc_array_new();
380 ml_FindAdv( p_ml
, p_results
, ML_ID
, NULL
, p_tree
);
381 playlist_t
* p_pl
= pl_Get( p_ml
);
382 playlist_Lock( p_pl
);
383 playlist_Clear( p_pl
, true );
384 for( int i
= 0; i
< vlc_array_count( p_results
); i
++ )
386 ml_result_t
* p_res
= ( ml_result_t
* ) vlc_array_item_at_index( p_results
, i
);
387 input_item_t
* p_item
;
390 p_item
= ml_CreateInputItem( p_ml
, p_res
->value
.i
);
391 playlist_AddInput( p_pl
, p_item
, PLAYLIST_APPEND
,
392 PLAYLIST_END
, true, true );
395 playlist_Unlock( p_pl
);
396 ml_DestroyResultArray( p_results
);
397 vlc_array_destroy( p_results
);
401 * @brief Returns a person list of given type
402 * @param p_ml The ML object
403 * @param p_media The Media object
404 * @param i_type The person type
405 * @note This function is thread safe
407 ml_person_t
* ml_GetPersonsFromMedia( media_library_t
* p_ml
,
409 const char *psz_role
)
412 assert( p_media
!= NULL
);
413 ml_person_t
* p_return
= NULL
;
414 ml_LockMedia( p_media
);
415 ml_person_t
* p_person
= p_media
->p_people
;
418 if( strcmp( p_person
->psz_role
, psz_role
) == 0 )
420 int i_ret
= ml_CreateAppendPerson( &p_return
, p_person
);
421 if( i_ret
!= VLC_SUCCESS
)
423 ml_UnlockMedia( p_media
);
424 ml_FreePeople( p_return
);
428 p_person
= p_person
->p_next
;
430 ml_UnlockMedia( p_media
);
431 //TODO: Fill up empty names + clean up list
436 * @brief Delete a certain type of people from a media
437 * @param p_media Media to delete from
438 * @param i_type Type of person to delete
439 * @note This function is threadsafe
441 void ml_DeletePersonTypeFromMedia( ml_media_t
* p_media
, const char *psz_role
)
444 ml_LockMedia( p_media
);
445 ml_person_t
* p_prev
= NULL
;
446 ml_person_t
* p_person
= p_media
->p_people
;
450 if( strcmp( p_person
->psz_role
, psz_role
) == 0 )
454 p_media
->p_people
= p_person
->p_next
;
455 p_person
->p_next
= NULL
;
456 ml_FreePeople( p_person
);
457 p_person
= p_media
->p_people
;
461 p_prev
->p_next
= p_person
->p_next
;
462 p_person
->p_next
= NULL
;
463 ml_FreePeople( p_person
);
464 p_person
= p_prev
->p_next
;
470 p_person
= p_person
->p_next
;
473 ml_UnlockMedia( p_media
);
476 #endif /* MEDIA_LIBRARY */