qt: playlist: use item title if available
[vlc.git] / modules / lua / vlc.c
blob1fe5a6faa809c51bf7b440bedbd2dca25a8e0047
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 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <assert.h>
32 #include <sys/stat.h>
34 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
36 #include "vlc.h"
38 #include <vlc_plugin.h>
39 #include <vlc_arrays.h>
40 #include <vlc_charset.h>
41 #include <vlc_fs.h>
42 #include <vlc_services_discovery.h>
43 #include <vlc_stream.h>
45 /*****************************************************************************
46 * Module descriptor
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 * );
77 vlc_module_begin ()
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" )
90 add_submodule ()
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") )
100 add_submodule ()
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,
108 TELNETPWD_LONGTEXT)
109 set_capability( "interface", 0 )
110 set_callbacks( Open_LuaTelnet, Close_LuaIntf )
111 set_description( N_("Lua Telnet") )
112 add_shortcut( "luatelnet", "telnet" )
114 add_submodule ()
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 )
120 add_submodule ()
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 )
126 add_submodule ()
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 )
133 add_submodule ()
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 )
139 add_submodule ()
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 )
146 add_submodule ()
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 )
151 change_volatile()
152 set_callbacks( Open_LuaSD, Close_LuaSD )
154 VLC_SD_PROBE_SUBMODULE
156 vlc_module_end ()
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 );
165 int j;
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] ) )
170 return 1;
172 return 0;
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))
184 return list;
186 if (likely(asprintf(list, "%s"DIR_SEP"lua"DIR_SEP"%s",
187 basedir, luadirname) != -1))
188 list++;
190 free(basedir);
191 return list;
194 int vlclua_dir_list(const char *luadirname, char ***restrict listp)
196 char **list = malloc(4 * sizeof(char *));
197 if (unlikely(list == NULL))
198 return VLC_EGENERIC;
200 *listp = list;
202 /* Lua scripts in user-specific data directory */
203 list = vlclua_dir_list_append(list, config_GetUserDir(VLC_USERDATA_DIR),
204 luadirname);
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);
216 else
217 free(datadir);
219 *list = NULL;
220 return VLC_SUCCESS;
223 void vlclua_dir_list_free( char **ppsz_dir_list )
225 for( char **ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
226 free( *ppsz_dir );
227 free( ppsz_dir_list );
230 /*****************************************************************************
231 * Will execute func on all scripts in luadirname, and stop if func returns
232 * success.
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 *),
237 void * user_data)
239 char **ppsz_dir_list = NULL;
240 int i_ret;
242 if( (i_ret = vlclua_dir_list( luadirname, &ppsz_dir_list )) != VLC_SUCCESS)
243 return i_ret;
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,
252 file_compare );
253 if( i_files < 0 )
254 continue;
256 char **ppsz_file = ppsz_filelist;
257 char **ppsz_fileend = ppsz_filelist + i_files;
259 while( ppsz_file < ppsz_fileend )
261 char *psz_filename;
263 if( asprintf( &psz_filename,
264 "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) == -1 )
265 psz_filename = NULL;
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 )
274 break;
278 while( ppsz_file < ppsz_fileend )
279 free( *(ppsz_file++) );
280 free( ppsz_filelist );
282 if( i_ret == VLC_SUCCESS )
283 break;
285 vlclua_dir_list_free( ppsz_dir_list );
286 return i_ret;
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++ )
298 char *psz_filename;
299 struct stat st;
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 );
305 return NULL;
308 if( vlc_stat( psz_filename, &st ) == 0
309 && S_ISREG( st.st_mode ) )
311 vlclua_dir_list_free( ppsz_dir_list );
312 return psz_filename;
314 free( psz_filename );
317 vlclua_dir_list_free( ppsz_dir_list );
318 return NULL;
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 ); \
338 free( 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();
375 /* ... item */
376 lua_getfield( L, -1, "meta" );
377 /* ... item meta */
378 if( lua_istable( L, -1 ) )
380 lua_pushnil( L );
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" */
389 continue;
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 /*****************************************************************************
406 * Playlist utilities
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 ) )
418 lua_pushnil( L );
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 );
427 else
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" );
442 return NULL;
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");
452 return NULL;
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" );
469 /* Read duration */
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 */
480 char **optv = NULL;
481 int optc = 0;
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,
488 ITEM_TYPE_UNKNOWN,
489 ITEM_NET_UNKNOWN);
490 if (unlikely(item == NULL))
491 goto out;
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);
503 if (title == NULL)
504 input_item_SetTitle(item, name);
505 free(title);
507 /* Read custom meta data: item must be on top of stack*/
508 vlclua_read_custom_meta_data(obj, L, item);
510 out:
511 while (optc > 0)
512 free(optv[--optc]);
513 free(optv);
514 return item;
517 static int vlc_sd_probe_Open( vlc_object_t *obj )
519 if( lua_Disabled( obj ) )
520 return VLC_EGENERIC;
522 vlc_dictionary_t name_d;
524 char **ppsz_dir_list;
525 if( vlclua_dir_list( "sd", &ppsz_dir_list ) )
526 return VLC_ENOMEM;
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,
533 file_compare );
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, '.' );
540 if( temp )
541 *temp = '\0';
543 if( !vlc_dictionary_has_key( &name_d, *ppsz_file ) )
544 vlc_dictionary_insert( &name_d, *ppsz_file, &name_d );
545 free( *ppsz_file );
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 );
553 if( names != NULL )
555 for( char **name = names; *name; ++name )
557 r = vlclua_probe_sd( obj, *name );
558 if( r != VLC_PROBE_CONTINUE )
559 break;
562 for( char **name = names; *name; ++name )
563 free( *name );
565 free( names );
567 vlc_dictionary_clear( &name_d, NULL, NULL );
569 return r;
572 static int vlclua_add_modules_path_inner( lua_State *L, const char *psz_path )
574 int count = 0;
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 );
579 count ++;
582 return count;
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 );
593 if( !psz_path )
594 return 1;
596 char *psz_char = strrchr( psz_path, DIR_SEP_CHAR );
597 if( !psz_char )
599 free( psz_path );
600 return 1;
602 *psz_char = '\0';
604 /* psz_path now holds the file's directory */
605 psz_char = strrchr( psz_path, DIR_SEP_CHAR );
606 if( !psz_char )
608 free( psz_path );
609 return 1;
611 *psz_char = '\0';
613 /* Push package on stack */
614 int count = 0;
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++ );
629 free( psz_path );
631 for( ; *ppsz_dir; ppsz_dir++ )
633 psz_path = *ppsz_dir;
634 /* FIXME: doesn't work well with meta/... modules due to the double
635 * directory depth */
636 psz_char = strrchr( psz_path, DIR_SEP_CHAR );
637 if( !psz_char )
639 vlclua_dir_list_free( ppsz_dir_list );
640 return 1;
643 *psz_char = '\0';
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 );
655 return 0;
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 );
664 free( uri );
665 return ret;
667 if( !strncasecmp( uri, "file://", 7 ) ) {
668 int ret = luaL_dofile( L, uri + 7 );
669 free( uri );
670 return ret;
672 stream_t *s = vlc_stream_NewURL( p_this, uri );
673 if( !s )
675 free( uri );
676 return 1;
678 int64_t i_size = stream_Size( s );
679 char *p_buffer = ( i_size > 0 ) ? malloc( i_size ) : NULL;
680 if( !p_buffer )
682 // FIXME: read the whole stream until we reach the end (if no size)
683 vlc_stream_Delete( s );
684 free( uri );
685 return 1;
687 int64_t i_read = vlc_stream_Read( s, p_buffer, (int) i_size );
688 int i_ret = ( i_read == i_size ) ? 0 : 1;
689 if( !i_ret )
690 i_ret = luaL_loadbuffer( L, p_buffer, (size_t) i_size, uri );
691 if( !i_ret )
692 i_ret = lua_pcall( L, 0, LUA_MULTRET, 0 );
693 vlc_stream_Delete( s );
694 free( p_buffer );
695 free( uri );
696 return i_ret;