X-Git-Url: https://git.camperquake.de/gitweb.cgi?p=xmlrtorrent.git;a=blobdiff_plain;f=xmlrtorrent.pl;h=fae32f70fa8507027a561e1d09aa3dafce711c8a;hp=2089ce82cfca3aca1d97cf49881d35f0bf3be622;hb=HEAD;hpb=65a4e435c4cc6e72c0f852b4abc4374747fb1a55 diff --git a/xmlrtorrent.pl b/xmlrtorrent.pl index 2089ce8..fae32f7 100644 --- a/xmlrtorrent.pl +++ b/xmlrtorrent.pl @@ -10,21 +10,31 @@ use vars qw($VERSION %IRSSI); use XML::Simple; use Data::Dumper; use File::Spec; -use xmlrtorrent; +use List::Util qw(max); +my @talkers; +my $talker; my $conf; my $conffile = File::Spec->catfile(Irssi::get_irssi_dir(), 'xmlrtorrent.xml'); my $scriptdir = File::Spec->catfile(Irssi::get_irssi_dir(), 'scripts'); +my $plugindir = File::Spec->catfile($scriptdir, 'xmlrtorrent'); my %torrentlist = (); my $torrentindex = 1; -my $rtorrent; my @outputstack = (undef); my $PARAMS = { - 'XMLURL' => 'http://localhost/RPC2', + '_QUEUE' => {}, + '_AUTOSAVE' => 1, }; +# Handle module unload/irssi shutdown +sub UNLOAD { + if ($conf->{'xmlrtorrent'}->{'_AUTOSAVE'}) { + cmd_save(); + } +} + # activate debug here my $debug = 0; @@ -71,6 +81,10 @@ my $xmlrtorrent_commands = { cmd_remote(@_); }, + 'talker' => sub { + cmd_talker(@_); + }, + 'debug' => sub { $debug = 1; write_irssi('Enabled debugging'); @@ -80,35 +94,17 @@ my $xmlrtorrent_commands = { $debug = 0; write_irssi('Disabled debugging'); }, -}; - -sub write_irssi { - my @text = @_; - my $output = $outputstack[0]; - - $text[0] = 'xmlrtorrent: ' . $text[0]; - - if (defined($output) and ref($output)) { - $output->print(sprintf(shift(@text), @text), MSGLEVEL_CLIENTCRAP); - } else { - Irssi::print(sprintf(shift(@text), @text)); - } -} - -sub push_output { - unshift(@outputstack, shift); -} - -sub pop_output { - shift(@outputstack); -} + 'autosave' => sub { + $conf->{'xmlrtorrent'}->{'_AUTOSAVE'} = 1; + write_irssi('Autosave enabled'); + }, -sub write_debug { - if ($debug) { - write_irssi(@_); - } -} + 'noautosave' => sub { + $conf->{'xmlrtorrent'}->{'_AUTOSAVE'} = 0; + write_irssi('Autosave disabled'); + }, +}; # This is shamelessly stolen from pythons urlgrabber sub format_number { @@ -137,7 +133,36 @@ sub format_number { return sprintf($format, $number, $symbols[$depth]); } +sub write_irssi { + my @text = @_; + my $output = $outputstack[0]; + + my $format = '%%mxmlrtorrent: %%n' . shift(@text); + + # escape % in parameters from irssi + s/%/%%/g foreach @text; + + if (defined($output) and ref($output)) { + $output->print(sprintf($format, @text), MSGLEVEL_CLIENTCRAP); + } else { + Irssi::print(sprintf($format, @text)); + } + +} + +sub push_output { + unshift(@outputstack, shift); +} + +sub pop_output { + shift(@outputstack); +} +sub write_debug { + if ($debug) { + write_irssi(@_); + } +} sub check_for_link { my ($signal,$parammessage,$paramchannel,$paramnick,$paramserver) = @_; @@ -146,7 +171,6 @@ sub check_for_link { my $message = ($parammessage == -1) ? '' : $signal->[$parammessage]; my $nick = ($paramnick == -1)?defined($server)?$server->{'nick'}:'':$signal->[$paramnick]; my $g; - my $m; my $p; my $witem; @@ -155,27 +179,19 @@ sub check_for_link { } else { $witem = Irssi::window_item_find($target); } - + # Look if we should ignore this line if ($message =~ m,(?:\s|^)/nosave(?:\s|$),) { return; } - + push_output($witem); # Look if there is a torrent link in there - $message =~ m|(http://\S*\.torrent)|; - $m = $1; - while (defined($m)) { - write_debug('Torrent-URL: %s', $m); - $torrentlist{$torrentindex++} = {'CHANNEL' => $target, 'NICK' => $nick, 'URL' => $m}; - - # Remove the matched part from the message and try again (there may be - # more!) - $message =~ s/$m//; - - $message =~ m|(http://.*\.torrent)|; - $m = $1; + + while ($message =~ m,(http://\S*\.(?:torrent|penis)),g) { + write_debug('Torrent-URL: %s', $1); + $torrentlist{$torrentindex++} = {'CHANNEL' => $target, 'NICK' => $nick, 'URL' => $1}; } pop_output(); @@ -198,19 +214,32 @@ sub cmd_queue { $u = $torrentlist{$id}->{'URL'}; write_debug('Sending %s to rtorrent', $u); - unless(defined($rtorrent->load_start($u))) { - write_irssi('Error sending URL %s: %s', $u, $rtorrent->errstr()); + unless(defined($talker->load_start($u))) { + write_irssi('%%RError sending URL %s: %s', $u, $talker->errstr()); } else { + write_irssi('%s enqueued', $u); delete($torrentlist{$id}); } + } elsif ('add' eq $subcmd) { + unless(defined($id)) { + return; + } + $torrentlist{$torrentindex++} = {'CHANNEL' => '', 'NICK' => '', 'URL' => $id}; } elsif (('list' eq $subcmd) or !defined($subcmd)) { + my $l; write_irssi('List of queued torrents'); - foreach (sort(keys(%torrentlist))) { - write_irssi(' %d: %s@%s: %s', $_, - $torrentlist{$_}->{'NICK'}, - $torrentlist{$_}->{'CHANNEL'}, - $torrentlist{$_}->{'URL'}); + if (0 == scalar(keys(%torrentlist))) { + write_irssi(' (no torrents in local queue)'); + } else { + foreach (sort(keys(%torrentlist))) { + write_irssi(' %3d: %s@%s: %s', $_, + $torrentlist{$_}->{'NICK'}, + $torrentlist{$_}->{'CHANNEL'}, + $torrentlist{$_}->{'URL'}); + } } + } else { + write_irssi('Unknown subcommand: %s', $subcmd); } } @@ -219,29 +248,42 @@ sub cmd_remote { my ($subcmd, $id, @params) = @_; my $rqueue; - if ('queue' eq $subcmd) { - unless(defined($rqueue = $rtorrent->download_list())) { - write_irssi('Error getting list of downloads: %s', $rtorrent->errstr()); + if (('list' eq $subcmd) or !defined($subcmd)) { + unless(defined($rqueue = $talker->download_list())) { + write_irssi('Error getting list of downloads: %s', $talker->errstr()); return; } - foreach (@{$rqueue}) { - write_irssi('%s%s: %sB/%sB done, %sb/s up, %sb/s down', - $_->[6]?'*':' ', - $_->[0], - format_number($_->[2]), - format_number($_->[1]), - format_number($_->[3]), - format_number($_->[4])); + write_irssi('List of remote torrents'); + if (0 == scalar(@{$rqueue})) { + write_irssi(' (no torrents in remote queue)'); + } else { + foreach (@{$rqueue}) { + write_irssi(' %s%s: %sB/%sB done (%d%%), %sB/s up, %sB/s down', + $_->{'ACTIVE'}?'*':' ', + $_->{'NAME'}, + format_number($_->{'BYTES_DONE'}), + format_number($_->{'SIZE_BYTES'}), + $_->{'BYTES_DONE'}*100/$_->{'SIZE_BYTES'}, + format_number($_->{'UP_RATE'}), + format_number($_->{'DOWN_RATE'})); + } } } } sub cmd_save { + + my %mappedqueue; + + # XML::Simple has some problems with numbers as nodenames, + # so we have to modify our queue a bit. + %mappedqueue = map {("_$_" => $torrentlist{$_})} keys(%torrentlist); eval { open(CONF, '>'.$conffile) or die 'Could not open config file'; + $conf->{'xmlrtorrent'}->{'_QUEUE'} = \%mappedqueue; print CONF XML::Simple::XMLout($conf, KeepRoot => 1, KeyAttr => {'config' => 'module', 'option' => 'key'}); close(CONF); }; @@ -256,43 +298,84 @@ sub cmd_set { my $target = shift; my $key = shift; my $val = shift; + my $p; - if ('global' eq $target) { - if(exists($PARAMS->{$key})) { - $conf->{'xmlrtorrent'}->{$key} = $val; - if ('XMLURL' eq $key) { - unless(defined($rtorrent = xmlrtorrent->new('XMLURL' => $conf->{'xmlrtorrent'}->{'XMLURL'}))) { - write_irssi('Could not initialize XMLRPC instance'); - return; - } - } - } else { - write_irssi('Key %s does not exist', $key); + foreach $p (@talkers) { + if ($p->{'NAME'} eq $target) { + $p->setval($key, $val); + return; } } + write_irssi('No such module'); } - sub cmd_show { my $target = shift; my $p; my $e; + + if (defined($target)) { + foreach $p (@talkers) { + if ($p->{'NAME'} eq $target) { + write_irssi($p->getconfstr()); + return; + } + } + write_irssi('No such module'); + } else { + write_irssi('Loaded talkers:'); + foreach $p (@talkers) { + write_irssi(' %s', $p->{'NAME'}); + }; + } } sub cmd_help { my $target = shift; my $p; - write_irssi(<<'EOT'); + if (defined($target)) { + foreach $p (@talkers) { + if ($p->{'NAME'} eq $target) { + write_irssi($p->gethelpstr()); + return; + } + } + write_irssi('No such module'); + } else { + write_irssi(<<'EOT'); Supported commands: - save: Save the current configuration - help: Display this help + save: save the current configuration + help [modulename]: display this help or module specific help + show [modulename]: show loaded modules or the current parameters of a module + talker [modulename]: display or set the talker to use debug: enable debugging messages nodebug: disable debugging messages EOT +; + } +} + +sub cmd_talker { + my $target = shift; + my $p; + + if (defined($target)) { + foreach $p (@talkers) { + if (($p->{'NAME'} eq $target) && ($p->{'TYPE'} eq 'talker')) { + $talker = $p; + $conf->{'xmlrtorrent'}->{'talker'} = $target; + return; + } + } + write_irssi('No such talker'); + } else { + write_irssi('Current talker: %s', $conf->{'xmlrtorrent'}->{'talker'}); + } } + # save on unload sub sig_command_script_unload { my $script = shift; @@ -301,6 +384,64 @@ sub sig_command_script_unload { } } +sub ploader { + + my $dir = shift; + my $pattern = shift; + my $type = shift; + my @list; + my $p; + my $g; + my @g = (); + + opendir(D, $dir) || return (); + @list = grep {/$pattern/ && -f File::Spec->catfile($dir, $_) } readdir(D); + closedir(D); + + foreach $p (@list) { + write_debug('Trying to load %s:', $p); + $p =~ s/\.pm$//; + eval qq{ require xmlrtorrent::$p; }; + if ($@) { + write_irssi('Failed to load plugin: %s', "$@"); + next; + } + + $g = eval qq{ xmlrtorrent::$p->new(); }; + if ($@) { + write_irssi('Failed to instanciate: %s', "$@"); + delete($INC{$p}); + next; + } + + write_debug('found %s %s', $g->{'TYPE'}, $g->{'NAME'}); + if ($type eq $g->{'TYPE'}) { + push(@g, $g); + $g->setio(sub {Irssi::print(shift)}); + } else { + write_irssi('%s has wrong type (got %s, expected %s)', $p, $g->{'TYPE'}, $type); + delete($INC{$p}); + } + } + + write_debug('Loaded %d plugins', $#g+1); + + return @g; +} + +sub _load_modules($) { + + my $path = shift; + + foreach (keys(%INC)) { + if ($INC{$_} =~ m|^$path|) { + write_debug('Removing %s from $INC', $_); + delete($INC{$_}); + } + } + @talkers = ploader($path, '.*Talker\.pm$', 'talker'); +} + sub init_xmlrtorrent { my $bindings = shift; @@ -317,11 +458,32 @@ sub init_xmlrtorrent { } } - unless(defined($rtorrent = xmlrtorrent->new('XMLURL' => $conf->{'xmlrtorrent'}->{'XMLURL'}))) { - write_irssi('Could not initialize XMLRPC instance'); + _load_modules($plugindir); + + unless (defined(@talkers)) { + write_irssi('No talkers found, can not proceed.'); return; } + $talker = $talkers[0]; + foreach $p (@talkers) { + if ($conf->{'xmlrtorrent'}->{'talker'} eq $p->{'NAME'}) { + $talker = $p; + } + } + write_debug('Selected %s as talker', $talker->{'NAME'}); + $conf->{'xmlrtorrent'}->{'talker'} = $talker->{'NAME'}; + + # Loop through all plugins and load the config + foreach $p (@talkers) { + $conf->{'xmlrtorrent'}->{'config'}->{$p->{'NAME'}} = $p->mergeconfig($conf->{'xmlrtorrent'}->{'config'}->{$p->{'NAME'}}); + } + + # Restore the queue + %torrentlist = %{$conf->{'xmlrtorrent'}->{'_QUEUE'}}; + %torrentlist = map { my $a = substr($_, 1); ("$a" => $torrentlist{$_}) } keys(%torrentlist); + $torrentindex = max(keys(%torrentlist)) + 1; + if ($bindings) { Irssi::signal_add_first('command script load', 'sig_command_script_unload'); @@ -356,6 +518,8 @@ sub cmdhandler { if (exists($xmlrtorrent_commands->{$cmd})) { $xmlrtorrent_commands->{$cmd}->(@params); + } else { + write_irssi('Unknown command: %s', $cmd); } pop_output();