From: Antoine Cellerier Date: Mon, 14 May 2007 22:27:36 +0000 (+0000) Subject: * luaplaylist.c: First usable version of the luaplaylist module X-Git-Tag: 0.9.0-test0~7438 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=bea5e7ec249798ecb960a5ca947c912b61da7045;p=vlc * luaplaylist.c: First usable version of the luaplaylist module * share/luaplaylist/*: Lua playlist scripts for youtube, daily motion, metacafe and google video The luaplaylist demux has a score of 0 so you have to use --demux lua,any to enable it. I'll change its score once people report that it works fine. --- diff --git a/modules/demux/playlist/luaplaylist.c b/modules/demux/playlist/luaplaylist.c index 3eb315d738..b8b9a99aa4 100644 --- a/modules/demux/playlist/luaplaylist.c +++ b/modules/demux/playlist/luaplaylist.c @@ -28,6 +28,7 @@ #include #include #include +#include #include /* ENOMEM */ #include "playlist.h" @@ -88,9 +89,8 @@ static int vlclua_peek( lua_State *p_state ) n = lua_tonumber( p_state, 1 ); lua_pop( p_state, i ); i_peek = stream_Peek( p_demux->s, &p_peek, n ); - lua_pushnumber( p_state, i_peek ); lua_pushlstring( p_state, (const char *)p_peek, i_peek ); - return 2; + return 1; } static int vlclua_readline( lua_State *p_state ) @@ -115,6 +115,31 @@ static int vlclua_decode_uri( lua_State *p_state ) return 1; } +static int vlclua_resolve_xml_special_chars( lua_State *p_state ) +{ + int i = lua_gettop( p_state ); + if( !i ) return 0; + const char *psz_cstring = lua_tostring( p_state, 1 ); + if( !psz_cstring ) return 0; + char *psz_string = strdup( psz_cstring ); + lua_pop( p_state, i ); + resolve_xml_special_chars( psz_string ); + lua_pushstring( p_state, psz_string ); + free( psz_string ); + return 1; +} + +static int file_select( const char *file ) +{ + int i = strlen( file ); + return i > 4 && !strcmp( file+i-4, ".lua" ); +} + +static int file_compare( const char **a, const char **b ) +{ + return strcmp( *a, *b ); +} + /***************************************************************************** * Import_LuaPlaylist: main import function *****************************************************************************/ @@ -122,6 +147,22 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t *)p_this; lua_State *p_state; + int i_ret = VLC_EGENERIC; + char **ppsz_filelist = NULL; + char **ppsz_fileend = NULL; + char **ppsz_file; + DIR *dir; + char *psz_filename = NULL; + int i_files; + const char psz_dir[] = "share/luaplaylist"; /* FIXME */ + + static luaL_Reg p_reg[] = + { + { "readline", vlclua_readline }, + { "peek", vlclua_peek }, + { "decode_uri", vlclua_decode_uri }, + { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars } + }; p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) ); if( !p_demux->p_sys ) @@ -145,13 +186,6 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) /* Load Lua libraries */ luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */ - static luaL_Reg p_reg[] = - { - { "readline", vlclua_readline }, - { "peek", vlclua_peek }, - { "decode_uri", vlclua_decode_uri } - }; - luaL_register( p_state, "vlc", p_reg ); lua_pushlightuserdata( p_state, p_demux ); lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" ); @@ -162,45 +196,82 @@ int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) lua_pop( p_state, 1 ); + dir = utf8_opendir( psz_dir ); + if( !dir ) goto error; + i_files = utf8_loaddir( dir, &ppsz_filelist, file_select, file_compare ); + if( i_files < 1 ) goto error; + ppsz_fileend = ppsz_filelist + i_files; + + for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ ) { - const char *psz_filename = "share/luaplaylist/test.lua"; - int i_ret; + free( psz_filename ); psz_filename = NULL; + asprintf( &psz_filename, "%s/%s", psz_dir, *ppsz_file ); + msg_Dbg( p_demux, "Trying Lua playlist script %s", psz_filename ); + + /* Ugly hack to delete previous versions of the probe() and parse() + * functions. */ + lua_pushnil( p_state ); + lua_pushnil( p_state ); + lua_setglobal( p_state, "probe" ); + lua_setglobal( p_state, "parse" ); /* Load and run the script(s) */ if( luaL_dofile( p_state, psz_filename ) ) { - msg_Warn( p_demux, "Error while runing script %s: %s", psz_filename, lua_tostring( p_state, lua_gettop( p_state ) ) ); - return VLC_EGENERIC; + msg_Warn( p_demux, "Error loading script %s: %s", psz_filename, + lua_tostring( p_state, lua_gettop( p_state ) ) ); + lua_pop( p_state, 1 ); + continue; } - if( lua_gettop( p_state ) ) - { - i_ret = lua_toboolean( p_state, 1 ); - printf( "Script returned %d: %d\n", 1, i_ret ); + lua_getglobal( p_state, "probe" ); - while( lua_gettop( p_state ) != 1 ) - { - printf( "Script returned %d: %s\n", lua_gettop( p_state ), - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); - } + if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function probe() not found", psz_filename ); + lua_pop( p_state, 1 ); + continue; + } + if( lua_pcall( p_state, 0, 1, 0 ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function probe(): %s", psz_filename, + lua_tostring( p_state, lua_gettop( p_state ) ) ); lua_pop( p_state, 1 ); + continue; + } - if( !i_ret ) + if( lua_gettop( p_state ) ) + { + if( lua_toboolean( p_state, 1 ) ) { - E_(Close_LuaPlaylist)( p_this ); - return VLC_EGENERIC; + msg_Dbg( p_demux, "Lua playlist script %s's " + "probe() function was successful", psz_filename ); + i_ret = VLC_SUCCESS; } + lua_pop( p_state, 1 ); + + if( i_ret == VLC_SUCCESS ) break; } - else + } + + error: + free( psz_filename ); + + if( ppsz_filelist ) { - E_(Close_LuaPlaylist)( p_this ); - return VLC_EGENERIC; + for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; + ppsz_file++ ) + free( *ppsz_file ); + free( ppsz_filelist ); } - } - return VLC_SUCCESS; + if( dir ) closedir( dir ); + if( i_ret != VLC_SUCCESS ) + E_(Close_LuaPlaylist)( p_this ); + return i_ret; } /***************************************************************************** @@ -215,17 +286,91 @@ void E_(Close_LuaPlaylist)( vlc_object_t *p_this ) static int Demux( demux_t *p_demux ) { - /*input_item_t *p_input;*/ + input_item_t *p_input; + lua_State *p_state = p_demux->p_sys->p_state; + char psz_filename[] = "FIXME"; INIT_PLAYLIST_STUFF; -#if 0 - p_input = input_ItemNewExt( p_playlist, psz_url, psz_title, 0, NULL, -1 ); - playlist_BothAddInput( p_playlist, p_input, - p_item_in_category, - PLAYLIST_APPEND | PLAYLIST_SPREPARSE, - PLAYLIST_END, NULL, NULL, VLC_FALSE ); -#endif + lua_getglobal( p_state, "parse" ); + + if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function parse() not found", psz_filename ); + E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); + return VLC_EGENERIC; + } + + if( lua_pcall( p_state, 0, 1, 0 ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function parse(): %s", psz_filename, + lua_tostring( p_state, lua_gettop( p_state ) ) ); + E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); + return VLC_EGENERIC; + } + + if( lua_gettop( p_state ) ) + { + int 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 ) ) + { + const char *psz_url = NULL; + const char *psz_title = NULL; + lua_getfield( p_state, t+2, "url" ); + if( lua_isstring( p_state, t+3 ) ) + { + psz_url = lua_tostring( p_state, t+3 ); + printf("URL: %s\n", psz_url ); + lua_getfield( p_state, t+2, "title" ); + if( lua_isstring( p_state, t+4 ) ) + { + psz_title = lua_tostring( p_state, t+4 ); + printf("Title: %s\n", psz_title ); + } + else + { + psz_title = psz_url; + } + p_input = input_ItemNewExt( p_playlist, psz_url, + psz_title, 0, NULL, -1 ); + playlist_BothAddInput( + p_playlist, p_input, + p_item_in_category, + PLAYLIST_APPEND | PLAYLIST_SPREPARSE, + PLAYLIST_END, NULL, NULL, VLC_FALSE ); + lua_pop( p_state, 1 ); /* pop "title" */ + } + else + { + printf("URL isn't a string\n"); + } + lua_pop( p_state, 1 ); /* pop "url" */ + } + else + { + printf("This isn't a table !!!\n" ); + } + lua_pop( p_state, 1 ); /* pop the value, keep the key for + * the next lua_next() call */ + } + lua_pop( p_state, 1 ); /* pop the last key */ + } + else + { + msg_Warn( p_demux, "Script didn't return a table" ); + } + } + else + { + msg_Err( p_demux, "Script went completely foobar" ); + } HANDLE_PLAY_AND_RELEASE; diff --git a/share/Makefile.am b/share/Makefile.am index 31b257e810..b5420069b9 100644 --- a/share/Makefile.am +++ b/share/Makefile.am @@ -234,4 +234,9 @@ DIST_osdmenu_dvd = \ osdmenu/dvd/volume/volume05.png \ osdmenu/dvd/unselect/barroff.png -DIST_lua= luaplaylist/test.lua +DIST_lua= \ + luaplaylist/README \ + luaplaylist/dailymotion.lua \ + luaplaylist/youtube.lua \ + luaplaylist/metacafe.lua \ + luaplaylist/googlevideo.lua diff --git a/share/luaplaylist/README.txt b/share/luaplaylist/README.txt new file mode 100644 index 0000000000..03d1072a48 --- /dev/null +++ b/share/luaplaylist/README.txt @@ -0,0 +1 @@ +See "youtube.lua" for instructions to code your own Lua playlist script. diff --git a/share/luaplaylist/dailymotion.lua b/share/luaplaylist/dailymotion.lua new file mode 100644 index 0000000000..8943f5f88f --- /dev/null +++ b/share/luaplaylist/dailymotion.lua @@ -0,0 +1,22 @@ +-- $Id$ + +-- Probe function. +function probe() + return vlc.access == "http" + and string.match( vlc.path, "dailymotion.com" ) + and vlc.peek( 9 ) == " ): return the first characters from the playlist file. + * vlc.readline(): return a new line of playlist data on each call. + THIS FUNCTION SHOULD NOT BE USED IN peek(). + * vlc.decode_uri( ): decode %xy characters in a string. + * vlc.resolve_xml_special_chars( ): decode &abc; characters in a string. + +Lua scripts are tried in alphabetical order in the luaplaylist/ directory. + +Lua documentation is available on http://www.lua.org . +VLC uses Lua 5.1 +All the Lua standard libraries are available. +--]] + +-- Helper function to get a parameter's value in a URL +function get_url_param( url, name ) + return string.gsub( vlc.path, "^.*"..name.."=([^&]*).*$", "%1" ) +end + +-- Probe function. +function probe() + return vlc.access == "http" + and string.match( vlc.path, "youtube.com" ) + and ( string.match( vlc.path, "watch%?v=" ) + or string.match( vlc.path, "watch_fullscreen%?video_id=" ) + or string.match( vlc.path, "p.swf" ) + or string.match( vlc.path, "player2.swf" ) ) +end + +-- Parse function. +function parse() + local p = {} + if string.match( vlc.path, "watch%?v=" ) + then -- This is the HTML page's URL + p[1] = { url = string.gsub( vlc.path, "^(.*)watch%?v=([^&]*).*$", "http://%1v/%2" ) } + while true do + -- Try to find the video's title + line = vlc.readline() + if not line then break end + if string.match( line, "