1 /*****************************************************************************
2 * luaplaylist.c : Lua playlist demux module
3 *****************************************************************************
4 * Copyright (C) 2007 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea at videolan tod 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 *****************************************************************************/
28 #include <vlc_demux.h>
30 #include <vlc_strings.h>
31 #include <vlc_charset.h>
33 #include <errno.h> /* ENOMEM */
36 #include <lua.h> /* Low level lua C API */
37 #include <lauxlib.h> /* Higher level C API */
38 #include <lualib.h> /* Lua libs */
40 /*****************************************************************************
42 *****************************************************************************/
43 static int E_(Import_LuaPlaylist)( vlc_object_t *p_this );
44 static void E_(Close_LuaPlaylist)( vlc_object_t *p_this );
46 static int Demux( demux_t *p_demux );
47 static int Control( demux_t *p_demux, int i_query, va_list args );
49 /*****************************************************************************
51 *****************************************************************************/
53 add_shortcut( "lua" );
54 set_category( CAT_INPUT );
55 set_subcategory( SUBCAT_INPUT_DEMUX );
57 set_shortname( _("Lua Playlist") );
58 set_description( _("Lua Playlist Parser Interface") );
59 set_capability( "demux2", 0 );
60 set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) );
63 /*****************************************************************************
65 *****************************************************************************/
71 static demux_t *vlclua_get_demux( lua_State *p_state )
74 lua_getglobal( p_state, "vlc" );
75 lua_getfield( p_state, lua_gettop( p_state ), "private" );
76 p_demux = (demux_t*)lua_topointer( p_state, lua_gettop( p_state ) );
77 lua_pop( p_state, 2 );
81 static int vlclua_peek( lua_State *p_state )
83 demux_t *p_demux = vlclua_get_demux( p_state );
84 int i = lua_gettop( p_state );
89 n = lua_tonumber( p_state, 1 );
90 lua_pop( p_state, i );
91 i_peek = stream_Peek( p_demux->s, &p_peek, n );
92 lua_pushlstring( p_state, (const char *)p_peek, i_peek );
96 static int vlclua_readline( lua_State *p_state )
98 demux_t *p_demux = vlclua_get_demux( p_state );
99 char *psz_line = stream_ReadLine( p_demux->s );
100 lua_pushstring( p_state, psz_line );
104 static int vlclua_decode_uri( lua_State *p_state )
106 int i = lua_gettop( p_state );
108 const char *psz_cstring = lua_tostring( p_state, 1 );
109 if( !psz_cstring ) return 0;
110 char *psz_string = strdup( psz_cstring );
111 lua_pop( p_state, i );
112 decode_URI( psz_string );
113 lua_pushstring( p_state, psz_string );
118 static int vlclua_resolve_xml_special_chars( lua_State *p_state )
120 int i = lua_gettop( p_state );
122 const char *psz_cstring = lua_tostring( p_state, 1 );
123 if( !psz_cstring ) return 0;
124 char *psz_string = strdup( psz_cstring );
125 lua_pop( p_state, i );
126 resolve_xml_special_chars( psz_string );
127 lua_pushstring( p_state, psz_string );
132 static int file_select( const char *file )
134 int i = strlen( file );
135 return i > 4 && !strcmp( file+i-4, ".lua" );
138 static int file_compare( const char **a, const char **b )
140 return strcmp( *a, *b );
143 /*****************************************************************************
144 * Import_LuaPlaylist: main import function
145 *****************************************************************************/
146 int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
148 demux_t *p_demux = (demux_t *)p_this;
150 int i_ret = VLC_EGENERIC;
151 char **ppsz_filelist = NULL;
152 char **ppsz_fileend = NULL;
155 char *psz_filename = NULL;
157 const char psz_dir[] = "share/luaplaylist"; /* FIXME */
159 static luaL_Reg p_reg[] =
161 { "readline", vlclua_readline },
162 { "peek", vlclua_peek },
163 { "decode_uri", vlclua_decode_uri },
164 { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars }
167 p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
168 if( !p_demux->p_sys )
173 p_demux->pf_control = Control;
174 p_demux->pf_demux = Demux;
176 /* Initialise Lua state structure */
177 p_state = luaL_newstate();
180 msg_Err( p_demux, "Could not create new Lua State" );
181 free( p_demux->p_sys );
184 p_demux->p_sys->p_state = p_state;
186 /* Load Lua libraries */
187 luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */
189 luaL_register( p_state, "vlc", p_reg );
190 lua_pushlightuserdata( p_state, p_demux );
191 lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
192 lua_pushstring( p_state, p_demux->psz_path );
193 lua_setfield( p_state, lua_gettop( p_state ) - 1, "path" );
194 lua_pushstring( p_state, p_demux->psz_access );
195 lua_setfield( p_state, lua_gettop( p_state ) - 1, "access" );
197 lua_pop( p_state, 1 );
199 dir = utf8_opendir( psz_dir );
200 if( !dir ) goto error;
201 i_files = utf8_loaddir( dir, &ppsz_filelist, file_select, file_compare );
202 if( i_files < 1 ) goto error;
203 ppsz_fileend = ppsz_filelist + i_files;
205 for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
207 free( psz_filename ); psz_filename = NULL;
208 asprintf( &psz_filename, "%s/%s", psz_dir, *ppsz_file );
209 msg_Dbg( p_demux, "Trying Lua playlist script %s", psz_filename );
211 /* Ugly hack to delete previous versions of the probe() and parse()
213 lua_pushnil( p_state );
214 lua_pushnil( p_state );
215 lua_setglobal( p_state, "probe" );
216 lua_setglobal( p_state, "parse" );
218 /* Load and run the script(s) */
219 if( luaL_dofile( p_state, psz_filename ) )
221 msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
222 lua_tostring( p_state, lua_gettop( p_state ) ) );
223 lua_pop( p_state, 1 );
227 lua_getglobal( p_state, "probe" );
229 if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
231 msg_Warn( p_demux, "Error while runing script %s, "
232 "function probe() not found", psz_filename );
233 lua_pop( p_state, 1 );
237 if( lua_pcall( p_state, 0, 1, 0 ) )
239 msg_Warn( p_demux, "Error while runing script %s, "
240 "function probe(): %s", psz_filename,
241 lua_tostring( p_state, lua_gettop( p_state ) ) );
242 lua_pop( p_state, 1 );
246 if( lua_gettop( p_state ) )
248 if( lua_toboolean( p_state, 1 ) )
250 msg_Dbg( p_demux, "Lua playlist script %s's "
251 "probe() function was successful", psz_filename );
254 lua_pop( p_state, 1 );
256 if( i_ret == VLC_SUCCESS ) break;
261 free( psz_filename );
265 for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
268 free( ppsz_filelist );
271 if( dir ) closedir( dir );
272 if( i_ret != VLC_SUCCESS )
273 E_(Close_LuaPlaylist)( p_this );
277 /*****************************************************************************
278 * Deactivate: frees unused data
279 *****************************************************************************/
280 void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
282 demux_t *p_demux = (demux_t *)p_this;
283 lua_close( p_demux->p_sys->p_state );
284 free( p_demux->p_sys );
287 static int Demux( demux_t *p_demux )
289 input_item_t *p_input;
290 lua_State *p_state = p_demux->p_sys->p_state;
291 char psz_filename[] = "FIXME";
295 lua_getglobal( p_state, "parse" );
297 if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
299 msg_Warn( p_demux, "Error while runing script %s, "
300 "function parse() not found", psz_filename );
301 E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
305 if( lua_pcall( p_state, 0, 1, 0 ) )
307 msg_Warn( p_demux, "Error while runing script %s, "
308 "function parse(): %s", psz_filename,
309 lua_tostring( p_state, lua_gettop( p_state ) ) );
310 E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
314 if( lua_gettop( p_state ) )
316 int t = lua_gettop( p_state );
317 if( lua_istable( p_state, t ) )
319 lua_pushnil( p_state );
320 while( lua_next( p_state, t ) )
322 if( lua_istable( p_state, t+2 ) )
324 const char *psz_url = NULL;
325 const char *psz_title = NULL;
326 lua_getfield( p_state, t+2, "url" );
327 if( lua_isstring( p_state, t+3 ) )
329 psz_url = lua_tostring( p_state, t+3 );
330 printf("URL: %s\n", psz_url );
331 lua_getfield( p_state, t+2, "title" );
332 if( lua_isstring( p_state, t+4 ) )
334 psz_title = lua_tostring( p_state, t+4 );
335 printf("Title: %s\n", psz_title );
341 p_input = input_ItemNewExt( p_playlist, psz_url,
342 psz_title, 0, NULL, -1 );
343 playlist_BothAddInput(
346 PLAYLIST_APPEND | PLAYLIST_SPREPARSE,
347 PLAYLIST_END, NULL, NULL, VLC_FALSE );
348 lua_pop( p_state, 1 ); /* pop "title" */
352 printf("URL isn't a string\n");
354 lua_pop( p_state, 1 ); /* pop "url" */
358 printf("This isn't a table !!!\n" );
360 lua_pop( p_state, 1 ); /* pop the value, keep the key for
361 * the next lua_next() call */
363 lua_pop( p_state, 1 ); /* pop the last key */
367 msg_Warn( p_demux, "Script didn't return a table" );
372 msg_Err( p_demux, "Script went completely foobar" );
375 HANDLE_PLAY_AND_RELEASE;
377 return -1; /* Needed for correct operation of go back */
380 static int Control( demux_t *p_demux, int i_query, va_list args )