]> git.sesse.net Git - vlc/blob - modules/misc/lua/luaplaylist.c
6a4710c6eff8431603b309275e25f490da3c3da4
[vlc] / modules / misc / lua / 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 #ifdef HAVE_SYS_STAT_H
35 #   include <sys/stat.h>
36 #endif
37
38 #include "vlclua.h"
39
40
41 /*****************************************************************************
42  * Local prototypes
43  *****************************************************************************/
44 static int Demux( demux_t *p_demux );
45 static int Control( demux_t *p_demux, int i_query, va_list args );
46
47 /*****************************************************************************
48  *
49  *****************************************************************************/
50 struct demux_sys_t
51 {
52     lua_State *p_state;
53     char *psz_filename;
54 };
55
56 /*****************************************************************************
57  *
58  *****************************************************************************/
59
60 static int vlclua_demux_peek( lua_State *p_state )
61 {
62     demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
63     int i = lua_gettop( p_state );
64     int n;
65     byte_t *p_peek;
66     int i_peek;
67     if( !i ) return 0;
68     n = lua_tonumber( p_state, 1 );
69     lua_pop( p_state, i );
70     i_peek = stream_Peek( p_demux->s, &p_peek, n );
71     lua_pushlstring( p_state, (const char *)p_peek, i_peek );
72     return 1;
73 }
74
75 static int vlclua_demux_read( lua_State *p_state )
76 {
77     demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
78     int i = lua_gettop( p_state );
79     int n;
80     byte_t *p_read;
81     int i_read;
82     if( !i ) return 0;
83     n = lua_tonumber( p_state, 1 );
84     lua_pop( p_state, i );
85     i_read = stream_Read( p_demux->s, &p_read, n );
86     lua_pushlstring( p_state, (const char *)p_read, i_read );
87     return 1;
88 }
89
90 static int vlclua_demux_readline( lua_State *p_state )
91 {
92     demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
93     char *psz_line = stream_ReadLine( p_demux->s );
94     if( psz_line )
95     {
96         lua_pushstring( p_state, psz_line );
97         free( psz_line );
98     }
99     else
100     {
101         lua_pushnil( p_state );
102     }
103     return 1;
104 }
105
106
107 /* Functions to register */
108 static luaL_Reg p_reg[] =
109 {
110     { "peek", vlclua_demux_peek },
111     { "decode_uri", vlclua_decode_uri },
112     { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
113     { "msg_dbg", vlclua_msg_dbg },
114     { "msg_warn", vlclua_msg_warn },
115     { "msg_err", vlclua_msg_err },
116     { "msg_info", vlclua_msg_info },
117     { NULL, NULL }
118 };
119
120 /* Functions to register for parse() function call only */
121 static luaL_Reg p_reg_parse[] =
122 {
123     { "read", vlclua_demux_read },
124     { "readline", vlclua_demux_readline },
125     { NULL, NULL }
126 };
127
128 /*****************************************************************************
129  * Called through lua_scripts_batch_execute to call 'probe' on
130  * the script pointed by psz_filename.
131  *****************************************************************************/
132 static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
133                             lua_State * p_state, void * user_data )
134 {
135     demux_t * p_demux = (demux_t *)p_this;
136
137     p_demux->p_sys->psz_filename = strdup(psz_filename);
138     
139     /* Ugly hack to delete previous versions of the probe() and parse()
140     * functions. */
141     lua_pushnil( p_state );
142     lua_pushnil( p_state );
143     lua_setglobal( p_state, "probe" );
144     lua_setglobal( p_state, "parse" );
145     
146     /* Load and run the script(s) */
147     if( luaL_dofile( p_state, psz_filename ) )
148     {
149         msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
150                   lua_tostring( p_state, lua_gettop( p_state ) ) );
151         lua_pop( p_state, 1 );
152         return VLC_EGENERIC;
153     }
154     
155     lua_getglobal( p_state, "probe" );
156     
157     if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
158     {
159         msg_Warn( p_demux, "Error while runing script %s, "
160                   "function probe() not found", psz_filename );
161         lua_pop( p_state, 1 );
162         return VLC_EGENERIC;
163     }
164     
165     if( lua_pcall( p_state, 0, 1, 0 ) )
166     {
167         msg_Warn( p_demux, "Error while runing script %s, "
168                   "function probe(): %s", psz_filename,
169                   lua_tostring( p_state, lua_gettop( p_state ) ) );
170         lua_pop( p_state, 1 );
171         return VLC_EGENERIC;
172     }
173     
174     if( lua_gettop( p_state ) )
175     {
176         int i_ret = VLC_EGENERIC;
177         if( lua_toboolean( p_state, 1 ) )
178         {
179             msg_Dbg( p_demux, "Lua playlist script %s's "
180                      "probe() function was successful", psz_filename );
181             i_ret = VLC_SUCCESS;
182         }
183         lua_pop( p_state, 1 );
184         
185         return i_ret;
186     }
187     return VLC_EGENERIC;
188 }
189
190 /*****************************************************************************
191  * Import_LuaPlaylist: main import function
192  *****************************************************************************/
193 int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
194 {
195     demux_t *p_demux = (demux_t *)p_this;
196     lua_State *p_state;
197
198     p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
199     if( !p_demux->p_sys )
200     {
201         return VLC_ENOMEM;
202     }
203
204     p_demux->p_sys->psz_filename = NULL;
205
206     p_demux->pf_control = Control;
207     p_demux->pf_demux = Demux;
208
209     /* Initialise Lua state structure */
210     p_state = luaL_newstate();
211     if( !p_state )
212     {
213         msg_Err( p_demux, "Could not create new Lua State" );
214         free( p_demux->p_sys );
215         return VLC_EGENERIC;
216     }
217     p_demux->p_sys->p_state = p_state;
218
219     /* Load Lua libraries */
220     luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */
221
222     luaL_register( p_state, "vlc", p_reg );
223     lua_pushlightuserdata( p_state, p_demux );
224     lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
225     lua_pushstring( p_state, p_demux->psz_path );
226     lua_setfield( p_state, lua_gettop( p_state ) - 1, "path" );
227     lua_pushstring( p_state, p_demux->psz_access );
228     lua_setfield( p_state, lua_gettop( p_state ) - 1, "access" );
229
230     lua_pop( p_state, 1 );
231
232     return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript,
233                                          p_state, NULL );
234 }
235
236
237
238 /*****************************************************************************
239  * Deactivate: frees unused data
240  *****************************************************************************/
241 void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
242 {
243     demux_t *p_demux = (demux_t *)p_this;
244     lua_close( p_demux->p_sys->p_state );
245     free( p_demux->p_sys->psz_filename );
246     free( p_demux->p_sys );
247 }
248
249 static inline void read_options( demux_t *p_demux, lua_State *p_state,
250                                  int o, int t, int *pi_options,
251                                  char ***pppsz_options )
252 {
253     lua_getfield( p_state, o, "options" );
254     if( lua_istable( p_state, t ) )
255     {
256         lua_pushnil( p_state );
257         while( lua_next( p_state, t ) )
258         {
259             if( lua_isstring( p_state, t+2 ) )
260             {
261                 char *psz_option = strdup( lua_tostring( p_state, t+2 ) );
262                 msg_Dbg( p_demux, "Option: %s", psz_option );
263                 INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
264                              psz_option );
265             }
266             else
267             {
268                 msg_Warn( p_demux, "Option should be a string" );
269             }
270             lua_pop( p_state, 1 ); /* pop option */
271         }
272     }
273     lua_pop( p_state, 1 ); /* pop "options" */
274 }
275
276
277 static int Demux( demux_t *p_demux )
278 {
279     input_item_t *p_input;
280     lua_State *p_state = p_demux->p_sys->p_state;
281     char *psz_filename = p_demux->p_sys->psz_filename;
282     int t;
283
284     playlist_t *p_playlist = pl_Yield( p_demux );
285     input_thread_t *p_input_thread = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
286     input_item_t *p_current_input = input_GetItem( p_input_thread );
287
288     luaL_register( p_state, "vlc", p_reg_parse );
289
290     lua_getglobal( p_state, "parse" );
291
292     if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
293     {
294         msg_Warn( p_demux, "Error while runing script %s, "
295                   "function parse() not found", psz_filename );
296         E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
297         return VLC_EGENERIC;
298     }
299
300     if( lua_pcall( p_state, 0, 1, 0 ) )
301     {
302         msg_Warn( p_demux, "Error while runing script %s, "
303                   "function parse(): %s", psz_filename,
304                   lua_tostring( p_state, lua_gettop( p_state ) ) );
305         E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
306         return VLC_EGENERIC;
307     }
308
309     /* Check that the Lua stack is big enough and grow it if needed.
310      * Should be ok since LUA_MINSTACK is 20 but we never know. */
311     lua_checkstack( p_state, 8 );
312
313     if( ( t = lua_gettop( p_state ) ) )
314     {
315
316         if( lua_istable( p_state, t ) )
317         {
318             lua_pushnil( p_state );
319             while( lua_next( p_state, t ) )
320             {
321                 if( lua_istable( p_state, t+2 ) )
322                 {
323                     lua_getfield( p_state, t+2, "path" );
324                     if( lua_isstring( p_state, t+3 ) )
325                     {
326                         const char  *psz_path     = NULL;
327                         const char  *psz_name     = NULL;
328                         char       **ppsz_options = NULL;
329                         int          i_options    = 0;
330                         mtime_t      i_duration   = -1;
331
332                         /* Read path and name */
333                         psz_path = lua_tostring( p_state, t+3 );
334                         msg_Dbg( p_demux, "Path: %s", psz_path );
335                         lua_getfield( p_state, t+2, "name" );
336                         if( lua_isstring( p_state, t+4 ) )
337                         {
338                             psz_name = lua_tostring( p_state, t+4 );
339                             msg_Dbg( p_demux, "Name: %s", psz_name );
340                         }
341                         else
342                         {
343                             psz_name = psz_path;
344                         }
345
346                         /* Read duration */
347                         lua_getfield( p_state, t+2, "duration" );
348                         if( lua_isnumber( p_state, t+5 ) )
349                         {
350                             i_duration = (mtime_t)lua_tointeger( p_state, t+5 );
351                             i_duration *= 1000000;
352                         }
353                         lua_pop( p_state, 1 ); /* pop "duration" */
354
355                         /* Read options */
356                         read_options( p_demux, p_state, t+2, t+5,
357                                       &i_options, &ppsz_options );
358
359                         /* Create input item */
360                         p_input = input_ItemNewExt( p_playlist, psz_path,
361                                                     psz_name, i_options,
362                                                     (const char **)ppsz_options,
363                                                     i_duration );
364                         lua_pop( p_state, 1 ); /* pop "name" */
365
366                         /* Read meta data */
367                         vlclua_read_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4, p_input );
368
369                         /* Read custom meta data */
370                         vlclua_read_custom_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4,
371                                                p_input );
372
373                         /* Append item to playlist */
374                         input_ItemAddSubItem( p_current_input, p_input );
375
376                         while( i_options > 0 )
377                             free( ppsz_options[--i_options] );
378                         free( ppsz_options );
379                     }
380                     else
381                     {
382                         msg_Warn( p_demux,
383                                  "Playlist item's path should be a string" );
384                     }
385                     lua_pop( p_state, 1 ); /* pop "path" */
386                 }
387                 else
388                 {
389                     msg_Warn( p_demux, "Playlist item should be a table" );
390                 }
391                 lua_pop( p_state, 1 ); /* pop the value, keep the key for
392                                         * the next lua_next() call */
393             }
394         }
395         else
396         {
397             msg_Warn( p_demux, "Script didn't return a table" );
398         }
399     }
400     else
401     {
402         msg_Err( p_demux, "Script went completely foobar" );
403     }
404
405     vlc_object_release( p_input_thread );
406     vlc_object_release( p_playlist );
407
408     return -1; /* Needed for correct operation of go back */
409 }
410
411 static int Control( demux_t *p_demux, int i_query, va_list args )
412 {
413     return VLC_EGENERIC;
414 }