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