Add sami extension for auto-loading of subs
[vlc.git] / src / misc / addons.c
blobfd5ee96e560c641e48043179df1acb251785f725
1 /*****************************************************************************
2 * addons.c: VLC addons manager
3 *****************************************************************************
4 * Copyright (C) 2014 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_atomic.h>
27 #include <vlc_modules.h>
28 #include <vlc_arrays.h>
29 #include <vlc_interrupt.h>
30 #include "libvlc.h"
32 #include <vlc_addons.h>
34 /*****************************************************************************
35 * Structures/definitions
36 *****************************************************************************/
38 typedef struct addon_entry_owner
40 addon_entry_t entry;
41 atomic_uint refs;
42 } addon_entry_owner_t;
44 struct addons_manager_private_t
46 vlc_object_t *p_parent;
48 struct
50 vlc_thread_t thread;
51 vlc_cond_t waitcond;
52 bool b_live;
53 vlc_mutex_t lock;
54 vlc_interrupt_t *p_interrupt;
55 DECL_ARRAY(char*) uris;
56 DECL_ARRAY(addon_entry_t*) entries;
57 } finder;
59 struct
61 vlc_thread_t thread;
62 vlc_cond_t waitcond;
63 bool b_live;
64 vlc_mutex_t lock;
65 vlc_interrupt_t *p_interrupt;
66 DECL_ARRAY(addon_entry_t*) entries;
67 } installer;
70 static void *FinderThread( void * );
71 static void LoadLocalStorage( addons_manager_t *p_manager );
73 /*****************************************************************************
74 * Public functions
75 *****************************************************************************/
77 addon_entry_t * addon_entry_New(void)
79 addon_entry_owner_t *owner = calloc( 1, sizeof(addon_entry_owner_t) );
80 if( unlikely(owner == NULL) )
81 return NULL;
83 atomic_init( &owner->refs, 1 );
85 addon_entry_t *p_entry = &owner->entry;
86 vlc_mutex_init( &p_entry->lock );
87 ARRAY_INIT( p_entry->files );
88 return p_entry;
91 addon_entry_t * addon_entry_Hold( addon_entry_t * p_entry )
93 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
95 atomic_fetch_add( &owner->refs, 1 );
96 return p_entry;
99 void addon_entry_Release( addon_entry_t * p_entry )
101 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
103 if( atomic_fetch_sub(&owner->refs, 1) != 1 )
104 return;
106 free( p_entry->psz_name );
107 free( p_entry->psz_summary );
108 free( p_entry->psz_description );
109 free( p_entry->psz_archive_uri );
110 free( p_entry->psz_author );
111 free( p_entry->psz_source_uri );
112 free( p_entry->psz_image_uri );
113 free( p_entry->psz_image_data );
114 free( p_entry->psz_source_module );
115 free( p_entry->psz_version );
116 free( p_entry->p_custom );
118 addon_file_t *p_file;
119 FOREACH_ARRAY( p_file, p_entry->files )
120 free( p_file->psz_filename );
121 free( p_file->psz_download_uri );
122 free( p_file );
123 FOREACH_END()
124 ARRAY_RESET( p_entry->files );
126 vlc_mutex_destroy( &p_entry->lock );
127 free( owner );
130 addons_manager_t *addons_manager_New( vlc_object_t *p_this,
131 const struct addons_manager_owner *restrict owner )
133 addons_manager_t *p_manager = malloc( sizeof(addons_manager_t) );
134 if ( !p_manager ) return NULL;
136 p_manager->p_priv = malloc( sizeof(addons_manager_private_t) );
137 if ( !p_manager->p_priv )
139 free( p_manager );
140 return NULL;
143 p_manager->owner = *owner;
144 p_manager->p_priv->p_parent = p_this;
146 p_manager->p_priv->finder.p_interrupt = vlc_interrupt_create();
147 p_manager->p_priv->installer.p_interrupt = vlc_interrupt_create();
148 if ( !p_manager->p_priv->finder.p_interrupt ||
149 !p_manager->p_priv->installer.p_interrupt )
151 if( p_manager->p_priv->finder.p_interrupt )
152 vlc_interrupt_destroy( p_manager->p_priv->finder.p_interrupt );
153 if( p_manager->p_priv->installer.p_interrupt )
154 vlc_interrupt_destroy( p_manager->p_priv->installer.p_interrupt );
155 free( p_manager->p_priv );
156 free( p_manager );
157 return NULL;
160 #define INIT_QUEUE( name ) \
161 p_manager->p_priv->name.b_live = false;\
162 vlc_mutex_init( &p_manager->p_priv->name.lock );\
163 vlc_cond_init( &p_manager->p_priv->name.waitcond );\
164 ARRAY_INIT( p_manager->p_priv->name.entries );
166 INIT_QUEUE( finder )
167 INIT_QUEUE( installer )
168 ARRAY_INIT( p_manager->p_priv->finder.uris );
170 return p_manager;
173 void addons_manager_Delete( addons_manager_t *p_manager )
175 bool b_live;
177 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
178 b_live = p_manager->p_priv->finder.b_live;
179 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
180 if ( b_live )
182 vlc_interrupt_kill( p_manager->p_priv->finder.p_interrupt );
183 vlc_join( p_manager->p_priv->finder.thread, NULL );
186 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
187 b_live = p_manager->p_priv->installer.b_live;
188 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
189 if ( b_live )
191 vlc_interrupt_kill( p_manager->p_priv->installer.p_interrupt );
192 vlc_join( p_manager->p_priv->installer.thread, NULL );
195 #define FREE_QUEUE( name ) \
196 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->name.entries )\
197 addon_entry_Release( p_entry );\
198 FOREACH_END();\
199 ARRAY_RESET( p_manager->p_priv->name.entries );\
200 vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
201 vlc_cond_destroy( &p_manager->p_priv->name.waitcond );\
202 vlc_interrupt_destroy( p_manager->p_priv->name.p_interrupt );
204 FREE_QUEUE( finder )
205 FREE_QUEUE( installer )
206 FOREACH_ARRAY( char *psz_uri, p_manager->p_priv->finder.uris )
207 free( psz_uri );
208 FOREACH_END();
209 ARRAY_RESET( p_manager->p_priv->finder.uris );
211 free( p_manager->p_priv );
212 free( p_manager );
215 void addons_manager_Gather( addons_manager_t *p_manager, const char *psz_uri )
217 if ( !psz_uri )
218 return;
220 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
222 ARRAY_APPEND( p_manager->p_priv->finder.uris, strdup( psz_uri ) );
224 if( !p_manager->p_priv->finder.b_live )
226 if( vlc_clone( &p_manager->p_priv->finder.thread, FinderThread, p_manager,
227 VLC_THREAD_PRIORITY_LOW ) )
229 msg_Err( p_manager->p_priv->p_parent,
230 "cannot spawn entries provider thread" );
231 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
232 return;
234 p_manager->p_priv->finder.b_live = true;
237 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
238 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
241 /*****************************************************************************
242 * Private functions
243 *****************************************************************************/
245 static addon_entry_t * getHeldEntryByUUID( addons_manager_t *p_manager,
246 const addon_uuid_t uuid )
248 addon_entry_t *p_return = NULL;
249 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
250 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->finder.entries )
251 if ( !memcmp( p_entry->uuid, uuid, sizeof( addon_uuid_t ) ) )
253 p_return = p_entry;
254 addon_entry_Hold( p_return );
255 break;
257 FOREACH_END()
258 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
259 return p_return;
262 static void MergeSources( addons_manager_t *p_manager,
263 addon_entry_t **pp_addons, int i_count )
265 addon_entry_t *p_entry, *p_manager_entry;
266 addon_uuid_t zerouuid;
267 memset( zerouuid, 0, sizeof( addon_uuid_t ) );
268 for ( int i=0; i < i_count; i++ )
270 p_entry = pp_addons[i];
271 vlc_mutex_lock( &p_entry->lock );
272 if ( memcmp( p_entry->uuid, zerouuid, sizeof( addon_uuid_t ) ) )
273 p_manager_entry = getHeldEntryByUUID( p_manager, p_entry->uuid );
274 else
275 p_manager_entry = NULL;
276 if ( !p_manager_entry )
278 ARRAY_APPEND( p_manager->p_priv->finder.entries, p_entry );
279 p_manager->owner.addon_found( p_manager, p_entry );
281 else
283 vlc_mutex_lock( &p_manager_entry->lock );
284 if ( ( p_manager_entry->psz_version && p_entry->psz_version )
285 && /* FIXME: better version comparison */
286 strcmp( p_manager_entry->psz_version, p_entry->psz_version )
289 p_manager_entry->e_flags |= ADDON_UPDATABLE;
291 vlc_mutex_unlock( &p_manager_entry->lock );
292 addon_entry_Release( p_manager_entry );
294 vlc_mutex_unlock( &p_entry->lock );
298 static void LoadLocalStorage( addons_manager_t *p_manager )
300 addons_finder_t *p_finder =
301 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
302 p_finder->obj.flags |= OBJECT_FLAGS_NOINTERACT;
304 module_t *p_module = module_need( p_finder, "addons finder",
305 "addons.store.list", true );
306 if( p_module )
308 ARRAY_INIT( p_finder->entries );
309 p_finder->psz_uri = NULL;
310 p_finder->pf_find( p_finder );
311 module_unneed( p_finder, p_module );
313 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
315 ARRAY_RESET( p_finder->entries );
317 vlc_object_release( p_finder );
320 static void finder_thread_interrupted( void* p_data )
322 addons_manager_t *p_manager = p_data;
323 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
324 p_manager->p_priv->finder.b_live = false;
325 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
326 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
329 static void *FinderThread( void *p_data )
331 addons_manager_t *p_manager = p_data;
332 int i_cancel = vlc_savecancel();
333 vlc_interrupt_set( p_manager->p_priv->finder.p_interrupt );
335 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
336 while( p_manager->p_priv->finder.b_live )
338 char *psz_uri;
340 vlc_interrupt_register( finder_thread_interrupted, p_data );
341 while( p_manager->p_priv->finder.uris.i_size == 0 &&
342 p_manager->p_priv->finder.b_live )
344 vlc_cond_wait( &p_manager->p_priv->finder.waitcond,
345 &p_manager->p_priv->finder.lock );
347 vlc_interrupt_unregister();
348 if( !p_manager->p_priv->finder.b_live )
349 break;
350 psz_uri = p_manager->p_priv->finder.uris.p_elems[0];
351 ARRAY_REMOVE( p_manager->p_priv->finder.uris, 0 );
353 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
355 addons_finder_t *p_finder =
356 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
358 if( p_finder != NULL )
360 p_finder->obj.flags |= OBJECT_FLAGS_NOINTERACT;
361 module_t *p_module;
362 ARRAY_INIT( p_finder->entries );
363 p_finder->psz_uri = psz_uri;
365 p_module = module_need( p_finder, "addons finder", NULL, false );
366 if( p_module )
368 p_finder->pf_find( p_finder );
369 module_unneed( p_finder, p_module );
370 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
372 ARRAY_RESET( p_finder->entries );
373 free( psz_uri );
374 vlc_object_release( p_finder );
377 p_manager->owner.discovery_ended( p_manager );
378 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
381 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
382 vlc_restorecancel( i_cancel );
383 return NULL;
386 static int addons_manager_WriteCatalog( addons_manager_t *p_manager )
388 int i_return = VLC_EGENERIC;
390 addons_storage_t *p_storage =
391 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
392 p_storage->obj.flags |= OBJECT_FLAGS_NOINTERACT;
394 module_t *p_module = module_need( p_storage, "addons storage",
395 "addons.store.install", true );
396 if( p_module )
398 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
399 i_return = p_storage->pf_catalog( p_storage, p_manager->p_priv->finder.entries.p_elems,
400 p_manager->p_priv->finder.entries.i_size );
401 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
402 module_unneed( p_storage, p_module );
404 vlc_object_release( p_storage );
406 return i_return;
409 int addons_manager_LoadCatalog( addons_manager_t *p_manager )
411 LoadLocalStorage( p_manager );
412 return VLC_SUCCESS;
415 static int installOrRemoveAddon( addons_manager_t *p_manager, addon_entry_t *p_entry, bool b_install )
417 int i_return = VLC_EGENERIC;
419 addons_storage_t *p_storage =
420 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
421 p_storage->obj.flags |= OBJECT_FLAGS_NOINTERACT;
423 module_t *p_module = module_need( p_storage, "addons storage",
424 "addons.store.install", true );
425 if( p_module )
427 if ( b_install )
428 i_return = p_storage->pf_install( p_storage, p_entry );
429 else
430 i_return = p_storage->pf_remove( p_storage, p_entry );
431 module_unneed( p_storage, p_module );
432 msg_Dbg( p_manager->p_priv->p_parent, "InstallAddon returns %d", i_return );
433 if ( i_return == VLC_SUCCESS )
435 /* Reset flags */
436 vlc_mutex_lock( &p_entry->lock );
437 p_entry->e_flags = ADDON_MANAGEABLE;
438 vlc_mutex_unlock( &p_entry->lock );
441 vlc_object_release( p_storage );
443 return i_return;
446 static void installer_thread_interrupted( void* p_data )
448 addons_manager_t *p_manager = p_data;
449 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
450 p_manager->p_priv->installer.b_live = false;
451 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
452 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
455 static void *InstallerThread( void *p_data )
457 addons_manager_t *p_manager = p_data;
458 int i_cancel = vlc_savecancel();
459 vlc_interrupt_set( p_manager->p_priv->installer.p_interrupt );
460 int i_ret;
462 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
463 while( p_manager->p_priv->installer.b_live )
465 vlc_interrupt_register( installer_thread_interrupted, p_data );
466 while ( !p_manager->p_priv->installer.entries.i_size &&
467 p_manager->p_priv->installer.b_live )
469 /* No queued addons */
470 vlc_cond_wait( &p_manager->p_priv->installer.waitcond,
471 &p_manager->p_priv->installer.lock );
473 vlc_interrupt_unregister();
474 if( !p_manager->p_priv->installer.b_live )
475 break;
477 addon_entry_t *p_entry = p_manager->p_priv->installer.entries.p_elems[0];
478 ARRAY_REMOVE( p_manager->p_priv->installer.entries, 0 );
479 addon_entry_Hold( p_entry );
480 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
482 vlc_mutex_lock( &p_entry->lock );
483 /* DO WORK */
484 if ( p_entry->e_state == ADDON_INSTALLED )
486 p_entry->e_state = ADDON_UNINSTALLING;
487 vlc_mutex_unlock( &p_entry->lock );
489 /* notify */
490 p_manager->owner.addon_changed( p_manager, p_entry );
492 i_ret = installOrRemoveAddon( p_manager, p_entry, false );
494 vlc_mutex_lock( &p_entry->lock );
495 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_NOTINSTALLED
496 : ADDON_INSTALLED;
498 else if ( p_entry->e_state == ADDON_NOTINSTALLED )
500 p_entry->e_state = ADDON_INSTALLING;
501 vlc_mutex_unlock( &p_entry->lock );
503 /* notify */
504 p_manager->owner.addon_changed( p_manager, p_entry );
506 i_ret = installOrRemoveAddon( p_manager, p_entry, true );
508 vlc_mutex_lock( &p_entry->lock );
509 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_INSTALLED
510 : ADDON_NOTINSTALLED;
512 vlc_mutex_unlock( &p_entry->lock );
513 /* !DO WORK */
515 p_manager->owner.addon_changed( p_manager, p_entry );
517 addon_entry_Release( p_entry );
519 addons_manager_WriteCatalog( p_manager );
520 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
522 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
523 vlc_restorecancel( i_cancel );
524 return NULL;
527 static int InstallEntry( addons_manager_t *p_manager, addon_entry_t *p_entry )
529 if ( p_entry->e_type == ADDON_UNKNOWN ||
530 p_entry->e_type == ADDON_PLUGIN ||
531 p_entry->e_type == ADDON_OTHER )
532 return VLC_EBADVAR;
534 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
535 ARRAY_APPEND( p_manager->p_priv->installer.entries, p_entry );
536 if( !p_manager->p_priv->installer.b_live )
538 if( vlc_clone( &p_manager->p_priv->installer.thread, InstallerThread, p_manager,
539 VLC_THREAD_PRIORITY_LOW ) )
541 msg_Err( p_manager->p_priv->p_parent,
542 "cannot spawn addons installer thread" );
543 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
544 return VLC_EGENERIC;
546 else
547 p_manager->p_priv->installer.b_live = true;
549 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
550 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
551 return VLC_SUCCESS;
554 int addons_manager_Install( addons_manager_t *p_manager, const addon_uuid_t uuid )
556 addon_entry_t *p_install_entry = getHeldEntryByUUID( p_manager, uuid );
557 if ( ! p_install_entry ) return VLC_EGENERIC;
558 int i_ret = InstallEntry( p_manager, p_install_entry );
559 addon_entry_Release( p_install_entry );
560 return i_ret;
563 int addons_manager_Remove( addons_manager_t *p_manager, const addon_uuid_t uuid )
565 return addons_manager_Install( p_manager, uuid );