You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

398 lines
12 KiB

/*****************************************************************************
* sd.c: Services discovery related functions
*****************************************************************************
* Copyright (C) 2007-2008 the VideoLAN team
*
* Authors: Antoine Cellerier <dionoea at videolan tod org>
* Fabio Ritrovato <sephiroth87 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
#include <math.h>
#include <vlc_common.h>
#include <vlc_services_discovery.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#include <vlc_hash.h>
#include "../vlc.h"
#include "../libs.h"
static int vlclua_sd_delete_common( input_item_t **pp_item )
{
assert(pp_item != NULL);
input_item_t *p_item = *pp_item;
if (p_item != NULL) /* item may be NULL if already removed earlier */
input_item_Release( p_item );
return 1;
}
static int vlclua_sd_remove_common( lua_State *L, input_item_t **pp_item )
{
services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
if (pp_item == NULL)
return luaL_error( L, "expected item" );
input_item_t *p_item = *pp_item;
if (*pp_item == NULL)
return luaL_error( L, "already removed item" );
services_discovery_RemoveItem( p_sd, p_item );
input_item_Release( p_item );
/* Make sure we won't try to remove it again */
*pp_item = NULL;
return 1;
}
/*** Input item ***/
static int vlclua_sd_item_delete( lua_State *L )
{
input_item_t **pp_item = luaL_checkudata( L, 1, "input_item_t" );
return vlclua_sd_delete_common( pp_item );
}
#define vlclua_item_luareg( a ) \
{ "set_" # a, vlclua_item_set_ ## a },
#define vlclua_item_meta( lowercase, normal ) \
static int vlclua_item_set_ ## lowercase ( lua_State *L )\
{\
services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );\
input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "input_item_t" );\
if( *pp_node )\
{\
if( lua_isstring( L, -1 ) )\
{\
input_item_Set ## normal ( *pp_node, lua_tostring( L, -1 ) );\
} else\
msg_Err( p_sd, "Error parsing set_ " # lowercase " arguments" );\
}\
return 1;\
}
vlclua_item_meta(title, Title)
vlclua_item_meta(artist, Artist)
vlclua_item_meta(genre, Genre)
vlclua_item_meta(copyright, Copyright)
vlclua_item_meta(album, Album)
vlclua_item_meta(tracknum, TrackNum)
vlclua_item_meta(description, Description)
vlclua_item_meta(rating, Rating)
vlclua_item_meta(date, Date)
vlclua_item_meta(setting, Setting)
vlclua_item_meta(url, URL)
vlclua_item_meta(language, Language)
vlclua_item_meta(nowplaying, NowPlaying)
vlclua_item_meta(publisher, Publisher)
vlclua_item_meta(encodedby, EncodedBy)
vlclua_item_meta(arturl, ArtworkURL)
vlclua_item_meta(trackid, TrackID)
vlclua_item_meta(tracktotal, TrackTotal)
vlclua_item_meta(director, Director)
vlclua_item_meta(season, Season)
vlclua_item_meta(episode, Episode)
vlclua_item_meta(showname, ShowName)
vlclua_item_meta(actors, Actors)
static const luaL_Reg vlclua_item_reg[] = {
vlclua_item_luareg(title)
vlclua_item_luareg(artist)
vlclua_item_luareg(genre)
vlclua_item_luareg(copyright)
vlclua_item_luareg(album)
vlclua_item_luareg(tracknum)
vlclua_item_luareg(description)
vlclua_item_luareg(rating)
vlclua_item_luareg(date)
vlclua_item_luareg(setting)
vlclua_item_luareg(url)
vlclua_item_luareg(language)
vlclua_item_luareg(nowplaying)
vlclua_item_luareg(publisher)
vlclua_item_luareg(encodedby)
vlclua_item_luareg(arturl)
vlclua_item_luareg(trackid)
vlclua_item_luareg(tracktotal)
vlclua_item_luareg(director)
vlclua_item_luareg(season)
vlclua_item_luareg(episode)
vlclua_item_luareg(showname)
vlclua_item_luareg(actors)
{ NULL, NULL }
};
static input_item_t *vlclua_sd_create_item( services_discovery_t *p_sd,
lua_State *L )
{
if( !lua_istable( L, -1 ) )
{
msg_Err( p_sd, "Error: argument must be table" );
return NULL;
}
lua_getfield( L, -1, "path" );
if( !lua_isstring( L, -1 ) )
{
msg_Err( p_sd, "Error: \"%s\" parameter is required", "path" );
return NULL;
}
const char *psz_path = lua_tostring( L, -1 );
lua_getfield( L, -2, "title" );
const char *psz_title = luaL_checkstring( L, -1 )
? luaL_checkstring( L, -1 )
: psz_path;
input_item_t *p_input = input_item_New( psz_path, psz_title );
lua_pop( L, 2 );
if( unlikely(p_input == NULL) )
return NULL;
/* The table must be at the top of the stack when calling
* vlclua_read_options() */
char **ppsz_options = NULL;
int i_options = 0;
lua_pushvalue( L, -1 );
vlclua_read_options( p_sd, L, &i_options, &ppsz_options );
lua_pop( L, 1 );
input_item_AddOptions( p_input, i_options, (const char **)ppsz_options,
VLC_INPUT_OPTION_TRUSTED );
while( i_options > 0 )
free( ppsz_options[--i_options] );
free( ppsz_options );
vlclua_read_meta_data( p_sd, L, p_input );
/* This one is to be tested... */
vlclua_read_custom_meta_data( p_sd, L, p_input );
/* The duration is given in seconds, convert to microseconds */
lua_getfield( L, -1, "duration" );
if( lua_isnumber( L, -1 ) )
p_input->i_duration = llround(lua_tonumber( L, -1 ) * 1e6);
else if( !lua_isnil( L, -1 ) )
msg_Warn( p_sd, "Item duration should be a number (in seconds)." );
lua_pop( L, 1 );
/* string to build the input item uid */
lua_getfield( L, -1, "uiddata" );
if( lua_isstring( L, -1 ) )
{
char *s = strdup( luaL_checkstring( L, -1 ) );
if ( s )
{
vlc_hash_md5_t md5;
vlc_hash_md5_Init( &md5 );
vlc_hash_md5_Update( &md5, s, strlen( s ) );
free( s );
char tmp[VLC_HASH_MD5_DIGEST_HEX_SIZE];
vlc_hash_FinishHex( &md5, tmp );
input_item_AddInfo( p_input, "uid", "md5", "%s", tmp );
}
}
lua_pop( L, 1 );
input_item_t **udata = lua_newuserdata( L, sizeof( input_item_t * ) );
*udata = p_input;
if( luaL_newmetatable( L, "input_item_t" ) )
{
lua_newtable( L );
luaL_register( L, NULL, vlclua_item_reg );
lua_setfield( L, -2, "__index" );
lua_pushcfunction( L, vlclua_sd_item_delete );
lua_setfield( L, -2, "__gc" );
lua_pushliteral( L, "none of your business" );
lua_setfield( L, -2, "__metatable" );
}
lua_setmetatable( L, -2 );
return p_input;
}
/*** Input item tree node ***/
static int vlclua_sd_node_delete( lua_State *L )
{
input_item_t **pp_item = luaL_checkudata( L, 1, "node" );
return vlclua_sd_delete_common( pp_item );
}
static int vlclua_sd_add_sub_common( services_discovery_t *p_sd,
input_item_t **pp_node,
input_item_t *p_input )
{
if( *pp_node != NULL && p_input != NULL )
services_discovery_AddSubItem( p_sd, *pp_node, p_input );
return 1;
}
static int vlclua_node_add_subitem( lua_State *L )
{
services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" );
return vlclua_sd_add_sub_common( p_sd, pp_node,
vlclua_sd_create_item( p_sd, L ) );
}
static const luaL_Reg vlclua_node_reg[];
static input_item_t *vlclua_sd_create_node( services_discovery_t *p_sd,
lua_State *L )
{
if( !lua_istable( L, -1 ) )
{
msg_Err( p_sd, "Error: argument must be table" );
return NULL;
}
lua_getfield( L, -1, "title" );
if( !lua_isstring( L, -1 ) )
{
msg_Err( p_sd, "Error: \"%s\" parameter is required", "title" );
return NULL;
}
const char *psz_name = lua_tostring( L, -1 );
input_item_t *p_input = input_item_NewExt( INPUT_ITEM_URI_NOP, psz_name,
INPUT_DURATION_INDEFINITE,
ITEM_TYPE_NODE,
ITEM_NET_UNKNOWN );
lua_pop( L, 1 );
if( unlikely(p_input == NULL) )
return NULL;
lua_getfield( L, -1, "arturl" );
if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) )
{
char *psz_value = strdup( lua_tostring( L, -1 ) );
EnsureUTF8( psz_value );
msg_Dbg( p_sd, "ArtURL: %s", psz_value );
/* TODO: ask for art download if not local file */
input_item_SetArtURL( p_input, psz_value );
free( psz_value );
}
lua_pop( L, 1 );
input_item_t **udata = lua_newuserdata( L, sizeof( input_item_t * ) );
*udata = p_input;
if( luaL_newmetatable( L, "node" ) )
{
lua_newtable( L );
luaL_register( L, NULL, vlclua_node_reg );
lua_setfield( L, -2, "__index" );
lua_pushcfunction( L, vlclua_sd_node_delete );
lua_setfield( L, -2, "__gc" );
}
lua_setmetatable( L, -2 );
return p_input;
}
static int vlclua_node_add_subnode( lua_State *L )
{
services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" );
return vlclua_sd_add_sub_common( p_sd, pp_node,
vlclua_sd_create_node( p_sd, L ) );
}
static const luaL_Reg vlclua_node_reg[] = {
{ "add_subitem", vlclua_node_add_subitem },
{ "add_subnode", vlclua_node_add_subnode },
{ NULL, NULL }
};
/*** Services discovery instance ***/
static int vlclua_sd_add_common( services_discovery_t *p_sd,
input_item_t *p_input )
{
if( p_input != NULL )
services_discovery_AddItem( p_sd, p_input );
return 1;
}
static int vlclua_sd_add_item( lua_State *L )
{
services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
return vlclua_sd_add_common( p_sd, vlclua_sd_create_item( p_sd, L ) );
}
static int vlclua_sd_add_node( lua_State *L )
{
services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
return vlclua_sd_add_common( p_sd, vlclua_sd_create_node( p_sd, L ) );
}
static int vlclua_sd_remove_item( lua_State *L )
{
input_item_t **pp_input = luaL_checkudata( L, 1, "input_item_t" );
return vlclua_sd_remove_common( L, pp_input );
}
static int vlclua_sd_remove_node( lua_State *L )
{
input_item_t **pp_input = luaL_checkudata( L, 1, "node" );
return vlclua_sd_remove_common( L, pp_input );
}
static const luaL_Reg vlclua_sd_sd_reg[] = {
{ "add_item", vlclua_sd_add_item },
{ "add_node", vlclua_sd_add_node },
{ "remove_item", vlclua_sd_remove_item },
{ "remove_node", vlclua_sd_remove_node },
{ NULL, NULL }
};
void luaopen_sd_sd( lua_State *L )
{
lua_newtable( L );
luaL_register( L, NULL, vlclua_sd_sd_reg );
lua_setfield( L, -2, "sd" );
}