]> git.sesse.net Git - vlc/blob - modules/misc/lua/vlc.c
Use config_GetDataDir() instead of DATA_PATH.
[vlc] / modules / misc / lua / vlc.c
1 /*****************************************************************************
2  * vlc.c: Generic lua inteface functions
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *          Pierre d'Herbemont <pdherbemont # videolan.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/vlc.h>
37 #include <vlc_meta.h>
38 #include <vlc_charset.h>
39 #include <vlc_aout.h>
40
41 #include <lua.h>        /* Low level lua C API */
42 #include <lauxlib.h>    /* Higher level C API */
43 #include <lualib.h>     /* Lua libs */
44
45 #include "vlc.h"
46
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50
51 #define INTF_TEXT N_("Lua interface")
52 #define INTF_LONGTEXT N_("Lua interface module to load")
53
54 #define CONFIG_TEXT N_("Lua inteface configuration")
55 #define CONFIG_LONGTEXT N_("Lua interface configuration string. Format is: '[\"<interface module name>\"] = { <option> = <value>, ...}, ...'.")
56
57 vlc_module_begin();
58     add_submodule();
59         add_shortcut( "luameta" );
60         set_shortname( N_( "Lua Meta" ) );
61         set_description( _("Fetch metadata using lua scripts") );
62         set_capability( "meta fetcher", 10 );
63         set_callbacks( E_(FindMeta), NULL );
64     add_submodule();
65         set_shortname( N_( "Lua Art" ) );
66         set_description( _("Fetch artwork using lua scripts") );
67         set_capability( "art finder", 10 );
68         set_callbacks( E_(FindArt), NULL );
69     add_submodule();
70         add_shortcut( "luaplaylist" );
71         set_category( CAT_INPUT );
72         set_subcategory( SUBCAT_INPUT_DEMUX );
73         set_shortname( _("Lua Playlist") );
74         set_description( _("Lua Playlist Parser Interface") );
75         set_capability( "demux2", 2 );
76         set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) );
77     add_submodule();
78         add_shortcut( "luaintf" );
79         add_shortcut( "luarc" );
80         /* add_shortcut( "rc" ); */
81         add_shortcut( "luahotkeys" );
82         /* add_shortcut( "hotkeys" ); */
83         add_shortcut( "luatelnet" );
84         /* add_shortcut( "telnet" ); */
85         add_shortcut( "luahttp" );
86         /* add_shortcut( "http" ); */
87         set_description( _("Lua Interface Module") );
88         set_capability( "interface", 0 );
89         add_string( "lua-intf", "dummy", NULL,
90                     INTF_TEXT, INTF_LONGTEXT, VLC_FALSE );
91         add_string( "lua-config", "", NULL,
92                     CONFIG_TEXT, CONFIG_LONGTEXT, VLC_FALSE );
93         set_callbacks( E_(Open_LuaIntf), E_(Close_LuaIntf) );
94 vlc_module_end();
95
96 /*****************************************************************************
97  * Internal lua<->vlc utils
98  *****************************************************************************/
99 vlc_object_t * vlclua_get_this( lua_State *L )
100 {
101     vlc_object_t * p_this;
102     lua_getglobal( L, "vlc" );
103     lua_getfield( L, -1, "private" );
104     p_this = (vlc_object_t*)lua_topointer( L, lua_gettop( L ) );
105     lua_pop( L, 2 );
106     return p_this;
107 }
108
109 /*****************************************************************************
110  * VLC error code translation
111  *****************************************************************************/
112 int vlclua_push_ret( lua_State *L, int i_error )
113 {
114     lua_pushnumber( L, i_error );
115     lua_pushstring( L, vlc_error( i_error ) );
116     return 2;
117 }
118
119 /*****************************************************************************
120  * Get the VLC version string
121  *****************************************************************************/
122 int vlclua_version( lua_State *L )
123 {
124     lua_pushstring( L, VLC_Version() );
125     return 1;
126 }
127
128 /*****************************************************************************
129  * Get the VLC copyright
130  *****************************************************************************/
131 int vlclua_copyright( lua_State *L )
132 {
133     lua_pushstring( L, COPYRIGHT_MESSAGE );
134     return 1;
135 }
136
137 /*****************************************************************************
138  * Get the VLC license msg/disclaimer
139  *****************************************************************************/
140 int vlclua_license( lua_State *L )
141 {
142     lua_pushstring( L, LICENSE_MSG );
143     return 1;
144 }
145
146 /*****************************************************************************
147  * Quit VLC
148  *****************************************************************************/
149 int vlclua_quit( lua_State *L )
150 {
151     vlc_object_t *p_this = vlclua_get_this( L );
152     /* The rc.c code also stops the playlist ... not sure if this is needed
153      * though. */
154     vlc_object_kill( p_this->p_libvlc );
155     return 0;
156 }
157
158 /*****************************************************************************
159  * Global properties getters
160  *****************************************************************************/
161 int vlclua_datadir( lua_State *L )
162 {
163     lua_pushstring( L, config_GetDataDir() );
164     return 1;
165 }
166 int vlclua_homedir( lua_State *L )
167 {
168     lua_pushstring( L, vlclua_get_this( L )->p_libvlc->psz_homedir );
169     return 1;
170 }
171 int vlclua_configdir( lua_State *L )
172 {
173     lua_pushstring( L, vlclua_get_this( L )->p_libvlc->psz_configdir );
174     return 1;
175 }
176 int vlclua_cachedir( lua_State *L )
177 {
178     lua_pushstring( L, vlclua_get_this( L )->p_libvlc->psz_cachedir );
179     return 1;
180 }
181 int vlclua_datadir_list( lua_State *L )
182 {
183     const char *psz_dirname = luaL_checkstring( L, 1 );
184     vlc_object_t *p_this = vlclua_get_this( L );
185     char  *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
186     char **ppsz_dir = ppsz_dir_list;
187     int i = 1;
188
189     if( vlclua_dir_list( p_this, psz_dirname, ppsz_dir_list ) != VLC_SUCCESS )
190         return 0;
191     lua_newtable( L );
192     for( ; *ppsz_dir; ppsz_dir++ )
193     {
194         lua_pushstring( L, *ppsz_dir );
195         lua_rawseti( L, -2, i );
196         i ++;
197     }
198     return 1;
199 }
200
201 /*****************************************************************************
202  * Volume related
203  *****************************************************************************/
204 int vlclua_volume_set( lua_State *L )
205 {
206     vlc_object_t *p_this = vlclua_get_this( L );
207     int i_volume = luaL_checkint( L, 1 );
208     /* Do we need to check that i_volume is in the AOUT_VOLUME_MIN->MAX range?*/
209     return vlclua_push_ret( L, aout_VolumeSet( p_this, i_volume ) );
210 }
211
212 int vlclua_volume_get( lua_State *L )
213 {
214     vlc_object_t *p_this = vlclua_get_this( L );
215     audio_volume_t i_volume;
216     if( aout_VolumeGet( p_this, &i_volume ) == VLC_SUCCESS )
217         lua_pushnumber( L, i_volume );
218     else
219         lua_pushnil( L );
220     return 1;
221 }
222
223 int vlclua_volume_up( lua_State *L )
224 {
225     audio_volume_t i_volume;
226     aout_VolumeUp( vlclua_get_this( L ),
227                    luaL_optint( L, 1, 1 ),
228                    &i_volume );
229     lua_pushnumber( L, i_volume );
230     return 1;
231 }
232
233 int vlclua_volume_down( lua_State *L )
234 {
235     audio_volume_t i_volume;
236     aout_VolumeDown( vlclua_get_this( L ),
237                      luaL_optint( L, 1, 1 ),
238                      &i_volume );
239     lua_pushnumber( L, i_volume );
240     return 1;
241 }
242
243 /*****************************************************************************
244  * Stream handling
245  *****************************************************************************/
246 int vlclua_stream_new( lua_State *L )
247 {
248     vlc_object_t * p_this = vlclua_get_this( L );
249     stream_t * p_stream;
250     const char * psz_url;
251     psz_url = luaL_checkstring( L, -1 );
252     p_stream = stream_UrlNew( p_this, psz_url );
253     if( !p_stream )
254         return luaL_error( L, "Error when opening url: `%s'", psz_url );
255     lua_pushlightuserdata( L, p_stream );
256     return 1;
257 }
258
259 int vlclua_stream_read( lua_State *L )
260 {
261     stream_t * p_stream;
262     int n;
263     byte_t *p_read;
264     int i_read;
265     p_stream = (stream_t *)luaL_checklightuserdata( L, 1 );
266     n = luaL_checkint( L, 2 );
267     p_read = malloc( n );
268     if( !p_read ) return vlclua_error( L );
269     i_read = stream_Read( p_stream, p_read, n );
270     lua_pushlstring( L, (const char *)p_read, i_read );
271     free( p_read );
272     return 1;
273 }
274
275 int vlclua_stream_readline( lua_State *L )
276 {
277     stream_t * p_stream;
278     p_stream = (stream_t *)luaL_checklightuserdata( L, 1 );
279     char *psz_line = stream_ReadLine( p_stream );
280     if( psz_line )
281     {
282         lua_pushstring( L, psz_line );
283         free( psz_line );
284     }
285     else
286         lua_pushnil( L );
287     return 1;
288 }
289
290 int vlclua_stream_delete( lua_State *L )
291 {
292     stream_t * p_stream;
293     p_stream = (stream_t *)luaL_checklightuserdata( L, 1 );
294     stream_Delete( p_stream );
295     return 0;
296 }
297
298 /*****************************************************************************
299  * String transformations
300  *****************************************************************************/
301 int vlclua_decode_uri( lua_State *L )
302 {
303     int i_top = lua_gettop( L );
304     int i;
305     for( i = 1; i <= i_top; i++ )
306     {
307         const char *psz_cstring = luaL_checkstring( L, 1 );
308         char *psz_string = strdup( psz_cstring );
309         lua_remove( L, 1 ); /* remove elements to prevent being limited by
310                              * the stack's size (this function will work with
311                              * up to (stack size - 1) arguments */
312         decode_URI( psz_string );
313         lua_pushstring( L, psz_string );
314         free( psz_string );
315     }
316     return i_top;
317 }
318
319 int vlclua_resolve_xml_special_chars( lua_State *L )
320 {
321     int i_top = lua_gettop( L );
322     int i;
323     for( i = 1; i <= i_top; i++ )
324     {
325         const char *psz_cstring = luaL_checkstring( L, 1 );
326         char *psz_string = strdup( psz_cstring );
327         lua_remove( L, 1 ); /* remove elements to prevent being limited by
328                              * the stack's size (this function will work with
329                              * up to (stack size - 1) arguments */
330         resolve_xml_special_chars( psz_string );
331         lua_pushstring( L, psz_string );
332         free( psz_string );
333     }
334     return i_top;
335 }
336
337 int vlclua_convert_xml_special_chars( lua_State *L )
338 {
339     int i_top = lua_gettop( L );
340     int i;
341     for( i = 1; i <= i_top; i++ )
342     {
343         char *psz_string = convert_xml_special_chars( luaL_checkstring(L,1) );
344         lua_remove( L, 1 );
345         lua_pushstring( L, psz_string );
346         free( psz_string );
347     }
348     return i_top;
349 }
350
351 /*****************************************************************************
352  * Messaging facilities
353  *****************************************************************************/
354 int vlclua_msg_dbg( lua_State *L )
355 {
356     int i_top = lua_gettop( L );
357     vlc_object_t *p_this = vlclua_get_this( L );
358     int i;
359     for( i = 1; i <= i_top; i++ )
360         msg_Dbg( p_this, "%s", luaL_checkstring( L, 1 ) );
361     return 0;
362 }
363 int vlclua_msg_warn( lua_State *L )
364 {
365     int i_top = lua_gettop( L );
366     vlc_object_t *p_this = vlclua_get_this( L );
367     int i;
368     for( i = 1; i <= i_top; i++ )
369         msg_Warn( p_this, "%s", luaL_checkstring( L, i ) );
370     return 0;
371 }
372 int vlclua_msg_err( lua_State *L )
373 {
374     int i_top = lua_gettop( L );
375     vlc_object_t *p_this = vlclua_get_this( L );
376     int i;
377     for( i = 1; i <= i_top; i++ )
378         msg_Err( p_this, "%s", luaL_checkstring( L, i ) );
379     return 0;
380 }
381 int vlclua_msg_info( lua_State *L )
382 {
383     int i_top = lua_gettop( L );
384     vlc_object_t *p_this = vlclua_get_this( L );
385     int i;
386     for( i = 1; i <= i_top; i++ )
387         msg_Info( p_this, "%s", luaL_checkstring( L, i ) );
388     return 0;
389 }
390
391 /*****************************************************************************
392  *
393  *****************************************************************************/
394 static int file_select( const char *file )
395 {
396     int i = strlen( file );
397     return i > 4 && !strcmp( file+i-4, ".lua" );
398 }
399
400 static int file_compare( const char **a, const char **b )
401 {
402     return strcmp( *a, *b );
403 }
404
405 int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname,
406                      char **ppsz_dir_list )
407 {
408     if( asprintf( &ppsz_dir_list[0], "%s" DIR_SEP "%s",
409                    p_this->p_libvlc->psz_datadir, luadirname ) < 0 )
410         return VLC_ENOMEM;
411
412 #   if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
413     {
414         const char *psz_vlcpath = config_GetDataDir();
415         if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s",
416                       psz_vlcpath, luadirname )  < 0 )
417             return VLC_ENOMEM;
418
419         if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s",
420                       psz_vlcpath, luadirname )  < 0 )
421             return VLC_ENOMEM;
422     }
423 #   else
424     if( asprintf( &ppsz_dir_list[1],
425                   "share" DIR_SEP "%s", luadirname ) < 0 )
426         return VLC_ENOMEM;
427
428 #   ifdef HAVE_SYS_STAT_H
429     {
430         struct stat stat_info;
431         if( ( utf8_stat( ppsz_dir_list[1], &stat_info ) == -1 )
432             || !S_ISDIR( stat_info.st_mode ) )
433         {
434             free(ppsz_dir_list[1]);
435             if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s",
436                           config_GetDataDir (), luadirname ) < 0 )
437                 return VLC_ENOMEM;
438         }
439     }
440 #   endif
441 #   endif
442     return VLC_SUCCESS;
443 }
444
445 /*****************************************************************************
446  * Will execute func on all scripts in luadirname, and stop if func returns
447  * success.
448  *****************************************************************************/
449 int vlclua_scripts_batch_execute( vlc_object_t *p_this,
450                                   const char * luadirname,
451                                   int (*func)(vlc_object_t *, const char *, lua_State *, void *),
452                                   lua_State * L,
453                                   void * user_data)
454 {
455     int i_ret = VLC_EGENERIC;
456
457     DIR   *dir           = NULL;
458     char **ppsz_filelist = NULL;
459     char **ppsz_fileend  = NULL;
460     char **ppsz_file;
461
462     char  *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
463     char **ppsz_dir;
464
465     i_ret = vlclua_dir_list( p_this, luadirname, ppsz_dir_list );
466     if( i_ret != VLC_SUCCESS )
467         return i_ret;
468     i_ret = VLC_EGENERIC;
469
470
471     for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
472     {
473         int i_files;
474
475         if( ppsz_filelist )
476         {
477             for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
478                  ppsz_file++ )
479                 free( *ppsz_file );
480             free( ppsz_filelist );
481             ppsz_filelist = NULL;
482         }
483
484         if( dir )
485         {
486             closedir( dir );
487         }
488
489         msg_Dbg( p_this, "Trying Lua scripts in %s", *ppsz_dir );
490         dir = utf8_opendir( *ppsz_dir );
491
492         if( !dir ) continue;
493         i_files = utf8_loaddir( dir, &ppsz_filelist, file_select,
494                                 file_compare );
495         if( i_files < 1 ) continue;
496         ppsz_fileend = ppsz_filelist + i_files;
497
498         for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
499         {
500             char  *psz_filename;
501             if( asprintf( &psz_filename,
502                           "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) < 0)
503                 return VLC_ENOMEM;
504             msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
505
506             i_ret = func( p_this, psz_filename, L, user_data );
507
508             free( psz_filename );
509
510             if( i_ret == VLC_SUCCESS ) break;
511         }
512         if( i_ret == VLC_SUCCESS ) break;
513     }
514
515     if( ppsz_filelist )
516     {
517         for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
518              ppsz_file++ )
519             free( *ppsz_file );
520         free( ppsz_filelist );
521     }
522     for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
523         free( *ppsz_dir );
524
525     if( dir ) closedir( dir );
526
527     return i_ret;
528 }
529
530
531 /*****************************************************************************
532  * Meta data setters utility.
533  * Playlist item table should be on top of the stack when these are called
534  *****************************************************************************/
535 void __vlclua_read_meta_data( vlc_object_t *p_this, lua_State *L,
536                               input_item_t *p_input )
537 {
538 #define TRY_META( a, b )                                        \
539     lua_getfield( L, -1, a );                                   \
540     if( lua_isstring( L, -1 ) )                                 \
541     {                                                           \
542         char *psz_value = strdup( lua_tostring( L, -1 ) );      \
543         EnsureUTF8( psz_value );                                \
544         msg_Dbg( p_this, #b ": %s", psz_value );                \
545         input_item_Set ## b ( p_input, psz_value );             \
546         free( psz_value );                                      \
547     }                                                           \
548     lua_pop( L, 1 ); /* pop a */
549     TRY_META( "title", Title );
550     TRY_META( "artist", Artist );
551     TRY_META( "genre", Genre );
552     TRY_META( "copyright", Copyright );
553     TRY_META( "album", Album );
554     TRY_META( "tracknum", TrackNum );
555     TRY_META( "description", Description );
556     TRY_META( "rating", Rating );
557     TRY_META( "date", Date );
558     TRY_META( "setting", Setting );
559     TRY_META( "url", URL );
560     TRY_META( "language", Language );
561     TRY_META( "nowplaying", NowPlaying );
562     TRY_META( "publisher", Publisher );
563     TRY_META( "encodedby", EncodedBy );
564     TRY_META( "arturl", ArtURL );
565     TRY_META( "trackid", TrackID );
566 }
567
568 void __vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *L,
569                                      input_item_t *p_input )
570 {
571     /* ... item */
572     lua_getfield( L, -1, "meta" );
573     /* ... item meta */
574     if( lua_istable( L, -1 ) )
575     {
576         lua_pushnil( L );
577         /* ... item meta nil */
578         while( lua_next( L, -2 ) )
579         {
580             /* ... item meta key value */
581             if( !lua_isstring( L, -2 ) )
582             {
583                 msg_Warn( p_this, "Custom meta data category name must be "
584                                    "a string" );
585             }
586             else if( !lua_istable( L, -1 ) )
587             {
588                 msg_Warn( p_this, "Custom meta data category contents "
589                                    "must be a table" );
590             }
591             else
592             {
593                 const char *psz_meta_category = lua_tostring( L, -2 );
594                 msg_Dbg( p_this, "Found custom meta data category: %s",
595                          psz_meta_category );
596                 lua_pushnil( L );
597                 /* ... item meta key value nil */
598                 while( lua_next( L, -2 ) )
599                 {
600                     /* ... item meta key value key2 value2 */
601                     if( !lua_isstring( L, -2 ) )
602                     {
603                         msg_Warn( p_this, "Custom meta category item name "
604                                            "must be a string." );
605                     }
606                     else if( !lua_isstring( L, -1 ) )
607                     {
608                         msg_Warn( p_this, "Custom meta category item value "
609                                            "must be a string." );
610                     }
611                     else
612                     {
613                         const char *psz_meta_name =
614                             lua_tostring( L, -2 );
615                         const char *psz_meta_value =
616                             lua_tostring( L, -1 );
617                         msg_Dbg( p_this, "Custom meta %s, %s: %s",
618                                  psz_meta_category, psz_meta_name,
619                                  psz_meta_value );
620                         input_ItemAddInfo( p_input, psz_meta_category,
621                                            psz_meta_name, psz_meta_value );
622                     }
623                     lua_pop( L, 1 ); /* pop item */
624                     /* ... item meta key value key2 */
625                 }
626                 /* ... item meta key value */
627             }
628             lua_pop( L, 1 ); /* pop category */
629             /* ... item meta key */
630         }
631         /* ... item meta */
632     }
633     lua_pop( L, 1 ); /* pop "meta" */
634     /* ... item -> back to original stack */
635 }
636
637 /*****************************************************************************
638  * Playlist utilities
639  ****************************************************************************/
640 /**
641  * Playlist item table should be on top of the stack when this is called
642  */
643 void __vlclua_read_options( vlc_object_t *p_this, lua_State *L,
644                             int *pi_options, char ***pppsz_options )
645 {
646     lua_getfield( L, -1, "options" );
647     if( lua_istable( L, -1 ) )
648     {
649         lua_pushnil( L );
650         while( lua_next( L, -2 ) )
651         {
652             if( lua_isstring( L, -1 ) )
653             {
654                 char *psz_option = strdup( lua_tostring( L, -1 ) );
655                 msg_Dbg( p_this, "Option: %s", psz_option );
656                 INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
657                              psz_option );
658             }
659             else
660             {
661                 msg_Warn( p_this, "Option should be a string" );
662             }
663             lua_pop( L, 1 ); /* pop option */
664         }
665     }
666     lua_pop( L, 1 ); /* pop "options" */
667 }
668
669 int __vlclua_playlist_add_internal( vlc_object_t *p_this, lua_State *L,
670                                     playlist_t *p_playlist,
671                                     input_item_t *p_parent, vlc_bool_t b_play )
672 {
673     int i_count = 0;
674
675     /* playlist */
676     if( lua_istable( L, -1 ) )
677     {
678         lua_pushnil( L );
679         /* playlist nil */
680         while( lua_next( L, -2 ) )
681         {
682             /* playlist key item */
683             /* <Parse playlist item> */
684             if( lua_istable( L, -1 ) )
685             {
686                 lua_getfield( L, -1, "path" );
687                 /* playlist key item path */
688                 if( lua_isstring( L, -1 ) )
689                 {
690                     const char   *psz_path     = NULL;
691                     const char   *psz_name     = NULL;
692                     char        **ppsz_options = NULL;
693                     int           i_options    = 0;
694                     mtime_t       i_duration   = -1;
695                     input_item_t *p_input;
696
697                     /* Read path and name */
698                     psz_path = lua_tostring( L, -1 );
699                     msg_Dbg( p_this, "Path: %s", psz_path );
700                     lua_getfield( L, -2, "name" );
701                     /* playlist key item path name */
702                     if( lua_isstring( L, -1 ) )
703                     {
704                         psz_name = lua_tostring( L, -1 );
705                         msg_Dbg( p_this, "Name: %s", psz_name );
706                     }
707                     else
708                     {
709                         if( !lua_isnil( L, -1 ) )
710                             msg_Warn( p_this, "Playlist item name should be a string." );
711                         psz_name = psz_path;
712                     }
713
714                     /* Read duration */
715                     lua_getfield( L, -3, "duration" );
716                     /* playlist key item path name duration */
717                     if( lua_isnumber( L, -1 ) )
718                     {
719                         i_duration = (mtime_t)(lua_tonumber( L, -1 )*1e6);
720                     }
721                     else if( !lua_isnil( L, -1 ) )
722                     {
723                         msg_Warn( p_this, "Playlist item duration should be a number (in seconds)." );
724                     }
725                     lua_pop( L, 1 ); /* pop "duration" */
726
727                     /* playlist key item path name */
728
729                     /* Read options: item must be on top of stack */
730                     lua_pushvalue( L, -3 );
731                     /* playlist key item path name item */
732                     vlclua_read_options( p_this, L, &i_options, &ppsz_options );
733
734                     /* Create input item */
735                     p_input = input_ItemNewExt( p_playlist, psz_path,
736                                                 psz_name, i_options,
737                                                 (const char **)ppsz_options,
738                                                 i_duration );
739                     lua_pop( L, 3 ); /* pop "path name item" */
740                     /* playlist key item */
741
742                     /* Read meta data: item must be on top of stack */
743                     vlclua_read_meta_data( p_this, L, p_input );
744
745                     /* Read custom meta data: item must be on top of stack*/
746                     vlclua_read_custom_meta_data( p_this, L, p_input );
747
748                     /* Append item to playlist */
749                     if( p_parent ) /* Add to node */
750                         input_ItemAddSubItem( p_parent, p_input );
751                     else if( b_play ) /* Play */
752                         playlist_AddInput( p_playlist, p_input,
753                                            PLAYLIST_APPEND | PLAYLIST_GO,
754                                            PLAYLIST_END, VLC_TRUE, VLC_FALSE );
755                     else /* Enqueue */
756                         playlist_AddInput( p_playlist, p_input,
757                                            PLAYLIST_APPEND | PLAYLIST_PREPARSE,
758                                            PLAYLIST_END, VLC_TRUE, VLC_FALSE );
759                     i_count ++; /* increment counter */
760                     vlc_gc_decref( p_input );
761                     while( i_options > 0 )
762                         free( ppsz_options[--i_options] );
763                     free( ppsz_options );
764                 }
765                 else
766                 {
767                     lua_pop( L, 1 ); /* pop "path" */
768                     msg_Warn( p_this,
769                              "Playlist item's path should be a string" );
770                 }
771                 /* playlist key item */
772             }
773             else
774             {
775                 msg_Warn( p_this, "Playlist item should be a table" );
776             }
777             /* <Parse playlist item> */
778             lua_pop( L, 1 ); /* pop the value, keep the key for
779                               * the next lua_next() call */
780             /* playlist key */
781         }
782         /* playlist */
783     }
784     else
785     {
786         msg_Warn( p_this, "Playlist should be a table." );
787     }
788     return i_count;
789 }