File Coverage

blib/lib/Net/uTorrent.pm
Criterion Covered Total %
statement 27 234 11.5
branch 0 16 0.0
condition n/a
subroutine 9 31 29.0
pod 20 22 90.9
total 56 303 18.4


line stmt bran cond sub pod time code
1             package Net::uTorrent;
2              
3 1     1   28059 use URI;
  1         7045  
  1         27  
4 1     1   710 use URI::QueryParam;
  1         634  
  1         22  
5 1     1   729 use HTTP::Request::Common;
  1         18834  
  1         69  
6 1     1   1032 use LWP::UserAgent;
  1         23386  
  1         30  
7 1     1   998 use JSON::XS;
  1         5912  
  1         60  
8 1     1   1066 use HTML::TreeBuilder;
  1         34657  
  1         14  
9              
10 1     1   62 use 5.010000;
  1         4  
  1         38  
11 1     1   7 use strict;
  1         2  
  1         37  
12 1     1   5 use warnings;
  1         3  
  1         2960  
13              
14             require Exporter;
15              
16             our @ISA = qw(Exporter);
17              
18             our %EXPORT_TAGS = ( 'all' => [ qw() ] );
19              
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21              
22             our @EXPORT = qw();
23              
24             our $VERSION = '0.02';
25              
26              
27             our $token;
28             our $api_url;
29             our $http_request = HTTP::Request->new;
30             our $ua = LWP::UserAgent->new;
31             our $html_tree = HTML::TreeBuilder->new;
32             our $json = JSON::XS->new;
33             our $cid; #cache id
34             our $error;
35              
36              
37              
38             sub new {
39 0     0 0   my ($class,%args) = @_;
40 0           my $self = bless({}, $class);
41              
42 0           my $host = $args{'hostname'};
43 0           my $port = $args{'port'};
44 0           my $user = $args{'user'};
45 0           my $pass = $args{'pass'};
46            
47 0           $ua->credentials (
48             "$host:$port",
49             'uTorrent',
50             $user => $pass
51             );
52 0           $api_url = "http://$host:$port/gui/";
53 0           my $http = $ua->get($api_url."token.html");
54 0           my $parsed = $html_tree->parse($http->decoded_content); # Build HTML tree of elements
55 0           $token = $parsed->{'_body'}->{'_content'}[0]->{'_content'}[0]; # Get authentication token
56              
57 0           return $self;
58             }
59            
60            
61             sub token {
62 0     0 1   return $token;
63             }
64              
65             sub login_success {
66              
67 0 0   0 1   if ($token) { return 1; }
  0            
68 0           else { return 0; }
69              
70             }
71            
72             sub torrents {
73 0     0 1   my ($class,%args) = @_;
74            
75 0           my %params = (
76             list => 1,
77             );
78 0           %params = (%params,%args); #Add optional cache id from argument.
79 0           my @args = qw();
80 0           push @args,\%params;
81 0           my $json_text = api_query_result(@args);
82 0           my $decoded_json = $json->decode($json_text);
83 0           $cid = $decoded_json->{torrentc};
84            
85 0           my @torrent = qw();
86            
87 0           for (@{$decoded_json->{torrents}}) {
  0            
88 0           my @keys = qw (
89             infohash status name size percent downloaded
90             uploaded ratio upstream downstream eta label
91             peers_connected peers_in_swarm seeds_connected
92             seeds_in_swarm availability queue_order remaining
93             );
94 0           my @values = @$_;
95 0           my %torrent_details;
96 0           for (my $i = 0; $i <= $#keys; $i++) { $torrent_details{$keys[$i]} = $values[$i]; }
  0            
97 0           push @torrent,\%torrent_details;
98              
99             }
100 0           return \@torrent;
101             }
102            
103             sub cache_id {
104 0     0 1   return $cid;
105             }
106              
107             sub get_settings {
108 0     0 1   my ($class,$type) = @_;
109 0 0         $type = 'array' unless $type;
110 0           my @settings;
111             my %settings;
112 0           my @type = qw (integer boolean string);
113              
114 0           my @args = qw();
115 0           push @args,{action => 'getsettings'};
116 0           my $json_text = api_query_result(@args);
117 0           my $decoded_json = $json->decode($json_text);
118            
119 0           for (@{$decoded_json->{settings}}) {
  0            
120 0           my %settings_info;
121 0           my @keys = qw (name type value);
122 0           my @values = @$_;
123 0           for (my $i = 0; $i <= $#keys; $i++) { $settings_info{$keys[$i]} = $values[$i]; }
  0            
124 0           $settings_info{'type_human'} = $type[$settings_info{'type'}];
125 0           $settings{$settings_info{'name'}} = \%settings_info;
126 0 0         if ($type eq 'hash') { delete $settings_info{'name'}; }
  0            
127 0           push @settings,\%settings_info;
128             }
129 0 0         if ($type eq 'array') { return \@settings; }
  0 0          
130 0           elsif ($type eq 'hash') { return \%settings; }
131             }
132              
133             sub set_settings {
134 0     0 1   my ($class,%settings) = @_;
135 0           my @args;
136 0           push @args, {action => 'setsetting'};
137 0           for my $key (keys %settings) {
138 0           push @args, { 's' => $key };
139 0           push @args, { 'v' => $settings{$key} };
140             }
141 0           api_query_result(@args);
142 0           return;
143             }
144             sub filelist {
145 0     0 1   my ($class,@infohash) = @_;
146 0           my @args;
147 0           my @priority_human = qw(skip low normal high);
148 0           my @keys = qw(filename size downloaded priority);
149            
150 0           push @args,{action => 'getfiles'};
151 0           for my $hash (@infohash) { push @args,{hash => $hash}; }
  0            
152 0           my $json_text = api_query_result(@args);
153 0           my $i = 0; for ($json_text =~ /,"(files)":/g) { my $append = $i++; $json_text =~ s/,"(files)":/,"files$append":/; } # Ugly hack to avoid repeating keys due to bug in uTorrent's JSON generator
  0            
  0            
  0            
154 0           my $decoded_json = $json->decode($json_text);
155 0           my %torrent_hash;
156 0           for my $key (keys %$decoded_json) {
157 0 0         if ($key ne 'build') {
158 0           my $torrent = $decoded_json->{$key};
159 0           my $infohash = $$torrent[0];
160 0           my $file_array = $$torrent[1];
161 0           my @files_in_torrent = qw();
162 0           for my $file (@$file_array) {
163 0           my %fileinfo;
164 0           for (my $i = 0; $i <= $#$file; $i++) { $fileinfo{$keys[$i]} = $$file[$i]; }
  0            
165 0           $fileinfo{'priority_human'} = $priority_human[$fileinfo{'priority'}];
166 0           push @files_in_torrent,\%fileinfo;
167            
168             }
169 0           $torrent_hash{$infohash} = \@files_in_torrent;
170             }
171              
172             }
173 0           return \%torrent_hash;
174             }
175              
176             sub get_properties {
177              
178 0     0 1   my ($class,@infohash) = @_;
179 0           my @args;
180 0           push @args,{action => 'getprops'};
181 0           for my $hash (@infohash) { push @args,{hash => $hash}; }
  0            
182 0           my $json_text = api_query_result(@args);
183 0           my $i = 0; for ($json_text =~ /,"(props)":/g) { my $append = $i++; $json_text =~ s/,"(props)":/,"props$append":/; } # Ugly hack to avoid repeating keys due to bug in uTorrent's JSON generator
  0            
  0            
  0            
184 0           my $decoded_json = $json->decode($json_text);
185 0           my %properties;
186 0           for my $key (keys %$decoded_json) {
187 0 0         if ($key ne 'build') {
188 0           my $property = $decoded_json->{$key};
189 0           my @trackers = split(/\s+/,$$property[0]->{trackers});
190 0           $$property[0]->{trackers} = \@trackers;
191 0           $properties{$$property[0]->{hash}} = $$property[0];
192             }
193             }
194 0           return \%properties;
195             }
196              
197             sub set_properties {
198 0     0 1   my ($class,@properties) = @_;
199 0           my @args;
200 0           push @args,{action => 'setprops'};
201 0           for my $property (@properties) {
202 0           push @args, { hash => $$property{'hash'}};
203 0           delete $$property{'hash'};
204 0           for my $key (keys %$property) {
205 0           push (@args, { 's' => $key } );
206 0           push (@args, { 'v' => $$property{$key} } );
207             }
208             }
209 0           api_query_result(@args);
210 0           return;
211              
212              
213              
214             }
215              
216             sub start {
217 0     0 1   my ($class,@infohash) = @_;
218 0           my @args;
219 0           push @args, {action => 'start'};
220 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
221 0           api_query_result(@args);
222             }
223              
224              
225             sub stop {
226 0     0 1   my ($class,@infohash) = @_;
227 0           my @args;
228 0           push @args, {action => 'stop'};
229 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
230 0           api_query_result(@args);
231              
232             }
233              
234             sub pause {
235 0     0 1   my ($class,@infohash) = @_;
236 0           my @args;
237 0           push @args, {action => 'pause'};
238 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
239 0           api_query_result(@args);
240              
241             }
242              
243             sub resume {
244 0     0 1   my ($class,@infohash) = @_;
245 0           my @args;
246 0           push @args, {action => 'unpause'};
247 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
248 0           api_query_result(@args);
249              
250             }
251              
252             sub force_start {
253 0     0 1   my ($class,@infohash) = @_;
254 0           my @args;
255 0           push @args, {action => 'forcestart'};
256 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
257 0           api_query_result(@args);
258              
259              
260             }
261              
262             sub recheck {
263 0     0 1   my ($class,@infohash) = @_;
264 0           my @args;
265 0           push @args, {action => 'recheck'};
266 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
267 0           api_query_result(@args);
268              
269             }
270              
271             sub remove_torrent {
272 0     0 1   my ($class,@infohash) = @_;
273 0           my @args;
274 0           push @args, {action => 'remove'};
275 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
276 0           api_query_result(@args);
277              
278              
279             }
280              
281              
282             sub remove_data {
283 0     0 1   my ($class,@infohash) = @_;
284 0           my @args;
285 0           push @args, {action => 'removedata'};
286 0           for (@infohash) { push @args, { hash => $_ }; }
  0            
287 0           api_query_result(@args);
288              
289              
290             }
291              
292              
293             sub set_priority {
294 0     0 1   my ($class,%args_hash) = @_;
295 0           my @args;
296 0           push @args, { action => 'setprio' };
297 0           push @args, { hash => $args_hash{'hash'} };
298 0           push @args, { p => $args_hash{'priority'} };
299 0           delete $args_hash{'hash'};
300 0           for (@{$args_hash{'files'}}) { push @args, { f => $_}; }
  0            
  0            
301 0           api_query_result(@args);
302 0           return;
303             }
304              
305              
306             sub add_url {
307 0     0 1   my ($class,$url) = @_;
308 0           my @args;
309 0           push @args, { action => 'add-url' };
310 0           push @args, { 's' => $url };
311 0           api_query_result(@args);
312 0           return;
313             }
314              
315              
316             sub add_file {
317 0     0 1   my ($class,$file) = @_;
318 0           my $api = URI->new($api_url);
319 0           $api->query_param(action => 'add-file');
320 0           $api->query_param(t => $token);
321            
322             # This is the only method that uses HTTP POST, so a separate subroutine is not necessary.
323            
324 0           my $request = $ua->request (
325             POST $api,
326             Content_Type => 'multipart/form-data',
327             Content => [
328             'torrent_file' => [$file]
329             ]
330            
331             );
332              
333 0           my $json_text = $request->content;
334 0           my $decoded_json = $json->decode($json_text);
335 0           my $status = $$decoded_json{error};
336 0 0         $status = 'Success' unless $status;
337 0           return { Status => $status};
338              
339             }
340              
341             ############################### Module's own sub routines ###############################
342              
343             sub api_query_result {
344 0     0 0   my (@params) = @_;
345 0           my $api = URI->new($api_url);
346 0           for my $array_value (@params) {
347 0           for my $key (keys %$array_value) {
348 0           $api->query_param_append($key => $$array_value{$key});
349             }
350             }
351              
352 0           $api->query_param(token => $token);
353 0           my $http = $ua->get($api);
354 0           return $http->decoded_content;
355             }
356              
357              
358            
359             1;
360              
361             __END__