#!/usr/bin/perl
my $APP = 'xkcd';
use vars qw($VERSION);
$VERSION = '0.082';
use strict;
use WWW::Mechanize;
use Getopt::Long;
use Pod::Usage;
use HTML::Entities qw(decode_entities);
my $m = WWW::Mechanize->new(
onwarn => undef,
onerror => sub { print "Oops. Something went wrong on the server side :(\n"; },
);
$m->agent_alias('Linux Mozilla');
my $xkcd_base = 'http://xkcd.com';
my $xkcd_img = 'http://imgs.xkcd.com/comics';
my $xkcd_url = undef;
our($opt_comic_no, $opt_alt) = (-1, 0);
our($opt_download, @opt_download_all);
our($opt_random);
our($opt_no_zoom);
GetOptions(
'c|comic:i' => \$opt_comic_no,
'a|alt|text' => \$opt_alt,
'l|latest' => sub { $opt_comic_no = -1; },
'download=s' => \$opt_download,
'download-all:i' => \@opt_download_all, # Support range? 30 .. 42
'r|random' => \$opt_random,
'no-zoom' => \$opt_no_zoom,
'h|help' => sub { pod2usage(verbose => 1); },
'm|man' => sub { pod2usage(verbose => 3); },
'v|version' => sub { printf("%s v%s\n", $APP, $VERSION) and exit; },
);
$opt_comic_no = $ARGV[0] if $ARGV[0] =~ /^\d+$/;
if($opt_random) {
$m->get("http://dynamic.xkcd.com/random/comic/");
($opt_comic_no) = $m->base =~ m|(\d+)/?$|;
}
$xkcd_url = "$xkcd_base/$opt_comic_no/";
if(@opt_download_all) {
my $latest_comic = latest($xkcd_base, 1);
for(1 .. $latest_comic) {
download_comic($_, $opt_download);
}
exit;
}
if($opt_download) {
download_comic($opt_comic_no, $opt_download);
exit;
}
if( ($opt_comic_no == -1) and not($opt_alt)) {
latest($xkcd_base);
}
if($opt_alt) {
if($opt_comic_no == -1) {
$opt_comic_no = latest($xkcd_base, 1);
$xkcd_url = $xkcd_base;
}
my(undef, $txt) = fetch($xkcd_url);
$txt = _format($txt);
print "\e[1mxkcd # $opt_comic_no\e[m:\n";
print "$txt\n";
exit;
}
display_img( fetch($xkcd_url) );
sub latest {
my($url, $action) = @_;
$m->get($url) or die($!);
for($m->content) {
if($_ =~ m/Permanent.+: (http:.+)</) {
my $new_url = $1;
($opt_comic_no) = $new_url =~ m|(\d+)/?$|;
if($action) {
return $opt_comic_no;
}
display_img( fetch( $new_url ));
exit;
}
}
return;
}
sub _format {
my $str = shift;
$str = join(".\n", split(/(?:\.|!|\?) /, $str));
$str =~ s/^/ / unless $str =~ /^\s+/;
return $str;
}
sub download_comic {
my($comic, $dest) = @_;
defined $dest or $dest = './';
$dest .= '/' if $dest !~ m;.*/$;;
my $url;
if($comic == -1) {
$url = $xkcd_base;
}
elsif($comic !~ /^\d+$/) {
print STDERR "'$comic' is not an integer!\n" and exit 1;
}
else {
$url = "$xkcd_base/$comic/";
}
my ($img, undef) = fetch($url);
$m->get($img);
my($filename) = $img =~ m;.+/(.+\.(?:png|jpg))$;i;
$filename = "$comic-$filename" unless $comic eq -1;
$dest .= $filename;
print "xkcd #$comic => $dest\n" if $m->save_content($dest) == 0;
return;
}
sub fetch {
my $url = shift;
$m->get($url);
my($img_url, $alt) = (undef) x 2;
for($m->content) {
if(m;<img src="($xkcd_img/.+\.(?:png|jpg))" title="(.+)" alt;) {
$img_url = $1;
$alt = $2;
last;
}
}
return($img_url, decode_entities($alt));
}
sub display_img {
my($url, $text) = @_;
$text = _format($text);
$m->get($url);
if( x() ) {
system(get_viewer(), $url) == 0
and printf("\e[1m%s\e[0m:\n%s\n",
"xkcd # $opt_comic_no", $text,
);
return;
}
print "$url\n";
print "Press any key to continue...\n";
while(<STDIN>) {
last;
}
printf("%s\n%s\n",
$opt_comic_no, $text,
);
}
sub x {
exists($ENV{DISPLAY}) and return 1;
return 0;
}
sub get_viewer {
my $feh = system("which feh &> /dev/null");
if( ($feh << 8) == 0 ) {
my @feh = ('feh', qw(-F -Q -x -B black));
push @feh, '-Z' unless $opt_no_zoom;
return @feh;
}
my $display = system("which display &> /dev/null");
if( ($display << 8) == 0 ) {
return('display');
}
print STDERR "No viewer available.\n" and exit 1;
}
=pod
=head1 NAME
xkcd - xkcd in your console
=head1 USAGE
xkcd [OPTIONS]
=head1 DESCRIPTION
Command-line interface to xkcd. View, download and enjoy xkcd comics straight
from your shell.
=head1 OPTIONS
-c, --comic comic number
-l, --latest show the latest xkcd comic
-r, --random operate on a random xkcd comic
-a, --alt show the 'mouse over' text
-d, --download download the comic to DESTINATION
--download-all download all comics to DESTINATION
--no-zoom don't zoom the comic
-h, --help show the help and exit
-v, --version show version info and exit
-m, --man show the documentation and exit
=head1 PREREQUISITES
To view the xkcd comic in X, B<feh> or B<display> (from the ImageMagick suite)
needs to be installed.
=head1 AUTHOR
Magnus Woldrich
CPAN ID: WOLDRICH
magnus@trapd00r.se
http://japh.se
=head1 REPORTING BUGS
Report bugs on the issue tracker ( L<https://github.com/trapd00r/xkcd/issues> )
or by mail to <magnus@trapd00r.se>
=head1 COPYRIGHT
Copyright (C) 2010, 2011 Magnus Woldrich. All right reserved.
This program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
=cut