Add "version" to --version output
[grake.git] / bin / grake
blobc3f59b3ba28f2718346cef7ddc7564fd395f8b05
1 #!/usr/bin/perl
2 # -*- coding: ascii -*-
5 # Copyright (C) 2010 Toni Gundogdu <legatvs@gmail.com>.
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 use warnings;
22 use strict;
24 use version 0.77 (); our $VERSION = version->declare("0.0.6");
26 binmode STDOUT, ":utf8";
27 binmode STDERR, ":utf8";
29 use Getopt::ArgvFile( home => 1, startupFilename => [qw(.grakerc)] );
30 use Getopt::Long qw(:config bundling);
32 my %config;
34 exit main();
36 sub init {
37 GetOptions(
38 \%config,
39 'interactive|i',
40 'title|t',
41 'json',
42 'csv',
43 'proxy=s',
44 'no_proxy|no-proxy',
45 'quiet|q',
46 'version' => \&print_version,
47 'license' => \&print_license,
48 'help' => \&print_help,
49 ) or exit 1;
51 $config{title} ||= $config{json};
52 $config{title} ||= $config{csv};
55 sub print_version {
56 print "grake version $VERSION\n";
57 exit 0;
60 sub print_license {
61 print
62 "Copyright (C) 2010 Toni Gundogdu. GNU GPL v3+. This is free software;
63 see the source for copying conditions. There is NO warranty; not even
64 for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
66 exit 0;
69 sub print_help {
70 require Pod::Usage;
71 Pod::Usage::pod2usage( -exitstatus => 0, -verbose => 1 );
74 my @ids;
75 my @links;
77 sub main {
79 init();
81 print_help() unless scalar @ARGV;
83 print STDERR "Checking ..." unless $config{quiet};
85 require LWP;
87 my $a = new LWP::UserAgent;
89 $a->env_proxy; # http://search.cpan.org/perldoc?LWP::UserAgent
91 $a->proxy( 'http', $config{proxy} ) if $config{proxy};
92 $a->no_proxy('') if $config{no_proxy};
94 require URI::Escape;
96 my $q = qr{v[=/]((?>[-_\w]{11}))};
98 foreach (@ARGV) {
100 my $r = $a->get($_);
102 unless ( $r->is_success ) {
103 printf STDERR "\nerror: $_: %s\n", $r->status_line;
104 next;
107 my $d = URI::Escape::uri_unescape( $r->content );
108 @ids = uniq( ( @ids, $d =~ /$q/g ) );
110 print STDERR "." unless $config{quiet};
113 unless ( scalar @ids ) {
114 print STDERR "error: nothing found.\n";
115 return 0;
117 else { print STDERR "done.\n" unless $config{quiet}; }
119 print STDERR "Getting ..." unless $config{quiet};
121 foreach my $id (@ids) {
123 my %tmp = (
124 id => $id,
125 url => "http://youtube.com/watch?v=$id",
126 gvi => "http://www.youtube.com/get_video_info?&video_id=$id"
127 . "&el=detailpage&ps=default&eurl=&gl=US&hl=en",
128 title => undef,
129 selected => 1
132 $tmp{title} = get_title( $a, $tmp{gvi} ) if $config{title};
134 push @links, \%tmp;
137 print STDERR "done.\n" unless $config{quiet};
139 prompt() if $config{interactive};
141 if ( $config{json} ) { print qq/{\n "video": [\n/; }
143 my $i = 0;
145 foreach (@links) {
147 if ( $_->{selected} or not $config{interactive} ) {
149 ++$i;
151 my $t = $_->{title} || "";
152 $t =~ s/"/\\"/g;
154 if ( $config{json} ) {
156 print ",\n" if $i > 1;
158 print " {\n"
159 . qq/ "title": "$t",\n/
160 . qq/ "url": "$_->{url}"\n/ . " }",
164 elsif ( $config{csv} ) { print qq/"$t","$_->{url}"\n/; }
166 else { print "$_->{url}\n"; }
172 if ( $config{json} ) { print "\n ]\n}\n"; }
174 return 0;
177 sub get_title {
179 my ( $a, $url ) = @_;
181 my $r = $a->get($url);
183 unless ( $r->is_success ) {
184 printf STDERR "\nerror: $url: %s\n", $r->status_line;
185 return;
188 my $title;
190 require CGI;
192 my $q = CGI->new( $r->content );
194 if ( $q->param('reason') ) {
195 printf STDERR "\nerror: %s: %s (errorcode: %d)\n",
196 $url, $q->param("reason"), $q->param("errorcode");
198 else {
199 require Encode;
200 $title = Encode::decode_utf8( $q->param('title') );
203 unless ($title) {
204 print STDERR "\nwarning: $url: use id instead\n"
205 unless $config{quiet};
207 else { print STDERR "." unless $config{quiet}; }
209 return $title;
212 sub uniq {
213 return keys %{ { map { $_ => 1 } @_ } };
214 } # Original order lost.
216 my $done = 0;
218 sub prompt {
220 my %cmds = (
221 'h' => \&help,
222 'q' => \&quit,
223 'l' => \&list,
224 'a' => \&select_all,
225 'n' => \&select_none,
226 'i' => \&invert_selection,
227 'd' => \&dump,
230 print STDERR
231 "Enter prompt. Type \"help\" to get a list of commands.\n";
232 list();
234 my $p = "(grake) ";
236 while ( not $done ) {
238 print STDERR $p;
240 my $ln = <STDIN>;
242 next unless $ln;
243 chomp $ln;
245 if ( $ln =~ /(\d+)/ ) { toggle_caption($1); }
247 else {
248 next unless $ln =~ /(\w)/;
249 $cmds{$1}() if defined $cmds{$1};
256 sub help {
257 print STDERR "Commands:
258 help .. this
259 list .. display found links (> indicates selected for download)
260 all .. select all
261 none .. select none
262 invert .. invert selection
263 (number) .. toggle caption
264 dump .. dump selected links and exit
265 quit .. quit without dumping links\n"
266 . qq/Command name abbreviations are allowed, e.g. "h" instead of "help"\n/;
269 sub quit { exit 0; }
271 sub list {
272 my $i = 0;
273 foreach (@links) {
274 printf STDERR "%2s%02d: %s\n", $_->{selected}
275 ? ">"
276 : "",
277 ++$i,
278 $_->{title} || $_->{url};
282 sub select_all {
283 $_->{selected} = 1 foreach @links;
284 list();
287 sub select_none {
288 $_->{selected} = 0 foreach @links;
289 list();
292 sub invert_selection {
293 $_->{selected} = not $_->{selected} foreach @links;
294 list();
297 sub dump { $done = 1; }
299 sub toggle_caption {
300 my $i = (shift) - 1;
301 if ( $i >= 0 && exists $links[$i] ) {
302 $links[$i]->{selected} = not $links[$i]->{selected};
303 list();
305 else { print STDERR "error: out of range\n"; }
308 __END__
310 =head1 NAME
312 grake - Youtube video link scanner
314 =head1 SYNOPSIS
316 grake [-q] [-i] [-t] [--csv | --json] [--proxy E<lt>addrE<gt> | --no-proxy]
317 [<url>...]
319 =head1 DESCRIPTION
321 grake is a command line tool for scanning webpages for Youtube video links.
322 Each found link is separated with a newline and dumped to the standard output.
324 You can use grake together with such tools like C<cclive(1)>. If you
325 need to select the videos, use the C<--interactive> switch.
327 =head1 OPTIONS
329 --help print help and exit
330 --version print version and exit
331 --license print license and exit
332 -q, --quiet be quiet
333 -i, --interactive run in interactive mode
334 -t, --title get title for video link
335 --json print details in json, implies -t
336 --csv print details in csv, implies -t
337 --proxy arg (=http_env) use proxy for http connections
338 --no-proxy disable use of http proxy
340 =head1 OPTION DESCRIPTIONS
342 =over 4
344 =item B<--help>
346 Print help and exit.
348 =item B<--version>
350 Print version and exit.
352 =item B<--license>
354 Print license and exit.
356 =item B<-q, --quiet>
358 Be quiet.
360 =item B<-i, --interactive>
362 Enable interactive prompt which can be used to select the found
363 video links to be dumped to stdout. By default grake dumps all
364 found links without prompting.
366 =item B<-t, --title>
368 Get a video title for each found link. The default is no.
370 =item B<--json>
372 Print details in JSON. Negates C<--csv>. Implies C<--title>.
374 =item B<--csv>
376 Print details in CSV ("$title","$url"\n). Implies C<--title>.
378 =item B<--proxy> I<arg>
380 Use I<arg> for HTTP proxy, e.g. "http://foo:1234". Overrides the http_proxy
381 environment setting.
383 =item B<--no-proxy>
385 Disable use of HTTP proxy. Overrides both C<--proxy> and http_proxy environment
386 settings.
388 =back
390 =head1 EXAMPLES
392 =over 4
394 =item B<grake "http://youtube.com">
396 Typical use.
398 =item B<grake --json "http://youtube.com">
400 Same but print details in JSON.
402 =item B<grake "http://youtube.com" | cclive>
404 Download the found videos with C<cclive(1)>.
406 =back
408 =head1 EXIT STATUS
410 Exits 0 on success, otherwise E<gt>0;
412 =head1 FILES
414 =over 4
416 =item $HOME/.grakerc, for example:
418 echo "--title" >> ~/.grakerc
420 =back
422 =head1 NOTES
424 =over 4
426 =item B<http_proxy>
428 grake depends on LWP::UserAgent which reads the http_proxy environment
429 setting.
430 env http_proxy=http://foo:1234 grake URL
432 =item B<Project>
434 <http://grake.googlecode.com/>
436 =item B<Development repository>
438 <git://repo.or.cz/grake.git>
440 e.g. git clone git://repo.or.cz/grake.git
442 =back
444 =head1 SEE ALSO
446 C<cclive(1)>
448 =head1 AUTHOR
450 Toni Gundogdu <legatvs gmail com>
452 =cut