├── src ├── cclog ├── ccre ├── ccsig ├── ccvar ├── ccerror ├── ccfile ├── ccinput ├── ccutil ├── ccfstream ├── ccinternal ├── ccoptions ├── ccapplication ├── ccprogressbar ├── compat │ ├── util.h │ ├── error_pt9.cpp │ ├── error_pt4.cpp │ ├── media_pt4.cpp │ ├── options.h │ ├── util.cpp │ ├── media_pt9.cpp │ ├── query_pt4.cpp │ ├── query_pt9.cpp │ ├── media.h │ ├── error.h │ └── query.h ├── ccquvi ├── cc │ ├── re.h │ ├── var.h │ ├── application.h │ ├── util.h │ ├── wait.cpp │ ├── error.cpp │ ├── internal.h │ ├── background.cpp │ ├── curl.cpp │ ├── main.cpp │ ├── get.cpp │ ├── file.h │ ├── error.h │ ├── sig.h │ ├── exec.cpp │ ├── log.h │ ├── fstream.h │ ├── input.h │ ├── re.cpp │ ├── application.cpp │ ├── file.cpp │ ├── progressbar.h │ └── options.h └── Makefile.am ├── AUTHORS ├── doc ├── Makefile.am ├── man1 │ ├── Makefile.am │ └── cclive.1.txt └── asciidoc.conf ├── .gitignore ├── Makefile.am ├── gen-news.sh ├── gen-ver.sh ├── bootstrap.sh ├── README ├── configure.ac ├── NEWS └── ChangeLog.pre-gitlog /src/cclog: -------------------------------------------------------------------------------- 1 | #include "cc/log.h" 2 | -------------------------------------------------------------------------------- /src/ccre: -------------------------------------------------------------------------------- 1 | #include "cc/re.h" 2 | -------------------------------------------------------------------------------- /src/ccsig: -------------------------------------------------------------------------------- 1 | #include "cc/sig.h" 2 | -------------------------------------------------------------------------------- /src/ccvar: -------------------------------------------------------------------------------- 1 | #include "cc/var.h" 2 | -------------------------------------------------------------------------------- /src/ccerror: -------------------------------------------------------------------------------- 1 | #include "cc/error.h" 2 | -------------------------------------------------------------------------------- /src/ccfile: -------------------------------------------------------------------------------- 1 | #include "cc/file.h" 2 | -------------------------------------------------------------------------------- /src/ccinput: -------------------------------------------------------------------------------- 1 | #include "cc/input.h" 2 | -------------------------------------------------------------------------------- /src/ccutil: -------------------------------------------------------------------------------- 1 | #include "cc/util.h" 2 | -------------------------------------------------------------------------------- /src/ccfstream: -------------------------------------------------------------------------------- 1 | #include "cc/fstream.h" 2 | -------------------------------------------------------------------------------- /src/ccinternal: -------------------------------------------------------------------------------- 1 | #include "cc/internal.h" 2 | -------------------------------------------------------------------------------- /src/ccoptions: -------------------------------------------------------------------------------- 1 | #include "cc/options.h" 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Toni Gundogdu 2 | -------------------------------------------------------------------------------- /src/ccapplication: -------------------------------------------------------------------------------- 1 | #include "cc/application.h" 2 | -------------------------------------------------------------------------------- /src/ccprogressbar: -------------------------------------------------------------------------------- 1 | #include "cc/progressbar.h" 2 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST=asciidoc.conf 2 | SUBDIRS=man1 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | autom4te.cache 2 | configure.scan 3 | autoscan.log 4 | patches/ 5 | *.patch 6 | *.diff 7 | todo/ 8 | tmp/ 9 | *.swp 10 | usr/ 11 | *~ 12 | Makefile.in 13 | aclocal.m4 14 | config.aux/ar-lib 15 | config.aux/config.guess 16 | config.aux/config.sub 17 | config.aux/depcomp 18 | config.aux/install-sh 19 | config.aux/ltmain.sh 20 | config.aux/missing 21 | config.h.in 22 | configure 23 | doc/Makefile.in 24 | doc/man1/Makefile.in 25 | m4/libtool.m4 26 | m4/ltoptions.m4 27 | m4/ltsugar.m4 28 | m4/ltversion.m4 29 | m4/lt~obsolete.m4 30 | src/Makefile.in 31 | INSTALL 32 | -------------------------------------------------------------------------------- /doc/man1/Makefile.am: -------------------------------------------------------------------------------- 1 | MAN1_TXT=cclive.1.txt 2 | DOC_MAN1=cclive.1 3 | 4 | MAN_TXT=$(MAN1_TXT) 5 | 6 | ASCIIDOC_OPTS=\ 7 | -f $(top_srcdir)/doc/asciidoc.conf\ 8 | -a cclive_version="$(VERSION)" 9 | 10 | if HAVE_A2X 11 | $(DOC_MAN1): 12 | $(AM_V_GEN)$(A2X) -f manpage --asciidoc-opts="$(ASCIIDOC_OPTS)" \ 13 | -D $(builddir) $(srcdir)/$@.txt 14 | doc: $(DOC_MAN1) 15 | else 16 | doc: 17 | @echo configure did not find a2x 18 | endif # HAVE_A2X 19 | 20 | MOSTLYCLEANFILES=$(DOC_MAN1) 21 | dist_man_MANS=$(DOC_MAN1) 22 | EXTRA_DIST=$(MAN_TXT) 23 | 24 | # vim: set ts=2 sw=2 tw=72 expandtab: 25 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS=-I m4 2 | EXTRA_DIST= gen-ver.sh VERSION 3 | 4 | SUBDIRS= src 5 | if WITH_MANUAL 6 | SUBDIRS+= doc 7 | endif 8 | 9 | .PHONY: ChangeLog VERSION doc 10 | 11 | VERSION: 12 | @if test -d "$(top_srcdir)/.git" -o -f "$(top_srcdir)/.git"; then \ 13 | "$(top_srcdir)/gen-ver.sh" "$(top_srcdir)" > $@; \ 14 | fi 15 | 16 | ChangeLog: 17 | @if test -d "$(top_srcdir)/.git" -o -f "$(top_srcdir)/.git"; then \ 18 | git log --stat --name-only --date=short --abbrev-commit \ 19 | --after="Thu Aug 12 15:54:58 2010" > $@; \ 20 | fi 21 | 22 | doc: 23 | @make -C $(top_builddir)/doc/man1/ doc 24 | 25 | dist-hook: ChangeLog VERSION 26 | -------------------------------------------------------------------------------- /src/compat/util.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef compat_util_h 22 | #define compat_util_h 23 | 24 | namespace quvi 25 | { 26 | std::string support_to_s(const std::map&); 27 | std::string version(); 28 | } // namespace quvi 29 | 30 | #endif // compat_util_h 31 | 32 | // vim: set ts=2 sw=2 tw=72 expandtab: 33 | -------------------------------------------------------------------------------- /gen-news.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # cclive 4 | # Copyright (C) 2012 Toni Gundogdu 5 | # 6 | # This file is part of cclive . 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU Affero General Public License as 10 | # published by the Free Software Foundation, either version 3 of the 11 | # License, or (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # Affero General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU Affero General Public 19 | # License along with this program. If not, see 20 | # . 21 | # 22 | set -e 23 | IFS=$' 24 | ' 25 | v=`./gen-ver.sh` 26 | b="$v [`date +%F`] 27 | 28 | " 29 | t=`git describe --abbrev=0` 30 | for a in `git log --no-merges --format='%an' "$t"..HEAD | sort | uniq`; do 31 | c=`git log --no-merges --format=" - %s [%h]" --author="$a" "$t"..HEAD | sort -h` 32 | b="$b$a: 33 | $c 34 | " 35 | done 36 | b="$b 37 | `git diff --shortstat "$t"..HEAD` 38 | 39 | " 40 | n=`cat NEWS` 41 | F=NEWS 42 | echo "$b" >"$F" ; echo "$n" >>"$F" 43 | -------------------------------------------------------------------------------- /src/ccquvi: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef ccquvi 22 | #define ccquvi 23 | 24 | #include "config.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef HAVE_LIBQUVI_0_9 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #endif // ccquvi 42 | 43 | // vim: set ts=2 sw=2 tw=72 expandtab: 44 | -------------------------------------------------------------------------------- /src/cc/re.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2011 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cclive_re_h 22 | #define cclive_re_h 23 | 24 | namespace cc 25 | { 26 | 27 | namespace re 28 | { 29 | 30 | bool subst(const std::string&, std::string&); 31 | bool capture(const std::string&, std::string&); 32 | bool grep(const std::string&, const std::string&); 33 | void tr(const std::string&, std::string&); 34 | void trim(std::string&); 35 | 36 | } // namespace re 37 | 38 | } // namespace cc 39 | 40 | #endif // cclive_re_h 41 | 42 | // vim: set ts=2 sw=2 tw=72 expandtab: 43 | -------------------------------------------------------------------------------- /src/cc/var.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__var_h 22 | #define cc__var_h 23 | 24 | #include 25 | #include 26 | 27 | namespace cc 28 | { 29 | boost::iostreams::filtering_ostream log; 30 | namespace var 31 | { 32 | volatile sig_atomic_t recv_sigwinch = 0; 33 | volatile sig_atomic_t recv_sigusr1 = 0; 34 | size_t max_width = 0; 35 | } // namespace sig 36 | } // namespace cc 37 | 38 | #endif // cc__var_h 39 | 40 | // vim: set ts=2 sw=2 tw=72 expandtab: 41 | -------------------------------------------------------------------------------- /src/cc/application.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cclive_application_h 22 | #define cclive_application_h 23 | 24 | #include 25 | 26 | namespace cc 27 | { 28 | 29 | class application 30 | { 31 | public: 32 | typedef enum {ok=EXIT_SUCCESS, error=EXIT_FAILURE} exit_status; 33 | public: 34 | inline application():_curl(NULL) { } 35 | inline virtual ~application() { } 36 | public: 37 | exit_status exec(int const, char const**); 38 | private: 39 | void *_curl; 40 | }; 41 | 42 | } // namespace cc 43 | 44 | #endif // cclive_application_h 45 | 46 | // vim: set ts=2 sw=2 tw=72 expandtab: 47 | -------------------------------------------------------------------------------- /src/compat/error_pt9.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | namespace quvi 25 | { 26 | 27 | void error_pt9::_init(quvi_t q) 28 | { 29 | _what = quvi_errmsg(q); 30 | quvi_get(q, QUVI_INFO_RESPONSE_CODE, &_resp_code); 31 | _quvi_code = quvi_errcode(q); 32 | } 33 | 34 | std::string error_pt9::to_s() const 35 | { 36 | return (boost::format("what=%s, resp_code=%ld, quvi_code=%ld") 37 | % _what.c_str() 38 | % _resp_code 39 | % _quvi_code).str(); 40 | } 41 | 42 | } // namespace quvi 43 | 44 | // vim: set ts=2 sw=2 tw=72 expandtab: 45 | -------------------------------------------------------------------------------- /src/compat/error_pt4.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | namespace quvi 25 | { 26 | 27 | void error_pt4::_init(quvi_t q) 28 | { 29 | _what = quvi_strerror(q, static_cast(_quvi_code)); 30 | quvi_getinfo(q, QUVIINFO_RESPONSECODE, &_resp_code); 31 | } 32 | 33 | std::string error_pt4::to_s() const 34 | { 35 | return (boost::format("what=%s, resp_code=%ld, quvi_code=%ld") 36 | % _what.c_str() 37 | % _resp_code 38 | % _quvi_code).str(); 39 | } 40 | 41 | } // namespace quvi 42 | 43 | // vim: set ts=2 sw=2 tw=72 expandtab: 44 | -------------------------------------------------------------------------------- /src/cc/util.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2011-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cclive_util_h 22 | #define cclive_util_h 23 | 24 | namespace boost 25 | { 26 | namespace program_options 27 | { 28 | class variables_map; 29 | } 30 | } 31 | 32 | namespace cc 33 | { 34 | 35 | namespace po = boost::program_options; 36 | class file; 37 | 38 | void get(quvi::media&, void*, const po::variables_map&); 39 | void exec(const file&, const po::variables_map&); 40 | void curl_setup(void*, const po::variables_map&); 41 | 42 | void go_background(const std::string&, bool&); 43 | std::string perror(const std::string& p=""); 44 | void wait(const int); 45 | 46 | } // namespace cc 47 | 48 | #endif // cclive_util_h 49 | 50 | /* vim: set ts=2 sw=2 tw=72 expandtab: */ 51 | -------------------------------------------------------------------------------- /src/cc/wait.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | 25 | #ifdef HAVE_UNISTD_H 26 | #include 27 | #endif 28 | 29 | #ifdef _WIN32 30 | #include 31 | #define sleep(n) Sleep(n*1000) 32 | #endif 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | namespace cc 39 | { 40 | 41 | void wait(const int retry_wait) 42 | { 43 | for (int i=1; i<=retry_wait; ++i) 44 | { 45 | if (i % 5 == 0) 46 | cc::log << i; 47 | else 48 | cc::log << "."; 49 | 50 | cc::log << std::flush; 51 | sleep(1); 52 | } 53 | cc::log << std::endl; 54 | } 55 | 56 | } // namespace cc 57 | 58 | // vim: set ts=2 sw=2 tw=72 expandtab: 59 | -------------------------------------------------------------------------------- /src/compat/media_pt4.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | namespace quvi 25 | { 26 | 27 | static void _get_s(quvi_media_t qm, QUVIproperty qp, std::string& dst) 28 | { 29 | char *s; 30 | 31 | dst.clear(); 32 | quvi_getprop(qm, qp, &s); 33 | 34 | if (strlen(s) >0) 35 | dst = s; 36 | } 37 | 38 | void media_pt4::_init(quvi_t, quvi_media_t qm) 39 | { 40 | quvi_getprop(qm, QUVIPROP_MEDIACONTENTLENGTH, &_content_length); 41 | _get_s(qm, QUVIPROP_MEDIACONTENTTYPE, _content_type); 42 | _get_s(qm, QUVIPROP_FILESUFFIX, _file_ext); 43 | _get_s(qm, QUVIPROP_MEDIAURL, _stream_url); 44 | _get_s(qm, QUVIPROP_PAGETITLE, _title); 45 | _get_s(qm, QUVIPROP_MEDIAID, _id); 46 | } 47 | 48 | } // namespace quvi 49 | 50 | // vim: set ts=2 sw=2 tw=72 expandtab: 51 | -------------------------------------------------------------------------------- /src/cc/error.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2011 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | namespace cc 32 | { 33 | 34 | std::string perror(const std::string& p/*=""*/) 35 | { 36 | #if defined (HAVE_STRERROR) || defined (HAVE_STRERROR_R) 37 | std::string s; 38 | #ifdef HAVE_STRERROR_R 39 | char buf[256]; 40 | s = strerror_r(errno, buf, sizeof(buf)); 41 | #else // HAVE_STRERROR_R 42 | s = strerror(errno); 43 | #endif // HAVE_STRERROR_R 44 | return s; 45 | #else // HAVE_STRERROR || HAVE_STRERROR_R 46 | perror(p.c_str()); // No strerror or strerror_r 47 | #endif // HAVE_STRERROR || HAVE_STRERROR_R 48 | } 49 | 50 | } // namspace cclive 51 | 52 | // vim: set ts=2 sw=2 tw=72 expandtab: 53 | -------------------------------------------------------------------------------- /src/compat/options.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef compat_options_h 22 | #define compat_options_h 23 | 24 | namespace quvi { 25 | 26 | class options 27 | { 28 | inline void _copy(const options& a) 29 | { 30 | statusfunc = a.statusfunc; 31 | useragent = a.useragent; 32 | resolve = a.resolve; 33 | stream = a.stream; 34 | } 35 | public: 36 | inline options(const options& a):statusfunc(NULL), resolve(true) { _copy(a); } 37 | inline options():statusfunc(NULL), resolve(true) { } 38 | inline options& operator=(const options& a) 39 | { 40 | if (this != &a) 41 | _copy(a); 42 | return *this; 43 | } 44 | inline virtual ~options() { statusfunc=NULL; } 45 | public: 46 | quvi_callback_status statusfunc; 47 | std::string useragent; 48 | std::string stream; 49 | bool resolve; 50 | }; 51 | 52 | } // namespace quvi 53 | 54 | #endif // compat_options_h 55 | 56 | // vim: set ts=2 sw=2 tw=72 expandtab: 57 | -------------------------------------------------------------------------------- /gen-ver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # cclive 4 | # Copyright (C) 2012 Toni Gundogdu 5 | # 6 | # This file is part of cclive . 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU Affero General Public License as 10 | # published by the Free Software Foundation, either version 3 of the 11 | # License, or (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # Affero General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU Affero General Public 19 | # License along with this program. If not, see 20 | # . 21 | # 22 | dir=`dirname $0` 23 | o= 24 | # flags: 25 | c= # strip off the 'v' prefix 26 | 27 | # VERSION file is part of the dist tarball. 28 | from_VERSION_file() 29 | { 30 | o=`cat "$dir/VERSION" 2>/dev/null` 31 | } 32 | 33 | from_git_describe() 34 | { 35 | [ -d "$dir/.git" -o -f "$dir/.git" ] && { 36 | o=`git describe --match "v[0-9]*" --abbrev=4 HEAD 2>/dev/null` 37 | } 38 | } 39 | 40 | dump_vn() 41 | { 42 | [ -n "$c" ] && o=${o#v} # strip off the 'v' prefix. 43 | echo $o 44 | exit 0 45 | } 46 | 47 | help() 48 | { 49 | echo "$0 [OPTIONS] 50 | -h Show this help and exit 51 | -c Strip off the 'v' prefix from the output" 52 | exit 0 53 | } 54 | 55 | while [ $# -gt 0 ] 56 | do 57 | case "$1" in 58 | -c) c=1;; 59 | -h) help;; 60 | *) break;; 61 | esac 62 | shift 63 | done 64 | 65 | from_VERSION_file 66 | [ -z "$o" ] && from_git_describe 67 | [ -n "$o" ] && dump_vn 68 | exit 1 69 | 70 | # vim: set ts=2 sw=2 tw=72 expandtab: 71 | -------------------------------------------------------------------------------- /doc/asciidoc.conf: -------------------------------------------------------------------------------- 1 | ## linkman: macro 2 | # Inspired by/borrowed from the pacman source tree at doc/asciidoc.conf 3 | # 4 | # Usage: linkman:command[manpage-section] 5 | # 6 | # Note, {0} is the manpage section, while {target} is the command. 7 | # 8 | # Show man link as: (
); if section is defined, else just show 9 | # the command. 10 | 11 | [macros] 12 | (?su)[\\]?(?Plinkman):(?P\S*?)\[(?P.*?)\]= 13 | 14 | [attributes] 15 | asterisk=* 16 | plus=+ 17 | caret=^ 18 | startsb=[ 19 | endsb=] 20 | backslash=\ 21 | tilde=~ 22 | apostrophe=' 23 | backtick=` 24 | litdd=-- 25 | 26 | ifdef::backend-docbook[] 27 | [linkman-inlinemacro] 28 | {0%{target}} 29 | {0#} 30 | {0#{target}{0}} 31 | {0#} 32 | endif::backend-docbook[] 33 | 34 | ifdef::backend-docbook[] 35 | ifndef::docbook-xsl-172[] 36 | # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. 37 | # v1.72 breaks with this because it replaces dots not in roff requests. 38 | [listingblock] 39 | {title} 40 | 41 | | 42 | 43 | {title#} 44 | endif::docbook-xsl-172[] 45 | endif::backend-docbook[] 46 | 47 | ifdef::doctype-manpage[] 48 | ifdef::backend-docbook[] 49 | [header] 50 | template::[header-declarations] 51 | 52 | 53 | {mantitle} 54 | {manvolnum} 55 | cclive 56 | {cclive_version} 57 | cclive Manual 58 | 59 | 60 | {manname} 61 | {manpurpose} 62 | 63 | endif::backend-docbook[] 64 | endif::doctype-manpage[] 65 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # cclive 4 | # Copyright (C) 2012-2013 Toni Gundogdu 5 | # 6 | # This file is part of cclive . 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU Affero General Public License as 10 | # published by the Free Software Foundation, either version 3 of the 11 | # License, or (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # Affero General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU Affero General Public 19 | # License along with this program. If not, see 20 | # . 21 | # 22 | set -e 23 | 24 | source=.gitignore 25 | cachedir=autom4te.cache 26 | 27 | cleanup() 28 | { 29 | echo "WARNING 30 | This will remove the files specified in the $source file. This will also 31 | remove the $cachedir/ directory with all of its contents. 32 | Bail out now (^C) or hit enter to continue." 33 | read n1 34 | for file in `cat $source`; do # Remove files only. 35 | [ -e "$file" ] && [ -f "$file" ] && rm -f "$file" 36 | done 37 | [ -e "$cachedir" ] && rm -rf "$cachedir" 38 | rmdir -p config.aux 2>/dev/null 39 | exit 0 40 | } 41 | 42 | help() 43 | { 44 | echo "Usage: $0 [-c|-h] 45 | -h Show this help and exit 46 | -c Make the source tree 'maintainer clean' 47 | Run without options to (re)generate the configuration files." 48 | exit 0 49 | } 50 | 51 | clean_flag=off 52 | while [ $# -gt 0 ] 53 | do 54 | case "$1" in 55 | -c) cleanup;; 56 | -h) help;; 57 | *) break;; 58 | esac 59 | shift 60 | done 61 | 62 | echo "Generate configuration files..." 63 | autoreconf -if && echo "You can now run 'configure'" 64 | -------------------------------------------------------------------------------- /src/compat/util.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | namespace quvi 27 | { 28 | 29 | // Version. 30 | 31 | std::string version() 32 | { 33 | #ifdef HAVE_LIBQUVI_0_9 34 | std::stringstream s; 35 | s << quvi_version(QUVI_VERSION) << "\n " 36 | << " built on " << quvi_version(QUVI_VERSION_BUILD_TIME) << " " 37 | << "for " << quvi_version(QUVI_VERSION_BUILD_TARGET) << "\n " 38 | << " with " << quvi_version(QUVI_VERSION_BUILD_CC_CFLAGS) << "\n " 39 | << "configuration: " << quvi_version(QUVI_VERSION_CONFIGURATION); 40 | return s.str(); 41 | #else 42 | return quvi_version(QUVI_VERSION_LONG); 43 | #endif 44 | } 45 | 46 | typedef std::map::const_iterator smci; 47 | 48 | static const std::string _domain_patt_to_s(smci i) 49 | { 50 | std::string d = (*i).first; 51 | #ifndef HAVE_LIBQUVI_0_9 /* Only libquvi 0.4 needs this. */ 52 | pcrecpp::RE("%w\\+").GlobalReplace("com", &d); 53 | pcrecpp::RE("%").GlobalReplace("", &d); 54 | #endif 55 | return d; 56 | } 57 | 58 | std::string support_to_s(const std::map& map) 59 | { 60 | std::stringstream b; 61 | 62 | for (smci iter = map.begin(); iter != map.end(); ++iter) 63 | b << _domain_patt_to_s(iter) << "\n"; 64 | 65 | return b.str(); 66 | } 67 | 68 | } // namespace quvi 69 | 70 | // vim: set ts=2 sw=2 tw=72 expandtab: 71 | -------------------------------------------------------------------------------- /src/cc/internal.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2011,2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | // NOTE: Include this header instead of 'config.h' 22 | 23 | #ifndef cc__internal_h 24 | #define cc__internal_h 25 | 26 | #include 27 | #include "config.h" 28 | 29 | // Boost.Program_Options 30 | 31 | #if (BOOST_VERSION <= 104900) 32 | // There is a bug in Boost.Program_Options (1.49 at least, possibly 33 | // earlier) that causes the library to provide an incomplete error 34 | // message when an option is given multiple times (and this isn't 35 | // acceptable). 1.53+ appears to work OK, 1.50-1.52 unconfirmed. 36 | #ifndef HAVE_BUG__BOOST_PO__MULTIPLE_OCCURRENCES 37 | #define HAVE_BUG__BOOST_PO__MULTIPLE_OCCURRENCES 38 | #endif 39 | #endif 40 | 41 | // Boost.Filesystem 42 | 43 | #define BOOST_FILESYSTEM_NO_DEPRECATED 44 | 45 | #if (BOOST_VERSION >= 104400) // Boost 1.44+ 46 | #ifndef BOOST_FILESYSTEM_VERSION 47 | #define BOOST_FILESYSTEM_VERSION 3 // Default in 1.46.0 48 | #endif 49 | #endif 50 | 51 | #ifndef if_optsw_given 52 | #define if_optsw_given(__varmap,__varname)\ 53 | if (__varmap[__varname].as()) 54 | #endif 55 | 56 | #ifndef ifn_optsw_given 57 | #define ifn_optsw_given(__varmap,__varname)\ 58 | if (! __varmap[__varname].as()) 59 | #endif 60 | 61 | #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) 62 | #ifdef HAVE_VFORK 63 | #define vfork fork 64 | #endif 65 | #endif 66 | 67 | #endif // cc__internal_h 68 | 69 | /* vim: set ts=2 sw=2 tw=72 expandtab: */ 70 | -------------------------------------------------------------------------------- /src/compat/media_pt9.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | namespace quvi 25 | { 26 | 27 | static void _get_s(quvi_media_t qm, QuviMediaProperty qmp, std::string& dst) 28 | { 29 | char *s = NULL; 30 | 31 | dst.clear(); 32 | quvi_media_get(qm, qmp, &s); 33 | 34 | if (s != NULL && strlen(s) >0) 35 | dst = s; 36 | } 37 | 38 | static void _qmi_s(quvi_http_metainfo_t qmi, 39 | const QuviHTTPMetaInfoProperty qmip, 40 | std::string& dst) 41 | { 42 | char *s = NULL; 43 | 44 | dst.clear(); 45 | quvi_http_metainfo_get(qmi, qmip, &s); 46 | 47 | if (s != NULL && strlen(s) >0) 48 | dst = s; 49 | } 50 | 51 | void media_pt9::_init(quvi_t q, quvi_media_t qm) 52 | { 53 | _get_s(qm, QUVI_MEDIA_STREAM_PROPERTY_URL, _stream_url); 54 | _get_s(qm, QUVI_MEDIA_PROPERTY_TITLE, _title); 55 | _get_s(qm, QUVI_MEDIA_PROPERTY_ID, _id); 56 | 57 | quvi_http_metainfo_t qmi = quvi_http_metainfo_new(q, _stream_url.c_str()); 58 | if (quvi_ok(q) == QUVI_FALSE) 59 | { 60 | quvi_http_metainfo_free(qmi); 61 | throw error(q); 62 | } 63 | 64 | quvi_http_metainfo_get(qmi, QUVI_HTTP_METAINFO_PROPERTY_LENGTH_BYTES, 65 | &_content_length); 66 | 67 | _qmi_s(qmi, QUVI_HTTP_METAINFO_PROPERTY_CONTENT_TYPE, _content_type); 68 | _qmi_s(qmi, QUVI_HTTP_METAINFO_PROPERTY_FILE_EXTENSION, _file_ext); 69 | 70 | quvi_http_metainfo_free(qmi); 71 | } 72 | 73 | } // namespace quvi 74 | 75 | // vim: set ts=2 sw=2 tw=72 expandtab: 76 | -------------------------------------------------------------------------------- /src/cc/background.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #ifdef HAVE_UNISTD_H 24 | #include 25 | #endif 26 | 27 | #ifdef HAVE_SYS_STAT_H 28 | #include 29 | #endif 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace cc 39 | { 40 | 41 | namespace io = boost::iostreams; 42 | 43 | #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) 44 | void go_background(const std::string& log_file, bool& omit) 45 | { 46 | const pid_t pid = fork(); 47 | 48 | if (pid < 0) 49 | { 50 | cc::perror("fork"); 51 | exit(application::error); 52 | } 53 | else if (pid != 0) 54 | { 55 | std::clog // Parent exits 56 | << "Run in background (pid: " 57 | << static_cast(pid) 58 | << "). Redirect output to \"" 59 | << log_file 60 | << "\"." 61 | << std::endl; 62 | exit(0); 63 | } 64 | 65 | // Child continues and becomes the session leader. 66 | setsid(); 67 | 68 | // Clear file mode creation mask. 69 | umask(0); 70 | 71 | // Close unneeded file descriptors/streams. 72 | freopen("/dev/null", "w", stdout); 73 | freopen("/dev/null", "w", stderr); 74 | freopen("/dev/null", "r", stdin); 75 | 76 | // Redirect output to log file. 77 | cc::log.push(io::tee(cc::sink::flushable_file(log_file))); 78 | 79 | // Omit output to stderr (using cc::log). 80 | omit = true; 81 | } 82 | #endif // HAVE_WORKING_(V)FORK 83 | 84 | } // namespace cc 85 | 86 | // vim: set ts=2 sw=2 tw=72 expandtab: 87 | -------------------------------------------------------------------------------- /src/cc/curl.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace cc 30 | { 31 | 32 | namespace po = boost::program_options; 33 | 34 | static void _set_proxy(CURL *c, const po::variables_map& vm) 35 | { 36 | if (vm.count(OPT__PROXY)) 37 | { 38 | curl_easy_setopt(c, CURLOPT_PROXY, 39 | vm[OPT__PROXY].as().c_str()); 40 | } 41 | if_optsw_given(vm, OPT__NO_PROXY) 42 | curl_easy_setopt(c, CURLOPT_PROXY, ""); 43 | } 44 | 45 | void curl_setup(CURL *c, const po::variables_map& vm) 46 | { 47 | const int n = vm[OPT__THROTTLE].as().value() * 1024; 48 | const curl_off_t t = static_cast(n); 49 | 50 | curl_easy_setopt(c, CURLOPT_MAX_RECV_SPEED_LARGE, t); 51 | curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); 52 | 53 | if_optsw_given(vm, OPT__VERBOSE_LIBCURL) 54 | curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); 55 | 56 | #ifndef HAVE_LIBQUVI_0_9 57 | curl_easy_setopt(c, CURLOPT_USERAGENT, 58 | vm[OPT__AGENT].as().c_str()); 59 | #endif 60 | 61 | curl_easy_setopt(c, CURLOPT_DNS_CACHE_TIMEOUT, 62 | vm[OPT__DNS_CACHE_TIMEOUT].as().value()); 63 | 64 | curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, 65 | vm[OPT__CONNECT_TIMEOUT].as().value()); 66 | 67 | curl_easy_setopt(c, CURLOPT_TIMEOUT, 68 | vm[OPT__TRANSFER_TIMEOUT].as().value()); 69 | 70 | _set_proxy(c, vm); 71 | } 72 | 73 | } // namespace cc 74 | 75 | // vim: set ts=2 sw=2 tw=72 expandtab: 76 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | src=\ 3 | cc/application.cpp\ 4 | cc/background.cpp\ 5 | cc/curl.cpp\ 6 | cc/error.cpp\ 7 | cc/exec.cpp\ 8 | cc/file.cpp\ 9 | cc/get.cpp\ 10 | cc/main.cpp\ 11 | cc/re.cpp\ 12 | cc/wait.cpp 13 | 14 | if HAVE_LIBQUVI_0_9 15 | src+=\ 16 | compat/error_pt9.cpp\ 17 | compat/media_pt9.cpp\ 18 | compat/query_pt9.cpp 19 | else 20 | src+=\ 21 | compat/error_pt4.cpp\ 22 | compat/media_pt4.cpp\ 23 | compat/query_pt4.cpp 24 | endif 25 | src+=\ 26 | compat/util.cpp 27 | 28 | hdr=\ 29 | ccapplication\ 30 | ccerror\ 31 | ccfile\ 32 | ccfstream\ 33 | ccinput\ 34 | ccinternal\ 35 | cclog\ 36 | ccoptions\ 37 | ccprogressbar\ 38 | ccquvi\ 39 | ccre\ 40 | ccsig\ 41 | ccutil\ 42 | ccvar 43 | 44 | hdr+=\ 45 | cc/application.h\ 46 | cc/error.h\ 47 | cc/file.h\ 48 | cc/fstream.h\ 49 | cc/input.h\ 50 | cc/internal.h\ 51 | cc/log.h\ 52 | cc/options.h\ 53 | cc/progressbar.h\ 54 | cc/re.h\ 55 | cc/sig.h\ 56 | cc/util.h\ 57 | cc/var.h 58 | 59 | hdr+=\ 60 | compat/error.h\ 61 | compat/media.h\ 62 | compat/options.h\ 63 | compat/query.h\ 64 | compat/util.h 65 | 66 | bin_PROGRAMS= cclive 67 | cclive_SOURCES= $(src) $(hdr) 68 | 69 | AM_CPPFLAGS= -I$(top_srcdir)/src 70 | AM_CPPFLAGS+= $(libquvi_CFLAGS) 71 | AM_CPPFLAGS+= $(libcurl_CFLAGS) 72 | AM_CPPFLAGS+= $(libpcre_CFLAGS) 73 | AM_CPPFLAGS+= $(libpcrepp_CFLAGS) 74 | AM_CPPFLAGS+= $(glibmm_CFLAGS) 75 | AM_CPPFLAGS+= $(glib_CFLAGS) 76 | AM_CPPFLAGS+= $(BOOST_CPPFLAGS) 77 | 78 | cclive_LDFLAGS= $(BOOST_DATE_TIME_LDFLAGS) 79 | cclive_LDFLAGS+=$(BOOST_PROGRAM_OPTIONS_LDFLAGS) 80 | cclive_LDFLAGS+=$(BOOST_IOSTREAMS_LDFLAGS) 81 | cclive_LDFLAGS+=$(BOOST_FILESYSTEM_LDFLAGS) 82 | cclive_LDFLAGS+=$(BOOST_SYSTEM_LDFLAGS) 83 | 84 | cclive_LDADD= $(libquvi_LIBS) 85 | cclive_LDADD+= $(libcurl_LIBS) 86 | cclive_LDADD+= $(libpcre_LIBS) 87 | cclive_LDADD+= $(libpcrecpp_LIBS) 88 | cclive_LDADD+= $(glibmm_LIBS) 89 | cclive_LDADD+= $(glib_LIBS) 90 | cclive_LDADD+= $(BOOST_DATE_TIME_LIBS) 91 | cclive_LDADD+= $(BOOST_PROGRAM_OPTIONS_LIBS) 92 | cclive_LDADD+= $(BOOST_IOSTREAMS_LIBS) 93 | cclive_LDADD+= $(BOOST_FILESYSTEM_LIBS) 94 | cclive_LDADD+= $(BOOST_SYSTEM_LIBS) 95 | 96 | install-exec-hook: 97 | if ENABLE_CCL 98 | cd $(DESTDIR)$(bindir) && rm -f ccl$(EXEEXT) && \ 99 | $(LN_S) cclive$(EXEEXT) ccl$(EXEEXT) 100 | endif 101 | 102 | uninstall-hook: 103 | if ENABLE_CCL 104 | cd $(DESTDIR)$(bindir) && \ 105 | rm -f ccl$(EXEEXT) 106 | endif 107 | -------------------------------------------------------------------------------- /src/cc/main.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | using namespace cc; 39 | 40 | int main(int const argc, char const **argv) 41 | { 42 | setlocale(LC_ALL, ""); 43 | application::exit_status es = application::ok; 44 | try 45 | { 46 | application app; 47 | es = app.exec(argc, argv); 48 | } 49 | // --version, --help, etc. 50 | catch (const cc::exit_program&) 51 | { 52 | // Fall through. 53 | } 54 | catch (const cc::error::no_input&) 55 | { 56 | std::clog << "error: no input URL" << std::endl; 57 | es = application::error; 58 | } 59 | // Thrown by quvi::query constructor (e.g. quvi_init, quvi_new). 60 | catch (const quvi::error& e) 61 | { 62 | std::clog << "libquvi: error: " << e.what() << std::endl; 63 | es = application::error; 64 | } 65 | // Thrown by boost (e.g. cc::go_background failure). 66 | catch (const std::runtime_error& e) 67 | { 68 | std::clog << "error: " << e.what() << std::endl; 69 | es = application::error; 70 | } 71 | // Thrown by boost::program_options (cc::options). 72 | catch (const boost::exception& x) 73 | { 74 | es = static_cast(cc::error::print(x)); 75 | } 76 | return es; 77 | } 78 | 79 | // vim: set ts=2 sw=2 tw=72 expandtab: 80 | -------------------------------------------------------------------------------- /src/cc/get.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace cc 33 | { 34 | 35 | namespace po = boost::program_options; 36 | 37 | void get(quvi::media& media, void *curl, const po::variables_map& vm) 38 | { 39 | const int max_retries = vm[OPT__MAX_RETRIES].as().value(); 40 | const int retry_wait = vm[OPT__RETRY_WAIT].as().value(); 41 | 42 | const bool no_download = vm[OPT__NO_DOWNLOAD].as(); 43 | const bool exec = vm.count(OPT__EXEC); 44 | 45 | int retry = 0; 46 | 47 | while (retry <= max_retries) 48 | { 49 | cc::file file(media, vm); 50 | 51 | if (file.nothing_todo()) 52 | { 53 | if (exec) 54 | cc::exec(file, vm); 55 | #define E_NOTHING_TODO "media retrieved completely already" 56 | throw std::runtime_error(E_NOTHING_TODO); 57 | #undef E_NOTHING_TODO 58 | } 59 | 60 | // Download media. 61 | 62 | if (retry > 0) 63 | { 64 | cc::log 65 | << "Retrying " 66 | << retry 67 | << " of " 68 | << max_retries 69 | << " ... " 70 | << std::flush; 71 | 72 | cc::wait(retry_wait); 73 | } 74 | 75 | cc::log << file.to_s(media) << std::endl; 76 | ++retry; 77 | 78 | if (!no_download) 79 | { 80 | if (!file.write(media, curl, vm)) 81 | continue; // Retry. 82 | 83 | if (exec) 84 | cc::exec(file, vm); 85 | } 86 | 87 | break; // Stop retrying. 88 | } 89 | } 90 | 91 | } // namespace cc 92 | 93 | // vim: set ts=2 sw=2 tw=72 expandtab: 94 | -------------------------------------------------------------------------------- /src/cc/file.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cclive_file_h 22 | #define cclive_file_h 23 | 24 | namespace fs = boost::filesystem; 25 | 26 | namespace cc 27 | { 28 | 29 | class file 30 | { 31 | public: 32 | file(const quvi::media&, const po::variables_map&); 33 | 34 | inline file(): _initial_length(0), _nothing_todo(false) { } 35 | 36 | inline file(const file& f): _initial_length(0), _nothing_todo(false) 37 | { 38 | _swap(f); 39 | } 40 | 41 | inline file& operator=(const file& f) 42 | { 43 | if (this != &f) _swap(f); 44 | return *this; 45 | } 46 | public: 47 | bool write(const quvi::media&, void*, const po::variables_map&) const; 48 | public: 49 | std::string to_s(const quvi::media&) const; 50 | inline const std::string& title() const { return _title; } 51 | inline const std::string& path() const { return _path; } 52 | inline const std::string& name() const { return _name; } 53 | inline const bool nothing_todo() const { return _nothing_todo; } 54 | inline double initial_length() const { return _initial_length; } 55 | inline bool should_continue() const { return _initial_length >0; } 56 | inline int set_errmsg(const std::string& e) { _errmsg = e; return 0; } 57 | private: 58 | void _init(const quvi::media&, const po::variables_map&); 59 | 60 | inline void _swap(const file& f) 61 | { 62 | _title = f._title; 63 | _name = f._name; 64 | _path = f._path; 65 | _initial_length = f._initial_length; 66 | _nothing_todo = f._nothing_todo; 67 | } 68 | inline void store_path(const fs::path& p) 69 | { 70 | _name = p.filename().string(); 71 | _path = p.string(); 72 | } 73 | static inline double exists(const std::string& fpath) 74 | { 75 | if (fs::exists(fpath)) 76 | return static_cast(fs::file_size(fpath)); 77 | return 0; 78 | } 79 | private: 80 | double _initial_length; 81 | std::string _errmsg; 82 | bool _nothing_todo; 83 | std::string _title; 84 | std::string _name; 85 | std::string _path; 86 | }; 87 | 88 | } // namespace cc 89 | 90 | #endif // cclive_file_h 91 | 92 | // vim: set ts=2 sw=2 tw=72 expandtab: 93 | -------------------------------------------------------------------------------- /src/compat/query_pt4.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | namespace quvi 27 | { 28 | 29 | void query_pt4::_init() 30 | { 31 | const QUVIcode qc = quvi_init(&_quvi); 32 | if (qc != QUVI_OK) 33 | throw error(_quvi, qc); 34 | } 35 | 36 | void query_pt4::_close() 37 | { 38 | if (_quvi != NULL) 39 | { 40 | quvi_close(&_quvi); 41 | _quvi = NULL; 42 | } 43 | } 44 | 45 | void query_pt4::_configure(const quvi::options& opts) const 46 | { 47 | if (! opts.stream.empty()) 48 | quvi_setopt(_quvi, QUVIOPT_FORMAT, opts.stream.c_str()); 49 | 50 | quvi_setopt(_quvi, QUVIOPT_STATUSFUNCTION, opts.statusfunc); 51 | quvi_setopt(_quvi, QUVIOPT_NORESOLVE, opts.resolve ? 0L:1L); 52 | quvi_setopt(_quvi, QUVIOPT_CATEGORY, QUVIPROTO_HTTP); 53 | } 54 | 55 | void *query_pt4::setup_curl(const po::variables_map& vm) const 56 | { 57 | CURL *c; 58 | quvi_getinfo(_quvi, QUVIINFO_CURL, &c); 59 | cc::curl_setup(c, vm); 60 | return c; 61 | } 62 | 63 | std::string query_pt4::streams(const std::string& url, 64 | const quvi::options& opts) const 65 | { 66 | _configure(opts); 67 | 68 | char *fmts; 69 | 70 | const QUVIcode qc = 71 | quvi_query_formats(_quvi, const_cast(url.c_str()), &fmts); 72 | 73 | if (qc != QUVI_OK) 74 | throw error(_quvi, qc); 75 | 76 | std::string r = fmts; 77 | quvi_free(fmts); 78 | 79 | return r; 80 | } 81 | 82 | media query_pt4::parse(const std::string& url, const options& opts) const 83 | { 84 | _configure(opts); 85 | 86 | quvi_media_t qm; 87 | QUVIcode qc = quvi_parse(_quvi, const_cast(url.c_str()), &qm); 88 | 89 | if (qc != QUVI_OK) 90 | throw error(_quvi, qc); 91 | 92 | media r(_quvi, qm); 93 | quvi_parse_close(&qm); 94 | 95 | return r; 96 | } 97 | 98 | std::map query_pt4::support() const 99 | { 100 | quvi_setopt(_quvi, QUVIOPT_CATEGORY, QUVIPROTO_HTTP); 101 | 102 | std::map r; 103 | char *a, *b; 104 | 105 | while (quvi_next_supported_website(_quvi, &a, &b) == QUVI_OK) 106 | { 107 | r[a] = b; 108 | quvi_free(a); 109 | quvi_free(b); 110 | } 111 | return r; 112 | } 113 | 114 | } // namespace quvi 115 | 116 | // vim: set ts=2 sw=2 tw=72 expandtab: 117 | -------------------------------------------------------------------------------- /src/cc/error.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__error_h 22 | #define cc__error_h 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace cc 34 | { 35 | 36 | namespace error 37 | { 38 | 39 | static inline std::string strerror(const int ec) 40 | { 41 | char buf[256]; 42 | return strerror_r(ec, buf, sizeof(buf)); 43 | } 44 | 45 | static inline std::string strerror() { return strerror(errno); } 46 | 47 | typedef boost::tuple type_tuple; 48 | typedef boost::error_info errinfo_tuple; 49 | 50 | static inline int print(const boost::exception& x) 51 | { 52 | std::clog << "error: "; 53 | 54 | if (std::string const *s = 55 | boost::get_error_info(x)) 56 | { 57 | std::clog << *s << ": "; 58 | } 59 | 60 | bool print_what = true; 61 | if (int const *n = 62 | boost::get_error_info(x)) 63 | { 64 | std::clog << cc::error::strerror(*n) << " (" << *n << ")"; 65 | print_what = false; 66 | } 67 | 68 | if (type_tuple const *t = boost::get_error_info(x)) 69 | { 70 | std::clog << boost::get<0>(*t) << ": " << boost::get<1>(*t); 71 | print_what = false; 72 | } 73 | 74 | try 75 | { 76 | throw; 77 | } 78 | catch (const std::exception& x) 79 | { 80 | if (print_what) 81 | std::clog << x.what(); 82 | } 83 | catch (...) 84 | { 85 | std::clog << boost::diagnostic_information(x); 86 | } 87 | std::clog << std::endl; 88 | return EXIT_FAILURE; 89 | } 90 | 91 | struct no_input : virtual std::exception, virtual boost::exception { }; 92 | struct fstream : virtual std::exception, virtual boost::exception { }; 93 | struct config : virtual std::exception, virtual boost::exception { }; 94 | struct tuple : virtual std::exception, virtual boost::exception { }; 95 | 96 | } // namespace error 97 | 98 | struct exit_program : virtual std::exception, virtual boost::exception { }; 99 | struct nothing_todo : virtual std::exception, virtual boost::exception { }; 100 | 101 | } // namespace cc 102 | #endif // cc__error_h 103 | 104 | /* vim: set ts=2 sw=2 tw=72 expandtab: */ 105 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | General 2 | ======= 3 | 4 | cclive is a tool for downloading media from YouTube and similar 5 | websites. It has a low memory footprint compared to other existing 6 | tools. 7 | 8 | Home : http://cclive.sourceforge.net/ 9 | gitweb: http://repo.or.cz/w/cclive.git 10 | 11 | Installation 12 | ============ 13 | 14 | Notes 15 | ----- 16 | 17 | * This document does not cover how to use GNU Autotools for building 18 | and installing software, instead, refer to the GNU Autotools 19 | documentation, the INSTALL file and the "configure --help" output for 20 | more information. 21 | 22 | * If you're not familiar with GNU Autotools and/or installing software 23 | from the source code, you may prefer to install cclive using a binary 24 | package, instead. You can find cclive packaged for many distros 25 | already. 26 | 27 | * If you have previously installed cclive from the source code to your 28 | system, the recommended practice is to uninstall the previous 29 | installation first, before you continue with the new one. You would, 30 | typically, run "make uninstall" (as root if necessary) from the same 31 | directory you ran "make install" from. 32 | 33 | * If you are building cclive from the git repository code, run the 34 | `bootstrap.sh' first. This generates the configuration files. See also 35 | the -h output for any additional info. 36 | 37 | 38 | Simple install procedure 39 | ------------------------ 40 | 41 | % ./configure && make 42 | 43 | [ Become root if necessary ] 44 | % make install 45 | 46 | The configure script supports also the following options which are 47 | relevant to the installation: 48 | 49 | --with(out)-manual 50 | Install the manual page for cclive 51 | 52 | --(enable|disable)-ccl (default:yes) 53 | Make a symbolic link from 'cclive' (executable) to 'ccl'. 54 | 55 | 56 | Requirements 57 | ------------ 58 | 59 | * libquvi 0.4 60 | http://quvi.sourceforge.net/ 61 | $ sudo aptitude install libquvi-dev 62 | 63 | * libpcre 8.02 64 | http://pcre.org 65 | $ sudo aptitude install libpcre3-dev 66 | 67 | * libboost 1.49.0 68 | http://www.boost.org/ 69 | $ sudo aptitude install libboost-program-options-dev libboost-system-dev \ 70 | libboost-filesystem-dev libboost-iostreams-dev \ 71 | libboost-date-time-dev 72 | 73 | * libcurl 7.20.0 74 | http://curl.haxx.se/ 75 | $ sudo aptitude install libcurl4-gnutls-dev 76 | (or libcurl4-openssl-dev) 77 | 78 | * glibmm 2.24.2 79 | http://library.gnome.org/devel/glibmm/ 80 | $ sudo aptitude install libglibmm-2.4-dev 81 | 82 | * GLib 2.24.2 83 | http://library.gnome.org/devel/glib/ 84 | $ sudo aptitude install libglib2.0-dev 85 | 86 | * pkg-config for tracking the compilation flags needed for libraries 87 | http://www.freedesktop.org/software/pkgconfig/ 88 | $ sudo aptitude install build-essential 89 | 90 | * GNU make is recommended 91 | http://www.gnu.org/software/make/ 92 | $ sudo aptitude install build-essential 93 | 94 | 95 | How to report bugs 96 | ================== 97 | 98 | Please see: http://cclive.sourceforge.net/#bugs 99 | 100 | 101 | Patches 102 | ======= 103 | 104 | Please see: http://cclive.sourceforge.net/contrib/ 105 | 106 | 107 | License 108 | ======= 109 | 110 | cclive is Free Software, licensed under the GNU Affero GPLv3+. 111 | -------------------------------------------------------------------------------- /src/compat/query_pt9.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | namespace quvi 30 | { 31 | 32 | void query_pt9::_init() 33 | { 34 | _quvi = quvi_new(); 35 | if (quvi_ok(_quvi) == QUVI_FALSE) 36 | throw error(_quvi); 37 | } 38 | 39 | void query_pt9::_close() 40 | { 41 | quvi_free(_quvi); 42 | _quvi = NULL; 43 | } 44 | 45 | void query_pt9::_configure(const quvi::options& qopts) const 46 | { 47 | quvi_set(_quvi, QUVI_OPTION_USER_AGENT, qopts.useragent.c_str()); 48 | quvi_set(_quvi, QUVI_OPTION_CALLBACK_STATUS, qopts.statusfunc); 49 | } 50 | 51 | void *query_pt9::setup_curl(const po::variables_map& vm) const 52 | { 53 | CURL *c; 54 | quvi_get(_quvi, QUVI_INFO_CURL_HANDLE, &c); 55 | cc::curl_setup(c, vm); 56 | return c; 57 | } 58 | 59 | std::string query_pt9::streams(const std::string& url, 60 | const quvi::options& opts) const 61 | { 62 | _configure(opts); 63 | 64 | quvi_media_t qm = quvi_media_new(_quvi, url.c_str()); 65 | if (quvi_ok(_quvi) == QUVI_FALSE) 66 | { 67 | quvi_media_free(qm); 68 | throw error(_quvi); 69 | } 70 | 71 | std::stringstream ss; 72 | char *id; 73 | 74 | while (quvi_media_stream_next(qm) == QUVI_TRUE) 75 | { 76 | quvi_media_get(qm, QUVI_MEDIA_STREAM_PROPERTY_ID, &id); 77 | if (strlen(id) >0) 78 | { 79 | if (ss.str().length() >0) 80 | ss << ","; 81 | ss << id; 82 | } 83 | } 84 | quvi_media_free(qm); 85 | return ss.str(); 86 | } 87 | 88 | media query_pt9::parse(const std::string& url, 89 | const quvi::options& opts) const 90 | { 91 | _configure(opts); 92 | 93 | quvi_media_t qm = quvi_media_new(_quvi, url.c_str()); 94 | if (quvi_ok(_quvi) == QUVI_FALSE) 95 | throw error(_quvi); 96 | 97 | if (! opts.stream.empty()) 98 | quvi_media_stream_select(qm, opts.stream.c_str()); 99 | 100 | media r(_quvi, qm); 101 | quvi_media_free(qm); 102 | 103 | return r; 104 | } 105 | 106 | std::map query_pt9::support() const 107 | { 108 | std::map r; 109 | char *v; 110 | 111 | while (quvi_script_next(_quvi, QUVI_SCRIPT_TYPE_MEDIA) == QUVI_TRUE) 112 | { 113 | quvi_script_get(_quvi, QUVI_SCRIPT_TYPE_MEDIA, 114 | QUVI_SCRIPT_PROPERTY_DOMAINS, &v); 115 | r[v] = ""; 116 | } 117 | return r; 118 | } 119 | 120 | } // namespace quvi 121 | 122 | // vim: set ts=2 sw=2 tw=72 expandtab: 123 | -------------------------------------------------------------------------------- /src/cc/sig.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__sig_h 22 | #define cc__sig_h 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace cc 33 | { 34 | 35 | namespace var 36 | { 37 | extern volatile sig_atomic_t recv_sigwinch; 38 | extern volatile sig_atomic_t recv_sigusr1; 39 | extern size_t max_width; 40 | } 41 | 42 | namespace sig 43 | { 44 | 45 | namespace cv = cc::var; 46 | 47 | static inline void default_handler(int n) 48 | { 49 | switch (n) 50 | { 51 | case SIGWINCH: 52 | cv::recv_sigwinch = 1; 53 | break; 54 | case SIGUSR1: 55 | cv::recv_sigusr1 = 1; 56 | break; 57 | default: 58 | std::clog << __PRETTY_FUNCTION__ << ": warning: unhandled signal `" 59 | << n << "'" << std::endl; 60 | } 61 | } 62 | 63 | static inline size_t query_term_width() 64 | { 65 | struct winsize w; 66 | if (ioctl(fileno(stderr), TIOCGWINSZ, &w) <0) 67 | return 0; 68 | return w.ws_col; 69 | } 70 | 71 | static inline size_t calc_term_spaceleft(const size_t len, size_t& curr_width) 72 | { 73 | static const size_t default_term_width = 80; 74 | if (cv::max_width ==0 || cv::recv_sigwinch ==1) 75 | { 76 | cv::max_width = query_term_width(); 77 | if (cv::max_width ==0) 78 | cv::max_width = default_term_width; 79 | } 80 | curr_width = cv::max_width; 81 | return (cv::max_width-len-1); 82 | } 83 | 84 | static inline size_t calc_term_spaceleft(const size_t len) 85 | { 86 | size_t curr; 87 | return calc_term_spaceleft(len, curr); 88 | } 89 | 90 | } // namespace sig 91 | 92 | template 93 | struct sighandler : boost::noncopyable 94 | { 95 | inline virtual ~sighandler() { reset(); } 96 | inline sighandler() { setup(); } 97 | private: 98 | inline void setup() 99 | { 100 | ours.sa_handler = cc::sig::default_handler; 101 | sigemptyset(&ours.sa_mask); 102 | ours.sa_flags = 0; 103 | 104 | sigaction(T, NULL, &theirs); 105 | 106 | if (theirs.sa_handler != SIG_IGN) 107 | sigaction(T, &ours, NULL); 108 | } 109 | inline void reset() const 110 | { 111 | if (theirs.sa_handler != SIG_IGN) 112 | sigaction(T, &theirs, NULL); 113 | } 114 | private: 115 | struct sigaction theirs; 116 | struct sigaction ours; 117 | }; 118 | 119 | typedef struct sighandler type_sigwinch; 120 | typedef struct sighandler type_sigusr1; 121 | 122 | typedef boost::scoped_ptr sigwinch_handler_scptr; 123 | typedef boost::scoped_ptr sigusr1_handler_scptr; 124 | 125 | } // namespace cc 126 | 127 | #endif // cc__sigwinch_h 128 | 129 | // vim: set ts=2 sw=2 tw=72 expandtab: 130 | -------------------------------------------------------------------------------- /src/cc/exec.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | namespace cc 42 | { 43 | 44 | typedef std::vector vs; 45 | 46 | static int invoke_exec(const vs& args) 47 | { 48 | const size_t sz = args.size(); 49 | const char **argv = new const char* [sz+2]; 50 | if (!argv) 51 | throw std::runtime_error("memory allocation error"); 52 | 53 | argv[0] = args[0].c_str(); 54 | 55 | for (size_t i=1; i(); 122 | BOOST_FOREACH(std::string e, m) 123 | { 124 | pcrecpp::RE("%f").GlobalReplace(file.path(), &e); 125 | pcrecpp::RE("%n").GlobalReplace(file.name(), &e); 126 | pcrecpp::RE("%t").GlobalReplace(file.title(), &e); 127 | 128 | vs args; 129 | tokenize("([\"'](.*?)[\"']|\\S+)", e, args); 130 | invoke_exec(args); 131 | } 132 | } 133 | 134 | } // namespace cc 135 | 136 | // vim: set ts=2 sw=2 tw=72 expandtab: 137 | -------------------------------------------------------------------------------- /src/compat/media.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef compat_media_h 22 | #define compat_media_h 23 | 24 | namespace quvi { 25 | 26 | class media_base 27 | { 28 | virtual void _init(quvi_t, quvi_media_t) = 0; 29 | protected: 30 | inline virtual ~media_base() { } 31 | }; 32 | 33 | class media_impl : media_base 34 | { 35 | protected: 36 | std::string _content_type; 37 | std::string _stream_url; 38 | std::string _file_ext; 39 | std::string _title; 40 | std::string _id; 41 | double _content_length; 42 | protected: 43 | inline media_impl(const media_impl& a): _content_length(0) { _copy(a); } 44 | inline media_impl(): _content_length(0) { } 45 | inline virtual ~media_impl() { } 46 | inline media_impl& operator=(const media_impl& a) 47 | { 48 | if (this != &a) 49 | _copy(a); 50 | return *this; 51 | } 52 | inline void _copy(const media_impl& a) 53 | { 54 | _content_type = a._content_type; 55 | _stream_url = a._stream_url; 56 | _file_ext = a._file_ext; 57 | _title = a._title; 58 | _id = a._id; 59 | 60 | _content_length = a._content_length; 61 | } 62 | public: 63 | inline const std::string& content_type() const { return _content_type; } 64 | inline const std::string& stream_url() const { return _stream_url; } 65 | inline const std::string& file_ext() const { return _file_ext; } 66 | inline const std::string& title() const { return _title; } 67 | inline const std::string& id() const { return _id; } 68 | inline double content_length() const { return _content_length; } 69 | }; 70 | 71 | class media_pt4 : public media_impl 72 | { 73 | void _init(quvi_t, quvi_media_t); 74 | public: 75 | inline media_pt4(const media_pt4& a): media_impl(a) { } 76 | inline media_pt4(quvi_t q, quvi_media_t qm): media_impl() { _init(q, qm); } 77 | inline media_pt4(): media_impl() { } 78 | inline virtual ~media_pt4() { } 79 | inline media_pt4& operator=(const media_pt4& a) 80 | { 81 | if (this != &a) 82 | _copy(a); 83 | return *this; 84 | } 85 | }; 86 | 87 | class media_pt9 : public media_impl 88 | { 89 | void _init(quvi_t, quvi_media_t); 90 | public: 91 | inline media_pt9(const media_pt9& a): media_impl(a) { } 92 | inline media_pt9(quvi_t q, quvi_media_t qm): media_impl() { _init(q, qm); } 93 | inline media_pt9(): media_impl() { } 94 | inline virtual ~media_pt9() { } 95 | inline media_pt9& operator=(const media_pt9& a) 96 | { 97 | if (this != &a) 98 | _copy(a); 99 | return *this; 100 | } 101 | }; 102 | 103 | #ifdef HAVE_LIBQUVI_0_9 104 | typedef class media_pt9 media; 105 | #else 106 | typedef class media_pt4 media; 107 | #endif 108 | 109 | } // namespace quvi 110 | 111 | #endif // compat_media_h 112 | 113 | // vim: set ts=2 sw=2 tw=72 expandtab: 114 | -------------------------------------------------------------------------------- /src/compat/error.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef compat_error_h 22 | #define compat_error_h 23 | 24 | namespace quvi 25 | { 26 | 27 | class error_base 28 | { 29 | virtual void _init(quvi_t) = 0; 30 | public: 31 | virtual bool cannot_retry() const = 0; 32 | virtual std::string to_s() const = 0; 33 | protected: 34 | inline virtual ~error_base() { } 35 | }; 36 | 37 | class error_impl : error_base 38 | { 39 | protected: 40 | inline error_impl(const error_impl& a): _resp_code(0), _quvi_code(0) 41 | { 42 | _copy(a); 43 | } 44 | inline error_impl(): _resp_code(0), _quvi_code(0) { } 45 | inline virtual ~error_impl() { } 46 | inline error_impl& operator=(const error_impl& a) 47 | { 48 | if (this != &a) 49 | _copy(a); 50 | return *this; 51 | } 52 | inline void _copy(const error_impl& a) 53 | { 54 | _resp_code = a._resp_code; 55 | _quvi_code = a._quvi_code; 56 | _what = a._what; 57 | } 58 | public: 59 | inline const std::string& what() const 60 | { 61 | return _what; 62 | } 63 | inline long response_code() const 64 | { 65 | return _resp_code; 66 | } 67 | inline long quvi_code() const 68 | { 69 | return _quvi_code; 70 | } 71 | protected: 72 | std::string _what; 73 | long _resp_code; 74 | long _quvi_code; 75 | }; 76 | 77 | class error_pt4 : public error_impl 78 | { 79 | void _init(quvi_t); 80 | public: 81 | inline error_pt4(): error_impl() { } 82 | inline error_pt4(const error_pt4& a): error_impl(a) { } 83 | inline ~error_pt4() { } 84 | inline error_pt4(quvi_t q, const long qc): error_impl() 85 | { 86 | _quvi_code = qc; 87 | _init(q); 88 | } 89 | inline error_pt4& operator=(const error_pt4& a) 90 | { 91 | if (this != &a) 92 | _copy(a); 93 | return *this; 94 | } 95 | inline bool cannot_retry() const 96 | { 97 | return (_resp_code >= 400 || _quvi_code != QUVI_OK); 98 | } 99 | std::string to_s() const; 100 | }; 101 | 102 | class error_pt9 : public error_impl 103 | { 104 | void _init(quvi_t); 105 | public: 106 | inline error_pt9(): error_impl() { } 107 | inline error_pt9(const error_pt9& a): error_impl(a) { } 108 | inline ~error_pt9() { } 109 | inline error_pt9(quvi_t q, const long qc=-1): error_impl() 110 | { 111 | _init(q); // Sets _quvi_code 112 | } 113 | inline error_pt9& operator=(const error_pt9& a) 114 | { 115 | if (this != &a) 116 | _copy(a); 117 | return *this; 118 | } 119 | inline bool cannot_retry() const 120 | { 121 | return (_resp_code >= 400 || _quvi_code != QUVI_OK); 122 | } 123 | std::string to_s() const; 124 | }; 125 | 126 | #ifdef HAVE_LIBQUVI_0_9 127 | typedef class error_pt9 error; 128 | #else 129 | typedef class error_pt4 error; 130 | #endif 131 | 132 | } // namespace quvi 133 | 134 | #endif // compat_error_h 135 | 136 | // vim: set ts=2 sw=2 tw=72 expandtab: 137 | -------------------------------------------------------------------------------- /src/cc/log.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__log_h 22 | #define cc__log_h 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | namespace cc 33 | { 34 | 35 | extern boost::iostreams::filtering_ostream log; 36 | 37 | namespace sink 38 | { 39 | 40 | struct omit : boost::iostreams::sink 41 | { 42 | inline std::streamsize write(const char *s, std::streamsize n) 43 | { 44 | if (!_omit) std::clog.write(s, n); 45 | return n; 46 | } 47 | 48 | inline explicit omit(bool omit=false): _omit(omit) { } 49 | 50 | private: 51 | bool _omit; 52 | }; 53 | 54 | namespace fs = boost::filesystem; 55 | 56 | struct flushable_file : boost::noncopyable 57 | { 58 | typedef char char_type; 59 | 60 | struct category : boost::noncopyable, 61 | boost::iostreams::output_seekable, 62 | boost::iostreams::flushable_tag, 63 | boost::iostreams::closable_tag, 64 | boost::iostreams::device_tag { }; 65 | 66 | #define DEFAULT_MODE std::ios::trunc|std::ios::out 67 | inline flushable_file(const std::string& fpath, 68 | const std::ios_base::openmode m = DEFAULT_MODE) 69 | : _mode(m), _fpath(fpath) 70 | { 71 | _open(); 72 | } 73 | #undef DEFAULT_MODE 74 | 75 | inline flushable_file(const flushable_file& o) { _copy(o); } 76 | 77 | inline flushable_file& operator=(const flushable_file& o) 78 | { 79 | if (this != &o) _copy(o); 80 | return *this; 81 | } 82 | 83 | inline std::streampos seek(std::streamoff n, std::ios_base::seekdir s) 84 | { 85 | _s.seekp(n, s); 86 | _s.seekg(n, s); 87 | return n; 88 | } 89 | 90 | inline std::streamsize write(const char *s, std::streamsize n) 91 | { 92 | _s.write(s, n); 93 | return n; 94 | } 95 | 96 | inline std::streamsize read(char_type *t, std::streamsize n) 97 | { 98 | _s.read(t, n); 99 | return n; 100 | } 101 | 102 | inline bool is_open() const { return _s.is_open(); } 103 | 104 | inline bool flush() 105 | { 106 | _s.flush(); 107 | return true; 108 | } 109 | 110 | inline void close() 111 | { 112 | flush(); 113 | _s.close(); 114 | } 115 | 116 | private: 117 | inline void _copy(const flushable_file& o) 118 | { 119 | close(); 120 | _fpath = o._fpath; 121 | _mode = o._mode; 122 | _open(); 123 | } 124 | 125 | inline void _open() 126 | { 127 | _fpath = fs::system_complete(fs::path(_fpath)).string(); 128 | _s.open(_fpath.c_str(), _mode); 129 | if (_s.fail()) 130 | { 131 | BOOST_THROW_EXCEPTION(cc::error::fstream() 132 | << boost::errinfo_file_name(_fpath) 133 | << boost::errinfo_errno(errno)); 134 | } 135 | } 136 | 137 | private: 138 | std::ios_base::openmode _mode; 139 | std::string _fpath; 140 | std::fstream _s; 141 | }; 142 | 143 | } // namespace log 144 | 145 | } // namespace cc 146 | 147 | #endif 148 | 149 | // vim: set ts=2 sw=2 tw=72 expandtab: 150 | -------------------------------------------------------------------------------- /src/compat/query.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef compat_query_h 22 | #define compat_query_h 23 | 24 | namespace boost 25 | { 26 | namespace program_options 27 | { 28 | class variables_map; 29 | } 30 | } 31 | 32 | namespace po = boost::program_options; 33 | 34 | namespace quvi { 35 | 36 | class query_base 37 | { 38 | virtual std::string streams(const std::string&, const quvi::options&) const = 0; 39 | virtual media parse(const std::string&, const quvi::options&) const = 0; 40 | virtual std::map support() const = 0; 41 | virtual void _configure(const quvi::options&) const = 0; 42 | virtual void *setup_curl(const po::variables_map&) const = 0; 43 | virtual void _close() = 0; 44 | virtual void _init() = 0; 45 | protected: 46 | inline virtual ~query_base() { } 47 | }; 48 | 49 | class query_impl : query_base 50 | { 51 | protected: 52 | inline query_impl(const query_impl&): _quvi(0) {} 53 | inline query_impl(): _quvi(0) {} 54 | inline query_impl& operator=(const query_impl&) { return *this; } 55 | inline virtual ~query_impl() {} 56 | protected: 57 | quvi_t _quvi; 58 | }; 59 | 60 | class query_pt4 : public query_impl 61 | { 62 | void _configure(const quvi::options&) const; 63 | void _close(); 64 | void _init(); 65 | public: 66 | inline query_pt4(const query_pt4& a): query_impl(a) { _init(); } 67 | inline query_pt4(): query_impl() { _init(); } 68 | inline virtual ~query_pt4() { _close(); } 69 | inline query_pt4& operator=(const query_pt4& a) 70 | { 71 | if (this != &a) 72 | { 73 | _close(); 74 | _init(); 75 | } 76 | return *this; 77 | } 78 | public: 79 | std::string streams(const std::string&, const quvi::options&) const; 80 | media parse(const std::string&, const quvi::options&) const; 81 | std::map support() const; 82 | void *setup_curl(const po::variables_map&) const; 83 | }; 84 | 85 | class query_pt9 : public query_impl 86 | { 87 | void _configure(const quvi::options&) const; 88 | void _close(); 89 | void _init(); 90 | public: 91 | inline query_pt9(const query_pt9& a): query_impl(a) { _init(); } 92 | inline query_pt9(): query_impl() { _init(); } 93 | inline virtual ~query_pt9() { _close(); } 94 | inline query_pt9& operator=(const query_pt9& a) 95 | { 96 | if (this != &a) 97 | { 98 | _close(); 99 | _init(); 100 | } 101 | return *this; 102 | } 103 | public: 104 | std::string streams(const std::string&, const quvi::options&) const; 105 | media parse(const std::string&, const quvi::options&) const; 106 | std::map support() const; 107 | void *setup_curl(const po::variables_map&) const; 108 | }; 109 | 110 | #ifdef HAVE_LIBQUVI_0_9 111 | typedef class query_pt9 query; 112 | #else 113 | typedef class query_pt4 query; 114 | #endif 115 | 116 | } // namespace quvi 117 | 118 | #endif // compat_query_h 119 | 120 | // vim: set ts=2 sw=2 tw=72 expandtab: 121 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | 6 | AC_INIT([cclive], m4_esyscmd([./gen-ver.sh -c | tr -d '\n']), 7 | [http://cclive.sf.net/bugs/],[],[http://cclive.sf.net/]) 8 | 9 | AC_CONFIG_SRCDIR([src/cc/application.cpp]) 10 | AC_CONFIG_HEADERS([config.h]) 11 | AC_CONFIG_AUX_DIR([config.aux]) 12 | AC_CONFIG_MACRO_DIR([m4]) 13 | 14 | AC_CANONICAL_TARGET 15 | AC_USE_SYSTEM_EXTENSIONS 16 | 17 | AC_DEFINE_UNQUOTED([CANONICAL_TARGET], "$target", 18 | [Define to canonical target]) 19 | 20 | AM_INIT_AUTOMAKE([1.11.1 -Wall -Werror dist-xz no-dist-gzip tar-ustar]) 21 | AM_SILENT_RULES([yes]) 22 | 23 | # GNU Automake 1.12 requires this macro. Earlier versions do not 24 | # recognize this macro. Work around this. 25 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 26 | 27 | LT_INIT([disable-static]) 28 | LT_PREREQ([2.2.6]) 29 | 30 | # Checks for programs. 31 | AC_PROG_CXX 32 | AC_PROG_CC 33 | AC_PROG_CPP 34 | AC_PROG_LN_S 35 | 36 | AC_DEFINE_UNQUOTED([CXXFLAGS], "$CXXFLAGS", [Define to compiler flags]) 37 | AC_DEFINE_UNQUOTED([CXX], "$CXX", [Define to compiler]) 38 | 39 | AC_PATH_PROG([DATE], [date], [no]) 40 | AS_IF([test x"$DATE" != "xno"], [build_time=`$DATE +"%F %T %z"`]) 41 | AC_DEFINE_UNQUOTED([BUILD_TIME], ["$build_time"], [We have build time]) 42 | 43 | AC_PATH_PROG([A2X], [a2x], [no]) 44 | AM_CONDITIONAL([HAVE_A2X], [test x"$A2X" != "xno"]) 45 | AC_SUBST([A2X]) 46 | 47 | AS_IF([test x"$A2X" = "xno" && test -d "$srcdir/.git"], 48 | AC_MSG_ERROR([a2x is required to create man pages when building from git])]) 49 | 50 | # Checks for libraries. 51 | 52 | have_libquvi_0_9= 53 | PKG_CHECK_MODULES([libquvi], [libquvi-0.9 >= 0.9], 54 | [ 55 | AC_DEFINE([HAVE_LIBQUVI_0_9], [1], [We have libquvi 0.9]) 56 | have_libquvi_0_9=yes 57 | ], 58 | [PKG_CHECK_MODULES([libquvi], [libquvi >= 0.4])]) 59 | AM_CONDITIONAL([HAVE_LIBQUVI_0_9], [test x"$have_libquvi_0_9" = "xyes"]) 60 | 61 | PKG_CHECK_MODULES([libcurl], [libcurl >= 7.18.0]) 62 | PKG_CHECK_MODULES([libpcre], [libpcre >= 8.02]) 63 | PKG_CHECK_MODULES([libpcrecpp], [libpcrecpp >= 8.02]) 64 | PKG_CHECK_MODULES([glibmm], [glibmm-2.4 >= 2.24]) 65 | PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.24]) 66 | 67 | BOOST_REQUIRE([1.49.0]) 68 | # Boost: Header-only libs 69 | BOOST_FOREACH 70 | BOOST_FORMAT 71 | # Boost: Libs requiring linking 72 | BOOST_DATE_TIME 73 | BOOST_PROGRAM_OPTIONS 74 | BOOST_IOSTREAMS 75 | BOOST_FILESYSTEM 76 | BOOST_SYSTEM 77 | 78 | # Checks for header files. 79 | AC_CHECK_HEADERS([sys/ioctl.h unistd.h sys/types.h sys/stat.h signal.h]) 80 | 81 | # Checks for typedefs, structures, and compiler characteristics. 82 | AC_HEADER_STDBOOL 83 | AC_TYPE_SIZE_T 84 | AC_TYPE_PID_T 85 | AC_C_INLINE 86 | 87 | # Checks for library functions. 88 | AC_CHECK_FUNCS([strerror strstr getpid sigaction memset setlocale]) 89 | AC_FUNC_ERROR_AT_LINE 90 | AC_FUNC_STRERROR_R 91 | AC_FUNC_FORK 92 | 93 | # Version. 94 | VN=`$srcdir/gen-ver.sh` 95 | AC_DEFINE_UNQUOTED([VN],["$VN"], [We have version number from gen-ver.sh]) 96 | 97 | # --with-manual 98 | AC_ARG_WITH([manual], 99 | [AS_HELP_STRING([--with-manual], 100 | [Install manual page(s) @<:@default=yes@:>@])], 101 | [], 102 | [with_manual=yes]) 103 | AM_CONDITIONAL([WITH_MANUAL], [test x"$with_manual" != "xno"]) 104 | 105 | # --enable-ccl 106 | AC_ARG_ENABLE([ccl], 107 | [AS_HELP_STRING([--enable-ccl], 108 | [Symlink from 'cclive' executable to 'ccl' @<:@default=yes@:>@])], 109 | [], 110 | [enable_ccl=yes]) 111 | AM_CONDITIONAL([ENABLE_CCL], [test x"$enable_ccl" = "xyes"]) 112 | 113 | AC_CONFIG_FILES([ 114 | Makefile 115 | doc/Makefile 116 | doc/man1/Makefile 117 | src/Makefile 118 | ]) 119 | 120 | AC_OUTPUT 121 | 122 | AC_MSG_NOTICE([ 123 | version: ${VERSION} 124 | prefix: ${prefix} 125 | compiler: ${CXX} 126 | flags: ${CXXFLAGS} 127 | System types: 128 | target ${target} 129 | build ${build} 130 | host ${host} 131 | Options: 132 | add ccl symlink: ${enable_ccl} 133 | install manual : ${with_manual}]) 134 | 135 | # vim: set ts=2 sw=2 tw=72 expandtab: 136 | -------------------------------------------------------------------------------- /src/cc/fstream.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__fstream_h 22 | #define cc__fstream_h 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace cc 33 | { 34 | 35 | struct fstream; 36 | typedef boost::shared_ptr fstream_shptr; 37 | 38 | struct fstream : boost::noncopyable 39 | { 40 | inline fstream(const std::string& fpath, 41 | const std::ios_base::openmode& m=std::ofstream::binary) 42 | { 43 | _o = boost::shared_ptr(new std::ofstream(fpath.c_str(),m)); 44 | if (_o->fail()) 45 | { 46 | BOOST_THROW_EXCEPTION(cc::error::fstream() 47 | << boost::errinfo_file_name(fpath) 48 | << boost::errinfo_errno(errno)); 49 | } 50 | _fpath = fpath; 51 | } 52 | 53 | static inline fstream_shptr create(const std::string& fpath) 54 | { 55 | return fstream_shptr(new fstream(fpath, std::ofstream::trunc)); 56 | } 57 | 58 | static inline fstream_shptr append(const std::string& fpath) 59 | { 60 | return fstream_shptr(new fstream(fpath, std::ofstream::app)); 61 | } 62 | 63 | static inline fstream_shptr determine_mode(const std::string& fpath, 64 | uintmax_t& n) 65 | { 66 | boost::system::error_code ec; // Throw nothing. 67 | n = boost::filesystem::file_size(fpath, ec); 68 | return (n >0) ? append(fpath) : create(fpath); 69 | } 70 | 71 | static inline std::string read_ignore_errors(const std::string& fpath) 72 | { 73 | return read_contents(fpath, false); 74 | } 75 | 76 | static inline std::string read(const std::string& fpath) 77 | { 78 | return read_contents(fpath); 79 | } 80 | 81 | inline size_t write(const char *data, const size_t size) 82 | { 83 | _o->write(data, size); 84 | if (_o->fail()) 85 | { 86 | BOOST_THROW_EXCEPTION(cc::error::fstream() 87 | << boost::errinfo_file_name(_fpath) 88 | << boost::errinfo_errno(errno)); 89 | } 90 | _o->flush(); 91 | if (_o->fail()) 92 | { 93 | BOOST_THROW_EXCEPTION(cc::error::fstream() 94 | << boost::errinfo_file_name(_fpath) 95 | << boost::errinfo_errno(errno)); 96 | } 97 | return size; 98 | } 99 | 100 | private: 101 | static inline std::string read_contents(const std::string& fpath, 102 | const bool throws_if_fails=true) 103 | { 104 | const fs::path p = fs::system_complete(fpath); 105 | 106 | boost::system::error_code ec; 107 | if (fs::is_directory(p, ec)) 108 | { 109 | BOOST_THROW_EXCEPTION(cc::error::tuple() 110 | << cc::error::errinfo_tuple( 111 | boost::make_tuple(p.string(), "is a directory"))); 112 | } 113 | 114 | std::ifstream f(p.string().c_str()); 115 | std::string r; 116 | if (!f) 117 | { 118 | if (throws_if_fails) 119 | { 120 | BOOST_THROW_EXCEPTION(cc::error::fstream() 121 | << boost::errinfo_file_name(fpath) 122 | << boost::errinfo_errno(errno)); 123 | } 124 | return r; 125 | } 126 | std::copy(std::istreambuf_iterator(f), 127 | std::istreambuf_iterator(), 128 | std::back_inserter(r)); 129 | return r; 130 | } 131 | 132 | private: 133 | boost::shared_ptr _o; 134 | std::string _fpath; 135 | }; 136 | 137 | } // namespace cc 138 | 139 | #endif // cc__fstream_h 140 | 141 | // vim: set ts=2 sw=2 tw=72 expandtab: 142 | -------------------------------------------------------------------------------- /src/cc/input.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__input_h 22 | #define cc__input_h 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | namespace cc 37 | { 38 | 39 | namespace po = boost::program_options; 40 | 41 | struct input : boost::noncopyable 42 | { 43 | typedef std::vector vs; 44 | 45 | static inline vs parse(const po::variables_map& vm) 46 | { 47 | vs r; 48 | (vm.count("url") ==0) 49 | ? parse_without_rargs(vm,r) 50 | : parse_with_rargs(vm,r); 51 | return remove_duplicates(r); 52 | } 53 | 54 | private: 55 | template static T duplicates(T first, T last) 56 | { 57 | while (first != last) 58 | { 59 | T next(first); 60 | last = std::remove(++next, last, *first); 61 | first = next; 62 | } 63 | return last; 64 | } 65 | 66 | static inline const vs& remove_duplicates(vs& r) 67 | { 68 | r.erase(duplicates(r.begin(), r.end()), r.end()); 69 | if (r.size() ==0) 70 | BOOST_THROW_EXCEPTION(cc::error::no_input()); 71 | return r; 72 | } 73 | 74 | static inline const vs& parse_without_rargs(const po::variables_map& vm, 75 | vs& dst) 76 | { 77 | return extract_uris(dst, read_stdin()); 78 | } 79 | 80 | static inline const vs& parse_with_rargs(const po::variables_map& vm, 81 | vs& dst) 82 | { 83 | BOOST_FOREACH(const std::string& s, vm["url"].as()) 84 | determine_input(dst, s, true, "neither a valid URI or a local file"); 85 | return dst; 86 | } 87 | 88 | static inline const vs& extract_uris(vs& dst, const std::string& s) 89 | { 90 | gchar **r = g_uri_list_extract_uris(s.c_str()); 91 | for (int i=0; r[i] != NULL; ++i) 92 | { 93 | try 94 | { determine_input(dst, r[i], false, "an invalid URI"); } 95 | catch (...) 96 | { 97 | g_strfreev(r); 98 | throw; 99 | } 100 | } 101 | g_strfreev(r); 102 | return dst; 103 | } 104 | 105 | static inline const vs& read_from_uri(vs& dst, const std::string& s) 106 | { 107 | try 108 | { 109 | const std::string r = Glib::filename_from_uri(s); 110 | extract_uris(dst, cc::fstream::read(r)); 111 | } 112 | catch (const Glib::ConvertError& x) 113 | { 114 | BOOST_THROW_EXCEPTION(cc::error::tuple() 115 | << cc::error::errinfo_tuple(boost::make_tuple(s, x.what()))); 116 | } 117 | return dst; 118 | } 119 | 120 | static inline void determine_input(vs& dst, const std::string& s, 121 | const bool try_read_as_file, 122 | const std::string& emsg) 123 | { 124 | const std::string& c = Glib::uri_parse_scheme(s); 125 | if (c.length() ==0 && try_read_as_file) 126 | extract_uris(dst, cc::fstream::read(s)); 127 | else if (c == "http" || c == "https") 128 | dst.push_back(s); 129 | else if (c == "file") 130 | read_from_uri(dst, s); 131 | else 132 | { 133 | BOOST_THROW_EXCEPTION(cc::error::tuple() 134 | << cc::error::errinfo_tuple(boost::make_tuple(s, emsg))); 135 | } 136 | } 137 | 138 | static inline std::string read_stdin() 139 | { 140 | std::stringstream s; 141 | char c; 142 | while (std::cin.get(c)) 143 | s << c; 144 | return s.str(); 145 | } 146 | }; 147 | 148 | } // namespace cc 149 | 150 | #endif // cc__input_h 151 | 152 | // vim: set ts=2 sw=2 tw=72 expandtab: 153 | -------------------------------------------------------------------------------- /src/cc/re.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010,2011,2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | namespace cc 30 | { 31 | 32 | namespace re 33 | { 34 | 35 | static void chk_err(const pcrecpp::RE& re) 36 | { 37 | if (re.error().length() >0) 38 | { 39 | BOOST_THROW_EXCEPTION(cc::error::tuple() 40 | << cc::error::errinfo_tuple( 41 | boost::make_tuple(re.pattern(), re.error()))); 42 | } 43 | } 44 | 45 | static pcrecpp::RE_Options _init_re_opts(const std::string& flags) 46 | { 47 | pcrecpp::RE_Options opts; 48 | opts.set_caseless(strstr(flags.c_str(), "i") != 0); 49 | opts.set_utf8(true); 50 | return opts; 51 | } 52 | 53 | bool subst(const std::string& re, std::string& src) 54 | { 55 | std::string pat, sub, flags; 56 | 57 | static const char delims_b[] = "\\{\\<\\[\\(\\/"; 58 | static const char delims_c[] = "\\}\\>\\]\\)\\/"; 59 | 60 | boost::format fmt = 61 | boost::format("^s[%1%](.*)[%2%][%3%](.*)[%4%](.*)$") 62 | % delims_b % delims_c % delims_b % delims_c; 63 | 64 | pcrecpp::RE rx(fmt.str(), pcrecpp::UTF8()); 65 | 66 | if (rx.PartialMatch(re, &pat, &sub, &flags)) 67 | { 68 | if (src.empty()) // Verify regexp only. 69 | return true; 70 | 71 | pcrecpp::RE_Options opts = _init_re_opts(flags); 72 | pcrecpp::RE subs(pat, opts); 73 | chk_err(subs); 74 | 75 | (strstr(flags.c_str(), "g")) 76 | ? subs.GlobalReplace(sub, &src) 77 | : subs.Replace(sub, &src); 78 | 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | bool capture(const std::string& re, std::string& src) 85 | { 86 | std::string pat, flags; 87 | pcrecpp::RE rx("^\\/(.*)\\/(.*)$", pcrecpp::UTF8()); 88 | 89 | if (rx.PartialMatch(re, &pat, &flags)) 90 | { 91 | if (src.empty()) // Check regexp 92 | return true; 93 | 94 | pcrecpp::RE_Options opts = _init_re_opts(flags); 95 | if (strstr(flags.c_str(), "g") != 0) 96 | { 97 | std::string orig(src); 98 | pcrecpp::StringPiece sp(orig); 99 | pcrecpp::RE re(pat, opts); 100 | chk_err(re); 101 | src.clear(); 102 | 103 | std::string s; 104 | while (re.FindAndConsume(&sp, &s)) 105 | src += s; 106 | } 107 | else 108 | { 109 | std::string tmp = src; 110 | src.clear(); 111 | pcrecpp::RE re(pat, opts); 112 | chk_err(re); 113 | re.PartialMatch(tmp, &src); 114 | } 115 | return true; 116 | } 117 | return false; 118 | } 119 | 120 | bool grep(const std::string& r, const std::string& s) 121 | { 122 | pcrecpp::RE re(r, pcrecpp::UTF8()); 123 | chk_err(re); 124 | return re.PartialMatch(s); 125 | } 126 | 127 | static void tr_subst(const std::string& r, std::string& s) 128 | { 129 | static const char pattern[] = "^s\\/(.*)\\/(.*)\\/(.*)$"; 130 | 131 | pcrecpp::RE rx(pattern, pcrecpp::UTF8()); 132 | std::string pat, sub, flags; 133 | 134 | if (! rx.PartialMatch(r, &pat, &sub, &flags)) 135 | { 136 | const std::string& m = 137 | (boost::format("nothing matched `%1%'") % pattern).str(); 138 | 139 | BOOST_THROW_EXCEPTION(cc::error::tuple() 140 | << cc::error::errinfo_tuple(boost::make_tuple(r, m))); 141 | } 142 | 143 | if (s.empty()) // Validate regexp only. 144 | return; 145 | 146 | pcrecpp::RE_Options o = _init_re_opts(flags); 147 | pcrecpp::RE subs(pat, o); 148 | chk_err(subs); 149 | 150 | (strstr(flags.c_str(), "g")) 151 | ? subs.GlobalReplace(sub, &s) 152 | : subs.Replace(sub, &s); 153 | } 154 | 155 | static void tr_filter(const std::string& r, std::string& s) 156 | { 157 | static const char pattern[] = "^\\/(.*)\\/(.*)$"; 158 | 159 | pcrecpp::RE rx(pattern, pcrecpp::UTF8()); 160 | std::string pat, flags; 161 | 162 | if (! rx.PartialMatch(r, &pat, &flags)) 163 | { 164 | const std::string& m = 165 | (boost::format("nothing matched `%1%'") % pattern).str(); 166 | 167 | BOOST_THROW_EXCEPTION(cc::error::tuple() 168 | << cc::error::errinfo_tuple(boost::make_tuple(r, m))); 169 | } 170 | 171 | if (s.empty()) // Validate regexp only. 172 | return; 173 | 174 | pcrecpp::RE_Options o = _init_re_opts(flags); 175 | 176 | if (strstr(flags.c_str(), "g") != 0) 177 | { 178 | std::string orig(s); 179 | pcrecpp::StringPiece sp(orig); 180 | s.clear(); 181 | 182 | pcrecpp::RE re(pat, o); 183 | chk_err(re); 184 | std::string tmp; 185 | 186 | while (re.FindAndConsume(&sp, &tmp)) 187 | s += tmp; 188 | } 189 | else 190 | { 191 | std::string tmp = s; 192 | s.clear(); 193 | pcrecpp::RE re(pat, o); 194 | chk_err(re); 195 | re.PartialMatch(tmp, &s); 196 | } 197 | } 198 | 199 | void tr(const std::string& r, std::string& s) 200 | { 201 | if (pcrecpp::RE("^s\\/", pcrecpp::UTF8()).PartialMatch(r)) 202 | tr_subst(r, s); 203 | else 204 | tr_filter(r, s); 205 | } 206 | 207 | void trim(std::string& s) 208 | { 209 | tr_subst("s/^[\\s]+//", s); 210 | tr_subst("s/\\s+$//", s); 211 | tr_subst("s/\\s\\s+/ /g", s); 212 | } 213 | 214 | } // namespace re 215 | 216 | } // namespace cc 217 | 218 | // vim: set ts=2 sw=2 tw=72 expandtab: 219 | -------------------------------------------------------------------------------- /src/cc/application.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include // is_any_of 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | namespace cc 43 | { 44 | 45 | static void handle_fetch(const quvi_word type, void*) 46 | { 47 | #ifdef HAVE_LIBQUVI_0_9 48 | if (type == QUVI_CALLBACK_STATUS_DONE) 49 | #else 50 | if (type == QUVISTATUSTYPE_DONE) 51 | #endif 52 | cc::log << " "; 53 | } 54 | 55 | static void print_done() 56 | { 57 | cc::log << "done.\n"; 58 | } 59 | 60 | static void handle_verify(const quvi_word type) 61 | { 62 | #ifdef HAVE_LIBQUVI_0_9 63 | if (type == QUVI_CALLBACK_STATUS_DONE) 64 | #else 65 | if (type == QUVISTATUSTYPE_DONE) 66 | #endif 67 | print_done(); 68 | } 69 | 70 | static void handle_resolve(const quvi_word type) 71 | { 72 | #ifdef HAVE_LIBQUVI_0_9 73 | if (type == QUVI_CALLBACK_STATUS_DONE) 74 | #else 75 | if (type == QUVISTATUSTYPE_DONE) 76 | #endif 77 | cc::log << " "; 78 | } 79 | 80 | #ifdef HAVE_LIBQUVI_0_9 81 | static void status_callback_pt9(const quvi_word status, const quvi_word type, 82 | void *data, void *userdata) 83 | { 84 | cc::log << "."; 85 | switch (status) 86 | { 87 | case QUVI_CALLBACK_STATUS_FETCH: 88 | handle_fetch(type, data); 89 | break; 90 | case QUVI_CALLBACK_STATUS_HTTP_QUERY_METAINFO: 91 | handle_verify(type); 92 | break; 93 | case QUVI_CALLBACK_STATUS_RESOLVE: 94 | handle_resolve(type); 95 | break; 96 | } 97 | } 98 | #else 99 | static void status_callback_pt4(const quvi_word status, const quvi_word type, 100 | void *data) 101 | { 102 | cc::log << "."; 103 | switch (status) 104 | { 105 | case QUVISTATUS_FETCH: 106 | handle_fetch(type, data); 107 | break; 108 | case QUVISTATUS_VERIFY: 109 | handle_verify(type); 110 | break; 111 | case QUVISTATUS_RESOLVE: 112 | handle_resolve(type); 113 | break; 114 | } 115 | } 116 | #endif 117 | 118 | #ifdef HAVE_LIBQUVI_0_9 119 | static int status_callback(long status_type, void *data, void *userdata) 120 | #else 121 | static int status_callback(long status_type, void *data) 122 | #endif 123 | { 124 | const quvi_word status = quvi_loword(status_type); 125 | const quvi_word type = quvi_hiword(status_type); 126 | 127 | #ifdef HAVE_LIBQUVI_0_9 128 | status_callback_pt9(status, type, data, userdata); 129 | #else 130 | status_callback_pt4(status, type, data); 131 | #endif 132 | cc::log << std::flush; 133 | 134 | return QUVI_OK; 135 | } 136 | 137 | static void print_retrying(const int retry, 138 | const int max_retries, 139 | const int retry_wait) 140 | { 141 | if (retry > 0) 142 | { 143 | cc::log 144 | << "Retrying " 145 | << retry 146 | << " of " 147 | << max_retries 148 | << " ... " 149 | << std::flush; 150 | 151 | cc::wait(retry_wait); 152 | } 153 | } 154 | 155 | static void print_checking(const int i, const int n) 156 | { 157 | if (n > 1) cc::log << "(" << i << " of " << n << ") "; 158 | cc::log << "Checking ... " << std::flush; 159 | } 160 | 161 | static void print_quvi_error(const quvi::error& e) 162 | { 163 | cc::log << "libquvi: error: " << e.what() << std::endl; 164 | } 165 | 166 | namespace po = boost::program_options; 167 | typedef std::vector vs; 168 | 169 | static std::string format_streams(const std::string& s) 170 | { 171 | vs v; 172 | boost::split(v, s, boost::is_any_of("|,")); 173 | const size_t m = v.size(); 174 | 175 | if (m ==0) 176 | v.push_back("default"); 177 | 178 | std::stringstream r; 179 | r << "\n"; 180 | 181 | size_t i = 0, c = 0; 182 | BOOST_FOREACH(const std::string& a, v) 183 | { 184 | r << boost::format("%|22s|") % a; 185 | ++c; 186 | if (++i ==3) 187 | { 188 | if (c (); 228 | BOOST_FOREACH(const std::string& s, va) 229 | { 230 | static vs vb; 231 | boost::split(vb, s, boost::is_any_of(":")); 232 | if (vb.size() == 2) // vb[0]=pattern, vb[1]=format 233 | { 234 | if (cc::re::grep(vb[0], url)) 235 | { 236 | fmt = vb[1]; 237 | return; 238 | } 239 | } 240 | vb.clear(); 241 | } 242 | } 243 | 244 | static void set_stream(const std::string& url, quvi::options& qopts, 245 | const po::variables_map& vm) 246 | { 247 | std::string r = vm[OPT__STREAM].as(); 248 | if (r == "default" && vm.count(OPT__PREFER_FORMAT)) 249 | parse_prefer_format(url, r, vm); 250 | qopts.stream = r; 251 | } 252 | 253 | application::exit_status application::exec(int const argc, char const **argv) 254 | { 255 | const boost::scoped_ptr o(new cc::options(argc, argv)); 256 | const po::variables_map vm = o->values(); 257 | 258 | // Parse input. 259 | 260 | const vs& input_urls = cc::input::parse(vm); 261 | const size_t n = input_urls.size(); 262 | 263 | // Set up quvi. 264 | 265 | quvi::query query; // Throws quvi::error caught in main.cpp 266 | 267 | quvi::options qopts; 268 | qopts.useragent = vm[OPT__AGENT].as(); /* libquvi 0.9+ */ 269 | qopts.resolve = ! vm[OPT__NO_RESOLVE].as(); 270 | qopts.statusfunc = status_callback; 271 | 272 | // Omit flag. 273 | 274 | bool omit = vm[OPT__QUIET].as(); 275 | 276 | // Go to background. 277 | 278 | #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) 279 | const bool background_given = vm[OPT__BACKGROUND].as(); 280 | if (background_given) 281 | cc::go_background(vm[OPT__LOG_FILE].as(), omit); 282 | #endif 283 | 284 | // Omit std output. Note that --background flips this above. 285 | 286 | cc::log.push(cc::sink::omit(omit)); 287 | cc::log.setf(std::ios::fixed); 288 | 289 | // Print streams. 290 | 291 | if_optsw_given(vm, OPT__PRINT_STREAMS) 292 | return print_streams(query, qopts, input_urls, vm); 293 | 294 | #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) 295 | #ifdef HAVE_GETPID 296 | if (background_given) 297 | { 298 | cc::log 299 | << "Running in background (pid: " 300 | << static_cast(getpid()) 301 | << ")." 302 | << std::endl; 303 | } 304 | #endif 305 | #endif 306 | 307 | // Setup signal handlers. 308 | 309 | cc::sigwinch_handler_scptr sw(new cc::type_sigwinch); 310 | cc::sigusr1_handler_scptr su(new cc::type_sigusr1); 311 | 312 | // For each input URL. 313 | 314 | size_t i = 0; 315 | 316 | const int max_retries = vm[OPT__MAX_RETRIES].as().value(); 317 | const int retry_wait = vm[OPT__RETRY_WAIT].as().value(); 318 | 319 | exit_status es = ok; 320 | 321 | BOOST_FOREACH(const std::string& url, input_urls) 322 | { 323 | ++i; 324 | 325 | try 326 | { 327 | int retry = 0; 328 | 329 | while (retry <= max_retries) 330 | { 331 | print_retrying(retry, max_retries, retry_wait); 332 | ++retry; 333 | 334 | print_checking(i, n); 335 | quvi::media m; 336 | 337 | try 338 | { 339 | set_stream(url, qopts, vm); 340 | _curl = query.setup_curl(vm); 341 | m = query.parse(url, qopts); 342 | } 343 | catch(const quvi::error& e) 344 | { 345 | if (e.cannot_retry()) 346 | throw e; 347 | else 348 | print_quvi_error(e); 349 | } 350 | 351 | cc::get(m, _curl, vm); 352 | break; // Stop retrying. 353 | } 354 | es = ok; 355 | } 356 | 357 | catch(const quvi::error& e) 358 | { 359 | print_quvi_error(e); 360 | es = application::error; 361 | } 362 | 363 | catch(const std::runtime_error& e) 364 | { 365 | cc::log << "error: " << e.what() << std::endl; 366 | es = application::error; 367 | } 368 | } 369 | return es; 370 | } 371 | 372 | } // namespace cc 373 | 374 | // vim: set ts=2 sw=2 tw=72 expandtab: 375 | -------------------------------------------------------------------------------- /src/cc/file.cpp: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2010-2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef HAVE_UNISTD_H 28 | #include 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | namespace cc 48 | { 49 | 50 | namespace po = boost::program_options; 51 | 52 | file::file(const quvi::media& media, const po::variables_map& vm) 53 | : _initial_length(0), _nothing_todo(false) 54 | { 55 | try 56 | { 57 | _init(media, vm); 58 | } 59 | catch (const cc::nothing_todo&) 60 | { 61 | _nothing_todo = true; 62 | } 63 | } 64 | 65 | #define E "server response code %ld, expected 200 or 206 (conn_code=%ld)" 66 | 67 | static std::string format_unexpected_http_error( 68 | const long resp_code, 69 | const long conn_code) 70 | { 71 | return (boost::format(E) % resp_code % conn_code).str(); 72 | } 73 | 74 | #undef E 75 | 76 | #define E "%s (curl_code=%ld, resp_code=%ld, conn_code=%ld)" 77 | 78 | static std::string format_error(const CURLcode curl_code, 79 | const long resp_code, 80 | const long conn_code) 81 | { 82 | const std::string e = curl_easy_strerror(curl_code); 83 | return (boost::format(E) % e % curl_code % resp_code % conn_code).str(); 84 | } 85 | 86 | #undef E 87 | 88 | static std::string io_error(const std::string& fpath) 89 | { 90 | std::string s = fpath + ": "; 91 | if (errno) 92 | s += cc::perror(); 93 | else 94 | s += "unknown i/o error"; 95 | return (s); 96 | } 97 | 98 | static std::string io_error(const cc::file& f) 99 | { 100 | return io_error(f.path()); 101 | } 102 | 103 | class write_data 104 | { 105 | public: 106 | inline write_data(cc::file *f, const po::variables_map& vm) 107 | : vm(vm), o(NULL), f(f) { } 108 | inline ~write_data() 109 | { 110 | if (o == NULL) 111 | return; 112 | 113 | o->flush(); 114 | o->close(); 115 | 116 | delete o; 117 | o = NULL; 118 | } 119 | inline void open_file() 120 | { 121 | std::ios_base::openmode mode = std::ofstream::binary; 122 | 123 | if_optsw_given(vm, OPT__OVERWRITE) 124 | mode |= std::ofstream::trunc; 125 | else 126 | { 127 | if (f->should_continue()) 128 | mode |= std::ofstream::app; 129 | } 130 | 131 | o = new std::ofstream(f->path().c_str(), mode); 132 | if (o->fail()) 133 | throw std::runtime_error(io_error(*f)); 134 | } 135 | public: 136 | po::variables_map vm; 137 | std::ofstream *o; 138 | cc::file *f; 139 | }; 140 | 141 | static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata) 142 | { 143 | write_data *w = reinterpret_cast(userdata); 144 | const size_t rsize = size*nmemb; 145 | 146 | w->o->write(static_cast(ptr), rsize); 147 | if (w->o->fail()) 148 | return w->f->set_errmsg(io_error(*w->f)); 149 | 150 | w->o->flush(); 151 | if (w->o->fail()) 152 | return w->f->set_errmsg(io_error(*w->f)); 153 | 154 | return rsize; 155 | } 156 | 157 | static int progress_cb(void *ptr, double, double now, double, double) 158 | { 159 | if (cc::var::recv_sigusr1) 160 | { 161 | cc::var::recv_sigusr1 = 0; 162 | return 1; // Return a non-zero value to abort this transfer. 163 | } 164 | return reinterpret_cast(ptr)->update(now); 165 | } 166 | 167 | static void _set(write_data *w, const quvi::media& m, CURL *c, 168 | cc::progressbar *pb, const double initial_length, 169 | const po::variables_map& vm) 170 | { 171 | curl_easy_setopt(c, CURLOPT_URL, m.stream_url().c_str()); 172 | 173 | curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_cb); 174 | curl_easy_setopt(c, CURLOPT_WRITEDATA, w); 175 | 176 | curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, progress_cb); 177 | curl_easy_setopt(c, CURLOPT_PROGRESSDATA, pb); 178 | curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0L); 179 | 180 | curl_easy_setopt(c, CURLOPT_ENCODING, "identity"); 181 | curl_easy_setopt(c, CURLOPT_HEADER, 0L); 182 | 183 | if_optsw_given(vm, OPT__TIMESTAMP) 184 | curl_easy_setopt(c, CURLOPT_FILETIME, 1L); 185 | 186 | curl_easy_setopt(c, CURLOPT_RESUME_FROM_LARGE, 187 | static_cast(initial_length)); 188 | } 189 | 190 | static void _restore(CURL *c) 191 | { 192 | curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, NULL); 193 | curl_easy_setopt(c, CURLOPT_ENCODING, ""); 194 | 195 | curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, NULL); 196 | curl_easy_setopt(c, CURLOPT_PROGRESSDATA, NULL); 197 | curl_easy_setopt(c, CURLOPT_NOPROGRESS, 1L); 198 | 199 | curl_easy_setopt(c, CURLOPT_RESUME_FROM_LARGE, 0L); 200 | } 201 | 202 | namespace fs = boost::filesystem; 203 | 204 | bool file::write(const quvi::media& m, CURL *curl, 205 | const po::variables_map& vm) const 206 | { 207 | write_data w(const_cast(this), vm); 208 | w.open_file(); 209 | 210 | cc::progressbar *pb = new cc::progressbar(vm, m, _initial_length); 211 | _set(&w, m, curl, pb, _initial_length, vm); 212 | 213 | boost::scoped_ptr sc_pb(pb); 214 | 215 | const CURLcode rc = curl_easy_perform(curl); 216 | _restore(curl); 217 | 218 | // Restore curl settings. 219 | 220 | curl_easy_setopt(curl, CURLOPT_HEADER, 1L); 221 | curl_easy_setopt(curl, CURLOPT_FILETIME, 0L); 222 | curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); 223 | curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, 0L); 224 | curl_easy_setopt(curl, 225 | CURLOPT_MAX_RECV_SPEED_LARGE, 226 | static_cast(0L)); 227 | 228 | long resp_code = 0; 229 | long conn_code = 0; 230 | 231 | curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE, &conn_code); 232 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp_code); 233 | 234 | std::string error; 235 | 236 | if (CURLE_OK == rc) 237 | { 238 | if (resp_code != 200 && resp_code != 206) 239 | error = format_unexpected_http_error(resp_code, conn_code); 240 | } 241 | else 242 | { 243 | if (CURLE_WRITE_ERROR == rc) // write_cb returned != rsize 244 | error = _errmsg; 245 | else 246 | error = format_error(rc, resp_code, conn_code); 247 | } 248 | 249 | if (!error.empty()) 250 | return pb->print_error(resp_code, rc, error); 251 | 252 | if_optsw_given(vm, OPT__TIMESTAMP) 253 | { 254 | long ft = -1; 255 | curl_easy_getinfo(curl, CURLINFO_FILETIME, &ft); 256 | if (ft >=0) 257 | fs::last_write_time(_path, ft); 258 | } 259 | 260 | return true; 261 | } 262 | 263 | static double to_mb(const double bytes) 264 | { 265 | return bytes/(1024*1024); 266 | } 267 | 268 | std::string file::to_s(const quvi::media& m) const 269 | { 270 | const double length = to_mb(m.content_length()); 271 | 272 | boost::format fmt = boost::format("%s %.2fM [%s]") 273 | % _name % length % m.content_type(); 274 | 275 | return fmt.str(); 276 | } 277 | 278 | static fs::path output_fpath(const po::variables_map& vm, 279 | const std::string& fname) 280 | { 281 | const fs::path& r = 282 | fs::system_complete(vm[OPT__OUTPUT_DIR].as()); 283 | return r / fname; 284 | } 285 | 286 | void file::_init(const quvi::media& media, const po::variables_map& vm) 287 | { 288 | _title = media.title(); 289 | 290 | // NOTE: output-file overrides the filename-format. 291 | 292 | if (vm.count(OPT__OUTPUT_FILE)) 293 | { 294 | const std::string& ofname = vm[OPT__OUTPUT_FILE].as(); 295 | store_path(output_fpath(vm, ofname)); 296 | 297 | _initial_length = cc::file::exists(_path); 298 | 299 | if ( _initial_length >= media.content_length() 300 | && ! vm[OPT__OVERWRITE].as()) 301 | { 302 | BOOST_THROW_EXCEPTION(cc::nothing_todo()); 303 | } 304 | } 305 | else // Use filename-format. 306 | { 307 | std::string fname_fmt = vm[OPT__FILENAME_FORMAT].as(); 308 | pcrecpp::RE("%s").GlobalReplace(media.file_ext(), &fname_fmt); 309 | pcrecpp::RE("%i").GlobalReplace(media.id(), &fname_fmt); 310 | 311 | // Cleanup media title before applying it to the filename-format. 312 | 313 | const cc::vtr& tr = vm[OPT__TR].as(); 314 | std::string s = media.title(); 315 | 316 | BOOST_FOREACH(const cc::tr& t, tr) 317 | cc::re::tr(t.str(), s); 318 | cc::re::trim(s); 319 | 320 | pcrecpp::RE("%t").GlobalReplace(s, &fname_fmt); 321 | 322 | // output-dir 323 | 324 | const fs::path& base_fpath = output_fpath(vm, fname_fmt); 325 | store_path(base_fpath); 326 | 327 | ifn_optsw_given(vm, OPT__OVERWRITE) 328 | { 329 | for (int i=0; i=media.content_length()) 336 | BOOST_THROW_EXCEPTION(cc::nothing_todo()); 337 | else 338 | { 339 | if_optsw_given(vm, OPT__CONTINUE) 340 | break; // Try to resume the transfer. 341 | } 342 | 343 | // Append a digit to the (base) file name. 344 | 345 | const std::string& s = 346 | (boost::format("%1%.%2%") % base_fpath.string() % i).str(); 347 | 348 | store_path(fs::system_complete(s)); 349 | } 350 | } 351 | } 352 | 353 | if_optsw_given(vm, OPT__OVERWRITE) 354 | _initial_length = 0; 355 | } 356 | 357 | } // namespace cc 358 | 359 | // vim: set ts=2 sw=2 tw=72 expandtab: 360 | -------------------------------------------------------------------------------- /src/cc/progressbar.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu 3 | * 4 | * This file is part of cclive . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * . 19 | */ 20 | 21 | #ifndef cc__progressbar_h 22 | #define cc__progressbar_h 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | namespace cc 33 | { 34 | 35 | static inline std::string ieee_1541_symbol(double& n) 36 | { 37 | typedef enum {Ki=1024, Mi=1048576, Gi=1073741824} ieee_1541; 38 | int i = 0; 39 | if (n >=Gi) 40 | { 41 | n /= Gi; 42 | i = 2; 43 | } 44 | else if (n >=Mi) 45 | { 46 | n /= Mi; 47 | i = 1; 48 | } 49 | else 50 | n /= Ki; 51 | #define _s(n) #n 52 | static const char *r[] = {_s(Ki), _s(Mi), _s(Gi), NULL}; 53 | #undef _s 54 | return r[i]; 55 | } 56 | 57 | namespace po = boost::program_options; 58 | namespace pt = boost::posix_time; 59 | 60 | struct progressbar : boost::noncopyable 61 | { 62 | progressbar(const po::variables_map& vm, const quvi::media& qm, 63 | const double initial_length) 64 | { 65 | _length_bytes.content = qm.content_length(); 66 | _length_bytes.initial = initial_length; 67 | 68 | _cntrs.time_started = pt::microsec_clock::universal_time(); 69 | _cntrs.bytes_received = 0; 70 | _cntrs.num_dots = 0; 71 | 72 | _flags.failed = false; 73 | _flags.done = false; 74 | 75 | _opts.update_interval = 76 | vm[OPT__UPDATE_INTERVAL].as().value(); 77 | 78 | ifn_optsw_given(vm, OPT__BACKGROUND) 79 | _opts.mode = vm[OPT__PROGRESSBAR].as().value(); 80 | else 81 | _opts.mode = cc::progressbar_mode::dotline; 82 | } 83 | 84 | inline virtual ~progressbar() { finish(); } 85 | 86 | inline int update(double dlnow) 87 | { 88 | if (dlnow ==0 || _flags.failed) 89 | return 0; 90 | 91 | const pt::time_duration& td = 92 | pt::microsec_clock::universal_time() - _cntrs.time_started; 93 | 94 | if (_flags.done) 95 | dlnow = _length_bytes.content; 96 | else 97 | { 98 | const double diff = 99 | td.total_microseconds() - _cntrs.last_update.total_microseconds(); 100 | 101 | if (diff < (_opts.update_interval*1e+6)) 102 | return 0; 103 | } 104 | 105 | double elapsed = td.total_seconds(); 106 | if (elapsed ==0) 107 | elapsed = 1e+4; 108 | 109 | const bool inactive = (dlnow ==0); 110 | double rate = (elapsed >0) ? (dlnow/elapsed):0; 111 | 112 | std::string eta, rate_symbol; 113 | if (! inactive) 114 | { 115 | if (! _flags.done) 116 | { 117 | const double left = 118 | (_length_bytes.content - (dlnow + _length_bytes.initial)) /rate; 119 | eta = eta_from_seconds(left+.5); 120 | } 121 | else 122 | { 123 | rate = (_length_bytes.content - _length_bytes.initial) / elapsed; 124 | eta = eta_from_seconds(elapsed); 125 | } 126 | rate_symbol = ieee_1541_symbol(rate); 127 | } 128 | else 129 | { 130 | rate_symbol = "--.-"; 131 | eta = "--:--"; 132 | } 133 | 134 | double size = dlnow; 135 | if (! _flags.done) 136 | size += _length_bytes.initial; 137 | 138 | int percent = 0; 139 | if (_length_bytes.content >0) 140 | { 141 | percent = (100.0 * size / _length_bytes.content); 142 | if (percent >= 100) 143 | percent = 100; 144 | } 145 | 146 | _cntrs.bytes_received = dlnow; 147 | _cntrs.last_update = td; 148 | 149 | switch (_opts.mode) 150 | { 151 | case cc::progressbar_mode::dotline: 152 | return render_dotline(rate_symbol, eta, percent, rate, size); 153 | case cc::progressbar_mode::simple: 154 | return render_simple(percent, size); 155 | case cc::progressbar_mode::normal: 156 | default: 157 | return render_normal(rate_symbol, eta, percent, rate, size); 158 | } 159 | } 160 | 161 | inline bool print_error(const long resp_code, const CURLcode rc, 162 | std::string& errmsg) 163 | { 164 | _flags.failed = true; 165 | 166 | // Do not retry if this is an "unrecoverable" error. 167 | if (resp_code >= 400 && resp_code <= 500) 168 | { 169 | clear_line(errmsg); 170 | BOOST_THROW_EXCEPTION(std::runtime_error(errmsg)); 171 | } 172 | 173 | bool r = false; // Retry by default. 174 | if (rc == 42) // 42=Operation aborted by callback (libcurl). 175 | { 176 | errmsg = "sigusr1 received: interrupt current download"; 177 | r = true; // Skip - do not attempt to retry. 178 | } 179 | 180 | cc::log << clear_line((boost::format("error: %s") % errmsg).str()) 181 | << std::endl; 182 | 183 | return r; 184 | } 185 | 186 | private: 187 | inline void finish() 188 | { 189 | if (_length_bytes.content >0 190 | && ((_cntrs.bytes_received + _length_bytes.initial) 191 | >_length_bytes.content)) 192 | { 193 | _length_bytes.content = _length_bytes.initial + _cntrs.bytes_received; 194 | } 195 | 196 | if (! _flags.failed) 197 | { 198 | _flags.done = true; 199 | update(-1); 200 | } 201 | } 202 | 203 | inline void dotline_render_dot() const 204 | { cc::log << "." << ( (_cntrs.num_dots%3 ==0) ? " ":"") << std::flush; } 205 | 206 | inline void dotline_render_info(const std::string& rate_symbol, 207 | const std::string& eta, 208 | const double percent, 209 | const double rate, 210 | double size, 211 | const bool nl=false) const 212 | { 213 | const std::string& size_symbol = ieee_1541_symbol(size); 214 | 215 | const std::string& s = 216 | (boost::format(" %6.1f%s %6.1f%s/s %4s %3.0f%%%s") 217 | % size 218 | % size_symbol 219 | % rate 220 | % rate_symbol 221 | % eta 222 | % percent 223 | % (nl ? "\n":"") 224 | ).str(); 225 | 226 | cc::log << s << std::flush; 227 | } 228 | 229 | inline int render_dotline(const std::string& rate_symbol, 230 | const std::string& eta, 231 | const double percent, 232 | const double rate, 233 | double size) 234 | { 235 | ++_cntrs.num_dots; 236 | 237 | if (_flags.done) 238 | { 239 | while (_cntrs.num_dots < 31) 240 | { 241 | dotline_render_dot(); 242 | ++_cntrs.num_dots; 243 | } 244 | dotline_render_info(rate_symbol, eta, percent, rate, size); 245 | return 0; 246 | } 247 | 248 | if (_cntrs.num_dots >= 31) 249 | { 250 | dotline_render_info(rate_symbol, eta, percent, rate, size, true); 251 | _cntrs.num_dots = 0; 252 | } 253 | else 254 | dotline_render_dot(); 255 | 256 | return 0; 257 | } 258 | 259 | inline int render_simple(const double percent, double size) const 260 | { 261 | const std::string& size_symbol = ieee_1541_symbol(size); 262 | 263 | const std::string& s = 264 | (boost::format(" %3.0f%% %6.1f%s received\r") 265 | % percent 266 | % size 267 | % size_symbol 268 | ).str(); 269 | 270 | cc::log << s << std::flush; 271 | return 0; 272 | } 273 | 274 | inline int render_normal(const std::string& rate_symbol, 275 | const std::string& eta, 276 | const double percent, 277 | const double rate, 278 | double size) const 279 | { 280 | const std::string& size_symbol = ieee_1541_symbol(size); 281 | 282 | const std::string& s = 283 | (boost::format(" %3.0f%% %6.1f%s %6.1f%s/s %4s") 284 | % percent 285 | % size 286 | % size_symbol 287 | % rate 288 | % rate_symbol 289 | % eta 290 | ).str(); 291 | 292 | const long l = s.length(); 293 | size_t term_width; 294 | 295 | const size_t space_left = cc::sig::calc_term_spaceleft(l, term_width); 296 | if (term_width > space_left) 297 | { 298 | cc::log << render_meter(percent, space_left) 299 | << s << (_flags.done ? "\n":"\r") 300 | << std::flush; 301 | } 302 | return 0; 303 | } 304 | 305 | static inline std::string render_meter(const double percent, 306 | const long space_left) 307 | { 308 | std::ostringstream o; 309 | o << ("["); 310 | const int m = static_cast(space_left * percent / 100.0); 311 | for (int i=0; (o.tellp() < space_left); ++i) 312 | o << (i0 if a resumed transfer 338 | } _length_bytes; 339 | struct 340 | { 341 | cc::progressbar_mode::mode_type mode; 342 | int update_interval; 343 | } _opts; 344 | struct 345 | { 346 | pt::time_duration last_update; 347 | pt::ptime time_started; 348 | double bytes_received; 349 | int num_dots; 350 | } _cntrs; 351 | struct 352 | { 353 | bool failed; 354 | bool done; 355 | } _flags; 356 | }; 357 | 358 | } // namespace cc 359 | 360 | #endif // cc__progressbar_h 361 | 362 | // vim: set ts=2 sw=2 tw=72 expandtab: 363 | -------------------------------------------------------------------------------- /doc/man1/cclive.1.txt: -------------------------------------------------------------------------------- 1 | cclive(1) 2 | ========= 3 | 4 | NAME 5 | ---- 6 | cclive - media stream extraction tool 7 | 8 | SYNOPSIS 9 | -------- 10 | [verse] 11 | 'cclive' [OPTIONS] [URL|FILE ...] 12 | 13 | DESCRIPTION 14 | ----------- 15 | cclive is a command line tool for downloading media streams from YouTube 16 | and similar websites. 17 | 18 | INPUT 19 | ----- 20 | The command will read stdin by default. The input is expected to contain 21 | URLs. The command arguments are expected to be either URLs or file 22 | paths. If the input is read from either stdin or a file, the contents 23 | are read as RFC2483. The input may also contain URIs (file://) to local 24 | files. 25 | 26 | OPTIONS 27 | ------- 28 | 29 | Core 30 | ~~~~ 31 | 32 | -b, --background:: 33 | Go to background after startup. The output written to stdout 34 | will be written to the file specified with --log-file. 35 | 36 | -F, --config-file :: 37 | Read the program arguments from the specified file instead of the 38 | default ~/.ccliverc file. See also <>. 39 | 40 | -c, --continue:: 41 | Resume partially downloaded media. 42 | ---- 43 | config: 44 | continue = {true|false} 45 | ---- 46 | 47 | -e, --exec :: 48 | Invoke the specified command after each successfully finished 49 | download. The command is also invoked if the media is fully 50 | retrieved already. This option may be specified multiple times. 51 | See also <>. 52 | + 53 | All occurences of the following sequences will be replaced 54 | in the 'arg': 55 | ---- 56 | %f .. Full path to the downloaded media file 57 | %n .. Name of the downloaded media file 58 | %t .. Unfiltered media title 59 | 60 | config: 61 | exec = arg 62 | ---- 63 | 64 | -n, --no-download:: 65 | Do not download the media, print the details only. 66 | 67 | -s, --stream :: 68 | Download the specified media stream. By default the program will get 69 | the 'default' stream. The stream selection with linkman:libquvi[3] 0.4 70 | is basic at most, whereas linkman:libquvi[3] 0.9+ provides a more 71 | advanced facility for this. 72 | + 73 | linkman:libquvi[3] 0.9+ treats the ID value as a regex PATTERN and 74 | matches it against the linkman:libquvi-scripts[7] returned media 75 | stream IDs. Additionally, the value of ID may be a 'comma-separated 76 | list' of regex PATTERNs. 77 | + 78 | In comparison, linkman:libquvi[3] 0.4 only checks whether the strings 79 | (the value of ID and the returned media stream ID) are equal. 80 | + 81 | The ID value may also contain the following reserved keywords: 82 | + 83 | - 'croak' - tell linkman:libquvi[3] (0.9+) to exit with an error 84 | when reached 85 | - 'best' - tell linkman:libquvi[3] to choose the best quality stream 86 | 87 | + 88 | The 'best' quality is determined by linkman:libquvi-scripts[7]. The 89 | method varies, depending on the linkman:libquvi[3] version and the 90 | website. 91 | + 92 | For example, linkman:libquvi-scripts[7] 0.9+ would typically compare the 93 | media quality properties (e.g. the video height property). In reality, 94 | the method will vary depending on the data provided by the media hosting 95 | service, and the implementation of the support script that was written 96 | for the website. 97 | + 98 | See the --version output to confirm whether cclive was built with 99 | linkman:libquvi[3] 0.9 or later. See also <>. 100 | ---- 101 | config: 102 | stream = arg 103 | ---- 104 | 105 | -N, --timestamp:: 106 | Try to preserve the file modification time (as returned by the server, 107 | if any). Using this option will cause the program to change the 108 | modification time of the file to that of returned by the server. 109 | + 110 | NOTE: libcurl will parse this value from the returned "Last-Modified" 111 | HTTP header. This header may not always be present in which case 112 | the program will quietly ignore this option. 113 | ---- 114 | config: 115 | timestamp = {true|false} 116 | ---- 117 | 118 | Informative 119 | ~~~~~~~~~~~ 120 | 121 | -h, --help:: 122 | Print help and exit. 123 | 124 | -S, --print-streams:: 125 | Print the available media streams. The printed values (media stream 126 | IDs) may be used with --stream and --prefer-format options. The 127 | available streams are determined by linkman:libquvi[3] and 128 | linkman:libquvi-scripts[7]. 129 | 130 | -D, --print-config:: 131 | Print the value of all defined configuration options to stdout. 132 | 133 | -u, --support:: 134 | Print the supported website domains and exit. 135 | 136 | -v, --version:: 137 | Print the program version and exit. 138 | 139 | Output 140 | ~~~~~~ 141 | 142 | -f, --filename-format :: 143 | Specify how the downloaded media file should be named. All occurences 144 | of the following sequences will be replaced in the 'arg': 145 | ---- 146 | %t .. Media title (after applying --tr) 147 | %s .. Media file extension 148 | %i .. Media ID 149 | ---- 150 | The default value is "%t.%s". 151 | ---- 152 | config: 153 | filename-format = arg 154 | ---- 155 | 156 | -l, --log-file :: 157 | Write log output to the specified file. The program will ignore this 158 | option value unless it is being used together with --background. The 159 | existing log file will be ovewritten. By default, the program will use 160 | "cclive_log" as the log file name. 161 | 162 | -d, --output-dir :: 163 | Write downloaded media to the specified directory. By default, the 164 | program will write the media to the current working directory. 165 | ---- 166 | config: 167 | output-dir = arg 168 | ---- 169 | 170 | -O, --output-file :: 171 | Write media to the specified file. Overrides --filename-format. 172 | 173 | -W, --overwrite:: 174 | Overwrite existing media files. 175 | 176 | -R, --progressbar :: 177 | Use the progressbar of the specified type. The program will ignore 178 | the value of this option with --background. The arg may be one of the 179 | following values: 180 | * dotline (also implied by --background) 181 | * normal (default) 182 | * simple 183 | ---- 184 | config: 185 | progressbar = arg 186 | ---- 187 | 188 | -q, --quiet:: 189 | Turn off all output to stdout and stderr with the exception of 190 | --verbose-libcurl. 191 | 192 | -t, --tr :: 193 | Specify to translate the characters in the media titles before they 194 | are used in the media file names. The 'arg' is a regular expression 195 | pattern. The default value is "/(\w|\s)/g". This option may be 196 | specified multiple times. See also <>. 197 | ---- 198 | config: 199 | tr = arg 200 | ---- 201 | 202 | -i, --update-interval :: 203 | Specify the progressbar update interval in seconds. The default value 204 | is 1. 205 | ---- 206 | config: 207 | update-interval = arg 208 | ---- 209 | 210 | -B, --verbose-libcurl:: 211 | Enable libcURL verbose output. 212 | 213 | Network 214 | ~~~~~~~ 215 | 216 | -a, --agent :: 217 | Identify cclive as 'arg' to the HTTP servers. The default value is 218 | "Mozilla/5.0". 219 | ---- 220 | config: 221 | agent = arg 222 | ---- 223 | 224 | -C, --connect-timeout :: 225 | Maximum time in seconds that the program should allow the connection 226 | to the server to take. This only limits the connection phase, once it 227 | has connected, this option is no more of use. Set to 0 to disable 228 | connection timeout (it will then only timeout on the system's internal 229 | timeouts). The default is 30. 230 | ---- 231 | config: 232 | connect-time = arg 233 | ---- 234 | 235 | -A, --dns-cache-timeout :: 236 | The name resolves will be kept in the memory for this number of 237 | seconds. Set to 0 to completely disable DNS caching, or to -1 to make 238 | the cached entries to remain in the memory forever. The default is 60. 239 | ---- 240 | config: 241 | dns-cache-timeout = arg 242 | ---- 243 | 244 | -m, --max-retries :: 245 | Specify the number of downloading retries before giving up. Set to 0 246 | to disable. The default is 5. 247 | + 248 | Note that the program will skip retrying altogether if the server 249 | returned HTTP 400 (and over), or if linkman:libquvi[3] returned an 250 | unrecoverable error (e.g. missing linkman:libquvi-scripts[7]). 251 | ---- 252 | config: 253 | max-retries = arg 254 | ---- 255 | 256 | -X, --no-proxy:: 257 | Disable use of HTTP proxy. Overrides --proxy and http_proxy environment 258 | settings. 259 | 260 | -r, --no-resolve:: 261 | Do not resolve HTTP URL redirections. Using this option will result in 262 | the program not being able to follow URL redirections which are often 263 | used by different URL shortening services. 264 | + 265 | When libquvi 0.9 or later is being used with cclive, this option will 266 | be ignored. The library will always resolve any URL redirections. See 267 | --version output to see if cclive was compiled with libquvi 0.9+. 268 | ---- 269 | config: 270 | no-resolve = {true|false} 271 | ---- 272 | 273 | -x, --proxy I:: 274 | Use the specified proxy address (e.g. http://foo:1234) for HTTP 275 | connections. By default, libcURL (which cclive and linkman:libquvi[3] 276 | use) will use the value of http_proxy. Using this option will override 277 | the http_proxy environment value. 278 | + 279 | See linkman:curl[1] for more information about the supported 280 | environment variables. 281 | ---- 282 | config: 283 | proxy = arg 284 | ---- 285 | 286 | -w, --retry-wait :: 287 | Wait the specified number of seconds before retrying after a failed 288 | attempt. The default is 5. If the value is 0, the program will skip 289 | this step. 290 | ---- 291 | config: 292 | retry-wait = arg 293 | ---- 294 | 295 | -H, --throttle :: 296 | Do not exceed the specified transfer rate (Ki/s). If 'arg' is 0 297 | (default), the throttling will be disabled. 298 | ---- 299 | config: 300 | throttle = arg 301 | ---- 302 | 303 | -T, --transfer-timeout :: 304 | Maximum time in seconds that the program should allow the transfer 305 | operation to take. Normally, name lookups can take a considerable 306 | amount of time, and limiting operations to less than a few minutes 307 | will risk aborting perfectly normal operations. This option will cause 308 | libcURL to use the SIGALRM to enable the timeout system calls. The 309 | default is 0 (disabled). 310 | ---- 311 | config: 312 | transfer-timeout = arg 313 | ---- 314 | 315 | Deprecated 316 | ~~~~~~~~~~ 317 | 318 | These options will be removed eventually in the later versions of 319 | cclive. 320 | 321 | -p, --prefer-format :: 322 | The '--stream' is intended to replace this option, eventually. The 323 | stream selection will be provided by libquvi 0.9+. This was done 324 | previously by cclive. See also <>. 325 | + 326 | Have the program choose the format (media stream) to download by 327 | matching the regular expression pattern match to the media URL as 328 | specified in the 'arg'. 329 | + 330 | This option may be specified multiple times. The use of --stream will 331 | override the rules specified using this option. 332 | + 333 | The 'arg' is a 'string pair' separated by a colon. The 'string pair' 334 | consists of: 335 | * a regular expression pattern 336 | * format (media stream) to download 337 | ---- 338 | config: 339 | prefer-format = arg 340 | ---- 341 | 342 | [[EXAMPLES]] 343 | EXAMPLES 344 | -------- 345 | 346 | * Typical use: 347 | + 348 | ---- 349 | $ cclive "URL" 350 | ---- 351 | 352 | * Query the available media streams: 353 | + 354 | ---- 355 | $ cclive -S "URL" 356 | ---- 357 | 358 | * Download the best quality media stream: 359 | + 360 | ---- 361 | $ cclive -s best "URL" 362 | ---- 363 | 364 | * With linkman:libquvi[3] 0.9+, you can pass a list of stream ID 365 | patterns to match: 366 | + 367 | ---- 368 | $ cclive -s foo,baz,best "URL" 369 | ---- 370 | + 371 | See the --stream description for more details. 372 | 373 | * Replace all occurences of 'foo' with 'bar' in the media title before 374 | it is used in the media file name: 375 | + 376 | ---- 377 | $ cclive -t 's/foo/bar/g' "URL" 378 | ---- 379 | 380 | * Go to background, redirect output to 'foo.log' file: 381 | + 382 | ---- 383 | $ cclive -b --log-file foo.log "URL" 384 | ---- 385 | 386 | * Interrupt the current transfer of all of the matching processes, 387 | this (USR1) will cause cclive to move onto the next URL in the batch: 388 | + 389 | ---- 390 | $ pkill -USR1 cclive 391 | ---- 392 | 393 | * Print the path to the downloaded media file using linkman:echo[1] and 394 | open the media file in linkman:totem[1]: 395 | + 396 | ---- 397 | $ cclive --exec 'echo "%f"' --exec 'totem "%f"' "URL" 398 | ---- 399 | 400 | * Process a batch of media URLs: 401 | + 402 | ---- 403 | $ cat URLs 404 | http://foo 405 | http://bar 406 | http://baz 407 | http://qux 408 | $ cat URLs | cclive 409 | $ cclive < URLs 410 | $ cclive URLs 411 | ---- 412 | 413 | [[FILES]] 414 | FILES 415 | ----- 416 | 417 | ~/.ccliverc:: 418 | The program will read this location by default. A different location 419 | may be defined with --config-file. 420 | 421 | Example 422 | ~~~~~~~ 423 | 424 | prefer-format = ^.*youtube.*\.com:fmt43_360p 425 | prefer-format = dailym:best 426 | 427 | filename-format = %i_(%t).%s 428 | tr = /(\w)/g 429 | 430 | progressbar = simple 431 | continue = true 432 | 433 | proxy = http://foo:1234 434 | no-resolve = false 435 | 436 | exec = /usr/bin/totem %f 437 | 438 | [[STREAMS]] 439 | STREAMS 440 | ------- 441 | The availability and the identification of the media streams is 442 | determined by linkman:libquvi[3] and linkman:libquvi-scripts[7]. 443 | 444 | YouTube 445 | ~~~~~~~ 446 | 447 | linkman:libquvi-scripts[7] returns the media stream IDs containing the 448 | 'itag' value so that they can be referred to as such using the --stream 449 | or --prefer-format options. The --print-streams may be used to get a 450 | list of the available media streams. 451 | 452 | libquvi-scripts 0.4:: 453 | The 'fmt' prefix is used to identify the streams. For example: 454 | 455 | itag: 22 456 | quvi: fmt22_720p 457 | 458 | libquvi-scripts 0.9 and later:: 459 | The 'iXX' prefix is used in the media stream IDs. For example: 460 | 461 | itag: 22 462 | quvi: hd720_mp4_i22_720p 463 | 464 | For more information about the YouTube video quality and codecs, visit 465 | `http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs`. 466 | 467 | EXIT STATUS 468 | ----------- 469 | The program will exit with EXIT_SUCCESS (on POSIX systems this is 0) on 470 | success, and with EXIT_FAILURE (on POSIX systems this is 1) if an error 471 | occurred. 472 | 473 | FURTHER RESOURCES 474 | ----------------- 475 | The development code may be cloned from `git://repo.or.cz/cclive.git`. 476 | The gitweb is accessible at `http://repo.or.cz/w/cclive.git`. 477 | 478 | Authors 479 | ------- 480 | Toni Gundogdu :: 481 | Author. 482 | 483 | Reporting bugs 484 | -------------- 485 | Report bugs to the cclive-devel mailing list 486 | where the development and the 487 | maintenance is primarily done. You do not have to be subscribed to the 488 | list to send a message there. 489 | 490 | LICENSE 491 | ------- 492 | cclive is Free Software, licensed under the GNU Affero GPLv3+. 493 | 494 | SEE ALSO 495 | -------- 496 | linkman:libquvi-scripts[7], linkman:libquvi[3] 497 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | v0.9.3 [2013-11-23] 2 | 3 | Limao Luo: 4 | - %e has been renamed to %s (--help) [fef8987] 5 | Toni Gundogdu: 6 | - AC_PREREQ: Bump to 2.69 [c6591e6] 7 | - Cleanup and devise more C++ eloquence 8 | - Add cc::sig [b3dfaf2] 9 | - Setup sig{winch,usr1} handlers only once 10 | - Use sigaction(2) instead of signal(2) 11 | - cc::file 12 | - Rewrite for cc::sig (sigusr1) [711dc10] 13 | - Use updated cc::progressbar [8236649] 14 | - _init: Rewrite/cleanup [fdf4565] 15 | - cc::input: Do not unescape input URIs [34026cb] 16 | - Reduce duplicate code [17a0e27] 17 | - cc::log: Rewrite, make header-only [8a60f4e] 18 | - cc::progressbar: Rewrite, make header-only [5431098] 19 | - Rewrite for cc::sig (sigwinch) 20 | - Use IEEE-1541 symbols 21 | - cc::re: Throw Boost.Exception based exception [dc132f7] 22 | 23 | 22 files changed, 773 insertions(+), 859 deletions(-) 24 | 25 | 26 | v0.9.2 [2013-08-08] 27 | 28 | Toni Gundogdu: 29 | [!] glibmm 2.24+ is now a prerequisite 30 | - Add --timestamp: Try to preserve modification time (PORT) [50213d7] 31 | - Bump the required Boost version to v1.49 [86978ab] 32 | - [DOC] --update-interval: Accepts now integers only [6426f8f] 33 | - Improve cc::input (complete rewrite) [81303f9] 34 | - Improve cc:options (complete rewrite) [314e722] 35 | 36 | 28 files changed, 1434 insertions(+), 873 deletions(-) 37 | 38 | 39 | v0.9.1 [2013-07-16] 40 | 41 | Toni Gundogdu: 42 | [!] GLib 2.24+ is now a prerequisite 43 | [!] Relicense under AGPLv3+ 44 | - Add cc::input -- improve input parsing [ad1f2e6] 45 | - DOC: cclive(1): Revise INPUT section [75d1b5c] 46 | - DOC: Revise --stream description [401e8c0] 47 | - FIX: configure.ac: AS_IF for date(1) [0019e62] 48 | - Deprecate --prefer-format in favor of --stream with libquvi 0.9+ [f55f70d] 49 | - Rewrite status_callback for libquvi v0.9.2 [7d94207] 50 | - Make --stream configurable [993a0be] 51 | - Move --support implementation to print_support [3d35e5e] 52 | - --filename-format: Remove depr. support for %h sequence [ad214c7] 53 | - --print-streams: Make output similar to quvi(1) 0.9+ [2294e93] 54 | - --version: Add CXX and CXXFLAGS to the output [65f8373] 55 | - --version: Print copyr notice to stderr instead of stdout [6c1f678] 56 | - --version: Provide additional info about libquvi [9e6e50e] 57 | 58 | 45 files changed, 976 insertions(+), 616 deletions(-) 59 | 60 | 61 | v0.9.0 [2013-05-25] 62 | 63 | Toni Gundogdu: 64 | - Add libquvi 0.9+ compatibility layer 65 | - Add --print-config [bf22b60] 66 | - Add short-option for --verbose-libcurl [239ea61] 67 | - Drop libquvi 0.2.16.x support 68 | - README: Bump libquvi prereq. to 0.4.0+ [99b47eb] 69 | - FIX: Regression in retrying introduced by 4b118378 [b57e47b] 70 | - FIX: Skip transfer with USR1, do not retry transfer [293dbc9] 71 | - Merge --version and --license options [474c143] 72 | - Add build time to --version output [ca5e80f] 73 | - Remove all deprecated options/features 74 | - Remove depr. --format [e32fc26] 75 | - Remove depr. --query-formats [e8114a0] 76 | - Remove depr. --regexp [0392fb0] 77 | - Remove depr. --subst [8d2ac37] 78 | - Update m4/boost.m4 to the latest [982bbc7] 79 | 80 | 28 files changed, 782 insertions(+), 422 deletions(-) 81 | 82 | 83 | v0.7.12 -- [2013-02-10, r:Toni Gundogdu] 84 | 85 | Toni Gundogdu: 86 | - Add quvi compatibility layer [cda6cc4] 87 | - Add short-option -h [8a279a6] 88 | - Add short-option -v [ec969f1] 89 | - Deprecate --format, add --stream [949d476] 90 | - Deprecate --query-formats, add --print-streams [7188199] 91 | - doc: Revise manual [777d82a] 92 | - FIX: Do not ignore I/O errors during file transfer [6f2e711] 93 | - options: Make 'continue' configurable [92bd61f] 94 | - options: Make 'no-resolve' configurable [4afab3e] 95 | - Upgrade boost.m4 to the latest [c436150] 96 | 97 | See ChangeLog for a more detailed list of changes. 98 | 99 | 47 files changed, 1881 insertions(+), 1993 deletions(-) 100 | 101 | 102 | v0.7.11 -- [2012-12-12, r:Toni Gundogdu] 103 | 104 | Adam Sampson: 105 | - Check .error() for user-provided regexps [03d69e0] 106 | - Copy strings passed to StringPiece [5325ecc] 107 | 108 | 3 files changed, 31 insertions(+), 19 deletions(-) 109 | 110 | 111 | v0.7.10 (2012-08-28) / Toni Gundogdu 112 | 113 | Toni Gundogdu: 114 | - Append contents of INSTALL file to README file [a90c854] 115 | - README file will contain the cclive specific information 116 | - INSTALL file will now be provided by GNU Autotools 117 | - configure.ac: Add AC_PROG_LN_S [da6c39a] 118 | - configure.ac: Install ccl symlink by default [9ecaaf8] 119 | - configure.ac: Lower AC_PREREQ to 2.67 [e317832] 120 | - configure.ac: Modernize AM_INIT options [76fd2d0] 121 | - configure.ac: Modernize libtool options [0f39e93] 122 | - configure.ac: Replace deprecated macros [59fce53] 123 | - configure.ac: Run gen-ver.sh to get version value [4f0d4a8] 124 | - DOC: Manual: Revise descriptions for option sections [fe31766] 125 | - DOC: Rewrite EXIT STATUS section [6690303] 126 | - FIX: Return non-zero exit status if failure (#18) [4d3c506] 127 | - exit_status: Redefine as ok (EXIT_SUCCESS) and error (EXIT_FAILURE) [20cbf6d] 128 | - Replace "invalid_option" with "error" [980792e] 129 | - main: Use exit_status, have caught exceptions set exit status [99bd1b2] 130 | - gen-ver.sh: Rewrite completely [df0a4ce] 131 | - go_background: Return application::error instead [36158a1] 132 | - Install ccl symlink now by default, see --disable-ccl in README 133 | - install-exec-hook: Remove the 'ccl' symlink [e394f71] 134 | - m4/boost.m4: Update to the most recent [1044d88] 135 | - m4/version.m4: Remove the file [d1e6bae] 136 | 137 | 17 files changed, 206 insertions(+), 139 deletions(-) 138 | 139 | 140 | v0.7.9 (2012-03-03) / Toni Gundogdu 141 | 142 | Toni Gundogdu: 143 | - FIX: Print only HTTP category domains with --support (#17) [8172356] 144 | - FIX: --support: Strip lua escapes from domain patterns (#16) [b089d63] 145 | - Update boost.m4 to the latest version [edc7b12] 146 | 147 | 148 | 0.7.8 Sat Nov 26 2011 Toni Gundogdu 149 | Changes: 150 | - Improvements to --exec 151 | - Invoke --exec with "nothing todo" 152 | - Additional specifiers %n and %t 153 | - Allow multiple occurences 154 | - Argument handling 155 | 156 | 157 | 0.7.7 Sun Oct 16 2011 Toni Gundogdu 158 | Bugfixes: 159 | - Do not create doc dir unnecessarily with "make install" 160 | - Option -O evaluated incorrectly (#13) 161 | Changes: 162 | - Use QUVI_VERSION_SCRIPTS when available 163 | - Add YouTube fmtNN ref. URL to manual 164 | 165 | 166 | 0.7.6 Mon Sep 12 2011 Toni Gundogdu 167 | Changes: 168 | - Add --tr (depr. --regexp, --subst) 169 | - Add --prefer-format 170 | - Revise manual 171 | 172 | 173 | 0.7.5.1 Thu Aug 18 2011 Toni Gundogdu 174 | Changes: 175 | - Lower quvi prerequisite to 0.2.16.1 176 | 177 | 178 | 0.7.5 Sat Aug 13 2011 Toni Gundogdu 179 | 180 | Changes: 181 | - Make --update-interval a "configurable" option 182 | - Implement 'simple' progressbar 183 | - Add --progressbar 184 | Fixes: 185 | - --update-interval (with negative) values doesn't work as intended (#11) 186 | 187 | 188 | 0.7.4.1 Thu Jun 23 2011 Toni Gundogdu 189 | 190 | Fixes: dist 191 | - make: rm: cannot remove `cclive': Is a directory (#9) 192 | 193 | 194 | 0.7.4 Sat Jun 18 2011 Toni Gundogdu 195 | 196 | Changes: 197 | - Add --no-resolve (replaces --no-shortened) 198 | - Rewrite to use quvi "media interface" 199 | - Add support for reading from files 200 | - Deprecate "--format list" 201 | - Remove --no-shortened 202 | - Add --query-format 203 | 204 | Changes: dist 205 | - configure: Rename --with-man -> --with-manual 206 | - Bump quvi prerequisite to 0.2.17 207 | - Revert back to GNU Autotools 208 | 209 | 210 | 0.7.3.1 Tue Mar 15 2011 Toni Gundogdu 211 | Bugfixes: 212 | - CMake Error at src/CMakeLists.txt:43 (#7) 213 | 214 | 215 | 0.7.3 Sat Mar 12 2011 Toni Gundogdu 216 | Changes: 217 | - Add QUVIOPT_CATEGORY check 218 | - Set QUVIPROTO_HTTP 219 | - Do not build quvicpp as a static library 220 | - Build quvicpp in the "cclive" target instead 221 | - See also ChangeLog for 3a867d7 commit message 222 | Bugfixes: 223 | - Compilation with boost 1.46.0 (#6) 224 | - Boost::Filesystem uses v3 as the new default in 1.46.0 225 | 226 | 227 | 0.7.2 Mon Feb 07 2011 Toni Gundogdu 228 | Changes: 229 | - Add SIGUSR1 handler 230 | - Add progress meter 231 | Bugfixes: 232 | - Normal progressbar min-max rendering while resizing window 233 | 234 | 235 | 0.7.1 Fri Jan 07 2011 Toni Gundogdu 236 | Changes: 237 | - Lower pcre prereq. to 8.02, thanks to Alejandro Garrido Mota 238 | - Show --background in options on platforms that support fork 239 | - Print pid to log file with --background 240 | - Add getpid check to CMakeLists.txt 241 | - Add --no-shortened switch 242 | - Make --quiet exclusive 243 | Bugfixes: 244 | - Wrong full path for downloaded file (#5) (d#608393) 245 | - Number of various issues with --background log 246 | 247 | 248 | 0.7.0.1 Sat Dec 18 2010 Toni Gundogdu 249 | Changes: 250 | - Add --output-dir 251 | 252 | 253 | 0.7.0 rc2 Sat Dec 04 2010 Toni Gundogdu 254 | Changes: 255 | - Lower boost prerequisite to 1.42.0, thanks to Alejandro Garrido Mota 256 | - Add --format [| ] 257 | - gzip manual before installing it 258 | Bugfixes: 259 | - CMAKE_VERBOSE_MAKEFILE 260 | - Manual install dir 261 | 262 | 263 | 0.7.0 rc1 Sat Nov 20 2010 legatvs 264 | Changes: 265 | - progressbar.cpp: Rewrite to_unit 266 | 267 | 268 | 0.7.0 beta4 Wed Nov 03 2010 legatvs 269 | Changes: 270 | - Add --regexp check 271 | - Clean up manual page 272 | - Improve progressbar line clearing 273 | 274 | 275 | Version 0.7.0 beta 3 276 | October 15, 2010 277 | 278 | Changes: 279 | * Print --help, --version, --license, --support to stdout 280 | 281 | 282 | Version 0.7.0 beta 2 283 | September 30, 2010 284 | 285 | Changes: 286 | * Migrated issue tracker to sf.net (read more: ) 287 | * Use "git describe" for version instead 288 | * Add --max-retries, --retry-wait (#47) 289 | * --version, print libquvi 290 | 291 | 292 | Version 0.7.0 beta 1 293 | September 1, 2010 294 | 295 | Changes: 296 | * Add prerequisite: Boost library 297 | * Build system: GNU Autotools -> CMake 298 | * Rewrite everything 299 | 300 | See also: 301 | 302 | 303 | 304 | 0.6.1 305 | ------------- 306 | 307 | --format no longer uses hardcoded values. The accepted 308 | values are now provided by libquvi. 309 | 310 | 311 | 0.6.0 312 | ------------- 313 | 314 | Significant changes to the project. 0.6.0 has gone through 315 | tedious testing but expect bugs. 316 | 317 | * Added new dependency for libquvi (http://quvi.googlecode.com/) 318 | - libquvi is a brand new a spinoff library 319 | - based on cclive 0.5 host specific code 320 | - takes care of all of the page fetching and parsing 321 | 322 | * cclive will now prepend "error: libquvi:" if an error occurs 323 | in the library, this means that any such bug should be reported 324 | to the *quvi bug tracker* (see the link above) 325 | 326 | * Added support for (cctv) multi-segment videos, see also: 327 | http://code.google.com/p/clive/issues/detail?id=45 328 | 329 | * Adult websites are now only supported if libquvi was built 330 | with the support (default:no) 331 | 332 | * CCLIVE_CURLINIT return code is now obsolete, libquvi 333 | initializes libcurl and spews out the init error 334 | 335 | * The code responsible for the underlaying "retry-transfer" 336 | logic was rewritten for better readability 337 | 338 | * Removed the libiconv dependency, libquvi takes care of 339 | the character set conversions now 340 | 341 | * Tests no longer run any of the old host specific checks, 342 | the quvi package takes care of this now, other changes 343 | to the test suite include: 344 | 345 | * ADULT_OK environment variable is now obsolete, 346 | this is now libquvi territory 347 | 348 | * Renamed CCLIVE_TEST_OPTS -> CCLIVE_TEST_OPTIONS 349 | 350 | * Tests are now perl scripts 351 | 352 | * See INSTALL file for the list of test suite supported 353 | environment variables 354 | 355 | * libquvi introduces some misc. minor improvements to the 356 | page fetching and parsing, e.g. spiegel videos now skip 357 | the video page fetch altogether 358 | 359 | * Spyfilms video ids were previously (0.5.9) glued together 360 | from various video page elements, as of 0.6.0 (and libquvi) 361 | spyfilms video ids are generated using different scheme 362 | 363 | * This is a special case, all other supported websites 364 | use unique ids for their videos 365 | 366 | * Option changes 367 | 368 | * Remove --stream option. This option was supported for historical 369 | reasons only, until now. It has been considered deprecated since 370 | the introduction of --stream-pass in 0.4.6. 371 | 372 | 373 | 0.5.7 374 | ------------- 375 | 376 | Multiple regular expressions can now be supplied for --substitute. 377 | 378 | For example: 379 | % cclive -S "s/old/new/i s/[/:-]/_/g" URL 380 | 381 | Note the whitespace between the regular expressions. 382 | 383 | 384 | 0.5.6 385 | ------------- 386 | 387 | Added --substitute which mimics Perl's s/old/new/(gi) substitution. 388 | It can be used to replace substrings in output filenames. 389 | 390 | Example: 391 | % cclive -S "s/old/new/i" URL 392 | % cclive -S "s/[/:-]/_/g" URL 393 | 394 | Changed --regexp to accept Perl-like regular expressions, e.g. 395 | % cclive -r "/(\w+)/" URL 396 | % cclive -r "/(\w+)/g" URL 397 | % cclive -r "/([a-e])/gi" URL 398 | 399 | Note that this breaks the backward compatibility. 400 | * --regexp now expects /pattern/(gi) 401 | * --find-all is no longer supported (use /g instead) 402 | 403 | Added --background which is similar to what clive (2.1.x) once had. 404 | The progress indication while in the background is very simplistic. 405 | Note that --logfile and --logfile-interval options were also added 406 | to support --background. 407 | 408 | 409 | 0.5.2 410 | ------------- 411 | 412 | New youtube format: fmt34. This was previously referred to as youtube 413 | default format which no longer appears to be the case. Note that the 414 | fmt34 video quality and resolution may vary. 415 | 416 | Fixes a few bugs. 417 | 418 | Redtube support is broken. 419 | http://code.google.com/p/cclive/issues/detail?id=5 420 | 421 | 422 | 0.5.0 423 | ------------- 424 | 425 | The dependency of Perl and HTML::Tokenizer module have been replaced with 426 | libiconv and PCRE for significantly lower system footprint. Perl was previously 427 | used for parsing video titles and cleaning them up. 428 | 429 | configure no longer supports --enable-sigwinch. The support is now compiled 430 | into the program automatically if signal.h is found and defines SIGWINCH. 431 | 432 | tests/ have been rewritten in C++. 433 | 434 | Other notable changes: 435 | 436 | Removed options: 437 | --title 438 | - obsoleted by --filename-format since 0.4.1 439 | --cclass, --no-cclass 440 | - obsoleted by --regexp and --find-all (below) 441 | 442 | New options: 443 | -r, --regexp 444 | - Defines the regular expression that can be used to "filter" 445 | video titles before they are used for filenames 446 | 447 | -g, --find-all 448 | - Used together with the above to repeat the matching to find 449 | all occurences, similar to Perl's /g option 450 | 451 | Example: 452 | cclive -gr "(\w|\s)" URL 453 | 454 | Comparison: 455 | ----------- 456 | SIZE RES Notes 457 | clive 2.2.4 ~13924k ~10388k Perl all the way 458 | cclive 0.4.7 (w/ perl) ~8300k ~6908k Video titles enabled 459 | cclive 0.4.7 (w/o perl) ~5940k ~3224k No video titles 460 | cclive 0.5.0 ~5780k ~3196k Video titles enabled by default 461 | 462 | SIZE = total size of the process (text, data, and stack) 463 | RES = current amount of resident memory 464 | 465 | Note : Figures are approximates. cclive was built with "-O2 -march=pentium4" 466 | Tested: FreeBSD 7.2, numbers provided by top(1), numbers vary slightly per URL 467 | -------------------------------------------------------------------------------- /ChangeLog.pre-gitlog: -------------------------------------------------------------------------------- 1 | 2 | Version 0.6.6 3 | 4 | Changes: 5 | * Bump autoconf prereq to 2.68 6 | 7 | Bugfixes: 8 | * Incorrect documentation for --retry=0 9 | 10 | 11 | Version 0.6.5 12 | August 12, 2010 13 | 14 | Changes: 15 | * Add --license option 16 | * Add --regexp default "/(\w|\s|\pL)/g" (issue #43) 17 | * Add --enable-tests (configure, disabled by default) 18 | 19 | 20 | Version 0.6.4 21 | July 25, 2010 22 | 23 | Changes: 24 | * Disable tests/multi-segment.pl (libquvi: cctv support broken) 25 | * Remove tests/noformat-eval.pl 26 | * Bump autoconf prereq to 2.66 27 | * New default --format value (`flv' -> `default'). 28 | * Warn if --format, --format-map use invalid values 29 | * Handle QUVI_LUA 30 | * Use CURLOPT_RESUME_FROM_LARGE instead 31 | * Bump PKG_CHECK_MODULES(libquvi) to 0.2.0 32 | 33 | Bugfixes: 34 | * --format-map (#36) 35 | 36 | 37 | Version 0.6.3 38 | May 6, 2010 39 | 40 | Changes: 41 | * Update to use libquvi's quvi_next_supported_website function 42 | * Add AM_SILENT_RULES([yes]) 43 | 44 | 45 | Version 0.6.2 46 | (February 23, 2010) 47 | 48 | Changes: 49 | * tests/multi-segment.pl now checks for locale 50 | 51 | Bugfixes: 52 | * -f best, accepted again (closes issue #24) 53 | 54 | 55 | Version 0.6.1 56 | (Feburary 13, 2010) 57 | 58 | Changes: 59 | * Updated manual to reflect the dailymotion format id changes 60 | * Remove the hardcoded --format values 61 | # Use libquvi provided values instead 62 | * Add tests/noformat-eval.pl 63 | * Cleanup notice message (configure) 64 | * Add tests/pod-check.pl (replaces "make checkpod") 65 | 66 | Bugfixes: 67 | * tests/Makefile.am: Missing separator (Makefile:450, rep. FreeBSD) 68 | 69 | 70 | Version 0.6.0 71 | (February 2, 2010) 72 | 73 | User-visible changes: 74 | * Multi-segment support (e.g. some cctv videos) 75 | * Remove "Features" from --version output 76 | * Rewrite of retrying transfers 77 | * --version now prints libquvi version (instead of libcurl) 78 | 79 | Developer-visible changes: 80 | * Add dependency for libquvi (see NEWS and INSTALL files) 81 | * Remove libiconv dependency (ditto) 82 | 83 | General changes: 84 | * Tidy up project description (e.g. README) 85 | 86 | 87 | Version 0.5.9 88 | (January 26, 2010) 89 | 90 | User-visible changes: 91 | * --stream-exec, --stream-pass 92 | # Add support for the %f specifier 93 | # Thanks to webmaster.foto-schaal.de for the patch 94 | # Closes issue #22 95 | * Add spyfilms support 96 | # Thanks to Hanno Böck for initial patch and data 97 | 98 | 99 | Version 0.5.8 100 | (January 14, 2010) 101 | 102 | User-visible changes: 103 | * Fix "-f unavailable_format" for dailymotion 104 | # Use first found link instead of croaking with an error 105 | * Fix "-f best" for dailymotion 106 | # Use proper sorting method 107 | # Thanks to Markus Doppelbauer for reporting the issue 108 | * Fix tube8 support 109 | 110 | Developer-visible changes: 111 | * configure: dump build configuration 112 | * tests/sevenload.cpp: remove dead test link (closes issue #19) 113 | 114 | 115 | Version 0.5.7 116 | (December 18, 2009) 117 | 118 | User-visible changes: 119 | * Do not replace dashes with underscores in IDs 120 | * --substitute: support multiple regexps (re-closes issue #17) 121 | # Read manual page for updated details 122 | # See also NEWS file 123 | * Fix tube8 support 124 | 125 | Developer-visible changes: 126 | * tests: support MULTI_TEST env.var. (see INSTALL) 127 | * Remove redtube remains from the src tree 128 | 129 | 130 | Version 0.5.6 131 | (December 2, 2009) 132 | 133 | User-visible changes: 134 | * Add --background, --logfile, --logfile-interval 135 | * Add --substitute (closes issue #17) 136 | * Tweak static error strings (e.g. "system call failed" -> "system") 137 | * Croak if file open fails prior to video download (related to issue #17) 138 | * Sort --hosts output alphabetically (Thanks to Bram Neijt for the patch) 139 | * Decode properly predefined (X)HTML numeric character references 140 | * Changes to --regexp 141 | # Support Perl-like regexps (see NEWS file, updated manual for more) 142 | # Add support for Perl-like /i 143 | # Remove --find-all (use /g with --regexp instead) 144 | * Fix random sevenload http/401 errors (closes issue #14) 145 | # Caused by use of predefined HTML entities in video links 146 | * Fix CURLE_RANGE_ERROR handling for file transfers 147 | # Caused infinite loop of "Requested range was not delivered by the server" 148 | # Stop after --retry specified limit has been reached 149 | 150 | Developer-visible changes: 151 | * Bump AC_PREREQ to 2.65 152 | 153 | 154 | Version 0.5.5 155 | (November 14, 2009) 156 | 157 | User-visible changes: 158 | * Add predefined HTML character entity (e.g. ") conversion 159 | * Add support for tube8.com 160 | # Thanks to anonymous contributor for the patch 161 | * Allow dailymotion.* 162 | * Fix sevenload title parsing 163 | # Use config title instead 164 | 165 | Developer-visible changes: 166 | * Clear up project description ("mission statement") 167 | * docs/AddingSupport: add checklist and 0.6.0 note 168 | 169 | 170 | Version 0.5.4 171 | (October 29, 2009) 172 | 173 | User-visible changes: 174 | * Fix: youtube -f best (error: parse failed: no match: (?i)"fmt_map": "(\d+)) 175 | # Failed if fmt_map was an empty string 176 | # Closes issue #7 177 | * Add support for: 178 | # youjizz.com (closes issue #11) 179 | # xvideos.com (closes issues #12) 180 | # Both contributed by i.am.a.real.coward 181 | 182 | 183 | Version 0.5.3 184 | (October 15, 2009) 185 | 186 | User-visible changes: 187 | * Fix: google title parsing (closes issue #6) 188 | # Use instead 189 | 190 | Developer-visible changes: 191 | * Remove: AC_PROG_CPP (deprecated) 192 | * Fix: AM_ICONV failed to detect iconv (mingw, freebsd, ...) 193 | # Use a proper config.rpath file 194 | * Remove: use of tmpfile(3) (queryFileLength) 195 | # Read into memory instead when querying file length 196 | 197 | 198 | Version 0.5.2 199 | (September 29, 2009) 200 | 201 | User-visible changes: 202 | * Fix: dailymotion id parsing 203 | * Youtube: 204 | # Accept -f fmt34 205 | # fmt34 is now treated as yet another (new?) format 206 | # Rewrite youtube section of the FORMATS in the manual 207 | * Fix: cctv title parsing (related to issue #4) 208 | * Fix: Segmentation fault with -q switch (closes issue #3) 209 | * Vimeo: Fix title parsing (related to clive issue 37 "accented chars") 210 | * Golem: Parse title from fetch config xml instead 211 | 212 | Developer-visible changes: 213 | * Disable tests: redtube (see known-issues) 214 | * "make check": ADULT_OK env.var. enables redtube test 215 | * Bump autoconf prereq to 2.64 216 | * configure.ac: 217 | # New m4/version.m4 218 | # New AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2]) 219 | # Add AC_CONFIG_AUX_DIR 220 | # Add AC_CONFIG_MACRO_DIR 221 | # Add AC_PROG_CPP 222 | # Add AC_PROG_MKDIR_P 223 | # Add BUILD_DATE 224 | # Remove AC_C_CONST 225 | * Makefile.am: 226 | # Use top_srcdir and top_builddir for gengetopt, etc. 227 | * tests/Makefile.am: 228 | # Use top_builddir for CCLIVE_PATH 229 | 230 | Known issues: 231 | * Redtube: broken (verify -> http/404) 232 | 233 | 234 | Version 0.5.1 235 | (September 14, 2009) 236 | 237 | User-visible changes: 238 | * Fix: google support (title parsing) 239 | * Fix: break support (http/403, thanks to Werner Elsler for the fix) 240 | # <http://code.google.com/p/clive/issues/detail?id=38> 241 | 242 | Developer-visible changes: 243 | * Cleanup: remove unnecessary opts.h includes 244 | * Fix: error: 'winsize' was not declared in this scope (add TIOCGWINSZ check) 245 | * Handle HTTP/206 properly 246 | # Fixes: (invalid) "error: fetch failed: server returned http/206" 247 | 248 | 249 | Version 0.5.0 250 | (August 28, 2009) 251 | 252 | User-visible changes: 253 | * Add options: 254 | # --regexp, --find-all 255 | # --format-map 256 | # --overwrite 257 | # --retry 258 | # --retry-wait 259 | * Add website support (ported from clive): 260 | # cctv.com 261 | # ehrensenf.de 262 | # spiegel.de 263 | # redtube.com 264 | * Add embedded video link support: 265 | # vimeo 266 | # redtube 267 | * Fix: 268 | # Liveleak support: Unsupported protocol (rc=1) [whitespace in links] 269 | # --hosts: dailymotion: spak-mini -> spark-mini 270 | # ETA for resumed transfers 271 | # Average rate for finished transfers 272 | # --continue with local_length >= content_length 273 | * Remove options: 274 | # --title 275 | # --cclass 276 | # --no-cclass 277 | * Progressbar changes: 278 | # Zeropad ETA 279 | # Use 60s instead of 100s in time to string conversions 280 | 281 | Developer-visible changes: 282 | * Update: 283 | # NEWS 284 | # Manual 285 | # INSTALL 286 | * src/cmdline.c -> src/cmdline.cpp 287 | * $host.cpp: Reimpl. using pcre 288 | * New prerequisites: 289 | # libiconv 290 | # libpcre (cpp) 291 | * configure: 292 | # Add: make checkpod (replaces tests/pod.pl) 293 | # Remove: --with-perl 294 | # Remove: --enable-sigwinch (use SIGWINCH definition in signal.h) 295 | * tests/: Rewrite in c++ 296 | # Note the extension changes (.c -> .cpp) 297 | * tests/test.cpp: 298 | # Add support for CCLIVE_TEST_OPTS 299 | # Set CCLIVE_NO_CONFIG to ignore ~/.ccliverc (src/opts.cpp) 300 | * Add to tests/: 301 | # multi.cpp 302 | # returncode.cpp 303 | * tests/redtube.cpp: Replace dead test link 304 | * Remove: tests/pod.pl 305 | 306 | 307 | Version 0.4.7 308 | (August 5, 2009) 309 | 310 | User-visible changes: 311 | * Add: 312 | # myubo.com support 313 | * Thanks to Patrick Hoffmann <patrick@sobran.de> for the patch 314 | * Fix: 315 | # Dailymotion: spak-mini -> spark-mini 316 | # Funnyhub: ID parsing 317 | # Funnyhub: title parsing (proper cleanup) 318 | # --title with multiple video page links 319 | * Example: cclive -t URL URL2 320 | * Segfault after 1st video when HTML parsing begins for 2nd 321 | * Reason: Perl: incomplete cleanup 322 | 323 | Developer-visible changes: 324 | * Rewrite --title handling 325 | # Allow $host.cpp impl. override HTML <title> 326 | # hosthandler.cpp: Remove WITH_PERL block 327 | # pl.cpp: impl. PerlMgr 328 | * tests/youtube.c: new link 329 | 330 | 331 | Version 0.4.6 332 | (July 30, 2009) 333 | 334 | User-visible changes: 335 | * Add: 336 | # golem.de support (port from clive) 337 | # --format=(mp4|hd|hq|3gp) for youtube 338 | # --stream-pass option 339 | # clipfish.de support (port from clive) 340 | # funnyhub.com support 341 | * Thanks to Patrick Hoffmann <patrick@sobran.de> for the patch 342 | * Fix: 343 | # [unicode support] --cclass now works as expected with unicode as well 344 | # Dailymotion support 345 | * Change: 346 | # Pair --exec with --exec-run, the latter now triggers the former 347 | # --hosts: print supported formats 348 | # --version: print current locale setting 349 | # --stream-exec: remove depend on --stream 350 | 351 | Developer-visible changes: 352 | * Reduce "header hell" 353 | # $host.cpp includes are now cleaner 354 | # Slightly faster compilation 355 | * $host.cpp: Remove isHost overriding (HostHandler impl. obsoletes) 356 | * exec.cpp: 357 | # Improve system return code handling 358 | # Quote %i replacement values 359 | * configure.ac: add AC_FUNC_(RE)ALLOC (make cond.) 360 | * Change: tests/test.c: use system instead of execv 361 | # Some tests (e.g. -f) were skipped quietly 362 | * Change: make check: respect NO_INTERNET env.var. 363 | * Fix: src/opts.cpp: missing #include <cstdlib> 364 | # Detected on Ubuntu (gcc 4.3.3) 365 | 366 | 367 | 0.4.5 368 | (July 6, 2009) 369 | 370 | User-visible changes: 371 | * Add: Return codes (see also manual page) 372 | * Fix: Google mp4 support 373 | 1. Re-enable --format=mp4 374 | * Fix: Misc. typos in the manual page 375 | * Fix: help string for --format 376 | 377 | Developer-visible changes: 378 | * Add: pod2html check: make man now generates also html if pod2html is found 379 | * Add: mingw32-build.sh, mingw32-package.sh 380 | 1. Covers the steps on FreeBSD although should port easily 381 | * Add: --enable-tests (configure) 382 | 1. Enables tests run by "make check" 383 | 384 | 385 | 0.4.4 386 | (June 21, 2009) 387 | 388 | User-visible changes: 389 | * Add: Vimeo support 390 | * Change: --format=mp4 is no longer accepted (see 0.4.3 known-issues) 391 | 392 | Developer-visible changes: 393 | * hostfactory.cpp: tidy up switch-case 394 | 395 | 396 | 0.4.3 397 | (June 14, 2009) 398 | 399 | User-visible changes: 400 | * Add: --format=best support 401 | * Add: --format=fmt35 for Youtube 402 | * Add: --no-cclass 403 | * Add: Support for youtube-nocookie.com 404 | * Fix: --emit-csv: file length: add missing std::ios::fixed 405 | * Fix: Dailymotion ID parsing 406 | * Fix: Liveleak ID parsing 407 | * Fix: mangled error messages: rewrite curl error handling 408 | * Change: --emit-csv: remove initial length 409 | * Change: make --cclass, --no-cclass depend on --title 410 | * Rename: --download -> --format 411 | * Rename: --title-cclass -> --cclass 412 | * Remove: --format=fmt6 (Youtube no longer appears to support it) 413 | * Remove: --format=spark (Dailymotion, use "flv" instead) 414 | * Remove: --youtube-user, --youtube-pass (at least until login fix is found) 415 | 416 | Developer-visible changes: 417 | * Add: tests/youtube-nocookies.url 418 | * Add: examples/config file 419 | * Fix: cclive.pod (*** WARNING: (section) in ... deprecated at line ...) 420 | * Update: INSTALL 421 | 422 | Known-issues: 423 | * mp4 extraction fails (Google) 424 | 425 | 426 | 0.4.2 427 | (May 9, 2009) 428 | 429 | User-visible changes: 430 | * Change: --connect-timeout, --connect-timeout-socks default -> 30s 431 | 432 | Developer-visible changes: 433 | * src/curl.(cpp|h): make curlErrorBuffer a member variable 434 | * src/singleton.h: fix precision error reported on 64bit systems 435 | ** Thanks to Martin Wilke for the patch 436 | 437 | 438 | 0.4.1 439 | (May 4, 2009) 440 | 441 | User-visible changes: 442 | * Fix: port title parsing fix from clive (issue #8) 443 | * Add: --filename-format 444 | 445 | Developer-visible changes: 446 | * src/log.cpp: add missing unistd.h 447 | 448 | 449 | 0.4.0 450 | (April 28, 2009) 451 | 452 | User-visible changes: 453 | * Separation of stdout and stderr (previously all -> stderr) 454 | * Change: --version -> stdout (previously -> stderr) 455 | * Rename: --supported-hosts -> --hosts 456 | * Cleanup: manual page 457 | 458 | Developer-visible changes: 459 | * Rewrite to C++ (expect bugs) 460 | * Add: HACKING file 461 | * Add: missing cmdline.ggo file (Makefile.am) 462 | * Add: missing cclive.pod file (Makefile.am) 463 | * Rename: --with-sigwinch -> --enable-sigwinch (configure.ac) 464 | * Change: --enable-sigwinch: default -> no (configure.ac) 465 | * Rename: CHANGES -> ChangeLog 466 | 467 | Known-issues: 468 | * Youtube login does not work (throw an error, patches welcome!) 469 | 470 | 471 | 0.3.2 472 | (April 17, 2009) 473 | 474 | User-visible changes: 475 | * Add new options: (closes issue #2) 476 | - --connect-timeout 477 | - --connect-timeout-socks 478 | 479 | Developer-visible changes: 480 | * configure.ac: use AC_GNU_SOURCE instead of defining _GNU_SOURCE manually 481 | - main.c, dl.c, llst.c, host.c, login.c, util.c: remove _GNU_SOURCE define 482 | 483 | 484 | 0.3.1 485 | (April 4, 2009) 486 | 487 | User-visible changes: 488 | * Replace dashes in video IDs with underscores before using them in fnames 489 | * Use returned content-type for video file name suffix 490 | - Use "flv" for exceptions (e.g. octet-*, x-flv, etc.) 491 | * Add Youtube HD support (--download=mp4_hd) 492 | - Thanks bf for the patch 493 | 494 | Developer-visible changes: 495 | * configure.ac: Use CANONICAL_SYSTEM instead of CANONICAL_HOST 496 | - main.c: use CANONICAL_TARGET instead of CANONICAL_HOST 497 | * Add tests/youtube-hd.url 498 | * Cleanup manual page (cclive.1, cclive.pod) 499 | 500 | 501 | 0.3.0 502 | (March 27, 2009) 503 | 504 | User-visible changes: 505 | * Fix googlevideo support 506 | - Symptom: "error: 'docid: ' not found" 507 | - Cause: id extraction failed 508 | * Change --version output 509 | * Update OTHER section in the manual page (cclive.1, cclive.pod) 510 | 511 | Developer-visible changes 512 | * Fix various compilation warnings (gcc 4.3+) 513 | * Cleanup code (style-wise) 514 | - indent -nut -i4 -ldi8 515 | * Convert to use GNU Autotools 516 | * Remove GNU Makefile 517 | * Update INSTALL 518 | 519 | 520 | 0.2.6 521 | (March 24, 2009) 522 | 523 | User-visible changes: 524 | * Fix dailymotion support 525 | - Changes to website HTML; incorrect copy-to-string 526 | - Symptoms: output filename utter gibberish 527 | 528 | 529 | 0.2.5 530 | (March 17, 2009) 531 | 532 | User-visible changes: 533 | * Port lastfm-youtube support from clive 534 | * Fix googlevideo support 535 | 536 | Developer-visible changes 537 | * Add tests/lastfm.url 538 | * Add strlcpy.c 539 | * Touch cclive.pod and cclive.1 540 | 541 | 542 | 0.2.4 543 | (March 9, 2009) 544 | 545 | User-visible changes: 546 | * Add --title and --title-cclass options 547 | * Add --stream-exec and --stream options 548 | * Change --version to print build-time options (e.g. --with-sigwinch) 549 | * Remove -e short option (--exec) 550 | 551 | Developer-visible changes: 552 | * Touch INSTALL, cclive.pod, cclive.1 and README 553 | * Fix gcc 4.3+ "format not a string literal and no format arguments" warning 554 | * Makefile: add WITH_PERL [default:no] 555 | * Add missing GPL header (cclive.h) 556 | 557 | 558 | 0.2.3 559 | (February 20, 2009) 560 | 561 | User-visible changes: 562 | * Removed wmv format (breakcom) 563 | - persistant HTTP/404 (not found) error 564 | * Added --print-fname 565 | * Fixed file naming when output file exists already 566 | Symptoms: output file is called ".1" or ".2" etc. 567 | 568 | 569 | 0.2.2 570 | (February 15, 2009) 571 | 572 | User-visible changes: 573 | * Added --number-videos 574 | * Fixed HTTP proxy support (main.c, opts.c) 575 | - Fixed: --no-proxy was ignored if http_proxy env. variable was set 576 | - Fixed: http_proxy env.v. was misshandled causing cclive to crash at exit 577 | - missused cc.gi.proxy_given by setting it to 1 when http_proxy was set, 578 | causing gengetopt generated code to try to free an invalid pointer 579 | - Changed: libcurl is now responsible for using http_proxy 580 | - option --proxy=address still overrides http_proxy definition 581 | * Implemented SIGWINCH signal handling 582 | - progressbar now resizes along with terminal width but not below 80 chars 583 | * Fixed: --limit-rate no longer limits following page transfers after file dl 584 | 585 | Developer-visible changes: 586 | * Replaced remaining (excl. gengetopt generated) strcat with safer strlcat 587 | * Merged opts.c into main.c 588 | * Rewrote cclive struct 589 | - config parser (pp) is now freed immediately after parsing 590 | - Removed http_proxy_env from the struct 591 | - exec_mode is now a local variable 592 | * Rewrote up parse_opts function 593 | * Added WITH_SIGWINCH to Makefile, e.g. "make WITH_SIGWINCH=no" 594 | - if != "yes" cclive is built without the resizing feature described above 595 | which depends on SIGWINCH and ioctl(TIOCGWINSZ) 596 | 597 | 598 | 0.2.1 599 | (February 9, 2009) 600 | 601 | User-visible changes: 602 | * Tweaked progressbar to fit better 603 | * --limit-rate now limits file transfers only 604 | * Fixed crashing at exit while using --exec 605 | - Caused by incorrect use of free in llst_free (llst.c) 606 | 607 | 608 | 0.2.0 609 | (February 7, 2009) 610 | 611 | User-visible changes: 612 | * Added --exec option 613 | * Fixed sevenload support 614 | * Added support for dailymotion 615 | * Extracted videos are now saved as $host_$id.$suffix 616 | * Fixed file naming: $id-($host).$suffix.$num => $host-($id).$suffix.$num 617 | 618 | Developer-visible changes: 619 | * Updated TODO 620 | * Added AUTHORS 621 | * Added tests/dmotion.url 622 | * Updated tests/7load.url 623 | * Updated README with "cclive != clive" section 624 | * Use of FREE macro (cclive.h) instead of free(3) 625 | * Fixed incorrect freeing of embed to video page converted link 626 | - Symptoms: % cclive < tests/youtube.url => crash at fetch 2nd link 627 | * Cleaned up .c and .h files 628 | - Relocated some of the functions to reduce .h spam 629 | - Removed obsoleted .c files 630 | - Renamed much of the functions with "cc_" prefix 631 | - Removed "extern" from .h files 632 | 633 | 634 | 0.1.0 635 | (January 31, 2009) 636 | 637 | * initial release 638 | -------------------------------------------------------------------------------- /src/cc/options.h: -------------------------------------------------------------------------------- 1 | /* cclive 2 | * Copyright (C) 2013 Toni Gundogdu <legatvs@gmail.com> 3 | * 4 | * This file is part of cclive <http://cclive.sourceforge.net/>. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public 17 | * License along with this program. If not, see 18 | * <http://www.gnu.org/licenses/>. 19 | */ 20 | 21 | #ifndef cc__options_h 22 | #define cc__options_h 23 | 24 | #include <ccinternal> 25 | 26 | #include <boost/program_options/options_description.hpp> 27 | #include <boost/program_options/positional_options.hpp> 28 | #include <boost/program_options/variables_map.hpp> 29 | #include <boost/program_options/parsers.hpp> 30 | #include <boost/exception/diagnostic_information.hpp> 31 | #include <boost/exception/errinfo_file_name.hpp> 32 | #include <boost/exception/errinfo_errno.hpp> 33 | #include <boost/exception/info.hpp> 34 | #include <boost/algorithm/string/join.hpp> 35 | #include <boost/noncopyable.hpp> 36 | #include <boost/tuple/tuple.hpp> 37 | #include <boost/filesystem.hpp> 38 | #include <boost/foreach.hpp> 39 | #include <boost/format.hpp> 40 | #include <fstream> 41 | 42 | #include <ccerror> 43 | #include <ccre> 44 | 45 | // Core 46 | 47 | #define OPT__CONFIG_FILE "config-file" 48 | #define OPT__NO_DOWNLOAD "no-download" 49 | #define OPT__BACKGROUND "background" 50 | 51 | // Core -- configurable 52 | 53 | #define OPT__TIMESTAMP "timestamp" 54 | #define OPT__CONTINUE "continue" 55 | #define OPT__STREAM "stream" 56 | #define OPT__EXEC "exec" 57 | 58 | // Informative 59 | 60 | #define OPT__PRINT_STREAMS "print-streams" 61 | #define OPT__PRINT_CONFIG "print-config" 62 | #define OPT__SUPPORT "support" 63 | #define OPT__VERSION "version" 64 | #define OPT__HELP "help" 65 | 66 | // Output 67 | 68 | #define OPT__VERBOSE_LIBCURL "verbose-libcurl" 69 | #define OPT__OUTPUT_FILE "output-file" 70 | #define OPT__OVERWRITE "overwrite" 71 | #define OPT__LOG_FILE "log-file" 72 | #define OPT__QUIET "quiet" 73 | 74 | // Output -- configurable 75 | 76 | #define OPT__FILENAME_FORMAT "filename-format" 77 | #define OPT__UPDATE_INTERVAL "update-interval" 78 | #define OPT__PROGRESSBAR "progressbar" 79 | #define OPT__OUTPUT_DIR "output-dir" 80 | #define OPT__TR "tr" 81 | 82 | // Network 83 | 84 | #define OPT__NO_PROXY "no-proxy" 85 | 86 | // Network -- configurable 87 | 88 | #define OPT__DNS_CACHE_TIMEOUT "dns-cache-timeout" 89 | #define OPT__TRANSFER_TIMEOUT "transfer-timeout" 90 | #define OPT__CONNECT_TIMEOUT "connect-timeout" 91 | #define OPT__MAX_RETRIES "max-retries" 92 | #define OPT__RETRY_WAIT "retry-wait" 93 | #define OPT__NO_RESOLVE "no-resolve" 94 | #define OPT__THROTTLE "throttle" 95 | #define OPT__AGENT "agent" 96 | #define OPT__PROXY "proxy" 97 | 98 | // Deprecated -- configurable 99 | 100 | #define OPT__PREFER_FORMAT "prefer-format" 101 | 102 | // Hidden (positional) 103 | 104 | #define OPT__URL "url" 105 | 106 | namespace cc 107 | { 108 | 109 | namespace po = boost::program_options; 110 | namespace fs = boost::filesystem; 111 | 112 | typedef std::vector<std::string> vs; 113 | typedef std::ostream ostr; 114 | 115 | struct tr 116 | { 117 | inline friend ostr& operator<<(ostr& o, const tr& r) { return o<<r.str(); } 118 | explicit inline tr(const std::string& s) 119 | { 120 | try 121 | { 122 | std::string empty; 123 | re::tr(s, empty); 124 | } 125 | catch (const cc::error::tuple& x) 126 | { 127 | cc::error::type_tuple const *t = 128 | boost::get_error_info<cc::error::errinfo_tuple>(x); 129 | 130 | const std::string& w = boost::get<1>(*t); 131 | 132 | const std::string& m = 133 | (boost::format("invalid option value: %1%: %2%") % s % w).str(); 134 | 135 | BOOST_THROW_EXCEPTION(cc::error::tuple() 136 | << cc::error::errinfo_tuple(boost::make_tuple(OPT__TR, m))); 137 | } 138 | _str = s; 139 | } 140 | inline const std::string& str() const { return _str; } 141 | private: 142 | std::string _str; 143 | }; 144 | 145 | typedef std::vector<tr> vtr; 146 | 147 | inline static void validate(boost::any& v, 148 | const std::vector<std::string>& values, 149 | tr*, int) 150 | { 151 | po::validators::check_first_occurrence(v); 152 | const std::string& s = po::validators::get_single_string(values); 153 | v = boost::any(tr(s)); 154 | } 155 | 156 | struct progressbar_mode 157 | { 158 | typedef enum {normal, simple, dotline} mode_type; 159 | 160 | inline friend ostr& operator<<(ostr& o, const progressbar_mode& r) 161 | { return o << r.str(); } 162 | explicit inline progressbar_mode(const std::string& s) 163 | : _mode(normal) 164 | { 165 | if (s == "normal") 166 | _mode = normal; 167 | else if (s == "dotline") 168 | _mode = dotline; 169 | else if (s == "simple") 170 | _mode = simple; 171 | else 172 | { 173 | const std::string& m = 174 | (boost::format("invalid option value (`%1%')") % s).str(); 175 | 176 | BOOST_THROW_EXCEPTION(cc::error::tuple() 177 | << cc::error::errinfo_tuple(boost::make_tuple(OPT__PROGRESSBAR,m))); 178 | } 179 | _str = s; 180 | } 181 | inline const std::string& str() const { return _str; } 182 | inline mode_type value() const { return _mode; } 183 | private: 184 | std::string _str; 185 | mode_type _mode; 186 | }; 187 | 188 | inline static void validate(boost::any& v, 189 | const std::vector<std::string>& values, 190 | progressbar_mode*, int) 191 | { 192 | po::validators::check_first_occurrence(v); 193 | const std::string& s = po::validators::get_single_string(values); 194 | v = boost::any(progressbar_mode(s)); 195 | } 196 | 197 | struct integer 198 | { 199 | inline friend ostr& operator<<(ostr& o, const integer& r) 200 | { return o << r._value; } 201 | inline int value() const { return _value; } 202 | protected: 203 | explicit inline integer(): _value(0) { } 204 | inline static int cast(const std::string& optname, const std::string& s) 205 | { 206 | int r = 0; 207 | try 208 | { 209 | r = boost::lexical_cast<int>(s); 210 | } 211 | catch (const boost::bad_lexical_cast&) 212 | { 213 | const std::string& m = (boost::format("invalid option value: " 214 | "not an integer (`%1%')") 215 | % s).str(); 216 | integer::failed(optname, m); 217 | } 218 | return r; 219 | } 220 | inline static void failed(const std::string& optname, const std::string& m) 221 | { 222 | BOOST_THROW_EXCEPTION(cc::error::tuple() 223 | << cc::error::errinfo_tuple(boost::make_tuple(optname, m))); 224 | } 225 | virtual void set(const std::string&, const std::string&) = 0; 226 | virtual void set(const int, const std::string&) = 0; 227 | protected: 228 | int _value; 229 | }; 230 | 231 | struct integer_ge0 : integer // integer is greater than or equal to 0 232 | { 233 | protected: 234 | explicit inline integer_ge0(const std::string& optname,const std::string& s) 235 | : integer() { set(optname, s); } 236 | // _optname remains unset -- intended to be used by default_value only: 237 | explicit inline integer_ge0(const int n): integer() { set(n); } 238 | private: 239 | inline void set(const std::string& optname, const std::string& s) 240 | { set(integer::cast(optname, s), optname); } 241 | inline void set(const int n, const std::string& optname="") 242 | { 243 | if (n <0) 244 | { 245 | const std::string& m = 246 | (boost::format("invalid option value (%1% < 0)") % n).str(); 247 | 248 | integer::failed(optname, m); 249 | } 250 | _value = n; 251 | } 252 | }; 253 | 254 | struct dns_cache_timeout : integer_ge0 255 | { 256 | explicit inline dns_cache_timeout(int n): integer_ge0(n) { } 257 | explicit inline dns_cache_timeout(const std::string& s) 258 | : integer_ge0(OPT__DNS_CACHE_TIMEOUT, s) { } 259 | }; 260 | 261 | struct transfer_timeout : integer_ge0 262 | { 263 | explicit inline transfer_timeout(int n): integer_ge0(n) { } 264 | explicit inline transfer_timeout(const std::string& s) 265 | : integer_ge0(OPT__TRANSFER_TIMEOUT, s) { } 266 | }; 267 | 268 | struct connect_timeout : integer_ge0 269 | { 270 | explicit inline connect_timeout(int n): integer_ge0(n) { } 271 | explicit inline connect_timeout(const std::string& s) 272 | : integer_ge0(OPT__CONNECT_TIMEOUT, s) { } 273 | }; 274 | 275 | struct max_retries : integer_ge0 276 | { 277 | explicit inline max_retries(int n): integer_ge0(n) { } 278 | explicit inline max_retries(const std::string& s) 279 | : integer_ge0(OPT__MAX_RETRIES, s) { } 280 | }; 281 | 282 | struct retry_wait : integer_ge0 283 | { 284 | explicit inline retry_wait(int n): integer_ge0(n) { } 285 | explicit inline retry_wait(const std::string& s) 286 | : integer_ge0(OPT__RETRY_WAIT, s) { } 287 | }; 288 | 289 | struct throttle : integer_ge0 290 | { 291 | explicit inline throttle(int n): integer_ge0(n) { } 292 | explicit inline throttle(const std::string& s) 293 | : integer_ge0(OPT__THROTTLE, s) { } 294 | }; 295 | 296 | template <class T> 297 | inline static void integer_helper(boost::any& v, 298 | const std::vector<std::string>& values) 299 | { 300 | po::validators::check_first_occurrence(v); 301 | const std::string& s = po::validators::get_single_string(values); 302 | v = boost::any(T(s)); 303 | } 304 | 305 | inline static void validate(boost::any& v, 306 | const std::vector<std::string>& values, 307 | dns_cache_timeout*, int) 308 | { 309 | integer_helper<dns_cache_timeout>(v, values); 310 | } 311 | 312 | inline static void validate(boost::any& v, 313 | const std::vector<std::string>& values, 314 | transfer_timeout*, int) 315 | { 316 | integer_helper<transfer_timeout>(v, values); 317 | } 318 | 319 | inline static void validate(boost::any& v, 320 | const std::vector<std::string>& values, 321 | connect_timeout*, int) 322 | { 323 | integer_helper<connect_timeout>(v, values); 324 | } 325 | 326 | inline static void validate(boost::any& v, 327 | const std::vector<std::string>& values, 328 | max_retries*, int) 329 | { 330 | integer_helper<max_retries>(v, values); 331 | } 332 | 333 | inline static void validate(boost::any& v, 334 | const std::vector<std::string>& values, 335 | retry_wait*, int) 336 | { 337 | integer_helper<retry_wait>(v, values); 338 | } 339 | 340 | inline static void validate(boost::any& v, 341 | const std::vector<std::string>& values, 342 | throttle*, int) 343 | { 344 | integer_helper<throttle>(v, values); 345 | } 346 | 347 | struct integer_gt0 : integer // integer is greater than 0 348 | { 349 | protected: 350 | explicit inline integer_gt0(const std::string& optname,const std::string& s) 351 | : integer() { set(optname, s); } 352 | // _optname remains unset -- intended to be used by default_value only: 353 | explicit inline integer_gt0(const int n): integer() { set(n); } 354 | private: 355 | inline void set(const std::string& optname, const std::string& s) 356 | { set(integer::cast(optname, s), optname); } 357 | inline void set(const int n, const std::string& optname="") 358 | { 359 | if (n <=0) 360 | { 361 | const std::string& m = 362 | (boost::format("invalid option value (%1% < 1)") % n).str(); 363 | 364 | integer::failed(optname, m); 365 | } 366 | _value = n; 367 | } 368 | }; 369 | 370 | struct update_interval : integer_gt0 371 | { 372 | explicit inline update_interval(int n): integer_gt0(n) { } 373 | explicit inline update_interval(const std::string& s) 374 | : integer_gt0(OPT__UPDATE_INTERVAL, s) { } 375 | }; 376 | 377 | inline static void validate(boost::any& v, 378 | const std::vector<std::string>& values, 379 | update_interval*, int) 380 | { 381 | integer_helper<update_interval>(v, values); 382 | } 383 | 384 | struct options : boost::noncopyable 385 | { 386 | explicit inline options(int const argc, char const **argv) 387 | { 388 | fs::path cwd(fs::current_path()); 389 | fs::path fpath(cwd); 390 | 391 | const char *h = getenv("HOME"); 392 | if (h && strlen(h) >0) 393 | fpath = fs::system_complete(fs::path(h) / ".ccliverc"); 394 | 395 | const std::string default_config = fpath.string(); 396 | std::string config; 397 | 398 | po::options_description podc("Core"); 399 | 400 | podc.add_options() 401 | (OPT__CONFIG_FILE",F", 402 | po::value<std::string>(&config)->default_value(fpath.string()), 403 | "Initialization file to read") 404 | (OPT__NO_DOWNLOAD",n", 405 | po::bool_switch(), 406 | "Skip download, print media details only") 407 | #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) 408 | (OPT__BACKGROUND",b", 409 | po::bool_switch(), 410 | "Go to background after startup") 411 | #endif 412 | ; 413 | 414 | po::options_description podcc("Core -- Configurable"); 415 | 416 | podcc.add_options() 417 | (OPT__TIMESTAMP",N", 418 | po::bool_switch(), 419 | "Try to preserve file modification time") 420 | (OPT__CONTINUE",c", 421 | po::bool_switch(), 422 | "Try to resume partially downloaded media") 423 | (OPT__STREAM",s", 424 | po::value<std::string>()->default_value("default"), 425 | "Select media stream to download") 426 | (OPT__EXEC",e", 427 | po::value<vs>(), "Execute child program after download") 428 | ; 429 | 430 | po::options_description podi("Informative"); 431 | 432 | podi.add_options() 433 | (OPT__PRINT_STREAMS",S", 434 | po::bool_switch(), 435 | "Print available media streams for media URL") 436 | (OPT__PRINT_CONFIG",D", 437 | po::bool_switch(), 438 | "Print value of defined config options and exit") 439 | (OPT__SUPPORT",u", 440 | po::value<bool>()->zero_tokens()->implicit_value(true) 441 | ->notifier(print_support), 442 | "Print supported media host domains and exit") 443 | (OPT__VERSION",v", 444 | po::value<bool>()->zero_tokens()->implicit_value(true) 445 | ->notifier(print_version), 446 | "Print version information and exit") 447 | (OPT__HELP",h", 448 | po::bool_switch(), 449 | "Print help and exit") 450 | ; 451 | 452 | po::options_description podo("Output"); 453 | 454 | podo.add_options() 455 | (OPT__VERBOSE_LIBCURL",B", 456 | po::bool_switch(), 457 | "Enable verbose output for libcurl") 458 | (OPT__OUTPUT_FILE",O", 459 | po::value<std::string>(), 460 | "Write downloaded media to file") 461 | (OPT__OVERWRITE",W", 462 | po::bool_switch(), 463 | "Overwrite existing files") 464 | (OPT__LOG_FILE",l", 465 | po::value<std::string>()->default_value("cclive_log"), 466 | "Write log output to file") 467 | (OPT__QUIET",q", 468 | po::bool_switch(), 469 | "Turn off all output but errors/libcurl") 470 | ; 471 | 472 | po::options_description podoc("Output -- Configurable"); 473 | 474 | const vtr _default_tr = default_tr(); 475 | 476 | podoc.add_options() 477 | (OPT__FILENAME_FORMAT",f", 478 | po::value<std::string>()->default_value("%t.%s"), 479 | "Format of media file name") 480 | (OPT__UPDATE_INTERVAL",i", 481 | po::value<update_interval>()->default_value(update_interval(1)), 482 | "Progressbar update interval in seconds") 483 | (OPT__PROGRESSBAR",R", 484 | po::value<progressbar_mode>() 485 | ->default_value(progressbar_mode("normal")), 486 | "Use progressbar mode") 487 | (OPT__OUTPUT_DIR",d", 488 | po::value<std::string>()->default_value(cwd.string()), 489 | "Write downloaded media to directory") 490 | (OPT__TR",t", 491 | po::value<vtr>() 492 | ->default_value(_default_tr, _default_tr[0].str()), 493 | "Translate characters in media title") 494 | ; 495 | 496 | po::options_description podn("Network"); 497 | 498 | podn.add_options() 499 | (OPT__NO_PROXY",X", 500 | po::bool_switch(), 501 | "Disable use of HTTP proxy") 502 | ; 503 | 504 | po::options_description podnc("Network -- Configurable"); 505 | 506 | podnc.add_options() 507 | (OPT__DNS_CACHE_TIMEOUT",A", 508 | po::value<dns_cache_timeout>()->default_value(dns_cache_timeout(60)), 509 | "Seconds DNS resolves kept in memory") 510 | (OPT__TRANSFER_TIMEOUT",T", 511 | po::value<transfer_timeout>()->default_value(transfer_timeout(0),"off"), 512 | "Seconds transfer allowed to take") 513 | (OPT__CONNECT_TIMEOUT",C", 514 | po::value<connect_timeout>()->default_value(connect_timeout(30)), 515 | "Seconds connecting allowed to take") 516 | (OPT__MAX_RETRIES",m", 517 | po::value<max_retries>()->default_value(max_retries(5)), 518 | "Max download attempts before giving up") 519 | (OPT__RETRY_WAIT",w", 520 | po::value<retry_wait>()->default_value(retry_wait(5)), 521 | "Time to wait before retrying") 522 | (OPT__NO_RESOLVE",r", 523 | po::bool_switch(), 524 | "Do not resolve HTTP URL redirections") 525 | (OPT__THROTTLE",H", 526 | po::value<throttle>()->default_value(throttle(0),"off"), 527 | "Do not exceed transfer rate (KB/s)") 528 | (OPT__AGENT",a", 529 | po::value<std::string>()->default_value("Mozilla/5.0"), 530 | "Identify as arg to HTTP servers") 531 | (OPT__PROXY",x", 532 | po::value<std::string>(), 533 | "Use proxy for HTTP connections") 534 | ; 535 | 536 | po::options_description poddc("Deprecated -- Configurable"); 537 | 538 | poddc.add_options() 539 | (OPT__PREFER_FORMAT",p", 540 | po::value<vs>(), 541 | "Specify preferred stream for media hosts") 542 | ; 543 | 544 | // Visible. 545 | 546 | po::options_description podv; 547 | podv.add(podc).add(podcc).add(podi).add(podo).add(podoc) 548 | .add(podn).add(podnc).add(poddc); 549 | 550 | // Hidden. 551 | 552 | po::options_description podh; 553 | podh.add_options()(OPT__URL, po::value<vs>()); 554 | 555 | // Command line. 556 | 557 | po::options_description podl; 558 | podl.add(podv).add(podh); 559 | 560 | // Config. 561 | 562 | po::options_description podf; 563 | podf.add(podcc).add(podoc).add(podnc).add(poddc); 564 | 565 | // URL. 566 | 567 | po::positional_options_description ppod; 568 | ppod.add(OPT__URL, -1); 569 | 570 | try 571 | { 572 | store(po::command_line_parser(argc, argv) 573 | .options(podl).positional(ppod).run(), _vm); 574 | notify(_vm); 575 | } 576 | #ifdef HAVE_BUG__BOOST_PO__MULTIPLE_OCCURRENCES 577 | catch (const po::multiple_occurrences& x) 578 | { 579 | BOOST_THROW_EXCEPTION(cc::error::tuple() 580 | << cc::error::errinfo_tuple( 581 | boost::make_tuple(x.get_option_name(), x.what()))); 582 | } 583 | #endif 584 | catch (...) 585 | { 586 | throw; 587 | } 588 | 589 | if_optsw_given(_vm, OPT__HELP) 590 | { 591 | std::cout 592 | << boost::format("Usage: %1% [options] [args]") % PACKAGE_NAME 593 | << "\n" << podv << std::endl; 594 | BOOST_THROW_EXCEPTION(cc::exit_program()); 595 | } 596 | 597 | fpath = fs::system_complete(config); 598 | 599 | std::ifstream ifs(fpath.string().c_str()); 600 | if (ifs) 601 | { 602 | try 603 | { 604 | store(parse_config_file(ifs, podf), _vm); 605 | notify(_vm); 606 | } 607 | #ifdef HAVE_BUG__BOOST_PO__MULTIPLE_OCCURRENCES 608 | catch (const po::multiple_occurrences& x) 609 | { 610 | BOOST_THROW_EXCEPTION(cc::error::tuple() 611 | << cc::error::errinfo_tuple( 612 | boost::make_tuple(x.get_option_name(), x.what())) 613 | << boost::errinfo_file_name(fpath.string())); 614 | } 615 | #endif 616 | catch (const boost::exception& x) 617 | { 618 | x << boost::errinfo_file_name(fpath.string()); 619 | throw; 620 | } 621 | } 622 | else 623 | { 624 | // Unless this is the default config, raise an error if open failed. 625 | if (default_config != fpath) 626 | { 627 | BOOST_THROW_EXCEPTION(cc::error::config() 628 | << boost::errinfo_file_name(fpath.string()) 629 | << boost::errinfo_errno(errno)); 630 | } 631 | } 632 | 633 | if_optsw_given(_vm, OPT__PRINT_CONFIG) 634 | dump(); 635 | 636 | chk_depr(); 637 | } 638 | 639 | inline const po::variables_map& values() const { return _vm; } 640 | 641 | private: 642 | inline void dump() 643 | { 644 | for (po::variables_map::iterator i = _vm.begin(); i != _vm.end(); ++i) 645 | { 646 | const po::variable_value &v = i->second; 647 | 648 | if (v.empty()) 649 | continue; 650 | 651 | const std::type_info &t = v.value().type(); 652 | bool nl = true; 653 | 654 | if (t == typeid(bool)) 655 | { 656 | std::cout << boost::format("%1% is %2%") 657 | % i->first 658 | % ((v.as<bool>() ? "set" : "unset")); 659 | } 660 | else if (t == typeid(vs)) 661 | { 662 | const vs& values = v.as<vs>(); 663 | BOOST_FOREACH(const std::string& s, values) 664 | std::cout << i->first << "=" << s << "\n"; 665 | nl = false; 666 | } 667 | else if (t == typeid(vtr)) 668 | { 669 | const vtr& values = v.as<vtr>(); 670 | BOOST_FOREACH(const tr& r, values) 671 | std::cout << i->first << "=" << r << "\n"; 672 | nl = false; 673 | } 674 | else 675 | { 676 | std::cout << i->first << "="; 677 | if (t == typeid(dns_cache_timeout)) 678 | std::cout << v.as<dns_cache_timeout>(); 679 | else if (t == typeid(progressbar_mode)) 680 | std::cout << v.as<progressbar_mode>(); 681 | else if (t == typeid(transfer_timeout)) 682 | std::cout << v.as<transfer_timeout>(); 683 | else if (t == typeid(update_interval)) 684 | std::cout << v.as<update_interval>(); 685 | else if (t == typeid(connect_timeout)) 686 | std::cout << v.as<connect_timeout>(); 687 | else if (t == typeid(max_retries)) 688 | std::cout << v.as<max_retries>(); 689 | else if (t == typeid(retry_wait)) 690 | std::cout << v.as<retry_wait>(); 691 | else if (t == typeid(std::string)) 692 | std::cout << v.as<std::string>(); 693 | else if (t == typeid(throttle)) 694 | std::cout << v.as<throttle>(); 695 | else if (t == typeid(double)) 696 | std::cout << v.as<double>(); 697 | else if (t == typeid(int)) 698 | std::cout << v.as<int>(); 699 | else if (t == typeid(tr)) 700 | std::cout << v.as<tr>(); 701 | else 702 | std::cout << "skipped: an unknown option type"; 703 | } 704 | 705 | if (!nl) 706 | std::cout << std::flush; 707 | else 708 | std::cout << std::endl; 709 | } 710 | BOOST_THROW_EXCEPTION(cc::exit_program()); 711 | } 712 | 713 | inline static void depr_msg(const std::string& depr_optname, 714 | const std::string& repl_optname) 715 | { 716 | std::clog << boost::format("[!] Option `--%1%' is deprecated and " 717 | "will be removed later") % depr_optname; 718 | if (repl_optname.length() >0) 719 | { 720 | std::clog << boost::format("\n[!] Consider using the option " 721 | "`--%1%', instead") % repl_optname; 722 | } 723 | std::clog << std::endl; 724 | } 725 | 726 | #define if_given(o) if (_vm.count(o)) 727 | #define was_given_norepl(o) if_given(o) depr_msg(o,"") 728 | #define was_given(o,r) if_given(o) depr_msg(o,r) 729 | inline void chk_depr() 730 | { 731 | #ifdef HAVE_LIBQUVI_0_9 732 | was_given("prefer-format", "stream"); 733 | #endif 734 | } 735 | #undef was_given_norepl 736 | #undef was_given 737 | #undef if_given 738 | 739 | inline static void print_version(const bool&) 740 | { 741 | std::cout << PACKAGE_NAME" " 742 | #ifdef VN 743 | << VN 744 | #else 745 | << PACKAGE_VERSION 746 | #endif 747 | << "\n built on " << BUILD_TIME 748 | << " for " << CANONICAL_TARGET 749 | << "\n with " << CXX", "CXXFLAGS 750 | << "\n libquvi " << quvi::version() 751 | << "\n libquvi-scripts " 752 | << quvi_version(QUVI_VERSION_SCRIPTS) 753 | << std::endl; 754 | 755 | static const char copyr[] = 756 | "\nCopyright (C) 2010-2013 Toni Gundogdu <legatvs@gmail.com>\n" 757 | PACKAGE_NAME 758 | " comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of\n" 759 | PACKAGE_NAME 760 | " under the terms of the GNU Affero General Public License version\n" 761 | "3 or later. For more information, see " 762 | "<http://www.gnu.org/licenses/agpl.html>.\n\n" 763 | "To contact the developers, please mail to " 764 | "<cclive-devel@lists.sourceforge.net>"; 765 | 766 | std::cerr << copyr << std::endl; 767 | BOOST_THROW_EXCEPTION(cc::exit_program()); 768 | } 769 | 770 | inline static void print_support(const int&) 771 | { 772 | quvi::query q; 773 | std::cout << quvi::support_to_s(q.support()) << std::flush; 774 | BOOST_THROW_EXCEPTION(cc::exit_program()); 775 | } 776 | 777 | static vtr default_tr() 778 | { 779 | vtr r; 780 | r.push_back(tr("/(\\w|\\pL|\\s)/g")); 781 | return r; 782 | } 783 | 784 | private: 785 | po::variables_map _vm; 786 | }; 787 | 788 | } // namespace cc 789 | 790 | #endif // cc__options_h 791 | 792 | // vim: set ts=2 sw=2 tw=72 expandtab: 793 | --------------------------------------------------------------------------------