7022fad4d42165fcff884261af962c7ba554fb4f
[vlc.git] / modules / misc / lua / luameta.c
1 /*****************************************************************************
2  * luameta.c: Get meta/artwork using lua scripts
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *          Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifndef  _GNU_SOURCE
29 #   define  _GNU_SOURCE
30 #endif
31
32 #include <vlc/vlc.h>
33 #include <vlc_input.h>
34 #include <vlc_playlist.h>
35 #include <vlc_meta.h>
36 #include <vlc_url.h>
37 #include <vlc_strings.h>
38 #include <vlc_stream.h>
39 #include <vlc_charset.h>
40
41 #include "vlclua.h"
42
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
48                        lua_State * p_state, void * user_data );
49 static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
50                       lua_State * p_state, void * user_data );
51 static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item );
52
53
54 /*****************************************************************************
55  * Lua function bridge
56  *****************************************************************************/
57 /* Functions to register */
58 static luaL_Reg p_reg[] =
59 {
60     { "stream_new", vlclua_stream_new },
61     { "stream_read", vlclua_stream_read },
62     { "stream_readline", vlclua_stream_readline },
63     { "stream_delete", vlclua_stream_delete },
64     { "decode_uri", vlclua_decode_uri },
65     { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
66     { "msg_dbg", vlclua_msg_dbg },
67     { "msg_warn", vlclua_msg_warn },
68     { "msg_err", vlclua_msg_err },
69     { "msg_info", vlclua_msg_info },
70     { NULL, NULL }
71 };
72
73 /*****************************************************************************
74  * Init lua
75  *****************************************************************************/
76 static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item )
77 {
78     lua_State * p_state = luaL_newstate();
79     if( !p_state )
80     {
81         msg_Err( p_this, "Could not create new Lua State" );
82         return NULL;
83     }
84     char *psz_meta;
85
86     /* Load Lua libraries */
87     luaL_openlibs( p_state ); /* XXX: Don't open all the libs? */
88     
89     luaL_register( p_state, "vlc", p_reg );
90     
91     lua_pushlightuserdata( p_state, p_this );
92     lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
93    
94     psz_meta = input_item_GetName( p_item );
95     lua_pushstring( p_state, psz_meta );
96     lua_setfield( p_state, lua_gettop( p_state ) - 1, "name" );
97     free( psz_meta );
98    
99     psz_meta = input_item_GetArtist( p_item );
100     lua_pushstring( p_state, psz_meta );
101     lua_setfield( p_state, lua_gettop( p_state ) - 1, "artist" );
102     free( psz_meta );
103    
104     psz_meta = input_item_GetTitle( p_item ) ;
105     lua_pushstring( p_state, psz_meta );
106     lua_setfield( p_state, lua_gettop( p_state ) - 1, "title" );
107     free( psz_meta );
108    
109     psz_meta = input_item_GetAlbum( p_item );
110     lua_pushstring( p_state, psz_meta );
111     lua_setfield( p_state, lua_gettop( p_state ) - 1, "album" );
112     free( psz_meta );
113
114     psz_meta = input_item_GetArtURL( p_item );
115     lua_pushstring( p_state, psz_meta );
116     lua_setfield( p_state, lua_gettop( p_state ) - 1, "arturl" );
117     free( psz_meta );
118     /* XXX: all should be passed ( could use macro ) */
119
120     return p_state;
121 }
122
123 /*****************************************************************************
124  * Called through lua_scripts_batch_execute to call 'fetch_art' on the script
125  * pointed by psz_filename.
126  *****************************************************************************/
127 static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
128                       lua_State * p_state, void * user_data )
129 {
130     int i_ret = VLC_EGENERIC;
131     input_item_t * p_input = user_data;
132     int s;
133
134     /* Ugly hack to delete previous versions of the fetchart()
135     * functions. */
136     lua_pushnil( p_state );
137     lua_setglobal( p_state, "fetch_art" );
138     
139     /* Load and run the script(s) */
140     if( luaL_dofile( p_state, psz_filename ) )
141     {
142         msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
143                   lua_tostring( p_state, lua_gettop( p_state ) ) );
144         lua_pop( p_state, 1 );
145         return VLC_EGENERIC;
146     }
147
148     lua_getglobal( p_state, "fetch_art" );
149
150     if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
151     {
152         msg_Warn( p_this, "Error while runing script %s, "
153                   "function fetch_art() not found", psz_filename );
154         lua_pop( p_state, 1 );
155         return VLC_EGENERIC;
156     }
157
158     if( lua_pcall( p_state, 0, 1, 0 ) )
159     {
160         msg_Warn( p_this, "Error while runing script %s, "
161                   "function fetch_art(): %s", psz_filename,
162                   lua_tostring( p_state, lua_gettop( p_state ) ) );
163         lua_pop( p_state, 1 );
164         return VLC_EGENERIC;
165     }
166
167     if((s = lua_gettop( p_state )))
168     {
169         const char * psz_value;
170
171         if( lua_isstring( p_state, s ) )
172         {
173             psz_value = lua_tostring( p_state, s );
174             if( psz_value && *psz_value != 0 )
175             {
176                 msg_Dbg( p_this, "setting arturl: %s", psz_value );
177                 input_item_SetArtURL ( p_input, psz_value );
178                 i_ret = VLC_SUCCESS;
179             }
180         }
181         else
182         {
183             msg_Err( p_this, "Lua playlist script %s: "
184                  "didn't return a string", psz_filename );
185         }
186     }
187     else
188     {
189         msg_Err( p_this, "Script went completely foobar" );
190     }
191
192     return i_ret;
193 }
194
195 /*****************************************************************************
196  * Called through lua_scripts_batch_execute to call 'fetch_meta' on the script
197  * pointed by psz_filename.
198  *****************************************************************************/
199 static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
200                        lua_State * p_state, void * user_data )
201 {
202     input_item_t * p_input = user_data;
203     int t;
204
205     /* Ugly hack to delete previous versions of the fetchmeta()
206     * functions. */
207     lua_pushnil( p_state );
208     lua_setglobal( p_state, "fetch_meta" );
209     
210     /* Load and run the script(s) */
211     if( luaL_dofile( p_state, psz_filename ) )
212     {
213         msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
214                   lua_tostring( p_state, lua_gettop( p_state ) ) );
215         lua_pop( p_state, 1 );
216         return VLC_EGENERIC;
217     }
218     
219     lua_getglobal( p_state, "fetch_meta" );
220     
221     if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
222     {
223         msg_Warn( p_this, "Error while runing script %s, "
224                   "function fetch_meta() not found", psz_filename );
225         lua_pop( p_state, 1 );
226         return VLC_EGENERIC;
227     }
228     
229     if( lua_pcall( p_state, 0, 1, 0 ) )
230     {
231         msg_Warn( p_this, "Error while runing script %s, "
232                   "function fetch_meta(): %s", psz_filename,
233                   lua_tostring( p_state, lua_gettop( p_state ) ) );
234         lua_pop( p_state, 1 );
235         return VLC_EGENERIC;
236     }
237     
238
239     if((t = lua_gettop( p_state )))
240     {
241         if( lua_istable( p_state, t ) )
242         {
243             vlclua_read_meta_data( p_this, p_state, t, t+1, p_input );
244             vlclua_read_custom_meta_data( p_this, p_state, t, t+1, p_input );
245         }
246         else
247         {
248             msg_Err( p_this, "Lua playlist script %s: "
249                  "didn't return a table", psz_filename );
250         }
251     }
252     else
253     {
254         msg_Err( p_this, "Script went completely foobar" );
255     }
256
257     /* We tell the batch thing to continue, hence all script
258      * will get the change to add its meta. This behaviour could
259      * be changed. */
260     return VLC_EGENERIC;
261 }
262
263 /*****************************************************************************
264  * Module entry point for meta.
265  *****************************************************************************/
266 int E_(FindMeta)( vlc_object_t *p_this )
267 {
268     meta_engine_t *p_me = (meta_engine_t *)p_this;
269     input_item_t *p_item = p_me->p_item;
270     lua_State *p_state = vlclua_meta_init( p_this, p_item );
271     
272     int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, p_state, p_item );
273     lua_close( p_state );
274     return i_ret;
275 }
276
277 /*****************************************************************************
278  * Module entry point for art.
279  *****************************************************************************/
280 int E_(FindArt)( vlc_object_t *p_this )
281 {
282     playlist_t *p_playlist = (playlist_t *)p_this;
283     input_item_t *p_item = (input_item_t *)(p_playlist->p_private);
284     lua_State *p_state = vlclua_meta_init( p_this, p_item );
285     
286     int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, p_state, p_item );
287     lua_close( p_state );
288     return i_ret;
289 }
290