]> git.sesse.net Git - vlc/blob - src/misc/media_library.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / src / misc / media_library.c
1 /*****************************************************************************
2  * media_library.c: SQL-based media library: ML creators and destructors
3  *****************************************************************************
4  * Copyright (C) 2009-2010 the VideoLAN team and AUTHORS
5  * $Id$
6  *
7  * Authors: Srikanth Raju <srikiraju at gmail dot com>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #if !defined( __LIBVLC__ )
25   #error You are not libvlc or one of its plugins. You cannot include this file
26 #endif
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #if defined(MEDIA_LIBRARY)
33
34 #include <assert.h>
35 #include <vlc_media_library.h>
36 #include <vlc_modules.h>
37 #include "../libvlc.h"
38
39 /**
40  * @brief Destroy the medialibrary object
41  * @param Parent object that holds the media library object
42  */
43 void ml_Destroy( vlc_object_t * p_this )
44 {
45     media_library_t* p_ml = ( media_library_t* )p_this;
46     module_unneed( p_ml, p_ml->p_module );
47 }
48
49
50 /**
51  * Atomically set the reference count to 1.
52  * @param p_gc reference counted object
53  * @param pf_destruct destruction calback
54  * @return p_gc.
55  */
56 static void *ml_gc_init (ml_gc_object_t *p_gc, void (*pf_destruct) (ml_gc_object_t *))
57 {
58     /* There is no point in using the GC if there is no destructor... */
59     assert (pf_destruct);
60     p_gc->pf_destructor = pf_destruct;
61
62     p_gc->pool = false;
63     p_gc->refs = 1;
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);
68     return p_gc;
69 }
70
71
72
73 /**
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
78  * not able to load
79  */
80 media_library_t *ml_Create( vlc_object_t *p_this, char *psz_name )
81 {
82     media_library_t *p_ml;
83
84     p_ml = ( media_library_t * ) vlc_custom_create(
85                                 p_this, sizeof( media_library_t ),
86                                 VLC_OBJECT_GENERIC, "media-library" );
87     if( !p_ml )
88     {
89         msg_Err( p_this, "unable to create media library object" );
90         return NULL;
91     }
92     vlc_object_attach( p_ml, p_this );
93
94     p_ml->p_module = module_need( p_ml, "media-library", psz_name, false );
95     if( !p_ml->p_module )
96     {
97         vlc_object_release( p_ml );
98         msg_Err( p_this, "Media Library provider not found" );
99         return NULL;
100     }
101
102     return p_ml;
103 }
104
105 #undef ml_Hold
106 /**
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
111  */
112 media_library_t* ml_Hold( vlc_object_t* p_this )
113 {
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 );
118     if( p_ml == NULL &&
119             var_GetBool( p_this->p_libvlc, "load-media-library-on-startup" ) == false )
120     {
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;
124     }
125     if( p_ml )
126         vlc_object_hold( p_ml );
127     vlc_mutex_unlock( &( libvlc_priv( p_this->p_libvlc )->ml_lock ) );
128     return p_ml;
129 }
130
131 #undef ml_Release
132 /**
133  * @brief Release a reference to the media library singleton
134  * @param p_this Object that holds the reference
135  */
136 void ml_Release( vlc_object_t* p_this )
137 {
138     media_library_t* p_ml;
139     p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
140     if( p_ml == NULL )
141     {
142         msg_Warn( p_this->p_libvlc , "Spurious release ML called");
143         return;
144     }
145     assert( VLC_OBJECT( p_ml ) != p_this );
146     vlc_object_release( p_ml );
147 }
148
149 /**
150  * @brief Destructor for ml_media_t
151  */
152 static void media_Destroy( ml_gc_object_t *p_gc )
153 {
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 );
157     free( p_media );
158 }
159
160 /**
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
167  */
168 ml_media_t* media_New( media_library_t* p_ml, int id,
169         ml_select_e select, bool reload )
170 {
171     if( id == 0 )
172     {
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 );
177         return p_media;
178     }
179     else
180         return p_ml->functions.pf_GetMedia( p_ml, id, select, reload );
181 }
182
183 #undef ml_UpdateSimple
184 /**
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
191  */
192 int ml_UpdateSimple( media_library_t *p_media_library,
193                                      ml_select_e selected_type,
194                                      const char* psz_lvalue,
195                                      int id, ... )
196 {
197     ml_element_t *update;
198     vlc_array_t *array = vlc_array_new();
199     int i_ret = VLC_SUCCESS;
200
201     va_list args;
202     va_start( args, id );
203
204     ml_select_e sel;
205     do {
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 )
210         {
211             update->lvalue.str = va_arg( args, char* );
212             update->value.str = va_arg( args, char* );
213             vlc_array_append( array, update );
214         }
215         else if( sel == ML_PEOPLE_ID )
216         {
217            update->lvalue.str = va_arg( args, char* );
218            update->value.i = va_arg( args, int );
219            vlc_array_append( array, update );
220         }
221         else if( sel == ML_PEOPLE_ROLE )
222         {
223 #ifndef NDEBUG
224             msg_Dbg( p_media_library,
225                      "this argument is invalid for Update: %d",
226                      (int)sel );
227 #endif
228         }
229         else
230         {
231             switch( ml_AttributeIsString( sel ) )
232             {
233                 case -1:
234                     if( sel != ML_END )
235                     {
236 #ifndef NDEBUG
237                         msg_Dbg( p_media_library,
238                                  "this argument is invalid for Update: %d",
239                                  (int)sel );
240 #endif
241                         i_ret = VLC_EGENERIC;
242                     }
243                     else if( sel == ML_END )
244                         vlc_array_append( array, update );
245                     break;
246                 case 0:
247                     update->value.str = va_arg( args, char* );
248                     vlc_array_append( array, update );
249                     break;
250                 case 1:
251                     update->value.i = va_arg( args, int );
252                     vlc_array_append( array, update );
253                     break;
254             }
255         }
256     } while( sel != ML_END );
257
258     va_end( args );
259
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;
263     find->value.i = id ;
264     find->comp = ML_COMP_EQUAL;
265     p_where = ml_FtreeFastAnd( p_where, find );
266
267     /* Let's update the database ! */
268     if( i_ret == VLC_SUCCESS )
269         i_ret = ml_Update( p_media_library, selected_type, psz_lvalue,
270                             p_where, array );
271
272     /* Destroying array */
273     for( int i = 0; i < vlc_array_count( array ); i++ )
274     {
275         free( vlc_array_item_at_index( array, i ) );
276     }
277     vlc_array_destroy( array );
278     ml_FreeFindTree( p_where );
279
280     return i_ret;
281 }
282
283 /**
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!
294  */
295 ml_ftree_t* ml_OpConnectChilds( ml_op_e op, ml_ftree_t* left,
296         ml_ftree_t* right )
297 {
298     /* Use this Op for fresh trees (with only special nodes/none at all!) */
299     if( op == ML_OP_NONE )
300     {
301         assert( ml_FtreeHasOp( left ) == 0 );
302         if( left == NULL )
303             return right;
304         /* Percolate down tree only for special nodes */
305         assert( left->op == ML_OP_SPECIAL );
306         if( left->left == NULL )
307         {
308             left->left = right;
309             return left;
310         }
311         else
312         {
313             return ml_OpConnectChilds( ML_OP_NONE, left->left, right );
314         }
315     }
316     else if( op == ML_OP_NOT )
317     {
318         assert( right == NULL && left != NULL );
319         assert( ml_FtreeHasOp( left ) > 0 );
320     }
321     else if( op == ML_OP_SPECIAL )
322     {
323         assert( 0 );
324     }
325     else
326     {
327         assert( right != NULL && left != NULL );
328         assert( ml_FtreeHasOp( left ) > 0 );
329         assert( ml_FtreeHasOp( right ) > 0 );
330     }
331     ml_ftree_t* p_parent = (ml_ftree_t *) calloc( 1, sizeof( ml_ftree_t ) );
332     p_parent->op = op;
333     p_parent->left = left;
334     p_parent->right = right;
335     return p_parent;
336 }
337
338 #undef ml_FtreeSpec
339 /**
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
347  */
348 ml_ftree_t* ml_FtreeSpec( ml_ftree_t* tree,
349                                           ml_select_e crit,
350                                           int limit,
351                                           char* sort )
352 {
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;
366     return p_parent;
367 }
368
369
370 /**
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
374  */
375 void ml_PlaySmartPlaylistBasedOn( media_library_t* p_ml,
376                                                 ml_ftree_t* p_tree )
377 {
378     assert( p_tree );
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++ )
385     {
386         ml_result_t* p_res = ( ml_result_t* ) vlc_array_item_at_index( p_results, i );
387         input_item_t* p_item;
388         if( p_res )
389         {
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 );
393         }
394     }
395     playlist_Unlock( p_pl );
396     ml_DestroyResultArray( p_results );
397     vlc_array_destroy( p_results );
398 }
399
400 /**
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
406  */
407 ml_person_t*  ml_GetPersonsFromMedia( media_library_t* p_ml,
408                                                     ml_media_t* p_media,
409                                                     const char *psz_role )
410 {
411     VLC_UNUSED( p_ml );
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;
416     while( p_person )
417     {
418         if( strcmp( p_person->psz_role, psz_role ) == 0 )
419         {
420             int i_ret = ml_CreateAppendPerson( &p_return, p_person );
421             if( i_ret != VLC_SUCCESS )
422             {
423                 ml_UnlockMedia( p_media );
424                 ml_FreePeople( p_return );
425                 return NULL;
426             }
427         }
428         p_person = p_person->p_next;
429     }
430     ml_UnlockMedia( p_media );
431     //TODO: Fill up empty names + clean up list
432     return p_return;
433 }
434
435 /**
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
440  */
441 void ml_DeletePersonTypeFromMedia( ml_media_t* p_media, const char *psz_role )
442 {
443     assert( p_media );
444     ml_LockMedia( p_media );
445     ml_person_t* p_prev = NULL;
446     ml_person_t* p_person = p_media->p_people;
447
448     while( p_person )
449     {
450         if( strcmp( p_person->psz_role, psz_role ) == 0 )
451         {
452             if( p_prev == NULL )
453             {
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;
458             }
459             else
460             {
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;
465             }
466         }
467         else
468         {
469             p_prev = p_person;
470             p_person = p_person->p_next;
471         }
472     }
473     ml_UnlockMedia( p_media );
474 }
475
476 #endif /* MEDIA_LIBRARY */