1 /*****************************************************************************
2 * vlc.c: Generic lua interface functions
3 *****************************************************************************
4 * Copyright (C) 2007-2008 the VideoLAN team
6 * Authors: Antoine Cellerier <dionoea at videolan tod org>
7 * Pierre d'Herbemont <pdherbemont # videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
38 #include <vlc_plugin.h>
39 #include <vlc_arrays.h>
40 #include <vlc_charset.h>
42 #include <vlc_services_discovery.h>
43 #include <vlc_stream.h>
45 /*****************************************************************************
47 *****************************************************************************/
49 #define LUA_TEXT N_("Enable Lua plugins")
50 #define INTF_TEXT N_("Lua interface")
51 #define INTF_LONGTEXT N_("Lua interface module to load")
53 #define CONFIG_TEXT N_("Lua interface configuration")
54 #define CONFIG_LONGTEXT N_("Lua interface configuration string. Format is: '[\"<interface module name>\"] = { <option> = <value>, ...}, ...'.")
55 #define PASS_TEXT N_( "Password" )
56 #define PASS_LONGTEXT N_( "A single password restricts access " \
57 "to this interface." )
58 #define SRC_TEXT N_( "Source directory" )
59 #define SRC_LONGTEXT N_( "Source directory" )
60 #define INDEX_TEXT N_( "Directory index" )
61 #define INDEX_LONGTEXT N_( "Allow to build directory index" )
63 #define TELNETHOST_TEXT N_( "Host" )
64 #define TELNETHOST_LONGTEXT N_( "This is the host on which the " \
65 "interface will listen. It defaults to all network interfaces (0.0.0.0)." \
66 " If you want this interface to be available only on the local " \
67 "machine, enter \"127.0.0.1\"." )
68 #define TELNETPORT_TEXT N_( "Port" )
69 #define TELNETPORT_LONGTEXT N_( "This is the TCP port on which this " \
70 "interface will listen. It defaults to 4212." )
71 #define TELNETPWD_TEXT N_( "Password" )
72 #define TELNETPWD_LONGTEXT N_( "A single password restricts access " \
73 "to this interface." )
75 static int vlc_sd_probe_Open( vlc_object_t
* );
78 set_shortname( N_("Lua") )
79 set_description( N_("Lua interpreter") )
80 set_category( CAT_INTERFACE
)
81 set_subcategory( SUBCAT_INTERFACE_MAIN
)
83 add_bool( "lua", true, LUA_TEXT
, LUA_TEXT
, true );
84 add_string( "lua-intf", "dummy", INTF_TEXT
, INTF_LONGTEXT
, false )
85 add_string( "lua-config", "", CONFIG_TEXT
, CONFIG_LONGTEXT
, false )
86 set_capability( "interface", 0 )
87 set_callbacks( Open_LuaIntf
, Close_LuaIntf
)
88 add_shortcut( "luaintf" )
91 set_section( N_("Lua HTTP"), 0 )
92 add_password("http-password", NULL
, PASS_TEXT
, PASS_LONGTEXT
)
93 add_string ( "http-src", NULL
, SRC_TEXT
, SRC_LONGTEXT
, true )
94 add_bool ( "http-index", false, INDEX_TEXT
, INDEX_LONGTEXT
, true )
95 set_capability( "interface", 0 )
96 set_callbacks( Open_LuaHTTP
, Close_LuaIntf
)
97 add_shortcut( "luahttp", "http" )
98 set_description( N_("Lua HTTP") )
101 set_section( N_("Lua Telnet"), 0 )
102 add_string( "telnet-host", "localhost", TELNETHOST_TEXT
,
103 TELNETHOST_LONGTEXT
, true )
104 add_integer( "telnet-port", TELNETPORT_DEFAULT
, TELNETPORT_TEXT
,
105 TELNETPORT_LONGTEXT
, true )
106 change_integer_range( 1, 65535 )
107 add_password("telnet-password", NULL
, TELNETPWD_TEXT
,
109 set_capability( "interface", 0 )
110 set_callbacks( Open_LuaTelnet
, Close_LuaIntf
)
111 set_description( N_("Lua Telnet") )
112 add_shortcut( "luatelnet", "telnet" )
115 set_shortname( N_( "Lua Meta Fetcher" ) )
116 set_description( N_("Fetch meta data using lua scripts") )
117 set_capability( "meta fetcher", 10 )
118 set_callback( FetchMeta
)
121 set_shortname( N_( "Lua Meta Reader" ) )
122 set_description( N_("Read meta data using lua scripts") )
123 set_capability( "meta reader", 10 )
124 set_callback( ReadMeta
)
127 add_shortcut( "luaplaylist" )
128 set_shortname( N_("Lua Playlist") )
129 set_description( N_("Lua Playlist Parser Interface") )
130 set_capability( "demux", 2 )
131 set_callbacks( Import_LuaPlaylist
, Close_LuaPlaylist
)
134 set_shortname( N_( "Lua Art" ) )
135 set_description( N_("Fetch artwork using lua scripts") )
136 set_capability( "art finder", 10 )
137 set_callback( FindArt
)
140 set_shortname( N_("Lua Extension") )
141 set_description( N_("Lua Extension") )
142 add_shortcut( "luaextension" )
143 set_capability( "extension", 1 )
144 set_callbacks( Open_Extension
, Close_Extension
)
147 set_description( N_("Lua SD Module") )
148 add_shortcut( "luasd" )
149 set_capability( "services_discovery", 0 )
150 add_string( "lua-sd", "", NULL
, NULL
, false )
152 set_callbacks( Open_LuaSD
, Close_LuaSD
)
154 VLC_SD_PROBE_SUBMODULE
158 /*****************************************************************************
160 *****************************************************************************/
161 static const char *ppsz_lua_exts
[] = { ".luac", ".lua", ".vle", NULL
};
162 static int file_select( const char *file
)
164 int i
= strlen( file
);
166 for( j
= 0; ppsz_lua_exts
[j
]; j
++ )
168 int l
= strlen( ppsz_lua_exts
[j
] );
169 if( i
>= l
&& !strcmp( file
+i
-l
, ppsz_lua_exts
[j
] ) )
175 static int file_compare( const char **a
, const char **b
)
177 return strcmp( *a
, *b
);
180 static char **vlclua_dir_list_append( char **restrict list
, char *basedir
,
181 const char *luadirname
)
183 if (unlikely(basedir
== NULL
))
186 if (likely(asprintf(list
, "%s"DIR_SEP
"lua"DIR_SEP
"%s",
187 basedir
, luadirname
) != -1))
194 int vlclua_dir_list(const char *luadirname
, char ***restrict listp
)
196 char **list
= malloc(4 * sizeof(char *));
197 if (unlikely(list
== NULL
))
202 /* Lua scripts in user-specific data directory */
203 list
= vlclua_dir_list_append(list
, config_GetUserDir(VLC_USERDATA_DIR
),
206 char *libdir
= config_GetSysPath(VLC_PKG_LIBEXEC_DIR
, NULL
);
207 char *datadir
= config_GetSysPath(VLC_PKG_DATA_DIR
, NULL
);
208 bool both
= libdir
!= NULL
&& datadir
!= NULL
&& strcmp(libdir
, datadir
);
210 /* Tokenized Lua scripts in architecture-specific data directory */
211 list
= vlclua_dir_list_append(list
, libdir
, luadirname
);
213 /* Source Lua Scripts in architecture-independent data directory */
214 if (both
|| libdir
== NULL
)
215 list
= vlclua_dir_list_append(list
, datadir
, luadirname
);
223 void vlclua_dir_list_free( char **ppsz_dir_list
)
225 for( char **ppsz_dir
= ppsz_dir_list
; *ppsz_dir
; ppsz_dir
++ )
227 free( ppsz_dir_list
);
230 /*****************************************************************************
231 * Will execute func on all scripts in luadirname, and stop if func returns
233 *****************************************************************************/
234 int vlclua_scripts_batch_execute( vlc_object_t
*p_this
,
235 const char * luadirname
,
236 int (*func
)(vlc_object_t
*, const char *, const luabatch_context_t
*),
239 char **ppsz_dir_list
= NULL
;
242 if( (i_ret
= vlclua_dir_list( luadirname
, &ppsz_dir_list
)) != VLC_SUCCESS
)
245 i_ret
= VLC_EGENERIC
;
246 for( char **ppsz_dir
= ppsz_dir_list
; *ppsz_dir
; ppsz_dir
++ )
248 char **ppsz_filelist
;
250 msg_Dbg( p_this
, "Trying Lua scripts in %s", *ppsz_dir
);
251 int i_files
= vlc_scandir( *ppsz_dir
, &ppsz_filelist
, file_select
,
256 char **ppsz_file
= ppsz_filelist
;
257 char **ppsz_fileend
= ppsz_filelist
+ i_files
;
259 while( ppsz_file
< ppsz_fileend
)
263 if( asprintf( &psz_filename
,
264 "%s" DIR_SEP
"%s", *ppsz_dir
, *ppsz_file
) == -1 )
266 free( *(ppsz_file
++) );
268 if( likely(psz_filename
!= NULL
) )
270 msg_Dbg( p_this
, "Trying Lua playlist script %s", psz_filename
);
271 i_ret
= func( p_this
, psz_filename
, user_data
);
272 free( psz_filename
);
273 if( i_ret
== VLC_SUCCESS
)
278 while( ppsz_file
< ppsz_fileend
)
279 free( *(ppsz_file
++) );
280 free( ppsz_filelist
);
282 if( i_ret
== VLC_SUCCESS
)
285 vlclua_dir_list_free( ppsz_dir_list
);
289 char *vlclua_find_file( const char *psz_luadirname
, const char *psz_name
)
291 char **ppsz_dir_list
= NULL
;
292 vlclua_dir_list( psz_luadirname
, &ppsz_dir_list
);
294 for( char **ppsz_dir
= ppsz_dir_list
; *ppsz_dir
; ppsz_dir
++ )
296 for( const char **ppsz_ext
= ppsz_lua_exts
; *ppsz_ext
; ppsz_ext
++ )
301 if( asprintf( &psz_filename
, "%s"DIR_SEP
"%s%s", *ppsz_dir
,
302 psz_name
, *ppsz_ext
) < 0 )
304 vlclua_dir_list_free( ppsz_dir_list
);
308 if( vlc_stat( psz_filename
, &st
) == 0
309 && S_ISREG( st
.st_mode
) )
311 vlclua_dir_list_free( ppsz_dir_list
);
314 free( psz_filename
);
317 vlclua_dir_list_free( ppsz_dir_list
);
321 /*****************************************************************************
322 * Meta data setters utility.
323 * Playlist item table should be on top of the stack when these are called
324 *****************************************************************************/
325 #undef vlclua_read_meta_data
326 void vlclua_read_meta_data( vlc_object_t
*p_this
, lua_State
*L
,
327 input_item_t
*p_input
)
329 #define TRY_META( a, b ) \
330 lua_getfield( L, -1, a ); \
331 if( lua_isstring( L, -1 ) && \
332 strcmp( lua_tostring( L, -1 ), "" ) ) \
334 char *psz_value = strdup( lua_tostring( L, -1 ) ); \
335 EnsureUTF8( psz_value ); \
336 msg_Dbg( p_this, #b ": %s", psz_value ); \
337 input_item_Set ## b ( p_input, psz_value ); \
340 lua_pop( L, 1 ); /* pop a */
341 TRY_META( "title", Title
);
342 TRY_META( "artist", Artist
);
343 TRY_META( "genre", Genre
);
344 TRY_META( "copyright", Copyright
);
345 TRY_META( "album", Album
);
346 TRY_META( "tracknum", TrackNum
);
347 TRY_META( "description", Description
);
348 TRY_META( "rating", Rating
);
349 TRY_META( "date", Date
);
350 TRY_META( "setting", Setting
);
351 TRY_META( "url", URL
);
352 TRY_META( "language", Language
);
353 TRY_META( "nowplaying", NowPlaying
);
354 TRY_META( "publisher", Publisher
);
355 TRY_META( "encodedby", EncodedBy
);
356 TRY_META( "arturl", ArtURL
);
357 TRY_META( "trackid", TrackID
);
358 TRY_META( "director", Director
);
359 TRY_META( "season", Season
);
360 TRY_META( "episode", Episode
);
361 TRY_META( "show_name", ShowName
);
362 TRY_META( "actors", Actors
);
365 #undef vlclua_read_custom_meta_data
366 void vlclua_read_custom_meta_data( vlc_object_t
*p_this
, lua_State
*L
,
367 input_item_t
*p_input
)
369 /* Lock the input item and create the meta table if needed */
370 vlc_mutex_lock( &p_input
->lock
);
372 if( !p_input
->p_meta
)
373 p_input
->p_meta
= vlc_meta_New();
376 lua_getfield( L
, -1, "meta" );
378 if( lua_istable( L
, -1 ) )
381 /* ... item meta nil */
382 while( lua_next( L
, -2 ) )
384 /* ... item meta key value */
385 if( !lua_isstring( L
, -2 ) || !lua_isstring( L
, -1 ) )
387 msg_Err( p_this
, "'meta' keys and values must be strings");
388 lua_pop( L
, 1 ); /* pop "value" */
391 const char *psz_key
= lua_tostring( L
, -2 );
392 const char *psz_value
= lua_tostring( L
, -1 );
394 vlc_meta_AddExtra( p_input
->p_meta
, psz_key
, psz_value
);
396 lua_pop( L
, 1 ); /* pop "value" */
399 lua_pop( L
, 1 ); /* pop "meta" */
400 /* ... item -> back to original stack */
402 vlc_mutex_unlock( &p_input
->lock
);
405 /*****************************************************************************
407 ****************************************************************************/
409 * Playlist item table should be on top of the stack when this is called
411 #undef vlclua_read_options
412 void vlclua_read_options( vlc_object_t
*p_this
, lua_State
*L
,
413 int *pi_options
, char ***pppsz_options
)
415 lua_getfield( L
, -1, "options" );
416 if( lua_istable( L
, -1 ) )
419 while( lua_next( L
, -2 ) )
421 if( lua_isstring( L
, -1 ) )
423 char *psz_option
= strdup( lua_tostring( L
, -1 ) );
424 msg_Dbg( p_this
, "Option: %s", psz_option
);
425 TAB_APPEND( *pi_options
, *pppsz_options
, psz_option
);
429 msg_Warn( p_this
, "Option should be a string" );
431 lua_pop( L
, 1 ); /* pop option */
434 lua_pop( L
, 1 ); /* pop "options" */
437 input_item_t
*vlclua_read_input_item(vlc_object_t
*obj
, lua_State
*L
)
439 if (!lua_istable(L
, -1))
441 msg_Warn(obj
, "Playlist item should be a table" );
445 lua_getfield(L
, -1, "path");
447 /* playlist key item path */
448 if (!lua_isstring(L
, -1))
450 lua_pop(L
, 1); /* pop "path" */
451 msg_Warn(obj
, "Playlist item's path should be a string");
455 /* Read path and name */
456 const char *path
= lua_tostring(L
, -1);
457 msg_Dbg(obj
, "Path: %s", path
);
459 const char *name
= NULL
;
460 lua_getfield(L
, -2, "name");
461 if (lua_isstring(L
, -1))
463 name
= lua_tostring(L
, -1);
464 msg_Dbg(obj
, "Name: %s", name
);
466 else if (!lua_isnil(L
, -1))
467 msg_Warn(obj
, "Playlist item name should be a string" );
470 vlc_tick_t duration
= INPUT_DURATION_INDEFINITE
;
472 lua_getfield( L
, -3, "duration" );
473 if (lua_isnumber(L
, -1))
474 duration
= vlc_tick_from_sec( lua_tonumber(L
, -1) );
475 else if (!lua_isnil(L
, -1))
476 msg_Warn(obj
, "Playlist item duration should be a number (seconds)");
477 lua_pop( L
, 1 ); /* pop "duration" */
479 /* Read options: item must be on top of stack */
483 lua_pushvalue(L
, -3);
484 vlclua_read_options(obj
, L
, &optc
, &optv
);
486 /* Create input item */
487 input_item_t
*item
= input_item_NewExt(path
, name
, duration
,
490 if (unlikely(item
== NULL
))
493 input_item_AddOptions(item
, optc
, (const char **)optv
,
494 VLC_INPUT_OPTION_TRUSTED
);
495 lua_pop(L
, 3); /* pop "path name item" */
496 /* playlist key item */
498 /* Read meta data: item must be on top of stack */
499 vlclua_read_meta_data(obj
, L
, item
);
501 /* copy the psz_name to the meta data, if "Title" is still empty */
502 char* title
= input_item_GetTitle(item
);
504 input_item_SetTitle(item
, name
);
507 /* Read custom meta data: item must be on top of stack*/
508 vlclua_read_custom_meta_data(obj
, L
, item
);
517 static int vlc_sd_probe_Open( vlc_object_t
*obj
)
519 if( lua_Disabled( obj
) )
522 vlc_dictionary_t name_d
;
524 char **ppsz_dir_list
;
525 if( vlclua_dir_list( "sd", &ppsz_dir_list
) )
528 vlc_dictionary_init( &name_d
, 32 );
529 for( char **ppsz_dir
= ppsz_dir_list
; *ppsz_dir
; ppsz_dir
++ )
531 char **ppsz_filelist
;
532 int i_files
= vlc_scandir( *ppsz_dir
, &ppsz_filelist
, file_select
,
534 if( i_files
< 1 ) continue;
536 for( char **ppsz_file
= ppsz_filelist
;
537 ppsz_file
< ppsz_filelist
+ i_files
; ppsz_file
++ )
539 char *temp
= strchr( *ppsz_file
, '.' );
543 if( !vlc_dictionary_has_key( &name_d
, *ppsz_file
) )
544 vlc_dictionary_insert( &name_d
, *ppsz_file
, &name_d
);
547 free( ppsz_filelist
);
549 vlclua_dir_list_free( ppsz_dir_list
);
551 int r
= VLC_PROBE_CONTINUE
;
552 char **names
= vlc_dictionary_all_keys( &name_d
);
555 for( char **name
= names
; *name
; ++name
)
557 r
= vlclua_probe_sd( obj
, *name
);
558 if( r
!= VLC_PROBE_CONTINUE
)
562 for( char **name
= names
; *name
; ++name
)
567 vlc_dictionary_clear( &name_d
, NULL
, NULL
);
572 static int vlclua_add_modules_path_inner( lua_State
*L
, const char *psz_path
)
575 for( const char **ppsz_ext
= ppsz_lua_exts
; *ppsz_ext
; ppsz_ext
++ )
577 lua_pushfstring( L
, "%s"DIR_SEP
"modules"DIR_SEP
"?%s;",
578 psz_path
, *ppsz_ext
);
585 int vlclua_add_modules_path( lua_State
*L
, const char *psz_filename
)
587 /* Setup the module search path:
588 * * "The script's directory"/modules
589 * * "The script's parent directory"/modules
590 * * and so on for all the next directories in the directory list
592 char *psz_path
= strdup( psz_filename
);
596 char *psz_char
= strrchr( psz_path
, DIR_SEP_CHAR
);
604 /* psz_path now holds the file's directory */
605 psz_char
= strrchr( psz_path
, DIR_SEP_CHAR
);
613 /* Push package on stack */
615 lua_getglobal( L
, "package" );
617 /* psz_path now holds the file's parent directory */
618 count
+= vlclua_add_modules_path_inner( L
, psz_path
);
619 *psz_char
= DIR_SEP_CHAR
;
621 /* psz_path now holds the file's directory */
622 count
+= vlclua_add_modules_path_inner( L
, psz_path
);
624 char **ppsz_dir_list
= NULL
;
625 vlclua_dir_list( psz_char
+1/* gruik? */, &ppsz_dir_list
);
626 char **ppsz_dir
= ppsz_dir_list
;
628 for( ; *ppsz_dir
&& strcmp( *ppsz_dir
, psz_path
); ppsz_dir
++ );
631 for( ; *ppsz_dir
; ppsz_dir
++ )
633 psz_path
= *ppsz_dir
;
634 /* FIXME: doesn't work well with meta/... modules due to the double
636 psz_char
= strrchr( psz_path
, DIR_SEP_CHAR
);
639 vlclua_dir_list_free( ppsz_dir_list
);
644 count
+= vlclua_add_modules_path_inner( L
, psz_path
);
645 *psz_char
= DIR_SEP_CHAR
;
646 count
+= vlclua_add_modules_path_inner( L
, psz_path
);
649 lua_getfield( L
, -(count
+1), "path" ); /* Get package.path */
650 lua_concat( L
, count
+1 ); /* Concat vlc module paths and package.path */
651 lua_setfield( L
, -2, "path"); /* Set package.path */
652 lua_pop( L
, 1 ); /* Pop the package module */
654 vlclua_dir_list_free( ppsz_dir_list
);
658 /** Replacement for luaL_dofile, using VLC's input capabilities */
659 int vlclua_dofile( vlc_object_t
*p_this
, lua_State
*L
, const char *curi
)
661 char *uri
= ToLocaleDup( curi
);
662 if( !strstr( uri
, "://" ) ) {
663 int ret
= luaL_dofile( L
, uri
);
667 if( !strncasecmp( uri
, "file://", 7 ) ) {
668 int ret
= luaL_dofile( L
, uri
+ 7 );
672 stream_t
*s
= vlc_stream_NewURL( p_this
, uri
);
678 int64_t i_size
= stream_Size( s
);
679 char *p_buffer
= ( i_size
> 0 ) ? malloc( i_size
) : NULL
;
682 // FIXME: read the whole stream until we reach the end (if no size)
683 vlc_stream_Delete( s
);
687 int64_t i_read
= vlc_stream_Read( s
, p_buffer
, (int) i_size
);
688 int i_ret
= ( i_read
== i_size
) ? 0 : 1;
690 i_ret
= luaL_loadbuffer( L
, p_buffer
, (size_t) i_size
, uri
);
692 i_ret
= lua_pcall( L
, 0, LUA_MULTRET
, 0 );
693 vlc_stream_Delete( s
);