]> git.sesse.net Git - vlc/blob - modules/lua/libs/sd.c
48a2434341412ce3c81091bb14c4d88ff9a76e75
[vlc] / modules / lua / libs / sd.c
1 /*****************************************************************************
2  * sd.c: Services discovery related functions
3  *****************************************************************************
4  * Copyright (C) 2007-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *          Fabio Ritrovato <sephiroth87 at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifndef  _GNU_SOURCE
29 #   define  _GNU_SOURCE
30 #endif
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc_common.h>
37 #include <vlc_services_discovery.h>
38 #include <vlc_playlist.h>
39 #include <vlc_charset.h>
40
41 #include "../vlc.h"
42 #include "../libs.h"
43 #include "playlist.h"
44
45 /*****************************************************************************
46  *
47  *****************************************************************************/
48 static int vlclua_node_add_subitem( lua_State * );
49 static int vlclua_node_add_subnode( lua_State * );
50
51 static const luaL_Reg vlclua_node_reg[] = {
52     { "add_subitem", vlclua_node_add_subitem },
53     { "add_subnode", vlclua_node_add_subnode },
54     { NULL, NULL }
55 };
56
57 #define vlclua_item_luareg( a ) \
58 { "set_" # a, vlclua_item_set_ ## a },
59
60 #define vlclua_item_meta( lowercase, normal ) \
61 static int vlclua_item_set_ ## lowercase ( lua_State *L )\
62 {\
63     services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );\
64     input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "input_item_t" );\
65     if( *pp_node )\
66     {\
67         if( lua_isstring( L, -1 ) )\
68         {\
69             input_item_Set ## normal ( *pp_node, lua_tostring( L, -1 ) );\
70         } else\
71             msg_Err( p_sd, "Error parsing set_ " # lowercase " arguments" );\
72     }\
73     return 1;\
74 }
75
76 vlclua_item_meta(title, Title)
77 vlclua_item_meta(artist, Artist)
78 vlclua_item_meta(genre, Genre)
79 vlclua_item_meta(copyright, Copyright)
80 vlclua_item_meta(album, Album)
81 vlclua_item_meta(tracknum, TrackNum)
82 vlclua_item_meta(description, Description)
83 vlclua_item_meta(rating, Rating)
84 vlclua_item_meta(date, Date)
85 vlclua_item_meta(setting, Setting)
86 vlclua_item_meta(url, URL)
87 vlclua_item_meta(language, Language)
88 vlclua_item_meta(nowplaying, NowPlaying)
89 vlclua_item_meta(publisher, Publisher)
90 vlclua_item_meta(encodedby, EncodedBy)
91 vlclua_item_meta(arturl, ArtworkURL)
92 vlclua_item_meta(trackid, TrackID)
93
94 static const luaL_Reg vlclua_item_reg[] = {
95     vlclua_item_luareg(title)
96     vlclua_item_luareg(artist)
97     vlclua_item_luareg(genre)
98     vlclua_item_luareg(copyright)
99     vlclua_item_luareg(album)
100     vlclua_item_luareg(tracknum)
101     vlclua_item_luareg(description)
102     vlclua_item_luareg(rating)
103     vlclua_item_luareg(date)
104     vlclua_item_luareg(setting)
105     vlclua_item_luareg(url)
106     vlclua_item_luareg(language)
107     vlclua_item_luareg(nowplaying)
108     vlclua_item_luareg(publisher)
109     vlclua_item_luareg(encodedby)
110     vlclua_item_luareg(arturl)
111     vlclua_item_luareg(trackid)
112     { NULL, NULL }
113 };
114
115 static int vlclua_sd_get_services_names( lua_State *L )
116 {
117     playlist_t *p_playlist = vlclua_get_playlist_internal( L );
118     char **ppsz_longnames;
119     char **ppsz_names = vlc_sd_GetNames( p_playlist, &ppsz_longnames, NULL );
120     if( !ppsz_names )
121         return 0;
122
123     char **ppsz_longname = ppsz_longnames;
124     char **ppsz_name = ppsz_names;
125     lua_settop( L, 0 );
126     lua_newtable( L );
127     for( ; *ppsz_name; ppsz_name++,ppsz_longname++ )
128     {
129         lua_pushstring( L, *ppsz_longname );
130         lua_setfield( L, -2, *ppsz_name );
131         free( *ppsz_name );
132         free( *ppsz_longname );
133     }
134     free( ppsz_names );
135     free( ppsz_longnames );
136     return 1;
137 }
138
139 static int vlclua_sd_add( lua_State *L )
140 {
141     const char *psz_sd = luaL_checkstring( L, 1 );
142     playlist_t *p_playlist = vlclua_get_playlist_internal( L );
143     int i_ret = playlist_ServicesDiscoveryAdd( p_playlist, psz_sd );
144     return vlclua_push_ret( L, i_ret );
145 }
146
147 static int vlclua_sd_remove( lua_State *L )
148 {
149     const char *psz_sd = luaL_checkstring( L, 1 );
150     playlist_t *p_playlist = vlclua_get_playlist_internal( L );
151     int i_ret = playlist_ServicesDiscoveryRemove( p_playlist, psz_sd );
152     return vlclua_push_ret( L, i_ret );
153 }
154
155 static int vlclua_sd_is_loaded( lua_State *L )
156 {
157     const char *psz_sd = luaL_checkstring( L, 1 );
158     playlist_t *p_playlist = vlclua_get_playlist_internal( L );
159     lua_pushboolean( L, playlist_IsServicesDiscoveryLoaded( p_playlist, psz_sd ));
160     return 1;
161 }
162
163 static int vlclua_sd_add_node( lua_State *L )
164 {
165     services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
166     if( lua_istable( L, -1 ) )
167     {
168         lua_getfield( L, -1, "title" );
169         if( lua_isstring( L, -1 ) )
170         {
171             const char *psz_name = lua_tostring( L, -1 );
172             input_item_t *p_input = input_item_NewWithType( "vlc://nop",
173                                                             psz_name, 0, NULL, 0,
174                                                             -1, ITEM_TYPE_NODE );
175             lua_pop( L, 1 );
176
177             if( p_input )
178             {
179                 lua_getfield( L, -1, "arturl" );
180                 if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) )
181                 {
182                     char *psz_value = strdup( lua_tostring( L, -1 ) );
183                     EnsureUTF8( psz_value );
184                     msg_Dbg( p_sd, "ArtURL: %s", psz_value );
185                     /** @todo Ask for art download if not local file */
186                     input_item_SetArtURL( p_input, psz_value );
187                     free( psz_value );
188                 }
189                 lua_pop( L, 1 );
190                 lua_getfield( L, -1, "category" );
191                 if( lua_isstring( L, -1 ) )
192                     services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) );
193                 else
194                     services_discovery_AddItem( p_sd, p_input, NULL );
195                 input_item_t **udata = (input_item_t **)
196                                        lua_newuserdata( L, sizeof( input_item_t * ) );
197                 *udata = p_input;
198                 if( luaL_newmetatable( L, "node" ) )
199                 {
200                     lua_newtable( L );
201                     luaL_register( L, NULL, vlclua_node_reg );
202                     lua_setfield( L, -2, "__index" );
203                 }
204                 lua_setmetatable( L, -2 );
205             }
206         }
207         else
208             msg_Err( p_sd, "vlc.sd.add_node: the \"title\" parameter can't be empty" );
209     }
210     else
211         msg_Err( p_sd, "Error parsing add_node arguments" );
212     return 1;
213 }
214
215 static int vlclua_sd_add_item( lua_State *L )
216 {
217     services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
218     if( lua_istable( L, -1 ) )
219     {
220         lua_getfield( L, -1, "path" );
221         if( lua_isstring( L, -1 ) )
222         {
223             char **ppsz_options = NULL;
224             int i_options = 0;
225             const char *psz_path = lua_tostring( L, -1 );
226
227             vlclua_read_options( p_sd, L, &i_options, &ppsz_options );
228             lua_pop( L, 1 );
229             lua_getfield( L, -1, "title" );
230             const char *psz_title = luaL_checkstring( L, -1 ) ? luaL_checkstring( L, -1 ) : psz_path;
231             input_item_t *p_input = input_item_NewExt( psz_path, psz_title,
232                                                        i_options,
233                                                        (const char **)ppsz_options,
234                                                        VLC_INPUT_OPTION_TRUSTED, -1 );
235             lua_pop( L, 1 );
236
237             if( p_input )
238             {
239                 vlclua_read_meta_data( p_sd, L, p_input );
240                 /* This one is to be tested... */
241                 vlclua_read_custom_meta_data( p_sd, L, p_input );
242                 /* The duration is given in seconds, convert to microseconds */
243                 lua_getfield( L, -1, "duration" );
244                 if( lua_isnumber( L, -1 ) )
245                    input_item_SetDuration( p_input, (lua_tonumber( L, -1 )*1e6) );
246                 else if( !lua_isnil( L, -1 ) )
247                     msg_Warn( p_sd, "Item duration should be a number (in seconds)." );
248                 lua_pop( L, 1 );
249                 lua_getfield( L, -1, "category" );
250                 if( lua_isstring( L, -1 ) )
251                     services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) );
252                 else
253                     services_discovery_AddItem( p_sd, p_input, NULL );
254                 lua_pop( L, 1 );
255                 input_item_t **udata = (input_item_t **)
256                                        lua_newuserdata( L, sizeof( input_item_t * ) );
257                 *udata = p_input;
258                 if( luaL_newmetatable( L, "input_item_t" ) )
259                 {
260                     lua_newtable( L );
261                     luaL_register( L, NULL, vlclua_item_reg );
262                     lua_setfield( L, -2, "__index" );
263                     lua_pushliteral( L, "none of your business" );
264                     lua_setfield( L, -2, "__metatable" );
265                 }
266                 lua_setmetatable( L, -2 );
267                 vlc_gc_decref( p_input );
268             }
269             while( i_options > 0 )
270                 free( ppsz_options[--i_options] );
271             free( ppsz_options );
272         }
273         else
274             msg_Err( p_sd, "vlc.sd.add_item: the \"path\" parameter can't be empty" );
275     }
276     else
277         msg_Err( p_sd, "Error parsing add_item arguments" );
278     return 1;
279 }
280
281 static int vlclua_sd_remove_item( lua_State *L )
282 {
283     services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
284     if( !lua_isnil( L, 1 ) )
285     {
286         input_item_t **pp_input = luaL_checkudata( L, 1, "input_item_t" );
287         if( *pp_input )
288             services_discovery_RemoveItem( p_sd, *pp_input );
289         /* Make sure we won't try to remove it again */
290         *pp_input = NULL;
291     }
292     return 1;
293 }
294
295 static int vlclua_node_add_subitem( lua_State *L )
296 {
297     services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
298     input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" );
299     if( *pp_node )
300     {
301         if( lua_istable( L, -1 ) )
302         {
303             lua_getfield( L, -1, "path" );
304             if( lua_isstring( L, -1 ) )
305             {
306                 char **ppsz_options = NULL;
307                 int i_options = 0;
308                 const char *psz_path = lua_tostring( L, -1 );
309                 vlclua_read_options( p_sd, L, &i_options, &ppsz_options );
310                 input_item_node_t *p_input_node = input_item_node_Create( *pp_node );
311                 input_item_t *p_input = input_item_NewExt( psz_path,
312                                                            psz_path, i_options,
313                                                            (const char **)ppsz_options,
314                                                            VLC_INPUT_OPTION_TRUSTED, -1 );
315                 lua_pop( L, 1 );
316
317                 if( p_input )
318                 {
319                     vlclua_read_meta_data( p_sd, L, p_input );
320                     /* This one is to be tested... */
321                     vlclua_read_custom_meta_data( p_sd, L, p_input );
322                     lua_getfield( L, -1, "duration" );
323                     if( lua_isnumber( L, -1 ) )
324                         input_item_SetDuration( p_input, (lua_tonumber( L, -1 )*1e6) );
325                     else if( !lua_isnil( L, -1 ) )
326                         msg_Warn( p_sd, "Item duration should be a number (in seconds)." );
327                     lua_pop( L, 1 );
328                     input_item_node_AppendItem( p_input_node, p_input );
329                     input_item_node_PostAndDelete( p_input_node );
330                     input_item_t **udata = (input_item_t **)
331                                            lua_newuserdata( L, sizeof( input_item_t * ) );
332                     *udata = p_input;
333                     if( luaL_newmetatable( L, "input_item_t" ) )
334                     {
335                         lua_newtable( L );
336                         luaL_register( L, NULL, vlclua_item_reg );
337                         lua_setfield( L, -2, "__index" );
338                         lua_pushliteral( L, "none of your business" );
339                         lua_setfield( L, -2, "__metatable" );
340                     }
341                     lua_setmetatable( L, -2 );
342                     vlc_gc_decref( p_input );
343                 }
344                 while( i_options > 0 )
345                     free( ppsz_options[--i_options] );
346                 free( ppsz_options );
347             }
348             else
349                 msg_Err( p_sd, "node:add_subitem: the \"path\" parameter can't be empty" );
350         }
351         else
352             msg_Err( p_sd, "Error parsing add_subitem arguments" );
353     }
354     return 1;
355 }
356
357 static int vlclua_node_add_subnode( lua_State *L )
358 {
359     services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
360     input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" );
361     if( *pp_node )
362     {
363         if( lua_istable( L, -1 ) )
364         {
365             lua_getfield( L, -1, "title" );
366             if( lua_isstring( L, -1 ) )
367             {
368                 const char *psz_name = lua_tostring( L, -1 );
369                 input_item_node_t *p_input_node = input_item_node_Create( *pp_node );
370                 input_item_t *p_input = input_item_NewWithType( "vlc://nop",
371                                                                 psz_name, 0, NULL, 0,
372                                                                 -1, ITEM_TYPE_NODE );
373                 lua_pop( L, 1 );
374
375                 if( p_input )
376                 {
377                     lua_getfield( L, -1, "arturl" );
378                     if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) )
379                     {
380                         char *psz_value = strdup( lua_tostring( L, -1 ) );
381                         EnsureUTF8( psz_value );
382                         msg_Dbg( p_sd, "ArtURL: %s", psz_value );
383                         input_item_SetArtURL( p_input, psz_value );
384                         free( psz_value );
385                     }
386                     input_item_node_AppendItem( p_input_node, p_input );
387                     input_item_node_PostAndDelete( p_input_node );
388                     input_item_t **udata = (input_item_t **)
389                                            lua_newuserdata( L, sizeof( input_item_t * ) );
390                     *udata = p_input;
391                     if( luaL_newmetatable( L, "node" ) )
392                     {
393                         lua_newtable( L );
394                         luaL_register( L, NULL, vlclua_node_reg );
395                         lua_setfield( L, -2, "__index" );
396                     }
397                     lua_setmetatable( L, -2 );
398                 }
399             }
400             else
401                 msg_Err( p_sd, "node:add_node: the \"title\" parameter can't be empty" );
402         }
403         else
404             msg_Err( p_sd, "Error parsing add_node arguments" );
405     }
406     return 1;
407 }
408
409 /*****************************************************************************
410  *
411  *****************************************************************************/
412 static const luaL_Reg vlclua_sd_reg[] = {
413     { "get_services_names", vlclua_sd_get_services_names },
414     { "add", vlclua_sd_add },
415     { "remove", vlclua_sd_remove },
416     { "is_loaded", vlclua_sd_is_loaded },
417     { "add_node", vlclua_sd_add_node },
418     { "add_item", vlclua_sd_add_item },
419     { "remove_item", vlclua_sd_remove_item },
420     { NULL, NULL }
421 };
422
423 void luaopen_sd( lua_State *L )
424 {
425     lua_newtable( L );
426     luaL_register( L, NULL, vlclua_sd_reg );
427     lua_setfield( L, -2, "sd" );
428 }