#!/usr/bin/env perl

use strict;
use warnings;
use File::Spec::Functions qw(catdir catfile splitdir splitpath);
use List::Util 'first';

my ($LOG_FILE, $ACTION, $NAME, @args) = @ARGV;
die "Log file argument is missing" if !defined $LOG_FILE;
die "Action argument is missing" if !defined $ACTION;
die 'Name argument missing' if !defined $NAME;

my $sub = "_${ACTION}_torrent";
__PACKAGE__->$sub(@args);

sub _inserted_new_torrent {
    shift @_;
    my ($size, $torrent_file) = @args;

    for ($size, $torrent_file) {
        die "An argument is missing for 'inserted_new'" if !defined $_;
    }

    my $user = getpwuid((stat $torrent_file)[4]);
    _torrentlog($ACTION, $NAME, $user, $size);
    return;
}

sub _erased_torrent {
    shift @_;
    my ($size, $down, $up, $ratio) = @args;

    for ($size, $down, $up, $ratio) {
        die "An argument is missing for 'erased'" if !defined $_;
    }

    _torrentlog($ACTION, $NAME, $size, $down, $up, $ratio);
    return;
}

sub _finished_torrent {
    shift @_;
    my ($size, $torrent_file, $path) = @args;

    for ($size, $torrent_file) {
        die "An argument is missing for 'finished'" if !defined $_;
    }

    my @rars;
    my $rar_count = -1;

    if (length $path) {
        chdir $path or die "Can't chdir to $path\n";

        if (-d $NAME) {
            push @rars, _find_rar($NAME);
            push @rars, _find_rar($_) for grep { -d } glob "$NAME/CD*";
        }
        elsif ($NAME =~ /\.rar$/) {
            if (_rar_contents($NAME) > 1) {
                my $dest_dir = $NAME;
                $dest_dir =~ s/\.rar$//;
                mkdir $dest_dir;
                chdir $dest_dir or die "Can't chdir to $dest_dir\n";
            }
            push @rars, $NAME;
        }
        $rar_count = scalar @rars;
    }

    my $enqueued = (stat $torrent_file)[10];
    my $finished = time;
    _torrentlog($ACTION, $NAME, $enqueued, $finished, $size, $rar_count);

    if (@rars) {
        my $unrar_start = time;
        for my $rar (@rars) {
            my $rar_path = catfile($path, $rar);
            exit if fork; # let's not hold up rtorrent while we unrar
            system "unrar x -o+ '$rar_path'" and die "Can't unrar '$rar': $!";
        }
        my $unrar_finish = time;

        _torrentlog('unrar', $NAME, $unrar_start, $unrar_finish, scalar @rars);
    }

    return;
}

sub _torrentlog {
    my @values = @_;
    open my $log, ">>", $LOG_FILE or die "Can't open $LOG_FILE: $!";
    print $log join("\t", @values), "\n";
    close $log;
    return;
}

sub _find_rar {
    my ($dir) = @_;

    my @files = sort grep { chomp; /\.(?:r(?:ar|\d+)|\d+)$/ } qx/find '$dir' -maxdepth 1/;
    if (@files) {
        my $target = $files[0];
        if (my ($rar) = first { /\.rar$/ } @files) {
            $target = $rar;
        }

        return $target if _rar_contents($target) == 1;
    }
    return;
}

sub _rar_contents {
    my ($rar) = @_;
    return split /\n/, qx/unrar lb '$rar'/;
}

=encoding utf8

=head1 NAME

irctor-queue - Log RTorrent actions to a file

=head1 DESCRIPTION

This program is meant to be called by RTorrent's event handlers. It logs
actions to a log file based on the arguments it receives.

Optionally, when RTorrent tells it that a torrent has finished,
I<irctor-queue> can inspect the downloaded content and call L<unrar(1)> in
these situations:

=over 4

=item * The content is a single rar file. If the rar archive contains more
than one file/directory, a directory will be created to contain them, with
the same name as the archive without the ".rar" suffix)

=item * The content is a directory with a rar archive that contains a single
file/directory

=item * The content is a directory containing CD[1-9] subdirectories which
have rar archives (containing only a single file/directory each)

=back

L<POE::Component::IRC::Plugin::RTorrentStatus|POE::Component::IRC::Plugin::RTorrentStatus>
follows the log file created by this program and announces various events on IRC.

=head1 CONFIGURATION

F<.rtorrent.rc> must have the following lines in it. F</tmp/torrentlog> is
the log file it will write to. Change it if you want to keep it elsewhere.

 # irctor-queue hooks, with unrar
 system.method.set_key = event.download.inserted_new,irctor_inserted_new,"execute=irctor-queue,/tmp/torrentlog,inserted_new,$d.get_name=,$d.get_size_bytes=1,$d.get_loaded_file=1"
 system.method.set_key = event.download.finished,    irctor_finished,    "execute=irctor-queue,/tmp/torrentlog,finished,$d.get_name=,$d.get_size_bytes=1,$d.get_loaded_file=1"
 system.method.set_key = event.download.erased,      irctor_erased,      "execute=irctor-queue,/tmp/torrentlog,erased,$d.get_name=,$d.get_size_bytes=1,$d.get_completed_bytes=1,$d.get_up_total=1,$d.get_ratio=1"

=head2 With unrar functionality

If wou want the unrar functionality, you must add the directory where your
completed downloads are kept as an argument to C<irctor_finised>:

 system.method.set_key = event.download.finished, irctor_finished, "execute=irctor-queue,/tmp/torrentlog,finished,$d.get_name=,$d.get_size_bytes=1,$d.get_loaded_file=1,/home/foo/complete"

If you have an RTorrent hook which moves completed downloads to
some directory, make sure the name of that hook comes before (according to
ASCII sorting) the name of the C<irctor_finished> hook. This is necessary
because RTorrent executes hooks in alphabetical order.

=head1 AUTHOR

Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com

=head1 LICENSE AND COPYRIGHT

Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson

This program is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
