API: Return URLs in escaped (percent-encoded) form
[libquvi.git] / src / lua / exec_media_script_parse.c
blob9bb5313fec361e596a06225b4717c02bbd7c3622
1 /* libquvi
2 * Copyright (C) 2012,2013 Toni Gundogdu <legatvs@gmail.com>
4 * This file is part of libquvi <http://quvi.sourceforge.net/>.
6 * This library is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public
8 * License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General
17 * Public License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
22 * NOTE: The error messages produced in these functions are intended for
23 * developers. They would typically be seen when a new script is
24 * being developed or an old one is being maintained.
26 * These messages should be clear, indicating the actual error,
27 * minimizing the time spent on locating the problem in the script.
30 #include "config.h"
32 #include <lauxlib.h>
33 #include <glib.h>
35 #include "quvi.h"
36 /* -- */
37 #include "_quvi_s.h"
38 #include "_quvi_media_s.h"
39 #include "_quvi_script_s.h"
40 /* -- */
41 #include "lua/setfield.h"
42 #include "lua/chk.h"
43 #include "lua/def.h"
44 #include "misc/re.h"
46 static const gchar script_func[] = "parse";
48 static void _foreach_video_property(lua_State *l, _quvi_media_t qm,
49 _quvi_media_stream_t qms)
51 lua_pushnil(l);
52 while (lua_next(l, LI_KEY))
54 l_chk_assign_n(l, MSS_VIDEO_BITRATE_KBIT_S, &qms->video.bitrate_kbit_s);
55 l_chk_assign_s(l, MSS_VIDEO_ENCODING, qms->video.encoding, TRUE, FALSE);
56 l_chk_assign_n(l, MSS_VIDEO_HEIGHT, &qms->video.height);
57 l_chk_assign_n(l, MSS_VIDEO_WIDTH, &qms->video.width);
58 lua_pop(l, 1);
62 static void _foreach_audio_property(lua_State *l, _quvi_media_t qm,
63 _quvi_media_stream_t qms)
65 lua_pushnil(l);
66 while (lua_next(l, LI_KEY))
68 l_chk_assign_n(l, MSS_AUDIO_BITRATE_KBIT_S, &qms->audio.bitrate_kbit_s);
69 l_chk_assign_s(l, MSS_AUDIO_ENCODING, qms->audio.encoding, TRUE, FALSE);
70 lua_pop(l, 1);
74 static void _foreach_flag_property(lua_State *l, _quvi_media_t qm,
75 _quvi_media_stream_t qms)
77 lua_pushnil(l);
78 while (lua_next(l, LI_KEY))
80 l_chk_assign_b(l, MSS_FLAGS_BEST, &qms->flags.best);
81 lua_pop(l, 1);
85 typedef void (*foreach_cb)(lua_State*, _quvi_media_t, _quvi_media_stream_t);
87 static void _chk_stream_sublevel(const gchar *level, lua_State *l,
88 _quvi_media_t qm, _quvi_media_stream_t qms,
89 foreach_cb cb)
91 if (lua_isstring(l, LI_KEY) && lua_istable(l, LI_VALUE))
93 if (g_strcmp0(lua_tostring(l, LI_KEY), level) == 0)
94 cb(l, qm, qms);
98 static gpointer _media_stream_new()
100 _quvi_media_stream_t qms = g_new0(struct _quvi_media_stream_s, 1);
101 qms->video.encoding = g_string_new(NULL);
102 qms->audio.encoding = g_string_new(NULL);
103 qms->container = g_string_new(NULL);
104 qms->url = g_string_new(NULL);
105 qms->id = g_string_new(NULL);
106 return (qms);
109 static void _has_stream_url(lua_State *l, _quvi_media_stream_t qms,
110 const gchar *script_path, const gint i)
112 if (qms->url->len ==0)
114 static const gchar *_E =
115 "%s: %s: must return a media stream URL in `qargs.%s[%d].%s'";
117 luaL_error(l, _E, script_path, script_func, MS_STREAMS, i, MSS_URL);
121 static _quvi_media_stream_t _new_stream(lua_State *l, _quvi_media_t qm,
122 const gchar *script_path,
123 const gint i)
125 _quvi_media_stream_t qms = _media_stream_new();
127 lua_pushnil(l);
128 while (lua_next(l, LI_KEY))
130 _chk_stream_sublevel(MSS_VIDEO, l, qm, qms, _foreach_video_property);
131 _chk_stream_sublevel(MSS_AUDIO, l, qm, qms, _foreach_audio_property);
132 _chk_stream_sublevel(MSS_FLAGS, l, qm, qms, _foreach_flag_property);
133 l_chk_assign_s(l, MSS_CONTAINER, qms->container, TRUE, FALSE);
134 l_chk_assign_s(l, MSS_URL, qms->url, TRUE, TRUE);
135 l_chk_assign_s(l, MSS_ID, qms->id, TRUE, FALSE);
136 lua_pop(l, 1);
138 _has_stream_url(l, qms, script_path, i);
139 return (qms);
142 static void _chk_stream_ids(lua_State *l, _quvi_media_t qm,
143 const gchar *script_path)
145 _quvi_media_stream_t qms;
146 GSList *curr;
147 gint i;
149 if (g_slist_length(qm->streams) < 2)
150 return;
152 curr = qm->streams;
153 i = 1;
155 while (curr != NULL)
157 qms = (_quvi_media_stream_t) curr->data;
158 if (qms->id->len ==0)
160 g_warning("%s: %s: `qargs.%s[%d].%s' should not be empty; "
161 "each stream should have an ID when there are >1 "
162 "streams",
163 script_path, script_func, MS_STREAMS, i, MSS_ID);
165 curr = g_slist_next(curr);
166 ++i;
170 static void _foreach_stream(lua_State *l, _quvi_media_t qm,
171 const gchar *script_path)
173 gint i = 0;
175 lua_pushnil(l);
176 while (lua_next(l, LI_KEY))
178 if (lua_istable(l, LI_VALUE))
180 _quvi_media_stream_t qms = _new_stream(l, qm, script_path, ++i);
181 qm->streams = g_slist_prepend(qm->streams, qms);
183 lua_pop(l, 1);
185 qm->streams = g_slist_reverse(qm->streams);
186 _chk_stream_ids(l, qm, script_path);
189 /* Check for 'qargs.streams'. This is the only mandatory data. */
190 static void _chk_streams(lua_State *l, _quvi_media_t qm,
191 const gchar *script_path)
193 lua_pushstring(l, MS_STREAMS);
194 lua_gettable(l, LI_KEY);
196 if (lua_istable(l, LI_VALUE))
197 _foreach_stream(l, qm, script_path);
198 else
200 static const gchar *_E =
201 "%s: %s: must return a dictionary containing the `qargs.%s'";
203 luaL_error(l, _E, script_path, script_func, MS_STREAMS);
205 lua_pop(l, 1);
207 if (g_slist_length(qm->streams) ==0)
209 luaL_error(l, "%s: %s: must return at least one media stream",
210 script_path, script_func);
214 /* Check for optional media properties, e.g. title. */
215 static void _chk_optional(lua_State *l, _quvi_media_t qm)
217 lua_pushnil(l);
218 while (lua_next(l, LI_KEY))
220 l_chk_assign_n(l, MS_START_TIME_MS, &qm->start_time_ms);
221 l_chk_assign_n(l, MS_DURATION_MS, &qm->duration_ms);
222 l_chk_assign_s(l, MS_THUMB_URL, qm->url.thumbnail, TRUE, TRUE);
223 l_chk_assign_s(l, MS_TITLE, qm->title, TRUE, FALSE);
224 l_chk_assign_s(l, MS_ID, qm->id, TRUE, FALSE);
225 lua_pop(l, 1);
229 /* Check for the goto URL instruction. */
230 static gboolean _chk_goto_instr(lua_State *l, _quvi_media_t qm)
232 lua_pushnil(l);
233 while (lua_next(l, LI_KEY))
235 l_chk_assign_s(l, MS_GOTO_URL, qm->url.redirect_to, TRUE, TRUE);
236 lua_pop(l, 1);
238 return ((qm->url.redirect_to->len >0) ? TRUE:FALSE);
241 extern QuviError l_exec_util_convert_entities(_quvi_media_t);
242 extern gint c_reset(_quvi_t);
244 QuviError l_exec_media_script_parse(gpointer p, GSList *sl)
246 _quvi_script_t qs;
247 _quvi_media_t qm;
248 lua_State *l;
249 QuviError rc;
251 qm = (_quvi_media_t) p;
252 rc = QUVI_OK;
254 l = qm->handle.quvi->handle.lua;
255 c_reset(qm->handle.quvi);
257 qs = (_quvi_script_t) sl->data;
258 lua_getglobal(l, script_func);
260 if (!lua_isfunction(l, -1))
262 luaL_error(l, "%s: the function `%s' was not found",
263 qs->fpath->str, script_func);
266 lua_newtable(l);
267 l_set_reg_userdata(l, USERDATA_QUVI_T, (gpointer) qm->handle.quvi);
268 l_setfield_s(l, MS_INPUT_URL, qm->url.input->str, -1);
270 if (lua_pcall(l, 1, 1, 0))
272 g_string_assign(qm->handle.quvi->status.errmsg, lua_tostring(l, -1));
273 return (QUVI_ERROR_SCRIPT);
276 if (!lua_istable(l, -1))
278 static const gchar *_E =
279 "%s: %s: must return a dictionary, this is typically the `qargs'";
281 luaL_error(l, _E, qs->fpath->str, script_func);
284 if (_chk_goto_instr(l, qm) == FALSE)
285 _chk_streams(l, qm, qs->fpath->str);
287 _chk_optional(l, qm);
289 if (qm->title->len >0)
290 rc = l_exec_util_convert_entities(qm);
292 lua_pop(l, 1);
293 return (rc);
296 /* vim: set ts=2 sw=2 tw=72 expandtab: */