Set release details for 0.2.13
[quvi.git] / lib / quvi.c
blobd80f2a51becce2d41cea13264717f097442ec375
1 /* quvi
2 * Copyright (C) 2009,2010 Toni Gundogdu <legatvs@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
20 #include "config.h"
22 #include <assert.h>
23 #include <string.h>
25 #include <curl/curl.h>
27 #include "quvi/quvi.h"
28 #include "internal.h"
29 #include "lua_wrap.h"
30 #include "util.h"
31 #include "curl_wrap.h"
33 #define is_invarg(p) \
34 do { if (p == NULL) return (QUVI_INVARG); } while (0)
36 #define is_badhandle(h) \
37 do { if (h == NULL) return (QUVI_BADHANDLE); } while (0)
39 /* quvi_init */
41 QUVIcode quvi_init(quvi_t * dst)
43 _quvi_t quvi;
45 is_invarg(dst);
46 *dst = 0;
48 quvi = calloc(1, sizeof(*quvi));
49 if (!quvi)
50 return (QUVI_MEM);
52 *dst = (quvi_t) quvi;
54 curl_global_init(CURL_GLOBAL_ALL);
56 quvi->curl = curl_easy_init();
57 if (!quvi->curl)
59 _free(quvi);
60 return (QUVI_CURLINIT);
63 /* Set library defaults. */
64 quvi_setopt(quvi, QUVIOPT_FORMAT, "default");
65 quvi_setopt(quvi, QUVIOPT_CATEGORY, QUVIPROTO_HTTP);
67 curl_easy_setopt(quvi->curl, CURLOPT_USERAGENT, "Mozilla/5.0");
68 curl_easy_setopt(quvi->curl, CURLOPT_FOLLOWLOCATION, 1L);
69 curl_easy_setopt(quvi->curl, CURLOPT_NOBODY, 0L);
71 return (init_lua(quvi));
74 /* quvi_close */
76 void quvi_close(quvi_t * handle)
78 _quvi_t *quvi;
80 quvi = (_quvi_t *) handle;
82 if (quvi && *quvi)
85 free_lua(quvi);
86 assert((*quvi)->util_scripts == NULL);
87 assert((*quvi)->website_scripts == NULL);
89 _free((*quvi)->format);
90 assert((*quvi)->format == NULL);
92 _free((*quvi)->errmsg);
93 assert((*quvi)->errmsg == NULL);
95 curl_easy_cleanup((*quvi)->curl);
96 (*quvi)->curl = NULL;
98 _free(*quvi);
99 assert((*quvi) == NULL);
101 curl_global_cleanup();
105 /* quvi_supported */
107 QUVIcode quvi_supported(quvi_t quvi, char *url)
109 _quvi_video_t video;
110 QUVIcode rc;
112 is_invarg(url);
113 is_badhandle(quvi);
115 video = calloc(1, sizeof(*video));
116 if (!video)
117 return (QUVI_MEM);
119 video->quvi = quvi;
121 freprintf(&video->page_link, "%s", url);
123 rc = find_host_script(video);
125 quvi_parse_close((quvi_video_t) & video);
127 return (rc);
130 /* quvi_parse */
132 QUVIcode quvi_parse(quvi_t quvi, char *url, quvi_video_t * dst)
134 _quvi_video_t video;
135 QUVIcode rc;
137 is_invarg(dst);
138 *dst = 0;
139 is_invarg(url);
140 is_badhandle(quvi);
142 video = calloc(1, sizeof(*video));
143 if (!video)
144 return (QUVI_MEM);
146 *dst = video;
147 video->quvi = quvi;
149 freprintf(&video->page_link, "%s", url);
151 if (!video->quvi->no_shortened)
153 rc = is_shortened_url(video);
154 if (rc != QUVI_OK)
155 return (rc);
158 while (1)
160 rc = find_host_script_and_parse(video);
161 if (rc != QUVI_OK)
162 return (rc);
163 else
165 if (strlen(video->redirect)) /* Found a redirect. */
167 freprintf(&video->page_link, "%s", video->redirect);
168 continue;
170 else
171 break;
175 #ifdef HAVE_ICONV /* Convert character set encoding to utf8. */
176 if (video->charset)
177 to_utf8(video);
178 #endif
179 assert(video->title != NULL); /* must be set in the lua script */
181 video->title = from_html_entities(video->title);
183 if (!video->quvi->no_verify)
185 llst_node_t curr = video->link;
186 while (curr)
188 rc = query_file_length(video->quvi, curr);
189 if (rc != QUVI_OK)
190 break;
191 curr = curr->next;
195 /* set current video link to first link in the list. */
196 video->curr = video->link;
198 return (rc);
201 /* quvi_parse_close */
203 void quvi_parse_close(quvi_video_t * handle)
205 _quvi_video_t *video;
207 video = (_quvi_video_t *) handle;
209 if (video && *video)
211 llst_node_t curr = (*video)->link;
213 while (curr)
215 _quvi_video_link_t l = (_quvi_video_link_t) curr->data;
216 _free(l->url);
217 _free(l->suffix);
218 _free(l->content_type);
219 curr = curr->next;
221 llst_free(&(*video)->link);
223 _free((*video)->id);
224 _free((*video)->title);
225 _free((*video)->charset);
226 _free((*video)->page_link);
227 _free((*video)->host_id);
228 _free((*video)->redirect);
229 _free((*video)->starttime);
231 _free(*video);
235 static QUVIcode _setopt(_quvi_t quvi, QUVIoption opt, va_list arg)
238 #define _sets(opt) \
239 do { \
240 _free(opt); \
241 asprintf(&opt, "%s", va_arg(arg,char *)); \
242 } while(0); break
244 #define _setn(opt) \
245 do { opt = va_arg(arg,long); } while(0); break
247 switch (opt)
249 case QUVIOPT_FORMAT:
250 _sets(quvi->format);
251 case QUVIOPT_NOVERIFY:
252 _setn(quvi->no_verify);
253 case QUVIOPT_STATUSFUNCTION:
254 quvi->status_func = va_arg(arg, quvi_callback_status);
255 break;
256 case QUVIOPT_WRITEFUNCTION:
257 quvi->write_func = va_arg(arg, quvi_callback_write);
258 break;
259 case QUVIOPT_NOSHORTENED:
260 _setn(quvi->no_shortened);
261 case QUVIOPT_CATEGORY:
262 _setn(quvi->category);
263 default:
264 return (QUVI_INVARG);
266 #undef _sets
267 #undef _setn
268 return (QUVI_OK);
271 /* quvi_setopt */
273 QUVIcode quvi_setopt(quvi_t quvi, QUVIoption opt, ...)
275 va_list arg;
276 QUVIcode rc;
278 is_badhandle(quvi);
280 va_start(arg, opt);
281 rc = _setopt(quvi, opt, arg);
282 va_end(arg);
284 return (rc);
287 static const char empty[] = "";
289 static QUVIcode _getprop(_quvi_video_t video, QUVIproperty prop, ...)
291 _quvi_video_link_t qvl;
292 QUVIcode rc;
293 va_list arg;
294 double *dp;
295 char **sp;
296 long *lp;
297 int type;
299 qvl = (_quvi_video_link_t) video->curr->data;
300 assert(qvl != 0);
302 rc = QUVI_OK;
303 dp = 0;
304 sp = 0;
305 lp = 0;
307 va_start(arg, prop);
308 type = QUVIPROPERTY_TYPEMASK & (int)prop;
310 #define _initv(var,type) \
311 do { \
312 if ( !(var = va_arg(arg,type)) ) \
313 rc = QUVI_INVARG; \
314 } while (0); break
316 switch (type)
318 case QUVIPROPERTY_DOUBLE:
319 _initv(dp, double *);
320 case QUVIPROPERTY_STRING:
321 _initv(sp, char **);
322 case QUVIPROPERTY_LONG:
323 _initv(lp, long *);
324 default:
325 rc = QUVI_INVARG;
327 va_end(arg);
329 if (rc != QUVI_OK)
330 return rc;
332 #define _sets(with) \
333 do { *sp = with ? with:(char*)empty; } while(0); break
335 #define _setn(var,with) \
336 do { *var = with; } while(0); break
338 switch (prop)
340 case QUVIPROP_HOSTID:
341 _sets(video->host_id);
342 case QUVIPROP_PAGEURL:
343 _sets(video->page_link);
344 case QUVIPROP_PAGETITLE:
345 _sets(video->title);
346 case QUVIPROP_VIDEOID:
347 _sets(video->id);
348 case QUVIPROP_VIDEOURL:
349 _sets(qvl->url);
350 case QUVIPROP_VIDEOFILELENGTH:
351 _setn(dp, qvl->length);
352 case QUVIPROP_VIDEOFILECONTENTTYPE:
353 _sets(qvl->content_type);
354 case QUVIPROP_VIDEOFILESUFFIX:
355 _sets(qvl->suffix);
356 case QUVIPROP_HTTPCODE:
357 _setn(lp, video->quvi->httpcode);
358 case QUVIPROP_VIDEOFORMAT:
359 _sets(video->quvi->format);
360 case QUVIPROP_STARTTIME:
361 _sets(video->starttime);
362 default:
363 rc = QUVI_INVARG;
366 return (rc);
369 static QUVIcode _getinfo(_quvi_t quvi, QUVIinfo info, ...)
371 QUVIcode rc;
372 va_list arg;
373 double *dp;
374 char **sp;
375 void **vp;
376 long *lp;
377 int type;
379 rc = QUVI_OK;
380 dp = 0;
381 sp = 0;
382 vp = 0;
383 lp = 0;
385 va_start(arg, info);
386 type = QUVIINFO_TYPEMASK & (int)info;
388 switch (type)
390 case QUVIINFO_DOUBLE:
391 _initv(dp, double *);
392 case QUVIINFO_STRING:
393 _initv(sp, char **);
394 case QUVIINFO_LONG:
395 _initv(lp, long *);
396 case QUVIINFO_VOID:
397 _initv(vp, void **);
398 default:
399 rc = QUVI_INVARG;
401 va_end(arg);
403 if (rc != QUVI_OK)
404 return rc;
406 #define _setv(with) \
407 do { *vp = with ? with:NULL; } while(0); break
409 switch (info)
411 case QUVIINFO_CURL:
412 _setv(quvi->curl);
413 case QUVIINFO_CURLCODE:
414 _setn(lp, quvi->curlcode);
415 case QUVIINFO_HTTPCODE:
416 _setn(lp, quvi->httpcode);
417 default:
418 rc = QUVI_INVARG;
421 return (rc);
424 #undef _setv
425 #undef _setn
426 #undef _sets
427 #undef _initv
429 /* quvi_getinfo */
431 QUVIcode quvi_getinfo(quvi_t quvi, QUVIinfo info, ...)
433 va_list arg;
434 void *p;
436 is_badhandle(quvi);
438 va_start(arg, info);
439 p = va_arg(arg, void *);
440 va_end(arg);
442 return (_getinfo(quvi, info, p));
445 /* quvi_getprop */
447 QUVIcode quvi_getprop(quvi_video_t video, QUVIproperty prop, ...)
449 va_list arg;
450 void *p;
452 is_badhandle(video);
454 va_start(arg, prop);
455 p = va_arg(arg, void *);
456 va_end(arg);
458 return (_getprop(video, prop, p));
461 /* quvi_next_videolink */
463 QUVIcode quvi_next_videolink(quvi_video_t handle)
465 _quvi_video_t video;
467 is_badhandle(handle);
469 video = (_quvi_video_t) handle;
471 /* start from the first */
472 if (!video->curr)
474 video->curr = video->link;
475 return (QUVI_OK);
478 /* move to the next */
479 video->curr = video->curr->next;
480 if (!video->curr)
482 video->curr = video->link; /* reset */
483 return (QUVI_LAST);
486 return (QUVI_OK);
489 static llst_node_t curr_host = NULL;
491 /* quvi_next_supported_website */
493 QUVIcode
494 quvi_next_supported_website(quvi_t handle, char **domain,
495 char **formats)
497 struct lua_ident_s ident;
498 _quvi_t quvi;
499 QUVIcode rc;
501 is_badhandle(handle);
502 quvi = (_quvi_t) handle;
504 is_invarg(domain);
505 is_invarg(formats);
507 if (!quvi->website_scripts)
508 return (QUVI_NOLUAWEBSITE);
510 if (!curr_host)
511 curr_host = quvi->website_scripts;
512 else
514 curr_host = curr_host->next;
515 if (!curr_host)
516 return (QUVI_LAST);
519 ident.quvi = quvi;
520 ident.url = NULL;
521 ident.domain = NULL;
522 ident.formats = NULL;
524 rc = run_ident_func(&ident, curr_host);
526 if (rc == QUVI_NOSUPPORT)
528 /* The website scripts return QUVI_NOSUPPORT in all cases. This is
529 * because of the undefined URL that we pass to them above (ident.url
530 * = NULL). We are only interested in the `domain' and `formats'
531 * information anyway, so this is OK. */
532 if (ident.categories & quvi->category)
534 *domain = ident.domain;
535 *formats = ident.formats;
536 rc = QUVI_OK;
538 else
540 _free(ident.domain);
541 _free(ident.formats);
542 rc = quvi_next_supported_website(handle, domain, formats);
546 return (rc);
549 /* quvi_next_host, NOTE: deprecated. */
551 QUVIcode quvi_next_host(char **domain, char **formats)
553 *domain = *formats = NULL;
554 return (QUVI_LAST);
557 #undef is_badhandle
558 #undef is_invarg
560 /* quvi_strerror */
562 char *quvi_strerror(quvi_t handle, QUVIcode code)
564 static const char *errormsgs[] =
566 "no error",
567 "memory allocation failed",
568 "bad handle argument to function",
569 "invalid argument to function",
570 "curl initialization failed",
571 "end of list iteration",
572 "aborted by callback",
573 "lua initilization failed",
574 "lua website scripts not found",
575 "lua util scripts not found",
576 "invalid error code (internal _INTERNAL_QUVI_LAST)"
579 _quvi_t quvi = (_quvi_t) handle;
581 if (quvi)
583 if (code > _INTERNAL_QUVI_LAST)
584 return (quvi->errmsg);
586 else
588 if (code > _INTERNAL_QUVI_LAST)
589 code = _INTERNAL_QUVI_LAST;
592 return ((char *)errormsgs[code]);
595 /* quvi_version */
597 char *quvi_version(QUVIversion type)
599 static const char version[] = PACKAGE_VERSION;
600 static const char version_long[] =
601 #ifdef GIT_DESCRIBE
602 GIT_DESCRIBE
603 #else
604 PACKAGE_VERSION
605 #endif
606 #ifdef BUILD_DATE
607 " built on " BUILD_DATE
608 #endif
609 " for " CANONICAL_TARGET " ("
610 #ifdef HAVE_ICONV
612 #endif
613 #ifdef ENABLE_BROKEN
615 #endif
616 #ifdef ENABLE_NSFW
618 #endif
619 ")";
621 if (type == QUVI_VERSION_LONG)
622 return ((char *)version_long);
623 return ((char *)version);
626 /* quvi_free */
628 void quvi_free(void *ptr)
630 if (ptr != NULL)
631 free(ptr);
634 /* vim: set ts=2 sw=2 tw=72 expandtab: */