2 * Copyright (C) 2009,2010,2011 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
20 /* quvi.c - query media tool. */
30 #include <curl/curl.h>
34 #include "quvi/quvi.h"
40 do { if (p) { free(p); p=0; } } while (0)
43 extern char *strepl(const char *s
, const char *what
, const char *with
);
45 static int verbose_flag
= 1;
46 static quvi_t quvi
= NULL
;
47 static CURL
*curl
= NULL
;
49 typedef struct gengetopt_args_info
*opts_t
;
50 static opts_t opts
= NULL
;
52 static llst_node_t inputs
= NULL
;
54 /* prints to std(e)rr. */
55 static void spew_e(const char *fmt
, ...)
59 vfprintf(stderr
, fmt
, ap
);
63 /* respects (q)uiet, prints to std(e)rr. */
64 static void spew_qe(const char *fmt
, ...)
70 vfprintf(stderr
, fmt
, ap
);
74 /* glorified printf. */
75 static void spew(const char *fmt
, ...)
79 vfprintf(stdout
, fmt
, ap
);
83 static void handle_resolve_status(quvi_word type
)
85 if (type
== QUVISTATUSTYPE_DONE
)
88 spew_qe(":: Check for URL redirection ...");
91 static void handle_fetch_status(quvi_word type
, void *p
)
96 spew_qe(":: Fetch %s ...", (char *)p
);
98 case QUVISTATUSTYPE_CONFIG
:
99 spew_qe(":: Fetch config ...");
101 case QUVISTATUSTYPE_PLAYLIST
:
102 spew_qe(":: Fetch playlist ...");
104 case QUVISTATUSTYPE_DONE
:
110 static void handle_verify_status(quvi_word type
)
115 spew_qe(":: Verify media URL ...");
117 case QUVISTATUSTYPE_DONE
:
123 static int status_callback(long param
, void *data
)
125 quvi_word status
, type
;
127 status
= quvi_loword(param
);
128 type
= quvi_hiword(param
);
132 case QUVISTATUS_RESOLVE
:
133 handle_resolve_status(type
);
136 case QUVISTATUS_FETCH
:
137 handle_fetch_status(type
, data
);
140 case QUVISTATUS_VERIFY
:
141 handle_verify_status(type
);
150 static size_t write_callback(void *p
, size_t size
, size_t nmemb
,
153 size_t r
= quvi_write_callback_default(p
, size
, nmemb
, data
);
154 /* Could do something useful here. */
161 /* Divided into smaller blocks. Otherwise -pedantic objects. */
165 " * Copyright (C) 2009,2010,2011 Toni Gundogdu <legatvs@gmail.com>\n"
168 " * This library is free software; you can redistribute it and/or\n" \
169 " * modify it under the terms of the GNU Lesser General Public\n" \
170 " * License as published by the Free Software Foundation; either\n" \
171 " * version 2.1 of the License, or (at your option) any later version.\n"
174 " * This library is distributed in the hope that it will be useful,\n" \
175 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
176 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" \
177 " * Lesser General Public License for more details.\n"
180 " * You should have received a copy of the GNU Lesser General Public\n" \
181 " * License along with this library; if not, write to the Free Software\n" \
182 " * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" \
183 " * 02110-1301 USA\n" " */"
185 static void license()
187 printf("%s *\n%s *\n%s *\n%s\n",
188 LICENSE_1
, LICENSE_2
, LICENSE_3
, LICENSE_4
);
197 static void version()
199 printf("quvi version %s\n", quvi_version(QUVI_VERSION_LONG
));
203 static void dump_host(char *domain
, char *formats
)
205 printf("%s\t%s\n", domain
, formats
);
210 /* Wraps quvi_supported. */
211 static void supported(quvi_t quvi
)
213 QUVIcode rc
= QUVI_NOSUPPORT
;
216 for (i
=0; i
<opts
->inputs_num
; ++i
)
218 rc
= quvi_supported(quvi
, (char *)opts
->inputs
[i
]);
220 spew_qe("%s: OK\n", (char *)opts
->inputs
[i
]);
222 spew_qe("error: %s\n", quvi_strerror(quvi
, rc
));
228 static void format_help(quvi_t quvi
)
232 if (strcmp(opts
->format_arg
, "help") == 0)
235 " --format arg get format arg\n"
236 " --format list list websites and supported formats\n"
237 " --format list arg match arg to websites, list formats for matches\n"
239 " --format mp4_360p get format mp4_360p (youtube)\n"
240 " --format list youtube list youtube formats\n"
241 " --format list dailym list dailym(otion) formats\n"
246 else if (strcmp(opts
->format_arg
, "list") == 0)
254 quvi_next_supported_website(quvi
, &d
, &f
);
263 /* -f list <pattern> */
264 if (opts
->inputs_num
> 0)
265 print
= strstr(d
, (char *)opts
->inputs
[0]) != 0;
269 printf("%s:\n %s\n\n", d
, f
);
281 spew_e("%s\n", quvi_strerror(quvi
, rc
));
292 /* dumps all supported hosts to stdout. */
293 static void support(quvi_t quvi
)
297 if (opts
->inputs_num
> 0)
302 char *domain
, *formats
;
305 rc
= quvi_next_supported_website(quvi
, &domain
, &formats
);
310 dump_host(domain
, formats
);
316 spew_e("%s\n", quvi_strerror(quvi
, rc
));
324 static void invoke_exec(quvi_media_t media
)
326 char *cmd
, *media_url
, *q_media_url
;
329 quvi_getprop(media
, QUVIPROP_MEDIAURL
, &media_url
);
331 asprintf(&q_media_url
, "\"%s\"", media_url
);
333 cmd
= strdup(opts
->exec_arg
);
334 cmd
= strepl(cmd
, "%u", q_media_url
);
345 spew_e("error: failed to execute `%s'\n", cmd
);
348 spew_e("error: child exited with: %d\n", rc
>> 8);
359 double content_length
;
363 typedef struct parsed_link_s
*parsed_link_t
;
365 static void dump_media_link_xml(parsed_link_t p
, int i
)
367 char *media_url
= curl_easy_escape(curl
, p
->media_url
, 0);
369 spew(" <link id=\"%d\">\n", i
);
371 if (p
->content_length
)
372 spew(" <length_bytes>%.0f</length_bytes>\n", p
->content_length
);
374 if (strlen(p
->content_type
))
375 spew(" <content_type>%s</content_type>\n", p
->content_type
);
377 if (strlen(p
->file_suffix
))
378 spew(" <file_suffix>%s</file_suffix>\n", p
->file_suffix
);
380 spew(" <url>%s</url>\n"
382 media_url
? media_url
: p
->media_url
);
387 static void dump_media_link_old(parsed_link_t p
, int i
)
389 spew("link %02d : %s\n", i
, p
->media_url
);
391 if (p
->content_length
)
392 spew(":: length: %.0f\n", p
->content_length
);
394 if (strlen(p
->file_suffix
))
395 spew(":: suffix: %s\n", p
->file_suffix
);
397 if (strlen(p
->content_type
))
398 spew(":: content-type: %s\n", p
->content_type
);
401 static void dump_media_link_json(parsed_link_t p
, int i
)
404 " \"id\": \"%d\",\n", i
);
406 if (p
->content_length
)
407 spew(" \"length_bytes\": \"%.0f\",\n", p
->content_length
);
409 if (strlen(p
->content_type
))
410 spew(" \"content_type\": \"%s\",\n", p
->content_type
);
412 if (strlen(p
->file_suffix
))
413 spew(" \"file_suffix\": \"%s\",\n", p
->file_suffix
);
415 spew(" \"url\": \"%s\"\n"
421 static void dump_media_links(quvi_media_t media
)
426 struct parsed_link_s p
;
428 memset(&p
, 0, sizeof(&p
));
430 quvi_getprop(media
, QUVIPROP_MEDIAURL
, &p
.media_url
);
431 quvi_getprop(media
, QUVIPROP_MEDIACONTENTTYPE
, &p
.content_type
);
432 quvi_getprop(media
, QUVIPROP_MEDIACONTENTLENGTH
, &p
.content_length
);
433 quvi_getprop(media
, QUVIPROP_FILESUFFIX
, &p
.file_suffix
);
438 dump_media_link_xml(&p
,i
);
439 else if (opts
->old_given
)
440 dump_media_link_old(&p
,i
);
442 dump_media_link_json(&p
,i
);
444 while (quvi_next_media_url(media
) == QUVI_OK
);
459 typedef struct parsed_s
*parsed_t
;
461 static void dump_media_xml(parsed_t p
)
463 char *e_page_url
, *e_thumb_url
;
465 e_page_url
= curl_easy_escape(curl
, p
->page_url
, 0);
466 e_thumb_url
= curl_easy_escape(curl
, p
->thumb_url
, 0);
468 spew("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
469 "<media id=\"%s\" host=\"%s\">\n"
470 " <format_requested>%s</format_requested>\n"
471 " <page_title>%s</page_title>\n"
472 " <page_url>%s</page_url>\n",
477 e_page_url
? e_page_url
: "");
479 if (strlen(p
->start_time
))
480 spew(" <start_time>%s</start_time>\n", p
->start_time
);
482 if (e_thumb_url
&& strlen(e_thumb_url
))
483 spew(" <thumbnail_url>%s</thumbnail_url>\n", e_thumb_url
);
486 spew(" <duration>%.0f</duration>\n", p
->duration
);
490 curl_free(e_page_url
);
496 curl_free(e_thumb_url
);
501 static void dump_media_old(parsed_t p
)
503 spew(" > Dump media:\n"
508 "format : %s (requested)\n",
515 if (strlen(p
->start_time
))
516 spew("start time: %s\n", p
->start_time
);
518 if (strlen(p
->thumb_url
))
519 spew("thumbnail url: %s\n", p
->thumb_url
);
522 spew("duration: %.0f\n", p
->duration
);
525 static void dump_media_json(parsed_t p
)
529 t
= strdup(p
->page_title
);
530 t
= strepl(t
, "\"", "\\\"");
533 " \"host\": \"%s\",\n"
534 " \"page_title\": \"%s\",\n"
535 " \"page_url\": \"%s\",\n"
537 " \"format_requested\": \"%s\",\n",
544 if (strlen(p
->start_time
))
545 spew(" \"start_time\": \"%s\",\n", p
->start_time
);
547 if (strlen(p
->thumb_url
))
548 spew(" \"thumbnail_url\": \"%s\",\n", p
->thumb_url
);
551 spew(" \"duration\": \"%.0f\",\n", p
->duration
);
553 spew(" \"link\": [\n");
558 static void dump_media(quvi_media_t media
)
562 memset(&p
, 0, sizeof(p
));
564 quvi_getprop(media
, QUVIPROP_HOSTID
, &p
.host
);
565 quvi_getprop(media
, QUVIPROP_PAGEURL
, &p
.page_url
);
566 quvi_getprop(media
, QUVIPROP_PAGETITLE
, &p
.page_title
);
567 quvi_getprop(media
, QUVIPROP_MEDIAID
, &p
.media_id
);
568 quvi_getprop(media
, QUVIPROP_FORMAT
, &p
.format
);
569 quvi_getprop(media
, QUVIPROP_STARTTIME
, &p
.start_time
);
570 quvi_getprop(media
, QUVIPROP_MEDIATHUMBNAILURL
, &p
.thumb_url
);
571 quvi_getprop(media
, QUVIPROP_MEDIADURATION
, &p
.duration
);
575 else if (opts
->old_given
)
580 dump_media_links(media
);
584 else if (opts
->old_given
) ;
589 static void dump_error(quvi_t quvi
, QUVIcode rc
)
591 fprintf(stderr
, "error: %s\n", quvi_strerror(quvi
, rc
));
594 static quvi_t
init_quvi()
599 if ((rc
= quvi_init(&quvi
)) != QUVI_OK
)
601 dump_error(quvi
, rc
);
606 /* Set quvi options. */
608 if (opts
->format_given
)
609 quvi_setopt(quvi
, QUVIOPT_FORMAT
, opts
->format_arg
);
611 quvi_setopt(quvi
, QUVIOPT_NORESOLVE
, opts
->no_resolve_given
);
612 quvi_setopt(quvi
, QUVIOPT_NOVERIFY
, opts
->no_verify_given
);
614 if (opts
->category_all_given
)
615 quvi_setopt(quvi
, QUVIOPT_CATEGORY
, QUVIPROTO_ALL
);
619 if (opts
->category_http_given
)
621 if (opts
->category_mms_given
)
623 if (opts
->category_rtsp_given
)
625 if (opts
->category_rtmp_given
)
628 quvi_setopt(quvi
, QUVIOPT_CATEGORY
, n
);
631 quvi_setopt(quvi
, QUVIOPT_STATUSFUNCTION
, status_callback
);
632 quvi_setopt(quvi
, QUVIOPT_WRITEFUNCTION
, write_callback
);
634 /* Use the quvi created cURL handle. */
636 quvi_getinfo(quvi
, QUVIINFO_CURL
, &curl
);
639 if (opts
->agent_given
)
640 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, opts
->agent_arg
);
642 if (opts
->proxy_given
)
643 curl_easy_setopt(curl
, CURLOPT_PROXY
, opts
->proxy_arg
);
645 if (opts
->no_proxy_given
)
646 curl_easy_setopt(curl
, CURLOPT_PROXY
, "");
648 curl_easy_setopt(curl
, CURLOPT_VERBOSE
, opts
->verbose_libcurl_given
);
650 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
,
651 opts
->connect_timeout_arg
);
656 static void cleanup()
658 llst_node_t curr
= inputs
;
666 assert(inputs
== NULL
);
670 assert(quvi
== NULL
);
674 cmdline_parser_free(opts
);
677 assert(opts
== NULL
);
680 static void read_stdin()
683 while (fgets(b
, sizeof(b
), stdin
))
687 const size_t n
= strlen(b
)-1;
692 llst_add(&inputs
, strdup(b
));
697 static int read_input()
699 if (opts
->inputs_num
== 0)
704 for (i
=0; i
<opts
->inputs_num
; ++i
)
705 llst_add(&inputs
, strdup(opts
->inputs
[i
]));
707 return (llst_size(inputs
));
710 int main(int argc
, char *argv
[])
712 const char *url
, *home
, *no_config
, *fname
;
713 QUVIcode rc
, last_failure
;
720 assert(quvi
== NULL
);
721 assert(curl
== NULL
);
722 assert(opts
== NULL
);
723 assert(inputs
== NULL
);
725 no_config
= getenv("QUVI_NO_CONFIG");
729 home
= getenv("QUVI_HOME");
731 home
= getenv("HOME");
741 opts
= calloc(1, sizeof(struct gengetopt_args_info
));
745 /* Init cmdline parser. */
747 if (home
&& !no_config
)
752 asprintf(&path
, "%s%s", home
, fname
);
753 f
= fopen(path
, "r");
757 struct cmdline_parser_params
*pp
;
762 pp
= cmdline_parser_params_create();
763 pp
->check_required
= 0;
765 if (cmdline_parser_config_file(path
, opts
, pp
) == 0)
769 pp
->check_required
= 1;
771 if (cmdline_parser_ext(argc
, argv
, opts
, pp
) == 0)
782 if (cmdline_parser(argc
, argv
, opts
) != 0)
783 return (QUVI_INVARG
);
786 if (opts
->version_given
)
789 if (opts
->license_given
)
792 verbose_flag
= !opts
->quiet_given
;
796 if (opts
->support_given
)
799 if (opts
->format_given
)
804 inputs_num
= read_input();
808 spew_qe("error: no input links\n");
809 return (QUVI_INVARG
);
812 last_failure
= QUVI_OK
;
815 for (i
=0, curr
=inputs
; curr
; ++i
)
817 rc
= quvi_parse(quvi
, (char *)curr
->data
, &media
);
823 if (opts
->exec_given
)
829 while (quvi_next_media_url(media
) == QUVI_OK
);
838 quvi_parse_close(&media
);
845 spew_qe("Results: %d OK, %d failed (last 0x%02x), exit with 0x%02x\n",
846 inputs_num
- errors
, errors
, last_failure
, rc
);
852 /* vim: set ts=2 sw=2 tw=72 expandtab: */