line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package POE::Component::IRC::Plugin::RTorrentStatus; |
2
|
|
|
|
|
|
|
BEGIN { |
3
|
2
|
|
|
2
|
|
386439
|
$POE::Component::IRC::Plugin::RTorrentStatus::AUTHORITY = 'cpan:HINRIK'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
BEGIN { |
6
|
2
|
|
|
2
|
|
33
|
$POE::Component::IRC::Plugin::RTorrentStatus::VERSION = '0.17'; |
7
|
|
|
|
|
|
|
} |
8
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
19
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
108
|
|
10
|
2
|
|
|
2
|
|
12
|
use warnings FATAL => 'all'; |
|
2
|
|
|
|
|
12
|
|
|
2
|
|
|
|
|
97
|
|
11
|
2
|
|
|
2
|
|
9
|
use Carp qw(croak); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
243
|
|
12
|
2
|
|
|
2
|
|
2572
|
use DateTime; |
|
2
|
|
|
|
|
316388
|
|
|
2
|
|
|
|
|
81
|
|
13
|
2
|
|
|
2
|
|
2095
|
use DateTime::Format::Human::Duration; |
|
2
|
|
|
|
|
5186
|
|
|
2
|
|
|
|
|
75
|
|
14
|
2
|
|
|
2
|
|
15
|
use File::Glob ':glob'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
610
|
|
15
|
2
|
|
|
2
|
|
15
|
use File::Spec::Functions 'rel2abs'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
115
|
|
16
|
2
|
|
|
2
|
|
4348
|
use Format::Human::Bytes; |
|
2
|
|
|
|
|
1072
|
|
|
2
|
|
|
|
|
63
|
|
17
|
2
|
|
|
2
|
|
869
|
use POE::Component::IRC::Plugin qw(PCI_EAT_NONE); |
|
2
|
|
|
|
|
390
|
|
|
2
|
|
|
|
|
252
|
|
18
|
2
|
|
|
2
|
|
1784
|
use POE::Component::IRC::Common qw(NORMAL DARK_GREEN DARK_BLUE ORANGE TEAL BROWN PURPLE MAGENTA); |
|
2
|
|
|
|
|
24438
|
|
|
2
|
|
|
|
|
268
|
|
19
|
2
|
|
|
2
|
|
2016
|
use POE::Component::IRC::Plugin::FollowTail; |
|
2
|
|
|
|
|
206471
|
|
|
2
|
|
|
|
|
2895
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub new { |
22
|
1
|
|
|
1
|
1
|
7171
|
my ($package, %args) = @_; |
23
|
1
|
|
|
|
|
5
|
my $self = bless \%args, $package; |
24
|
|
|
|
|
|
|
|
25
|
1
|
50
|
|
|
|
10
|
if (!defined $self->{Torrent_log}) { |
26
|
0
|
|
|
|
|
0
|
croak __PACKAGE__ . ": No torrent log file defined"; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
1
|
50
|
33
|
|
|
16
|
if (ref $self->{Channels} ne 'ARRAY' || !$self->{Channels}) { |
30
|
0
|
|
|
|
|
0
|
croak __PACKAGE__ . ': No channels defined'; |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
1
|
|
|
|
|
46
|
$self->{Torrent_log} = rel2abs(bsd_glob($self->{Torrent_log})); |
34
|
1
|
50
|
|
|
|
40
|
if (!-e $self->{Torrent_log}) { |
35
|
0
|
0
|
|
|
|
0
|
open my $foo, '>', $self->{Torrent_log} |
36
|
|
|
|
|
|
|
or die "Can't create $self->{Torrent_log}: $!\n"; |
37
|
0
|
|
|
|
|
0
|
close $foo; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# defaults |
41
|
1
|
50
|
|
|
|
6
|
$self->{Method} = 'notice' if !defined $self->{Method}; |
42
|
1
|
50
|
|
|
|
4
|
$self->{Color} = 1 if !defined $self->{Color}; |
43
|
|
|
|
|
|
|
|
44
|
1
|
|
|
|
|
4
|
return $self; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub PCI_register { |
48
|
1
|
|
|
1
|
0
|
503
|
my ($self, $irc) = @_; |
49
|
|
|
|
|
|
|
|
50
|
1
|
|
|
|
|
13
|
$irc->plugin_add(TorrentTail => POE::Component::IRC::Plugin::FollowTail->new( |
51
|
|
|
|
|
|
|
filename => $self->{Torrent_log}, |
52
|
|
|
|
|
|
|
)); |
53
|
|
|
|
|
|
|
|
54
|
1
|
|
|
|
|
1033
|
$irc->plugin_register($self, 'SERVER', qw(tail_input)); |
55
|
1
|
|
|
|
|
34
|
return 1; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub PCI_unregister { |
59
|
1
|
|
|
1
|
0
|
2396
|
my ($self, $irc) = @_; |
60
|
1
|
|
|
|
|
4
|
return 1; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub S_tail_input { |
64
|
0
|
|
|
0
|
0
|
|
my ($self, $irc) = splice @_, 0, 2; |
65
|
0
|
|
|
|
|
|
my $filename = ${ $_[0] }; |
|
0
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
my $input = ${ $_[1] }; |
|
0
|
|
|
|
|
|
|
67
|
0
|
0
|
|
|
|
|
return if $filename ne $self->{Torrent_log}; |
68
|
|
|
|
|
|
|
|
69
|
0
|
|
|
|
|
|
my ($date, $action, @args) = split /\t/, $input; |
70
|
0
|
|
|
|
|
|
my $method = "_${action}_torrent"; |
71
|
0
|
|
|
|
|
|
my $msg = $self->$method(@args); |
72
|
|
|
|
|
|
|
|
73
|
0
|
0
|
|
|
|
|
if (defined $msg) { |
74
|
0
|
|
|
|
|
|
for my $chan (@{ $self->{Channels} }) { |
|
0
|
|
|
|
|
|
|
75
|
0
|
|
|
|
|
|
$irc->yield($self->{Method}, $chan, $msg); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
return PCI_EAT_NONE; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub _inserted_new_torrent { |
83
|
0
|
|
|
0
|
|
|
my ($self, $name, $user, $bytes) = @_; |
84
|
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
|
my $size = _fmt_bytes($bytes); |
86
|
0
|
0
|
|
|
|
|
my $msg = $self->{Color} |
87
|
|
|
|
|
|
|
? DARK_BLUE.'Enqueued: '.ORANGE.$name.NORMAL." ($size, by $user)" |
88
|
|
|
|
|
|
|
: "Enqueued: $name ($size, by $user)"; |
89
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
return $msg; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub _hash_queued_torrent { |
94
|
0
|
|
|
0
|
|
|
my ($self, $name, $enqueued, $finished, $bytes) = @_; |
95
|
|
|
|
|
|
|
|
96
|
0
|
|
|
|
|
|
my $duration = _duration($enqueued, $finished); |
97
|
0
|
|
|
|
|
|
my $secs = $finished - $enqueued; |
98
|
0
|
0
|
|
|
|
|
$secs = 1 if $secs == 0; # avoid division by zero |
99
|
|
|
|
|
|
|
|
100
|
0
|
|
|
|
|
|
my $bps = $bytes / $secs; |
101
|
0
|
|
|
|
|
|
my $size = _fmt_bytes($bps); |
102
|
0
|
|
|
|
|
|
my $rate = "$size/s"; |
103
|
|
|
|
|
|
|
|
104
|
0
|
0
|
|
|
|
|
return $self->{Color} |
105
|
|
|
|
|
|
|
? DARK_GREEN.'Finished: '.ORANGE.$name.NORMAL." in $duration ($rate); Checking hash..." |
106
|
|
|
|
|
|
|
: "Finished: $name in $duration ($rate). Checking hash..."; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _finished_torrent { |
110
|
0
|
|
|
0
|
|
|
my ($self, $name, $hash_started, $hash_done, $rars) = @_; |
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
my $duration = _duration($hash_started, $hash_done); |
113
|
|
|
|
|
|
|
|
114
|
0
|
0
|
|
|
|
|
my $msg = $self->{Color} |
115
|
|
|
|
|
|
|
? PURPLE.'Hashed: '.ORANGE.$name.NORMAL." in $duration" |
116
|
|
|
|
|
|
|
: "Hashed: $name in $duration"; |
117
|
|
|
|
|
|
|
|
118
|
0
|
0
|
|
|
|
|
if ($rars > 0) { |
119
|
0
|
0
|
|
|
|
|
my $archives = $rars > 1 ? 'archives' : 'archive'; |
120
|
0
|
|
|
|
|
|
$msg .= "; $rars $archives to unrar..."; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
return $msg; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub _unrar_torrent { |
127
|
0
|
|
|
0
|
|
|
my ($self, $name, $start, $finish, $rars, $file) = @_; |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
|
my $duration = _duration($start, $finish); |
130
|
0
|
0
|
|
|
|
|
my $archives = $rars > 1 ? 'archives' : 'archive'; |
131
|
0
|
0
|
|
|
|
|
my $info = defined $file ? $file : "$rars $archives"; |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
|
|
|
|
my $msg = $self->{Color} |
134
|
|
|
|
|
|
|
? MAGENTA.'Unrared: '.ORANGE.$name.NORMAL." in $duration ($info)" |
135
|
|
|
|
|
|
|
: "Unrared: $name in $duration ($info)"; |
136
|
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
|
return $msg; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub _unrar_failed_torrent { |
141
|
0
|
|
|
0
|
|
|
my ($self, $name, $error) = @_; |
142
|
0
|
0
|
|
|
|
|
$error = '' if !defined $error; |
143
|
|
|
|
|
|
|
|
144
|
0
|
0
|
|
|
|
|
return $self->{Color} |
145
|
|
|
|
|
|
|
? BROWN.'Unrar failed: '.ORANGE.$name.NORMAL.": $error" |
146
|
|
|
|
|
|
|
: "Unrared failed: $name: $error"; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _erased_torrent { |
150
|
0
|
|
|
0
|
|
|
my ($self, $name, $size_bytes, $down_bytes, $up_bytes, $ratio) = @_; |
151
|
0
|
|
|
|
|
|
my $up = _fmt_bytes($up_bytes); |
152
|
0
|
0
|
|
|
|
|
$ratio /= 1000 if $ratio != 0; |
153
|
0
|
|
|
|
|
|
$ratio = sprintf '%.2f', $ratio; |
154
|
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
my $msg; |
156
|
0
|
0
|
|
|
|
|
if ($size_bytes == $down_bytes) { |
157
|
0
|
0
|
|
|
|
|
$msg = $self->{Color} |
158
|
|
|
|
|
|
|
? TEAL.'Removed: '.ORANGE.$name.NORMAL." (ratio: $ratio, uploaded: $up)" |
159
|
|
|
|
|
|
|
: "Removed: $name (ratio: $ratio, uploaded: $up)"; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
else { |
162
|
0
|
|
|
|
|
|
my $done = sprintf '%.f%%', $down_bytes / $size_bytes * 100; |
163
|
0
|
0
|
|
|
|
|
$msg = $self->{Color} |
164
|
|
|
|
|
|
|
? BROWN.'Aborted: '.ORANGE.$name.NORMAL." ($done done, ratio: $ratio, uploaded: $up)" |
165
|
|
|
|
|
|
|
: "Aborted: $name ($done done, ratio: $ratio, uploaded: $up)"; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
|
return $msg; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub _duration { |
172
|
0
|
|
|
0
|
|
|
my ($start, $finish) = @_; |
173
|
|
|
|
|
|
|
|
174
|
0
|
|
|
|
|
|
my $enq_date = DateTime->from_epoch(epoch => $start); |
175
|
0
|
|
|
|
|
|
my $fin_date = DateTime->from_epoch(epoch => $finish); |
176
|
0
|
|
|
|
|
|
my $dur_obj = $fin_date - $enq_date; |
177
|
0
|
|
|
|
|
|
my $span = DateTime::Format::Human::Duration->new(); |
178
|
0
|
|
|
|
|
|
return $span->format_duration($dur_obj); |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub _fmt_bytes { |
182
|
0
|
|
|
0
|
|
|
my ($bytes) = @_; |
183
|
0
|
0
|
|
|
|
|
return '0B' if $bytes == 0; |
184
|
0
|
|
|
|
|
|
return Format::Human::Bytes::base2($_[0]) } |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
1; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=encoding utf8 |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 NAME |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
POE::Component::IRC::Plugin::RTorrentStatus - A PoCo-IRC plugin which prints RTorrent status messages to IRC |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 SYNOPSIS |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
To quickly get an IRC bot with this plugin up and running, you can use |
197
|
|
|
|
|
|
|
L: |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
$ pocoirc -s irc.perl.org -j '#bots' -a 'RTorrentStatus{ "Channels": ["#bots"], "Torrent_log": "/tmp/torrentlog" }' |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Or use it in your code: |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
use POE::Component::IRC::Plugin::RTorrentStatus; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# post status updates to #foobar |
206
|
|
|
|
|
|
|
$irc->plugin_add(Torrent => POE::Component::IRC::Plugin::RTorrentStatus->new( |
207
|
|
|
|
|
|
|
Torrent_log => '/tmp/torrentlog', |
208
|
|
|
|
|
|
|
Channels => ['#foobar'], |
209
|
|
|
|
|
|
|
)); |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head1 DESCRIPTION |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
POE::Component::IRC::Plugin::RTorrentStatus is a |
214
|
|
|
|
|
|
|
L plugin. It reads a log file |
215
|
|
|
|
|
|
|
generated by the included L program and posts messages to |
216
|
|
|
|
|
|
|
IRC describing the events. See the documentation for L |
217
|
|
|
|
|
|
|
on how to set it up with RTorrent. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
-MyBot:#channel- Enqueued: ubuntu-9.10-desktop-i386.iso (700MB, by hinrik) |
220
|
|
|
|
|
|
|
-MyBot:#channel- Aborted: ubuntu-9.10-desktop-i386.iso (10% done, ratio: 0.05, up: 35MB) |
221
|
|
|
|
|
|
|
-MyBot:#channel- Enqueued: ubuntu-9.10-desktop-amd64.iso (700MB, by hinrik) |
222
|
|
|
|
|
|
|
-MyBot:#channel- Finished: ubuntu-9.10-desktop-amd64.iso in 20 minutes (597kB/s); Checking hash... |
223
|
|
|
|
|
|
|
-MyBot:#channel- Hashed: ubuntu-9.10-desktop-amd64.iso in 10 seconds |
224
|
|
|
|
|
|
|
-MyBot:#channel- Removed: ubuntu-9.10-desktop-amd64.iso (ratio: 2.00, up: 1400MB) |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
And if you've got unraring enabled: |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
-MyBot:#channel- Enqueued: foobar (100MB, by hinrik) |
229
|
|
|
|
|
|
|
-MyBot:#channel- Finished: foobar in 10 minutes (171kB/s); Checking hash... |
230
|
|
|
|
|
|
|
-MyBot:#channel- Hashed: foobar in 5 seconds; 1 archive to unrar |
231
|
|
|
|
|
|
|
-MyBot:#channel- Unrared: foobar in 5 seconds (1 archive) |
232
|
|
|
|
|
|
|
-MyBot:#channel- Removed: foobar (ratio: 2.00, uploaded: 200MB) |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head1 METHODS |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 C |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Takes the following arguments: |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
B<'Torrent_log'>, the path to the torrent log file generated by the |
241
|
|
|
|
|
|
|
L program. This argument is required. |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
B<'Channels'>, an array reference of channels to post messages to. You must |
244
|
|
|
|
|
|
|
specify at least one channel. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
B<'Color'>, whether to print colorful status messages. True by default. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
B<'Method'>, how you want messages to be delivered. Valid options are |
249
|
|
|
|
|
|
|
'notice' (the default) and 'privmsg'. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
Returns a plugin object suitable for feeding to |
252
|
|
|
|
|
|
|
L's C method. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 AUTHOR |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Hinrik Ern SigurEsson, hinrik.sig@gmail.com |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Copyright 2010 Hinrik Ern SigurEsson |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
This program is free software, you can redistribute it and/or modify |
263
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=cut |