for i in $(srcdir)/share/luameta/*.* ; do \
$(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luameta/`basename $${i}` ; \
done ; \
+ $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf
+ for i in $(srcdir)/share/luaintf/*.* ; do \
+ $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/`basename $${i}` ; \
+ done ; \
+ $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/modules
+ for i in $(srcdir)/share/luaintf/modules/*.* ; do \
+ $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/modules/`basename $${i}` ; \
+ done ; \
$(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/dialogs
$(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/js
$(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/old
for i in $(srcdir)/share/luameta/*.* ; do \
$(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luameta/`basename $${i}` ; \
done ;
+ $(INSTALL) -d $(top_builddir)/vlc-${VERSION}/share/luaintf
+ for i in $(srcdir)/share/luaintf/*.* ; do \
+ $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luaintf/`basename $${i}` ; \
+ done ;
+ $(INSTALL) -d $(top_builddir)/vlc-${VERSION}/share/luaintf/modules
+ for i in $(srcdir)/share/luaintf/modules/*.* ; do \
+ $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luaintf/modules/`basename $${i}` ; \
+ done ;
mkdir -p "$(top_builddir)/vlc-${VERSION}/osdmenu"
cp $(srcdir)/share/osdmenu/*.* "$(top_builddir)/vlc-${VERSION}/osdmenu"
-SOURCES_lua = luaplaylist.c luameta.c vlclua.c vlclua.h
+SOURCES_lua = playlist.c meta.c intf.c vlc.c vlc.h callbacks.c objects.c variables.c configuration.c net.c vlm.c
--- /dev/null
+/*****************************************************************************
+ * callbacks.c: Generic lua<->vlc callbacks interface
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+typedef struct
+{
+ int i_index;
+ int i_type;
+ lua_State *L;
+} vlclua_callback_t;
+
+static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
+ lua_State *L = p_callback->L;
+
+ /* <empty stack> */
+ lua_getglobal( L, "vlc" );
+ /* vlc */
+ lua_getfield( L, -1, "callbacks" );
+ /* vlc callbacks */
+ lua_remove( L, -2 );
+ /* callbacks */
+ lua_pushinteger( L, p_callback->i_index );
+ /* callbacks index */
+ lua_gettable( L, -2 );
+ /* callbacks callbacks[index] */
+ lua_remove( L, -2 );
+ /* callbacks[index] */
+ lua_getfield( L, -1, "callback" );
+ /* callbacks[index] callback */
+ lua_pushstring( L, psz_var );
+ /* callbacks[index] callback var */
+ vlclua_pushvalue( L, p_callback->i_type, oldval );
+ /* callbacks[index] callback var oldval */
+ vlclua_pushvalue( L, p_callback->i_type, newval );
+ /* callbacks[index] callback var oldval newval */
+ lua_getfield( L, -5, "data" );
+ /* callbacks[index] callback var oldval newval data */
+ lua_remove( L, -6 );
+ /* callback var oldval newval data */
+
+ if( lua_pcall( L, 4, 0, 0 ) )
+ {
+ /* errormessage */
+ const char *psz_err = lua_tostring( L, -1 );
+ msg_Err( p_this, "Error while runing lua interface callback: %s",
+ psz_err );
+ /* empty the stack (should only contain the error message) */
+ lua_settop( L, 0 );
+ return VLC_EGENERIC;
+ }
+
+ /* empty the stack (should already be empty) */
+ lua_settop( L, 0 );
+
+ return VLC_SUCCESS;
+}
+
+int vlclua_add_callback( lua_State *L )
+{
+ vlclua_callback_t *p_callback;
+ static int i_index = 0;
+ vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+ const char *psz_var = luaL_checkstring( L, 2 );
+ lua_settop( L, 4 ); /* makes sure that optional data arg is set */
+ if( !lua_isfunction( L, 3 ) )
+ return vlclua_error( L );
+ i_index++;
+
+ p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
+ if( !p_callback )
+ return vlclua_error( L );
+
+ /* obj var func data */
+ lua_getglobal( L, "vlc" );
+ /* obj var func data vlc */
+ lua_getfield( L, -1, "callbacks" );
+ if( lua_isnil( L, -1 ) )
+ {
+ lua_pop( L, 1 );
+ lua_newtable( L );
+ lua_setfield( L, -2, "callbacks" );
+ lua_getfield( L, -1, "callbacks" );
+ }
+ /* obj var func data vlc callbacks */
+ lua_remove( L, -2 );
+ /* obj var func data callbacks */
+ lua_pushinteger( L, i_index );
+ /* obj var func data callbacks index */
+ lua_insert( L, -4 );
+ /* obj var index func data callbacks */
+ lua_insert( L, -4 );
+ /* obj var callbacks index func data */
+ lua_createtable( L, 0, 0 );
+ /* obj var callbacks index func data cbtable */
+ lua_insert( L, -2 );
+ /* obj var callbacks index func cbtable data */
+ lua_setfield( L, -2, "data" );
+ /* obj var callbacks index func cbtable */
+ lua_insert( L, -2 );
+ /* obj var callbacks index cbtable func */
+ lua_setfield( L, -2, "callback" );
+ /* obj var callbacks index cbtable */
+ lua_pushlightuserdata( L, p_obj ); /* will be needed in vlclua_del_callback */
+ /* obj var callbacks index cbtable p_obj */
+ lua_setfield( L, -2, "private1" );
+ /* obj var callbacks index cbtable */
+ lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
+ /* obj var callbacks index cbtable var */
+ lua_setfield( L, -2, "private2" );
+ /* obj var callbacks index cbtable */
+ lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
+ /* obj var callbacks index cbtable p_callback */
+ lua_setfield( L, -2, "private3" );
+ /* obj var callbacks index cbtable */
+ lua_settable( L, -3 );
+ /* obj var callbacks */
+ lua_pop( L, 3 );
+ /* <empty stack> */
+
+ /* Do not move this before the lua specific code (it somehow changes
+ * the function in the stack to nil) */
+ p_callback->i_index = i_index;
+ p_callback->i_type = var_Type( p_obj, psz_var );
+ p_callback->L = lua_newthread( L );
+
+ var_AddCallback( p_obj, psz_var, vlclua_callback, p_callback );
+ return 0;
+}
+
+int vlclua_del_callback( lua_State *L )
+{
+ vlclua_callback_t *p_callback;
+ vlc_bool_t b_found = VLC_FALSE;
+ vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+ const char *psz_var = luaL_checkstring( L, 2 );
+ lua_settop( L, 4 ); /* makes sure that optional data arg is set */
+ if( !lua_isfunction( L, 3 ) )
+ return vlclua_error( L );
+
+ /* obj var func data */
+ lua_getglobal( L, "vlc" );
+ /* obj var func data vlc */
+ lua_getfield( L, -1, "callbacks" );
+ if( lua_isnil( L, -1 ) )
+ return luaL_error( L, "Couldn't find matching callback." );
+ /* obj var func data vlc callbacks */
+ lua_remove( L, -2 );
+ /* obj var func data callbacks */
+ lua_pushnil( L );
+ /* obj var func data callbacks index */
+ while( lua_next( L, -2 ) )
+ {
+ /* obj var func data callbacks index value */
+ if( lua_isnumber( L, -2 ) )
+ {
+ lua_getfield( L, -1, "private2" );
+ /* obj var func data callbacks index value private2 */
+ if( lua_equal( L, 2, -1 ) ) /* var name is equal */
+ {
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ lua_getfield( L, -1, "callback" );
+ /* obj var func data callbacks index value callback */
+ if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
+ {
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ lua_getfield( L, -1, "data" ); /* callback data is equal */
+ /* obj var func data callbacks index value data */
+ if( lua_equal( L, 4, -1 ) )
+ {
+ vlc_object_t *p_obj2;
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ lua_getfield( L, -1, "private1" );
+ /* obj var func data callbacks index value private1 */
+ p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
+ if( p_obj2 == p_obj ) /* object is equal */
+ {
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ lua_getfield( L, -1, "private3" );
+ /* obj var func data callbacks index value private3 */
+ p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
+ lua_pop( L, 2 );
+ /* obj var func data callbacks index */
+ b_found = VLC_TRUE;
+ break;
+ }
+ else
+ {
+ /* obj var func data callbacks index value private1 */
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ }
+ }
+ else
+ {
+ /* obj var func data callbacks index value data */
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ }
+ }
+ else
+ {
+ /* obj var func data callbacks index value callback */
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ }
+ }
+ else
+ {
+ /* obj var func data callbacks index value private2 */
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index value */
+ }
+ }
+ /* obj var func data callbacks index value */
+ lua_pop( L, 1 );
+ /* obj var func data callbacks index */
+ }
+ if( b_found == VLC_FALSE )
+ /* obj var func data callbacks */
+ return luaL_error( L, "Couldn't find matching callback." );
+ /* else */
+ /* obj var func data callbacks index*/
+
+ var_DelCallback( p_obj, psz_var, vlclua_callback, p_callback );
+ free( p_callback );
+
+ /* obj var func data callbacks index */
+ lua_pushnil( L );
+ /* obj var func data callbacks index nil */
+ lua_settable( L, -3 ); /* delete the callback table entry */
+ /* obj var func data callbacks */
+ lua_pop( L, 5 );
+ /* <empty stack> */
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+ * configuration.c: Generic lua<->vlc config inteface
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ * Config handling
+ *****************************************************************************/
+int vlclua_config_get( lua_State *L )
+{
+ vlc_object_t * p_this = vlclua_get_this( L );
+ const char *psz_name;
+ psz_name = luaL_checkstring( L, 1 );
+ switch( config_GetType( p_this, psz_name ) )
+ {
+ case VLC_VAR_MODULE:
+ case VLC_VAR_STRING:
+ case VLC_VAR_FILE:
+ case VLC_VAR_DIRECTORY:
+ lua_pushstring( L, config_GetPsz( p_this, psz_name ) );
+ break;
+
+ case VLC_VAR_INTEGER:
+ lua_pushinteger( L, config_GetInt( p_this, psz_name ) );
+ break;
+
+ case VLC_VAR_BOOL:
+ lua_pushboolean( L, config_GetInt( p_this, psz_name ) );
+ break;
+
+ case VLC_VAR_FLOAT:
+ lua_pushnumber( L, config_GetFloat( p_this, psz_name ) );
+ break;
+
+ default:
+ return vlclua_error( L );
+
+ }
+ return 1;
+}
+
+int vlclua_config_set( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ const char *psz_name;
+ psz_name = luaL_checkstring( L, 1 );
+ switch( config_GetType( p_this, psz_name ) )
+ {
+ case VLC_VAR_MODULE:
+ case VLC_VAR_STRING:
+ case VLC_VAR_FILE:
+ case VLC_VAR_DIRECTORY:
+ config_PutPsz( p_this, psz_name, luaL_checkstring( L, 2 ) );
+ break;
+
+ case VLC_VAR_INTEGER:
+ config_PutInt( p_this, psz_name, luaL_checkint( L, 2 ) );
+ break;
+
+ case VLC_VAR_BOOL:
+ config_PutInt( p_this, psz_name, luaL_checkboolean( L, 2 ) );
+ break;
+
+ case VLC_VAR_FLOAT:
+ config_PutFloat( p_this, psz_name,
+ luaL_checknumber( L, 2 ) );
+ break;
+
+ default:
+ return vlclua_error( L );
+ }
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+ * intf.c: Generic lua inteface functions
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_meta.h>
+#include <vlc_charset.h>
+
+#include <vlc_interface.h>
+#include <vlc_playlist.h>
+#include <vlc_aout.h>
+#include <vlc_vout.h>
+#include <vlc_osd.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+struct intf_sys_t
+{
+ char *psz_filename;
+ lua_State *L;
+};
+
+/*****************************************************************************
+ * Internal lua<->vlc utils
+ *****************************************************************************/
+static inline playlist_t *vlclua_get_playlist_internal( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ return pl_Yield( p_this );
+}
+
+static input_thread_t * vlclua_get_input_internal( lua_State *L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ input_thread_t *p_input = p_playlist->p_input;
+ if( p_input ) vlc_object_yield( p_input );
+ vlc_object_release( p_playlist );
+ return p_input;
+}
+
+/* FIXME: This is high level logic. Should be implemented in lua */
+#define vlclua_var_toggle_or_set(a,b,c) \
+ __vlclua_var_toggle_or_set(a,VLC_OBJECT(b),c)
+static int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
+ const char *psz_name )
+{
+ vlc_bool_t b_bool;
+ if( lua_gettop( L ) > 1 ) return vlclua_error( L );
+
+ if( lua_gettop( L ) == 0 )
+ b_bool = !var_GetBool( p_obj, psz_name );
+ else /* lua_gettop( L ) == 1 */
+ {
+ b_bool = luaL_checkboolean( L, -1 )?VLC_TRUE:VLC_FALSE;
+ lua_pop( L, 1 );
+ }
+
+ if( b_bool != var_GetBool( p_obj, psz_name ) )
+ var_SetBool( p_obj, psz_name, b_bool );
+
+ lua_pushboolean( L, b_bool );
+ return 1;
+}
+
+/*****************************************************************************
+ * Libvlc TODO: move to vlc.c
+ *****************************************************************************/
+static int vlclua_get_libvlc( lua_State *L )
+{
+ vlclua_push_vlc_object( L, vlclua_get_this( L )->p_libvlc,
+ NULL );
+ return 1;
+}
+
+/*****************************************************************************
+ * Input handling
+ *****************************************************************************/
+static int vlclua_get_input( lua_State *L )
+{
+ input_thread_t *p_input = vlclua_get_input_internal( L );
+ if( p_input )
+ {
+ vlclua_push_vlc_object( L, p_input, vlclua_gc_release );
+ }
+ else lua_pushnil( L );
+ return 1;
+}
+
+static int vlclua_input_info( lua_State *L )
+{
+ input_thread_t * p_input = vlclua_get_input_internal( L );
+ int i_cat;
+ int i;
+ if( !p_input ) return vlclua_error( L );
+ vlc_mutex_lock( &input_GetItem(p_input)->lock );
+ i_cat = input_GetItem(p_input)->i_categories;
+ lua_createtable( L, 0, i_cat );
+ for( i = 0; i < i_cat; i++ )
+ {
+ info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
+ int i_infos = p_category->i_infos;
+ int j;
+ lua_pushstring( L, p_category->psz_name );
+ lua_createtable( L, 0, i_infos );
+ for( j = 0; j < i_infos; j++ )
+ {
+ info_t *p_info = p_category->pp_infos[j];
+ lua_pushstring( L, p_info->psz_name );
+ lua_pushstring( L, p_info->psz_value );
+ lua_settable( L, -3 );
+ }
+ lua_settable( L, -3 );
+ }
+ vlc_object_release( p_input );
+ return 1;
+}
+
+static int vlclua_is_playing( lua_State *L )
+{
+ input_thread_t * p_input = vlclua_get_input_internal( L );
+ lua_pushboolean( L, !!p_input );
+ return 1;
+}
+
+static int vlclua_get_title( lua_State *L )
+{
+ input_thread_t *p_input = vlclua_get_input_internal( L );
+ if( !p_input )
+ lua_pushnil( L );
+ else
+ {
+ lua_pushstring( L, input_GetItem(p_input)->psz_name );
+ vlc_object_release( p_input );
+ }
+ return 1;
+}
+
+/*****************************************************************************
+ * Vout control
+ *****************************************************************************/
+static int vlclua_fullscreen( lua_State *L )
+{
+ vout_thread_t *p_vout;
+ int i_ret;
+
+ input_thread_t * p_input = vlclua_get_input_internal( L );
+ if( !p_input ) return vlclua_error( L );
+
+ p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
+ if( !p_vout ) return vlclua_error( L );
+
+ i_ret = vlclua_var_toggle_or_set( L, p_vout, "fullscreen" );
+ vlc_object_release( p_vout );
+ vlc_object_release( p_input );
+ return i_ret;
+}
+
+static int vlc_osd_icon_from_string( const char *psz_name )
+{
+ static const struct
+ {
+ int i_icon;
+ const char *psz_name;
+ } pp_icons[] =
+ { { OSD_PAUSE_ICON, "pause" },
+ { OSD_PLAY_ICON, "play" },
+ { OSD_SPEAKER_ICON, "speaker" },
+ { OSD_MUTE_ICON, "mute" },
+ { 0, NULL } };
+ int i;
+ for( i = 0; pp_icons[i].psz_name; i++ )
+ {
+ if( !strcmp( psz_name, pp_icons[i].psz_name ) )
+ return pp_icons[i].i_icon;
+ }
+ return 0;
+}
+
+static int vlclua_osd_icon( lua_State *L )
+{
+ const char *psz_icon = luaL_checkstring( L, 1 );
+ int i_icon = vlc_osd_icon_from_string( psz_icon );
+ int i_chan = luaL_optint( L, 2, DEFAULT_CHAN );
+ if( !i_icon )
+ return luaL_error( L, "\"%s\" is not a valid osd icon.", psz_icon );
+ else
+ {
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vout_OSDIcon( p_this, i_chan, i_icon );
+ return 0;
+ }
+}
+
+static int vlclua_osd_message( lua_State *L )
+{
+ const char *psz_message = luaL_checkstring( L, 1 );
+ int i_chan = luaL_optint( L, 2, DEFAULT_CHAN );
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vout_OSDMessage( p_this, i_chan, psz_message );
+ return 0;
+}
+
+static int vlc_osd_slider_type_from_string( const char *psz_name )
+{
+ static const struct
+ {
+ int i_type;
+ const char *psz_name;
+ } pp_types[] =
+ { { OSD_HOR_SLIDER, "horizontal" },
+ { OSD_VERT_SLIDER, "vertical" },
+ { 0, NULL } };
+ int i;
+ for( i = 0; pp_types[i].psz_name; i++ )
+ {
+ if( !strcmp( psz_name, pp_types[i].psz_name ) )
+ return pp_types[i].i_type;
+ }
+ return 0;
+}
+
+static int vlclua_osd_slider( lua_State *L )
+{
+ int i_position = luaL_checkint( L, 1 );
+ const char *psz_type = luaL_checkstring( L, 2 );
+ int i_type = vlc_osd_slider_type_from_string( psz_type );
+ int i_chan = luaL_optint( L, 3, DEFAULT_CHAN );
+ if( !i_type )
+ return luaL_error( L, "\"%s\" is not a valid slider type.",
+ psz_type );
+ else
+ {
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vout_OSDSlider( p_this, i_chan, i_position, i_type );
+ return 0;
+ }
+}
+
+static int vlclua_spu_channel_register( lua_State *L )
+{
+ int i_chan;
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vout_thread_t *p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT,
+ FIND_ANYWHERE );
+ if( !p_vout )
+ return luaL_error( L, "Unable to find vout." );
+
+ spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER, &i_chan );
+ vlc_object_release( p_vout );
+ lua_pushinteger( L, i_chan );
+ return 1;
+}
+
+static int vlclua_spu_channel_clear( lua_State *L )
+{
+ int i_chan = luaL_checkint( L, 1 );
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vout_thread_t *p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT,
+ FIND_ANYWHERE );
+ if( !p_vout )
+ return luaL_error( L, "Unable to find vout." );
+
+ spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, i_chan );
+ vlc_object_release( p_vout );
+ return 0;
+}
+
+/*****************************************************************************
+ * Playlist control
+ *****************************************************************************/
+static int vlclua_get_playlist( lua_State *L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ if( p_playlist )
+ {
+ vlclua_push_vlc_object( L, p_playlist, vlclua_gc_release );
+ }
+ else lua_pushnil( L );
+ return 1;
+}
+
+static int vlclua_playlist_prev( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ playlist_Prev( p_playlist );
+ vlc_object_release( p_playlist );
+ return 0;
+}
+
+static int vlclua_playlist_next( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ playlist_Next( p_playlist );
+ vlc_object_release( p_playlist );
+ return 0;
+}
+
+static int vlclua_playlist_play( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ playlist_Play( p_playlist );
+ vlc_object_release( p_playlist );
+ return 0;
+}
+
+static int vlclua_playlist_stop( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ playlist_Stop( p_playlist );
+ vlc_object_release( p_playlist );
+ return 0;
+}
+
+static int vlclua_playlist_clear( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ playlist_Stop( p_playlist ); /* Isn't this already implied by Clear? */
+ playlist_Clear( p_playlist, VLC_FALSE );
+ vlc_object_release( p_playlist );
+ return 0;
+}
+
+static int vlclua_playlist_repeat( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "repeat" );
+ vlc_object_release( p_playlist );
+ return i_ret;
+}
+
+static int vlclua_playlist_loop( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "loop" );
+ vlc_object_release( p_playlist );
+ return i_ret;
+}
+
+static int vlclua_playlist_random( lua_State * L )
+{
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "random" );
+ vlc_object_release( p_playlist );
+ return i_ret;
+}
+
+static int vlclua_playlist_goto( lua_State * L )
+{
+ /* XXX: logic copied from rc.c ... i'm not sure that it's ok as it
+ * implies knowledge of the playlist internals. */
+ playlist_t *p_playlist;
+ int i_size;
+ playlist_item_t *p_item, *p_parent;
+
+ int i_pos;
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ i_pos = luaL_checkint( L, -1 );
+ lua_pop( L, 1 );
+ if( i_pos <= 0 ) return 0;
+
+ p_playlist = vlclua_get_playlist_internal( L );
+ /* The playlist stores 2 times the same item: onelevel & category */
+ i_size = p_playlist->items.i_size / 2;
+
+ if( i_pos > i_size )
+ {
+ vlc_object_release( p_playlist );
+ return 0;
+ }
+
+ p_item = p_parent = p_playlist->items.p_elems[i_pos*2-1];
+ while( p_parent->p_parent )
+ p_parent = p_parent->p_parent;
+ playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE,
+ p_parent, p_item );
+
+ vlc_object_release( p_playlist );
+ lua_pushboolean( L, 1 );
+ return 1;
+}
+
+static int vlclua_playlist_add( lua_State *L )
+{
+ int i_count;
+ vlc_object_t *p_this = vlclua_get_this( L );
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ i_count = vlclua_playlist_add_internal( p_this, L, p_playlist,
+ NULL, VLC_TRUE );
+ vlc_object_release( p_playlist );
+ lua_pushinteger( L, i_count );
+ return 1;
+}
+
+static int vlclua_playlist_enqueue( lua_State *L )
+{
+ int i_count;
+ vlc_object_t *p_this = vlclua_get_this( L );
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ i_count = vlclua_playlist_add_internal( p_this, L, p_playlist,
+ NULL, VLC_FALSE );
+ vlc_object_release( p_playlist );
+ lua_pushinteger( L, i_count );
+ return 1;
+}
+
+static int vlclua_playlist_get( lua_State *L )
+{
+ /* TODO: make it possible to get the tree playlist */
+ playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+ playlist_item_t *p_root;
+ int i;
+ if( lua_isboolean( L, 1 ) && lua_toboolean( L, 1 ) )
+ p_root = p_playlist->p_ml_onelevel; /* media library */
+ else
+ p_root = p_playlist->p_local_onelevel; /* local/normal playlist */
+ lua_createtable( L, p_root->i_children, 0 );
+ for( i = 0; i < p_root->i_children; i++ )
+ {
+ playlist_item_t *p_item = p_root->pp_children[i];
+ input_item_t *p_input = p_item->p_input;
+ lua_pushinteger( L, i+1 );
+ lua_newtable( L );
+ lua_pushstring( L, p_input->psz_name );
+ lua_setfield( L, -2, "name" );
+ lua_pushstring( L, p_input->psz_uri );
+ lua_setfield( L, -2, "path" );
+ lua_pushnumber( L, ((double)p_input->i_duration)*1e-6 );
+ lua_setfield( L, -2, "duration" );
+ lua_pushinteger( L, p_input->i_nb_played );
+ lua_setfield( L, -2, "nb_played" );
+ /* TODO: add (optional) info categories, meta, options, es */
+ lua_settable( L, -3 );
+ }
+ vlc_object_release( p_playlist );
+ return 1;
+}
+
+static int vlclua_playlist_sort( lua_State *L )
+{
+ /* allow setting the different sort keys */
+ return 0;
+}
+
+/* FIXME: split this in 3 different functions? */
+static int vlclua_playlist_status( lua_State *L )
+{
+ intf_thread_t *p_intf = (intf_thread_t *)vlclua_get_this( L );
+ playlist_t *p_playlist = pl_Yield( p_intf );
+ int i_count = 0;
+ lua_settop( L, 0 );
+ if( p_playlist->p_input )
+ {
+ char *psz_uri =
+ input_item_GetURI( input_GetItem( p_playlist->p_input ) );
+ lua_pushstring( L, psz_uri );
+ free( psz_uri );
+ lua_pushnumber( L, config_GetInt( p_intf, "volume" ) );
+ vlc_mutex_lock( &p_playlist->object_lock );
+ switch( p_playlist->status.i_status )
+ {
+ case PLAYLIST_STOPPED:
+ lua_pushstring( L, "stopped" );
+ break;
+ case PLAYLIST_RUNNING:
+ lua_pushstring( L, "running" );
+ break;
+ case PLAYLIST_PAUSED:
+ lua_pushstring( L, "paused" );
+ break;
+ default:
+ lua_pushstring( L, "unknown" );
+ break;
+ }
+ vlc_mutex_unlock( &p_playlist->object_lock );
+ i_count += 3;
+ }
+ vlc_object_release( p_playlist );
+ return i_count;
+}
+
+
+static int vlclua_lock_and_wait( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ int b_quit = vlc_object_lock_and_wait( p_this );
+ lua_pushboolean( L, b_quit );
+ return 1;
+}
+
+static int vlclua_signal( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vlc_object_signal( p_this );
+ return 0;
+}
+
+static int vlclua_mdate( lua_State *L )
+{
+ lua_pushnumber( L, mdate() );
+ return 1;
+}
+
+static int vlclua_intf_should_die( lua_State *L )
+{
+ intf_thread_t *p_intf = (intf_thread_t*)vlclua_get_this( L );
+ lua_pushboolean( L, intf_ShouldDie( p_intf ) );
+ return 1;
+}
+
+static luaL_Reg p_reg[] =
+{
+ { "input_info", vlclua_input_info },
+ { "is_playing", vlclua_is_playing },
+ { "get_title", vlclua_get_title },
+
+ { "fullscreen", vlclua_fullscreen },
+
+ { "mdate", vlclua_mdate },
+
+ { "module_command", vlclua_module_command },
+ { "libvlc_command", vlclua_libvlc_command },
+
+ { "decode_uri", vlclua_decode_uri },
+ { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
+
+ { "lock_and_wait", vlclua_lock_and_wait },
+ { "signal", vlclua_signal },
+
+ { "version", vlclua_version },
+ { "should_die", vlclua_intf_should_die },
+ { "quit", vlclua_quit },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_object[] =
+{
+ { "input", vlclua_get_input }, /* This is fast */
+ { "playlist", vlclua_get_playlist }, /* This is fast */
+ { "libvlc", vlclua_get_libvlc }, /* This is fast */
+
+ { "find", vlclua_object_find }, /* This is slow */
+ { "find_name", vlclua_object_find_name }, /* This is slow */
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_var[] =
+{
+ { "get", vlclua_var_get },
+ { "get_list", vlclua_var_get_list },
+ { "set", vlclua_var_set },
+ { "add_callback", vlclua_add_callback },
+ { "del_callback", vlclua_del_callback },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_config[] =
+{
+ { "get", vlclua_config_get },
+ { "set", vlclua_config_set },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_msg[] =
+{
+ { "dbg", vlclua_msg_dbg },
+ { "warn", vlclua_msg_warn },
+ { "err", vlclua_msg_err },
+ { "info", vlclua_msg_info },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_playlist[] =
+{
+ { "prev", vlclua_playlist_prev },
+ { "next", vlclua_playlist_next },
+ { "play", vlclua_playlist_play },
+ { "stop", vlclua_playlist_stop },
+ { "clear", vlclua_playlist_clear },
+ { "repeat_", vlclua_playlist_repeat },
+ { "loop", vlclua_playlist_loop },
+ { "random", vlclua_playlist_random },
+ { "goto", vlclua_playlist_goto },
+ { "status", vlclua_playlist_status },
+ { "add", vlclua_playlist_add },
+ { "enqueue", vlclua_playlist_enqueue },
+ { "get", vlclua_playlist_get },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_volume[] =
+{
+ { "get", vlclua_volume_get },
+ { "set", vlclua_volume_set },
+ { "up", vlclua_volume_up },
+ { "down", vlclua_volume_down },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_osd[] =
+{
+ { "icon", vlclua_osd_icon },
+ { "message", vlclua_osd_message },
+ { "slider", vlclua_osd_slider },
+ { "channel_register", vlclua_spu_channel_register },
+ { "channel_clear", vlclua_spu_channel_clear },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_net[] =
+{
+ { "url_parse", vlclua_url_parse },
+ { "listen_tcp", vlclua_net_listen_tcp },
+ { "listen_close", vlclua_net_listen_close },
+ { "accept", vlclua_net_accept },
+ { "close", vlclua_net_close },
+ { "send", vlclua_net_send },
+ { "recv", vlclua_net_recv },
+ { "select", vlclua_net_select },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_fd[] =
+{
+/* { "open", vlclua_fd_open },*/
+ { "read", vlclua_fd_read },
+ { "write", vlclua_fd_write },
+
+ { "new_fd_set", vlclua_fd_set_new },
+ { "fd_clr", vlclua_fd_clr },
+ { "fd_isset", vlclua_fd_isset },
+ { "fd_set", vlclua_fd_set },
+ { "fd_zero", vlclua_fd_zero },
+
+ { NULL, NULL }
+};
+
+static luaL_Reg p_reg_vlm[] =
+{
+ { "new", vlclua_vlm_new },
+ { "delete", vlclua_vlm_delete },
+ { "execute_command", vlclua_vlm_execute_command },
+
+ { NULL, NULL }
+};
+
+
+static void Run( intf_thread_t *p_intf );
+
+static char *FindFile( intf_thread_t *p_intf, const char *psz_name )
+{
+ char *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
+ char **ppsz_dir;
+ vlclua_dir_list( VLC_OBJECT(p_intf), "luaintf", ppsz_dir_list );
+ for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
+ {
+ char *psz_filename;
+ FILE *fp;
+ if( asprintf( &psz_filename, "%s"DIR_SEP"%s.lua", *ppsz_dir,
+ psz_name ) < 0 )
+ {
+ return NULL;
+ }
+ fp = fopen( psz_filename, "r" );
+ if( fp )
+ {
+ fclose( fp );
+ return psz_filename;
+ }
+ free( psz_filename );
+ }
+ return NULL;
+}
+
+static inline void luaL_register_submodule( lua_State *L, const char *psz_name,
+ const luaL_Reg *l )
+{
+ lua_newtable( L );
+ luaL_register( L, NULL, l );
+ lua_setfield( L, -2, psz_name );
+}
+
+static struct
+{
+ const char *psz_shortcut;
+ const char *psz_name;
+} pp_shortcuts[] = {
+ { "luarc", "rc" },
+ /* { "rc", "rc" }, */
+ { "luahotkeys", "hotkeys" },
+ /* { "hotkeys", "hotkeys" }, */
+ { "luatelnet", "telnet" },
+ /* { "telnet", "telnet" }, */
+ { NULL, NULL } };
+
+static vlc_bool_t WordInList( const char *psz_list, const char *psz_word )
+{
+ const char *psz_str = strstr( psz_list, psz_word );
+ int i_len = strlen( psz_word );
+ while( psz_str )
+ {
+ if( (psz_str == psz_list || *(psz_str-1) == ',' )
+ /* it doesn't start in middle of a word */
+ /* it doest end in middle of a word */
+ && ( psz_str[i_len] == '\0' || psz_str[i_len] == ',' ) )
+ return VLC_TRUE;
+ psz_str = strstr( psz_str, psz_word );
+ }
+ return VLC_FALSE;
+}
+
+static const char *GetModuleName( intf_thread_t *p_intf )
+{
+ int i;
+ const char *psz_intf;
+ if( *p_intf->psz_intf == '$' )
+ psz_intf = var_GetString( p_intf, p_intf->psz_intf+1 );
+ else
+ psz_intf = p_intf->psz_intf;
+ for( i = 0; pp_shortcuts[i].psz_name; i++ )
+ {
+ if( WordInList( psz_intf, pp_shortcuts[i].psz_shortcut ) )
+ return pp_shortcuts[i].psz_name;
+ }
+
+ return config_GetPsz( p_intf, "lua-intf" );
+}
+
+int E_(Open_LuaIntf)( vlc_object_t *p_this )
+{
+ intf_thread_t *p_intf = (intf_thread_t*)p_this;
+ intf_sys_t *p_sys;
+ lua_State *L;
+
+ const char *psz_name = GetModuleName( p_intf );
+ const char *psz_config;
+ vlc_bool_t b_config_set = VLC_FALSE;
+ if( !psz_name ) psz_name = "dummy";
+
+ p_intf->p_sys = (intf_sys_t*)malloc( sizeof(intf_sys_t*) );
+ if( !p_intf->p_sys )
+ {
+ return VLC_ENOMEM;
+ }
+ p_sys = p_intf->p_sys;
+ p_sys->psz_filename = FindFile( p_intf, psz_name );
+ if( !p_sys->psz_filename )
+ {
+ msg_Err( p_intf, "Couldn't find lua interface script \"%s\".",
+ psz_name );
+ return VLC_EGENERIC;
+ }
+ msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename );
+
+ L = luaL_newstate();
+ if( !L )
+ {
+ msg_Err( p_intf, "Could not create new Lua State" );
+ free( p_sys );
+ return VLC_EGENERIC;
+ }
+
+ luaL_openlibs( L ); /* FIXME: we don't want to have all the libs */
+
+ /* register our functions */
+ luaL_register( L, "vlc", p_reg );
+ /* store a pointer to p_intf */
+ lua_pushlightuserdata( L, p_intf );
+ lua_setfield( L, -2, "private" );
+ /* register submodules */
+ luaL_register_submodule( L, "object", p_reg_object );
+ luaL_register_submodule( L, "var", p_reg_var );
+ luaL_register_submodule( L, "config", p_reg_config );
+ luaL_register_submodule( L, "msg", p_reg_msg );
+ luaL_register_submodule( L, "playlist", p_reg_playlist );
+ luaL_register_submodule( L, "volume", p_reg_volume );
+ luaL_register_submodule( L, "osd", p_reg_osd );
+ luaL_register_submodule( L, "net", p_reg_net );
+ luaL_register_submodule( L, "fd", p_reg_fd );
+ luaL_register_submodule( L, "vlm", p_reg_vlm );
+ /* clean up */
+ lua_pop( L, 1 );
+
+ /* <gruik> */
+ /* Setup the module search path */
+ {
+ char *psz_command;
+ char *psz_char = strrchr(p_sys->psz_filename,DIR_SEP_CHAR);
+ *psz_char = '\0';
+ /* FIXME: don't use luaL_dostring */
+ if( asprintf( &psz_command,
+ "package.path = \"%s"DIR_SEP"modules"DIR_SEP"?.lua;\"..package.path",
+ p_sys->psz_filename ) < 0 )
+ return VLC_EGENERIC;
+ *psz_char = DIR_SEP_CHAR;
+ if( luaL_dostring( L, psz_command ) )
+ return VLC_EGENERIC;
+ }
+ /* </gruik> */
+
+ psz_config = config_GetPsz( p_intf, "lua-config" );
+ if( psz_config && *psz_config )
+ {
+ char *psz_buffer;
+ if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
+ {
+ printf("%s\n", psz_buffer);
+ if( luaL_dostring( L, psz_buffer ) == 1 )
+ msg_Err( p_intf, "Error while parsing \"lua-config\"." );
+ free( psz_buffer );
+ lua_getglobal( L, "config" );
+ if( lua_istable( L, -1 ) )
+ {
+ lua_getfield( L, -1, psz_name );
+ if( lua_istable( L, -1 ) )
+ {
+ lua_setglobal( L, "config" );
+ b_config_set = VLC_TRUE;
+ }
+ }
+ }
+ }
+ if( b_config_set == VLC_FALSE )
+ {
+ lua_newtable( L );
+ lua_setglobal( L, "config" );
+ }
+
+ p_sys->L = L;
+
+ p_intf->pf_run = Run;
+ p_intf->psz_header = strdup( psz_name ); /* Do I need to clean that up myself in E_(Close_LuaIntf)? */
+
+ return VLC_SUCCESS;
+}
+
+void E_(Close_LuaIntf)( vlc_object_t *p_this )
+{
+ intf_thread_t *p_intf = (intf_thread_t*)p_this;
+
+ lua_close( p_intf->p_sys->L );
+ free( p_intf->p_sys );
+}
+
+static void Run( intf_thread_t *p_intf )
+{
+ lua_State *L = p_intf->p_sys->L;
+
+ if( luaL_dofile( L, p_intf->p_sys->psz_filename ) )
+ {
+ msg_Err( p_intf, "Error loading script %s: %s",
+ p_intf->p_sys->psz_filename,
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
+ p_intf->b_die = VLC_TRUE;
+ return;
+ }
+ p_intf->b_die = VLC_TRUE;
+}
+++ /dev/null
-/*****************************************************************************
- * luaplaylist.c : Lua playlist demux module
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#include <vlc/vlc.h>
-#include <vlc_demux.h>
-#include <vlc_url.h>
-#include <vlc_strings.h>
-#include <vlc_charset.h>
-
-#include <errno.h> /* ENOMEM */
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-
-#include "vlclua.h"
-
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-static int Demux( demux_t *p_demux );
-static int Control( demux_t *p_demux, int i_query, va_list args );
-
-/*****************************************************************************
- *
- *****************************************************************************/
-struct demux_sys_t
-{
- lua_State *p_state;
- char *psz_filename;
-};
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-static int vlclua_demux_peek( lua_State *p_state )
-{
- demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- int n;
- byte_t *p_peek;
- int i_peek;
- if( !i ) return 0;
- n = lua_tonumber( p_state, 1 );
- lua_pop( p_state, i );
- i_peek = stream_Peek( p_demux->s, &p_peek, n );
- lua_pushlstring( p_state, (const char *)p_peek, i_peek );
- return 1;
-}
-
-static int vlclua_demux_read( lua_State *p_state )
-{
- demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- int n;
- byte_t *p_read;
- int i_read;
- if( !i ) return 0;
- n = lua_tonumber( p_state, 1 );
- lua_pop( p_state, i );
- i_read = stream_Read( p_demux->s, &p_read, n );
- lua_pushlstring( p_state, (const char *)p_read, i_read );
- return 1;
-}
-
-static int vlclua_demux_readline( lua_State *p_state )
-{
- demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
- char *psz_line = stream_ReadLine( p_demux->s );
- if( psz_line )
- {
- lua_pushstring( p_state, psz_line );
- free( psz_line );
- }
- else
- {
- lua_pushnil( p_state );
- }
- return 1;
-}
-
-
-/* Functions to register */
-static luaL_Reg p_reg[] =
-{
- { "peek", vlclua_demux_peek },
- { "decode_uri", vlclua_decode_uri },
- { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
- { "msg_dbg", vlclua_msg_dbg },
- { "msg_warn", vlclua_msg_warn },
- { "msg_err", vlclua_msg_err },
- { "msg_info", vlclua_msg_info },
- { NULL, NULL }
-};
-
-/* Functions to register for parse() function call only */
-static luaL_Reg p_reg_parse[] =
-{
- { "read", vlclua_demux_read },
- { "readline", vlclua_demux_readline },
- { NULL, NULL }
-};
-
-/*****************************************************************************
- * Called through lua_scripts_batch_execute to call 'probe' on
- * the script pointed by psz_filename.
- *****************************************************************************/
-static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
- lua_State * p_state, void * user_data )
-{
- demux_t * p_demux = (demux_t *)p_this;
-
- p_demux->p_sys->psz_filename = strdup(psz_filename);
-
- /* Ugly hack to delete previous versions of the probe() and parse()
- * functions. */
- lua_pushnil( p_state );
- lua_pushnil( p_state );
- lua_setglobal( p_state, "probe" );
- lua_setglobal( p_state, "parse" );
-
- /* Load and run the script(s) */
- if( luaL_dofile( p_state, psz_filename ) )
- {
- msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- lua_pop( p_state, 1 );
- return VLC_EGENERIC;
- }
-
- lua_getglobal( p_state, "probe" );
-
- if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
- {
- msg_Warn( p_demux, "Error while runing script %s, "
- "function probe() not found", psz_filename );
- lua_pop( p_state, 1 );
- return VLC_EGENERIC;
- }
-
- if( lua_pcall( p_state, 0, 1, 0 ) )
- {
- msg_Warn( p_demux, "Error while runing script %s, "
- "function probe(): %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- lua_pop( p_state, 1 );
- return VLC_EGENERIC;
- }
-
- if( lua_gettop( p_state ) )
- {
- int i_ret = VLC_EGENERIC;
- if( lua_toboolean( p_state, 1 ) )
- {
- msg_Dbg( p_demux, "Lua playlist script %s's "
- "probe() function was successful", psz_filename );
- i_ret = VLC_SUCCESS;
- }
- lua_pop( p_state, 1 );
-
- return i_ret;
- }
- return VLC_EGENERIC;
-}
-
-/*****************************************************************************
- * Import_LuaPlaylist: main import function
- *****************************************************************************/
-int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
-{
- demux_t *p_demux = (demux_t *)p_this;
- lua_State *p_state;
-
- p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
- if( !p_demux->p_sys )
- {
- return VLC_ENOMEM;
- }
-
- p_demux->p_sys->psz_filename = NULL;
-
- p_demux->pf_control = Control;
- p_demux->pf_demux = Demux;
-
- /* Initialise Lua state structure */
- p_state = luaL_newstate();
- if( !p_state )
- {
- msg_Err( p_demux, "Could not create new Lua State" );
- free( p_demux->p_sys );
- return VLC_EGENERIC;
- }
- p_demux->p_sys->p_state = p_state;
-
- /* Load Lua libraries */
- luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */
-
- luaL_register( p_state, "vlc", p_reg );
- lua_pushlightuserdata( p_state, p_demux );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
- lua_pushstring( p_state, p_demux->psz_path );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "path" );
- lua_pushstring( p_state, p_demux->psz_access );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "access" );
-
- lua_pop( p_state, 1 );
-
- return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript,
- p_state, NULL );
-}
-
-
-
-/*****************************************************************************
- * Deactivate: frees unused data
- *****************************************************************************/
-void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
-{
- demux_t *p_demux = (demux_t *)p_this;
- lua_close( p_demux->p_sys->p_state );
- free( p_demux->p_sys->psz_filename );
- free( p_demux->p_sys );
-}
-
-static inline void read_options( demux_t *p_demux, lua_State *p_state,
- int o, int t, int *pi_options,
- char ***pppsz_options )
-{
- lua_getfield( p_state, o, "options" );
- if( lua_istable( p_state, t ) )
- {
- lua_pushnil( p_state );
- while( lua_next( p_state, t ) )
- {
- if( lua_isstring( p_state, t+2 ) )
- {
- char *psz_option = strdup( lua_tostring( p_state, t+2 ) );
- msg_Dbg( p_demux, "Option: %s", psz_option );
- INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
- psz_option );
- }
- else
- {
- msg_Warn( p_demux, "Option should be a string" );
- }
- lua_pop( p_state, 1 ); /* pop option */
- }
- }
- lua_pop( p_state, 1 ); /* pop "options" */
-}
-
-
-static int Demux( demux_t *p_demux )
-{
- input_item_t *p_input;
- lua_State *p_state = p_demux->p_sys->p_state;
- char *psz_filename = p_demux->p_sys->psz_filename;
- int t;
-
- playlist_t *p_playlist = pl_Yield( p_demux );
- input_thread_t *p_input_thread = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
- input_item_t *p_current_input = input_GetItem( p_input_thread );
-
- luaL_register( p_state, "vlc", p_reg_parse );
-
- lua_getglobal( p_state, "parse" );
-
- if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
- {
- msg_Warn( p_demux, "Error while runing script %s, "
- "function parse() not found", psz_filename );
- E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
- return VLC_EGENERIC;
- }
-
- if( lua_pcall( p_state, 0, 1, 0 ) )
- {
- msg_Warn( p_demux, "Error while runing script %s, "
- "function parse(): %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
- return VLC_EGENERIC;
- }
-
- /* Check that the Lua stack is big enough and grow it if needed.
- * Should be ok since LUA_MINSTACK is 20 but we never know. */
- lua_checkstack( p_state, 8 );
-
- if( ( t = lua_gettop( p_state ) ) )
- {
-
- if( lua_istable( p_state, t ) )
- {
- lua_pushnil( p_state );
- while( lua_next( p_state, t ) )
- {
- if( lua_istable( p_state, t+2 ) )
- {
- lua_getfield( p_state, t+2, "path" );
- if( lua_isstring( p_state, t+3 ) )
- {
- const char *psz_path = NULL;
- const char *psz_name = NULL;
- char **ppsz_options = NULL;
- int i_options = 0;
- mtime_t i_duration = -1;
-
- /* Read path and name */
- psz_path = lua_tostring( p_state, t+3 );
- msg_Dbg( p_demux, "Path: %s", psz_path );
- lua_getfield( p_state, t+2, "name" );
- if( lua_isstring( p_state, t+4 ) )
- {
- psz_name = lua_tostring( p_state, t+4 );
- msg_Dbg( p_demux, "Name: %s", psz_name );
- }
- else
- {
- psz_name = psz_path;
- }
-
- /* Read duration */
- lua_getfield( p_state, t+2, "duration" );
- if( lua_isnumber( p_state, t+5 ) )
- {
- i_duration = (mtime_t)lua_tointeger( p_state, t+5 );
- i_duration *= 1000000;
- }
- lua_pop( p_state, 1 ); /* pop "duration" */
-
- /* Read options */
- read_options( p_demux, p_state, t+2, t+5,
- &i_options, &ppsz_options );
-
- /* Create input item */
- p_input = input_ItemNewExt( p_playlist, psz_path,
- psz_name, i_options,
- (const char **)ppsz_options,
- i_duration );
- lua_pop( p_state, 1 ); /* pop "name" */
-
- /* Read meta data */
- vlclua_read_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4, p_input );
-
- /* Read custom meta data */
- vlclua_read_custom_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4,
- p_input );
-
- /* Append item to playlist */
- input_ItemAddSubItem( p_current_input, p_input );
-
- while( i_options > 0 )
- free( ppsz_options[--i_options] );
- free( ppsz_options );
- }
- else
- {
- msg_Warn( p_demux,
- "Playlist item's path should be a string" );
- }
- lua_pop( p_state, 1 ); /* pop "path" */
- }
- else
- {
- msg_Warn( p_demux, "Playlist item should be a table" );
- }
- lua_pop( p_state, 1 ); /* pop the value, keep the key for
- * the next lua_next() call */
- }
- }
- else
- {
- msg_Warn( p_demux, "Script didn't return a table" );
- }
- }
- else
- {
- msg_Err( p_demux, "Script went completely foobar" );
- }
-
- vlc_object_release( p_input_thread );
- vlc_object_release( p_playlist );
-
- return -1; /* Needed for correct operation of go back */
-}
-
-static int Control( demux_t *p_demux, int i_query, va_list args )
-{
- return VLC_EGENERIC;
-}
/*****************************************************************************
- * luameta.c: Get meta/artwork using lua scripts
+ * meta.c: Get meta/artwork using lua scripts
*****************************************************************************
* Copyright (C) 2007 the VideoLAN team
* $Id$
#include <vlc_stream.h>
#include <vlc_charset.h>
-#include "vlclua.h"
+#include "vlc.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
- lua_State * p_state, void * user_data );
+ lua_State * L, void * user_data );
static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
- lua_State * p_state, void * user_data );
-static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item );
+ lua_State * L, void * user_data );
+static lua_State *vlclua_meta_init( vlc_object_t *p_this,
+ input_item_t * p_item );
/*****************************************************************************
/* Functions to register */
static luaL_Reg p_reg[] =
{
+ /* TODO: make an object out of the stream stuff, so we can do something
+ * like:
+ *
+ * s = vlc.stream_new( "http://www.videolan.org" )
+ * page = s:read( 2^16 )
+ * s:delete()
+ *
+ * instead of (which could be problematic since we don't check if
+ * s is really a stream object):
+ *
+ * s = vlc.stream_new( "http://www.videolan.org" )
+ * page = vlc.stream_read( s, 2^16 )
+ * vlc.stream_delete( s )
+ */
{ "stream_new", vlclua_stream_new },
{ "stream_read", vlclua_stream_read },
{ "stream_readline", vlclua_stream_readline },
*****************************************************************************/
static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item )
{
- lua_State * p_state = luaL_newstate();
- if( !p_state )
+ lua_State * L = luaL_newstate();
+ if( !L )
{
msg_Err( p_this, "Could not create new Lua State" );
return NULL;
char *psz_meta;
/* Load Lua libraries */
- luaL_openlibs( p_state ); /* XXX: Don't open all the libs? */
+ luaL_openlibs( L ); /* XXX: Don't open all the libs? */
- luaL_register( p_state, "vlc", p_reg );
+ luaL_register( L, "vlc", p_reg );
- lua_pushlightuserdata( p_state, p_this );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
+ lua_pushlightuserdata( L, p_this );
+ lua_setfield( L, -2, "private" );
psz_meta = input_item_GetName( p_item );
- lua_pushstring( p_state, psz_meta );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "name" );
+ lua_pushstring( L, psz_meta );
+ lua_setfield( L, -2, "name" );
free( psz_meta );
psz_meta = input_item_GetArtist( p_item );
- lua_pushstring( p_state, psz_meta );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "artist" );
+ lua_pushstring( L, psz_meta );
+ lua_setfield( L, -2, "artist" );
free( psz_meta );
psz_meta = input_item_GetTitle( p_item ) ;
- lua_pushstring( p_state, psz_meta );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "title" );
+ lua_pushstring( L, psz_meta );
+ lua_setfield( L, -2, "title" );
free( psz_meta );
psz_meta = input_item_GetAlbum( p_item );
- lua_pushstring( p_state, psz_meta );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "album" );
+ lua_pushstring( L, psz_meta );
+ lua_setfield( L, -2, "album" );
free( psz_meta );
psz_meta = input_item_GetArtURL( p_item );
- lua_pushstring( p_state, psz_meta );
- lua_setfield( p_state, lua_gettop( p_state ) - 1, "arturl" );
+ lua_pushstring( L, psz_meta );
+ lua_setfield( L, -2, "arturl" );
free( psz_meta );
/* XXX: all should be passed ( could use macro ) */
- return p_state;
+ return L;
}
/*****************************************************************************
* pointed by psz_filename.
*****************************************************************************/
static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
- lua_State * p_state, void * user_data )
+ lua_State * L, void * user_data )
{
int i_ret = VLC_EGENERIC;
input_item_t * p_input = user_data;
/* Ugly hack to delete previous versions of the fetchart()
* functions. */
- lua_pushnil( p_state );
- lua_setglobal( p_state, "fetch_art" );
+ lua_pushnil( L );
+ lua_setglobal( L, "fetch_art" );
/* Load and run the script(s) */
- if( luaL_dofile( p_state, psz_filename ) )
+ if( luaL_dofile( L, psz_filename ) )
{
msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- lua_pop( p_state, 1 );
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
return VLC_EGENERIC;
}
- lua_getglobal( p_state, "fetch_art" );
+ lua_getglobal( L, "fetch_art" );
- if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
+ if( !lua_isfunction( L, lua_gettop( L ) ) )
{
msg_Warn( p_this, "Error while runing script %s, "
"function fetch_art() not found", psz_filename );
- lua_pop( p_state, 1 );
+ lua_pop( L, 1 );
return VLC_EGENERIC;
}
- if( lua_pcall( p_state, 0, 1, 0 ) )
+ if( lua_pcall( L, 0, 1, 0 ) )
{
msg_Warn( p_this, "Error while runing script %s, "
"function fetch_art(): %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- lua_pop( p_state, 1 );
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
return VLC_EGENERIC;
}
- if((s = lua_gettop( p_state )))
+ if((s = lua_gettop( L )))
{
const char * psz_value;
- if( lua_isstring( p_state, s ) )
+ if( lua_isstring( L, s ) )
{
- psz_value = lua_tostring( p_state, s );
+ psz_value = lua_tostring( L, s );
if( psz_value && *psz_value != 0 )
{
lua_Dbg( p_this, "setting arturl: %s", psz_value );
i_ret = VLC_SUCCESS;
}
}
- else if( !lua_isnil( p_state, s ) )
+ else if( !lua_isnil( L, s ) )
{
msg_Err( p_this, "Lua art fetcher script %s: "
"didn't return a string", psz_filename );
* pointed by psz_filename.
*****************************************************************************/
static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
- lua_State * p_state, void * user_data )
+ lua_State * L, void * user_data )
{
input_item_t * p_input = user_data;
- int t;
- /* Ugly hack to delete previous versions of the fetchmeta()
- * functions. */
- lua_pushnil( p_state );
- lua_setglobal( p_state, "fetch_meta" );
+ /* In lua, setting a variable to nil is equivalent to deleting it */
+ lua_pushnil( L );
+ lua_setglobal( L, "fetch_meta" );
/* Load and run the script(s) */
- if( luaL_dofile( p_state, psz_filename ) )
+ if( luaL_dofile( L, psz_filename ) )
{
msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- lua_pop( p_state, 1 );
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
return VLC_EGENERIC;
}
- lua_getglobal( p_state, "fetch_meta" );
+ lua_getglobal( L, "fetch_meta" );
- if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
+ if( !lua_isfunction( L, lua_gettop( L ) ) )
{
msg_Warn( p_this, "Error while runing script %s, "
"function fetch_meta() not found", psz_filename );
- lua_pop( p_state, 1 );
+ lua_pop( L, 1 );
return VLC_EGENERIC;
}
- if( lua_pcall( p_state, 0, 1, 0 ) )
+ if( lua_pcall( L, 0, 1, 0 ) )
{
msg_Warn( p_this, "Error while runing script %s, "
"function fetch_meta(): %s", psz_filename,
- lua_tostring( p_state, lua_gettop( p_state ) ) );
- lua_pop( p_state, 1 );
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
return VLC_EGENERIC;
}
- if((t = lua_gettop( p_state )))
+ if( lua_gettop( L ) )
{
- if( lua_istable( p_state, t ) )
+ if( lua_istable( L, -1 ) )
{
- vlclua_read_meta_data( p_this, p_state, t, t+1, p_input );
- vlclua_read_custom_meta_data( p_this, p_state, t, t+1, p_input );
+ /* ... item */
+ vlclua_read_meta_data( p_this, L, p_input );
+ vlclua_read_custom_meta_data( p_this, L, p_input );
}
else
{
msg_Err( p_this, "Lua playlist script %s: "
- "didn't return a table", psz_filename );
+ "didn't return a table", psz_filename );
}
}
else
{
meta_engine_t *p_me = (meta_engine_t *)p_this;
input_item_t *p_item = p_me->p_item;
- lua_State *p_state = vlclua_meta_init( p_this, p_item );
+ lua_State *L = vlclua_meta_init( p_this, p_item );
- int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, p_state, p_item );
- lua_close( p_state );
+ int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, L, p_item );
+ lua_close( L );
return i_ret;
}
{
playlist_t *p_playlist = (playlist_t *)p_this;
input_item_t *p_item = (input_item_t *)(p_playlist->p_private);
- lua_State *p_state = vlclua_meta_init( p_this, p_item );
+ lua_State *L = vlclua_meta_init( p_this, p_item );
- int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, p_state, p_item );
- lua_close( p_state );
+ int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, L, p_item );
+ lua_close( L );
return i_ret;
}
--- /dev/null
+/*****************************************************************************
+ * net.c: Network related functions
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_network.h>
+#include <vlc_url.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+int vlclua_url_parse( lua_State *L )
+{
+ const char *psz_url = luaL_checkstring( L, 1 );
+ const char *psz_option = luaL_optstring( L, 2, NULL );
+ vlc_url_t url;
+
+ vlc_UrlParse( &url, psz_url, psz_option?*psz_option:0 );
+
+ lua_newtable( L );
+ lua_pushstring( L, url.psz_protocol );
+ lua_setfield( L, -2, "protocol" );
+ lua_pushstring( L, url.psz_username );
+ lua_setfield( L, -2, "username" );
+ lua_pushstring( L, url.psz_password );
+ lua_setfield( L, -2, "password" );
+ lua_pushstring( L, url.psz_host );
+ lua_setfield( L, -2, "host" );
+ lua_pushinteger( L, url.i_port );
+ lua_setfield( L, -2, "port" );
+ lua_pushstring( L, url.psz_path );
+ lua_setfield( L, -2, "path" );
+ lua_pushstring( L, url.psz_option );
+ lua_setfield( L, -2, "option" );
+
+ vlc_UrlClean( &url );
+
+ return 1;
+}
+
+int vlclua_net_listen_tcp( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ const char *psz_host = luaL_checkstring( L, 1 );
+ int i_port = luaL_checkint( L, 2 );
+ int *pi_fd = net_ListenTCP( p_this, psz_host, i_port );
+ if( pi_fd == NULL )
+ return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
+ lua_pushlightuserdata( L, pi_fd );
+ return 1;
+}
+
+int vlclua_net_listen_close( lua_State *L )
+{
+ int *pi_fd = (int*)luaL_checklightuserdata( L, 1 );
+ net_ListenClose( pi_fd );
+ return 0;
+}
+
+int vlclua_net_accept( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ int *pi_fd = (int*)luaL_checklightuserdata( L, 1 );
+ mtime_t i_wait = luaL_optint( L, 2, -1 ); /* default to block */
+ int i_fd = net_Accept( p_this, pi_fd, i_wait );
+ lua_pushinteger( L, i_fd );
+ return 1;
+}
+
+int vlclua_net_close( lua_State *L )
+{
+ int i_fd = luaL_checkint( L, 1 );
+ net_Close( i_fd );
+ return 0;
+}
+
+int vlclua_net_send( lua_State *L )
+{
+ int i_fd = luaL_checkint( L, 1 );
+ size_t i_len;
+ const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
+ i_len = luaL_optint( L, 3, i_len );
+ i_len = send( i_fd, psz_buffer, i_len, 0 );
+ lua_pushinteger( L, i_len );
+ return 1;
+}
+
+int vlclua_net_recv( lua_State *L )
+{
+ int i_fd = luaL_checkint( L, 1 );
+ size_t i_len = luaL_optint( L, 2, 1 );
+ char psz_buffer[i_len];
+ i_len = recv( i_fd, psz_buffer, i_len, 0 );
+ lua_pushlstring( L, psz_buffer, i_len );
+ return 1;
+}
+
+int vlclua_net_select( lua_State *L )
+{
+ int i_ret;
+ int i_nfds = luaL_checkint( L, 1 );
+ fd_set *fds_read = (fd_set*)luaL_checkuserdata( L, 2, sizeof( fd_set ) );
+ fd_set *fds_write = (fd_set*)luaL_checkuserdata( L, 3, sizeof( fd_set ) );
+ double f_timeout = luaL_checknumber( L, 4 );
+ struct timeval timeout;
+ timeout.tv_sec = (int)f_timeout;
+ timeout.tv_usec = (int)(1e6*(f_timeout-(double)((int)f_timeout)));
+ i_ret = select( i_nfds, fds_read, fds_write, 0, &timeout );
+ lua_pushinteger( L, i_ret );
+ lua_pushinteger( L, (double)timeout.tv_sec+((double)timeout.tv_usec)/1e-6 );
+ return 2;
+}
+
+int vlclua_fd_set_new( lua_State *L )
+{
+ fd_set *fds = (fd_set*)lua_newuserdata( L, sizeof( fd_set ) );
+ FD_ZERO( fds );
+ return 1;
+}
+
+int vlclua_fd_clr( lua_State *L )
+{
+ fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+ int i_fd = luaL_checkint( L, 2 );
+ FD_CLR( i_fd, fds );
+ return 0;
+}
+int vlclua_fd_isset( lua_State *L )
+{
+ fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+ int i_fd = luaL_checkint( L, 2 );
+ lua_pushboolean( L, FD_ISSET( i_fd, fds ) );
+ return 1;
+}
+int vlclua_fd_set( lua_State *L )
+{
+ fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+ int i_fd = luaL_checkint( L, 2 );
+ FD_SET( i_fd, fds );
+ return 0;
+}
+int vlclua_fd_zero( lua_State *L )
+{
+ fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+ FD_ZERO( fds );
+ return 0;
+}
+
+/*
+int vlclua_fd_open( lua_State *L )
+{
+}
+*/
+
+int vlclua_fd_write( lua_State *L )
+{
+ int i_fd = luaL_checkint( L, 1 );
+ size_t i_len;
+ ssize_t i_ret;
+ const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
+ i_len = luaL_optint( L, 3, i_len );
+ i_ret = write( i_fd, psz_buffer, i_len );
+ lua_pushinteger( L, i_ret );
+ return 1;
+}
+
+int vlclua_fd_read( lua_State *L )
+{
+ int i_fd = luaL_checkint( L, 1 );
+ size_t i_len = luaL_optint( L, 2, 1 );
+ char psz_buffer[i_len];
+ i_len = read( i_fd, psz_buffer, i_len );
+ lua_pushlstring( L, psz_buffer, i_len );
+ return 1;
+}
--- /dev/null
+/*****************************************************************************
+ * objects.c: Generic lua<->vlc object wrapper
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+typedef struct
+{
+ vlc_object_t *p_obj;
+} vlclua_object_t;
+
+/*****************************************************************************
+ * Generic vlc_object_t wrapper creation
+ *****************************************************************************/
+int __vlclua_push_vlc_object( lua_State *L, vlc_object_t *p_obj,
+ lua_CFunction pf_gc )
+{
+ vlclua_object_t *p_ud = (vlclua_object_t *)
+ lua_newuserdata( L, sizeof( vlclua_object_t ) );
+ p_ud->p_obj = p_obj;
+ lua_newtable( L );
+ /* Hide the metatable */
+ lua_pushstring( L, "__metatable" );
+ lua_pushstring( L, "none of your business" );
+ lua_settable( L, -3 );
+ if( pf_gc )
+ {
+ /* Set the garbage collector if needed */
+ lua_pushstring( L, "__gc" );
+ lua_pushcfunction( L, pf_gc );
+ lua_settable( L, -3 );
+ }
+ lua_setmetatable( L, -2 );
+ return 1;
+}
+
+int vlclua_gc_release( lua_State *L )
+{
+ vlclua_object_t *p_ud = (vlclua_object_t *)lua_touserdata( L, -1 );
+ lua_pop( L, 1 );
+ vlc_object_release( p_ud->p_obj );
+ return 0;
+}
+
+vlc_object_t *vlclua_checkobject( lua_State *L, int narg, int i_type )
+{
+ vlclua_object_t *p_obj = (vlclua_object_t *)luaL_checkuserdata( L, narg, sizeof( vlclua_object_t ) );
+ /* TODO: add some metatable based method to check that this isn't
+ * any userdata, but really some vlc object */
+ if( i_type )
+ {
+ if( p_obj->p_obj->i_object_type == i_type )
+ return p_obj->p_obj;
+ else
+ {
+ luaL_error( L, "VLC object type doesn't match requirements." );
+ return NULL; /* luaL_error alread longjmp-ed out of here.
+ * This is to make gcc happy */
+ }
+ }
+ else
+ {
+ return p_obj->p_obj;
+ }
+}
+
+static int vlc_object_type_from_string( const char *psz_name )
+{
+ static const struct
+ {
+ int i_type;
+ const char *psz_name;
+ } pp_objects[] =
+ { { VLC_OBJECT_GLOBAL, "global" },
+ { VLC_OBJECT_LIBVLC, "libvlc" },
+ { VLC_OBJECT_MODULE, "module" },
+ { VLC_OBJECT_INTF, "intf" },
+ { VLC_OBJECT_PLAYLIST, "playlist" },
+ { VLC_OBJECT_ITEM, "item" },
+ { VLC_OBJECT_INPUT, "input" },
+ { VLC_OBJECT_DECODER, "decoder" },
+ { VLC_OBJECT_VOUT, "vout" },
+ { VLC_OBJECT_AOUT, "aout" },
+ { VLC_OBJECT_SOUT, "sout" },
+ { VLC_OBJECT_HTTPD, "httpd" },
+ { VLC_OBJECT_PACKETIZER, "packetizer" },
+ { VLC_OBJECT_ENCODER, "encoder" },
+ { VLC_OBJECT_DIALOGS, "dialogs" },
+ { VLC_OBJECT_VLM, "vlm" },
+ { VLC_OBJECT_ANNOUNCE, "announce" },
+ { VLC_OBJECT_DEMUX, "demux" },
+ { VLC_OBJECT_ACCESS, "access" },
+ { VLC_OBJECT_STREAM, "stream" },
+ { VLC_OBJECT_OPENGL, "opengl" },
+ { VLC_OBJECT_FILTER, "filter" },
+ { VLC_OBJECT_VOD, "vod" },
+ { VLC_OBJECT_SPU, "spu" },
+ { VLC_OBJECT_SD, "sd" },
+ { VLC_OBJECT_XML, "xml" },
+ { VLC_OBJECT_OSDMENU, "osdmenu" },
+ { VLC_OBJECT_HTTPD_HOST, "httpd_host" },
+ { VLC_OBJECT_META_ENGINE, "meta_engine" },
+ { VLC_OBJECT_GENERIC, "generic" },
+ { 0, "" } };
+ int i;
+ for( i = 0; pp_objects[i].i_type; i++ )
+ {
+ if( !strcmp( psz_name, pp_objects[i].psz_name ) )
+ return pp_objects[i].i_type;
+ }
+ return 0;
+}
+
+static int vlc_object_search_mode_from_string( const char *psz_name )
+{
+ static const struct
+ {
+ int i_mode;
+ const char *psz_name;
+ } pp_modes[] =
+ { { FIND_PARENT, "parent" },
+ { FIND_CHILD, "child" },
+ { FIND_ANYWHERE, "anywhere" },
+ { 0, "" } };
+ int i;
+ for( i = 0; pp_modes[i].i_mode; i++ )
+ {
+ if( !strcmp( psz_name, pp_modes[i].psz_name ) )
+ return pp_modes[i].i_mode;
+ }
+ return 0;
+}
+
+int vlclua_object_find( lua_State *L )
+{
+ const char *psz_type = luaL_checkstring( L, 2 );
+ const char *psz_mode = luaL_checkstring( L, 3 );
+
+ vlc_object_t *p_this;
+ int i_type = vlc_object_type_from_string( psz_type );
+ int i_mode = vlc_object_search_mode_from_string( psz_mode );
+ vlc_object_t *p_result;
+
+ if( !i_type )
+ return luaL_error( L, "\"%s\" is not a valid object type.", psz_type );
+ if( !i_mode )
+ return luaL_error( L, "\"%s\" is not a valid search mode.", psz_mode );
+
+ if( lua_type( L, 1 ) == LUA_TNIL )
+ p_this = vlclua_get_this( L );
+ else
+ p_this = vlclua_checkobject( L, 1, 0 );
+
+ p_result = vlc_object_find( p_this, i_type, i_mode );
+ if( !p_result )
+ lua_pushnil( L );
+ else
+ vlclua_push_vlc_object( L, p_result, vlclua_gc_release );
+ return 1;
+}
+
+int vlclua_object_find_name( lua_State *L )
+{
+ const char *psz_name = luaL_checkstring( L, 2 );
+ const char *psz_mode = luaL_checkstring( L, 3 );
+
+ vlc_object_t *p_this;
+ int i_mode = vlc_object_search_mode_from_string( psz_mode );
+ vlc_object_t *p_result;
+
+ if( !i_mode )
+ return luaL_error( L, "\"%s\" is not a valid search mode.",
+ psz_mode );
+
+ if( lua_type( L, 1 ) == LUA_TNIL )
+ p_this = vlclua_get_this( L );
+ else
+ p_this = vlclua_checkobject( L, 1, 0 );
+
+ p_result = vlc_object_find_name( p_this, psz_name, i_mode );
+ if( !p_result )
+ lua_pushnil( L );
+ else
+ vlclua_push_vlc_object( L, p_result, vlclua_gc_release );
+ return 1;
+}
--- /dev/null
+/*****************************************************************************
+ * playlist.c : Lua playlist demux module
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <vlc/vlc.h>
+#include <vlc_demux.h>
+#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_charset.h>
+
+#include <errno.h> /* ENOMEM */
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#include "vlc.h"
+
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Demux( demux_t *p_demux );
+static int Control( demux_t *p_demux, int i_query, va_list args );
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+struct demux_sys_t
+{
+ lua_State *L;
+ char *psz_filename;
+};
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+static int vlclua_demux_peek( lua_State *L )
+{
+ demux_t *p_demux = (demux_t *)vlclua_get_this( L );
+ int i = lua_gettop( L );
+ int n;
+ const uint8_t *p_peek;
+ int i_peek;
+ if( !i ) return 0;
+ n = lua_tonumber( L, 1 );
+ lua_pop( L, i );
+ i_peek = stream_Peek( p_demux->s, &p_peek, n );
+ lua_pushlstring( L, (const char *)p_peek, i_peek );
+ return 1;
+}
+
+static int vlclua_demux_read( lua_State *L )
+{
+ demux_t *p_demux = (demux_t *)vlclua_get_this( L );
+ int i = lua_gettop( L );
+ int n;
+ byte_t *p_read;
+ int i_read;
+ if( !i ) return 0;
+ n = lua_tonumber( L, 1 );
+ lua_pop( L, i );
+ i_read = stream_Read( p_demux->s, &p_read, n );
+ lua_pushlstring( L, (const char *)p_read, i_read );
+ return 1;
+}
+
+static int vlclua_demux_readline( lua_State *L )
+{
+ demux_t *p_demux = (demux_t *)vlclua_get_this( L );
+ char *psz_line = stream_ReadLine( p_demux->s );
+ if( psz_line )
+ {
+ lua_pushstring( L, psz_line );
+ free( psz_line );
+ }
+ else
+ {
+ lua_pushnil( L );
+ }
+ return 1;
+}
+
+
+/* Functions to register */
+static luaL_Reg p_reg[] =
+{
+ { "peek", vlclua_demux_peek },
+ { "decode_uri", vlclua_decode_uri },
+ { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
+ { "msg_dbg", vlclua_msg_dbg },
+ { "msg_warn", vlclua_msg_warn },
+ { "msg_err", vlclua_msg_err },
+ { "msg_info", vlclua_msg_info },
+ { NULL, NULL }
+};
+
+/* Functions to register for parse() function call only */
+static luaL_Reg p_reg_parse[] =
+{
+ { "read", vlclua_demux_read },
+ { "readline", vlclua_demux_readline },
+ { NULL, NULL }
+};
+
+/*****************************************************************************
+ * Called through lua_scripts_batch_execute to call 'probe' on
+ * the script pointed by psz_filename.
+ *****************************************************************************/
+static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
+ lua_State * L, void * user_data )
+{
+ demux_t * p_demux = (demux_t *)p_this;
+
+ p_demux->p_sys->psz_filename = strdup(psz_filename);
+
+ /* In lua, setting a variable's value to nil is equivalent to deleting it */
+ lua_pushnil( L );
+ lua_pushnil( L );
+ lua_setglobal( L, "probe" );
+ lua_setglobal( L, "parse" );
+
+ /* Load and run the script(s) */
+ if( luaL_dofile( L, psz_filename ) )
+ {
+ msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
+ return VLC_EGENERIC;
+ }
+
+ lua_getglobal( L, "probe" );
+
+ if( !lua_isfunction( L, lua_gettop( L ) ) )
+ {
+ msg_Warn( p_demux, "Error while runing script %s, "
+ "function probe() not found", psz_filename );
+ lua_pop( L, 1 );
+ return VLC_EGENERIC;
+ }
+
+ if( lua_pcall( L, 0, 1, 0 ) )
+ {
+ msg_Warn( p_demux, "Error while runing script %s, "
+ "function probe(): %s", psz_filename,
+ lua_tostring( L, lua_gettop( L ) ) );
+ lua_pop( L, 1 );
+ return VLC_EGENERIC;
+ }
+
+ if( lua_gettop( L ) )
+ {
+ int i_ret = VLC_EGENERIC;
+ if( lua_toboolean( L, 1 ) )
+ {
+ msg_Dbg( p_demux, "Lua playlist script %s's "
+ "probe() function was successful", psz_filename );
+ i_ret = VLC_SUCCESS;
+ }
+ lua_pop( L, 1 );
+
+ return i_ret;
+ }
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Import_LuaPlaylist: main import function
+ *****************************************************************************/
+int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ lua_State *L;
+
+ p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
+ if( !p_demux->p_sys )
+ {
+ return VLC_ENOMEM;
+ }
+
+ p_demux->p_sys->psz_filename = NULL;
+
+ p_demux->pf_control = Control;
+ p_demux->pf_demux = Demux;
+
+ /* Initialise Lua state structure */
+ L = luaL_newstate();
+ if( !L )
+ {
+ msg_Err( p_demux, "Could not create new Lua State" );
+ free( p_demux->p_sys );
+ return VLC_EGENERIC;
+ }
+ p_demux->p_sys->L = L;
+
+ /* Load Lua libraries */
+ luaL_openlibs( L ); /* FIXME: Don't open all the libs? */
+
+ luaL_register( L, "vlc", p_reg );
+ lua_pushlightuserdata( L, p_demux );
+ lua_setfield( L, -2, "private" );
+ lua_pushstring( L, p_demux->psz_path );
+ lua_setfield( L, -2, "path" );
+ lua_pushstring( L, p_demux->psz_access );
+ lua_setfield( L, -2, "access" );
+
+ lua_pop( L, 1 );
+
+ return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript,
+ L, NULL );
+}
+
+
+
+/*****************************************************************************
+ * Deactivate: frees unused data
+ *****************************************************************************/
+void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ lua_close( p_demux->p_sys->L );
+ free( p_demux->p_sys->psz_filename );
+ free( p_demux->p_sys );
+}
+
+static int Demux( demux_t *p_demux )
+{
+ lua_State *L = p_demux->p_sys->L;
+ char *psz_filename = p_demux->p_sys->psz_filename;
+
+ playlist_t *p_playlist = pl_Yield( p_demux );
+ input_thread_t *p_input_thread = (input_thread_t *)
+ vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
+ input_item_t *p_current_input = input_GetItem( p_input_thread );
+
+ luaL_register( L, "vlc", p_reg_parse );
+
+ lua_getglobal( L, "parse" );
+
+ if( !lua_isfunction( L, lua_gettop( L ) ) )
+ {
+ msg_Warn( p_demux, "Error while runing script %s, "
+ "function parse() not found", psz_filename );
+ E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
+ return VLC_EGENERIC;
+ }
+
+ if( lua_pcall( L, 0, 1, 0 ) )
+ {
+ msg_Warn( p_demux, "Error while runing script %s, "
+ "function parse(): %s", psz_filename,
+ lua_tostring( L, lua_gettop( L ) ) );
+ E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
+ return VLC_EGENERIC;
+ }
+
+ if( lua_gettop( L ) )
+ vlclua_playlist_add_internal( p_demux, L, p_playlist,
+ p_current_input, 0 );
+ else
+ msg_Err( p_demux, "Script went completely foobar" );
+
+ vlc_object_release( p_input_thread );
+ vlc_object_release( p_playlist );
+
+ return -1; /* Needed for correct operation of go back */
+}
+
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+ return VLC_EGENERIC;
+}
--- /dev/null
+/*****************************************************************************
+ * variables.c: Generic lua<->vlc variables inteface
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ * Variables handling
+ *****************************************************************************/
+int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val )
+{
+ switch( i_type &= 0xf0 )
+ {
+ case VLC_VAR_VOID:
+ vlclua_error( L );
+ break;
+ case VLC_VAR_BOOL:
+ lua_pushboolean( L, val.b_bool );
+ break;
+ case VLC_VAR_INTEGER:
+ lua_pushinteger( L, val.i_int );
+ break;
+ case VLC_VAR_STRING:
+ lua_pushstring( L, val.psz_string );
+ break;
+ case VLC_VAR_FLOAT:
+ lua_pushnumber( L, val.f_float );
+ break;
+ case VLC_VAR_TIME:
+ /* FIXME? (we're losing some precision, but does it really matter?) */
+ lua_pushnumber( L, ((double)val.i_time)/1000000. );
+ break;
+ case VLC_VAR_ADDRESS:
+ vlclua_error( L );
+ break;
+ case VLC_VAR_MUTEX:
+ vlclua_error( L );
+ break;
+ case VLC_VAR_LIST:
+ {
+ int i_count = val.p_list->i_count;
+ int i;
+ lua_createtable( L, i_count, 0 );
+ for( i = 0; i < i_count; i++ )
+ {
+ lua_pushinteger( L, i+1 );
+ if( !vlclua_pushvalue( L, val.p_list->pi_types[i],
+ val.p_list->p_values[i] ) )
+ lua_pushnil( L );
+ lua_settable( L, -3 );
+ }
+ }
+ break;
+ default:
+ vlclua_error( L );
+ }
+ return 1;
+}
+
+static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
+{
+ switch( i_type & 0xf0 )
+ {
+ case VLC_VAR_VOID:
+ break;
+ case VLC_VAR_BOOL:
+ val->b_bool = luaL_checkboolean( L, -1 );
+ break;
+ case VLC_VAR_INTEGER:
+ val->i_int = luaL_checkint( L, -1 );
+ break;
+ case VLC_VAR_STRING:
+ val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
+ break;
+ case VLC_VAR_FLOAT:
+ val->f_float = luaL_checknumber( L, -1 );
+ break;
+ case VLC_VAR_TIME:
+ {
+ double f = luaL_checknumber( L, -1 );
+ val->i_time = (vlc_int64_t)(f*1000000.);
+ }
+ break;
+ case VLC_VAR_ADDRESS:
+ vlclua_error( L );
+ break;
+ case VLC_VAR_MUTEX:
+ vlclua_error( L );
+ break;
+ case VLC_VAR_LIST:
+ vlclua_error( L );
+ break;
+ default:
+ vlclua_error( L );
+ }
+ return 1;
+}
+
+int vlclua_var_get( lua_State *L )
+{
+ int i_type;
+ vlc_value_t val;
+ vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+ const char *psz_var = luaL_checkstring( L, 2 );
+ i_type = var_Type( p_obj, psz_var );
+ var_Get( p_obj, psz_var, &val );
+ lua_pop( L, 2 );
+ return vlclua_pushvalue( L, i_type, val );
+}
+
+int vlclua_var_set( lua_State *L )
+{
+ int i_type;
+ vlc_value_t val;
+ vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+ const char *psz_var = luaL_checkstring( L, 2 );
+ int i_ret;
+ i_type = var_Type( p_obj, psz_var );
+ vlclua_tovalue( L, i_type, &val );
+ i_ret = var_Set( p_obj, psz_var, val );
+ lua_pop( L, 3 );
+ return vlclua_push_ret( L, i_ret );
+}
+
+int vlclua_var_get_list( lua_State *L )
+{
+ vlc_value_t val;
+ vlc_value_t text;
+ vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+ const char *psz_var = luaL_checkstring( L, 2 );
+ int i_ret = var_Change( p_obj, psz_var, VLC_VAR_GETLIST, &val, &text );
+ if( i_ret < 0 ) return vlclua_push_ret( L, i_ret );
+ vlclua_pushvalue( L, VLC_VAR_LIST, val );
+ vlclua_pushvalue( L, VLC_VAR_LIST, text );
+ var_Change( p_obj, psz_var, VLC_VAR_FREELIST, &val, &text );
+ return 2;
+}
+
+int vlclua_module_command( lua_State *L )
+{
+ vlc_object_t * p_this = vlclua_get_this( L );
+ const char *psz_name;
+ const char *psz_cmd;
+ const char *psz_arg;
+ char *psz_msg;
+ psz_name = luaL_checkstring( L, 1 );
+ psz_cmd = luaL_checkstring( L, 2 );
+ psz_arg = luaL_checkstring( L, 3 );
+ lua_pop( L, 3 );
+ var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
+ if( psz_msg )
+ {
+ lua_pushstring( L, psz_msg );
+ free( psz_msg );
+ }
+ else
+ {
+ lua_pushstring( L, "" );
+ }
+ return 1;
+}
+
+int vlclua_libvlc_command( lua_State *L )
+{
+ vlc_object_t * p_this = vlclua_get_this( L );
+ const char *psz_cmd;
+ vlc_value_t val_arg;
+ psz_cmd = luaL_checkstring( L, 1 );
+ val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
+ lua_pop( L, 2 );
+ if( !var_Type( p_this->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
+ {
+ free( val_arg.psz_string );
+ return luaL_error( L, "libvlc's \"%s\" is not a command",
+ psz_cmd );
+ }
+
+ return vlclua_push_ret( L,
+ var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
+}
--- /dev/null
+/*****************************************************************************
+ * vlc.c: Generic lua inteface functions
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ * Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_meta.h>
+#include <vlc_charset.h>
+#include <vlc_aout.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+#define INTF_TEXT N_("Lua interface")
+#define INTF_LONGTEXT N_("Lua interface module to load")
+
+#define CONFIG_TEXT N_("Lua inteface configuration")
+#define CONFIG_LONGTEXT N_("Lua interface configuration string. Format is: '[\"<interface module name>\"] = { <option> = <value>, ...}, ...'.")
+
+vlc_module_begin();
+ add_shortcut( "luameta" );
+ set_shortname( N_( "Lua Meta" ) );
+ set_description( _("Fetch metadata using lua scripts") );
+ set_capability( "meta fetcher", 10 );
+ set_callbacks( E_(FindMeta), NULL );
+ add_submodule();
+ set_shortname( N_( "Lua Art" ) );
+ set_description( _("Fetch artwork using lua scripts") );
+ set_capability( "art finder", 10 );
+ set_callbacks( E_(FindArt), NULL );
+ add_submodule();
+ add_shortcut( "luaplaylist" );
+ set_category( CAT_INPUT );
+ set_subcategory( SUBCAT_INPUT_DEMUX );
+ set_shortname( _("Lua Playlist") );
+ set_description( _("Lua Playlist Parser Interface") );
+ set_capability( "demux2", 9 );
+ set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) );
+ add_submodule();
+ add_shortcut( "luaintf" );
+ add_shortcut( "luarc" );
+ /* add_shortcut( "rc" ); */
+ add_shortcut( "luahotkeys" );
+ /* add_shortcut( "hotkeys" ); */
+ add_shortcut( "luatelnet" );
+ /* add_shortcut( "telnet" ); */
+ set_description( _("Lua Interface Module") );
+ set_capability( "interface", 0 );
+ add_string( "lua-intf", "dummy", NULL,
+ INTF_TEXT, INTF_LONGTEXT, VLC_FALSE );
+ add_string( "lua-config", "", NULL,
+ CONFIG_TEXT, CONFIG_LONGTEXT, VLC_FALSE );
+ set_callbacks( E_(Open_LuaIntf), E_(Close_LuaIntf) );
+vlc_module_end();
+
+/*****************************************************************************
+ * Internal lua<->vlc utils
+ *****************************************************************************/
+vlc_object_t * vlclua_get_this( lua_State *L )
+{
+ vlc_object_t * p_this;
+ lua_getglobal( L, "vlc" );
+ lua_getfield( L, -1, "private" );
+ p_this = (vlc_object_t*)lua_topointer( L, lua_gettop( L ) );
+ lua_pop( L, 2 );
+ return p_this;
+}
+
+/*****************************************************************************
+ * VLC error code translation
+ *****************************************************************************/
+int vlclua_push_ret( lua_State *L, int i_error )
+{
+ lua_pushnumber( L, i_error );
+ lua_pushstring( L, vlc_error( i_error ) );
+ return 2;
+}
+
+/*****************************************************************************
+ * Get the VLC version string
+ *****************************************************************************/
+int vlclua_version( lua_State *L )
+{
+ lua_pushstring( L, VLC_Version() );
+ return 1;
+}
+
+/*****************************************************************************
+ * Quit VLC
+ *****************************************************************************/
+int vlclua_quit( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ /* The rc.c code also stops the playlist ... not sure if this is needed
+ * though. */
+ vlc_object_kill( p_this->p_libvlc );
+ return 0;
+}
+
+/*****************************************************************************
+ * Volume related
+ *****************************************************************************/
+int vlclua_volume_set( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ int i_volume = luaL_checkint( L, -1 );
+ /* Do we need to check that i_volume is in the AOUT_VOLUME_MIN->MAX range?*/
+ return vlclua_push_ret( L, aout_VolumeSet( p_this, i_volume ) );
+}
+
+int vlclua_volume_get( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ audio_volume_t i_volume;
+ if( aout_VolumeGet( p_this, &i_volume ) == VLC_SUCCESS )
+ lua_pushnumber( L, i_volume );
+ else
+ lua_pushnil( L );
+ return 1;
+}
+
+int vlclua_volume_up( lua_State *L )
+{
+ audio_volume_t i_volume;
+ aout_VolumeUp( vlclua_get_this( L ),
+ luaL_optint( L, 1, 1 ),
+ &i_volume );
+ lua_pushnumber( L, i_volume );
+ return 1;
+}
+
+int vlclua_volume_down( lua_State *L )
+{
+ audio_volume_t i_volume;
+ aout_VolumeDown( vlclua_get_this( L ),
+ luaL_optint( L, 1, 1 ),
+ &i_volume );
+ lua_pushnumber( L, i_volume );
+ return 1;
+}
+
+/*****************************************************************************
+ * Stream handling
+ *****************************************************************************/
+int vlclua_stream_new( lua_State *L )
+{
+ vlc_object_t * p_this = vlclua_get_this( L );
+ stream_t * p_stream;
+ const char * psz_url;
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ psz_url = luaL_checkstring( L, -1 );
+ lua_pop( L, 1 );
+ p_stream = stream_UrlNew( p_this, psz_url );
+ if( !p_stream ) return vlclua_error( L );
+ lua_pushlightuserdata( L, p_stream );
+ return 1;
+}
+
+int vlclua_stream_read( lua_State *L )
+{
+ stream_t * p_stream;
+ int n;
+ byte_t *p_read;
+ int i_read;
+ if( lua_gettop( L ) != 2 ) return vlclua_error( L );
+ p_stream = (stream_t *)luaL_checklightuserdata( L, -2 );
+ n = luaL_checkint( L, -1 );
+ lua_pop( L, 2 );
+ p_read = malloc( n );
+ if( !p_read ) return vlclua_error( L );
+ i_read = stream_Read( p_stream, p_read, n );
+ lua_pushlstring( L, (const char *)p_read, i_read );
+ free( p_read );
+ return 1;
+}
+
+int vlclua_stream_readline( lua_State *L )
+{
+ stream_t * p_stream;
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ p_stream = (stream_t *)luaL_checklightuserdata( L, -1 );
+ lua_pop( L, 1 );
+ char *psz_line = stream_ReadLine( p_stream );
+ if( psz_line )
+ {
+ lua_pushstring( L, psz_line );
+ free( psz_line );
+ }
+ else
+ {
+ lua_pushnil( L );
+ }
+ return 1;
+}
+
+int vlclua_stream_delete( lua_State *L )
+{
+ stream_t * p_stream;
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ p_stream = (stream_t *)luaL_checklightuserdata( L, -1 );
+ lua_pop( L, 1 );
+ stream_Delete( p_stream );
+ return 1;
+}
+
+/*****************************************************************************
+ * String transformations
+ *****************************************************************************/
+/* TODO: make it work for any number of arguments */
+int vlclua_decode_uri( lua_State *L )
+{
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ const char *psz_cstring = luaL_checkstring( L, 1 );
+ if( !psz_cstring ) return vlclua_error( L );
+ char *psz_string = strdup( psz_cstring );
+ lua_pop( L, 1 );
+ decode_URI( psz_string );
+ lua_pushstring( L, psz_string );
+ free( psz_string );
+ return 1;
+}
+
+/* TODO: make it work for any number of arguments */
+int vlclua_resolve_xml_special_chars( lua_State *L )
+{
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ const char *psz_cstring = luaL_checkstring( L, 1 );
+ if( !psz_cstring ) return vlclua_error( L );
+ char *psz_string = strdup( psz_cstring );
+ lua_pop( L, 1 );
+ resolve_xml_special_chars( psz_string );
+ lua_pushstring( L, psz_string );
+ free( psz_string );
+ return 1;
+}
+
+/*****************************************************************************
+ * Messaging facilities
+ *****************************************************************************/
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_dbg( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ const char *psz_cstring = luaL_checkstring( L, 1 );
+ if( !psz_cstring ) return vlclua_error( L );
+ msg_Dbg( p_this, "%s", psz_cstring );
+ return 0;
+}
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_warn( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ const char *psz_cstring = luaL_checkstring( L, 1 );
+ if( !psz_cstring ) return vlclua_error( L );
+ msg_Warn( p_this, "%s", psz_cstring );
+ return 0;
+}
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_err( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ const char *psz_cstring = luaL_checkstring( L, 1 );
+ if( !psz_cstring ) return vlclua_error( L );
+ msg_Err( p_this, "%s", psz_cstring );
+ return 0;
+}
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_info( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+ const char *psz_cstring = luaL_checkstring( L, 1 );
+ if( !psz_cstring ) return vlclua_error( L );
+ msg_Info( p_this, "%s", psz_cstring );
+ return 0;
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+static int file_select( const char *file )
+{
+ int i = strlen( file );
+ return i > 4 && !strcmp( file+i-4, ".lua" );
+}
+
+static int file_compare( const char **a, const char **b )
+{
+ return strcmp( *a, *b );
+}
+
+int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname,
+ char **ppsz_dir_list )
+{
+ if( asprintf( &ppsz_dir_list[0], "%s" DIR_SEP "%s",
+ p_this->p_libvlc->psz_datadir, luadirname ) < 0 )
+ return VLC_ENOMEM;
+
+# if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
+ {
+ const char *psz_vlcpath = config_GetDataDir();
+ if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s",
+ psz_vlcpath, luadirname ) < 0 )
+ return VLC_ENOMEM;
+
+ if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s",
+ psz_vlcpath, luadirname ) < 0 )
+ return VLC_ENOMEM;
+ }
+# else
+ if( asprintf( &ppsz_dir_list[1],
+ "share" DIR_SEP "%s", luadirname ) < 0 )
+ return VLC_ENOMEM;
+
+# ifdef HAVE_SYS_STAT_H
+ {
+ struct stat stat_info;
+ if( ( utf8_stat( ppsz_dir_list[1], &stat_info ) == -1 )
+ || !S_ISDIR( stat_info.st_mode ) )
+ {
+ free(ppsz_dir_list[1]);
+ if( asprintf( &ppsz_dir_list[1],
+ DATA_PATH DIR_SEP "%s", luadirname ) < 0 )
+ return VLC_ENOMEM;
+ }
+ }
+# endif
+# endif
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Will execute func on all scripts in luadirname, and stop if func returns
+ * success.
+ *****************************************************************************/
+int vlclua_scripts_batch_execute( vlc_object_t *p_this,
+ const char * luadirname,
+ int (*func)(vlc_object_t *, const char *, lua_State *, void *),
+ lua_State * L,
+ void * user_data)
+{
+ int i_ret = VLC_EGENERIC;
+
+ DIR *dir = NULL;
+ char **ppsz_filelist = NULL;
+ char **ppsz_fileend = NULL;
+ char **ppsz_file;
+
+ char *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
+ char **ppsz_dir;
+
+ i_ret = vlclua_dir_list( p_this, luadirname, ppsz_dir_list );
+ if( i_ret != VLC_SUCCESS )
+ return i_ret;
+ i_ret = VLC_EGENERIC;
+
+
+ for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
+ {
+ int i_files;
+
+ if( ppsz_filelist )
+ {
+ for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
+ ppsz_file++ )
+ free( *ppsz_file );
+ free( ppsz_filelist );
+ ppsz_filelist = NULL;
+ }
+
+ if( dir )
+ {
+ closedir( dir );
+ }
+
+ msg_Dbg( p_this, "Trying Lua scripts in %s", *ppsz_dir );
+ dir = utf8_opendir( *ppsz_dir );
+
+ if( !dir ) continue;
+ i_files = utf8_loaddir( dir, &ppsz_filelist, file_select,
+ file_compare );
+ if( i_files < 1 ) continue;
+ ppsz_fileend = ppsz_filelist + i_files;
+
+ for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
+ {
+ char *psz_filename;
+ if( asprintf( &psz_filename,
+ "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) < 0)
+ return VLC_ENOMEM;
+ msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
+
+ i_ret = func( p_this, psz_filename, L, user_data );
+
+ free( psz_filename );
+
+ if( i_ret == VLC_SUCCESS ) break;
+ }
+ if( i_ret == VLC_SUCCESS ) break;
+ }
+
+ if( ppsz_filelist )
+ {
+ for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
+ ppsz_file++ )
+ free( *ppsz_file );
+ free( ppsz_filelist );
+ }
+ for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
+ free( *ppsz_dir );
+
+ if( dir ) closedir( dir );
+
+ return i_ret;
+}
+
+
+/*****************************************************************************
+ * Meta data setters utility.
+ * Playlist item table should be on top of the stack when these are called
+ *****************************************************************************/
+void __vlclua_read_meta_data( vlc_object_t *p_this, lua_State *L,
+ input_item_t *p_input )
+{
+#define TRY_META( a, b ) \
+ lua_getfield( L, -1, a ); \
+ if( lua_isstring( L, -1 ) ) \
+ { \
+ char *psz_value = strdup( lua_tostring( L, -1 ) ); \
+ EnsureUTF8( psz_value ); \
+ msg_Dbg( p_this, #b ": %s", psz_value ); \
+ input_item_Set ## b ( p_input, psz_value ); \
+ free( psz_value ); \
+ } \
+ lua_pop( L, 1 ); /* pop a */
+ TRY_META( "title", Title );
+ TRY_META( "artist", Artist );
+ TRY_META( "genre", Genre );
+ TRY_META( "copyright", Copyright );
+ TRY_META( "album", Album );
+ TRY_META( "tracknum", TrackNum );
+ TRY_META( "description", Description );
+ TRY_META( "rating", Rating );
+ TRY_META( "date", Date );
+ TRY_META( "setting", Setting );
+ TRY_META( "url", URL );
+ TRY_META( "language", Language );
+ TRY_META( "nowplaying", NowPlaying );
+ TRY_META( "publisher", Publisher );
+ TRY_META( "encodedby", EncodedBy );
+ TRY_META( "arturl", ArtURL );
+ TRY_META( "trackid", TrackID );
+}
+
+void __vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *L,
+ input_item_t *p_input )
+{
+ /* ... item */
+ lua_getfield( L, -1, "meta" );
+ /* ... item meta */
+ if( lua_istable( L, -1 ) )
+ {
+ lua_pushnil( L );
+ /* ... item meta nil */
+ while( lua_next( L, -2 ) )
+ {
+ /* ... item meta key value */
+ if( !lua_isstring( L, -2 ) )
+ {
+ msg_Warn( p_this, "Custom meta data category name must be "
+ "a string" );
+ }
+ else if( !lua_istable( L, -1 ) )
+ {
+ msg_Warn( p_this, "Custom meta data category contents "
+ "must be a table" );
+ }
+ else
+ {
+ const char *psz_meta_category = lua_tostring( L, -2 );
+ msg_Dbg( p_this, "Found custom meta data category: %s",
+ psz_meta_category );
+ lua_pushnil( L );
+ /* ... item meta key value nil */
+ while( lua_next( L, -2 ) )
+ {
+ /* ... item meta key value key2 value2 */
+ if( !lua_isstring( L, -2 ) )
+ {
+ msg_Warn( p_this, "Custom meta category item name "
+ "must be a string." );
+ }
+ else if( !lua_isstring( L, -1 ) )
+ {
+ msg_Warn( p_this, "Custom meta category item value "
+ "must be a string." );
+ }
+ else
+ {
+ const char *psz_meta_name =
+ lua_tostring( L, -2 );
+ const char *psz_meta_value =
+ lua_tostring( L, -1 );
+ msg_Dbg( p_this, "Custom meta %s, %s: %s",
+ psz_meta_category, psz_meta_name,
+ psz_meta_value );
+ input_ItemAddInfo( p_input, psz_meta_category,
+ psz_meta_name, psz_meta_value );
+ }
+ lua_pop( L, 1 ); /* pop item */
+ /* ... item meta key value key2 */
+ }
+ /* ... item meta key value */
+ }
+ lua_pop( L, 1 ); /* pop category */
+ /* ... item meta key */
+ }
+ /* ... item meta */
+ }
+ lua_pop( L, 1 ); /* pop "meta" */
+ /* ... item -> back to original stack */
+}
+
+/*****************************************************************************
+ * Playlist utilities
+ ****************************************************************************/
+/**
+ * Playlist item table should be on top of the stack when this is called
+ */
+void __vlclua_read_options( vlc_object_t *p_this, lua_State *L,
+ int *pi_options, char ***pppsz_options )
+{
+ lua_getfield( L, -1, "options" );
+ if( lua_istable( L, -1 ) )
+ {
+ lua_pushnil( L );
+ while( lua_next( L, -2 ) )
+ {
+ if( lua_isstring( L, -1 ) )
+ {
+ char *psz_option = strdup( lua_tostring( L, -1 ) );
+ msg_Dbg( p_this, "Option: %s", psz_option );
+ INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
+ psz_option );
+ }
+ else
+ {
+ msg_Warn( p_this, "Option should be a string" );
+ }
+ lua_pop( L, 1 ); /* pop option */
+ }
+ }
+ lua_pop( L, 1 ); /* pop "options" */
+}
+
+int __vlclua_playlist_add_internal( vlc_object_t *p_this, lua_State *L,
+ playlist_t *p_playlist,
+ input_item_t *p_parent, vlc_bool_t b_play )
+{
+ int i_count = 0;
+
+ /* playlist */
+ if( lua_istable( L, -1 ) )
+ {
+ lua_pushnil( L );
+ /* playlist nil */
+ while( lua_next( L, -2 ) )
+ {
+ /* playlist key item */
+ /* <Parse playlist item> */
+ if( lua_istable( L, -1 ) )
+ {
+ lua_getfield( L, -1, "path" );
+ /* playlist key item path */
+ if( lua_isstring( L, -1 ) )
+ {
+ const char *psz_path = NULL;
+ const char *psz_name = NULL;
+ char **ppsz_options = NULL;
+ int i_options = 0;
+ mtime_t i_duration = -1;
+ input_item_t *p_input;
+
+ /* Read path and name */
+ psz_path = lua_tostring( L, -1 );
+ msg_Dbg( p_this, "Path: %s", psz_path );
+ lua_getfield( L, -2, "name" );
+ /* playlist key item path name */
+ if( lua_isstring( L, -1 ) )
+ {
+ psz_name = lua_tostring( L, -1 );
+ msg_Dbg( p_this, "Name: %s", psz_name );
+ }
+ else
+ {
+ if( !lua_isnil( L, -1 ) )
+ msg_Warn( p_this, "Playlist item name should be a string." );
+ psz_name = psz_path;
+ }
+
+ /* Read duration */
+ lua_getfield( L, -3, "duration" );
+ /* playlist key item path name duration */
+ if( lua_isnumber( L, -1 ) )
+ {
+ i_duration = (mtime_t)(lua_tonumber( L, -1 )*1e6);
+ }
+ else if( !lua_isnil( L, -1 ) )
+ {
+ msg_Warn( p_this, "Playlist item duration should be a number (in seconds)." );
+ }
+ lua_pop( L, 1 ); /* pop "duration" */
+
+ /* playlist key item path name */
+
+ /* Read options: item must be on top of stack */
+ lua_pushvalue( L, -3 );
+ /* playlist key item path name item */
+ vlclua_read_options( p_this, L, &i_options, &ppsz_options );
+
+ /* Create input item */
+ p_input = input_ItemNewExt( p_playlist, psz_path,
+ psz_name, i_options,
+ (const char **)ppsz_options,
+ i_duration );
+ lua_pop( L, 3 ); /* pop "path name item" */
+ /* playlist key item */
+
+ /* Read meta data: item must be on top of stack */
+ vlclua_read_meta_data( p_this, L, p_input );
+
+ /* Read custom meta data: item must be on top of stack*/
+ vlclua_read_custom_meta_data( p_this, L, p_input );
+
+ /* Append item to playlist */
+ if( p_parent ) /* Add to node */
+ input_ItemAddSubItem( p_parent, p_input );
+ else if( b_play ) /* Play */
+ playlist_AddInput( p_playlist, p_input,
+ PLAYLIST_APPEND | PLAYLIST_GO,
+ PLAYLIST_END, VLC_TRUE, VLC_FALSE );
+ else /* Enqueue */
+ playlist_AddInput( p_playlist, p_input,
+ PLAYLIST_APPEND | PLAYLIST_PREPARSE,
+ PLAYLIST_END, VLC_TRUE, VLC_FALSE );
+ i_count ++; /* increment counter */
+
+ while( i_options > 0 )
+ free( ppsz_options[--i_options] );
+ free( ppsz_options );
+ }
+ else
+ {
+ lua_pop( L, 1 ); /* pop "path" */
+ msg_Warn( p_this,
+ "Playlist item's path should be a string" );
+ }
+ /* playlist key item */
+ }
+ else
+ {
+ msg_Warn( p_this, "Playlist item should be a table" );
+ }
+ /* <Parse playlist item> */
+ lua_pop( L, 1 ); /* pop the value, keep the key for
+ * the next lua_next() call */
+ /* playlist key */
+ }
+ /* playlist */
+ }
+ else
+ {
+ msg_Warn( p_this, "Playlist should be a table." );
+ }
+ return i_count;
+}
--- /dev/null
+/*****************************************************************************
+ * luameta.c: Get meta/artwork using lua scripts
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ * Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLCLUA_H
+#define VLC_LUA_H
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_input.h>
+#include <vlc_playlist.h>
+#include <vlc_meta.h>
+#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_stream.h>
+#include <vlc_charset.h>
+
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+/*****************************************************************************
+ * Module entry points
+ *****************************************************************************/
+int E_(FindArt)( vlc_object_t * );
+int E_(FindMeta)( vlc_object_t * );
+
+int E_(Import_LuaPlaylist)( vlc_object_t * );
+void E_(Close_LuaPlaylist)( vlc_object_t * );
+
+int E_(Open_LuaIntf)( vlc_object_t * );
+void E_(Close_LuaIntf)( vlc_object_t * );
+
+
+/*****************************************************************************
+ * Lua debug
+ *****************************************************************************/
+static inline void lua_Dbg( vlc_object_t * p_this, const char * ppz_fmt, ... )
+{
+ if( p_this->p_libvlc->i_verbose < 3 )
+ return;
+
+ va_list ap;
+ va_start( ap, ppz_fmt );
+ __msg_GenericVa( ( vlc_object_t *)p_this, MSG_QUEUE_NORMAL,
+ VLC_MSG_DBG, MODULE_STRING,
+ ppz_fmt, ap );
+ va_end( ap );
+}
+
+/*****************************************************************************
+ * Functions that should be in lua ... but aren't for some obscure reason
+ *****************************************************************************/
+static inline int luaL_checkboolean( lua_State *L, int narg )
+{
+ luaL_checktype( L, narg, LUA_TBOOLEAN ); /* can raise an error */
+ return lua_toboolean( L, narg );
+}
+
+static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
+{
+ luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
+ return lua_topointer( L, narg );
+}
+
+static inline const void *luaL_checkuserdata( lua_State *L, int narg, size_t size )
+{
+ luaL_checktype( L, narg, LUA_TUSERDATA ); /* can raise an error */
+ if( size && size != lua_objlen( L, narg ) ) /* this isn't worth much ... but it might still prevent a few errors */
+ luaL_error( L, "user data size doesn't match" );
+ return lua_topointer( L, narg );
+}
+
+/*****************************************************************************
+ * Lua vlc_object_t wrapper
+ *****************************************************************************/
+int __vlclua_push_vlc_object( lua_State *L, vlc_object_t *p_obj,
+ lua_CFunction pf_gc );
+#define vlclua_push_vlc_object( a, b, c ) \
+ __vlclua_push_vlc_object( a, VLC_OBJECT( b ), c )
+vlc_object_t *vlclua_checkobject( lua_State *L, int narg, int i_type );
+int vlclua_gc_release( lua_State *L );
+int vlclua_object_find( lua_State *L );
+int vlclua_object_find_name( lua_State *L );
+
+int vlclua_add_callback( lua_State * );
+int vlclua_del_callback( lua_State * );
+
+int vlclua_url_parse( lua_State * );
+int vlclua_net_listen_tcp( lua_State * );
+int vlclua_net_listen_close( lua_State * );
+int vlclua_net_accept( lua_State * );
+int vlclua_net_close( lua_State * );
+int vlclua_net_send( lua_State * );
+int vlclua_net_recv( lua_State * );
+int vlclua_net_select( lua_State * );
+
+int vlclua_fd_set_new( lua_State * );
+int vlclua_fd_clr( lua_State * );
+int vlclua_fd_isset( lua_State * );
+int vlclua_fd_set( lua_State * );
+int vlclua_fd_zero( lua_State * );
+int vlclua_fd_read( lua_State * );
+int vlclua_fd_write( lua_State * );
+
+int vlclua_vlm_new( lua_State * );
+int vlclua_vlm_delete( lua_State * );
+int vlclua_vlm_execute_command( lua_State * );
+
+/*****************************************************************************
+ * Lua function bridge
+ *****************************************************************************/
+vlc_object_t * vlclua_get_this( lua_State * );
+#define vlclua_error( L ) luaL_error( L, "VLC lua error in file %s line %d (function %s)", __FILE__, __LINE__, __func__ )
+int vlclua_push_ret( lua_State *, int i_error );
+
+int vlclua_version( lua_State * );
+int vlclua_quit( lua_State * );
+
+int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val ); /* internal use only */
+int vlclua_var_get( lua_State * );
+int vlclua_var_get_list( lua_State * );
+int vlclua_var_set( lua_State * );
+int vlclua_module_command( lua_State * );
+int vlclua_libvlc_command( lua_State * );
+
+int vlclua_config_get( lua_State * );
+int vlclua_config_set( lua_State * );
+
+int vlclua_volume_set( lua_State * );
+int vlclua_volume_get( lua_State * );
+int vlclua_volume_up( lua_State * );
+int vlclua_volume_down( lua_State * );
+
+int vlclua_stream_new( lua_State * );
+int vlclua_stream_read( lua_State * );
+int vlclua_stream_readline( lua_State * );
+int vlclua_stream_delete( lua_State * );
+
+int vlclua_decode_uri( lua_State * );
+int vlclua_resolve_xml_special_chars( lua_State * );
+
+int vlclua_msg_dbg( lua_State * );
+int vlclua_msg_warn( lua_State * );
+int vlclua_msg_err( lua_State * );
+int vlclua_msg_info( lua_State * );
+
+/*****************************************************************************
+ * Will execute func on all scripts in luadirname, and stop if func returns
+ * success.
+ *****************************************************************************/
+int vlclua_scripts_batch_execute( vlc_object_t *p_this, const char * luadirname,
+ int (*func)(vlc_object_t *, const char *, lua_State *, void *),
+ lua_State * L, void * user_data );
+int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname, char **ppsz_dir_list );
+
+/*****************************************************************************
+ * Playlist and meta data internal utilities.
+ *****************************************************************************/
+void __vlclua_read_options( vlc_object_t *, lua_State *, int *, char *** );
+#define vlclua_read_options(a,b,c,d) __vlclua_read_options(VLC_OBJECT(a),b,c,d)
+void __vlclua_read_meta_data( vlc_object_t *, lua_State *, input_item_t * );
+#define vlclua_read_meta_data(a,b,c) __vlclua_read_meta_data(VLC_OBJECT(a),b,c)
+void __vlclua_read_custom_meta_data( vlc_object_t *, lua_State *,
+ input_item_t *);
+#define vlclua_read_custom_meta_data(a,b,c) __vlclua_read_custom_meta_data(VLC_OBJECT(a),b,c)
+int __vlclua_playlist_add_internal( vlc_object_t *, lua_State *, playlist_t *,
+ input_item_t *, vlc_bool_t );
+#define vlclua_playlist_add_internal(a,b,c,d,e) __vlclua_playlist_add_internal(VLC_OBJECT(a),b,c,d,e)
+
+
+#endif /* VLC_LUA_H */
+
+++ /dev/null
-/*****************************************************************************
- * vlclua.c: Generic lua inteface functions
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- * Pierre d'Herbemont <pdherbemont # videolan.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-#endif
-
-#include <vlc/vlc.h>
-#include <vlc_meta.h>
-#include <vlc_charset.h>
-
-#include <lua.h> /* Low level lua C API */
-#include <lauxlib.h> /* Higher level C API */
-#include <lualib.h> /* Lua libs */
-
-#include "vlclua.h"
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-
-vlc_module_begin();
- add_shortcut( "luameta" );
- set_shortname( N_( "Lua Meta" ) );
- set_description( _("Fetch metadata using lua scripts") );
- set_capability( "meta fetcher", 10 );
- set_callbacks( E_(FindMeta), NULL );
- add_submodule();
- set_shortname( N_( "Lua Art" ) );
- set_description( _("Fetch artwork using lua scripts") );
- set_capability( "art finder", 10 );
- set_callbacks( E_(FindArt), NULL );
- add_submodule();
- add_shortcut( "luaplaylist" );
- set_category( CAT_INPUT );
- set_subcategory( SUBCAT_INPUT_DEMUX );
- set_shortname( _("Lua Playlist") );
- set_description( _("Lua Playlist Parser Interface") );
- set_capability( "demux2", 9 );
- set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) );
-vlc_module_end();
-
-/*****************************************************************************
- * Lua function bridge
- *****************************************************************************/
-vlc_object_t * vlclua_get_this( lua_State *p_state )
-{
- vlc_object_t * p_this;
- lua_getglobal( p_state, "vlc" );
- lua_getfield( p_state, lua_gettop( p_state ), "private" );
- p_this = (vlc_object_t*)lua_topointer( p_state, lua_gettop( p_state ) );
- lua_pop( p_state, 2 );
- return p_this;
-}
-
-int vlclua_stream_new( lua_State *p_state )
-{
- vlc_object_t * p_this = vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- stream_t * p_stream;
- const char * psz_url;
- if( !i ) return 0;
- psz_url = lua_tostring( p_state, 1 );
- lua_pop( p_state, i );
- p_stream = stream_UrlNew( p_this, psz_url );
- if( !p_stream ) return 0;
- lua_pushlightuserdata( p_state, p_stream );
- return 1;
-}
-
-int vlclua_stream_read( lua_State *p_state )
-{
- int i = lua_gettop( p_state );
- stream_t * p_stream;
- int n;
- byte_t *p_read;
- int i_read;
- if( !i ) return 0;
- p_stream = (stream_t *)lua_topointer( p_state, 1 );
- n = lua_tonumber( p_state, 2 );
- lua_pop( p_state, i );
- p_read = malloc( n );
- if( !p_read ) return 0;
- i_read = stream_Read( p_stream, p_read, n );
- lua_pushlstring( p_state, (const char *)p_read, i_read );
- free( p_read );
- return 1;
-}
-
-int vlclua_stream_readline( lua_State *p_state )
-{
- int i = lua_gettop( p_state );
- stream_t * p_stream;
- if( !i ) return 0;
- p_stream = (stream_t *)lua_topointer( p_state, 1 );
- lua_pop( p_state, i );
- char *psz_line = stream_ReadLine( p_stream );
- if( psz_line )
- {
- lua_pushstring( p_state, psz_line );
- free( psz_line );
- }
- else
- {
- lua_pushnil( p_state );
- }
- return 1;
-}
-
-int vlclua_stream_delete( lua_State *p_state )
-{
- int i = lua_gettop( p_state );
- stream_t * p_stream;
- if( !i ) return 0;
- p_stream = (stream_t *)lua_topointer( p_state, 1 );
- lua_pop( p_state, i );
- stream_Delete( p_stream );
- return 1;
-}
-
-int vlclua_decode_uri( lua_State *p_state )
-{
- int i = lua_gettop( p_state );
- if( !i ) return 0;
- const char *psz_cstring = lua_tostring( p_state, 1 );
- if( !psz_cstring ) return 0;
- char *psz_string = strdup( psz_cstring );
- lua_pop( p_state, i );
- decode_URI( psz_string );
- lua_pushstring( p_state, psz_string );
- free( psz_string );
- return 1;
-}
-
-int vlclua_resolve_xml_special_chars( lua_State *p_state )
-{
- int i = lua_gettop( p_state );
- if( !i ) return 0;
- const char *psz_cstring = lua_tostring( p_state, 1 );
- if( !psz_cstring ) return 0;
- char *psz_string = strdup( psz_cstring );
- lua_pop( p_state, i );
- resolve_xml_special_chars( psz_string );
- lua_pushstring( p_state, psz_string );
- free( psz_string );
- return 1;
-}
-
-int vlclua_msg_dbg( lua_State *p_state )
-{
- vlc_object_t *p_this = vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- if( !i ) return 0;
- const char *psz_cstring = lua_tostring( p_state, 1 );
- if( !psz_cstring ) return 0;
- msg_Dbg( p_this, "%s", psz_cstring );
- return 0;
-}
-int vlclua_msg_warn( lua_State *p_state )
-{
- vlc_object_t *p_this = vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- if( !i ) return 0;
- const char *psz_cstring = lua_tostring( p_state, 1 );
- if( !psz_cstring ) return 0;
- msg_Warn( p_this, "%s", psz_cstring );
- return 0;
-}
-int vlclua_msg_err( lua_State *p_state )
-{
- vlc_object_t *p_this = vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- if( !i ) return 0;
- const char *psz_cstring = lua_tostring( p_state, 1 );
- if( !psz_cstring ) return 0;
- msg_Err( p_this, "%s", psz_cstring );
- return 0;
-}
-int vlclua_msg_info( lua_State *p_state )
-{
- vlc_object_t *p_this = vlclua_get_this( p_state );
- int i = lua_gettop( p_state );
- if( !i ) return 0;
- const char *psz_cstring = lua_tostring( p_state, 1 );
- if( !psz_cstring ) return 0;
- msg_Info( p_this, "%s", psz_cstring );
- return 0;
-}
-
-/*****************************************************************************
- *
- *****************************************************************************/
-static int file_select( const char *file )
-{
- int i = strlen( file );
- return i > 4 && !strcmp( file+i-4, ".lua" );
-}
-
-static int file_compare( const char **a, const char **b )
-{
- return strcmp( *a, *b );
-}
-
-
-/*****************************************************************************
- * Will execute func on all scripts in luadirname, and stop if func returns
- * success.
- *****************************************************************************/
-int vlclua_scripts_batch_execute( vlc_object_t *p_this,
- const char * luadirname,
- int (*func)(vlc_object_t *, const char *, lua_State *, void *),
- lua_State * p_state,
- void * user_data)
-{
- int i_ret = VLC_EGENERIC;
-
- DIR *dir = NULL;
- char **ppsz_filelist = NULL;
- char **ppsz_fileend = NULL;
- char **ppsz_file;
-
- char *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
- char **ppsz_dir;
-
- if( asprintf( &ppsz_dir_list[0], "%s" DIR_SEP "%s",
- p_this->p_libvlc->psz_datadir, luadirname ) < 0 )
- return VLC_ENOMEM;
-
-# if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
- {
- const char *psz_vlcpath = config_GetDataDir();
- if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s", psz_vlcpath, luadirname ) < 0 )
- return VLC_ENOMEM;
-
- if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s", psz_vlcpath, luadirname ) < 0 )
- return VLC_ENOMEM;
- }
-# else
- if( asprintf( &ppsz_dir_list[1],
- "share" DIR_SEP "%s", luadirname ) < 0 )
- return VLC_ENOMEM;
-
-# ifdef HAVE_SYS_STAT_H
- {
- struct stat stat_info;
- if( ( utf8_stat( ppsz_dir_list[1], &stat_info ) == -1 )
- || !S_ISDIR( stat_info.st_mode ) )
- {
- free(ppsz_dir_list[1]);
- if( asprintf( &ppsz_dir_list[1],
- DATA_PATH DIR_SEP "%s", luadirname ) < 0 )
- return VLC_ENOMEM;
- }
- }
-# endif
-# endif
-
- for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
- {
- int i_files;
-
- if( ppsz_filelist )
- {
- for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
- ppsz_file++ )
- free( *ppsz_file );
- free( ppsz_filelist );
- ppsz_filelist = NULL;
- }
-
- if( dir )
- {
- closedir( dir );
- }
-
- msg_Dbg( p_this, "Trying Lua scripts in %s", *ppsz_dir );
- dir = utf8_opendir( *ppsz_dir );
-
- if( !dir ) continue;
- i_files = utf8_loaddir( dir, &ppsz_filelist, file_select, file_compare );
- if( i_files < 1 ) continue;
- ppsz_fileend = ppsz_filelist + i_files;
-
- for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
- {
- char *psz_filename;
- if( asprintf( &psz_filename,
- "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) < 0)
- return VLC_ENOMEM;
- msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
-
- i_ret = func( p_this, psz_filename, p_state, user_data );
-
- free( psz_filename );
-
- if( i_ret == VLC_SUCCESS ) break;
- }
- if( i_ret == VLC_SUCCESS ) break;
- }
-
- if( ppsz_filelist )
- {
- for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
- ppsz_file++ )
- free( *ppsz_file );
- free( ppsz_filelist );
- }
- for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
- free( *ppsz_dir );
-
- if( dir ) closedir( dir );
-
- return i_ret;
-}
-
-
-/*****************************************************************************
- * Meta data setters utility.
- *****************************************************************************/
-void vlclua_read_meta_data( vlc_object_t *p_this, lua_State *p_state,
- int o, int t, input_item_t *p_input )
-{
- const char *psz_value;
-#define TRY_META( a, b ) \
- lua_getfield( p_state, o, a ); \
- if( lua_isstring( p_state, t ) ) \
- { \
- psz_value = lua_tostring( p_state, t ); \
- EnsureUTF8( psz_value ); \
- msg_Dbg( p_this, #b ": %s", psz_value ); \
- input_item_Set ## b ( p_input, psz_value ); \
- } \
- lua_pop( p_state, 1 ); /* pop a */
- TRY_META( "title", Title );
- TRY_META( "artist", Artist );
- TRY_META( "genre", Genre );
- TRY_META( "copyright", Copyright );
- TRY_META( "album", Album );
- TRY_META( "tracknum", TrackNum );
- TRY_META( "description", Description );
- TRY_META( "rating", Rating );
- TRY_META( "date", Date );
- TRY_META( "setting", Setting );
- TRY_META( "url", URL );
- TRY_META( "language", Language );
- TRY_META( "nowplaying", NowPlaying );
- TRY_META( "publisher", Publisher );
- TRY_META( "encodedby", EncodedBy );
- TRY_META( "arturl", ArtURL );
- TRY_META( "trackid", TrackID );
-}
-
-void vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *p_state,
- int o, int t, input_item_t *p_input )
-{
- lua_getfield( p_state, o, "meta" );
- if( lua_istable( p_state, t ) )
- {
- lua_pushnil( p_state );
- while( lua_next( p_state, t ) )
- {
- if( !lua_isstring( p_state, t+1 ) )
- {
- msg_Warn( p_this, "Custom meta data category name must be "
- "a string" );
- }
- else if( !lua_istable( p_state, t+2 ) )
- {
- msg_Warn( p_this, "Custom meta data category contents "
- "must be a table" );
- }
- else
- {
- const char *psz_meta_category = lua_tostring( p_state, t+1 );
- msg_Dbg( p_this, "Found custom meta data category: %s",
- psz_meta_category );
- lua_pushnil( p_state );
- while( lua_next( p_state, t+2 ) )
- {
- if( !lua_isstring( p_state, t+3 ) )
- {
- msg_Warn( p_this, "Custom meta category item name "
- "must be a string." );
- }
- else if( !lua_isstring( p_state, t+4 ) )
- {
- msg_Warn( p_this, "Custom meta category item value "
- "must be a string." );
- }
- else
- {
- const char *psz_meta_name =
- lua_tostring( p_state, t+3 );
- const char *psz_meta_value =
- lua_tostring( p_state, t+4 );
- msg_Dbg( p_this, "Custom meta %s, %s: %s",
- psz_meta_category, psz_meta_name,
- psz_meta_value );
- input_ItemAddInfo( p_input, psz_meta_category,
- psz_meta_name, psz_meta_value );
- }
- lua_pop( p_state, 1 ); /* pop item */
- }
- }
- lua_pop( p_state, 1 ); /* pop category */
- }
- }
- lua_pop( p_state, 1 ); /* pop "meta" */
-}
-
+++ /dev/null
-/*****************************************************************************
- * luameta.c: Get meta/artwork using lua scripts
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- * Pierre d'Herbemont <pdherbemont # videolan.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#ifndef VLCLUA_H
-#define VLC_LUA_H
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-#endif
-
-#include <vlc/vlc.h>
-#include <vlc_input.h>
-#include <vlc_playlist.h>
-#include <vlc_meta.h>
-#include <vlc_url.h>
-#include <vlc_strings.h>
-#include <vlc_stream.h>
-#include <vlc_charset.h>
-
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-
-#include <lua.h> /* Low level lua C API */
-#include <lauxlib.h> /* Higher level C API */
-#include <lualib.h> /* Lua libs */
-
-/*****************************************************************************
- * Module entry points
- *****************************************************************************/
-int E_(FindArt)( vlc_object_t * );
-int E_(FindMeta)( vlc_object_t * );
-
-int E_(Import_LuaPlaylist)( vlc_object_t * );
-void E_(Close_LuaPlaylist)( vlc_object_t * );
-
-
-/*****************************************************************************
- * Lua debug
- *****************************************************************************/
-static inline void lua_Dbg( vlc_object_t * p_this, const char * ppz_fmt, ... )
-{
- if( p_this->p_libvlc->i_verbose < 3 )
- return;
-
- va_list ap;
- va_start( ap, ppz_fmt );
- __msg_GenericVa( ( vlc_object_t *)p_this, MSG_QUEUE_NORMAL,
- VLC_MSG_DBG, MODULE_STRING,
- ppz_fmt, ap );
- va_end( ap );
-}
-
-/*****************************************************************************
- * Lua function bridge
- *****************************************************************************/
-vlc_object_t * vlclua_get_this( lua_State *p_state );
-int vlclua_stream_new( lua_State *p_state );
-int vlclua_stream_read( lua_State *p_state );
-int vlclua_stream_readline( lua_State *p_state );
-int vlclua_stream_delete( lua_State *p_state );
-int vlclua_decode_uri( lua_State *p_state );
-int vlclua_resolve_xml_special_chars( lua_State *p_state );
-int vlclua_msg_dbg( lua_State *p_state );
-int vlclua_msg_warn( lua_State *p_state );
-int vlclua_msg_err( lua_State *p_state );
-int vlclua_msg_info( lua_State *p_state );
-
-
-/*****************************************************************************
- * Will execute func on all scripts in luadirname, and stop if func returns
- * success.
- *****************************************************************************/
-int vlclua_scripts_batch_execute( vlc_object_t *p_this, const char * luadirname,
- int (*func)(vlc_object_t *, const char *, lua_State *, void *),
- lua_State * p_state, void * user_data);
-
-
-
-/*****************************************************************************
- * Meta data setters utility.
- *****************************************************************************/
-void vlclua_read_meta_data( vlc_object_t *p_this,
- lua_State *p_state, int o, int t, input_item_t *p_input );
-
-
-void vlclua_read_custom_meta_data( vlc_object_t *p_this,
- lua_State *p_state, int o, int t, input_item_t *p_input );
-
-#endif /* VLC_LUA_H */
-
--- /dev/null
+/*****************************************************************************
+ * objects.c: Generic lua VLM wrapper
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_vlm.h>
+
+#include <lua.h> /* Low level lua C API */
+#include <lauxlib.h> /* Higher level C API */
+#include <lualib.h> /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+int vlclua_vlm_new( lua_State *L )
+{
+ vlc_object_t *p_this = vlclua_get_this( L );
+ vlm_t *p_vlm = vlm_New( p_this );
+ if( !p_vlm )
+ return luaL_error( L, "Cannot start VLM." );
+ __vlclua_push_vlc_object( L, (vlc_object_t*)p_vlm, NULL );
+ return 1;
+}
+
+int vlclua_vlm_delete( lua_State *L )
+{
+ vlm_t *p_vlm = (vlm_t*)vlclua_checkobject( L, 1, VLC_OBJECT_VLM );
+ vlm_Delete( p_vlm );
+ return 0;
+}
+
+void push_message( lua_State *L, vlm_message_t *message );
+void push_message( lua_State *L, vlm_message_t *message )
+{
+ lua_createtable( L, 0, 2 );
+ lua_pushstring( L, message->psz_name );
+ lua_setfield( L, -2, "name" );
+ if( message->i_child > 0 )
+ {
+ int i;
+ lua_createtable( L, message->i_child, 0 );
+ for( i = 0; i < message->i_child; i++ )
+ {
+ lua_pushinteger( L, i+1 );
+ push_message( L, message->child[i] );
+ lua_settable( L, -3 );
+ }
+ lua_setfield( L, -2, "children" );
+ }
+ else
+ {
+ lua_pushstring( L, message->psz_value );
+ lua_setfield( L, -2, "value" );
+ }
+}
+
+int vlclua_vlm_execute_command( lua_State *L )
+{
+ vlm_t *p_vlm = (vlm_t*)vlclua_checkobject( L, 1, VLC_OBJECT_VLM );
+ const char *psz_command = luaL_checkstring( L, 2 );
+ vlm_message_t *message;
+ vlm_ExecuteCommand( p_vlm, psz_command, &message );
+ lua_settop( L, 0 );
+ push_message( L, message );
+ vlm_MessageDelete( message );
+ return 1;
+}
luaplaylist/youtube.lua \
luaplaylist/youtube_homepage.lua \
luaplaylist/metacafe.lua \
- luaplaylist/googlevideo.lua
+ luaplaylist/googlevideo.lua \
+ luaintf/rc.lua \
+ luaintf/hotkeys.lua \
+ luaintf/modules/common.lua \
+ luaintf/modules/host.lua \
+ luaintf/telnet.lua \
+ luaintf/dummy.lua
--- /dev/null
+--[[ This code is public domain (since it really isn't very interesting) ]]--
+
+msg = [[
+This is the `dummy' VLC Lua interface module.
+Please specify a VLC Lua interface to load with the --lua-intf option.
+VLC Lua interface modules include: `rc', `telnet' and `hotkeys'.
+For example: vlc -I lua --lua-intf rc]]
+--You can also use the alternate syntax: vlc -I "lua{intf=rc}"]]
+
+for line in string.gmatch(msg,"([^\n]+)\n*") do
+ vlc.msg.err(line)
+end
--- /dev/null
+--[==========================================================================[
+ hotkeys.lua: hotkey handling for VLC
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+--]==========================================================================]
+
+--[==========================================================================[
+ This is meant to replace modules/control/hotkeys.c
+ (which will require some changes in the VLC core hotkeys stuff)
+--]==========================================================================]
+
+require("common")
+--common.table_print(vlc,"vlc.\t")
+
+bindings = {
+ ["Ctrl-q"] = "quit",
+ ["Space"] = "play-pause",
+ [113] --[[q]] = "quit",
+ [119] --[[w]] = "demo",
+ [120] --[[x]] = "demo2",
+ }
+
+function quit()
+ print("Bye-bye!")
+ vlc.quit()
+end
+
+function demo()
+ vlc.osd.icon("speaker")
+end
+
+function demo2()
+ if not channel1 then
+ channel1 = vlc.osd.channel_register()
+ channel2 = vlc.osd.channel_register()
+ end
+ vlc.osd.message("Hey!",channel1)
+ vlc.osd.slider( 10, "horizontal", channel2 )
+end
+
+function action(func,delta)
+ return { func = func, delta = delta or 0, last = 0, times = 0 }
+end
+
+actions = {
+ ["quit"] = action(quit),
+ ["play-pause"] = action(play_pause),
+ ["demo"] = action(demo),
+ ["demo2"] = action(demo2),
+ }
+
+action = nil
+
+queue = {}
+
+function action_trigger( action )
+ print("action_trigger:",tostring(action))
+ local a = actions[action]
+ if a then
+ local date = vlc.mdate()
+ if a.delta and date > a.last + a.delta then
+ a.times = 0
+ else
+ a.times = a.times + 1
+ end
+ a.last = date
+ table.insert(queue,action)
+ vlc.signal()
+ else
+ vlc.msg_err("Key `"..key.."' points to unknown action `"..bindings[key].."'.")
+ end
+end
+
+function key_press( var, old, new, data )
+ local key = new
+ print("key_press:",tostring(key))
+ if bindings[key] then
+ action_trigger(bindings[key])
+ else
+ vlc.msg_err("Key `"..key.."' isn't bound to any action.")
+ end
+end
+
+vlc.var.add_callback( vlc.object.libvlc(), "key-pressed", key_press )
+--vlc.var.add_callback( vlc.object.libvlc(), "action-triggered", action_trigger )
+
+while not die do
+ if #queue ~= 0 then
+ local action = actions[queue[1]]
+ local ok, msg = pcall( action.func )
+ if not ok then
+ vlc.msg.err("Error while executing action `"..queue[1].."': "..msg)
+ end
+ table.remove(queue,1)
+ else
+ die = vlc.lock_and_wait()
+ end
+end
+
+-- Clean up
+vlc.var.del_callback( vlc.object.libvlc(), "key-pressed", key_press )
+--vlc.var.del_callback( vlc.object.libvlc(), "action-triggered", action_trigger )
--- /dev/null
+--[[ This code is public domain (since it really isn't very interesting) ]]--
+
+module("common",package.seeall)
+
+-- Iterate over a table in the keys' alphabetical order
+function pairs_sorted(t)
+ local s = {}
+ for k,_ in pairs(t) do table.insert(s,k) end
+ table.sort(s)
+ local i = 0
+ return function () i = i + 1; return s[i], t[s[i]] end
+end
+
+-- Return a function such as skip(foo)(a,b,c) = foo(b,c)
+function skip(foo)
+ return function(discard,...) return foo(...) end
+end
+
+-- Return a function such as setarg(foo,a)(b,c) = foo(a,b,c)
+function setarg(foo,a)
+ return function(...) return foo(a,...) end
+end
+
+-- Trigger a hotkey
+function hotkey(arg)
+ vlc.var.set( vlc.object.libvlc(), "key-pressed", vlc.config.get( arg ) )
+end
+
+-- Take a video snapshot
+function snapshot()
+ local vout = vlc.object.find(nil,"vout","anywhere")
+ if not vout then return end
+ vlc.var.set(vout,"video-snapshot",nil)
+end
+
+-- Naive (non recursive) table copy
+function table_copy(t)
+ c = {}
+ for i,v in pairs(t) do c[i]=v end
+ return c
+end
+
+-- strip leading and trailing spaces
+function strip(str)
+ return string.gsub(str, "^%s*(.-)%s*$", "%1")
+end
+
+-- print a table (recursively)
+function table_print(t,prefix)
+ local prefix = prefix or ""
+ for a,b in pairs_sorted(t) do
+ print(prefix..tostring(a),b)
+ if type(b)==type({}) then
+ table_print(b,prefix.."\t")
+ end
+ end
+end
+
+-- print the list of callbacks registered in lua
+-- usefull for debug purposes
+function print_callbacks()
+ print "callbacks:"
+ table_print(vlc.callbacks)
+end
+
+-- convert a duration (in seconds) to a string
+function durationtostring(duration)
+ return string.format("%02d:%02d:%02d",
+ math.floor(duration/3600),
+ math.floor(duration/60)%60,
+ math.floor(duration%60))
+end
--- /dev/null
+--[==========================================================================[
+ host.lua: VLC Lua interface command line host module
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+--]==========================================================================]
+
+--[==========================================================================[
+Example use:
+
+ require "host"
+ h = host.host()
+
+ -- Bypass any authentification
+ function on_password( client )
+ client:switch_status( host.status.read )
+ end
+ h.status_callbacks[host.status.password] = on_password
+
+ h:listen( "localhost:4212" )
+ h:listen( "*console" )
+ --or h:listen( { "localhost:4212", "*console" } )
+
+ -- The main loop
+ while not vlc.should_die() do
+ -- accept new connections
+ h:accept()
+
+ -- select active clients
+ local write, read = h:select( 0.1 ) -- 0.1 is a timeout in seconds
+
+ -- handle clients in write mode
+ for _, client in pairs(write) do
+ client:send()
+ client.buffer = ""
+ client:switch_status( host.status.read )
+ end
+
+ -- handle clients in read mode
+ for _, client in pairs(read) do
+ local str = client:recv(1000)
+ str = string.gsub(str,"\r?\n$","")
+ client.buffer = "Got `"..str.."'.\r\n"
+ client:switch_status( host.status.write )
+ end
+ end
+
+For complete examples see existing VLC Lua interface modules (ie telnet.lua)
+--]==========================================================================]
+
+module("host",package.seeall)
+
+status = { init = 0, read = 1, write = 2, password = 3 }
+client_type = { net = 1, stdio = 2, fifo = 3 }
+
+function host()
+ -- private data
+ local clients = {}
+ local listeners = {}
+ local status_callbacks = {}
+
+ -- private methods
+ local function new_fd_set()
+ function foo_fds(foo)
+ return function(fd,...) return foo(fd.fds,...) end
+ end
+ return { -- data
+ fds = vlc.fd.new_fd_set(),
+ -- methods
+ zero = foo_fds( vlc.fd.fd_zero ),
+ set = foo_fds( vlc.fd.fd_set ),
+ isset = foo_fds( vlc.fd.fd_isset ),
+ clr = foo_fds( vlc.fd.fd_clr ),
+ }
+ end
+
+ -- private data
+ local fds_read = new_fd_set()
+ local fds_write = new_fd_set()
+
+ -- private methods
+ local function client_accept( clients, listen )
+ local wait
+ if #clients == 0 then
+ wait = -1
+ else
+ wait = 0
+ end
+ return vlc.net.accept( listen, wait )
+ end
+
+ local function fd_client( client )
+ if client.status == status.read then
+ return client.rfd
+ else -- status.write
+ return client.wfd
+ end
+ end
+
+ local function send( client, data, len )
+ if len then
+ return vlc.net.send( client.wfd, data, len )
+ else
+ return vlc.net.send( client.wfd, data or client.buffer )
+ end
+ end
+
+ local function recv( client, len )
+ if len then
+ return vlc.net.recv( client.rfd, len )
+ else
+ return vlc.net.recv( client.rfd )
+ end
+ end
+
+ local function write( client, data )
+ return vlc.fd.write( client.wfd, data or client.buffer )
+ end
+
+ local function read( client, len )
+ if len then
+ return vlc.fd.read( client.rfd, len )
+ else
+ return vlc.fd.read( client.rfd )
+ end
+ end
+
+ local function del_client( client )
+ if client.type == client_type.stdio then
+ client:send( "Cannot delete stdin/stdout client.\n" )
+ return
+ end
+ for i, c in pairs(clients) do
+ if c == client then
+ if client.type == client_type.net then
+ if client.wfd ~= client.rfd then
+ vlc.net.close( client.rfd )
+ end
+ vlc.net.close( client.wfd )
+ end
+ clients[i] = nil
+ return
+ end
+ end
+ vlc.msg.err("couldn't find client to remove.")
+ end
+
+ local function switch_status( client, s )
+ if client.status == s then return end
+ client.status = s
+ if status_callbacks[s] then
+ status_callbacks[s]( client )
+ end
+ end
+
+ -- append a line to a client's (output) buffer
+ local function append( client, string )
+ client.buffer = client.buffer .. string .. "\r\n"
+ end
+
+ local function new_client( h, fd, wfd, t )
+ if fd < 0 then return end
+ local w, r
+ if t == client_type.net then
+ w = send
+ r = recv
+ else if t == client_type.stdio or t == client_type.fifo then
+ w = write
+ r = read
+ else
+ error("Unknown client type", t )
+ end end
+ local client = { -- data
+ rfd = fd,
+ wfd = wfd or fd,
+ status = status.init,
+ buffer = "",
+ type = t,
+ -- methods
+ fd = fd_client,
+ send = w,
+ recv = r,
+ del = del_client,
+ switch_status = switch_status,
+ append = append,
+ }
+ client:send( "VLC media player "..vlc.version().."\n" )
+ table.insert(clients, client)
+ client:switch_status(status.password)
+ end
+
+ function filter_client( fd, status, status2 )
+ local l = 0
+ fd:zero()
+ for _, client in pairs(clients) do
+ if client.status == status or client.status == status2 then
+ fd:set( client:fd() )
+ l = math.max( l, client:fd() )
+ end
+ end
+ return l
+ end
+
+ -- public methods
+ local function _listen_tcp( h, host, port )
+ if listeners.tcp and listeners.tcp[host]
+ and listeners.tcp[host][port] then
+ error("Already listening on tcp host `"..host..":"..tostring(port).."'")
+ end
+ if not listeners.tcp then
+ listeners.tcp = {}
+ end
+ if not listeners.tcp[host] then
+ listeners.tcp[host] = {}
+ end
+ local listener = vlc.net.listen_tcp( host, port )
+ listeners.tcp[host][port] = listener
+ if not listeners.tcp.list then
+ -- FIXME: if host == "list" we'll have a problem
+ listeners.tcp.list = {}
+ local m = { __mode = "v" } -- week values
+ setmetatable( listeners.tcp.list, m )
+ end
+ table.insert( listeners.tcp.list, listener )
+ end
+
+ local function _listen_stdio( h )
+
+ if listeners.stdio then
+ error("Already listening on stdio")
+ end
+ new_client( h, 0, 1, client_type.stdio )
+ listeners.stdio = true
+ end
+
+ local function _listen( h, url )
+ if type(url)==type({}) then
+ for _,u in pairs(url) do
+ h:listen( u )
+ end
+ else
+ vlc.msg.info( "Listening on host \""..url.."\"." )
+ if url == "*console" then
+ h:listen_stdio()
+ else
+ u = vlc.net.url_parse( url )
+ h:listen_tcp( u.host, u.port )
+ end
+ end
+ end
+
+ local function _accept( h )
+ if listeners.tcp then
+ local wait
+ if #clients == 0 and not listeners.stdio and #listeners.tcp.list == 1 then
+ wait = -1 -- blocking
+ else
+ wait = 0
+ end
+ for _, listener in pairs(listeners.tcp.list) do
+ local fd = vlc.net.accept( listener, wait )
+ new_client( h, fd, fd, client_type.net )
+ end
+ end
+ end
+
+ local function _select( h, timeout )
+ local nfds = math.max( filter_client( fds_read, status.read, status.password ),
+ filter_client( fds_write, status.write ) ) + 1
+ local ret = vlc.net.select( nfds, fds_read.fds, fds_write.fds,
+ timeout or 0.5 )
+ local wclients = {}
+ local rclients = {}
+ if ret > 0 then
+ for _, client in pairs(clients) do
+ if fds_write:isset( client:fd() ) then
+ table.insert(wclients,client)
+ end
+ if fds_read:isset( client:fd() ) then
+ table.insert(rclients,client)
+ end
+ end
+ end
+ return wclients, rclients
+ end
+
+ local function destructor( h )
+ print "destructor"
+ for _,client in pairs(clients) do
+ client:send("Shutting down.")
+ if client.type == client_type.tcp then
+ if client.wfd ~= client.rfd then
+ vlc.net.close(client.rfd)
+ end
+ vlc.net.close(client.wfd)
+ end
+ end
+
+ if listeners.tcp then
+ for _, listener in pairs(listeners.tcp.list) do
+ vlc.net.listen_close( listener )
+ end
+ end
+ end
+
+ local function _broadcast( h, msg )
+ for _,client in pairs(clients) do
+ client:send( msg )
+ end
+ end
+
+ -- the instance
+ local h = { -- data
+ status_callbacks = status_callbacks,
+ -- methods
+ listen = _listen,
+ listen_tcp = _listen_tcp,
+ listen_stdio = _listen_stdio,
+ accept = _accept,
+ select = _select,
+ broadcast = _broadcast,
+ }
+
+ -- the metatable
+ local m = { -- data
+ __metatable = "Nothing to see here. Move along.",
+ -- methods
+ __gc = destructor,
+ }
+
+ setmetatable( h, m )
+
+ return h
+end
--- /dev/null
+--[==========================================================================[
+ rc.lua: remote control module for VLC
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+--]==========================================================================]
+
+--[==========================================================================[
+ This is a modules/control/rc.c look alike (with a bunch of new features)
+
+ Use on local term:
+ vlc -I luarc
+ Use on tcp connection:
+ vlc -I luarc --lua-config "rc={host='localhost:4212'}"
+ Use on multiple hosts (term + 2 tcp ports):
+ vlc -I luarc --lua-config "rc={hosts={'*console','localhost:4212','localhost:5678'}}"
+
+ Note:
+ -I luarc is an alias for -I lua --lua-intf rc
+--]==========================================================================]
+
+require("common")
+skip = common.skip
+skip2 = function(foo) return skip(skip(foo)) end
+setarg = common.setarg
+strip = common.strip
+
+--[[ Setup default environement ]]
+env = { prompt = "> ";
+ width = 70;
+ autocompletion = 1;
+ autoalias = 1;
+ welcome = "Remote control interface initialized. Type `help' for help."
+ }
+
+--[[ Command functions ]]
+function set_env(name,client,value)
+ if value then
+ local var,val = split_input(value)
+ if val then
+ s = string.gsub(val,"\"(.*)\"","%1")
+ if type(client.env[var])==type(1) then
+ client.env[var] = tonumber(s)
+ else
+ client.env[var] = s
+ end
+ else
+ client:append( tostring(client.env[var]) )
+ end
+ else
+ for e,v in common.pairs_sorted(client.env) do
+ client:append(e.."="..v)
+ end
+ end
+end
+
+function save_env(name,client,value)
+ env = common.table_copy(client.env)
+end
+
+function alias(client,value)
+ if value then
+ local var,val = split_input(value)
+ if commands[var] and type(commands[var]) ~= type("") then
+ client:append("Error: cannot use a primary command as an alias name")
+ else
+ if commands[val] then
+ commands[var]=val
+ else
+ client:append("Error: unknown primary command `"..val.."'.")
+ end
+ end
+ else
+ for c,v in common.pairs_sorted(commands) do
+ if type(v)==type("") then
+ client:append(c.."="..v)
+ end
+ end
+ end
+end
+
+function fixme(name,client)
+ client:append( "FIXME: unimplemented command `"..name.."'." )
+end
+
+function logout(name,client)
+ if client.type == host.client_type.net then
+ client:send("Bye-bye!")
+ client:del()
+ else
+ client:append("Error: Can't logout of stdin/stdout. Use quit or shutdown to close VLC.")
+ end
+end
+
+function shutdown(name,client)
+ client:append("Bye-bye!")
+ h:broadcast("Shutting down.")
+ vlc.msg.err("Requested shutdown.")
+ vlc.quit()
+end
+
+function quit(name,client)
+ if client.type == host.client_type.net then
+ logout(name,client)
+ else
+ shutdown(name,client)
+ end
+end
+
+function add(name,client,arg)
+ -- TODO: parse (and use) options
+ local f
+ if name == "enqueue" then
+ f = vlc.playlist.enqueue
+ else
+ f = vlc.playlist.add
+ end
+ f({{path=arg}})
+end
+
+function playlist(name,client,arg)
+ -- TODO: add possibility to filter playlist items using a mask
+ local playlist
+ if arg == "ml" then
+ playlist = vlc.playlist.get(true)
+ client:append("+----[ Playlist - Media Library ]")
+ else
+ playlist = vlc.playlist.get()
+ client:append("+----[ Playlist ]")
+ end
+ for i, item in pairs(playlist) do
+ local str = "| "..tostring(i).." - "..item.name
+ if item.duration > 0 then
+ str = str.." ("..common.durationtostring(item.duration)..")"
+ end
+ if item.nb_played > 0 then
+ str = str.." played "..tostring(item.nb_played).." time"
+ if item.nb_played > 1 then
+ str = str .. "s"
+ end
+ end
+ client:append(str)
+ end
+ client:append("+----[ End of playlist ]")
+end
+
+function help(name,client,arg)
+ local width = client.env.width
+ local long = (name == "longhelp")
+ local extra = ""
+ if arg then extra = "matching `" .. arg .. "' " end
+ client:append("+----[ Remote control commands "..extra.."]")
+ for i, cmd in ipairs(commands_ordered) do
+ if (cmd == "" or not commands[cmd].adv or long)
+ and (not arg or string.match(cmd,arg)) then
+ local str = "| " .. cmd
+ if cmd ~= "" then
+ local val = commands[cmd]
+ if val.aliases then
+ for _,a in ipairs(val.aliases) do
+ str = str .. ", " .. a
+ end
+ end
+ if val.args then str = str .. " " .. val.args end
+ if #str%2 == 1 then str = str .. " " end
+ str = str .. string.rep(" .",(width-(#str+#val.help)-1)/2)
+ str = str .. string.rep(" ",width-#str-#val.help) .. val.help
+ end
+ client:append(str)
+ end
+ end
+ client:append("+----[ end of help ]")
+end
+
+function input_info(name,client)
+ local categories = vlc.input_info()
+ for cat, infos in pairs(categories) do
+ client:append("+----[ "..cat.." ]")
+ client:append("|")
+ for name, value in pairs(infos) do
+ client:append("| "..name..": "..value)
+ end
+ client:append("|")
+ end
+ client:append("+----[ end of stream info ]")
+end
+
+function playlist_status(name,client)
+ local a,b,c = vlc.playlist.status()
+ client:append( "( new input: " .. tostring(a) .. " )" )
+ client:append( "( audio volume: " .. tostring(b) .. " )")
+ client:append( "( state " .. tostring(c) .. " )")
+end
+
+function is_playing(name,client)
+ if vlc.is_playing() then client:append "1" else client:append "0" end
+end
+
+function ret_print(foo,start,stop)
+ local start = start or ""
+ local stop = stop or ""
+ return function(discard,client,...) client:append(start..tostring(foo(...))..stop) end
+end
+
+function get_time(var,client)
+ return function()
+ local input = vlc.object.input()
+ client:append(math.floor(vlc.var.get( input, var )))
+ end
+end
+
+function titlechap(name,client,value)
+ local input = vlc.object.input()
+ local var = string.gsub( name, "_.*$", "" )
+ if value then
+ vlc.var.set( input, var, value )
+ else
+ local item = vlc.var.get( input, var )
+ -- Todo: add item name conversion
+ client:apped(item)
+ end
+end
+function titlechap_offset(client,offset)
+ return function(name,value)
+ local input = vlc.object.input()
+ local var = string.gsub( name, "_.*$", "" )
+ vlc.var.set( input, var, vlc.var.get( input, var )+offset )
+ end
+end
+
+function seek(name,client,value)
+ local input = vlc.object.input()
+ if string.sub(value,#value)=="%" then
+ vlc.var.set(input,"position",tonumber(string.sub(value,1,#value-1))/100.)
+ else
+ vlc.var.set(input,"time",tonumber(value))
+ end
+end
+
+function volume(name,client,value)
+ if value then
+ vlc.volume.set(value)
+ else
+ client:append(tostring(vlc.volume.get()))
+ end
+end
+
+function rate(name,client)
+ local input = vlc.object.input()
+ if name == "normal" then
+ vlc.var.set(input,"rate",1000) -- FIXME: INPUT_RATE_DEFAULT
+ else
+ vlc.var.set(input,"rate-"..name,nil)
+ end
+end
+
+function listvalue(obj,var)
+ return function(client,value)
+ local o = vlc.object.find(nil,obj,"anywhere")
+ if not o then return end
+ if value then
+ vlc.var.set( o, var, value )
+ else
+ local c = vlc.var.get( o, var )
+ local v, l = vlc.var.get_list( o, var )
+ client:append("+----[ "..var.." ]")
+ for i,val in ipairs(v) do
+ local mark = (val==c)and " *" or ""
+ client:append("| "..tostring(val).." - "..tostring(l[i])..mark)
+ end
+ client:append("+----[ end of "..var.." ]")
+ end
+ end
+end
+
+function eval(client,val)
+ client:append(loadstring("return "..val)())
+end
+
+--[[ Declare commands, register their callback functions and provide
+ help strings here.
+ Syntax is:
+ "<command name>"; { func = <function>; [ args = "<str>"; ] help = "<str>"; [ adv = <bool>; ] [ aliases = { ["<str>";]* }; ] }
+ ]]
+commands_ordered = {
+ { "add"; { func = add; args = "XYZ"; help = "add XYZ to playlist" } };
+ { "enqueue"; { func = add; args = "XYZ"; help = "queue XYZ to playlist" } };
+ { "playlist"; { func = playlist; help = "show items currently in playlist" } };
+ { "play"; { func = skip2(vlc.playlist.play); help = "play stream" } };
+ { "stop"; { func = skip2(vlc.playlist.stop); help = "stop stream" } };
+ { "next"; { func = skip2(vlc.playlist.next); help = "next playlist item" } };
+ { "prev"; { func = skip2(vlc.playlist.prev); help = "previous playlist item" } };
+ { "goto"; { func = skip2(vlc.playlist.goto); help = "goto item at index" } };
+ { "repeat"; { func = skip2(vlc.playlist.repeat_); args = "[on|off]"; help = "toggle playlist repeat" } };
+ { "loop"; { func = skip2(vlc.playlist.loop); args = "[on|off]"; help = "toggle playlist loop" } };
+ { "random"; { func = skip2(vlc.playlist.random); args = "[on|off]"; help = "toggle playlist random" } };
+ { "clear"; { func = skip2(vlc.playlist.clear); help = "clear the playlist" } };
+ { "status"; { func = playlist_status; help = "current playlist status" } };
+ { "title"; { func = titlechap; args = "[X]"; help = "set/get title in current item" } };
+ { "title_n"; { func = titlechap_offset(1); help = "next title in current item" } };
+ { "title_p"; { func = titlechap_offset(-1); help = "previous title in current item" } };
+ { "chapter"; { func = titlechap; args = "[X]"; help = "set/get chapter in current item" } };
+ { "chapter_n"; { func = titlechap_offset(1); help = "next chapter in current item" } };
+ { "chapter_p"; { func = titlechap_offset(-1); help = "previous chapter in current item" } };
+ { "" };
+ { "seek"; { func = seek; args = "X"; help = "seek in seconds, for instance `seek 12'" } };
+ { "pause"; { func = setarg(common.hotkey,"key-play-pause"); help = "toggle pause" } };
+ { "fastforward"; { func = setarg(common.hotkey,"key-jump+extrashort"); help = "set to maximum rate" } };
+ { "rewind"; { func = setarg(common.hotkey,"key-jump-extrashort"); help = "set to minimum rate" } };
+ { "faster"; { func = rate; help = "faster playing of stream" } };
+ { "slower"; { func = rate; help = "slower playing of stream" } };
+ { "normal"; { func = rate; help = "normal playing of stream" } };
+ { "fullscreen"; { func = skip2(vlc.fullscreen); args = "[on|off]"; help = "toggle fullscreen"; aliases = { "f", "F" } } };
+ { "info"; { func = input_info; help = "information about the current stream" } };
+ { "get_time"; { func = get_time("time"); help = "seconds elapsed since stream's beginning" } };
+ { "is_playing"; { func = is_playing; help = "1 if a stream plays, 0 otherwise" } };
+ { "get_title"; { func = ret_print(vlc.get_title); help = "the title of the current stream" } };
+ { "get_length"; { func = get_time("length"); help = "the length of the current stream" } };
+ { "" };
+ { "volume"; { func = volume; args = "[X]"; help = "set/get audio volume" } };
+ { "volup"; { func = ret_print(vlc.volume.up,"( audio volume: "," )"); args = "[X]"; help = "raise audio volume X steps" } };
+ { "voldown"; { func = ret_print(vlc.volume.down,"( audio volume: "," )"); args = "[X]"; help = "lower audio volume X steps" } };
+ { "adev"; { func = skip(listvalue("aout","audio-device")); args = "[X]"; help = "set/get audio device" } };
+ { "achan"; { func = skip(listvalue("aout","audio-channels")); args = "[X]"; help = "set/get audio channels" } };
+ { "atrack"; { func = skip(listvalue("input","audio-es")); args = "[X]"; help = "set/get audio track" } };
+ { "vtrack"; { func = skip(listvalue("input","video-es")); args = "[X]"; help = "set/get video track" } };
+ { "vratio"; { func = skip(listvalue("vout","aspect-ratio")); args = "[X]"; help = "set/get video aspect ratio" } };
+ { "vcrop"; { func = skip(listvalue("vout","crop")); args = "[X]"; help = "set/get video crop"; aliases = { "crop" } } };
+ { "vzoom"; { func = skip(listvalue("vout","zoom")); args = "[X]"; help = "set/get video zoom"; aliases = { "zoom" } } };
+ { "snapshot"; { func = common.snapshot; help = "take video snapshot" } };
+ { "strack"; { func = skip(listvalue("input","spu-es")); args = "[X]"; help = "set/get subtitles track" } };
+ { "hotkey"; { func = skip(common.hotkey); args = "[hotkey name]"; help = "simulate hotkey press"; adv = true; aliases = { "key" } } };
+ { "menu"; { func = fixme; args = "[on|off|up|down|left|right|select]"; help = "use menu"; adv = true } };
+ { "" };
+ { "set"; { func = set_env; args = "[var [value]]"; help = "set/get env var"; adv = true } };
+ { "save_env"; { func = save_env; help = "save env vars (for future clients)"; adv = true } };
+ { "alias"; { func = skip(alias); args = "[cmd]"; help = "set/get command aliases"; adv = true } };
+ { "eval"; { func = skip(eval); help = "eval some lua (*debug*)"; adv =true } }; -- FIXME: comment out if you're not debugging
+ { "help"; { func = help; args = "[pattern]"; help = "a help message"; aliases = { "?" } } };
+ { "longhelp"; { func = help; args = "[pattern]"; help = "a longer help message" } };
+ { "logout"; { func = logout; help = "exit (if in a socket connection)" } };
+ { "quit"; { func = quit; help = "quit VLC (or logout if in a socket connection)" } };
+ { "shutdown"; { func = shutdown; help = "shutdown VLC" } };
+ }
+commands = {}
+for i, cmd in ipairs( commands_ordered ) do
+ if #cmd == 2 then
+ commands[cmd[1]]=cmd[2]
+ if cmd[2].aliases then
+ for _,a in ipairs(cmd[2].aliases) do
+ commands[a]=cmd[1]
+ end
+ end
+ end
+ commands_ordered[i]=cmd[1]
+end
+--[[ From now on commands_ordered is a list of the different command names
+ and commands is a associative array indexed by the command name. ]]
+
+-- Compute the column width used when printing a the autocompletion list
+env.colwidth = 0
+for c,_ in pairs(commands) do
+ if #c > env.colwidth then env.colwidth = #c end
+end
+env.coldwidth = env.colwidth + 1
+
+-- Count unimplemented functions
+do
+ local count = 0
+ local list = "("
+ for c,v in pairs(commands) do
+ if v.func == fixme then
+ count = count + 1
+ if count ~= 1 then
+ list = list..","
+ end
+ list = list..c
+ end
+ end
+ list = list..")"
+ if count ~= 0 then
+ env.welcome = env.welcome .. "\r\nWarning: "..count.." functions are still unimplemented "..list.."."
+ end
+end
+
+--[[ Utils ]]
+function split_input(input)
+ local input = strip(input)
+ local s = string.find(input," ")
+ if s then
+ return string.sub(input,0,s-1), strip(string.sub(input,s))
+ else
+ return input
+ end
+end
+
+function call_command(cmd,client,arg)
+ if type(commands[cmd]) == type("") then
+ cmd = commands[cmd]
+ end
+ local ok, msg
+ if arg ~= nil then
+ ok, msg = pcall( commands[cmd].func, cmd, client, arg )
+ else
+ ok, msg = pcall( commands[cmd].func, cmd, client )
+ end
+ if not ok then
+ local a = arg or ""
+ if a ~= "" then a = " " .. a end
+ client:append("Error in `"..cmd..a.."' ".. msg)
+ end
+end
+
+function call_libvlc_command(cmd,client,arg)
+ local ok, vlcerr, vlcmsg = pcall( vlc.libvlc_command, cmd, arg )
+ if not ok then
+ local a = arg or ""
+ if a ~= "" then a = " " .. a end
+ client:append("Error in `"..cmd..a.."' ".. vlcerr) -- when pcall fails, the 2nd arg is the error message.
+ end
+ return vlcerr
+end
+
+--[[ Setup host ]]
+require("host")
+h = host.host()
+-- No auth
+h.status_callbacks[host.status.password] = function(client)
+ client.env = common.table_copy( env )
+ client:send( client.env.welcome .. "\r\n")
+ client:switch_status(host.status.read)
+end
+-- Print prompt when switching a client's status to `read'
+h.status_callbacks[host.status.read] = function(client) client:send( client.env.prompt ) end
+
+h:listen( config.hosts or config.host or "*console" )
+
+--[[ The main loop ]]
+while not vlc.should_die() do
+ h:accept()
+ local write, read = h:select(0.1)
+
+ for _, client in pairs(write) do
+ local len = client:send()
+ client.buffer = string.sub(client.buffer,len+1)
+ if client.buffer == "" then client:switch_status(host.status.read) end
+ end
+
+ for _, client in pairs(read) do
+ local input = client:recv(1000)
+ if string.match(input,"\n$") then
+ client.buffer = string.gsub(client.buffer..input,"\r?\n$","")
+ done = true
+ elseif client.buffer == ""
+ and ((client.type == host.client_type.stdio and input == "")
+ or (client.type == host.client_type.net and input == "\004")) then
+ -- Caught a ^D
+ client.buffer = "quit"
+ done = true
+ else
+ client.buffer = client.buffer .. input
+ end
+ if done then
+ local cmd,arg = split_input(client.buffer)
+ client.buffer = ""
+ client:switch_status(host.status.write)
+ if commands[cmd] then
+ call_command(cmd,client,arg)
+ else
+ if client.type == host.client_type.stdio
+ and call_libvlc_command(cmd,client,arg) == 0 then
+ else
+ local choices = {}
+ if client.env.autocompletion ~= 0 then
+ for v,_ in common.pairs_sorted(commands) do
+ if string.sub(v,0,#cmd)==cmd then
+ table.insert(choices, v)
+ end
+ end
+ end
+ if #choices == 1 and client.env.autoalias ~= 0 then
+ -- client:append("Aliasing to \""..choices[1].."\".")
+ cmd = choices[1]
+ call_command(cmd,client,arg)
+ else
+ client:append("Unknown command `"..cmd.."'. Type `help' for help.")
+ if #choices ~= 0 then
+ client:append("Possible choices are:")
+ local cols = math.floor(client.env.width/(client.env.colwidth+1))
+ local fmt = "%-"..client.env.colwidth.."s"
+ for i = 1, #choices do
+ choices[i] = string.format(fmt,choices[i])
+ end
+ for i = 1, #choices, cols do
+ local j = i + cols - 1
+ if j > #choices then j = #choices end
+ client:append(" "..table.concat(choices," ",i,j))
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+--[==========================================================================[
+ telnet.lua: VLM interface plugin
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+--]==========================================================================]
+
+--[==========================================================================[
+ Copy (features wise) of the original VLC modules/control/telnet.c module.
+
+ Differences are:
+ * it's in Lua
+ * 'lock' command to lock the telnet promt
+ * possibility to listen on different hosts including stdin
+ for example:
+ listen on stdin: vlc -I lua --lua-intf telnet --lua-config "telnet={host='*console'}"
+ listen on stdin + 2 ports on localhost: vlc -I lua --lua-intf telnet --lua-config "telnet={hosts={'localhost:4212','localhost:5678','*console'}}"
+--]==========================================================================]
+
+require "host"
+
+--[[ Some telnet command special characters ]]
+WILL = "\251" -- Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
+WONT = "\252" -- Indicates the refusal to perform, or continue performing, the indicated option.
+DO = "\253" -- Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option.
+DONT = "\254" -- Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option.
+IAC = "\255" -- Interpret as command
+
+ECHO = "\001"
+
+--[[ Client status change callbacks ]]
+function on_password( client )
+ if client.type == host.client_type.net then
+ client:send( "Password: " ..IAC..WILL..ECHO )
+ else
+ -- no authentification needed on stdin
+ client:switch_status( host.status.read )
+ end
+end
+function on_read( client )
+ client:send( "> " )
+end
+function on_write( client )
+end
+
+--[[ Misc functions ]]
+function telnet_commands( client )
+ -- remove telnet command replies from the client's data
+ client.buffer = string.gsub( client.buffer, IAC.."["..DO..DONT..WILL..WONT.."].", "" )
+end
+
+function vlm_message_to_string(client,message,prefix)
+ local prefix = prefix or ""
+ if message.value then
+ client:append(prefix .. message.name .. " : " .. message.value)
+ return
+ else
+ client:append(prefix .. message.name)
+ if message.children then
+ for i,c in ipairs(message.children) do
+ vlm_message_to_string(client,c,prefix.." ")
+ end
+ end
+ return
+ end
+end
+
+--[[ Configure the host ]]
+h = host.host()
+
+h.status_callbacks[host.status.password] = on_password
+h.status_callbacks[host.status.read] = on_read
+h.status_callbacks[host.status.write] = on_write
+
+h:listen( config.hosts or config.host or "localhost:4212" )
+password = config.password or "admin"
+
+--[[ Launch vlm ]]
+vlm = vlc.vlm.new()
+
+--[[ Commands ]]
+function shutdown(client)
+ h:broadcast("Shutting down.\r\n")
+ vlc.msg.err("shutdown requested")
+ vlc.quit()
+ return true
+end
+function logout(client)
+ client:del()
+ return true
+end
+function quit(client)
+ if client.type == host.client_type.net then
+ return logout(client)
+ else
+ return shutdown(client)
+ end
+end
+function lock(client)
+ client:send("\r\n")
+ client:switch_status( host.status.password )
+ client.buffer = ""
+ return false
+end
+function help(client)
+ client:append(" Telnet Specific Commands:")
+ for c,t in pairs(commands) do
+ client:append(" "..c.." : "..t.help)
+ end
+ return true
+end
+commands = {
+ ["shutdown"] = { func = shutdown, help = "shutdown VLC" },
+ ["quit"] = { func = quit, help = "logout from telnet/shutdown VLC from local shell" },
+ ["logout"] = { func = logout, help = "logout" },
+ ["lock"] = { func = lock, help = "lock the telnet prompt" },
+ ["help"] = { func = help, help = "show this help", dovlm = true },
+ }
+
+function client_command( client )
+ local cmd = client.buffer
+ client.buffer = ""
+ if not commands[cmd] or not commands[cmd].func or commands[cmd].dovlm then
+ -- if it's not an interface specific command, it has to be a VLM command
+ message = vlc.vlm.execute_command( vlm, cmd )
+ vlm_message_to_string( client, message )
+ if not commands[cmd] or not commands[cmd].func and not commands[cmd].dovlm then
+ return true
+ end
+ end
+ ok, msg = pcall( commands[cmd].func, client )
+ if not ok then
+ client:append( "Error in `"..cmd.."' "..msg )
+ return true
+ end
+ return msg
+end
+
+--[[ The main loop ]]
+while not vlc.should_die() do
+ h:accept()
+ local w, r = h:select( 0.1 )
+
+ -- Handle writes
+ for _, client in pairs(w) do
+ local len = client:send()
+ client.buffer = string.sub(client.buffer,len+1)
+ if client.buffer == "" then client:switch_status( host.status.read ) end
+ end
+
+ -- Handle reads
+ for _, client in pairs(r) do
+ local str = client:recv(1000)
+ local done = false
+ if string.match(str,"\n$") then
+ client.buffer = string.gsub(client.buffer..str,"\r?\n$","")
+ done = true
+ elseif client.buffer == ""
+ and ((client.type == host.client_type.stdio and str == "")
+ or (client.type == host.client_type.net and str == "\004")) then
+ -- Caught a ^D
+ client.buffer = "quit"
+ done = true
+ else
+ client.buffer = client.buffer .. str
+ end
+ if client.type == host.client_type.net then
+ telnet_commands( client )
+ end
+ if done then
+ if client.status == host.status.password then
+ if client.buffer == password then
+ client:send( IAC..WONT..ECHO.."\r\nWelcome, Master\r\n" )
+ client.buffer = ""
+ client:switch_status( host.status.write )
+ else
+ client:send( "\r\nWrong password\r\nPassword: " )
+ client.buffer = ""
+ end
+ elseif client_command( client ) then
+ client:switch_status( host.status.write )
+ end
+ end
+ end
+end
+
+--[[ Clean up ]]
+vlc.vlm.delete( vlm )