buzzhumor.lua: Cleanup
[quvi.git] / src / quvi.c
blob24c5ee8b721e1c71ff7c7f9f96115706e17f1f0a
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 /* quvi.c - query video tool. */
22 #include "config.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <assert.h>
30 #include <curl/curl.h>
32 #include "platform.h"
34 #include "quvi/quvi.h"
35 #include "cmdline.h"
37 /* strepl.c */
38 extern char *strepl(const char *s, const char *what, const char *with);
40 static int verbose_flag = 1;
42 /* prints to std(e)rr. */
43 static void spew_e(const char *fmt, ...)
45 va_list ap;
46 va_start(ap, fmt);
47 vfprintf(stderr, fmt, ap);
48 va_end(ap);
51 /* respects (q)uiet, prints to std(e)rr. */
52 static void spew_qe(const char *fmt, ...)
54 va_list ap;
55 if (!verbose_flag)
56 return;
57 va_start(ap, fmt);
58 vfprintf(stderr, fmt, ap);
59 va_end(ap);
62 /* glorified printf. */
63 static void spew(const char *fmt, ...)
65 va_list ap;
66 va_start(ap, fmt);
67 vfprintf(stdout, fmt, ap);
68 va_end(ap);
71 typedef struct gengetopt_args_info opts_s;
73 static void handle_shortened_status(quvi_word type)
75 if (type == QUVISTATUSTYPE_DONE)
76 spew_qe("done.\n");
77 else
78 spew_qe(":: Check for shortened URL ...");
81 static void handle_fetch_status(quvi_word type, void *p)
83 switch (type) {
84 default:
85 spew_qe(":: Fetch %s ...", (char *)p);
86 break;
87 case QUVISTATUSTYPE_CONFIG:
88 spew_qe(":: Fetch config ...");
89 break;
90 case QUVISTATUSTYPE_PLAYLIST:
91 spew_qe(":: Fetch playlist ...");
92 break;
93 case QUVISTATUSTYPE_DONE:
94 spew_qe("done.\n");
95 break;
99 static void handle_verify_status(quvi_word type)
101 switch (type) {
102 default:
103 spew_qe(":: Verify video link ...");
104 break;
105 case QUVISTATUSTYPE_DONE:
106 spew_qe("done.\n");
107 break;
111 static int status_callback(long param, void *data)
113 quvi_word status, type;
115 status = quvi_loword(param);
116 type = quvi_hiword(param);
118 switch (status) {
120 case QUVISTATUS_SHORTENED:
121 handle_shortened_status(type);
122 break;
124 case QUVISTATUS_FETCH:
125 handle_fetch_status(type, data);
126 break;
128 case QUVISTATUS_VERIFY:
129 handle_verify_status(type);
130 break;
133 fflush(stderr);
135 return (0);
138 static size_t write_callback(void *p, size_t size, size_t nmemb, void *data)
140 size_t r = quvi_write_callback_default(p, size, nmemb, data);
141 /* Could do something useful here. */
142 #ifdef _0
143 puts(__func__);
144 #endif
145 return r;
148 extern char LICENSE[];
150 static void license(opts_s opts)
152 printf("%s\n", LICENSE);
153 cmdline_parser_free(&opts);
154 exit(0);
157 static void version(opts_s opts)
159 printf("quvi version %s\n", quvi_version(QUVI_VERSION_LONG));
160 cmdline_parser_free(&opts);
161 exit(0);
164 static void dump_host(char *domain, char *formats)
166 printf("%s\t%s\n", domain, formats);
167 quvi_free(domain);
168 quvi_free(formats);
171 /* Wraps quvi_supported. */
172 static void supported(quvi_t quvi, opts_s opts)
174 QUVIcode rc;
175 int i;
177 for (i = 0; i < opts.inputs_num; ++i) {
178 rc = quvi_supported(quvi, (char *)opts.inputs[i]);
179 if (rc == QUVI_OK)
180 spew_qe("%s: OK\n", (char *)opts.inputs[i]);
181 else
182 spew_qe("error: %s\n", quvi_strerror(quvi, rc));
185 quvi_close(&quvi);
186 cmdline_parser_free(&opts);
188 exit(rc);
191 /* dumps all supported hosts to stdout. */
192 static void support(quvi_t quvi, opts_s opts)
194 int done = 0;
196 if (opts.inputs_num > 0)
197 supported(quvi, opts);
199 while (!done) {
200 char *domain, *formats;
201 QUVIcode rc;
203 rc = quvi_next_supported_website(quvi, &domain, &formats);
205 switch (rc) {
206 case QUVI_OK:
207 dump_host(domain, formats);
208 break;
209 case QUVI_LAST:
210 done = 1;
211 break;
212 default:
213 spew_e("%s\n", quvi_strerror(quvi, rc));
214 break;
218 quvi_close(&quvi);
219 cmdline_parser_free(&opts);
221 exit(0);
224 static void invoke_exec(quvi_video_t video, const char *video_url, opts_s opts)
226 char *quoted_url, *arg;
227 int rc;
229 asprintf(&quoted_url, "\"%s\"", video_url);
231 arg = strdup(opts.exec_arg);
232 arg = strepl(arg, "%u", quoted_url);
234 free(quoted_url);
235 quoted_url = NULL;
237 rc = system(arg);
239 switch (rc) {
240 case 0:
241 break;
242 case -1:
243 spew_e("error: failed to execute `%s'\n", arg);
244 break;
245 default:
246 spew_e("error: child exited with: %d\n", rc >> 8);
247 break;
250 free(arg);
251 arg = NULL;
254 static void
255 dump_video_link_xml(CURL * curl,
256 int i,
257 char *video_url,
258 double file_length, char *file_ct, char *file_suffix)
260 char *url;
262 url = curl_easy_escape(curl, video_url, 0);
264 spew(" <link id=\"%d\">\n"
265 " <length_bytes>%.0f</length_bytes>\n"
266 " <content_type>%s</content_type>\n"
267 " <file_suffix>%s</file_suffix>\n"
268 " <url>%s</url>\n"
269 " </link>\n",
270 i, file_length, file_ct, file_suffix, url ? url : video_url);
272 if (url) {
273 curl_free(url);
274 url = NULL;
278 static void
279 dump_video_link_old(int i,
280 char *video_url,
281 double file_length, char *file_suffix, char *file_ct)
283 spew("link %02d : %s\n"
284 ":: length: %.0f\n:: suffix: %s\n:: content-type: %s\n\n",
285 i, video_url, file_length, file_suffix, file_ct);
288 static void
289 dump_video_link_json(int i,
290 char *video_url,
291 double file_length, char *file_suffix, char *file_ct)
293 spew(" {\n"
294 " \"id\": \"%d\",\n"
295 " \"length_bytes\": \"%.0f\",\n"
296 " \"content_type\": \"%s\",\n"
297 " \"file_suffix\": \"%s\",\n"
298 " \"url\": \"%s\"\n"
299 " }%s\n",
300 i, file_length, file_ct, file_suffix, video_url, i > 1 ? "," : "");
303 static void dump_video_links(quvi_video_t video, opts_s opts, CURL * curl)
305 int i = 0;
306 do {
307 char *video_url, *file_suffix, *file_ct;
308 double file_length;
310 quvi_getprop(video, QUVIPROP_VIDEOURL, &video_url);
311 quvi_getprop(video, QUVIPROP_VIDEOFILECONTENTTYPE, &file_ct);
312 quvi_getprop(video, QUVIPROP_VIDEOFILESUFFIX, &file_suffix);
313 quvi_getprop(video, QUVIPROP_VIDEOFILELENGTH, &file_length);
315 ++i;
317 if (opts.xml_given)
318 dump_video_link_xml(curl, i, video_url, file_length,
319 file_ct, file_suffix);
321 else if (opts.old_given)
322 dump_video_link_old(i, video_url, file_length, file_suffix, file_ct);
323 else
324 dump_video_link_json(i, video_url, file_length, file_suffix, file_ct);
326 while (quvi_next_videolink(video) == QUVI_OK);
329 static void
330 dump_video_xml(CURL * curl,
331 char *video_id, char *host, char *format, char *page_title,
332 char *page_link)
334 char *url;
336 url = curl_easy_escape(curl, page_link, 0);
338 spew("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
339 "<video id=\"%s\" host=\"%s\">\n"
340 " <format_requested>%s</format_requested>\n"
341 " <page_title>%s</page_title>\n"
342 " <page_url>%s</page_url>\n",
343 video_id, host, format, page_title, url ? url : page_link);
345 if (url) {
346 curl_free(url);
347 url = NULL;
352 static void
353 dump_video_old(char *video_id, char *host, char *format, char *page_title,
354 char *page_link)
356 spew(" > Dump video:\n"
357 "host : %s\n"
358 "url : %s\n"
359 "title : %s\n"
360 "id : %s\n"
361 "format : %s (requested)\n",
362 host, page_link, page_title, video_id, format);
365 static void
366 dump_video_json(char *video_id, char *host, char *format, char *page_title,
367 char *page_link)
369 char *t;
371 t = strdup(page_title);
372 t = strepl(t, "\"", "\\\"");
374 spew("{\n"
375 " \"host\": \"%s\",\n"
376 " \"page_title\": \"%s\",\n"
377 " \"page_url\": \"%s\",\n"
378 " \"id\": \"%s\",\n"
379 " \"format_requested\": \"%s\",\n"
380 " \"link\": [\n", host, t, page_link, video_id, format);
382 free(t);
383 t = NULL;
386 static void dump_video(quvi_video_t video, opts_s opts, CURL * curl)
388 char *page_link, *page_title, *video_id, *format, *host;
390 quvi_getprop(video, QUVIPROP_HOSTID, &host);
391 quvi_getprop(video, QUVIPROP_PAGEURL, &page_link);
392 quvi_getprop(video, QUVIPROP_PAGETITLE, &page_title);
393 quvi_getprop(video, QUVIPROP_VIDEOID, &video_id);
394 quvi_getprop(video, QUVIPROP_VIDEOFORMAT, &format);
396 if (opts.xml_given)
397 dump_video_xml(curl, video_id, host, format, page_title, page_link);
398 else if (opts.old_given)
399 dump_video_old(video_id, host, format, page_title, page_link);
400 else
401 dump_video_json(video_id, host, format, page_title, page_link);
403 dump_video_links(video, opts, curl);
405 if (opts.xml_given)
406 spew("</video>\n");
407 else if (opts.old_given) ;
408 else
409 spew(" ]\n}\n");
412 static void
413 expect_error(const char *what, const char *expected, const char *got)
415 fprintf(stderr, "error: %s:\n expected: \"%s\"\n got: \"%s\"\n\n",
416 what, expected, got);
419 static void
420 expect_error_d(const char *what, const double expected, const double got)
422 fprintf(stderr,
423 "error: %s:\n expected: \"%.0f\"\n got: \"%.0f\"\n\n", what,
424 expected, got);
427 static int check_values(quvi_video_t video, opts_s opts)
429 char *title, *expect, *id, *suffix;
430 double length;
431 int rc;
433 rc = 0;
435 if (opts.page_title_given) {
437 expect = opts.page_title_arg;
439 quvi_getprop(video, QUVIPROP_PAGETITLE, &title);
441 rc = strcmp(expect, title) != 0;
442 if (rc)
443 expect_error("page title", expect, title);
446 if (opts.video_id_given && !rc) {
448 expect = opts.video_id_arg;
450 quvi_getprop(video, QUVIPROP_VIDEOID, &id);
452 rc = strcmp(expect, id) != 0;
453 if (rc)
454 expect_error("video id", expect, id);
457 if (opts.file_suffix_given && !rc) {
459 expect = opts.file_suffix_arg;
461 quvi_getprop(video, QUVIPROP_VIDEOFILESUFFIX, &suffix);
463 rc = strcmp(expect, suffix) != 0;
464 if (rc)
465 expect_error("file suffix", expect, suffix);
468 if (opts.file_length_given && !rc) {
470 double expect_d = opts.file_length_arg;
472 quvi_getprop(video, QUVIPROP_VIDEOFILELENGTH, &length);
474 rc = expect_d != length;
475 if (rc)
476 expect_error_d("file length", expect_d, length);
479 return (rc);
482 static const char *tests[] = {
483 "http://www.cbsnews.com/video/watch/?id=7118769n",
484 "http://videos.sapo.pt/hd4ZBIHG80zFviLc5YEa",
485 "http://www.dailymotion.com/video/xdpig1_city-of-scars_shortfilms",
486 "http://www.spiegel.de/video/video-1012582.html",
487 "http://vimeo.com/1485507",
488 "http://en.sevenload.com/videos/IUL3gda-Funny-Football-Clips",
489 "http://www.liveleak.com/view?i=704_1228511265",
490 "http://video.google.com/videoplay?docid=-6970952080219955808",
491 "http://video.golem.de/internet/2174/firefox-3.5-test.html",
492 "http://www.funnyhub.com/videos/pages/crazy-hole-in-one.html",
493 "http://www.clipfish.de/video/3100131/matratzendomino/",
494 "http://www.youtube.com/watch?v=9dgSa4wmMzk",
495 "http://break.com/index/beach-tackle-whip-lash.html",
496 "http://www.gaskrank.tv/tv/rennstrecken/1-runde-oschersleben-14082008--6985.htm",
497 "http://www.buzzhumor.com/videos/32561/Girl_Feels_Shotgun_Power",
498 "http://www.funnyordie.com/videos/776d200b1c/etiquette-ninjas-episode-5-dicks-on-elevators",
499 "http://www.charlierose.com/view/interview/11125",
500 "http://www.academicearth.org/lectures/building-dynamic-websites-http",
501 /* uses "redirect". */
502 "http://www.academicearth.org/lectures/intro-roman-architecture",
503 "http://www.collegehumor.com/video:1942317",
504 "http://www.theonion.com/video/time-announces-new-version-of-magazine-aimed-at-ad,17950/",
505 "http://www.bloomberg.com/video/63722844/",
506 #ifdef ENABLE_NSFW
507 "http://www.tube8.com/fetish/japanese-melon-gal-censored/186133/",
508 "http://www.xvideos.com/video243887/devi_emmerson_body_painting",
509 "http://www.youjizz.com/videos/glamour-girls---melissa-125602.html",
510 #endif
511 #ifdef ENABLE_BROKEN
512 #endif
514 NULL
517 static void dump_error(quvi_t quvi, QUVIcode rc, opts_s opts)
519 if (rc != QUVI_NOSUPPORT)
520 fprintf(stderr, "\n");
522 fprintf(stderr, "error: %s\n", quvi_strerror(quvi, rc));
524 if (!opts.test_all_given) {
525 quvi_close(&quvi);
526 cmdline_parser_free(&opts);
527 exit(rc);
531 static void match_test(quvi_t quvi, opts_s opts, CURL * curl)
533 int i, no_match;
534 QUVIcode rc;
536 /* None of our tests (should) use shortened URLs. */
537 quvi_setopt(quvi, QUVIOPT_NOSHORTENED, 1L);
539 for (rc = QUVI_OK, no_match = 1, i = 0; tests[i]; ++i) {
540 if (strstr(tests[i], opts.test_arg) != NULL) {
541 quvi_video_t v;
543 no_match = 0;
544 rc = quvi_parse(quvi, (char *)tests[i], &v);
545 if (rc != QUVI_OK)
546 dump_error(quvi, rc, opts);
548 dump_video(v, opts, curl);
549 rc = check_values(v, opts);
551 if (opts.exec_given && rc == QUVI_OK) {
552 char *video_url = NULL;
553 do {
554 quvi_getprop(v, QUVIPROP_VIDEOURL, &video_url);
555 invoke_exec(v, video_url, opts);
556 } while (quvi_next_videolink(v) == QUVI_OK);
559 quvi_parse_close(&v);
563 if (no_match) {
564 fprintf(stderr, "error: nothing matched `%s'\n", opts.test_arg);
565 rc = QUVI_NOSUPPORT;
568 cmdline_parser_free(&opts);
569 quvi_close(&quvi);
571 exit(rc);
574 static void test_all(quvi_t quvi, opts_s opts, CURL * curl)
576 quvi_video_t video;
577 QUVIcode rc;
578 int i, m;
580 /* None of our tests (should) use shortened URLs. */
581 quvi_setopt(quvi, QUVIOPT_NOSHORTENED, 1L);
583 spew_qe(":: Run built-in video link tests.\n");
584 for (m = 0; tests[m]; ++m) ;
586 for (i = 0; i < m; ++i) {
587 spew_qe(" > Test #%02d/%02d:\n", i + 1, m);
589 rc = quvi_parse(quvi, (char *)tests[i], &video);
590 if (rc != QUVI_OK)
591 dump_error(quvi, rc, opts);
592 else {
593 if (opts.dump_given)
594 dump_video(video, opts, curl);
596 quvi_parse_close(&video);
598 spew_qe(":: Tests done.\n");
601 static quvi_t init_quvi(opts_s opts, CURL ** curl)
603 QUVIcode rc;
604 quvi_t quvi;
606 if ((rc = quvi_init(&quvi)) != QUVI_OK)
607 dump_error(quvi, rc, opts);
608 assert(quvi != 0);
610 /* Set quvi options. */
612 if (opts.format_given)
613 quvi_setopt(quvi, QUVIOPT_FORMAT, opts.format_arg);
615 quvi_setopt(quvi, QUVIOPT_NOSHORTENED, opts.no_shortened_given);
616 quvi_setopt(quvi, QUVIOPT_NOVERIFY, opts.no_verify_given);
618 quvi_setopt(quvi, QUVIOPT_STATUSFUNCTION, status_callback);
619 quvi_setopt(quvi, QUVIOPT_WRITEFUNCTION, write_callback);
621 /* Use the quvi created cURL handle. */
623 quvi_getinfo(quvi, QUVIINFO_CURL, curl);
624 assert(*curl != 0);
626 if (opts.agent_given)
627 curl_easy_setopt(*curl, CURLOPT_USERAGENT, opts.agent_arg);
629 if (opts.proxy_given)
630 curl_easy_setopt(*curl, CURLOPT_PROXY, opts.proxy_arg);
632 if (opts.no_proxy_given)
633 curl_easy_setopt(*curl, CURLOPT_PROXY, "");
635 curl_easy_setopt(*curl, CURLOPT_VERBOSE, opts.verbose_libcurl_given);
637 curl_easy_setopt(*curl, CURLOPT_CONNECTTIMEOUT, opts.connect_timeout_arg);
639 return (quvi);
642 int main(int argc, char *argv[])
644 const char *url, *home, *no_config, *fname;
645 quvi_video_t video;
646 int no_config_flag;
647 opts_s opts;
648 QUVIcode rc;
649 quvi_t quvi;
650 CURL *curl;
651 int i;
653 curl = NULL;
654 url = NULL;
656 no_config = getenv("QUVI_NO_CONFIG");
658 home = getenv("QUVI_HOME");
659 if (!home)
660 home = getenv("HOME");
662 no_config_flag = 1;
664 #ifndef HOST_W32
665 fname = "/.quvirc";
666 #else
667 fname = "\\quvirc";
668 #endif
670 /* Init cmdline parser. */
672 if (home && !no_config) {
673 char *path;
674 FILE *f;
676 asprintf(&path, "%s%s", home, fname);
677 f = fopen(path, "r");
679 if (f != NULL) {
680 struct cmdline_parser_params *pp;
682 fclose(f);
683 f = NULL;
685 pp = cmdline_parser_params_create();
686 pp->check_required = 0;
688 if (cmdline_parser_config_file(path, &opts, pp) == 0) {
689 pp->initialize = 0;
690 pp->override = 1;
691 pp->check_required = 1;
693 if (cmdline_parser_ext(argc, argv, &opts, pp) == 0)
694 no_config_flag = 0;
696 free(pp);
697 pp = NULL;
700 free(path);
701 path = NULL;
704 if (no_config_flag) {
705 if (cmdline_parser(argc, argv, &opts) != 0)
706 return (QUVI_INVARG);
709 if (opts.version_given)
710 version(opts);
712 if (opts.license_given)
713 license(opts);
715 verbose_flag = !opts.quiet_given;
717 quvi = init_quvi(opts, &curl);
719 if (opts.support_given)
720 support(quvi, opts);
722 /* User input */
724 if (opts.test_all_given)
725 test_all(quvi, opts, curl);
727 else if (opts.test_given)
728 match_test(quvi, opts, curl);
730 else {
732 if (opts.inputs_num == 0)
733 fprintf(stderr, "error: no input links\n");
735 for (i = 0; i < opts.inputs_num; ++i) {
737 rc = quvi_parse(quvi, (char *)opts.inputs[i], &video);
739 if (rc != QUVI_OK)
740 dump_error(quvi, rc, opts);
742 assert(video != 0);
743 dump_video(video, opts, curl);
745 if (opts.exec_given) {
746 char *video_url = NULL;
747 do {
748 quvi_getprop(video, QUVIPROP_VIDEOURL, &video_url);
749 invoke_exec(video, video_url, opts);
750 } while (quvi_next_videolink(video) == QUVI_OK);
753 quvi_parse_close(&video);
754 assert(video == 0);
758 /* Cleanup. */
760 quvi_close(&quvi);
761 assert(quvi == 0);
763 cmdline_parser_free(&opts);
765 return (QUVI_OK);