2 * Copyright (C) 2009-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. */
31 #include <curl/curl.h>
35 #include "quvi/quvi.h"
36 #include "quvi/llst.h"
41 do { if (p) { free(p); p=0; } } while (0)
44 extern char *strepl(const char *s
, const char *what
, const char *with
);
46 static int verbose_flag
= 1;
47 static quvi_t quvi
= NULL
;
48 static CURL
*curl
= NULL
;
50 typedef struct gengetopt_args_info
*opts_t
;
51 static opts_t opts
= NULL
;
53 static quvi_llst_node_t inputs
= NULL
;
55 /* prints to std(e)rr. */
56 static void spew_e(const char *fmt
, ...)
60 vfprintf(stderr
, fmt
, ap
);
64 /* respects (q)uiet, prints to std(e)rr. */
65 static void spew_qe(const char *fmt
, ...)
71 vfprintf(stderr
, fmt
, ap
);
75 /* glorified printf. */
76 static void spew(const char *fmt
, ...)
80 vfprintf(stdout
, fmt
, ap
);
84 static void dump_error(quvi_t quvi
, QUVIcode rc
)
86 fprintf(stderr
, "error: %s\n", quvi_strerror(quvi
, rc
));
89 static void handle_resolve_status(quvi_word type
)
91 if (type
== QUVISTATUSTYPE_DONE
)
94 spew_qe(":: Check for URL redirection ...");
97 static void handle_fetch_status(quvi_word type
, void *p
)
102 spew_qe(":: Fetch %s ...", (char *)p
);
104 case QUVISTATUSTYPE_CONFIG
:
105 spew_qe(":: Fetch config ...");
107 case QUVISTATUSTYPE_PLAYLIST
:
108 spew_qe(":: Fetch playlist ...");
110 case QUVISTATUSTYPE_DONE
:
116 static void handle_verify_status(quvi_word type
)
121 spew_qe(":: Verify media URL ...");
123 case QUVISTATUSTYPE_DONE
:
129 static int status_callback(long param
, void *data
)
131 quvi_word status
, type
;
133 status
= quvi_loword(param
);
134 type
= quvi_hiword(param
);
138 case QUVISTATUS_RESOLVE
:
139 handle_resolve_status(type
);
142 case QUVISTATUS_FETCH
:
143 handle_fetch_status(type
, data
);
146 case QUVISTATUS_VERIFY
:
147 handle_verify_status(type
);
156 /* Divided into smaller blocks. Otherwise -pedantic objects. */
160 " * Copyright (C) 2009,2010,2011 Toni Gundogdu <legatvs@gmail.com>\n"
163 " * This library is free software; you can redistribute it and/or\n" \
164 " * modify it under the terms of the GNU Lesser General Public\n" \
165 " * License as published by the Free Software Foundation; either\n" \
166 " * version 2.1 of the License, or (at your option) any later version.\n"
169 " * This library is distributed in the hope that it will be useful,\n" \
170 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
171 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" \
172 " * Lesser General Public License for more details.\n"
175 " * You should have received a copy of the GNU Lesser General Public\n" \
176 " * License along with this library; if not, write to the Free Software\n" \
177 " * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" \
178 " * 02110-1301 USA\n" " */"
180 static void license()
182 printf("%s *\n%s *\n%s *\n%s\n",
183 LICENSE_1
, LICENSE_2
, LICENSE_3
, LICENSE_4
);
192 static void version()
194 printf("quvi version %s\n", quvi_version(QUVI_VERSION_LONG
));
198 static void dump_host(char *domain
, char *formats
)
200 printf("%s\t%s\n", domain
, formats
);
205 /* Wraps quvi_supported_ident. */
206 static void supported(quvi_t quvi
)
208 #ifndef _QUVI_SUPPORTED
217 for (i
=0; i
<opts
->inputs_num
; ++i
)
219 rc
= quvi_supported_ident(quvi
, (char*)opts
->inputs
[i
], &ident
);
222 quvi_ident_getprop(ident
, QUVI_IDENT_PROPERTY_FORMATS
, &formats
);
223 spew("%10s : %s\n", formats
, (char *)opts
->inputs
[i
]);
224 quvi_supported_ident_close(&ident
);
233 /* Query which formats are available for the URL */
234 static void query_formats(quvi_t quvi
)
239 if (opts
->inputs_num
< 1)
241 spew_qe("error: no input URLs\n");
245 for (i
=0; i
<opts
->inputs_num
; ++i
)
247 char *formats
= NULL
;
249 rc
= quvi_query_formats(quvi
, (char*)opts
->inputs
[i
], &formats
);
252 spew("%10s : %s\n", formats
, opts
->inputs
[i
]);
262 /* dumps all supported hosts to stdout. */
263 static void support(quvi_t quvi
)
267 if (opts
->inputs_num
> 0)
272 char *domain
, *formats
;
275 rc
= quvi_next_supported_website(quvi
, &domain
, &formats
);
280 dump_host(domain
, formats
);
286 spew_e("%s\n", quvi_strerror(quvi
, rc
));
294 static void invoke_exec(quvi_media_t media
)
296 char *cmd
, *media_url
, *q_media_url
;
297 char *page_title
, *q_page_title
, *t
;
300 quvi_getprop(media
, QUVIPROP_PAGETITLE
, &page_title
);
301 t
= strdup(page_title
);
302 t
= strepl(t
, "\"", "\\\""); /* Escape existing double quotation marks */
303 asprintf(&q_page_title
, "\"%s\"", t
); /* Put inside quotation marks */
306 quvi_getprop(media
, QUVIPROP_MEDIAURL
, &media_url
);
307 asprintf(&q_media_url
, "\"%s\"", media_url
);
309 cmd
= strdup(opts
->exec_arg
);
310 cmd
= strepl(cmd
, "%t", q_page_title
);
311 cmd
= strepl(cmd
, "%u", q_media_url
);
323 spew_e("error: failed to execute `%s'\n", cmd
);
326 spew_e("error: child exited with: %d\n", rc
>> 8);
337 double content_length
;
341 typedef struct parsed_url_s
*parsed_url_t
;
343 static void dump_media_url_xml(parsed_url_t p
, int i
)
345 char *media_url
= curl_easy_escape(curl
, p
->media_url
, 0);
347 spew(" <link id=\"%d\">\n", i
);
349 if (p
->content_length
)
350 spew(" <length_bytes>%.0f</length_bytes>\n", p
->content_length
);
352 if (strlen(p
->content_type
))
353 spew(" <content_type>%s</content_type>\n", p
->content_type
);
355 if (strlen(p
->file_suffix
))
356 spew(" <file_suffix>%s</file_suffix>\n", p
->file_suffix
);
358 spew(" <url>%s</url>\n"
360 media_url
? media_url
: p
->media_url
);
365 static void dump_media_url_json(parsed_url_t p
, int i
, int prepend_newln
)
371 " \"id\": \"%d\",\n", i
);
373 if (p
->content_length
)
374 spew(" \"length_bytes\": \"%.0f\",\n", p
->content_length
);
376 if (strlen(p
->content_type
))
377 spew(" \"content_type\": \"%s\",\n", p
->content_type
);
379 if (strlen(p
->file_suffix
))
380 spew(" \"file_suffix\": \"%s\",\n", p
->file_suffix
);
382 spew(" \"url\": \"%s\"\n"
387 static void dump_media_urls(quvi_media_t media
)
389 int json_flag
=0, i
=1;
392 struct parsed_url_s p
;
394 memset(&p
, 0, sizeof(&p
));
396 quvi_getprop(media
, QUVIPROP_MEDIAURL
, &p
.media_url
);
397 quvi_getprop(media
, QUVIPROP_MEDIACONTENTTYPE
, &p
.content_type
);
398 quvi_getprop(media
, QUVIPROP_MEDIACONTENTLENGTH
, &p
.content_length
);
399 quvi_getprop(media
, QUVIPROP_FILESUFFIX
, &p
.file_suffix
);
402 dump_media_url_xml(&p
,i
);
405 dump_media_url_json(&p
, i
, i
>1);
410 while (quvi_next_media_url(media
) == QUVI_OK
);
428 typedef struct parsed_s
*parsed_t
;
430 static void dump_media_xml(parsed_t p
)
432 char *e_page_url
, *e_thumb_url
;
434 e_page_url
= curl_easy_escape(curl
, p
->page_url
, 0);
435 e_thumb_url
= curl_easy_escape(curl
, p
->thumb_url
, 0);
437 spew("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
438 "<media id=\"%s\" host=\"%s\">\n"
439 " <format_requested>%s</format_requested>\n"
440 " <page_title>%s</page_title>\n"
441 " <page_url>%s</page_url>\n",
446 e_page_url
? e_page_url
: "");
448 if (strlen(p
->start_time
))
449 spew(" <start_time>%s</start_time>\n", p
->start_time
);
451 if (e_thumb_url
&& strlen(e_thumb_url
))
452 spew(" <thumbnail_url>%s</thumbnail_url>\n", e_thumb_url
);
455 spew(" <duration>%.0f</duration>\n", p
->duration
);
459 curl_free(e_page_url
);
465 curl_free(e_thumb_url
);
470 static void dump_media_json(parsed_t p
)
474 t
= strdup(p
->page_title
);
475 t
= strepl(t
, "\"", "\\\"");
478 " \"host\": \"%s\",\n"
479 " \"page_title\": \"%s\",\n"
480 " \"page_url\": \"%s\",\n"
482 " \"format_requested\": \"%s\",\n",
489 if (strlen(p
->start_time
))
490 spew(" \"start_time\": \"%s\",\n", p
->start_time
);
492 if (strlen(p
->thumb_url
))
493 spew(" \"thumbnail_url\": \"%s\",\n", p
->thumb_url
);
496 spew(" \"duration\": \"%.0f\",\n", p
->duration
);
498 spew(" \"link\": [\n");
503 static void dump_media(quvi_media_t media
)
507 memset(&p
, 0, sizeof(p
));
509 quvi_getprop(media
, QUVIPROP_HOSTID
, &p
.host
);
510 quvi_getprop(media
, QUVIPROP_PAGEURL
, &p
.page_url
);
511 quvi_getprop(media
, QUVIPROP_PAGETITLE
, &p
.page_title
);
512 quvi_getprop(media
, QUVIPROP_MEDIAID
, &p
.media_id
);
513 quvi_getprop(media
, QUVIPROP_FORMAT
, &p
.format
);
514 quvi_getprop(media
, QUVIPROP_STARTTIME
, &p
.start_time
);
515 quvi_getprop(media
, QUVIPROP_MEDIATHUMBNAILURL
, &p
.thumb_url
);
516 quvi_getprop(media
, QUVIPROP_MEDIADURATION
, &p
.duration
);
523 dump_media_urls(media
);
531 static quvi_t
init_quvi()
536 if ((rc
= quvi_init(&quvi
)) != QUVI_OK
)
538 dump_error(quvi
, rc
);
543 /* Set quvi options. */
545 if (opts
->format_given
)
546 quvi_setopt(quvi
, QUVIOPT_FORMAT
, opts
->format_arg
);
548 quvi_setopt(quvi
, QUVIOPT_NORESOLVE
, opts
->no_resolve_given
);
549 quvi_setopt(quvi
, QUVIOPT_NOVERIFY
, opts
->no_verify_given
);
551 if (opts
->category_all_given
)
552 quvi_setopt(quvi
, QUVIOPT_CATEGORY
, QUVIPROTO_ALL
);
556 if (opts
->category_http_given
)
558 if (opts
->category_mms_given
)
560 if (opts
->category_rtsp_given
)
562 if (opts
->category_rtmp_given
)
565 quvi_setopt(quvi
, QUVIOPT_CATEGORY
, n
);
568 quvi_setopt(quvi
, QUVIOPT_STATUSFUNCTION
, status_callback
);
570 /* Use the quvi created cURL handle. */
572 quvi_getinfo(quvi
, QUVIINFO_CURL
, &curl
);
575 if (opts
->agent_given
)
576 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, opts
->agent_arg
);
578 if (opts
->proxy_given
)
579 curl_easy_setopt(curl
, CURLOPT_PROXY
, opts
->proxy_arg
);
581 if (opts
->no_proxy_given
)
582 curl_easy_setopt(curl
, CURLOPT_PROXY
, "");
584 curl_easy_setopt(curl
, CURLOPT_VERBOSE
, opts
->verbose_libcurl_given
);
586 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
,
587 opts
->connect_timeout_arg
);
592 static void cleanup()
594 quvi_llst_free(&inputs
);
595 assert(inputs
== NULL
);
599 assert(quvi
== NULL
);
603 cmdline_parser_free(opts
);
606 assert(opts
== NULL
);
609 static void read_from(FILE *f
, int close
)
616 while (fgets(b
, sizeof(b
), f
))
620 const size_t n
= strlen(b
)-1;
625 quvi_llst_append(&inputs
, strdup(b
));
636 static char *parse_url_scheme(const char *url
)
640 p
= strstr(url
, ":/");
644 asprintf(&r
, "%.*s", (int)(p
- url
), url
);
649 static int is_url(const char *s
)
651 char *p
= parse_url_scheme(s
);
660 static FILE* open_file(const char *path
)
662 FILE *f
= fopen(path
, "rt");
665 spew_e("error: %s: %s\n", path
, strerror(errno
));
672 static int read_input()
674 if (opts
->inputs_num
== 0)
679 for (i
=0; i
<opts
->inputs_num
; ++i
)
681 if (!is_url(opts
->inputs
[i
]))
682 read_from(open_file(opts
->inputs
[i
]), 1);
683 else /* Must be an URL. */
684 quvi_llst_append(&inputs
, strdup(opts
->inputs
[i
]));
687 return (quvi_llst_size(inputs
));
690 int main(int argc
, char *argv
[])
692 const char *home
, *no_config
, *fname
;
693 QUVIcode rc
, last_failure
;
694 quvi_llst_node_t curr
;
700 assert(quvi
== NULL
);
701 assert(curl
== NULL
);
702 assert(opts
== NULL
);
703 assert(inputs
== NULL
);
705 no_config
= getenv("QUVI_NO_CONFIG");
708 home
= getenv("QUVI_HOME");
710 home
= getenv("HOME");
720 opts
= calloc(1, sizeof(struct gengetopt_args_info
));
724 /* Init cmdline parser. */
726 if (home
&& !no_config
)
731 asprintf(&path
, "%s%s", home
, fname
);
732 f
= fopen(path
, "r");
736 struct cmdline_parser_params
*pp
;
741 pp
= cmdline_parser_params_create();
742 pp
->check_required
= 0;
744 if (cmdline_parser_config_file(path
, opts
, pp
) == 0)
748 pp
->check_required
= 1;
750 if (cmdline_parser_ext(argc
, argv
, opts
, pp
) == 0)
761 if (cmdline_parser(argc
, argv
, opts
) != 0)
762 return (QUVI_INVARG
);
765 if (opts
->version_given
)
768 if (opts
->license_given
)
771 verbose_flag
= !opts
->quiet_given
;
775 if (opts
->query_formats_given
)
778 if (opts
->support_given
)
783 inputs_num
= read_input();
787 spew_qe("error: no input URLs\n");
788 return (QUVI_INVARG
);
791 last_failure
= QUVI_OK
;
794 for (i
=0, curr
=inputs
; curr
; ++i
)
796 char *url
= quvi_llst_data(curr
);
797 rc
= quvi_parse(quvi
, url
, &media
);
803 if (opts
->exec_given
)
809 while (quvi_next_media_url(media
) == QUVI_OK
);
818 quvi_parse_close(&media
);
820 curr
= quvi_llst_next(curr
);
825 spew_qe("Results: %d OK, %d failed (last 0x%02x), exit with 0x%02x\n",
826 inputs_num
- errors
, errors
, last_failure
, rc
);
832 /* vim: set ts=2 sw=2 tw=72 expandtab: */