sd.c 18.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * sd.c: Services discovery related functions
 *****************************************************************************
4
 * Copyright (C) 2007-2008 the VideoLAN team
5 6 7
 * $Id$
 *
 * Authors: Antoine Cellerier <dionoea at videolan tod org>
8
 *          Fabio Ritrovato <sephiroth87 at videolan dot org>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * 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

32 33 34 35
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

36
#include <vlc_common.h>
37 38
#include <vlc_services_discovery.h>
#include <vlc_playlist.h>
39
#include <vlc_charset.h>
40
#include <vlc_md5.h>
41

42 43
#include "../vlc.h"
#include "../libs.h"
44 45 46 47

/*****************************************************************************
 *
 *****************************************************************************/
48
static int vlclua_node_add_subitem( lua_State * );
49
static int vlclua_node_add_subnode( lua_State * );
50 51 52

static const luaL_Reg vlclua_node_reg[] = {
    { "add_subitem", vlclua_node_add_subitem },
53
    { "add_subnode", vlclua_node_add_subnode },
54 55 56
    { NULL, NULL }
};

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
#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)
93
vlclua_item_meta(tracktotal, TrackTotal)
94 95 96 97 98
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    )
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

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)
118
    vlclua_item_luareg(tracktotal)
119 120 121 122 123
    vlclua_item_luareg(director  )
    vlclua_item_luareg(season    )
    vlclua_item_luareg(episode   )
    vlclua_item_luareg(showname  )
    vlclua_item_luareg(actors    )
124 125 126
    { NULL, NULL }
};

127
static int vlclua_sd_get_services_names( lua_State *L )
128
{
129
    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
130
    char **ppsz_longnames;
131
    char **ppsz_names = vlc_sd_GetNames( p_playlist, &ppsz_longnames, NULL );
132 133 134
    if( !ppsz_names )
        return 0;

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    char **ppsz_longname = ppsz_longnames;
    char **ppsz_name = ppsz_names;
    lua_settop( L, 0 );
    lua_newtable( L );
    for( ; *ppsz_name; ppsz_name++,ppsz_longname++ )
    {
        lua_pushstring( L, *ppsz_longname );
        lua_setfield( L, -2, *ppsz_name );
        free( *ppsz_name );
        free( *ppsz_longname );
    }
    free( ppsz_names );
    free( ppsz_longnames );
    return 1;
}

151
static int vlclua_sd_add( lua_State *L )
152 153 154 155 156 157 158
{
    const char *psz_sd = luaL_checkstring( L, 1 );
    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
    int i_ret = playlist_ServicesDiscoveryAdd( p_playlist, psz_sd );
    return vlclua_push_ret( L, i_ret );
}

159
static int vlclua_sd_remove( lua_State *L )
160 161 162 163 164 165 166
{
    const char *psz_sd = luaL_checkstring( L, 1 );
    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
    int i_ret = playlist_ServicesDiscoveryRemove( p_playlist, psz_sd );
    return vlclua_push_ret( L, i_ret );
}

167
static int vlclua_sd_is_loaded( lua_State *L )
168 169 170 171 172 173
{
    const char *psz_sd = luaL_checkstring( L, 1 );
    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
    lua_pushboolean( L, playlist_IsServicesDiscoveryLoaded( p_playlist, psz_sd ));
    return 1;
}
174

175 176 177 178 179 180 181 182
static int vlclua_sd_add_node( lua_State *L )
{
    services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
    if( lua_istable( L, -1 ) )
    {
        lua_getfield( L, -1, "title" );
        if( lua_isstring( L, -1 ) )
        {
183
            const char *psz_name = lua_tostring( L, -1 );
184 185 186
            input_item_t *p_input = input_item_NewExt( "vlc://nop",
                                                       psz_name, -1,
                                                       ITEM_TYPE_NODE, ITEM_NET_UNKNOWN );
187 188
            lua_pop( L, 1 );

189
            if( p_input )
190
            {
191 192 193 194 195 196 197 198 199 200
                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 );
                }
201 202
                lua_pop( L, 1 );
                lua_getfield( L, -1, "category" );
203 204 205 206
                if( lua_isstring( L, -1 ) )
                    services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) );
                else
                    services_discovery_AddItem( p_sd, p_input, NULL );
207 208 209 210 211 212 213 214 215 216
                input_item_t **udata = (input_item_t **)
                                       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_setmetatable( L, -2 );
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
            }
        }
        else
            msg_Err( p_sd, "vlc.sd.add_node: the \"title\" parameter can't be empty" );
    }
    else
        msg_Err( p_sd, "Error parsing add_node arguments" );
    return 1;
}

static int vlclua_sd_add_item( lua_State *L )
{
    services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
    if( lua_istable( L, -1 ) )
    {
232
        lua_getfield( L, -1, "path" );
233 234
        if( lua_isstring( L, -1 ) )
        {
235
            const char *psz_path = lua_tostring( L, -1 );
236

237
            lua_getfield( L, -2, "title" );
238
            const char *psz_title = luaL_checkstring( L, -1 ) ? luaL_checkstring( L, -1 ) : psz_path;
239 240 241 242 243 244 245 246

            /* 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, -3 );
            vlclua_read_options( p_sd, L, &i_options, &ppsz_options );

247
            input_item_t *p_input = input_item_New( psz_path, psz_title );
248
            lua_pop( L, 3 );
249

250
            if( p_input )
251
            {
252 253 254
                input_item_AddOptions( p_input, i_options,
                                       (const char **)ppsz_options,
                                       VLC_INPUT_OPTION_TRUSTED );
255 256 257 258 259 260 261 262 263 264
                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 ) )
                   input_item_SetDuration( p_input, (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 );
265
                lua_getfield( L, -1, "category" );
266 267 268 269
                if( lua_isstring( L, -1 ) )
                    services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) );
                else
                    services_discovery_AddItem( p_sd, p_input, NULL );
270
                lua_pop( L, 1 );
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

                /* 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 )
                    {
                        struct md5_s md5;
                        InitMD5( &md5 );
                        AddMD5( &md5, s, strlen( s ) );
                        EndMD5( &md5 );
                        free( s );
                        s = psz_md5_hash( &md5 );
                        if ( s )
                            input_item_AddInfo( p_input, "uid", "md5", "%s", s );
                        free( s );
                    }
                }
                lua_pop( L, 1 );

292 293 294 295 296
                input_item_t **udata = (input_item_t **)
                                       lua_newuserdata( L, sizeof( input_item_t * ) );
                *udata = p_input;
                if( luaL_newmetatable( L, "input_item_t" ) )
                {
297 298 299
                    lua_newtable( L );
                    luaL_register( L, NULL, vlclua_item_reg );
                    lua_setfield( L, -2, "__index" );
300 301 302 303
                    lua_pushliteral( L, "none of your business" );
                    lua_setfield( L, -2, "__metatable" );
                }
                lua_setmetatable( L, -2 );
304
                input_item_Release( p_input );
305
            }
Fabio Ritrovato's avatar
Fabio Ritrovato committed
306 307 308
            while( i_options > 0 )
                free( ppsz_options[--i_options] );
            free( ppsz_options );
309 310
        }
        else
311
            msg_Err( p_sd, "vlc.sd.add_item: the \"path\" parameter can't be empty" );
312 313 314 315 316 317 318 319 320
    }
    else
        msg_Err( p_sd, "Error parsing add_item arguments" );
    return 1;
}

static int vlclua_sd_remove_item( lua_State *L )
{
    services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
321
    if( !lua_isnil( L, 1 ) )
322
    {
323
        input_item_t **pp_input = luaL_checkudata( L, 1, "input_item_t" );
324 325
        if( *pp_input )
            services_discovery_RemoveItem( p_sd, *pp_input );
326 327
        /* Make sure we won't try to remove it again */
        *pp_input = NULL;
328 329 330 331
    }
    return 1;
}

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
static int vlclua_sd_remove_node( lua_State *L )
{
    services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L );
    if( !lua_isnil( L, 1 ) )
    {
        input_item_t **pp_input = luaL_checkudata( L, 1, "node" );
        if( *pp_input )
            services_discovery_RemoveItem( p_sd, *pp_input );
        /* Make sure we won't try to remove it again */
        *pp_input = NULL;
    }
    return 1;
}


347 348 349 350 351 352 353 354
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" );
    if( *pp_node )
    {
        if( lua_istable( L, -1 ) )
        {
355
            lua_getfield( L, -1, "path" );
356 357
            if( lua_isstring( L, -1 ) )
            {
358 359 360 361
                const char *psz_path = lua_tostring( L, -1 );

                /* The table must be at the top of the stack when calling
                 * vlclua_read_options() */
Fabio Ritrovato's avatar
Fabio Ritrovato committed
362 363
                char **ppsz_options = NULL;
                int i_options = 0;
364
                lua_pushvalue( L, -2 );
Fabio Ritrovato's avatar
Fabio Ritrovato committed
365
                vlclua_read_options( p_sd, L, &i_options, &ppsz_options );
366

367
                input_item_t *p_input = input_item_New( psz_path, psz_path );
368
                lua_pop( L, 2 );
369

370
                if( p_input )
371
                {
372 373 374
                    input_item_AddOptions( p_input, i_options,
                                           (const char **)ppsz_options,
                                           VLC_INPUT_OPTION_TRUSTED );
375 376
                    input_item_node_t *p_input_node = input_item_node_Create( *pp_node );

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
                    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 );
                    lua_getfield( L, -1, "duration" );
                    if( lua_isnumber( L, -1 ) )
                        input_item_SetDuration( p_input, (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 );
                    input_item_node_AppendItem( p_input_node, p_input );
                    input_item_node_PostAndDelete( p_input_node );
                    input_item_t **udata = (input_item_t **)
                                           lua_newuserdata( L, sizeof( input_item_t * ) );
                    *udata = p_input;
                    if( luaL_newmetatable( L, "input_item_t" ) )
                    {
393 394 395
                        lua_newtable( L );
                        luaL_register( L, NULL, vlclua_item_reg );
                        lua_setfield( L, -2, "__index" );
396 397 398 399
                        lua_pushliteral( L, "none of your business" );
                        lua_setfield( L, -2, "__metatable" );
                    }
                    lua_setmetatable( L, -2 );
400
                    input_item_Release( p_input );
401
                }
Fabio Ritrovato's avatar
Fabio Ritrovato committed
402 403 404
                while( i_options > 0 )
                    free( ppsz_options[--i_options] );
                free( ppsz_options );
405 406
            }
            else
407
                msg_Err( p_sd, "node:add_subitem: the \"path\" parameter can't be empty" );
408 409 410 411 412 413 414
        }
        else
            msg_Err( p_sd, "Error parsing add_subitem arguments" );
    }
    return 1;
}

415
static int vlclua_node_add_subnode( lua_State *L )
416 417 418 419 420 421 422 423 424 425
{
    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" );
    if( *pp_node )
    {
        if( lua_istable( L, -1 ) )
        {
            lua_getfield( L, -1, "title" );
            if( lua_isstring( L, -1 ) )
            {
426
                const char *psz_name = lua_tostring( L, -1 );
427 428 429
                input_item_t *p_input = input_item_NewExt( "vlc://nop",
                                                           psz_name, -1,
                                                           ITEM_TYPE_NODE, ITEM_NET_UNKNOWN );
430 431
                lua_pop( L, 1 );

432
                if( p_input )
433
                {
434 435
                    input_item_node_t *p_input_node = input_item_node_Create( *pp_node );

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
                    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 );
                        input_item_SetArtURL( p_input, psz_value );
                        free( psz_value );
                    }
                    input_item_node_AppendItem( p_input_node, p_input );
                    input_item_node_PostAndDelete( p_input_node );
                    input_item_t **udata = (input_item_t **)
                                           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_setmetatable( L, -2 );
457 458 459 460 461 462 463 464 465 466 467
                }
            }
            else
                msg_Err( p_sd, "node:add_node: the \"title\" parameter can't be empty" );
        }
        else
            msg_Err( p_sd, "Error parsing add_node arguments" );
    }
    return 1;
}

468 469 470
/*****************************************************************************
 *
 *****************************************************************************/
471
static const luaL_Reg vlclua_sd_sd_reg[] = {
472 473 474
    { "add_node", vlclua_sd_add_node },
    { "add_item", vlclua_sd_add_item },
    { "remove_item", vlclua_sd_remove_item },
475
    { "remove_node", vlclua_sd_remove_node },
476 477 478
    { NULL, NULL }
};

479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
void luaopen_sd_sd( lua_State *L )
{
    lua_newtable( L );
    luaL_register( L, NULL, vlclua_sd_sd_reg );
    lua_setfield( L, -2, "sd" );
}

static const luaL_Reg vlclua_sd_intf_reg[] = {
    { "get_services_names", vlclua_sd_get_services_names },
    { "add", vlclua_sd_add },
    { "remove", vlclua_sd_remove },
    { "is_loaded", vlclua_sd_is_loaded },
    { NULL, NULL }
};

void luaopen_sd_intf( lua_State *L )
495 496
{
    lua_newtable( L );
497
    luaL_register( L, NULL, vlclua_sd_intf_reg );
498 499
    lua_setfield( L, -2, "sd" );
}