Commit de31813f authored by Antoine Cellerier's avatar Antoine Cellerier

Add a new type of VLC Lua module: Interfaces.

Two things led me to add this new type of Lua modules:
 * Most interfaces duplicate code from one another (rc and telnet both deal
   with sockets, rc and hotkeys duplicate VLC interfacing code)
 * It's easier to code in Lua than in C (for high level stuff)
(* Users can code their own module easily, all they need is a text editor.)

Most of the changes in modules/misc/ are Lua wrappers for different C APIs
in VLC. I'd say that 90% of those wrappers won't change, the other 10% might
 need small changes (mostly to simplify the C code).

The VLC Lua "interface" module will look for lua scripts in the
*share*/luaintf/ directory. Files in *share*/luaintf/modules/ are lua modules
which can be used by multiple VLC Lua modules (using require "modulename").

This commit includes 4 Lua interface modules:
 * dummy.lua: the default module. Prints error messages and quits.
 * rc.lua: equivalent to the legacy rc.c module. Also includes a bunch of
   new features (+ multiple socket connections at a time work!). See file for
   more details.
 * telnet.lua: equivalent to the legacy telnet.c module. See file for more
   details.
 * hotkeys.lua: a rewrite of the hotkey handling module. This module is still
   experimental. I'll need to change VLC core hotkeys stuff to make it work
   like I want (ie: 1 hotkey triggers 1 action instead of the current 1 action
   can have 1 hotkey). This version executes 3 dummy actions when keys 'q',
   'w' or 'x' are pressed.

What's left to do:
 * Port the http interface plugin to this framework (at least for the
   macros/rpn part ... using <?vlc some lua code ?> à la PHP would be way
   easier than what we currently have).
 * Finish work on the hotkeys module.
 * Code a real telnet/rc module with autocompletion and all the cool features
   usually found in a telnet/terminal interface.
 * Trash the legacy C modules.

Stuff to test (which I can't test myself):
 * Win32 and Mac OS X specific changes to Makefile.am
 * Console interface under Win32. I expect it not to work.

Other stuff included in this changeset are:
 * Code cleanup (I'm sure that it's still possible to simplify some of the old lua bindings code).
 * That's pretty much it in fact :/
parent 154d9e74
...@@ -433,6 +433,14 @@ VLC-release.app: vlc ...@@ -433,6 +433,14 @@ VLC-release.app: vlc
for i in $(srcdir)/share/luameta/*.* ; do \ for i in $(srcdir)/share/luameta/*.* ; do \
$(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luameta/`basename $${i}` ; \ $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luameta/`basename $${i}` ; \
done ; \ 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/dialogs
$(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/js $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/js
$(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/old $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/old
...@@ -733,6 +741,14 @@ package-win32-base-debug: package-win-common ...@@ -733,6 +741,14 @@ package-win32-base-debug: package-win-common
for i in $(srcdir)/share/luameta/*.* ; do \ for i in $(srcdir)/share/luameta/*.* ; do \
$(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luameta/`basename $${i}` ; \ $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luameta/`basename $${i}` ; \
done ; 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" mkdir -p "$(top_builddir)/vlc-${VERSION}/osdmenu"
cp $(srcdir)/share/osdmenu/*.* "$(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
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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;
}
/***************************************************************************** /*****************************************************************************
* luameta.c: Get meta/artwork using lua scripts * meta.c: Get meta/artwork using lua scripts
***************************************************************************** *****************************************************************************
* Copyright (C) 2007 the VideoLAN team * Copyright (C) 2007 the VideoLAN team
* $Id$ * $Id$
...@@ -38,17 +38,18 @@ ...@@ -38,17 +38,18 @@
#include <vlc_stream.h> #include <vlc_stream.h>
#include <vlc_charset.h> #include <vlc_charset.h>
#include "vlclua.h" #include "vlc.h"
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static int fetch_meta( vlc_object_t *p_this, const char * 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 );
static int fetch_art( vlc_object_t *p_this, const char * 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 );
static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ); static lua_State *vlclua_meta_init( vlc_object_t *p_this,
input_item_t * p_item );
/***************************************************************************** /*****************************************************************************
...@@ -57,6 +58,20 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ...@@ -57,6 +58,20 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item
/* Functions to register */ /* Functions to register */
static luaL_Reg p_reg[] = 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_new", vlclua_stream_new },
{ "stream_read", vlclua_stream_read }, { "stream_read", vlclua_stream_read },
{ "stream_readline", vlclua_stream_readline }, { "stream_readline", vlclua_stream_readline },
...@@ -75,8 +90,8 @@ static luaL_Reg p_reg[] = ...@@ -75,8 +90,8 @@ static luaL_Reg p_reg[] =
*****************************************************************************/ *****************************************************************************/
static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ) static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item )
{ {
lua_State * p_state = luaL_newstate(); lua_State * L = luaL_newstate();
if( !p_state ) if( !L )
{ {
msg_Err( p_this, "Could not create new Lua State" ); msg_Err( p_this, "Could not create new Lua State" );
return NULL; return NULL;
...@@ -84,40 +99,40 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ...@@ -84,40 +99,40 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item
char *psz_meta; char *psz_meta;
/* Load Lua libraries */ /* 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_pushlightuserdata( L, p_this );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" ); lua_setfield( L, -2, "private" );
psz_meta = input_item_GetName( p_item ); psz_meta = input_item_GetName( p_item );
lua_pushstring( p_state, psz_meta ); lua_pushstring( L, psz_meta );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "name" ); lua_setfield( L, -2, "name" );
free( psz_meta ); free( psz_meta );
psz_meta = input_item_GetArtist( p_item ); psz_meta = input_item_GetArtist( p_item );
lua_pushstring( p_state, psz_meta ); lua_pushstring( L, psz_meta );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "artist" ); lua_setfield( L, -2, "artist" );
free( psz_meta ); free( psz_meta );
psz_meta = input_item_GetTitle( p_item ) ; psz_meta = input_item_GetTitle( p_item ) ;
lua_pushstring( p_state, psz_meta ); lua_pushstring( L, psz_meta );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "title" ); lua_setfield( L, -2, "title" );
free( psz_meta ); free( psz_meta );
psz_meta = input_item_GetAlbum( p_item ); psz_meta = input_item_GetAlbum( p_item );
lua_pushstring( p_state, psz_meta ); lua_pushstring( L, psz_meta );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "album" ); lua_setfield( L, -2, "album" );
free( psz_meta ); free( psz_meta );
psz_meta = input_item_GetArtURL( p_item ); psz_meta = input_item_GetArtURL( p_item );
lua_pushstring( p_state, psz_meta ); lua_pushstring( L, psz_meta );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "arturl" ); lua_setfield( L, -2, "arturl" );
free( psz_meta ); free( psz_meta );
/* XXX: all should be passed ( could use macro ) */ /* XXX: all should be passed ( could use macro ) */
return p_state; return L;
} }
/***************************************************************************** /*****************************************************************************
...@@ -125,7 +140,7 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ...@@ -125,7 +140,7 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item
* pointed by psz_filename. * pointed by psz_filename.
*****************************************************************************/ *****************************************************************************/
static int fetch_art( vlc_object_t *p_this, const char * 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; int i_ret = VLC_EGENERIC;
input_item_t * p_input = user_data; input_item_t * p_input = user_data;
...@@ -133,44 +148,44 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, ...@@ -133,44 +148,44 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
/* Ugly hack to delete previous versions of the fetchart() /* Ugly hack to delete previous versions of the fetchart()
* functions. */ * functions. */
lua_pushnil( p_state ); lua_pushnil( L );
lua_setglobal( p_state, "fetch_art" ); lua_setglobal( L, "fetch_art" );
/* Load and run the script(s) */ /* 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, msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; 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, " msg_Warn( p_this, "Error while runing script %s, "
"function fetch_art() not found", psz_filename ); "function fetch_art() not found", psz_filename );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; 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, " msg_Warn( p_this, "Error while runing script %s, "
"function fetch_art(): %s", psz_filename, "function fetch_art(): %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if((s = lua_gettop( p_state ))) if((s = lua_gettop( L )))
{ {
const char * psz_value; 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 ) if( psz_value && *psz_value != 0 )
{ {
lua_Dbg( p_this, "setting arturl: %s", psz_value ); lua_Dbg( p_this, "setting arturl: %s", psz_value );
...@@ -178,7 +193,7 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, ...@@ -178,7 +193,7 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
i_ret = VLC_SUCCESS; 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: " msg_Err( p_this, "Lua art fetcher script %s: "
"didn't return a string", psz_filename ); "didn't return a string", psz_filename );
...@@ -197,51 +212,50 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, ...@@ -197,51 +212,50 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
* pointed by psz_filename. * pointed by psz_filename.
*****************************************************************************/ *****************************************************************************/
static int fetch_meta( vlc_object_t *p_this, const char * 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; input_item_t * p_input = user_data;
int t;
/* Ugly hack to delete previous versions of the fetchmeta() /* In lua, setting a variable to nil is equivalent to deleting it */
* functions. */ lua_pushnil( L );
lua_pushnil( p_state ); lua_setglobal( L, "fetch_meta" );
lua_setglobal( p_state, "fetch_meta" );
/* Load and run the script(s) */ /* 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, msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; 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, " msg_Warn( p_this, "Error while runing script %s, "
"function fetch_meta() not found", psz_filename ); "function fetch_meta() not found", psz_filename );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; 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, " msg_Warn( p_this, "Error while runing script %s, "
"function fetch_meta(): %s", psz_filename, "function fetch_meta(): %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; 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 ); /* ... item */
vlclua_read_custom_meta_data( p_this, p_state, t, t+1, p_input ); vlclua_read_meta_data( p_this, L, p_input );
vlclua_read_custom_meta_data( p_this, L, p_input );
} }
else else
{ {
...@@ -267,10 +281,10 @@ int E_(FindMeta)( vlc_object_t *p_this ) ...@@ -267,10 +281,10 @@ int E_(FindMeta)( vlc_object_t *p_this )
{ {
meta_engine_t *p_me = (meta_engine_t *)p_this; meta_engine_t *p_me = (meta_engine_t *)p_this;
input_item_t *p_item = p_me->p_item; 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 ); int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, L, p_item );
lua_close( p_state ); lua_close( L );
return i_ret; return i_ret;
} }
...@@ -281,10 +295,10 @@ int E_(FindArt)( vlc_object_t *p_this ) ...@@ -281,10 +295,10 @@ int E_(FindArt)( vlc_object_t *p_this )
{ {
playlist_t *p_playlist = (playlist_t *)p_this; playlist_t *p_playlist = (playlist_t *)p_this;
input_item_t *p_item = (input_item_t *)(p_playlist->p_private); 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 ); int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, L, p_item );
lua_close( p_state ); lua_close( L );
return i_ret; return i_ret;
} }
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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;
}
/***************************************************************************** /*****************************************************************************
* luaplaylist.c : Lua playlist demux module * playlist.c : Lua playlist demux module
***************************************************************************** *****************************************************************************
* Copyright (C) 2007 the VideoLAN team * Copyright (C) 2007 the VideoLAN team
* $Id$ * $Id$
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
# include <sys/stat.h> # include <sys/stat.h>
#endif #endif
#include "vlclua.h" #include "vlc.h"
/***************************************************************************** /*****************************************************************************
...@@ -49,7 +49,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ); ...@@ -49,7 +49,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args );
*****************************************************************************/ *****************************************************************************/
struct demux_sys_t struct demux_sys_t
{ {
lua_State *p_state; lua_State *L;
char *psz_filename; char *psz_filename;
}; };
...@@ -57,48 +57,48 @@ struct demux_sys_t ...@@ -57,48 +57,48 @@ struct demux_sys_t
* *
*****************************************************************************/ *****************************************************************************/
static int vlclua_demux_peek( lua_State *p_state ) static int vlclua_demux_peek( lua_State *L )
{ {
demux_t *p_demux = (demux_t *)vlclua_get_this( p_state ); demux_t *p_demux = (demux_t *)vlclua_get_this( L );
int i = lua_gettop( p_state ); int i = lua_gettop( L );
int n; int n;
byte_t *p_peek; const uint8_t *p_peek;
int i_peek; int i_peek;
if( !i ) return 0; if( !i ) return 0;
n = lua_tonumber( p_state, 1 ); n = lua_tonumber( L, 1 );
lua_pop( p_state, i ); lua_pop( L, i );
i_peek = stream_Peek( p_demux->s, &p_peek, n ); i_peek = stream_Peek( p_demux->s, &p_peek, n );
lua_pushlstring( p_state, (const char *)p_peek, i_peek ); lua_pushlstring( L, (const char *)p_peek, i_peek );
return 1; return 1;
} }
static int vlclua_demux_read( lua_State *p_state ) static int vlclua_demux_read( lua_State *L )
{ {
demux_t *p_demux = (demux_t *)vlclua_get_this( p_state ); demux_t *p_demux = (demux_t *)vlclua_get_this( L );
int i = lua_gettop( p_state ); int i = lua_gettop( L );
int n; int n;
byte_t *p_read; byte_t *p_read;
int i_read; int i_read;
if( !i ) return 0; if( !i ) return 0;
n = lua_tonumber( p_state, 1 ); n = lua_tonumber( L, 1 );
lua_pop( p_state, i ); lua_pop( L, i );
i_read = stream_Read( p_demux->s, &p_read, n ); i_read = stream_Read( p_demux->s, &p_read, n );
lua_pushlstring( p_state, (const char *)p_read, i_read ); lua_pushlstring( L, (const char *)p_read, i_read );
return 1; return 1;
} }
static int vlclua_demux_readline( lua_State *p_state ) static int vlclua_demux_readline( lua_State *L )
{ {
demux_t *p_demux = (demux_t *)vlclua_get_this( p_state ); demux_t *p_demux = (demux_t *)vlclua_get_this( L );
char *psz_line = stream_ReadLine( p_demux->s ); char *psz_line = stream_ReadLine( p_demux->s );
if( psz_line ) if( psz_line )
{ {
lua_pushstring( p_state, psz_line ); lua_pushstring( L, psz_line );
free( psz_line ); free( psz_line );
} }
else else
{ {
lua_pushnil( p_state ); lua_pushnil( L );
} }
return 1; return 1;
} }
...@@ -130,57 +130,56 @@ static luaL_Reg p_reg_parse[] = ...@@ -130,57 +130,56 @@ static luaL_Reg p_reg_parse[] =
* the script pointed by psz_filename. * the script pointed by psz_filename.
*****************************************************************************/ *****************************************************************************/
static int probe_luascript( vlc_object_t *p_this, const char * psz_filename, static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
lua_State * p_state, void * user_data ) lua_State * L, void * user_data )
{ {
demux_t * p_demux = (demux_t *)p_this; demux_t * p_demux = (demux_t *)p_this;
p_demux->p_sys->psz_filename = strdup(psz_filename); p_demux->p_sys->psz_filename = strdup(psz_filename);
/* Ugly hack to delete previous versions of the probe() and parse() /* In lua, setting a variable's value to nil is equivalent to deleting it */
* functions. */ lua_pushnil( L );
lua_pushnil( p_state ); lua_pushnil( L );
lua_pushnil( p_state ); lua_setglobal( L, "probe" );
lua_setglobal( p_state, "probe" ); lua_setglobal( L, "parse" );
lua_setglobal( p_state, "parse" );
/* Load and run the script(s) */ /* Load and run the script(s) */
if( luaL_dofile( p_state, psz_filename ) ) if( luaL_dofile( L, psz_filename ) )
{ {
msg_Warn( p_demux, "Error loading script %s: %s", psz_filename, msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
lua_getglobal( p_state, "probe" ); lua_getglobal( L, "probe" );
if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) if( !lua_isfunction( L, lua_gettop( L ) ) )
{ {
msg_Warn( p_demux, "Error while runing script %s, " msg_Warn( p_demux, "Error while runing script %s, "
"function probe() not found", psz_filename ); "function probe() not found", psz_filename );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( lua_pcall( p_state, 0, 1, 0 ) ) if( lua_pcall( L, 0, 1, 0 ) )
{ {
msg_Warn( p_demux, "Error while runing script %s, " msg_Warn( p_demux, "Error while runing script %s, "
"function probe(): %s", psz_filename, "function probe(): %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( lua_gettop( p_state ) ) if( lua_gettop( L ) )
{ {
int i_ret = VLC_EGENERIC; int i_ret = VLC_EGENERIC;
if( lua_toboolean( p_state, 1 ) ) if( lua_toboolean( L, 1 ) )
{ {
msg_Dbg( p_demux, "Lua playlist script %s's " msg_Dbg( p_demux, "Lua playlist script %s's "
"probe() function was successful", psz_filename ); "probe() function was successful", psz_filename );
i_ret = VLC_SUCCESS; i_ret = VLC_SUCCESS;
} }
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return i_ret; return i_ret;
} }
...@@ -193,7 +192,7 @@ static int probe_luascript( vlc_object_t *p_this, const char * psz_filename, ...@@ -193,7 +192,7 @@ static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
{ {
demux_t *p_demux = (demux_t *)p_this; demux_t *p_demux = (demux_t *)p_this;
lua_State *p_state; lua_State *L;
p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) ); p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
if( !p_demux->p_sys ) if( !p_demux->p_sys )
...@@ -207,30 +206,30 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) ...@@ -207,30 +206,30 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
p_demux->pf_demux = Demux; p_demux->pf_demux = Demux;
/* Initialise Lua state structure */ /* Initialise Lua state structure */
p_state = luaL_newstate(); L = luaL_newstate();
if( !p_state ) if( !L )
{ {
msg_Err( p_demux, "Could not create new Lua State" ); msg_Err( p_demux, "Could not create new Lua State" );
free( p_demux->p_sys ); free( p_demux->p_sys );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
p_demux->p_sys->p_state = p_state; p_demux->p_sys->L = L;
/* Load Lua libraries */ /* Load Lua libraries */
luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */ luaL_openlibs( L ); /* FIXME: Don't open all the libs? */
luaL_register( p_state, "vlc", p_reg ); luaL_register( L, "vlc", p_reg );
lua_pushlightuserdata( p_state, p_demux ); lua_pushlightuserdata( L, p_demux );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" ); lua_setfield( L, -2, "private" );
lua_pushstring( p_state, p_demux->psz_path ); lua_pushstring( L, p_demux->psz_path );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "path" ); lua_setfield( L, -2, "path" );
lua_pushstring( p_state, p_demux->psz_access ); lua_pushstring( L, p_demux->psz_access );
lua_setfield( p_state, lua_gettop( p_state ) - 1, "access" ); lua_setfield( L, -2, "access" );
lua_pop( p_state, 1 ); lua_pop( L, 1 );
return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript, return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript,
p_state, NULL ); L, NULL );
} }
...@@ -241,55 +240,26 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) ...@@ -241,55 +240,26 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
void E_(Close_LuaPlaylist)( vlc_object_t *p_this ) void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
{ {
demux_t *p_demux = (demux_t *)p_this; demux_t *p_demux = (demux_t *)p_this;
lua_close( p_demux->p_sys->p_state ); lua_close( p_demux->p_sys->L );
free( p_demux->p_sys->psz_filename ); free( p_demux->p_sys->psz_filename );
free( p_demux->p_sys ); 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 ) static int Demux( demux_t *p_demux )
{ {
input_item_t *p_input; lua_State *L = p_demux->p_sys->L;
lua_State *p_state = p_demux->p_sys->p_state;
char *psz_filename = p_demux->p_sys->psz_filename; char *psz_filename = p_demux->p_sys->psz_filename;
int t;
playlist_t *p_playlist = pl_Yield( p_demux ); 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_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 ); input_item_t *p_current_input = input_GetItem( p_input_thread );
luaL_register( p_state, "vlc", p_reg_parse ); luaL_register( L, "vlc", p_reg_parse );
lua_getglobal( p_state, "parse" ); lua_getglobal( L, "parse" );
if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) if( !lua_isfunction( L, lua_gettop( L ) ) )
{ {
msg_Warn( p_demux, "Error while runing script %s, " msg_Warn( p_demux, "Error while runing script %s, "
"function parse() not found", psz_filename ); "function parse() not found", psz_filename );
...@@ -297,110 +267,20 @@ static int Demux( demux_t *p_demux ) ...@@ -297,110 +267,20 @@ static int Demux( demux_t *p_demux )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( lua_pcall( p_state, 0, 1, 0 ) ) if( lua_pcall( L, 0, 1, 0 ) )
{ {
msg_Warn( p_demux, "Error while runing script %s, " msg_Warn( p_demux, "Error while runing script %s, "
"function parse(): %s", psz_filename, "function parse(): %s", psz_filename,
lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_tostring( L, lua_gettop( L ) ) );
E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
/* Check that the Lua stack is big enough and grow it if needed. if( lua_gettop( L ) )
* Should be ok since LUA_MINSTACK is 20 but we never know. */ vlclua_playlist_add_internal( p_demux, L, p_playlist,
lua_checkstack( p_state, 8 ); p_current_input, 0 );
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 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" ); msg_Err( p_demux, "Script went completely foobar" );
}
vlc_object_release( p_input_thread ); vlc_object_release( p_input_thread );
vlc_object_release( p_playlist ); vlc_object_release( p_playlist );
......
/*****************************************************************************
* 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 ) );
}
/***************************************************************************** /*****************************************************************************
* vlclua.c: Generic lua inteface functions * vlc.c: Generic lua inteface functions
***************************************************************************** *****************************************************************************
* Copyright (C) 2007 the VideoLAN team * Copyright (C) 2007 the VideoLAN team
* $Id$ * $Id$
...@@ -32,17 +32,24 @@ ...@@ -32,17 +32,24 @@
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc_meta.h> #include <vlc_meta.h>
#include <vlc_charset.h> #include <vlc_charset.h>
#include <vlc_aout.h>
#include <lua.h> /* Low level lua C API */ #include <lua.h> /* Low level lua C API */
#include <lauxlib.h> /* Higher level C API */ #include <lauxlib.h> /* Higher level C API */
#include <lualib.h> /* Lua libs */ #include <lualib.h> /* Lua libs */
#include "vlclua.h" #include "vlc.h"
/***************************************************************************** /*****************************************************************************
* Module descriptor * 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(); vlc_module_begin();
add_shortcut( "luameta" ); add_shortcut( "luameta" );
set_shortname( N_( "Lua Meta" ) ); set_shortname( N_( "Lua Meta" ) );
...@@ -62,151 +69,244 @@ vlc_module_begin(); ...@@ -62,151 +69,244 @@ vlc_module_begin();
set_description( _("Lua Playlist Parser Interface") ); set_description( _("Lua Playlist Parser Interface") );
set_capability( "demux2", 9 ); set_capability( "demux2", 9 );
set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) ); 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(); vlc_module_end();
/***************************************************************************** /*****************************************************************************
* Lua function bridge * Internal lua<->vlc utils
*****************************************************************************/ *****************************************************************************/
vlc_object_t * vlclua_get_this( lua_State *p_state ) vlc_object_t * vlclua_get_this( lua_State *L )
{ {
vlc_object_t * p_this; vlc_object_t * p_this;
lua_getglobal( p_state, "vlc" ); lua_getglobal( L, "vlc" );
lua_getfield( p_state, lua_gettop( p_state ), "private" ); lua_getfield( L, -1, "private" );
p_this = (vlc_object_t*)lua_topointer( p_state, lua_gettop( p_state ) ); p_this = (vlc_object_t*)lua_topointer( L, lua_gettop( L ) );
lua_pop( p_state, 2 ); lua_pop( L, 2 );
return p_this; return p_this;
} }
int vlclua_stream_new( lua_State *p_state ) /*****************************************************************************
* VLC error code translation
*****************************************************************************/
int vlclua_push_ret( lua_State *L, int i_error )
{ {
vlc_object_t * p_this = vlclua_get_this( p_state ); lua_pushnumber( L, i_error );
int i = lua_gettop( p_state ); 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; stream_t * p_stream;
const char * psz_url; const char * psz_url;
if( !i ) return 0; if( lua_gettop( L ) != 1 ) return vlclua_error( L );
psz_url = lua_tostring( p_state, 1 ); psz_url = luaL_checkstring( L, -1 );
lua_pop( p_state, i ); lua_pop( L, 1 );
p_stream = stream_UrlNew( p_this, psz_url ); p_stream = stream_UrlNew( p_this, psz_url );
if( !p_stream ) return 0; if( !p_stream ) return vlclua_error( L );
lua_pushlightuserdata( p_state, p_stream ); lua_pushlightuserdata( L, p_stream );
return 1; return 1;
} }
int vlclua_stream_read( lua_State *p_state ) int vlclua_stream_read( lua_State *L )
{ {
int i = lua_gettop( p_state );
stream_t * p_stream; stream_t * p_stream;
int n; int n;
byte_t *p_read; byte_t *p_read;
int i_read; int i_read;
if( !i ) return 0; if( lua_gettop( L ) != 2 ) return vlclua_error( L );
p_stream = (stream_t *)lua_topointer( p_state, 1 ); p_stream = (stream_t *)luaL_checklightuserdata( L, -2 );
n = lua_tonumber( p_state, 2 ); n = luaL_checkint( L, -1 );
lua_pop( p_state, i ); lua_pop( L, 2 );
p_read = malloc( n ); p_read = malloc( n );
if( !p_read ) return 0; if( !p_read ) return vlclua_error( L );
i_read = stream_Read( p_stream, p_read, n ); i_read = stream_Read( p_stream, p_read, n );
lua_pushlstring( p_state, (const char *)p_read, i_read ); lua_pushlstring( L, (const char *)p_read, i_read );
free( p_read ); free( p_read );
return 1; return 1;
} }
int vlclua_stream_readline( lua_State *p_state ) int vlclua_stream_readline( lua_State *L )
{ {
int i = lua_gettop( p_state );
stream_t * p_stream; stream_t * p_stream;
if( !i ) return 0; if( lua_gettop( L ) != 1 ) return vlclua_error( L );
p_stream = (stream_t *)lua_topointer( p_state, 1 ); p_stream = (stream_t *)luaL_checklightuserdata( L, -1 );
lua_pop( p_state, i ); lua_pop( L, 1 );
char *psz_line = stream_ReadLine( p_stream ); char *psz_line = stream_ReadLine( p_stream );
if( psz_line ) if( psz_line )
{ {
lua_pushstring( p_state, psz_line ); lua_pushstring( L, psz_line );
free( psz_line ); free( psz_line );
} }
else else
{ {
lua_pushnil( p_state ); lua_pushnil( L );
} }
return 1; return 1;
} }
int vlclua_stream_delete( lua_State *p_state ) int vlclua_stream_delete( lua_State *L )
{ {
int i = lua_gettop( p_state );
stream_t * p_stream; stream_t * p_stream;
if( !i ) return 0; if( lua_gettop( L ) != 1 ) return vlclua_error( L );
p_stream = (stream_t *)lua_topointer( p_state, 1 ); p_stream = (stream_t *)luaL_checklightuserdata( L, -1 );
lua_pop( p_state, i ); lua_pop( L, 1 );
stream_Delete( p_stream ); stream_Delete( p_stream );
return 1; return 1;
} }
int vlclua_decode_uri( lua_State *p_state ) /*****************************************************************************
* String transformations
*****************************************************************************/
/* TODO: make it work for any number of arguments */
int vlclua_decode_uri( lua_State *L )
{ {
int i = lua_gettop( p_state ); if( lua_gettop( L ) != 1 ) return vlclua_error( L );
if( !i ) return 0; const char *psz_cstring = luaL_checkstring( L, 1 );
const char *psz_cstring = lua_tostring( p_state, 1 ); if( !psz_cstring ) return vlclua_error( L );
if( !psz_cstring ) return 0;
char *psz_string = strdup( psz_cstring ); char *psz_string = strdup( psz_cstring );
lua_pop( p_state, i ); lua_pop( L, 1 );
decode_URI( psz_string ); decode_URI( psz_string );
lua_pushstring( p_state, psz_string ); lua_pushstring( L, psz_string );
free( psz_string ); free( psz_string );
return 1; return 1;
} }
int vlclua_resolve_xml_special_chars( lua_State *p_state ) /* TODO: make it work for any number of arguments */
int vlclua_resolve_xml_special_chars( lua_State *L )
{ {
int i = lua_gettop( p_state ); if( lua_gettop( L ) != 1 ) return vlclua_error( L );
if( !i ) return 0; const char *psz_cstring = luaL_checkstring( L, 1 );
const char *psz_cstring = lua_tostring( p_state, 1 ); if( !psz_cstring ) return vlclua_error( L );
if( !psz_cstring ) return 0;
char *psz_string = strdup( psz_cstring ); char *psz_string = strdup( psz_cstring );
lua_pop( p_state, i ); lua_pop( L, 1 );
resolve_xml_special_chars( psz_string ); resolve_xml_special_chars( psz_string );
lua_pushstring( p_state, psz_string ); lua_pushstring( L, psz_string );
free( psz_string ); free( psz_string );
return 1; return 1;
} }
int vlclua_msg_dbg( lua_State *p_state ) /*****************************************************************************
* 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( p_state ); vlc_object_t *p_this = vlclua_get_this( L );
int i = lua_gettop( p_state ); if( lua_gettop( L ) != 1 ) return vlclua_error( L );
if( !i ) return 0; const char *psz_cstring = luaL_checkstring( L, 1 );
const char *psz_cstring = lua_tostring( p_state, 1 ); if( !psz_cstring ) return vlclua_error( L );
if( !psz_cstring ) return 0;
msg_Dbg( p_this, "%s", psz_cstring ); msg_Dbg( p_this, "%s", psz_cstring );
return 0; return 0;
} }
int vlclua_msg_warn( lua_State *p_state ) /* TODO: make it work for any number of arguments */
int vlclua_msg_warn( lua_State *L )
{ {
vlc_object_t *p_this = vlclua_get_this( p_state ); vlc_object_t *p_this = vlclua_get_this( L );
int i = lua_gettop( p_state ); if( lua_gettop( L ) != 1 ) return vlclua_error( L );
if( !i ) return 0; const char *psz_cstring = luaL_checkstring( L, 1 );
const char *psz_cstring = lua_tostring( p_state, 1 ); if( !psz_cstring ) return vlclua_error( L );
if( !psz_cstring ) return 0;
msg_Warn( p_this, "%s", psz_cstring ); msg_Warn( p_this, "%s", psz_cstring );
return 0; return 0;
} }
int vlclua_msg_err( lua_State *p_state ) /* TODO: make it work for any number of arguments */
int vlclua_msg_err( lua_State *L )
{ {
vlc_object_t *p_this = vlclua_get_this( p_state ); vlc_object_t *p_this = vlclua_get_this( L );
int i = lua_gettop( p_state ); if( lua_gettop( L ) != 1 ) return vlclua_error( L );
if( !i ) return 0; const char *psz_cstring = luaL_checkstring( L, 1 );
const char *psz_cstring = lua_tostring( p_state, 1 ); if( !psz_cstring ) return vlclua_error( L );
if( !psz_cstring ) return 0;
msg_Err( p_this, "%s", psz_cstring ); msg_Err( p_this, "%s", psz_cstring );
return 0; return 0;
} }
int vlclua_msg_info( lua_State *p_state ) /* TODO: make it work for any number of arguments */
int vlclua_msg_info( lua_State *L )
{ {
vlc_object_t *p_this = vlclua_get_this( p_state ); vlc_object_t *p_this = vlclua_get_this( L );
int i = lua_gettop( p_state ); if( lua_gettop( L ) != 1 ) return vlclua_error( L );
if( !i ) return 0; const char *psz_cstring = luaL_checkstring( L, 1 );
const char *psz_cstring = lua_tostring( p_state, 1 ); if( !psz_cstring ) return vlclua_error( L );
if( !psz_cstring ) return 0;
msg_Info( p_this, "%s", psz_cstring ); msg_Info( p_this, "%s", psz_cstring );
return 0; return 0;
} }
...@@ -225,27 +325,9 @@ static int file_compare( const char **a, const char **b ) ...@@ -225,27 +325,9 @@ static int file_compare( const char **a, const char **b )
return strcmp( *a, *b ); return strcmp( *a, *b );
} }
int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname,
/***************************************************************************** char **ppsz_dir_list )
* 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", if( asprintf( &ppsz_dir_list[0], "%s" DIR_SEP "%s",
p_this->p_libvlc->psz_datadir, luadirname ) < 0 ) p_this->p_libvlc->psz_datadir, luadirname ) < 0 )
return VLC_ENOMEM; return VLC_ENOMEM;
...@@ -253,10 +335,12 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this, ...@@ -253,10 +335,12 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this,
# if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32) # if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
{ {
const char *psz_vlcpath = config_GetDataDir(); const char *psz_vlcpath = config_GetDataDir();
if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s", psz_vlcpath, luadirname ) < 0 ) if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s",
psz_vlcpath, luadirname ) < 0 )
return VLC_ENOMEM; return VLC_ENOMEM;
if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s", psz_vlcpath, luadirname ) < 0 ) if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s",
psz_vlcpath, luadirname ) < 0 )
return VLC_ENOMEM; return VLC_ENOMEM;
} }
# else # else
...@@ -278,6 +362,34 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this, ...@@ -278,6 +362,34 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this,
} }
# endif # endif
# 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++ ) for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
{ {
...@@ -301,7 +413,8 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this, ...@@ -301,7 +413,8 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this,
dir = utf8_opendir( *ppsz_dir ); dir = utf8_opendir( *ppsz_dir );
if( !dir ) continue; if( !dir ) continue;
i_files = utf8_loaddir( dir, &ppsz_filelist, file_select, file_compare ); i_files = utf8_loaddir( dir, &ppsz_filelist, file_select,
file_compare );
if( i_files < 1 ) continue; if( i_files < 1 ) continue;
ppsz_fileend = ppsz_filelist + i_files; ppsz_fileend = ppsz_filelist + i_files;
...@@ -313,7 +426,7 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this, ...@@ -313,7 +426,7 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this,
return VLC_ENOMEM; return VLC_ENOMEM;
msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename ); msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
i_ret = func( p_this, psz_filename, p_state, user_data ); i_ret = func( p_this, psz_filename, L, user_data );
free( psz_filename ); free( psz_filename );
...@@ -340,21 +453,22 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this, ...@@ -340,21 +453,22 @@ int vlclua_scripts_batch_execute( vlc_object_t *p_this,
/***************************************************************************** /*****************************************************************************
* Meta data setters utility. * 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 *p_state, void __vlclua_read_meta_data( vlc_object_t *p_this, lua_State *L,
int o, int t, input_item_t *p_input ) input_item_t *p_input )
{ {
const char *psz_value;
#define TRY_META( a, b ) \ #define TRY_META( a, b ) \
lua_getfield( p_state, o, a ); \ lua_getfield( L, -1, a ); \
if( lua_isstring( p_state, t ) ) \ if( lua_isstring( L, -1 ) ) \
{ \ { \
psz_value = lua_tostring( p_state, t ); \ char *psz_value = strdup( lua_tostring( L, -1 ) ); \
EnsureUTF8( psz_value ); \ EnsureUTF8( psz_value ); \
msg_Dbg( p_this, #b ": %s", psz_value ); \ msg_Dbg( p_this, #b ": %s", psz_value ); \
input_item_Set ## b ( p_input, psz_value ); \ input_item_Set ## b ( p_input, psz_value ); \
free( psz_value ); \
} \ } \
lua_pop( p_state, 1 ); /* pop a */ lua_pop( L, 1 ); /* pop a */
TRY_META( "title", Title ); TRY_META( "title", Title );
TRY_META( "artist", Artist ); TRY_META( "artist", Artist );
TRY_META( "genre", Genre ); TRY_META( "genre", Genre );
...@@ -374,39 +488,45 @@ void vlclua_read_meta_data( vlc_object_t *p_this, lua_State *p_state, ...@@ -374,39 +488,45 @@ void vlclua_read_meta_data( vlc_object_t *p_this, lua_State *p_state,
TRY_META( "trackid", TrackID ); TRY_META( "trackid", TrackID );
} }
void vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *p_state, void __vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *L,
int o, int t, input_item_t *p_input ) input_item_t *p_input )
{ {
lua_getfield( p_state, o, "meta" ); /* ... item */
if( lua_istable( p_state, t ) ) lua_getfield( L, -1, "meta" );
/* ... item meta */
if( lua_istable( L, -1 ) )
{ {
lua_pushnil( p_state ); lua_pushnil( L );
while( lua_next( p_state, t ) ) /* ... item meta nil */
while( lua_next( L, -2 ) )
{ {
if( !lua_isstring( p_state, t+1 ) ) /* ... item meta key value */
if( !lua_isstring( L, -2 ) )
{ {
msg_Warn( p_this, "Custom meta data category name must be " msg_Warn( p_this, "Custom meta data category name must be "
"a string" ); "a string" );
} }
else if( !lua_istable( p_state, t+2 ) ) else if( !lua_istable( L, -1 ) )
{ {
msg_Warn( p_this, "Custom meta data category contents " msg_Warn( p_this, "Custom meta data category contents "
"must be a table" ); "must be a table" );
} }
else else
{ {
const char *psz_meta_category = lua_tostring( p_state, t+1 ); const char *psz_meta_category = lua_tostring( L, -2 );
msg_Dbg( p_this, "Found custom meta data category: %s", msg_Dbg( p_this, "Found custom meta data category: %s",
psz_meta_category ); psz_meta_category );
lua_pushnil( p_state ); lua_pushnil( L );
while( lua_next( p_state, t+2 ) ) /* ... item meta key value nil */
while( lua_next( L, -2 ) )
{ {
if( !lua_isstring( p_state, t+3 ) ) /* ... item meta key value key2 value2 */
if( !lua_isstring( L, -2 ) )
{ {
msg_Warn( p_this, "Custom meta category item name " msg_Warn( p_this, "Custom meta category item name "
"must be a string." ); "must be a string." );
} }
else if( !lua_isstring( p_state, t+4 ) ) else if( !lua_isstring( L, -1 ) )
{ {
msg_Warn( p_this, "Custom meta category item value " msg_Warn( p_this, "Custom meta category item value "
"must be a string." ); "must be a string." );
...@@ -414,21 +534,179 @@ void vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *p_state, ...@@ -414,21 +534,179 @@ void vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *p_state,
else else
{ {
const char *psz_meta_name = const char *psz_meta_name =
lua_tostring( p_state, t+3 ); lua_tostring( L, -2 );
const char *psz_meta_value = const char *psz_meta_value =
lua_tostring( p_state, t+4 ); lua_tostring( L, -1 );
msg_Dbg( p_this, "Custom meta %s, %s: %s", msg_Dbg( p_this, "Custom meta %s, %s: %s",
psz_meta_category, psz_meta_name, psz_meta_category, psz_meta_name,
psz_meta_value ); psz_meta_value );
input_ItemAddInfo( p_input, psz_meta_category, input_ItemAddInfo( p_input, psz_meta_category,
psz_meta_name, psz_meta_value ); psz_meta_name, psz_meta_value );
} }
lua_pop( p_state, 1 ); /* pop item */ 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( p_state, 1 ); /* pop category */ lua_pop( L, 1 ); /* pop option */
} }
} }
lua_pop( p_state, 1 ); /* pop "meta" */ 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;
}
...@@ -57,6 +57,9 @@ int E_(FindMeta)( vlc_object_t * ); ...@@ -57,6 +57,9 @@ int E_(FindMeta)( vlc_object_t * );
int E_(Import_LuaPlaylist)( vlc_object_t * ); int E_(Import_LuaPlaylist)( vlc_object_t * );
void E_(Close_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 * Lua debug
...@@ -74,21 +77,102 @@ static inline void lua_Dbg( vlc_object_t * p_this, const char * ppz_fmt, ... ) ...@@ -74,21 +77,102 @@ static inline void lua_Dbg( vlc_object_t * p_this, const char * ppz_fmt, ... )
va_end( 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 * Lua function bridge
*****************************************************************************/ *****************************************************************************/
vlc_object_t * vlclua_get_this( lua_State *p_state ); vlc_object_t * vlclua_get_this( lua_State * );
int vlclua_stream_new( lua_State *p_state ); #define vlclua_error( L ) luaL_error( L, "VLC lua error in file %s line %d (function %s)", __FILE__, __LINE__, __func__ )
int vlclua_stream_read( lua_State *p_state ); int vlclua_push_ret( lua_State *, int i_error );
int vlclua_stream_readline( lua_State *p_state );
int vlclua_stream_delete( lua_State *p_state ); int vlclua_version( lua_State * );
int vlclua_decode_uri( lua_State *p_state ); int vlclua_quit( lua_State * );
int vlclua_resolve_xml_special_chars( lua_State *p_state );
int vlclua_msg_dbg( lua_State *p_state ); int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val ); /* internal use only */
int vlclua_msg_warn( lua_State *p_state ); int vlclua_var_get( lua_State * );
int vlclua_msg_err( lua_State *p_state ); int vlclua_var_get_list( lua_State * );
int vlclua_msg_info( lua_State *p_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 * Will execute func on all scripts in luadirname, and stop if func returns
...@@ -96,19 +180,23 @@ int vlclua_msg_info( lua_State *p_state ); ...@@ -96,19 +180,23 @@ int vlclua_msg_info( lua_State *p_state );
*****************************************************************************/ *****************************************************************************/
int vlclua_scripts_batch_execute( vlc_object_t *p_this, const char * luadirname, int vlclua_scripts_batch_execute( vlc_object_t *p_this, const char * luadirname,
int (*func)(vlc_object_t *, const char *, lua_State *, void *), int (*func)(vlc_object_t *, const char *, lua_State *, void *),
lua_State * p_state, void * user_data); lua_State * L, void * user_data );
int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname, char **ppsz_dir_list );
/***************************************************************************** /*****************************************************************************
* Meta data setters utility. * Playlist and meta data internal utilities.
*****************************************************************************/ *****************************************************************************/
void vlclua_read_meta_data( vlc_object_t *p_this, void __vlclua_read_options( vlc_object_t *, lua_State *, int *, char *** );
lua_State *p_state, int o, int t, input_item_t *p_input ); #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)
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 */ #endif /* VLC_LUA_H */
/*****************************************************************************
* 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;
}
...@@ -256,4 +256,10 @@ DIST_lua= \ ...@@ -256,4 +256,10 @@ DIST_lua= \
luaplaylist/youtube.lua \ luaplaylist/youtube.lua \
luaplaylist/youtube_homepage.lua \ luaplaylist/youtube_homepage.lua \
luaplaylist/metacafe.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
--[[ 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
--[==========================================================================[
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 )
--[[ 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
--[==========================================================================[
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
--[==========================================================================[
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
--[==========================================================================[
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 )
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment