]> git.sesse.net Git - vlc/blob - modules/demux/playlist/luaplaylist.c
* luaplaylist.c: First usable version of the luaplaylist module
[vlc] / modules / demux / playlist / luaplaylist.c
1 /*****************************************************************************
2  * luaplaylist.c :  Lua playlist demux module
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc_demux.h>
29 #include <vlc_url.h>
30 #include <vlc_strings.h>
31 #include <vlc_charset.h>
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include "playlist.h"
35
36 #include <lua.h>        /* Low level lua C API */
37 #include <lauxlib.h>    /* Higher level C API */
38 #include <lualib.h>     /* Lua libs */
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int E_(Import_LuaPlaylist)( vlc_object_t *p_this );
44 static void E_(Close_LuaPlaylist)( vlc_object_t *p_this );
45
46 static int Demux( demux_t *p_demux );
47 static int Control( demux_t *p_demux, int i_query, va_list args );
48
49 /*****************************************************************************
50  * Module descriptor
51  *****************************************************************************/
52 vlc_module_begin();
53     add_shortcut( "lua" );
54     set_category( CAT_INPUT );
55     set_subcategory( SUBCAT_INPUT_DEMUX );
56
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) );
61 vlc_module_end();
62
63 /*****************************************************************************
64  *
65  *****************************************************************************/
66 struct demux_sys_t
67 {
68     lua_State *p_state;
69 };
70
71 static demux_t *vlclua_get_demux( lua_State *p_state )
72 {
73     demux_t *p_demux;
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 );
78     return p_demux;
79 }
80
81 static int vlclua_peek( lua_State *p_state )
82 {
83     demux_t *p_demux = vlclua_get_demux( p_state );
84     int i = lua_gettop( p_state );
85     int n;
86     byte_t *p_peek;
87     int i_peek;
88     if( !i ) return 0;
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 );
93     return 1;
94 }
95
96 static int vlclua_readline( lua_State *p_state )
97 {
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 );
101     return 1;
102 }
103
104 static int vlclua_decode_uri( lua_State *p_state )
105 {
106     int i = lua_gettop( p_state );
107     if( !i ) return 0;
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 );
114     free( psz_string );
115     return 1;
116 }
117
118 static int vlclua_resolve_xml_special_chars( lua_State *p_state )
119 {
120     int i = lua_gettop( p_state );
121     if( !i ) return 0;
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 );
128     free( psz_string );
129     return 1;
130 }
131
132 static int file_select( const char *file )
133 {
134     int i = strlen( file );
135     return i > 4 && !strcmp( file+i-4, ".lua" );
136 }
137
138 static int file_compare( const char **a, const char **b )
139 {
140     return strcmp( *a, *b );
141 }
142
143 /*****************************************************************************
144  * Import_LuaPlaylist: main import function
145  *****************************************************************************/
146 int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
147 {
148     demux_t *p_demux = (demux_t *)p_this;
149     lua_State *p_state;
150     int i_ret = VLC_EGENERIC;
151     char **ppsz_filelist = NULL;
152     char **ppsz_fileend = NULL;
153     char **ppsz_file;
154     DIR *dir;
155     char *psz_filename = NULL;
156     int i_files;
157     const char psz_dir[] = "share/luaplaylist"; /* FIXME */
158
159     static luaL_Reg p_reg[] =
160     {
161         { "readline", vlclua_readline },
162         { "peek", vlclua_peek },
163         { "decode_uri", vlclua_decode_uri },
164         { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars }
165     };
166
167     p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
168     if( !p_demux->p_sys )
169     {
170         return VLC_ENOMEM;
171     }
172
173     p_demux->pf_control = Control;
174     p_demux->pf_demux = Demux;
175
176     /* Initialise Lua state structure */
177     p_state = luaL_newstate();
178     if( !p_state )
179     {
180         msg_Err( p_demux, "Could not create new Lua State" );
181         free( p_demux->p_sys );
182         return VLC_EGENERIC;
183     }
184     p_demux->p_sys->p_state = p_state;
185
186     /* Load Lua libraries */
187     luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */
188
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" );
196
197     lua_pop( p_state, 1 );
198
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;
204
205     for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
206     {
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 );
210
211         /* Ugly hack to delete previous versions of the probe() and parse()
212          * functions. */
213         lua_pushnil( p_state );
214         lua_pushnil( p_state );
215         lua_setglobal( p_state, "probe" );
216         lua_setglobal( p_state, "parse" );
217
218         /* Load and run the script(s) */
219         if( luaL_dofile( p_state, psz_filename ) )
220         {
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 );
224             continue;
225         }
226
227         lua_getglobal( p_state, "probe" );
228
229         if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
230         {
231             msg_Warn( p_demux, "Error while runing script %s, "
232                       "function probe() not found", psz_filename );
233             lua_pop( p_state, 1 );
234             continue;
235         }
236
237         if( lua_pcall( p_state, 0, 1, 0 ) )
238         {
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 );
243             continue;
244         }
245
246         if( lua_gettop( p_state ) )
247         {
248             if( lua_toboolean( p_state, 1 ) )
249             {
250                 msg_Dbg( p_demux, "Lua playlist script %s's "
251                          "probe() function was successful", psz_filename );
252                 i_ret = VLC_SUCCESS;
253             }
254             lua_pop( p_state, 1 );
255
256             if( i_ret == VLC_SUCCESS ) break;
257         }
258     }
259
260     error:
261         free( psz_filename );
262
263         if( ppsz_filelist )
264         {
265             for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
266                  ppsz_file++ )
267                 free( *ppsz_file );
268             free( ppsz_filelist );
269         }
270
271         if( dir ) closedir( dir );
272         if( i_ret != VLC_SUCCESS )
273             E_(Close_LuaPlaylist)( p_this );
274         return i_ret;
275 }
276
277 /*****************************************************************************
278  * Deactivate: frees unused data
279  *****************************************************************************/
280 void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
281 {
282     demux_t *p_demux = (demux_t *)p_this;
283     lua_close( p_demux->p_sys->p_state );
284     free( p_demux->p_sys );
285 }
286
287 static int Demux( demux_t *p_demux )
288 {
289     input_item_t *p_input;
290     lua_State *p_state = p_demux->p_sys->p_state;
291     char psz_filename[] = "FIXME";
292
293     INIT_PLAYLIST_STUFF;
294
295     lua_getglobal( p_state, "parse" );
296
297     if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
298     {
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 ) );
302         return VLC_EGENERIC;
303     }
304
305     if( lua_pcall( p_state, 0, 1, 0 ) )
306     {
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 ) );
311         return VLC_EGENERIC;
312     }
313
314     if( lua_gettop( p_state ) )
315     {
316         int t = lua_gettop( p_state );
317         if( lua_istable( p_state, t ) )
318         {
319             lua_pushnil( p_state );
320             while( lua_next( p_state, t ) )
321             {
322                 if( lua_istable( p_state, t+2 ) )
323                 {
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 ) )
328                     {
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 ) )
333                         {
334                             psz_title = lua_tostring( p_state, t+4 );
335                             printf("Title: %s\n", psz_title );
336                         }
337                         else
338                         {
339                             psz_title = psz_url;
340                         }
341                         p_input = input_ItemNewExt( p_playlist, psz_url,
342                                                     psz_title, 0, NULL, -1 );
343                         playlist_BothAddInput(
344                             p_playlist, p_input,
345                             p_item_in_category,
346                             PLAYLIST_APPEND | PLAYLIST_SPREPARSE,
347                             PLAYLIST_END, NULL, NULL, VLC_FALSE );
348                         lua_pop( p_state, 1 ); /* pop "title" */
349                     }
350                     else
351                     {
352                         printf("URL isn't a string\n");
353                     }
354                     lua_pop( p_state, 1 ); /* pop "url" */
355                 }
356                 else
357                 {
358                     printf("This isn't a table !!!\n" );
359                 }
360                 lua_pop( p_state, 1 ); /* pop the value, keep the key for
361                                         * the next lua_next() call */
362             }
363             lua_pop( p_state, 1 ); /* pop the last key */
364         }
365         else
366         {
367             msg_Warn( p_demux, "Script didn't return a table" );
368         }
369     }
370     else
371     {
372         msg_Err( p_demux, "Script went completely foobar" );
373     }
374
375     HANDLE_PLAY_AND_RELEASE;
376
377     return -1; /* Needed for correct operation of go back */
378 }
379
380 static int Control( demux_t *p_demux, int i_query, va_list args )
381 {
382     return VLC_EGENERIC;
383 }