Add services discovery support and enhance playlist support for lua interface modules...
authorAntoine Cellerier <dionoea@videolan.org>
Mon, 12 Nov 2007 22:00:47 +0000 (22:00 +0000)
committerAntoine Cellerier <dionoea@videolan.org>
Mon, 12 Nov 2007 22:00:47 +0000 (22:00 +0000)
modules/misc/lua/Modules.am
modules/misc/lua/intf.c
modules/misc/lua/sd.c [new file with mode: 0644]
modules/misc/lua/vlc.h
share/http-lua/dialogs/playlist
share/http-lua/requests/playlist.xml
share/http-lua/requests/status.xml
share/luaintf/rc.lua

index efb12a8..36fabbb 100644 (file)
@@ -1 +1 @@
-SOURCES_lua = playlist.c meta.c intf.c vlc.c vlc.h callbacks.c objects.c variables.c configuration.c net.c vlm.c httpd.c acl.c
+SOURCES_lua = playlist.c meta.c intf.c vlc.c vlc.h callbacks.c objects.c variables.c configuration.c net.c vlm.c httpd.c acl.c sd.c
index e6ad115..88a69f4 100644 (file)
@@ -53,7 +53,7 @@ struct intf_sys_t
 /*****************************************************************************
  * Internal lua<->vlc utils
  *****************************************************************************/
-static inline playlist_t *vlclua_get_playlist_internal( lua_State *L )
+playlist_t *vlclua_get_playlist_internal( lua_State *L )
 {
     vlc_object_t *p_this = vlclua_get_this( L );
     return pl_Yield( p_this );
@@ -355,6 +355,15 @@ static int vlclua_playlist_next( lua_State * L )
     return 0;
 }
 
+static int vlclua_playlist_skip( lua_State * L )
+{
+    int i_skip = luaL_checkint( L, 1 );
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Skip( p_playlist, i_skip );
+    vlc_object_release( p_playlist );
+    return 0;
+}
+
 static int vlclua_playlist_play( lua_State * L )
 {
     playlist_t *p_playlist = vlclua_get_playlist_internal( L );
@@ -363,6 +372,14 @@ static int vlclua_playlist_play( lua_State * L )
     return 0;
 }
 
+static int vlclua_playlist_pause( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Pause( 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 );
@@ -406,37 +423,14 @@ static int vlclua_playlist_random( lua_State * L )
 
 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 );
-
+    int i_id = luaL_checkint( L, 1 );
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    int i_ret = playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
+                                  VLC_TRUE, NULL,
+                                  playlist_ItemGetById( p_playlist, i_id,
+                                                        VLC_TRUE ) );
     vlc_object_release( p_playlist );
-    lua_pushboolean( L, 1 );
-    return 1;
+    return vlclua_push_ret( L, i_ret );
 }
 
 static int vlclua_playlist_add( lua_State *L )
@@ -463,23 +457,31 @@ static int vlclua_playlist_enqueue( lua_State *L )
     return 1;
 }
 
-static int vlclua_playlist_get( lua_State *L )
+static void push_playlist_item( lua_State *L, playlist_item_t *p_item );
+static void push_playlist_item( lua_State *L, playlist_item_t *p_item )
 {
-    /* 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++ )
+    input_item_t *p_input = p_item->p_input;
+    int i_flags = p_item->i_flags;
+    lua_newtable( L );
+    lua_pushinteger( L, p_item->i_id );
+    lua_setfield( L, -2, "id" );
+    lua_newtable( L );
+#define CHECK_AND_SET_FLAG( name, label ) \
+    if( i_flags & PLAYLIST_ ## name ## _FLAG ) \
+    { \
+        lua_pushboolean( L, 1 ); \
+        lua_setfield( L, -2, #label ); \
+    }
+    CHECK_AND_SET_FLAG( SAVE, save )
+    CHECK_AND_SET_FLAG( SKIP, skip )
+    CHECK_AND_SET_FLAG( DBL, disabled )
+    CHECK_AND_SET_FLAG( RO, ro )
+    CHECK_AND_SET_FLAG( REMOVE, remove )
+    CHECK_AND_SET_FLAG( EXPANDED, expanded )
+#undef CHECK_AND_SET_FLAG
+    lua_setfield( L, -2, "flags" );
+    if( p_input )
     {
-        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 );
@@ -492,16 +494,166 @@ static int vlclua_playlist_get( lua_State *L )
         lua_pushinteger( L, p_input->i_nb_played );
         lua_setfield( L, -2, "nb_played" );
         /* TODO: add (optional) info categories, meta, options, es */
+    }
+    if( p_item->i_children >= 0 )
+    {
+        int i;
+        lua_createtable( L, p_item->i_children, 0 );
+        for( i = 0; i < p_item->i_children; i++ )
+        {
+            push_playlist_item( L, p_item->pp_children[i] );
+            lua_rawseti( L, -2, i+1 );
+        }
+        lua_setfield( L, -2, "children" );
+    }
+}
+
+static int vlclua_playlist_get( lua_State *L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    int b_category = luaL_optboolean( L, 2, 1 ); /* Default to tree playlist (discared when 1st argument is a playlist_item's id) */
+    playlist_item_t *p_item = NULL;
+
+    if( lua_isnumber( L, 1 ) )
+    {
+        int i_id = lua_tointeger( L, 1 );
+        p_item = playlist_ItemGetById( p_playlist, i_id, VLC_TRUE );
+        if( !p_item )
+        {
+            vlc_object_release( p_playlist );
+            return 0; /* Should we return an error instead? */
+        }
+    }
+    else if( lua_isstring( L, 1 ) )
+    {
+        const char *psz_what = lua_tostring( L, 1 );
+        if( !strcasecmp( psz_what, "normal" )
+         || !strcasecmp( psz_what, "playlist" ) )
+            p_item = b_category ? p_playlist->p_local_category
+                                : p_playlist->p_local_onelevel;
+        else if( !strcasecmp( psz_what, "ml" )
+              || !strcasecmp( psz_what, "media library" ) )
+            p_item = b_category ? p_playlist->p_ml_category
+                                : p_playlist->p_ml_onelevel;
+        else if( !strcasecmp( psz_what, "root" ) )
+            p_item = b_category ? p_playlist->p_root_category
+                                : p_playlist->p_root_onelevel;
+        else
+        {
+            int i;
+            for( i = 0; i < p_playlist->i_sds; i++ )
+            {
+                if( !strcasecmp( psz_what,
+                                 p_playlist->pp_sds[i]->p_sd->psz_module ) )
+                {
+                    p_item = b_category ? p_playlist->pp_sds[i]->p_cat
+                                        : p_playlist->pp_sds[i]->p_one;
+                    break;
+                }
+            }
+            if( !p_item )
+            {
+                vlc_object_release( p_playlist );
+                return 0; /* Should we return an error instead? */
+            }
+        }
+    }
+    else
+    {
+        p_item = b_category ? p_playlist->p_root_category
+                            : p_playlist->p_root_onelevel;
+    }
+    push_playlist_item( L, p_item );
+    vlc_object_release( p_playlist );
+    return 1;
+}
+#if 0
+    int s;
+    lua_createtable( L, 0, 2 + p_playlist->i_sds );
+    for( s = -2; s < p_playlist->i_sds; s++ )
+    {
+        playlist_item_t *p_root;
+        switch( s )
+        {
+            case -2:
+                /* local/normal playlist */
+                lua_pushstring( L, "local" );
+                p_root = p_playlist->p_local_onelevel;
+                break;
+            case -1:
+                /* media library */
+                lua_pushstring( L, "ml" );
+                p_root = p_playlist->p_ml_onelevel;
+                break;
+            default:
+                lua_pushstring( L, p_playlist->pp_sds[s]->p_sd->psz_module );
+                printf("%s\n", p_playlist->pp_sds[s]->p_sd->psz_module );
+                p_root = p_playlist->pp_sds[s]->p_one;
+                break;
+        }
+        printf("s = %d\n", s);
+        printf("children = %d\n", p_root->i_children );
+        push_playlist_item( L, p_root );
         lua_settable( L, -3 );
     }
+    printf("done\n");
+#endif
+
+static int vlclua_playlist_search( lua_State *L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    const char *psz_string = luaL_optstring( L, 1, "" );
+    int b_category = luaL_optboolean( L, 2, 1 ); /* default to category */
+    playlist_LiveSearchUpdate( p_playlist,
+                               b_category ? p_playlist->p_root_category
+                                          : p_playlist->p_root_onelevel,
+                               psz_string );
+    push_playlist_item( L, p_playlist->p_root_category );
     vlc_object_release( p_playlist );
     return 1;
 }
 
+static int vlc_sort_key_from_string( const char *psz_name )
+{
+    static const struct
+    {
+        const char *psz_name;
+        int i_key;
+    } pp_keys[] =
+        { { "id", SORT_ID },
+          { "title", SORT_TITLE },
+          { "title nodes first", SORT_TITLE_NODES_FIRST },
+          { "artist", SORT_ARTIST },
+          { "genre", SORT_GENRE },
+          { "random", SORT_RANDOM },
+          { "duration", SORT_DURATION },
+          { "title numeric", SORT_TITLE_NUMERIC },
+          { "album", SORT_ALBUM },
+          { NULL, -1 } };
+    int i;
+    for( i = 0; pp_keys[i].psz_name; i++ )
+    {
+        if( !strcmp( psz_name, pp_keys[i].psz_name ) )
+            return pp_keys[i].i_key;
+    }
+    return -1;
+}
+
 static int vlclua_playlist_sort( lua_State *L )
 {
     /* allow setting the different sort keys */
-    return 0;
+    int i_mode = vlc_sort_key_from_string( luaL_checkstring( L, 1 ) );
+    if( i_mode == -1 )
+        return luaL_error( L, "Invalid search key." );
+    int i_type = luaL_optboolean( L, 2, 0 ) ? ORDER_REVERSE : ORDER_NORMAL;
+    int b_category = luaL_optboolean( L, 3, 1 ); /* default to category */
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_item_t *p_root = b_category ? p_playlist->p_local_category
+                                         : p_playlist->p_local_onelevel;
+    int i_ret = playlist_RecursiveNodeSort( p_playlist, p_root, i_mode,
+                                            i_type );
+    vlc_object_release( p_playlist );
+    return vlclua_push_ret( L, i_ret );
 }
 
 /* FIXME: split this in 3 different functions? */
@@ -655,7 +807,9 @@ static luaL_Reg p_reg_playlist[] =
 {
     { "prev", vlclua_playlist_prev },
     { "next", vlclua_playlist_next },
+    { "skip", vlclua_playlist_skip },
     { "play", vlclua_playlist_play },
+    { "pause", vlclua_playlist_pause },
     { "stop", vlclua_playlist_stop },
     { "clear", vlclua_playlist_clear },
     { "repeat_", vlclua_playlist_repeat },
@@ -666,11 +820,24 @@ static luaL_Reg p_reg_playlist[] =
     { "add", vlclua_playlist_add },
     { "enqueue", vlclua_playlist_enqueue },
     { "get", vlclua_playlist_get },
+    { "search", vlclua_playlist_search },
+    { "sort", vlclua_playlist_sort },
+
     { "stats", vlclua_input_stats },
 
     { NULL, NULL }
 };
 
+static luaL_Reg p_reg_sd[] =
+{
+    { "get_services_names", vlclua_sd_get_services_names },
+    { "add", vlclua_sd_add },
+    { "remove", vlclua_sd_remove },
+    { "is_loaded", vlclua_sd_is_loaded },
+
+    { NULL, NULL }
+};
+
 static luaL_Reg p_reg_volume[] =
 {
     { "get", vlclua_volume_get },
@@ -888,6 +1055,7 @@ int E_(Open_LuaIntf)( vlc_object_t *p_this )
     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, "sd", p_reg_sd );
     luaL_register_submodule( L, "volume", p_reg_volume );
     luaL_register_submodule( L, "osd", p_reg_osd );
     luaL_register_submodule( L, "net", p_reg_net );
diff --git a/modules/misc/lua/sd.c b/modules/misc/lua/sd.c
new file mode 100644 (file)
index 0000000..3d10e48
--- /dev/null
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * sd.c: Services discovery 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_services_discovery.h>
+#include <vlc_playlist.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_sd_get_services_names( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    char **ppsz_longnames;
+    char **ppsz_names = services_discovery_GetServicesNames( p_this, &ppsz_longnames );
+    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;
+}
+
+int vlclua_sd_add( lua_State *L )
+{
+    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 );
+    vlc_object_release( p_playlist );
+    return vlclua_push_ret( L, i_ret );
+}
+
+int vlclua_sd_remove( lua_State *L )
+{
+    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 );
+    vlc_object_release( p_playlist );
+    return vlclua_push_ret( L, i_ret );
+}
+
+int vlclua_sd_is_loaded( lua_State *L )
+{
+    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 ));
+    vlc_object_release( p_playlist );
+    return 1;
+}
index 77d61f1..f458fc3 100644 (file)
@@ -86,6 +86,11 @@ static inline int luaL_checkboolean( lua_State *L, int narg )
     return lua_toboolean( L, narg );
 }
 
+static inline int luaL_optboolean( lua_State *L, int narg, int def )
+{
+    return luaL_opt( L, luaL_checkboolean, narg, def );
+}
+
 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
 {
     luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
@@ -119,6 +124,9 @@ int vlclua_gc_release( lua_State *L );
 int vlclua_object_find( lua_State *L );
 int vlclua_object_find_name( lua_State *L );
 
+vlc_object_t * vlclua_get_this( lua_State * );
+playlist_t *vlclua_get_playlist_internal( lua_State * );
+
 int vlclua_add_callback( lua_State * );
 int vlclua_del_callback( lua_State * );
 
@@ -163,11 +171,15 @@ int vlclua_acl_add_host( lua_State * );
 int vlclua_acl_add_net( lua_State * );
 int vlclua_acl_load_file( lua_State * );
 
+int vlclua_sd_get_services_names( lua_State * );
+int vlclua_sd_add( lua_State * );
+int vlclua_sd_remove( lua_State * );
+int vlclua_sd_is_loaded( lua_State * );
+
 
 /*****************************************************************************
  * Lua function bridge
  *****************************************************************************/
-vlc_object_t * vlclua_get_this( lua_State * );
 #define vlclua_error( L ) luaL_error( L, "VLC lua error in file %s line %d (function %s)", __FILE__, __LINE__, __func__ )
 int vlclua_push_ret( lua_State *, int i_error );
 
index dbb8500..03d1611 100644 (file)
@@ -61,14 +61,17 @@ This dialog needs the following dialogs to be fully functional: <none>
             <span class="btn_text">Sort</span>
           </button>
           <div id="menu_sort" class="menu" >
-            <button class="menuout" onclick="pl_sort(1,0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Name ascending" >Name</button><br/>
-            <button class="menuout" onclick="pl_sort(1,1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Name descending" >Name reverse</button><br/>
-            <button class="menuout" onclick="pl_sort(3,0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Author ascending" >Author</button><br/>
-            <button class="menuout" onclick="pl_sort(3,1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Author ascending" >Author reverse</button><br/>
-            <button class="menuout" onclick="pl_sort(5,0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Randomize" >Random</button><br/>
-            <button class="menuout" onclick="pl_sort(7,0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Track number" >Track number</button><br/>
-            <button class="menuout" onclick="pl_sort(0,0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Id ascending" >Id</button><br/>
-            <button class="menuout" onclick="pl_sort(0,1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Id descending" >Id reverse</button><br/>
+            <button class="menuout" onclick="pl_sort('title',0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Title ascending" >Title</button><br/>
+            <button class="menuout" onclick="pl_sort('title',1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Title descending" >Title reverse</button><br/>
+            <button class="menuout" onclick="pl_sort('artist',0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Artist ascending" >Artist</button><br/>
+            <button class="menuout" onclick="pl_sort('artist',1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Artist ascending" >Artist reverse</button><br/>
+            <button class="menuout" onclick="pl_sort('album',0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Album ascending" >Album</button><br/>
+            <button class="menuout" onclick="pl_sort('album',1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Album ascending" >Album reverse</button><br/>
+            <button class="menuout" onclick="pl_sort('genre',0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Genre ascending" >Genre</button><br/>
+            <button class="menuout" onclick="pl_sort('genre',1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Genre ascending" >Genre reverse</button><br/>
+            <button class="menuout" onclick="pl_sort('random',0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Randomize" >Random</button><br/>
+            <button class="menuout" onclick="pl_sort('id',0);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Id ascending" >Id</button><br/>
+            <button class="menuout" onclick="pl_sort('id',1);hide_menu('menu_sort');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" title="Sort by Id descending" >Id reverse</button><br/>
           </div>
         </td>
         <td onmouseover="show_menu('menu_sd');" onmouseout="hide_menu('menu_sd');">
@@ -77,12 +80,12 @@ This dialog needs the following dialogs to be fully functional: <none>
             <span class="btn_text">Services Discovery</span>
           </button>
           <div id="menu_sd" class="menu" >
-          <?vlc --[[ FIXME
-            <vlc id="rpn" param1="services_discovery" />
-            <vlc id="foreach" param1="sd" param2="object" />
-            <button onclick="pl_sd('<vlc id="value" param1="sd" />');hide_menu('menu_sd');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" class="menuout" title="Toggle <vlc id="value" param1="sd.name" />" ><vlc id="value" param1="sd.name" /></button><br/>
-            <vlc id="end" />
-          ]] ?>
+          <?vlc
+            local sd = vlc.sd.get_services_names()
+            for n,ln in pairs(sd) do
+              print([[<button onclick="pl_sd(']]..n..[[');hide_menu('menu_sd');" onmouseover="setclass(this,'menuover');" onmouseout="setclass(this,'menuout');" class="menuout" title="Toggle ]]..ln..[[" >]]..ln..[[</button><br/>]])
+            end
+          ?>
           </div>
         </td>
       </tr>
index 823bd2c..463f5d3 100644 (file)
@@ -25,17 +25,44 @@ vim:syntax=lua
 < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
 ]] ?>
 
-<node id="0" name="Undefined" ro="ro">
 <?vlc
-local playlist = vlc.playlist.get()
-for i,item in ipairs(playlist) do
-  local name, path = vlc.convert_xml_special_chars(item.name,item.path)
-  print("<leaf id='"..tostring(i).."' uri='"..path.."' name='"..name.."' ro='ro' duration='"..tostring(item.duration).."'/>")
+--[[<node id="0" name="Undefined" ro="ro">]]
+function print_playlist(item)
+  if item.children then
+    local name = vlc.convert_xml_special_chars(item.name)
+    print("<node id=\""..tostring(item.id).."\" name=\""..name.."\" ro=\""..(item.flags.ro and "ro" or "rw").."\">")
+    for _, c in ipairs(item.children) do
+      print_playlist(c)
+    end
+    print("</node>")
+  else
+    local name, path = vlc.convert_xml_special_chars(item.name,item.path or "")
+    print("<leaf id='"..tostring(item.id).."' uri='"..path.."' name='"..name.."' ro='"..(item.flags.ro and "ro" or "rw").."' duration='"..tostring(item.duration).."'/>")
+  end
 end
+function a(t,pre)
+  local pre = pre or ""
+  for k,v in pairs(t) do
+    vlc.msg.err(pre..tostring(k).." : "..tostring(v))
+    if type(v) == "table" then
+      a(v,pre.."  ")
+    end
+  end
+end
+--[[
+for cat,pl in pairs(p) do
+  print("<node id=\"-1\" name=\""..cat.."\" ro=\"ro\">")
+  print_playlist(pl)
+  print("</node>")
+end
+--]]
+local p = vlc.playlist.get()
+-- a(p) Uncomment to debug
+print_playlist(p)
 ?>
-</node>
 
 <?vlc --[[
+</node>
 <node id="9" name="Nevermind" ro="rw">
 <leaf id="10" current="current" uri="file:///mnt/stuff/media/Nirvana/Nevermind/01 - Smells Like Teen Spirit.mp3" name="Smells Like Teen Spirit" ro="rw" duration="-1"/>
 ]]?>
index c126005..0e4b827 100644 (file)
@@ -64,7 +64,7 @@ elseif command == "pl_delete" then
 elseif command == "pl_empty" then
   vlc.playlist.clear()
 elseif command == "pl_sort" then
-  vlc.msg.err("FIXME: pl_sort unimplemented")
+  vlc.playlist.sort( val, id > 0 )
 elseif command == "pl_random" then
   vlc.playlist.random()
 elseif command == "pl_loop" then
@@ -72,14 +72,11 @@ elseif command == "pl_loop" then
 elseif command == "pl_repeat" then
   vlc.playlist.repeat_()
 elseif command == "pl_sd" then
-  vlc.msg.err("FIXME: pl_sd unimplemented")
-  --[[
-    <vlc id="if" param1="val value services_discovery_is_loaded" />
-      <vlc id="rpn" param1="val value services_discovery_remove" />
-    <vlc id="else" />
-      <vlc id="rpn" param1="val value services_discovery_add" />
-    <vlc id="end" />
-  ]]
+  if vlc.sd.is_loaded(val) then
+    vlc.sd.remove(val)
+  else
+    vlc.sd.add(val)
+  end
 elseif command == "fullscreen" then
   vlc.fullscreen()
 elseif command == "snapshot" then
index d02fb8b..acdbdc6 100644 (file)
@@ -164,29 +164,92 @@ function add(name,client,arg)
 end
 
 function playlist(name,client,arg)
-    -- TODO: add possibility to filter playlist items using a mask
+    function playlist0(item,prefix)
+        local prefix = prefix or ""
+        if not item.flags.disabled then
+            local str = "| "..prefix..tostring(item.id).." - "..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
+                str = str .. "]"
+            end
+            client:append(str)
+        end
+        if item.children then
+            for _, c in ipairs(item.children) do
+                playlist0(c,prefix.."  ")
+            end
+        end
+    end
     local playlist
-    if arg == "ml" then
-        playlist = vlc.playlist.get(true)
-        client:append("+----[ Playlist - Media Library ]")
+    if name == "search" then
+        playlist = vlc.playlist.search(arg or "")
+    else
+        if tonumber(arg) then
+            print "number"
+            playlist = vlc.playlist.get(tonumber(arg))
+        elseif arg then
+            print "string"
+            playlist = vlc.playlist.get(arg)
+        else
+            playlist = vlc.playlist.get()
+        end
+    end
+    if name == "search" then
+        client:append("+----[ Search - "..(arg or "`reset'").." ]")
+    else
+        client:append("+----[ Playlist - "..playlist.name.." ]")
+    end
+    if playlist.children then
+        for _, item in ipairs(playlist.children) do
+            playlist0(item)
+        end
+    else
+        playlist0(playlist)
+    end
+    if name == "search" then
+        client:append("+----[ End of search - Use `search' to reset ]")
     else
-        playlist = vlc.playlist.get()
-        client:append("+----[ Playlist ]")
+        client:append("+----[ End of 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
+
+function playlist_sort(name,client,arg)
+    if not arg then
+        client:append("Valid sort keys are: id, title, artist, genre, random, duration, album.")
+    else
+        vlc.playlist.sort(arg)
+    end
+end
+
+function services_discovery(name,client,arg)
+    if arg then
+        if vlc.sd.is_loaded(arg) then
+            vlc.sd.remove(arg)
+            client:append(arg.." disabled.")
+        else
+            vlc.sd.add(arg)
+            client:append(arg.." enabled.")
         end
-        if item.nb_played > 0 then
-            str = str.." played "..tostring(item.nb_played).." time"
-            if item.nb_played > 1 then
-                str = str .. "s"
+    else
+        local sd = vlc.sd.get_services_names()
+        client:append("+----[ Services discovery ]")
+        for n,ln in pairs(sd) do
+            local status
+            if vlc.sd.is_loaded(n) then
+                status = "enabled"
+            else
+                status = "disabled"
             end
+            client:append("| "..n..": " .. ln .. " (" .. status .. ")")
         end
-        client:append(str)
+        client:append("+----[ End of services discovery ]")
     end
-    client:append("+----[ End of playlist ]")
 end
 
 function print_text(label,text)
@@ -338,6 +401,9 @@ 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" } };
+    { "search"; { func = playlist; args = "[string]"; help = "search for items in playlist (or reset search)" } };
+    { "sort"; { func = playlist_sort; args = "key"; help = "sort the playlist" } };
+    { "sd"; { func = services_discovery; args = "[sd]"; help = "show services discovery or toggle" } };
     { "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" } };