1 /*****************************************************************************
2 * vlc.c: Generic lua interface functions
3 *****************************************************************************
4 * Copyright (C) 2007 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea at videolan tod org>
8 * Pierre d'Herbemont <pdherbemont # videolan.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
41 #include <vlc_charset.h>
44 #include <lua.h> /* Low level lua C API */
45 #include <lauxlib.h> /* Higher level C API */
46 #include <lualib.h> /* Lua libs */
50 /*****************************************************************************
52 *****************************************************************************/
54 #define INTF_TEXT N_("Lua interface")
55 #define INTF_LONGTEXT N_("Lua interface module to load")
57 #define CONFIG_TEXT N_("Lua interface configuration")
58 #define CONFIG_LONGTEXT N_("Lua interface configuration string. Format is: '[\"<interface module name>\"] = { <option> = <value>, ...}, ...'.")
62 set_shortname( N_( "Lua Art" ) );
63 set_description( N_("Fetch artwork using lua scripts") );
64 set_capability( "art finder", 10 );
65 set_callbacks( FindArt, NULL );
67 add_shortcut( "luaplaylist" );
68 set_category( CAT_INPUT );
69 set_subcategory( SUBCAT_INPUT_DEMUX );
70 set_shortname( N_("Lua Playlist") );
71 set_description( N_("Lua Playlist Parser Interface") );
72 set_capability( "demux", 2 );
73 set_callbacks( Import_LuaPlaylist, Close_LuaPlaylist );
75 add_shortcut( "luaintf" );
76 add_shortcut( "luarc" );
77 /* add_shortcut( "rc" ); */
78 add_shortcut( "luahotkeys" );
79 /* add_shortcut( "hotkeys" ); */
80 add_shortcut( "luatelnet" );
81 /* add_shortcut( "telnet" ); */
82 add_shortcut( "luahttp" );
83 /* add_shortcut( "http" ); */
84 set_description( N_("Lua Interface Module") );
85 set_capability( "interface", 0 );
86 add_string( "lua-intf", "dummy", NULL,
87 INTF_TEXT, INTF_LONGTEXT, false );
88 add_string( "lua-config", "", NULL,
89 CONFIG_TEXT, CONFIG_LONGTEXT, false );
90 set_callbacks( Open_LuaIntf, Close_LuaIntf );
93 /*****************************************************************************
94 * Internal lua<->vlc utils
95 *****************************************************************************/
96 vlc_object_t * vlclua_get_this( lua_State *L )
98 vlc_object_t * p_this;
99 lua_getglobal( L, "vlc" );
100 lua_getfield( L, -1, "private" );
101 p_this = (vlc_object_t*)lua_topointer( L, lua_gettop( L ) );
106 /*****************************************************************************
107 * VLC error code translation
108 *****************************************************************************/
109 int vlclua_push_ret( lua_State *L, int i_error )
111 lua_pushnumber( L, i_error );
112 lua_pushstring( L, vlc_error( i_error ) );
116 /*****************************************************************************
117 * Get the VLC version string
118 *****************************************************************************/
119 int vlclua_version( lua_State *L )
121 lua_pushstring( L, VLC_Version() );
125 /*****************************************************************************
126 * Get the VLC copyright
127 *****************************************************************************/
128 int vlclua_copyright( lua_State *L )
130 lua_pushstring( L, COPYRIGHT_MESSAGE );
134 /*****************************************************************************
135 * Get the VLC license msg/disclaimer
136 *****************************************************************************/
137 int vlclua_license( lua_State *L )
139 lua_pushstring( L, LICENSE_MSG );
143 /*****************************************************************************
145 *****************************************************************************/
146 int vlclua_quit( lua_State *L )
148 vlc_object_t *p_this = vlclua_get_this( L );
149 /* The rc.c code also stops the playlist ... not sure if this is needed
151 vlc_object_kill( p_this->p_libvlc );
155 /*****************************************************************************
156 * Global properties getters
157 *****************************************************************************/
158 int vlclua_datadir( lua_State *L )
160 lua_pushstring( L, config_GetDataDir() );
163 int vlclua_homedir( lua_State *L )
165 lua_pushstring( L, config_GetHomeDir() );
168 int vlclua_configdir( lua_State *L )
170 char *dir = config_GetUserConfDir();
171 lua_pushstring( L, dir );
175 int vlclua_cachedir( lua_State *L )
177 char *dir = config_GetCacheDir();
178 lua_pushstring( L, dir );
182 int vlclua_datadir_list( lua_State *L )
184 const char *psz_dirname = luaL_checkstring( L, 1 );
185 vlc_object_t *p_this = vlclua_get_this( L );
186 char *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
187 char **ppsz_dir = ppsz_dir_list;
190 if( vlclua_dir_list( p_this, psz_dirname, ppsz_dir_list ) != VLC_SUCCESS )
193 for( ; *ppsz_dir; ppsz_dir++ )
195 lua_pushstring( L, *ppsz_dir );
196 lua_rawseti( L, -2, i );
202 /*****************************************************************************
204 *****************************************************************************/
205 int vlclua_volume_set( lua_State *L )
207 vlc_object_t *p_this = vlclua_get_this( L );
208 int i_volume = luaL_checkint( L, 1 );
209 /* Do we need to check that i_volume is in the AOUT_VOLUME_MIN->MAX range?*/
210 return vlclua_push_ret( L, aout_VolumeSet( p_this, i_volume ) );
213 int vlclua_volume_get( lua_State *L )
215 vlc_object_t *p_this = vlclua_get_this( L );
216 audio_volume_t i_volume;
217 if( aout_VolumeGet( p_this, &i_volume ) == VLC_SUCCESS )
218 lua_pushnumber( L, i_volume );
224 int vlclua_volume_up( lua_State *L )
226 audio_volume_t i_volume;
227 aout_VolumeUp( vlclua_get_this( L ),
228 luaL_optint( L, 1, 1 ),
230 lua_pushnumber( L, i_volume );
234 int vlclua_volume_down( lua_State *L )
236 audio_volume_t i_volume;
237 aout_VolumeDown( vlclua_get_this( L ),
238 luaL_optint( L, 1, 1 ),
240 lua_pushnumber( L, i_volume );
244 /*****************************************************************************
246 *****************************************************************************/
247 int vlclua_stream_new( lua_State *L )
249 vlc_object_t * p_this = vlclua_get_this( L );
251 const char * psz_url;
252 psz_url = luaL_checkstring( L, -1 );
253 p_stream = stream_UrlNew( p_this, psz_url );
255 return luaL_error( L, "Error when opening url: `%s'", psz_url );
256 lua_pushlightuserdata( L, p_stream );
260 int vlclua_stream_read( lua_State *L )
266 p_stream = (stream_t *)luaL_checklightuserdata( L, 1 );
267 n = luaL_checkint( L, 2 );
268 p_read = malloc( n );
269 if( !p_read ) return vlclua_error( L );
270 i_read = stream_Read( p_stream, p_read, n );
271 lua_pushlstring( L, (const char *)p_read, i_read );
276 int vlclua_stream_readline( lua_State *L )
279 p_stream = (stream_t *)luaL_checklightuserdata( L, 1 );
280 char *psz_line = stream_ReadLine( p_stream );
283 lua_pushstring( L, psz_line );
291 int vlclua_stream_delete( lua_State *L )
294 p_stream = (stream_t *)luaL_checklightuserdata( L, 1 );
295 stream_Delete( p_stream );
299 /*****************************************************************************
300 * String transformations
301 *****************************************************************************/
302 int vlclua_decode_uri( lua_State *L )
304 int i_top = lua_gettop( L );
306 for( i = 1; i <= i_top; i++ )
308 const char *psz_cstring = luaL_checkstring( L, 1 );
309 char *psz_string = strdup( psz_cstring );
310 lua_remove( L, 1 ); /* remove elements to prevent being limited by
311 * the stack's size (this function will work with
312 * up to (stack size - 1) arguments */
313 decode_URI( psz_string );
314 lua_pushstring( L, psz_string );
320 int vlclua_resolve_xml_special_chars( lua_State *L )
322 int i_top = lua_gettop( L );
324 for( i = 1; i <= i_top; i++ )
326 const char *psz_cstring = luaL_checkstring( L, 1 );
327 char *psz_string = strdup( psz_cstring );
328 lua_remove( L, 1 ); /* remove elements to prevent being limited by
329 * the stack's size (this function will work with
330 * up to (stack size - 1) arguments */
331 resolve_xml_special_chars( psz_string );
332 lua_pushstring( L, psz_string );
338 int vlclua_convert_xml_special_chars( lua_State *L )
340 int i_top = lua_gettop( L );
342 for( i = 1; i <= i_top; i++ )
344 char *psz_string = convert_xml_special_chars( luaL_checkstring(L,1) );
346 lua_pushstring( L, psz_string );
352 /*****************************************************************************
353 * Messaging facilities
354 *****************************************************************************/
355 int vlclua_msg_dbg( lua_State *L )
357 int i_top = lua_gettop( L );
358 vlc_object_t *p_this = vlclua_get_this( L );
360 for( i = 1; i <= i_top; i++ )
361 msg_Dbg( p_this, "%s", luaL_checkstring( L, 1 ) );
364 int vlclua_msg_warn( lua_State *L )
366 int i_top = lua_gettop( L );
367 vlc_object_t *p_this = vlclua_get_this( L );
369 for( i = 1; i <= i_top; i++ )
370 msg_Warn( p_this, "%s", luaL_checkstring( L, i ) );
373 int vlclua_msg_err( lua_State *L )
375 int i_top = lua_gettop( L );
376 vlc_object_t *p_this = vlclua_get_this( L );
378 for( i = 1; i <= i_top; i++ )
379 msg_Err( p_this, "%s", luaL_checkstring( L, i ) );
382 int vlclua_msg_info( lua_State *L )
384 int i_top = lua_gettop( L );
385 vlc_object_t *p_this = vlclua_get_this( L );
387 for( i = 1; i <= i_top; i++ )
388 msg_Info( p_this, "%s", luaL_checkstring( L, i ) );
392 /*****************************************************************************
394 *****************************************************************************/
395 static int file_select( const char *file )
397 int i = strlen( file );
398 return i > 4 && !strcmp( file+i-4, ".lua" );
401 static int file_compare( const char **a, const char **b )
403 return strcmp( *a, *b );
406 int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname,
407 char **ppsz_dir_list )
410 char *datadir = config_GetUserDataDir();
411 if( datadir == NULL )
414 if( asprintf( &ppsz_dir_list[i], "%s" DIR_SEP "lua" DIR_SEP "%s",
415 datadir, luadirname ) < 0 )
423 # if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
425 const char *psz_vlcpath = config_GetDataDir();
426 if( asprintf( &ppsz_dir_list[i], "%s" DIR_SEP "lua" DIR_SEP "%s",
427 psz_vlcpath, luadirname ) < 0 )
430 if( asprintf( &ppsz_dir_list[i], "%s" DIR_SEP "share" DIR_SEP "lua" DIR_SEP "%s",
431 psz_vlcpath, luadirname ) < 0 )
437 if( asprintf( &ppsz_dir_list[i],
438 "share" DIR_SEP "lua" DIR_SEP "%s", luadirname ) < 0 )
441 # ifdef HAVE_SYS_STAT_H
443 struct stat stat_info;
444 if( ( utf8_stat( ppsz_dir_list[i], &stat_info ) == -1 )
445 || !S_ISDIR( stat_info.st_mode ) )
447 free(ppsz_dir_list[i]);
448 if( asprintf( &ppsz_dir_list[i], "%s" DIR_SEP "lua" DIR_SEP "%s",
449 config_GetDataDir (), luadirname ) < 0 )
459 /*****************************************************************************
460 * Will execute func on all scripts in luadirname, and stop if func returns
462 *****************************************************************************/
463 int vlclua_scripts_batch_execute( vlc_object_t *p_this,
464 const char * luadirname,
465 int (*func)(vlc_object_t *, const char *, lua_State *, void *),
469 int i_ret = VLC_EGENERIC;
471 char **ppsz_filelist = NULL;
472 char **ppsz_fileend = NULL;
475 char *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
478 i_ret = vlclua_dir_list( p_this, luadirname, ppsz_dir_list );
479 if( i_ret != VLC_SUCCESS )
481 i_ret = VLC_EGENERIC;
484 for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
490 for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
493 free( ppsz_filelist );
494 ppsz_filelist = NULL;
497 msg_Dbg( p_this, "Trying Lua scripts in %s", *ppsz_dir );
498 i_files = utf8_scandir( *ppsz_dir, &ppsz_filelist, file_select,
500 if( i_files < 1 ) continue;
501 ppsz_fileend = ppsz_filelist + i_files;
503 for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
506 if( asprintf( &psz_filename,
507 "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) < 0)
509 msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
511 i_ret = func( p_this, psz_filename, L, user_data );
513 free( psz_filename );
515 if( i_ret == VLC_SUCCESS ) break;
517 if( i_ret == VLC_SUCCESS ) break;
522 for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
525 free( ppsz_filelist );
527 for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
534 /*****************************************************************************
535 * Meta data setters utility.
536 * Playlist item table should be on top of the stack when these are called
537 *****************************************************************************/
538 void __vlclua_read_meta_data( vlc_object_t *p_this, lua_State *L,
539 input_item_t *p_input )
541 #define TRY_META( a, b ) \
542 lua_getfield( L, -1, a ); \
543 if( lua_isstring( L, -1 ) ) \
545 char *psz_value = strdup( lua_tostring( L, -1 ) ); \
546 EnsureUTF8( psz_value ); \
547 msg_Dbg( p_this, #b ": %s", psz_value ); \
548 input_item_Set ## b ( p_input, psz_value ); \
551 lua_pop( L, 1 ); /* pop a */
552 TRY_META( "title", Title );
553 TRY_META( "artist", Artist );
554 TRY_META( "genre", Genre );
555 TRY_META( "copyright", Copyright );
556 TRY_META( "album", Album );
557 TRY_META( "tracknum", TrackNum );
558 TRY_META( "description", Description );
559 TRY_META( "rating", Rating );
560 TRY_META( "date", Date );
561 TRY_META( "setting", Setting );
562 TRY_META( "url", URL );
563 TRY_META( "language", Language );
564 TRY_META( "nowplaying", NowPlaying );
565 TRY_META( "publisher", Publisher );
566 TRY_META( "encodedby", EncodedBy );
567 TRY_META( "arturl", ArtURL );
568 TRY_META( "trackid", TrackID );
571 void __vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *L,
572 input_item_t *p_input )
575 lua_getfield( L, -1, "meta" );
577 if( lua_istable( L, -1 ) )
580 /* ... item meta nil */
581 while( lua_next( L, -2 ) )
583 /* ... item meta key value */
584 if( !lua_isstring( L, -2 ) )
586 msg_Warn( p_this, "Custom meta data category name must be "
589 else if( !lua_istable( L, -1 ) )
591 msg_Warn( p_this, "Custom meta data category contents "
596 const char *psz_meta_category = lua_tostring( L, -2 );
597 msg_Dbg( p_this, "Found custom meta data category: %s",
600 /* ... item meta key value nil */
601 while( lua_next( L, -2 ) )
603 /* ... item meta key value key2 value2 */
604 if( !lua_isstring( L, -2 ) )
606 msg_Warn( p_this, "Custom meta category item name "
607 "must be a string." );
609 else if( !lua_isstring( L, -1 ) )
611 msg_Warn( p_this, "Custom meta category item value "
612 "must be a string." );
616 const char *psz_meta_name =
617 lua_tostring( L, -2 );
618 const char *psz_meta_value =
619 lua_tostring( L, -1 );
620 msg_Dbg( p_this, "Custom meta %s, %s: %s",
621 psz_meta_category, psz_meta_name,
623 input_ItemAddInfo( p_input, psz_meta_category,
624 psz_meta_name, psz_meta_value );
626 lua_pop( L, 1 ); /* pop item */
627 /* ... item meta key value key2 */
629 /* ... item meta key value */
631 lua_pop( L, 1 ); /* pop category */
632 /* ... item meta key */
636 lua_pop( L, 1 ); /* pop "meta" */
637 /* ... item -> back to original stack */
640 /*****************************************************************************
642 ****************************************************************************/
644 * Playlist item table should be on top of the stack when this is called
646 void __vlclua_read_options( vlc_object_t *p_this, lua_State *L,
647 int *pi_options, char ***pppsz_options )
649 lua_getfield( L, -1, "options" );
650 if( lua_istable( L, -1 ) )
653 while( lua_next( L, -2 ) )
655 if( lua_isstring( L, -1 ) )
657 char *psz_option = strdup( lua_tostring( L, -1 ) );
658 msg_Dbg( p_this, "Option: %s", psz_option );
659 INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
664 msg_Warn( p_this, "Option should be a string" );
666 lua_pop( L, 1 ); /* pop option */
669 lua_pop( L, 1 ); /* pop "options" */
672 int __vlclua_playlist_add_internal( vlc_object_t *p_this, lua_State *L,
673 playlist_t *p_playlist,
674 input_item_t *p_parent, bool b_play )
678 assert( p_parent || p_playlist );
681 if( lua_istable( L, -1 ) )
685 while( lua_next( L, -2 ) )
687 /* playlist key item */
688 /* <Parse playlist item> */
689 if( lua_istable( L, -1 ) )
691 lua_getfield( L, -1, "path" );
692 /* playlist key item path */
693 if( lua_isstring( L, -1 ) )
695 const char *psz_path = NULL;
696 const char *psz_name = NULL;
697 char **ppsz_options = NULL;
699 mtime_t i_duration = -1;
700 input_item_t *p_input;
702 /* Read path and name */
703 psz_path = lua_tostring( L, -1 );
704 msg_Dbg( p_this, "Path: %s", psz_path );
705 lua_getfield( L, -2, "name" );
706 /* playlist key item path name */
707 if( lua_isstring( L, -1 ) )
709 psz_name = lua_tostring( L, -1 );
710 msg_Dbg( p_this, "Name: %s", psz_name );
714 if( !lua_isnil( L, -1 ) )
715 msg_Warn( p_this, "Playlist item name should be a string." );
720 lua_getfield( L, -3, "duration" );
721 /* playlist key item path name duration */
722 if( lua_isnumber( L, -1 ) )
724 i_duration = (mtime_t)(lua_tonumber( L, -1 )*1e6);
726 else if( !lua_isnil( L, -1 ) )
728 msg_Warn( p_this, "Playlist item duration should be a number (in seconds)." );
730 lua_pop( L, 1 ); /* pop "duration" */
732 /* playlist key item path name */
734 /* Read options: item must be on top of stack */
735 lua_pushvalue( L, -3 );
736 /* playlist key item path name item */
737 vlclua_read_options( p_this, L, &i_options, &ppsz_options );
739 /* Create input item */
740 p_input = input_ItemNewExt( p_playlist, psz_path,
742 (const char **)ppsz_options,
744 lua_pop( L, 3 ); /* pop "path name item" */
745 /* playlist key item */
747 /* Read meta data: item must be on top of stack */
748 vlclua_read_meta_data( p_this, L, p_input );
750 /* Read custom meta data: item must be on top of stack*/
751 vlclua_read_custom_meta_data( p_this, L, p_input );
753 /* Append item to playlist */
754 if( p_parent ) /* Add to node */
755 input_ItemAddSubItem( p_parent, p_input );
756 else /* Play or Enqueue (preparse) */
757 /* FIXME: playlist_AddInput() can fail */
758 playlist_AddInput( p_playlist, p_input,
760 ( b_play ? PLAYLIST_GO : PLAYLIST_PREPARSE ),
761 PLAYLIST_END, true, false );
762 i_count ++; /* increment counter */
763 vlc_gc_decref( p_input );
764 while( i_options > 0 )
765 free( ppsz_options[--i_options] );
766 free( ppsz_options );
770 lua_pop( L, 1 ); /* pop "path" */
772 "Playlist item's path should be a string" );
774 /* playlist key item */
778 msg_Warn( p_this, "Playlist item should be a table" );
780 /* <Parse playlist item> */
781 lua_pop( L, 1 ); /* pop the value, keep the key for
782 * the next lua_next() call */
789 msg_Warn( p_this, "Playlist should be a table." );