File Coverage

lib/Transmission/Torrent.pm
Criterion Covered Total %
statement 46 109 42.2
branch 1 24 4.1
condition 0 2 0.0
subroutine 17 26 65.3
pod 4 4 100.0
total 68 165 41.2


line stmt bran cond sub pod time code
1             # ex:ts=4:sw=4:sts=4:et
2             package Transmission::Torrent;
3             # See Transmission::Client for copyright statement.
4              
5             =head1 NAME
6              
7             Transmission::Torrent - Transmission torrent object
8              
9             =head1 DESCRIPTION
10              
11             See "3.2 Torrent Mutators" and "3.3 Torrent accessors" from
12             L<https://trac.transmissionbt.com/browser/trunk/extras/rpc-spec.txt>
13              
14             This class handles data related to a torrent known to Transmission.
15              
16             =head1 SEE ALSO
17              
18             L<Transmission::AttributeRole>
19              
20             =cut
21              
22 4     4   100427 use Moose;
  4         469789  
  4         43  
23 4     4   31641 use List::MoreUtils qw(uniq);
  4         12739  
  4         45  
24 4     4   4957 use Transmission::Torrent::File;
  4         13  
  4         213  
25 4     4   51 use Transmission::Types ':all';
  4         7  
  4         40  
26              
27             BEGIN {
28 4     4   12898 with 'Transmission::AttributeRole';
29             }
30              
31             =head1 ATTRIBUTES
32              
33             =head2 id
34              
35             $id = $self->id;
36              
37             Returns the id that identifies this torrent in transmission.
38              
39             =cut
40              
41             has id => (
42             is => 'ro',
43             isa => 'Int',
44             writer => '_set_id',
45             required => 1,
46             );
47              
48             =head2 bandwidth_priority
49              
50             $self->bandwidth_priority($num);
51              
52             This torrent's bandwidth.
53              
54             =head2 download_limit
55              
56             $self->download_limit($num);
57              
58             Maximum download speed (in K/s).
59              
60             =head2 download_limited
61              
62             $self->download_limited($bool);
63              
64             True if "downloadLimit" is honored.
65              
66             =head2 honors_session_limits
67              
68             $self->honors_session_limits($bool);
69              
70             True if session upload limits are honored.
71              
72             =head2 location
73              
74             $self->location($str);
75              
76             New location of the torrent's content
77              
78             =head2 peer_limit
79              
80             $self->peer_limit($num);
81              
82             Maximum number of peers
83              
84             =head2 seed_ratio_limit
85              
86             $self->seed_ratio_limit($num);
87              
88             Session seeding ratio.
89              
90             =head2 seed_ratio_mode
91              
92             $self->seed_ratio_mode($num);
93              
94             Which ratio to use. See tr_ratiolimit.
95              
96             =head2 upload_limit
97              
98             $self->upload_limit($num);
99              
100             Maximum upload speed (in K/s)
101              
102             =head2 upload_limited
103              
104             $self->upload_limited($bool);
105              
106             True if "upload_limit" is honored
107              
108             =head2 activity_date
109              
110             $num = $self->activity_date;
111              
112             =head2 added_date
113              
114             $num = $self->added_date;
115              
116             =head2 bandwidth_priority
117              
118             $num = $self->bandwidth_priority;
119              
120             =head2 comment
121              
122             $str = $self->comment;
123              
124             =head2 corrupt_ever
125              
126             $num = $self->corrupt_ever;
127              
128             =head2 creator
129              
130             $str = $self->creator;
131              
132             =head2 date_created
133              
134             $num = $self->date_created;
135              
136             =head2 desired_available
137              
138             $num = $self->desired_available;
139              
140             =head2 done_date
141              
142             $num = $self->done_date;
143              
144             =head2 download_dir
145              
146             $str = $self->download_dir;
147              
148             =head2 downloaded_ever
149              
150             $num = $self->downloaded_ever;
151              
152             =head2 downloaders
153              
154             $num = $self->downloaders;
155              
156             =head2 download_limit
157              
158             $num = $self->download_limit;
159              
160             =head2 download_limited
161              
162             $bool = $self->download_limited;
163              
164             =head2 error
165              
166             $num = $self->error;
167              
168             =head2 error_string
169              
170             $str = $self->error_string;
171              
172             =head2 eta
173              
174             $num = $self->eta;
175              
176             =head2 hash_str
177              
178             $str = $self->hash_string;
179              
180             =head2 have_unchecked
181              
182             $num = $self->have_unchecked;
183              
184             =head2 have_valid
185              
186             $num = $self->have_valid;
187              
188             =head2 honors_session_limits
189              
190             $bool = $self->honors_session_limits;
191              
192             =head2 is_private
193              
194             $bool = $self->is_private;
195              
196             =head2 leechers
197              
198             $num = $self->leechers;
199              
200             =head2 left_until_done
201              
202             $num = $self->left_until_done;
203              
204             =head2 manual_announce_time
205              
206             $num = $self->manual_announce_time;
207              
208             =head2 max_connected_peers
209              
210             $num = $self->max_connected_peers;
211              
212             =head2 name
213              
214             $str = $self->name;
215              
216             =head2 peer
217              
218             $num = $self->peer;
219              
220             =head2 peers_connected
221              
222             $num = $self->peers_connected;
223              
224             =head2 peers_getting_from_us
225              
226             $num = $self->peers_getting_from_us;
227              
228             =head2 peers_known
229              
230             $num = $self->peers_known;
231              
232             =head2 peers_sending_to_us
233              
234             $num = $self->peers_sending_to_us;
235              
236             =head2 percent_done
237              
238             $num = $self->percent_done;
239              
240             =head2 pieces
241              
242             $str = $self->pieces;
243              
244             =head2 piece_count
245              
246             $num = $self->piece_count;
247              
248             =head2 piece_size
249              
250             $num = $self->piece_size;
251              
252             =head2 rate_download
253              
254             $num = $self->rate_download;
255              
256             =head2 rate_upload
257              
258             $num = $self->rate_upload;
259              
260             =head2 recheck_progress
261              
262             $num = $self->recheck_progress;
263              
264             =head2 seeders
265              
266             $num = $self->seeders;
267              
268             =head2 seed_ratio_limit
269              
270             $num = $self->seed_ratio_limit;
271              
272             =head2 seed_ratio_mode
273              
274             $num = $self->seed_ratio_mode;
275              
276             =head2 size_when_done
277              
278             $num = $self->size_when_done;
279              
280             =head2 start_date
281              
282             $num = $self->start_date;
283              
284             =head2 status
285              
286             $str = $self->status;
287              
288             =head2 swarm_speed
289              
290             $num = $self->swarm_speed;
291              
292             =head2 times_completed
293              
294             $num = $self->times_completed;
295              
296             =head2 total_size
297              
298             $num = $self->total_size;
299              
300             =head2 torrent_file
301              
302             $str = $self->torrent_file;
303              
304             =head2 uploaded_ever
305              
306             $num = $self->uploaded_ever;
307              
308             =head2 upload_limit
309              
310             $num = $self->upload_limit;
311              
312             =head2 upload_limited
313              
314             $bool = $self->upload_limited;
315              
316             =head2 upload_ratio
317              
318             $num = $self->upload_ratio;
319              
320             =head2 webseeds_sending_to_us
321              
322             $num = $self->webseeds_sending_to_us;
323              
324             =cut
325              
326             BEGIN {
327             my $create_setter = sub {
328 40         77 my $camel = $_[0];
329              
330             return sub {
331 1 50   1   31 return if($_[0]->lazy_write);
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
332 1         32 $_[0]->client->rpc('torrent-set' =>
333             ids => [ $_[0]->id ], $camel => $_[1],
334             );
335 40         165 };
336 4     4   11834 };
337              
338             my $create_getter = sub {
339 208         353 my $camel = $_[0];
340              
341             return sub {
342 0         0 my $data = $_[0]->client->rpc('torrent-get' =>
343             ids => [ $_[0]->id ],
344             fields => [ $camel ],
345             );
346              
347 0 0       0 return unless($data);
348 0         0 return $data->{'torrents'}[0]{$camel};
349 208         755 };
350 4         15 };
351              
352 4         19 my %SET = (
353             #'files-wanted' => array,
354             #'files-unwanted' => array,
355             'location' => string,
356             'peer-limit' => number,
357             #'priority-high' => array,
358             #'priority-low' => array,
359             #'priority-normal' => array,
360             );
361 4         1191 our %BOTH = ( # meant for internal usage
362             bandwidthPriority => number,
363             downloadLimit => number,
364             downloadLimited => boolean,
365             honorsSessionLimits => boolean,
366             seedRatioLimit => double,
367             seedRatioMode => number,
368             uploadLimit => number,
369             uploadLimited => boolean,
370             );
371 4         4120 our %READ = ( # meant for internal usage
372             activityDate => number,
373             addedDate => number,
374             comment => string,
375             corruptEver => number,
376             creator => string,
377             dateCreated => number,
378             desiredAvailable => number,
379             doneDate => number,
380             downloadDir => string,
381             downloadedEver => number,
382             downloaders => number,
383             error => number,
384             errorString => string,
385             eta => number,
386             hashString => string,
387             haveUnchecked => number,
388             haveValid => number,
389             isPrivate => boolean,
390             leechers => number,
391             leftUntilDone => number,
392             manualAnnounceTime => number,
393             maxConnectedPeers => number,
394             name => string,
395             peersConnected => number,
396             peersGettingFromUs => number,
397             peersKnown => number,
398             peersSendingToUs => number,
399             percentDone => double,
400             pieceCount => number,
401             pieceSize => number,
402             rateDownload => number,
403             rateUpload => number,
404             recheckProgress => double,
405             seeders => number,
406             sizeWhenDone => number,
407             startDate => number,
408             status => string,
409             swarmSpeed => number,
410             timesCompleted => number,
411             totalSize => number,
412             torrentFile => string,
413             uploadedEver => number,
414             uploadRatio => double,
415             webseedsSendingToUs => number,
416             );
417             #peers => array,
418             #peersFrom => object,
419             #pieces => string,
420             #priorities => array,
421             #trackers => array,
422             #trackerStats => array,
423             #wanted => array,
424             #webseeds => array,
425              
426 4         20730 for my $camel (keys %SET) {
427 8         1080 my $name = __PACKAGE__->_camel2Normal($camel);
428 8         39 my $setter = $create_setter->($camel);
429              
430 8         46 __PACKAGE__->meta->add_method("write_$name" => $setter);
431              
432             has $name => (
433             is => 'rw',
434 8         854 isa => $SET{$camel},
435             coerce => 1,
436             trigger => $setter,
437             );
438             }
439              
440 4         1051 for my $camel (keys %BOTH) {
441 32         7225 my $name = __PACKAGE__->_camel2Normal($camel);
442 32         132 my $setter = $create_setter->($camel);
443 32         75 my $getter = $create_getter->($camel);
444              
445 32         116 __PACKAGE__->meta->add_method("write_$name" => $setter);
446              
447             has $name => (
448             is => 'rw',
449 32         2280 isa => $BOTH{$camel},
450             coerce => 1,
451             lazy => 1,
452             trigger => $setter,
453             default => $getter,
454             );
455             }
456              
457 4         1031 for my $camel (keys %READ) {
458 176         42405 my $name = __PACKAGE__->_camel2Normal($camel);
459 176         774 my $getter = $create_getter->($camel);
460              
461             has $name => (
462             is => 'ro',
463 176         904 isa => $READ{$camel},
464             coerce => 1,
465             writer => "_set_$name",
466             lazy => 1,
467             default => $getter,
468             );
469             }
470              
471             __PACKAGE__->meta->add_method(read => sub {
472 0     0   0 my $self = shift;
473 0         0 my @fields = uniq(@_, 'id'); # id should always be requested
474 0         0 my $lazy = $self->lazy_write;
475 0         0 my $data;
476              
477 0 0       0 $data = $self->client->rpc('torrent-get' =>
478             ids => [ $self->id ],
479             fields => [ @fields ],
480             ) or return;
481              
482 0 0       0 $data = $data->{'torrents'}[0] or return;
483              
484             # prevent from fireing off trigger in attributes
485 0         0 $self->lazy_write(1);
486              
487 0         0 for my $camel (keys %$data) {
488 0         0 my $name = __PACKAGE__->_camel2Normal($camel);
489 0 0       0 my $writer = $READ{$camel} ? "_set_$name" : $name;
490              
491 0         0 $self->$writer($data->{$camel});
492             }
493              
494             # reset lazy_write
495 0         0 $self->lazy_write($lazy);
496              
497 0         0 return 1;
498 4         987 });
499              
500             __PACKAGE__->meta->add_method(read_all => sub {
501 0     0   0 my $self = shift;
502 0         0 return $self->read(keys %BOTH, keys %READ);
503 4         309 });
504              
505 4         3516 $READ{'id'} = 'Int'; # this is required to be read
506             }
507              
508             =head2 files
509              
510             $array_ref = $self->files;
511             $self->clear_files;
512              
513             Returns an array of L<Transmission::Torrent::File>s.
514              
515             =cut
516              
517             has files => (
518             is => 'ro',
519             isa => 'ArrayRef',
520             lazy_build => 1,
521             );
522              
523             sub _build_files {
524 0     0   0 my $self = shift;
525 0         0 my $files = [];
526 0         0 my $stats = [];
527 0         0 my $id = 0;
528 0         0 my $data;
529              
530 0         0 $data = $self->client->rpc('torrent-get' =>
531             ids => [ $self->id ],
532             fields => [ qw/ files fileStats / ],
533             );
534              
535 0 0       0 return [] unless($data);
536              
537 0         0 $files = $data->{'torrents'}[0]{'files'};
538 0         0 $stats = $data->{'torrents'}[0]{'fileStats'};
539              
540             # this has to be true: @$files == @$stats
541 0         0 while(@$stats) {
542 0 0       0 my $stats = shift @$stats or last;
543 0         0 my $file = shift @$files;
544              
545 0         0 push @$files,
546             Transmission::Torrent::File->new(id => $id, %$stats, %$file);
547              
548 0         0 $id++;
549             }
550              
551 0         0 return $files;
552             }
553              
554             =head1 METHODS
555              
556             =head2 BUILDARGS
557              
558             $hash_ref = $self->BUILDARGS(\%args);
559              
560             Convert keys in C<%args> from "CamelCase" to "camel_case".
561              
562             =cut
563              
564             sub BUILDARGS {
565 1     1 1 4743 my $self = shift;
566 1         8 my $args = $self->SUPER::BUILDARGS(@_);
567              
568 1         18 $self->_camel2Normal($args);
569              
570 1         6 return $args;
571             }
572              
573             =head2 read
574              
575             $bool = $self->read('id', 'name', 'eta');
576              
577             This method will refresh all requested attributes in one RPC request, while
578             calling one and one attribute, results in one-and-one request.
579              
580             =head2 read_all
581              
582             $bool = $self->read_all;
583              
584             Similar to L</read>, but requests all attributes.
585              
586             =head2 start
587              
588             See L<Transmission::Client::start()>.
589              
590             =head2 stop
591              
592             See L<Transmission::Client::stop()>.
593              
594             =head2 verify
595              
596             See L<Transmission::Client::verify()>.
597              
598             =cut
599              
600             {
601             for my $name (qw/ start stop verify /) {
602             __PACKAGE__->meta->add_method($name => sub {
603 0     0     $_[0]->client->$name(ids => $_[0]->id);
        0      
        0      
604             });
605             }
606             }
607              
608             =head2 move
609              
610             $bool = $self->move($path);
611              
612             Will move the torrent content to C<$path>.
613              
614             =cut
615              
616             sub move {
617 0     0 1   my $self = shift;
618 0           my $path = shift;
619              
620 0 0         unless($path) {
621 0           $self->client_error("Required argument 'path' is missing");
622 0           return;
623             }
624              
625 0           return $self->client->move(
626             ids => [$self->id],
627             location => $path,
628             move => 1,
629             );
630             }
631              
632             =head2 write_wanted
633              
634             $bool = $self->write_wanted;
635              
636             Will write "wanted" information from L</files> to transmission.
637              
638             =cut
639              
640             sub write_wanted {
641 0     0 1   my $self = shift;
642 0           my %wanted = ( wanted => [], unwanted => [] );
643 0           my $ok;
644              
645 0           for my $file (@{ $self->files }) {
  0            
646 0 0         push @{ $wanted{ $file->wanted ? 'wanted' : 'unwanted' } }, $file->id;
  0            
647             }
648              
649 0           for my $key (qw/wanted unwanted/) {
650             # Transmission interpret an empty list to mean all files
651 0 0         next unless @{$wanted{$key}};
  0            
652              
653             $self->client->rpc('torrent-set' =>
654 0 0         ids => [ $self->id ], "files-$key" => $wanted{$key}
655             ) or return;
656             }
657              
658 0           return 1;
659             }
660              
661             =head2 write_priority
662              
663             $bool = $self->write_priority;
664              
665             Will write "priorty" information from L</files> to transmission.
666              
667             =cut
668              
669             sub write_priority {
670 0     0 1   my $self = shift;
671 0           my %priority = ( low => [], normal => [], high => [] );
672 0           my %map = ( -1 => 'low', 0 => 'normal', 1 => 'high' );
673              
674 0           for my $file (@{ $self->files }) {
  0            
675 0   0       my $key = $map{ $file->priority } || 'normal';
676 0           push @{ $priority{$key} }, $file->id;
  0            
677             }
678              
679 0           for my $key (qw/low normal high/) {
680             $self->client->rpc('torrent-set' =>
681 0 0         ids => [ $self->id ], "priority-$key" => $priority{$key}
682             ) or return;
683             }
684              
685 0           return 1;
686             }
687              
688             =head1 LICENSE
689              
690             =head1 AUTHOR
691              
692             See L<Transmission::Client>.
693              
694             =cut
695              
696             1;