| [22481] | 1 | package P2P::Transmission::Remote; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use 5.8.1; |
|---|
| [22485] | 5 | our $VERSION = '0.02'; |
|---|
| [22481] | 6 | |
|---|
| 7 | use Carp; |
|---|
| 8 | use JSON::XS; |
|---|
| 9 | use LWP::UserAgent; |
|---|
| 10 | use URI; |
|---|
| [23093] | 11 | use Path::Class; |
|---|
| 12 | use File::Temp; |
|---|
| [22481] | 13 | |
|---|
| 14 | use Moose; |
|---|
| 15 | use Moose::Util::TypeConstraints; |
|---|
| 16 | |
|---|
| 17 | subtype 'Uri' |
|---|
| 18 | => as 'Object' |
|---|
| 19 | => where { $_->isa('URI') }; |
|---|
| 20 | |
|---|
| 21 | coerce 'Uri' |
|---|
| 22 | => from 'Object' |
|---|
| 23 | => via { $_->isa('URI') |
|---|
| 24 | ? $_ : Params::Coerce::coerce( 'URI', $_ ) } |
|---|
| 25 | => from 'Str' |
|---|
| 26 | => via { URI->new( $_, 'http' ) }; |
|---|
| 27 | |
|---|
| 28 | has url => ( |
|---|
| 29 | is => 'rw', |
|---|
| 30 | isa => 'Uri', |
|---|
| 31 | default => sub { URI->new("http://localhost:9091/") }, |
|---|
| 32 | lazy => 1, |
|---|
| 33 | coerce => 1, |
|---|
| 34 | ); |
|---|
| 35 | |
|---|
| 36 | has user_agent => ( |
|---|
| 37 | is => 'rw', |
|---|
| 38 | isa => 'LWP::UserAgent', |
|---|
| 39 | default => sub { LWP::UserAgent->new }, |
|---|
| 40 | lazy => 1, |
|---|
| 41 | ); |
|---|
| 42 | |
|---|
| 43 | has username => ( |
|---|
| 44 | is => 'rw', |
|---|
| 45 | isa => 'Str', |
|---|
| 46 | ); |
|---|
| 47 | |
|---|
| 48 | has password => ( |
|---|
| 49 | is => 'rw', |
|---|
| 50 | isa => 'Str', |
|---|
| 51 | ); |
|---|
| 52 | |
|---|
| 53 | sub _prepare_auth { |
|---|
| 54 | my $self = shift; |
|---|
| 55 | if ( $self->username && $self->password ) { |
|---|
| 56 | # set Digest auth credentials |
|---|
| 57 | $self->user_agent->credentials( $self->url->host_port, "Transmission RPC Server", $self->username, $self->password ); |
|---|
| 58 | } |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | sub _request { |
|---|
| 62 | my($self, $method, $args) = @_; |
|---|
| 63 | |
|---|
| 64 | my $url = $self->url . "transmission/rpc"; |
|---|
| 65 | |
|---|
| 66 | my $req = HTTP::Request->new( POST => $url ); |
|---|
| 67 | $req->header( Accept => "application/json, text/javascript, */*" ); |
|---|
| 68 | $req->header( "Content-Type" => "application/json" ); |
|---|
| 69 | |
|---|
| 70 | $self->_prepare_auth; |
|---|
| 71 | |
|---|
| 72 | my $body = JSON::XS::encode_json({ |
|---|
| 73 | method => $method, |
|---|
| 74 | arguments => $args, |
|---|
| 75 | }); |
|---|
| 76 | |
|---|
| 77 | $req->header( "Content-Length" => length($body) ); |
|---|
| 78 | $req->content($body); |
|---|
| 79 | |
|---|
| 80 | my $ua = $self->user_agent; |
|---|
| 81 | my $res = $ua->request( $req ); |
|---|
| 82 | |
|---|
| 83 | my $result = JSON::XS::decode_json( $res->content ); |
|---|
| 84 | |
|---|
| 85 | if ($result->{result} ne 'success') { |
|---|
| 86 | croak $result->{result}; |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | return $result->{arguments}; |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | sub _cmd_torrents { |
|---|
| 93 | my($self, $methods, @torrents) = @_; |
|---|
| 94 | $self->_request($methods, { ids => [ map $_->{id}, @torrents ] }); |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | sub torrents { |
|---|
| 98 | my $self = shift; |
|---|
| 99 | |
|---|
| 100 | my $res = $self->_request("torrent-get", { |
|---|
| 101 | fields => [ "addedDate","announceURL","comment","creator","dateCreated", |
|---|
| 102 | "downloadedEver","error","errorString","eta","hashString","haveUnchecked","haveValid", |
|---|
| 103 | "id","isPrivate","leechers","leftUntilDone","name","peersGettingFromUs","peersKnown", |
|---|
| 104 | "peersSendingToUs","rateDownload","rateUpload","seeders","sizeWhenDone","status","swarmSpeed", |
|---|
| 105 | "totalSize","uploadedEver" ], |
|---|
| 106 | }); |
|---|
| 107 | |
|---|
| 108 | return @{ $res->{torrents} }; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | sub start { |
|---|
| 112 | my $self = shift; |
|---|
| 113 | $self->_cmd_torrents("torrent-start", @_); |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | sub stop { |
|---|
| 117 | my $self = shift; |
|---|
| 118 | $self->_cmd_torrents("torrent-stop", @_); |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | sub remove { |
|---|
| 122 | my $self = shift; |
|---|
| 123 | $self->_cmd_torrents("torrent-remove", @_); |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| [23093] | 126 | sub add { |
|---|
| 127 | my $self = shift; |
|---|
| 128 | my ($filename) = @_; |
|---|
| 129 | |
|---|
| 130 | if ($filename =~ m{^http://}) { |
|---|
| 131 | my $basedir = dir( $ENV{HOME}, '.torrents' ); |
|---|
| 132 | unless ( -e $basedir ) { |
|---|
| 133 | mkdir $basedir; |
|---|
| 134 | } |
|---|
| 135 | my $tmp = File::Temp->new( |
|---|
| 136 | TEMPLATE => 'tempXXXXX', |
|---|
| 137 | UNLINK => 0, |
|---|
| 138 | DIR => $basedir, |
|---|
| 139 | SUFFIX => '.torrent' |
|---|
| 140 | ); |
|---|
| 141 | my $ua = $self->user_agent; |
|---|
| 142 | $ua->mirror( $filename, $tmp->filename ); |
|---|
| 143 | $filename = $tmp->filename; |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | my $res = $self->_request("torrent-add", { |
|---|
| 147 | filename => $filename, |
|---|
| 148 | }); |
|---|
| 149 | |
|---|
| 150 | return $res; |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| [22481] | 153 | 1; |
|---|
| 154 | __END__ |
|---|
| 155 | |
|---|
| 156 | =encoding utf-8 |
|---|
| 157 | |
|---|
| 158 | =for stopwords API url |
|---|
| 159 | |
|---|
| 160 | =head1 NAME |
|---|
| 161 | |
|---|
| 162 | P2P::Transmission::Remote - Control Transmission using its Remote API |
|---|
| 163 | |
|---|
| 164 | =head1 SYNOPSIS |
|---|
| 165 | |
|---|
| 166 | use P2P::Transmission::Remote; |
|---|
| 167 | |
|---|
| 168 | my $client = P2P::Transmission::Remote->new; |
|---|
| 169 | for my $torrent ($client->torrents) { |
|---|
| 170 | print $torrent->{name}; |
|---|
| 171 | $client->stop($torrent); |
|---|
| 172 | } |
|---|
| 173 | |
|---|
| 174 | =head1 DESCRIPTION |
|---|
| 175 | |
|---|
| 176 | P2P::Transmission::Remote is a client module to control torrent |
|---|
| 177 | software Transmission using its Remote API. You need to enable its |
|---|
| 178 | Remote and allows access from your client machine (usually localhost). |
|---|
| 179 | |
|---|
| 180 | =head1 METHODS |
|---|
| 181 | |
|---|
| 182 | =over 4 |
|---|
| 183 | |
|---|
| 184 | =item url |
|---|
| 185 | |
|---|
| 186 | Gets and sets the URL of Transmission Remote API. Defaults to I<http://localhost:9091/>. |
|---|
| 187 | |
|---|
| 188 | =item user_agent |
|---|
| 189 | |
|---|
| 190 | Gets and sets the User Agent object to make API calls. |
|---|
| 191 | |
|---|
| 192 | =item torrents |
|---|
| 193 | |
|---|
| 194 | my @torrents = $client->torrents; |
|---|
| 195 | |
|---|
| 196 | Gets the list of Torrent data. |
|---|
| 197 | |
|---|
| 198 | =item start, stop, remove |
|---|
| 199 | |
|---|
| 200 | $client->start(@torrents); |
|---|
| 201 | $client->stop(@torrents); |
|---|
| 202 | $client->remove(@torrents); |
|---|
| 203 | |
|---|
| 204 | Starts, stops and removes the torrent transfer. |
|---|
| 205 | |
|---|
| 206 | =item upload |
|---|
| 207 | |
|---|
| 208 | $client->upload($torrent_path); |
|---|
| 209 | |
|---|
| 210 | Adds a new torrent by uploading the file. |
|---|
| 211 | |
|---|
| 212 | =back |
|---|
| 213 | |
|---|
| 214 | =head1 AUTHOR |
|---|
| 215 | |
|---|
| 216 | Tatsuhiko Miyagawa E<lt>miyagawa@cpan.orgE<gt> |
|---|
| 217 | |
|---|
| 218 | =head1 LICENSE |
|---|
| 219 | |
|---|
| 220 | This library is free software; you can redistribute it and/or modify |
|---|
| 221 | it under the same terms as Perl itself. |
|---|
| 222 | |
|---|
| 223 | =head1 SEE ALSO |
|---|
| 224 | |
|---|
| 225 | L<P2P::Transmission> |
|---|
| 226 | |
|---|
| 227 | =cut |
|---|