]> git.sesse.net Git - vlc/blob - src/misc/media_library.c
Remove obvious statements.
[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 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #if defined(MEDIA_LIBRARY)
29
30 #include <assert.h>
31 #include <vlc_media_library.h>
32 #include <vlc_modules.h>
33 #include "../libvlc.h"
34
35 /**
36  * @brief Destroy the medialibrary object
37  * @param Parent object that holds the media library object
38  */
39 void ml_Destroy( vlc_object_t * p_this )
40 {
41     media_library_t* p_ml = ( media_library_t* )p_this;
42     module_unneed( p_ml, p_ml->p_module );
43 }
44
45
46 /**
47  * Atomically set the reference count to 1.
48  * @param p_gc reference counted object
49  * @param pf_destruct destruction calback
50  * @return p_gc.
51  */
52 static void *ml_gc_init (ml_gc_object_t *p_gc, void (*pf_destruct) (ml_gc_object_t *))
53 {
54     /* There is no point in using the GC if there is no destructor... */
55     assert (pf_destruct);
56     p_gc->pf_destructor = pf_destruct;
57
58     p_gc->pool = false;
59     p_gc->refs = 1;
60     /* Nobody else can possibly lock the spin - it's there as a barrier */
61     vlc_spin_init (&p_gc->spin);
62     vlc_spin_lock (&p_gc->spin);
63     vlc_spin_unlock (&p_gc->spin);
64     return p_gc;
65 }
66
67
68
69 /**
70  * @brief Create an instance of the media library
71  * @param p_this Parent object
72  * @param psz_name Name which is passed to module_need (not needed)
73  * @return p_ml created and attached, module loaded. NULL if
74  * not able to load
75  */
76 media_library_t *ml_Create( vlc_object_t *p_this, char *psz_name )
77 {
78     media_library_t *p_ml;
79
80     p_ml = ( media_library_t * ) vlc_custom_create(
81                                 p_this, sizeof( media_library_t ),
82                                 VLC_OBJECT_GENERIC, "media-library" );
83     if( !p_ml )
84     {
85         msg_Err( p_this, "unable to create media library object" );
86         return NULL;
87     }
88     vlc_object_attach( p_ml, p_this );
89
90     p_ml->p_module = module_need( p_ml, "media-library", psz_name, false );
91     if( !p_ml->p_module )
92     {
93         vlc_object_release( p_ml );
94         msg_Err( p_this, "Media Library provider not found" );
95         return NULL;
96     }
97
98     return p_ml;
99 }
100
101 #undef ml_Get
102 /**
103  * @brief Acquire a reference to the media library singleton
104  * @param p_this Object that holds the reference
105  * @return media_library_t The ml object. NULL if not compiled with
106  * media library or if unable to load
107  */
108 media_library_t* ml_Get( vlc_object_t* p_this )
109 {
110     media_library_t* p_ml;
111     vlc_mutex_lock( &( libvlc_priv( p_this->p_libvlc )->ml_lock ) );
112     p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
113     assert( VLC_OBJECT( p_ml ) != p_this );
114     if( p_ml == NULL &&
115         !var_GetBool( p_this->p_libvlc, "load-media-library-on-startup" ) )
116     {
117         libvlc_priv (p_this->p_libvlc)->p_ml
118             = ml_Create( VLC_OBJECT( p_this->p_libvlc ), NULL );
119         p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
120     }
121     vlc_mutex_unlock( &( libvlc_priv( p_this->p_libvlc )->ml_lock ) );
122     return p_ml;
123 }
124
125 /**
126  * @brief Destructor for ml_media_t
127  */
128 static void media_Destroy( ml_gc_object_t *p_gc )
129 {
130     ml_media_t* p_media = ml_priv( p_gc, ml_media_t );
131     vlc_mutex_destroy( &p_media->lock );
132     ml_FreeMediaContent( p_media );
133     free( p_media );
134 }
135
136 /**
137  * @brief Object constructor for ml_media_t
138  * @param p_ml The media library object
139  * @param id If 0, this item isn't in database. If non zero, it is and
140  * it will be a singleton
141  * @param select Type of object
142  * @param reload Whether to reload from database
143  */
144 ml_media_t* media_New( media_library_t* p_ml, int id,
145         ml_select_e select, bool reload )
146 {
147     if( id == 0 )
148     {
149         ml_media_t* p_media = NULL;
150         p_media = ( ml_media_t* )calloc( 1, sizeof( ml_media_t ) );
151         ml_gc_init( &p_media->ml_gc_data, media_Destroy );
152         vlc_mutex_init( &p_media->lock );
153         return p_media;
154     }
155     else
156         return p_ml->functions.pf_GetMedia( p_ml, id, select, reload );
157 }
158
159 #undef ml_UpdateSimple
160 /**
161  * @brief Update a given table
162  * @param p_media_library The media library object
163  * @param selected_type The table to update
164  * @param psz_lvalue The role of the person if selected_type = ML_PEOPLE
165  * @param id The id of the row to update
166  * @param ... The update data. [SelectType [RoleType] Value] ... ML_END
167  */
168 int ml_UpdateSimple( media_library_t *p_media_library,
169                                      ml_select_e selected_type,
170                                      const char* psz_lvalue,
171                                      int id, ... )
172 {
173     ml_element_t *update;
174     vlc_array_t *array = vlc_array_new();
175     int i_ret = VLC_SUCCESS;
176
177     va_list args;
178     va_start( args, id );
179
180     ml_select_e sel;
181     do {
182         update = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
183         sel = ( ml_select_e ) va_arg( args, int );
184         update->criteria = sel;
185         if( sel == ML_PEOPLE )
186         {
187             update->lvalue.str = va_arg( args, char* );
188             update->value.str = va_arg( args, char* );
189             vlc_array_append( array, update );
190         }
191         else if( sel == ML_PEOPLE_ID )
192         {
193            update->lvalue.str = va_arg( args, char* );
194            update->value.i = va_arg( args, int );
195            vlc_array_append( array, update );
196         }
197         else if( sel == ML_PEOPLE_ROLE )
198         {
199 #ifndef NDEBUG
200             msg_Dbg( p_media_library,
201                      "this argument is invalid for Update: %d",
202                      (int)sel );
203 #endif
204         }
205         else
206         {
207             switch( ml_AttributeIsString( sel ) )
208             {
209                 case -1:
210                     if( sel != ML_END )
211                     {
212 #ifndef NDEBUG
213                         msg_Dbg( p_media_library,
214                                  "this argument is invalid for Update: %d",
215                                  (int)sel );
216 #endif
217                         i_ret = VLC_EGENERIC;
218                     }
219                     else if( sel == ML_END )
220                         vlc_array_append( array, update );
221                     break;
222                 case 0:
223                     update->value.str = va_arg( args, char* );
224                     vlc_array_append( array, update );
225                     break;
226                 case 1:
227                     update->value.i = va_arg( args, int );
228                     vlc_array_append( array, update );
229                     break;
230             }
231         }
232     } while( sel != ML_END );
233
234     va_end( args );
235
236     ml_ftree_t* p_where = NULL;
237     ml_ftree_t* find = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
238     find->criteria = ML_ID;
239     find->value.i = id ;
240     find->comp = ML_COMP_EQUAL;
241     p_where = ml_FtreeFastAnd( p_where, find );
242
243     /* Let's update the database ! */
244     if( i_ret == VLC_SUCCESS )
245         i_ret = ml_Update( p_media_library, selected_type, psz_lvalue,
246                             p_where, array );
247
248     /* Destroying array */
249     for( int i = 0; i < vlc_array_count( array ); i++ )
250     {
251         free( vlc_array_item_at_index( array, i ) );
252     }
253     vlc_array_destroy( array );
254     ml_FreeFindTree( p_where );
255
256     return i_ret;
257 }
258
259 /**
260  * @brief Connect up a find tree
261  * @param op operator to connect with
262  * If op = ML_OP_NONE, then you are connecting to a tree consisting of
263  * only SPECIAL nodes.
264  * If op = ML_OP_NOT, then right MUST be NULL
265  * op must not be ML_OP_SPECIAL, @see ml_FtreeSpec
266  * @param left part of the tree
267  * @param right part of the tree
268  * @return Pointer to new tree
269  * @note Use the helpers!
270  */
271 ml_ftree_t* ml_OpConnectChilds( ml_op_e op, ml_ftree_t* left,
272         ml_ftree_t* right )
273 {
274     /* Use this Op for fresh trees (with only special nodes/none at all!) */
275     if( op == ML_OP_NONE )
276     {
277         assert( ml_FtreeHasOp( left ) == 0 );
278         if( left == NULL )
279             return right;
280         /* Percolate down tree only for special nodes */
281         assert( left->op == ML_OP_SPECIAL );
282         if( left->left == NULL )
283         {
284             left->left = right;
285             return left;
286         }
287         else
288         {
289             return ml_OpConnectChilds( ML_OP_NONE, left->left, right );
290         }
291     }
292     else if( op == ML_OP_NOT )
293     {
294         assert( right == NULL && left != NULL );
295         assert( ml_FtreeHasOp( left ) > 0 );
296     }
297     else if( op == ML_OP_SPECIAL )
298     {
299         assert( 0 );
300     }
301     else
302     {
303         assert( right != NULL && left != NULL );
304         assert( ml_FtreeHasOp( left ) > 0 );
305         assert( ml_FtreeHasOp( right ) > 0 );
306     }
307     ml_ftree_t* p_parent = (ml_ftree_t *) calloc( 1, sizeof( ml_ftree_t ) );
308     p_parent->op = op;
309     p_parent->left = left;
310     p_parent->right = right;
311     return p_parent;
312 }
313
314 #undef ml_FtreeSpec
315 /**
316  * @brief Attaches a special node to a tree
317  * @param tree Tree to attach special node to
318  * @param crit Criteria may be SORT_ASC, SORT_DESC, LIMIT or DISTINCT
319  * @param limit Limit used if LIMIT criteria used
320  * @param Sort string used if SORT criteria is used
321  * @return Pointer to new tree
322  * @note Use the helpers
323  */
324 ml_ftree_t* ml_FtreeSpec( ml_ftree_t* tree,
325                                           ml_select_e crit,
326                                           int limit,
327                                           char* sort )
328 {
329     assert( crit == ML_SORT_ASC || crit == ML_LIMIT || crit == ML_SORT_DESC ||
330             crit == ML_DISTINCT );
331     ml_ftree_t* right = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
332     right->criteria = crit;
333     if( crit == ML_LIMIT )
334         right->value.i = limit;
335     else if( crit == ML_SORT_ASC || crit == ML_SORT_DESC )
336         right->value.str = strdup( sort );
337     right->op = ML_OP_NONE;
338     ml_ftree_t* p_parent = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
339     p_parent->right = right;
340     p_parent->op = ML_OP_SPECIAL;
341     p_parent->left = tree;
342     return p_parent;
343 }
344
345
346 /**
347  * @brief Creates and adds the playlist based on a given find tree
348  * @param p_ml Media library object
349  * @param p_tree Find tree to create SELECT
350  */
351 void ml_PlaySmartPlaylistBasedOn( media_library_t* p_ml,
352                                                 ml_ftree_t* p_tree )
353 {
354     assert( p_tree );
355     vlc_array_t* p_results = vlc_array_new();
356     ml_FindAdv( p_ml, p_results, ML_ID, NULL, p_tree );
357     playlist_t* p_pl = pl_Get( p_ml );
358     playlist_Lock( p_pl );
359     playlist_Clear( p_pl, true );
360     for( int i = 0; i < vlc_array_count( p_results ); i++ )
361     {
362         ml_result_t* p_res = ( ml_result_t* ) vlc_array_item_at_index( p_results, i );
363         input_item_t* p_item;
364         if( p_res )
365         {
366             p_item = ml_CreateInputItem( p_ml, p_res->value.i );
367             playlist_AddInput( p_pl, p_item, PLAYLIST_APPEND,
368                            PLAYLIST_END, true, true );
369         }
370     }
371     playlist_Unlock( p_pl );
372     ml_DestroyResultArray( p_results );
373     vlc_array_destroy( p_results );
374 }
375
376 /**
377  * @brief Returns a person list of given type
378  * @param p_ml The ML object
379  * @param p_media The Media object
380  * @param i_type The person type
381  * @note This function is thread safe
382  */
383 ml_person_t*  ml_GetPersonsFromMedia( media_library_t* p_ml,
384                                                     ml_media_t* p_media,
385                                                     const char *psz_role )
386 {
387     VLC_UNUSED( p_ml );
388     assert( p_media != NULL );
389     ml_person_t* p_return = NULL;
390     ml_LockMedia( p_media );
391     ml_person_t* p_person = p_media->p_people;
392     while( p_person )
393     {
394         if( strcmp( p_person->psz_role, psz_role ) == 0 )
395         {
396             int i_ret = ml_CreateAppendPerson( &p_return, p_person );
397             if( i_ret != VLC_SUCCESS )
398             {
399                 ml_UnlockMedia( p_media );
400                 ml_FreePeople( p_return );
401                 return NULL;
402             }
403         }
404         p_person = p_person->p_next;
405     }
406     ml_UnlockMedia( p_media );
407     //TODO: Fill up empty names + clean up list
408     return p_return;
409 }
410
411 /**
412  * @brief Delete a certain type of people from a media
413  * @param p_media Media to delete from
414  * @param i_type Type of person to delete
415  * @note This function is threadsafe
416  */
417 void ml_DeletePersonTypeFromMedia( ml_media_t* p_media, const char *psz_role )
418 {
419     assert( p_media );
420     ml_LockMedia( p_media );
421     ml_person_t* p_prev = NULL;
422     ml_person_t* p_person = p_media->p_people;
423
424     while( p_person )
425     {
426         if( strcmp( p_person->psz_role, psz_role ) == 0 )
427         {
428             if( p_prev == NULL )
429             {
430                 p_media->p_people = p_person->p_next;
431                 p_person->p_next = NULL;
432                 ml_FreePeople( p_person );
433                 p_person = p_media->p_people;
434             }
435             else
436             {
437                 p_prev->p_next = p_person->p_next;
438                 p_person->p_next = NULL;
439                 ml_FreePeople( p_person );
440                 p_person = p_prev->p_next;
441             }
442         }
443         else
444         {
445             p_prev = p_person;
446             p_person = p_person->p_next;
447         }
448     }
449     ml_UnlockMedia( p_media );
450 }
451
452 #endif /* MEDIA_LIBRARY */