control/dynamic_media_list.c: New dynamic media list object. It shouldn't have great...
[vlc.git] / src / control / dynamic_media_list.c
1 /*****************************************************************************
2  * dynamic_media_list.c: libvlc new API media list functions
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
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 #include "libvlc_internal.h"
25 #include <vlc/libvlc.h>
26 #include <assert.h>
27 #include "vlc_arrays.h"
28
29 static const char * libvlc_default_dynamic_media_list_tag_key = "VLCPlaylist";
30
31 /*
32  * Private functions
33  */
34
35 /**************************************************************************
36  *       own_media_list_item_added (private) (Event Callback)
37  **************************************************************************/
38 static void
39 own_media_list_item_added( const libvlc_event_t * p_event, void * p_user_data )
40 {
41     /* Add our tag */
42     libvlc_dynamic_media_list_t * p_dmlist = p_user_data;
43     libvlc_media_descriptor_t * p_md = p_event->u.media_list_item_added.item;
44
45     if( !p_dmlist->tag || !p_dmlist->psz_tag_key )
46         return;
47
48     libvlc_media_descriptor_add_tag( p_md, p_dmlist->tag,
49                 p_dmlist->psz_tag_key, NULL );
50 }
51
52 /**************************************************************************
53  *       own_media_list_item_removed (private) (Event Callback)
54  **************************************************************************/
55 static void
56 own_media_list_item_deleted( const libvlc_event_t * p_event, void * p_user_data )
57 {
58     /* Remove our tag */
59     libvlc_dynamic_media_list_t * p_dmlist = p_user_data;
60     libvlc_media_descriptor_t * p_md = p_event->u.media_list_item_added.item;
61
62     if( !p_dmlist->tag || !p_dmlist->psz_tag_key )
63         return;
64
65     libvlc_media_descriptor_remove_tag( p_md, p_dmlist->tag,
66                 p_dmlist->psz_tag_key, NULL );
67 }
68
69
70 /**************************************************************************
71  *       dynamic_list_propose_item (private) (Event Callback)
72  *
73  * This is called if the dynamic sublist's data provider adds a new item.
74  **************************************************************************/
75 static void
76 dynamic_list_propose_item( const libvlc_event_t * p_event, void * p_user_data )
77 {
78     /* Check if the item matches our tag query */
79     libvlc_dynamic_media_list_t * p_dmlist = p_user_data;
80     libvlc_media_descriptor_t * p_md = p_event->u.media_list_item_added.item;
81
82     //libvlc_media_descriptor_lock( p_md );
83     if( libvlc_tag_query_match( p_dmlist->p_query, p_md, NULL ) )
84     {
85         int i = libvlc_media_list_index_of_item( p_dmlist->p_mlist, p_md, NULL );
86         if( i >= 0 )
87         {
88             /* We already have it */
89             libvlc_media_list_unlock( p_dmlist->p_mlist );
90             return;
91         }
92         
93         libvlc_media_list_lock( p_dmlist->p_mlist );
94         libvlc_media_list_add_media_descriptor( p_dmlist->p_mlist, p_md, NULL );
95         libvlc_media_list_unlock( p_dmlist->p_mlist );
96     }
97     //libvlc_media_descriptor_unlock( p_md );
98 }
99
100 /**************************************************************************
101  *       dynamic_list_remove_item (private) (Event Callback)
102  *
103  * This is called if the dynamic sublist's data provider adds a new item.
104  **************************************************************************/
105 static void
106 dynamic_list_remove_item( const libvlc_event_t * p_event, void * p_user_data )
107 {
108     /* Remove the item here too if we have it */
109     libvlc_dynamic_media_list_t * p_dmlist = p_user_data;
110     libvlc_media_descriptor_t * p_md = p_event->u.media_list_item_deleted.item;
111
112     //libvlc_media_descriptor_lock( p_md );
113     if( libvlc_tag_query_match( p_dmlist->p_query, p_md, NULL ) )
114     {
115         int i;
116         libvlc_media_list_lock( p_dmlist->p_mlist );        
117         i = libvlc_media_list_index_of_item( p_dmlist->p_mlist, p_md, NULL );
118         if ( i < 0 )
119         {
120             /* We've missed one item addition, that could happen especially
121              * if we add item in a threaded maner, so we just ignore */
122             libvlc_media_list_unlock( p_dmlist->p_mlist );
123             //libvlc_media_descriptor_unlock( p_md );           
124             return;
125         }
126         libvlc_media_list_remove_index( p_dmlist->p_mlist, i, NULL );
127         libvlc_media_list_unlock( p_dmlist->p_mlist );
128     }
129     //libvlc_media_descriptor_unlock( p_md );
130 }
131
132 /**************************************************************************
133  *       dynamic_list_change_item (private) (Event Callback)
134  *
135  * This is called if the dynamic sublist's data provider adds a new item.
136  **************************************************************************/
137 static void
138 dynamic_list_change_item( const libvlc_event_t * p_event , void * p_user_data)
139 {
140     /* Check if the item matches still our tag query */
141
142     libvlc_dynamic_media_list_t * p_dmlist = p_user_data;
143     libvlc_media_descriptor_t * p_md = p_event->u.media_list_item_changed.item;
144     int index;
145
146     libvlc_media_list_lock( p_dmlist->p_mlist );        
147     
148     index = libvlc_media_list_index_of_item( p_dmlist->p_mlist, p_md, NULL );
149     if( index < 0 )
150     {
151         libvlc_media_list_unlock( p_dmlist->p_mlist );     
152         return; /* Not found, no prob, just ignore */
153     }
154
155     //libvlc_media_descriptor_lock( p_md );
156     if( !libvlc_tag_query_match( p_dmlist->p_query, p_md, NULL ) )
157         libvlc_media_list_remove_index( p_dmlist->p_mlist, index, NULL );
158     //libvlc_media_descriptor_unlock( p_md );
159
160     libvlc_media_list_unlock( p_dmlist->p_mlist );        
161 }
162
163 /*
164  * Public libvlc functions
165  */
166
167 /**************************************************************************
168  *       new (Public)
169  **************************************************************************/
170 libvlc_dynamic_media_list_t *
171 libvlc_dynamic_media_list_new(
172                 libvlc_media_list_t * p_mlist,
173                 libvlc_tag_query_t * p_query,
174                 libvlc_tag_t tag,
175                 libvlc_exception_t * p_e )
176 {
177     libvlc_dynamic_media_list_t * p_dmlist;
178     libvlc_event_manager_t * p_em;
179     int count, i;
180
181     (void)p_e;
182
183     p_dmlist = malloc(sizeof(libvlc_dynamic_media_list_t));
184
185     if( !p_mlist )
186         return NULL;
187
188     p_dmlist->i_refcount = 1;
189     p_dmlist->p_libvlc_instance = p_mlist->p_libvlc_instance;
190     p_dmlist->tag = strdup( tag );
191     p_dmlist->psz_tag_key = strdup( libvlc_default_dynamic_media_list_tag_key );
192
193     p_dmlist->p_mlist = libvlc_media_list_new( p_mlist->p_libvlc_instance, p_e );
194     if( !p_dmlist->p_mlist )
195     {
196         if( !libvlc_exception_raised( p_e ) )
197             libvlc_exception_raise( p_e, "Can't get the new media_list" );
198         return NULL;
199     }
200
201     /* We have a query */
202     libvlc_tag_query_retain( p_query );
203     p_dmlist->p_query = p_query;
204
205     /* We have a media provider */
206     libvlc_media_list_retain( p_mlist );
207     p_dmlist->p_media_provider = p_mlist;
208
209     libvlc_media_list_lock( p_mlist );        
210     
211     count = libvlc_media_list_count( p_mlist, p_e );
212
213     /* This should be running in a thread, a good plan to achieve that
214      * move all the dynamic code to libvlc_tag_query. */
215     for( i = 0; i < count; i++ )
216     {
217         libvlc_media_descriptor_t * p_md;
218         p_md = libvlc_media_list_item_at_index( p_mlist, i, p_e );
219         if( libvlc_tag_query_match( p_query, p_md, NULL ) )
220             libvlc_media_list_add_media_descriptor( p_dmlist, p_md, p_e );
221     }
222
223     /* And we will listen to its event, so we can update p_dmlist->p_mlist
224      * accordingly */
225     p_em = libvlc_media_list_event_manager( p_mlist, p_e );
226     libvlc_event_attach( p_em, libvlc_MediaListItemAdded,
227                          dynamic_list_propose_item, p_dmlist, p_e );
228     libvlc_event_attach( p_em, libvlc_MediaListItemDeleted,
229                          dynamic_list_remove_item, p_dmlist, p_e );
230     libvlc_event_attach( p_em, libvlc_MediaListItemChanged,
231                          dynamic_list_change_item, p_dmlist, p_e );
232
233     libvlc_media_list_unlock( p_mlist );        
234
235     /* Make sure item added/removed will gain/loose our mark */
236     p_em = libvlc_media_list_event_manager( p_dmlist->p_mlist, p_e );
237     libvlc_event_attach( p_em, libvlc_MediaListItemAdded,
238                          own_media_list_item_added, p_dmlist, p_e );
239     libvlc_event_attach( p_em, libvlc_MediaListItemDeleted,
240                          own_media_list_item_deleted, p_dmlist, p_e );
241
242
243     return p_dmlist;
244 }
245
246 /**************************************************************************
247  *       release (Public)
248  *
249  * Release an object.
250  **************************************************************************/
251 void libvlc_dynamic_media_list_release( libvlc_dynamic_media_list_t * p_dmlist )
252 {
253     p_dmlist->i_refcount--;
254     if( p_dmlist->i_refcount > 0 )
255         return;
256
257     free( p_dmlist->tag );
258     free( p_dmlist->psz_tag_key );
259
260     /* Refcount null, time to free */
261     if( p_dmlist->p_media_provider )
262         libvlc_media_list_release( p_dmlist->p_media_provider );
263
264     if( p_dmlist->p_query )
265         libvlc_tag_query_release( p_dmlist->p_query );
266  
267     free( p_dmlist );
268 }
269
270 /**************************************************************************
271  *       retain (Public)
272  **************************************************************************/
273 void libvlc_dynamic_media_list_retain( libvlc_dynamic_media_list_t * p_dmlist )
274 {
275     p_dmlist->i_refcount++;
276 }
277
278
279 /**************************************************************************
280  *       media_list (Public)
281  **************************************************************************/
282 libvlc_media_list_t *
283 libvlc_dynamic_media_list_media_list(
284                                 libvlc_dynamic_media_list_t * p_dmlist,
285                                 libvlc_exception_t * p_e )
286 {
287     libvlc_media_list_retain( p_dmlist->p_mlist );
288     return p_dmlist->p_mlist;
289 }