├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── README.md ├── composer.json ├── examples └── queue.php ├── lib └── Transmission │ ├── Client.php │ ├── Model │ ├── AbstractModel.php │ ├── File.php │ ├── FreeSpace.php │ ├── ModelInterface.php │ ├── Peer.php │ ├── Session.php │ ├── Stats │ │ ├── Session.php │ │ └── Stats.php │ ├── Status.php │ ├── Torrent.php │ ├── Tracker.php │ └── TrackerStats.php │ ├── Transmission.php │ └── Util │ ├── PropertyMapper.php │ └── ResponseValidator.php ├── phpunit.xml.dist └── tests ├── Transmission ├── Mock │ └── Model.php └── Tests │ ├── ClientTest.php │ ├── Model │ ├── AbstractModelTest.php │ ├── FileTest.php │ ├── PeerTest.php │ ├── SessionTest.php │ ├── StatusTest.php │ ├── TorrentTest.php │ ├── TrackerStatsTest.php │ └── TrackerTest.php │ ├── TransmissionTest.php │ └── Util │ ├── PropertyMapperTest.php │ └── ResponseValidatorTest.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | composer.phar 4 | phpunit.xml 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: [5.3, 5.4, 5.5] 4 | 5 | before_script: 6 | - composer install --dev --prefer-source 7 | 8 | script: 9 | phpunit --coverage-text 10 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Version Changes 2 | 3 | 0.1.0 - Initial release 4 | 5 | 0.2.0 - Rewrote the entire public API 6 | 7 | 0.3.0 - Added support for authentication 8 | 9 | 0.4.0 - The library now requires at least PHP 5.3.2 10 | - Added support for getting files downloaded by torrent 11 | - Added support for getting trackers used by a torrent 12 | - Added support for getting peers connected to 13 | - The torrent now contains: 14 | * Whether it is finished 15 | * The up- and download rate (in bytes/s) 16 | * The size of the download (when completed) 17 | * The ETA of the download 18 | * The percentage of the download completed 19 | - Made the authentication more flexible 20 | - The client now sends an User-Agent header with each request 21 | - Added support for starting, stopping, veryfing and 22 | requesting a reannounce of torrents 23 | 24 | 0.5.0 - Fix a bug in the authentication/authorization mechanism 25 | - A whole lot of other stuff including management of the 26 | Transmission session (setting global download speed limit 27 | and toggling the speed limit among others). 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Ramon Kleiss all(); 35 | 36 | // Getting a specific torrent from the download queue 37 | $torrent = $transmission->get(1); 38 | 39 | // (you can also get a torrent by the hash of the torrent) 40 | $torrent = $transmission->get(/* torrent hash */); 41 | 42 | // Adding a torrent to the download queue 43 | $torrent = $transmission->add(/* path to torrent */); 44 | 45 | // Removing a torrent from the download queue 46 | $torrent = $transmission->get(1); 47 | $transmission->remove($torrent); 48 | 49 | // Or if you want to delete all local data too 50 | $transmission->remove($torrent, true); 51 | 52 | // You can also get the Trackers that the torrent currently uses 53 | // These are instances of the Transmission\Model\Tracker class 54 | $trackers = $torrent->getTrackers(); 55 | 56 | // You can also get the Trackers statistics and info that the torrent currently has 57 | // These are instances of the Transmission\Model\trackerStats class 58 | $trackerStats = $torrent->getTrackerStats(); 59 | 60 | // To get the start date/time of the torrent in UNIX Timestamp format 61 | $startTime = $torrent -> getStartDate(); 62 | 63 | // To get the number of peers connected 64 | $connectedPeers = $torrent -> getPeersConnected(); 65 | 66 | // Getting the files downloaded by the torrent are available too 67 | // These are instances of Transmission\Model\File 68 | $files = $torrent->getFiles(); 69 | 70 | // You can start, stop, verify the torrent and ask the tracker for 71 | // more peers to connect to 72 | $transmission->stop($torrent); 73 | $transmission->start($torrent); 74 | $transmission->start($torrent, true); // Pass true if you want to start the torrent immediatly 75 | $transmission->verify($torrent); 76 | $transmission->reannounce($torrent); 77 | ``` 78 | 79 | To find out which information is contained by the torrent, check 80 | [`Transmission\Model\Torrent`](https://github.com/kleiram/transmission-php/tree/master/lib/Transmission/Model/Torrent.php). 81 | 82 | By default, the library will try to connect to `localhost:9091`. If you want to 83 | connect to another host or post you can pass those to the constructor of the 84 | `Transmission` class: 85 | 86 | ```php 87 | all(); 93 | $torrent = $transmission->get(1); 94 | $torrent = $transmission->add(/* path to torrent */); 95 | 96 | // When you already have a torrent, you don't have to pass the client again 97 | $torrent->delete(); 98 | ``` 99 | 100 | It is also possible to pass the torrent data directly instead of using a file 101 | but the metadata must be base64-encoded: 102 | 103 | ```php 104 | add(/* base64-encoded metainfo */, true); 106 | ``` 107 | 108 | If the Transmission server is secured with a username and password you can 109 | authenticate using the `Client` class: 110 | 111 | ```php 112 | authenticate('username', 'password'); 118 | $transmission = new Transmission(); 119 | $transmission->setClient($client); 120 | ``` 121 | 122 | Additionally, you can control the actual Transmission setting. This means 123 | you can modify the global download limit or change the download directory: 124 | 125 | ```php 126 | getSession(); 131 | 132 | $session->setDownloadDir('/home/foo/downloads/complete'); 133 | $session->setIncompleteDir('/home/foo/downloads/incomplete'); 134 | $session->setIncompleteDirEnabled(true); 135 | $session->save(); 136 | ``` 137 | 138 | ## Testing 139 | 140 | Testing is done using [PHPUnit](https://github.com/sebastianbergmann/phpunit). To 141 | test the application, you have to install the dependencies using Composer before 142 | running the tests: 143 | 144 | ```bash 145 | $ curl -s https://getcomposer.org/installer | php 146 | $ php composer.phar install 147 | $ phpunit --coverage-text 148 | ``` 149 | 150 | ## Integration into frameworks 151 | 152 | Currently, there's a [Symfony](https://github.com/chellem/TransmissionBundle) 153 | bundle in development which allows you to easily use this library within Symfony. 154 | 155 | ## Changelog 156 | 157 | Version Changes 158 | 159 | 0.1.0 - Initial release 160 | 161 | 0.2.0 - Rewrote the entire public API 162 | 163 | 0.3.0 - Added support for authentication 164 | 165 | 0.4.0 - The library now requires at least PHP 5.3.2 166 | - Added support for getting files downloaded by torrent 167 | - Added support for getting trackers used by a torrent 168 | - Added support for getting peers connected to 169 | - The torrent now contains: 170 | * Whether it is finished 171 | * The up- and download rate (in bytes/s) 172 | * The size of the download (when completed) 173 | * The ETA of the download 174 | * The percentage of the download completed 175 | - Made the authentication more flexible 176 | - The client now sends an User-Agent header with each request 177 | - Added support for starting, stopping, veryfing and 178 | requesting a reannounce of torrents 179 | 180 | 0.5.0 - Fix a bug in the authentication/authorization mechanism 181 | - A whole lot of other stuff including management of the 182 | Transmission session (setting global download speed limit 183 | and toggling the speed limit among others). 184 | 185 | ## License 186 | 187 | This library is licensed under the BSD 2-clause license. 188 | 189 | Copyright (c) 2014, Ramon Kleiss 190 | All rights reserved. 191 | 192 | Redistribution and use in source and binary forms, with or without 193 | modification, are permitted provided that the following conditions are met: 194 | 195 | 1. Redistributions of source code must retain the above copyright notice, this 196 | list of conditions and the following disclaimer. 197 | 2. Redistributions in binary form must reproduce the above copyright notice, 198 | this list of conditions and the following disclaimer in the documentation 199 | and/or other materials provided with the distribution. 200 | 201 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 202 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 203 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 204 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 205 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 206 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 207 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 208 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 209 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 210 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 211 | 212 | The views and conclusions contained in the software and documentation are those 213 | of the authors and should not be interpreted as representing official policies, 214 | either expressed or implied, of the FreeBSD Project. 215 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kleiram/transmission-php", 3 | "description": "PHP Transmission client", 4 | "keywords": ["transmission", "torrent", "download"], 5 | "type": "library", 6 | "license": "BSD-2-Clause", 7 | "authors": [ 8 | { 9 | "name": "Ramon Kleiss", 10 | "email": "ramon@cubilon.nl" 11 | } 12 | ], 13 | "require": { 14 | "kriswallsmith/buzz": ">=0.9", 15 | "symfony/property-access": ">=2.2.1" 16 | }, 17 | "autoload": { 18 | "psr-0": { 19 | "Transmission": "lib/" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/queue.php: -------------------------------------------------------------------------------- 1 | all(); 6 | 7 | echo "Downloading to: {$transmission->getSession()->getDownloadDir()}\n"; 8 | 9 | foreach ($queue as $torrent) { 10 | echo "{$torrent->getName()}"; 11 | 12 | if ($torrent->isFinished()) { 13 | echo ": done\n"; 14 | } else { 15 | if ($torrent->isDownloading()) { 16 | echo ": {$torrent->getPercentDone()}% "; 17 | echo "(eta: ". gmdate("H:i:s", $torrent->getEta()) .")\n"; 18 | } else { 19 | echo ": paused\n"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Transmission/Client.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Client 15 | { 16 | /** 17 | * @var string 18 | */ 19 | const DEFAULT_HOST = 'localhost'; 20 | 21 | /** 22 | * @var integer 23 | */ 24 | const DEFAULT_PORT = 9091; 25 | 26 | /** 27 | * @var string 28 | */ 29 | const DEFAULT_PATH = '/transmission/rpc'; 30 | 31 | /** 32 | * @var string 33 | */ 34 | const TOKEN_HEADER = 'X-Transmission-Session-Id'; 35 | 36 | /** 37 | * @var string 38 | */ 39 | protected $host = self::DEFAULT_HOST; 40 | 41 | /** 42 | * @var integer 43 | */ 44 | protected $port = self::DEFAULT_PORT; 45 | 46 | /** 47 | * @var string 48 | */ 49 | protected $path = self::DEFAULT_PATH; 50 | 51 | /** 52 | * @var string 53 | */ 54 | protected $token; 55 | 56 | /** 57 | * @var ClientInterface 58 | */ 59 | protected $client; 60 | 61 | /** 62 | * @var string 63 | */ 64 | protected $auth; 65 | 66 | /** 67 | * Constructor 68 | * 69 | * @param string $host The hostname of the Transmission server 70 | * @param integer $port The port the Transmission server is listening on 71 | * @param string $path The path to Transmission server rpc api 72 | */ 73 | public function __construct($host = null, $port = null, $path = null) 74 | { 75 | $this->token = null; 76 | $this->client = new Curl(); 77 | 78 | if ($host) $this->setHost($host); 79 | if ($port) $this->setPort($port); 80 | if ($path) $this->setPath($path); 81 | } 82 | 83 | /** 84 | * Authenticate against the Transmission server 85 | * 86 | * @param string $username 87 | * @param string $password 88 | */ 89 | public function authenticate($username, $password) 90 | { 91 | $this->auth = base64_encode($username .':'. $password); 92 | } 93 | 94 | /** 95 | * Make an API call 96 | * 97 | * @param string $method 98 | * @param array $arguments 99 | * @return \stdClass 100 | * @throws \RuntimeException 101 | */ 102 | public function call($method, array $arguments) 103 | { 104 | list($request, $response) = $this->compose($method, $arguments); 105 | 106 | try { 107 | $this->getClient()->send($request, $response); 108 | } catch (\Exception $e) { 109 | throw new \RuntimeException( 110 | 'Could not connect to Transmission', 111 | 0, 112 | $e 113 | ); 114 | } 115 | 116 | return $this->validateResponse($response, $method, $arguments); 117 | } 118 | 119 | /** 120 | * Get the URL used to connect to Transmission 121 | * 122 | * @return string 123 | */ 124 | public function getUrl() 125 | { 126 | return sprintf( 127 | 'http://%s:%d', 128 | $this->getHost(), 129 | $this->getPort() 130 | ); 131 | } 132 | 133 | /** 134 | * Set the hostname of the Transmission server 135 | * 136 | * @param string $host 137 | */ 138 | public function setHost($host) 139 | { 140 | $this->host = (string) $host; 141 | } 142 | 143 | /** 144 | * Get the hostname of the Transmission server 145 | * 146 | * @return string 147 | */ 148 | public function getHost() 149 | { 150 | return $this->host; 151 | } 152 | 153 | /** 154 | * Set the port the Transmission server is listening on 155 | * 156 | * @param integer $port 157 | */ 158 | public function setPort($port) 159 | { 160 | $this->port = (integer) $port; 161 | } 162 | 163 | /** 164 | * Get the port the Transmission server is listening on 165 | * 166 | * @return integer 167 | */ 168 | public function getPort() 169 | { 170 | return $this->port; 171 | } 172 | 173 | /** 174 | * Set the path to Transmission server rpc api 175 | * 176 | * @param string $path 177 | */ 178 | public function setPath($path) 179 | { 180 | return $this->path = (string) $path; 181 | } 182 | 183 | /** 184 | * Get the path to Transmission server rpc api 185 | */ 186 | public function getPath() 187 | { 188 | return $this->path; 189 | } 190 | 191 | /** 192 | * Set the CSRF-token of the Transmission client 193 | * 194 | * @param string $token 195 | */ 196 | public function setToken($token) 197 | { 198 | $this->token = (string) $token; 199 | } 200 | 201 | /** 202 | * Get the CSRF-token for the Transmission client 203 | * 204 | * @return string 205 | */ 206 | public function getToken() 207 | { 208 | return $this->token; 209 | } 210 | 211 | /** 212 | * Set the Buzz client used to connect to Transmission 213 | * 214 | * @param ClientInterface $client 215 | */ 216 | public function setClient(ClientInterface $client) 217 | { 218 | $this->client = $client; 219 | } 220 | 221 | /** 222 | * Get the Buzz client used to connect to Transmission 223 | * 224 | * @return ClientInterface 225 | */ 226 | public function getClient() 227 | { 228 | return $this->client; 229 | } 230 | 231 | /** 232 | * @param string $method 233 | * @param array $arguments 234 | * @return array|Request[]|Response[] 235 | */ 236 | protected function compose($method, $arguments) 237 | { 238 | $request = new Request('POST', $this->getPath(), $this->getUrl()); 239 | $request->addHeader(sprintf('%s: %s', self::TOKEN_HEADER, $this->getToken())); 240 | $request->setContent(json_encode(array( 241 | 'method' => $method, 242 | 'arguments' => $arguments 243 | ))); 244 | 245 | if (is_string($this->auth)) { 246 | $request->addHeader(sprintf('Authorization: Basic %s', $this->auth)); 247 | } 248 | 249 | return array($request, new Response()); 250 | } 251 | 252 | /** 253 | * @param Response $response 254 | * @param string $method 255 | * @param array $arguments 256 | * @return \stdClass 257 | * @throws \RuntimeException 258 | */ 259 | protected function validateResponse($response, $method, $arguments) 260 | { 261 | if (!in_array($response->getStatusCode(), array(200, 401, 409))) { 262 | throw new \RuntimeException('Unexpected response received from Transmission'); 263 | } 264 | 265 | if ($response->getStatusCode() == 401) { 266 | throw new \RuntimeException('Access to Transmission requires authentication'); 267 | } 268 | 269 | if ($response->getStatusCode() == 409) { 270 | $this->setToken($response->getHeader(self::TOKEN_HEADER)); 271 | 272 | return $this->call($method, $arguments); 273 | } 274 | 275 | return json_decode($response->getContent()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /lib/Transmission/Model/AbstractModel.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class AbstractModel implements ModelInterface 13 | { 14 | /** 15 | * @var Client 16 | */ 17 | protected $client; 18 | 19 | /** 20 | * Constructor 21 | * 22 | * @param Client $client 23 | */ 24 | public function __construct(Client $client = null) 25 | { 26 | $this->client = $client; 27 | } 28 | 29 | /** 30 | * @param Client $client 31 | */ 32 | public function setClient(Client $client) 33 | { 34 | $this->client = $client; 35 | } 36 | 37 | /** 38 | * @return Client 39 | */ 40 | public function getClient() 41 | { 42 | return $this->client; 43 | } 44 | 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | public static function getMapping() 49 | { 50 | return array(); 51 | } 52 | 53 | /** 54 | * @param string $method 55 | * @param array $arguments 56 | */ 57 | protected function call($method, $arguments) 58 | { 59 | if ($this->client) { 60 | ResponseValidator::validate( 61 | $method, 62 | $this->client->call($method, $arguments) 63 | ); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/Transmission/Model/File.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class File extends AbstractModel 8 | { 9 | /** 10 | * @var string 11 | */ 12 | protected $name; 13 | 14 | /** 15 | * @var integer 16 | */ 17 | protected $size; 18 | 19 | /** 20 | * @var integer 21 | */ 22 | protected $completed; 23 | 24 | /** 25 | * @param string $name 26 | */ 27 | public function setName($name) 28 | { 29 | $this->name = (string) $name; 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | public function getName() 36 | { 37 | return $this->name; 38 | } 39 | 40 | /** 41 | * @param integer $size 42 | */ 43 | public function setSize($size) 44 | { 45 | $this->size = (integer) $size; 46 | } 47 | 48 | /** 49 | * @return integer 50 | */ 51 | public function getSize() 52 | { 53 | return $this->size; 54 | } 55 | 56 | /** 57 | * @param integer $size 58 | */ 59 | public function setCompleted($completed) 60 | { 61 | $this->completed = (integer) $completed; 62 | } 63 | 64 | /** 65 | * @return integer 66 | */ 67 | public function getCompleted() 68 | { 69 | return $this->completed; 70 | } 71 | 72 | /** 73 | * @return boolean 74 | */ 75 | public function isDone() 76 | { 77 | return $this->getSize() == $this->getCompleted(); 78 | } 79 | 80 | /** 81 | * {@inheritDoc} 82 | */ 83 | public static function getMapping() 84 | { 85 | return array( 86 | 'name' => 'name', 87 | 'length' => 'size', 88 | 'bytesCompleted' => 'completed' 89 | ); 90 | } 91 | 92 | public function __toString() 93 | { 94 | return $this->name; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/Transmission/Model/FreeSpace.php: -------------------------------------------------------------------------------- 1 | path; 27 | } 28 | 29 | /** 30 | * Sets the value of path. 31 | * 32 | * @param string $path the path 33 | */ 34 | public function setPath($path) 35 | { 36 | $this->path = $path; 37 | } 38 | 39 | /** 40 | * Gets the value of size. 41 | * 42 | * @return integer 43 | */ 44 | public function getSize() 45 | { 46 | return $this->size; 47 | } 48 | 49 | /** 50 | * Sets the value of size. 51 | * 52 | * @param integer $size the size 53 | */ 54 | public function setSize($size) 55 | { 56 | $this->size = $size; 57 | } 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | public static function getMapping() 63 | { 64 | return array( 65 | 'path' => 'path', 66 | 'size-bytes' => 'size', 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/Transmission/Model/ModelInterface.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | interface ModelInterface 10 | { 11 | /** 12 | * Get the mapping of the model 13 | * 14 | * @return array 15 | */ 16 | public static function getMapping(); 17 | } 18 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Peer.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class Peer extends AbstractModel 8 | { 9 | /** 10 | * @var string 11 | */ 12 | protected $address; 13 | 14 | /** 15 | * @var integer 16 | */ 17 | protected $port; 18 | 19 | /** 20 | * @var string 21 | */ 22 | protected $clientName; 23 | 24 | /** 25 | * @var boolean 26 | */ 27 | protected $clientChoked; 28 | 29 | /** 30 | * @var boolean 31 | */ 32 | protected $clientInterested; 33 | 34 | /** 35 | * @var boolean 36 | */ 37 | protected $downloading; 38 | 39 | /** 40 | * @var boolean 41 | */ 42 | protected $encrypted; 43 | 44 | /** 45 | * @var boolean 46 | */ 47 | protected $incoming; 48 | 49 | /** 50 | * @var boolean 51 | */ 52 | protected $uploading; 53 | 54 | /** 55 | * @var boolean 56 | */ 57 | protected $utp; 58 | 59 | /** 60 | * @var boolean 61 | */ 62 | protected $peerChoked; 63 | 64 | /** 65 | * @var boolean 66 | */ 67 | protected $peerInterested; 68 | 69 | /** 70 | * @var double 71 | */ 72 | protected $progress; 73 | 74 | /** 75 | * @var integer 76 | */ 77 | protected $uploadRate; 78 | 79 | /** 80 | * @var integer 81 | */ 82 | protected $downloadRate; 83 | 84 | /** 85 | * @param string $address 86 | */ 87 | public function setAddress($address) 88 | { 89 | $this->address = (string) $address; 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | public function getAddress() 96 | { 97 | return $this->address; 98 | } 99 | 100 | /** 101 | * @param integer $port 102 | */ 103 | public function setPort($port) 104 | { 105 | $this->port = (integer) $port; 106 | } 107 | 108 | /** 109 | * @return integer 110 | */ 111 | public function getPort() 112 | { 113 | return $this->port; 114 | } 115 | 116 | /** 117 | * @param string $clientName 118 | */ 119 | public function setClientName($clientName) 120 | { 121 | $this->clientName = (string) $clientName; 122 | } 123 | 124 | /** 125 | * @return string 126 | */ 127 | public function getClientName() 128 | { 129 | return $this->clientName; 130 | } 131 | 132 | /** 133 | * @param boolean $choked 134 | */ 135 | public function setClientChoked($choked) 136 | { 137 | $this->clientChoked = (boolean) $choked; 138 | } 139 | 140 | /** 141 | * @return boolean 142 | */ 143 | public function isClientChoked() 144 | { 145 | return $this->clientChoked; 146 | } 147 | 148 | /** 149 | * @param boolean $interested 150 | */ 151 | public function setClientInterested($interested) 152 | { 153 | $this->clientInterested = (boolean) $interested; 154 | } 155 | 156 | /** 157 | * @return boolean 158 | */ 159 | public function isClientInterested() 160 | { 161 | return $this->clientInterested; 162 | } 163 | 164 | /** 165 | * @param boolean $downloading 166 | */ 167 | public function setDownloading($downloading) 168 | { 169 | $this->downloading = (boolean) $downloading; 170 | } 171 | 172 | /** 173 | * @return boolean 174 | */ 175 | public function isDownloading() 176 | { 177 | return $this->downloading; 178 | } 179 | 180 | /** 181 | * @param boolean $encrypted 182 | */ 183 | public function setEncrypted($encrypted) 184 | { 185 | $this->encrypted = (boolean) $encrypted; 186 | } 187 | 188 | /** 189 | * @return boolean 190 | */ 191 | public function isEncrypted() 192 | { 193 | return $this->encrypted; 194 | } 195 | 196 | /** 197 | * @param boolean $incoming 198 | */ 199 | public function setIncoming($incoming) 200 | { 201 | $this->incoming = (boolean) $incoming; 202 | } 203 | 204 | /** 205 | * @return boolean 206 | */ 207 | public function isIncoming() 208 | { 209 | return $this->incoming; 210 | } 211 | 212 | /** 213 | * @param boolean $uploading 214 | */ 215 | public function setUploading($uploading) 216 | { 217 | $this->uploading = (boolean) $uploading; 218 | } 219 | 220 | /** 221 | * @return boolean 222 | */ 223 | public function isUploading() 224 | { 225 | return $this->uploading; 226 | } 227 | 228 | /** 229 | * @param boolean $utp 230 | */ 231 | public function setUtp($utp) 232 | { 233 | $this->utp = (boolean) $utp; 234 | } 235 | 236 | /** 237 | * @return boolean 238 | */ 239 | public function isUtp() 240 | { 241 | return $this->utp; 242 | } 243 | 244 | /** 245 | * @param boolean $choked 246 | */ 247 | public function setPeerChoked($choked) 248 | { 249 | $this->peerChoked = (boolean) $choked; 250 | } 251 | 252 | /** 253 | * @return boolean 254 | */ 255 | public function isPeerChoked() 256 | { 257 | return $this->peerChoked; 258 | } 259 | 260 | /** 261 | * @param boolean $interested 262 | */ 263 | public function setPeerInterested($interested) 264 | { 265 | $this->peerInterested = (boolean) $interested; 266 | } 267 | 268 | /** 269 | * @return boolean 270 | */ 271 | public function isPeerInterested() 272 | { 273 | return $this->peerInterested; 274 | } 275 | 276 | /** 277 | * @param double $progress 278 | */ 279 | public function setProgress($progress) 280 | { 281 | $this->progress = (double) $progress; 282 | } 283 | 284 | /** 285 | * @return double 286 | */ 287 | public function getProgress() 288 | { 289 | return $this->progress; 290 | } 291 | 292 | /** 293 | * @param integer $rate 294 | */ 295 | public function setUploadRate($rate) 296 | { 297 | $this->uploadRate = (integer) $rate; 298 | } 299 | 300 | /** 301 | * @return integer 302 | */ 303 | public function getUploadRate() 304 | { 305 | return $this->uploadRate; 306 | } 307 | 308 | /** 309 | * @param integer $rate 310 | */ 311 | public function setDownloadRate($rate) 312 | { 313 | $this->downloadRate = (integer) $rate; 314 | } 315 | 316 | /** 317 | * @return integer 318 | */ 319 | public function getDownloadRate() 320 | { 321 | return $this->downloadRate; 322 | } 323 | 324 | /** 325 | * {@inheritDoc} 326 | */ 327 | public static function getMapping() 328 | { 329 | return array( 330 | 'address' => 'address', 331 | 'port' => 'port', 332 | 'clientName' => 'clientName', 333 | 'clientIsChoked' => 'clientChoked', 334 | 'clientIsInterested' => 'clientInterested', 335 | 'isDownloadingFrom' => 'downloading', 336 | 'isEncrypted' => 'encrypted', 337 | 'isIncoming' => 'incoming', 338 | 'isUploadingTo' => 'uploading', 339 | 'isUTP' => 'utp', 340 | 'peerIsChoked' => 'peerChoked', 341 | 'peerIsInterested' => 'peerInterested', 342 | 'progress' => 'progress', 343 | 'rateToClient' => 'uploadRate', 344 | 'rateFromClient' => 'downloadRate' 345 | ); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Session.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Session extends AbstractModel 11 | { 12 | /** 13 | * @var integer 14 | */ 15 | protected $altSpeedDown; 16 | 17 | /** 18 | * @var boolean 19 | */ 20 | protected $altSpeedEnabled; 21 | 22 | /** 23 | * @var string 24 | */ 25 | protected $downloadDir; 26 | 27 | /** 28 | * @var boolean 29 | */ 30 | protected $downloadQueueEnabled; 31 | 32 | /** 33 | * @var integer 34 | */ 35 | protected $downloadQueueSize; 36 | 37 | /** 38 | * @var string 39 | */ 40 | protected $incompleteDir; 41 | 42 | /** 43 | * @var boolean 44 | */ 45 | protected $incompleteDirEnabled; 46 | 47 | /** 48 | * @var string 49 | */ 50 | protected $torrentDoneScript; 51 | 52 | /** 53 | * @var boolean 54 | */ 55 | protected $torrentDoneScriptEnabled; 56 | 57 | /** 58 | * @var double 59 | */ 60 | protected $seedRatioLimit; 61 | 62 | /** 63 | * @var boolean 64 | */ 65 | protected $seedRatioLimited; 66 | 67 | /** 68 | * @var integer 69 | */ 70 | protected $seedQueueSize; 71 | 72 | /** 73 | * @var boolean 74 | */ 75 | protected $seedQueueEnabled; 76 | 77 | /** 78 | * @var integer 79 | */ 80 | protected $downloadSpeedLimit; 81 | 82 | /** 83 | * @var boolean 84 | */ 85 | protected $downloadSpeedLimitEnabled; 86 | 87 | /** 88 | * @var integer 89 | */ 90 | protected $uploadSpeedLimit; 91 | 92 | /** 93 | * @var boolean 94 | */ 95 | protected $uploadSpeedLimitEnabled; 96 | 97 | /** 98 | * @param integer $speed 99 | */ 100 | public function setAltSpeedDown($speed) 101 | { 102 | $this->altSpeedDown = (integer) $speed; 103 | } 104 | 105 | /** 106 | * @return integer 107 | */ 108 | public function getAltSpeedDown() 109 | { 110 | return $this->altSpeedDown; 111 | } 112 | 113 | /** 114 | * @param boolean $enabled 115 | */ 116 | public function setAltSpeedEnabled($enabled) 117 | { 118 | $this->altSpeedEnabled = (boolean) $enabled; 119 | } 120 | 121 | /** 122 | * @return boolean 123 | */ 124 | public function isAltSpeedEnabled() 125 | { 126 | return $this->altSpeedEnabled; 127 | } 128 | 129 | /** 130 | * @param string $downloadDir 131 | */ 132 | public function setDownloadDir($downloadDir) 133 | { 134 | $this->downloadDir = (string) $downloadDir; 135 | } 136 | 137 | /** 138 | * @return string 139 | */ 140 | public function getDownloadDir() 141 | { 142 | return $this->downloadDir; 143 | } 144 | 145 | /** 146 | * @param boolean $enabled 147 | */ 148 | public function setDownloadQueueEnabled($enabled) 149 | { 150 | $this->downloadQueueEnabled = (boolean) $enabled; 151 | } 152 | 153 | /** 154 | * @return boolean 155 | */ 156 | public function isDownloadQueueEnabled() 157 | { 158 | return $this->downloadQueueEnabled; 159 | } 160 | 161 | /** 162 | * @param integer $size 163 | */ 164 | public function setDownloadQueueSize($size) 165 | { 166 | $this->downloadQueueSize = (integer) $size; 167 | } 168 | 169 | /** 170 | * @return integer 171 | */ 172 | public function getDownloadQueueSize() 173 | { 174 | return $this->downloadQueueSize; 175 | } 176 | 177 | /** 178 | * @param string $directory 179 | */ 180 | public function setIncompleteDir($directory) 181 | { 182 | $this->incompleteDir = (string) $directory; 183 | } 184 | 185 | /** 186 | * @return string 187 | */ 188 | public function getIncompleteDir() 189 | { 190 | return $this->incompleteDir; 191 | } 192 | 193 | /** 194 | * @param boolean $enabled 195 | */ 196 | public function setIncompleteDirEnabled($enabled) 197 | { 198 | $this->incompleteDirEnabled = (boolean) $enabled; 199 | } 200 | 201 | /** 202 | * @return boolean 203 | */ 204 | public function isIncompleteDirEnabled() 205 | { 206 | return $this->incompleteDirEnabled; 207 | } 208 | 209 | /** 210 | * @param string $filename 211 | */ 212 | public function setTorrentDoneScript($filename) 213 | { 214 | $this->torrentDoneScript = (string) $filename; 215 | } 216 | 217 | /** 218 | * @return string 219 | */ 220 | public function getTorrentDoneScript() 221 | { 222 | return $this->torrentDoneScript; 223 | } 224 | 225 | /** 226 | * @param boolean $enabled 227 | */ 228 | public function setTorrentDoneScriptEnabled($enabled) 229 | { 230 | $this->torrentDoneScriptEnabled = (boolean) $enabled; 231 | } 232 | 233 | /** 234 | * @return boolean 235 | */ 236 | public function isTorrentDoneScriptEnabled() 237 | { 238 | return $this->torrentDoneScriptEnabled; 239 | } 240 | 241 | /** 242 | * @param double $limit 243 | */ 244 | public function setSeedRatioLimit($limit) 245 | { 246 | $this->seedRatioLimit = (double) $limit; 247 | } 248 | 249 | /** 250 | * @return double 251 | */ 252 | public function getSeedRatioLimit() 253 | { 254 | return $this->seedRatioLimit; 255 | } 256 | 257 | /** 258 | * @param boolean $limited 259 | */ 260 | public function setSeedRatioLimited($limited) 261 | { 262 | $this->seedRatioLimited = (boolean) $limited; 263 | } 264 | 265 | /** 266 | * @return boolean 267 | */ 268 | public function isSeedRatioLimited() 269 | { 270 | return $this->seedRatioLimited; 271 | } 272 | 273 | /** 274 | * @param integer $size 275 | */ 276 | public function setSeedQueueSize($size) 277 | { 278 | $this->seedQueueSize = (integer) $size; 279 | } 280 | 281 | /** 282 | * @return integer 283 | */ 284 | public function getSeedQueueSize() 285 | { 286 | return $this->seedQueueSize; 287 | } 288 | 289 | /** 290 | * @param boolean $enabled 291 | */ 292 | public function setSeedQueueEnabled($enabled) 293 | { 294 | $this->seedQueueEnabled = (boolean) $enabled; 295 | } 296 | 297 | /** 298 | * @return boolean 299 | */ 300 | public function isSeedQueueEnabled() 301 | { 302 | return $this->seedQueueEnabled; 303 | } 304 | 305 | /** 306 | * @param integer $limit 307 | */ 308 | public function setDownloadSpeedLimit($limit) 309 | { 310 | $this->downloadSpeedLimit = (integer) $limit; 311 | } 312 | 313 | /** 314 | * @return integer 315 | */ 316 | public function getDownloadSpeedLimit() 317 | { 318 | return $this->downloadSpeedLimit; 319 | } 320 | 321 | /** 322 | * @param boolean $enabled 323 | */ 324 | public function setDownloadSpeedLimitEnabled($enabled) 325 | { 326 | $this->downloadSpeedLimitEnabled = (boolean) $enabled; 327 | } 328 | 329 | /** 330 | * @return boolean 331 | */ 332 | public function isDownloadSpeedLimitEnabled() 333 | { 334 | return $this->downloadSpeedLimitEnabled; 335 | } 336 | 337 | /** 338 | * @param integer $limit 339 | */ 340 | public function setUploadSpeedLimit($limit) 341 | { 342 | $this->uploadSpeedLimit = (integer) $limit; 343 | } 344 | 345 | /** 346 | * @return integer 347 | */ 348 | public function getUploadSpeedLimit() 349 | { 350 | return $this->uploadSpeedLimit; 351 | } 352 | 353 | /** 354 | * @param boolean $enabled 355 | */ 356 | public function setUploadSpeedLimitEnabled($enabled) 357 | { 358 | $this->uploadSpeedLimitEnabled = (boolean) $enabled; 359 | } 360 | 361 | /** 362 | * @return boolean 363 | */ 364 | public function isUploadSpeedLimitEnabled() 365 | { 366 | return $this->uploadSpeedLimitEnabled; 367 | } 368 | 369 | /** 370 | * {@inheritDoc} 371 | */ 372 | public static function getMapping() 373 | { 374 | return array( 375 | 'alt-speed-down' => 'altSpeedDown', 376 | 'alt-speed-enabled' => 'altSpeedEnabled', 377 | 'download-dir' => 'downloadDir', 378 | 'download-queue-enabled' => 'downloadQueueEnabled', 379 | 'download-queue-size' => 'downloadQueueSize', 380 | 'incomplete-dir' => 'incompleteDir', 381 | 'incomplete-dir-enabled' => 'incompleteDirEnabled', 382 | 'script-torrent-done-filename' => 'torrentDoneScript', 383 | 'script-torrent-done-enabled' => 'torrentDoneScriptEnabled', 384 | 'seedRatioLimit' => 'seedRatioLimit', 385 | 'seedRatioLimited' => 'seedRatioLimited', 386 | 'seed-queue-size' => 'seedQueueSize', 387 | 'seed-queue-enabled' => 'seedQueueEnabled', 388 | 'speed-limit-down' => 'downloadSpeedLimit', 389 | 'speed-limit-down-enabled' => 'downloadSpeedLimitEnabled', 390 | 'speed-limit-up' => 'uploadSpeedLimit', 391 | 'speed-limit-up-enabled' => 'uploadSpeedLimitEnabled', 392 | ); 393 | } 394 | 395 | public function save() 396 | { 397 | $arguments = array(); 398 | 399 | foreach ($this->getMapping() as $key => $value) { 400 | $arguments[$key] = $this->{$value}; 401 | } 402 | 403 | if (!empty($arguments) && $this->getClient()) { 404 | ResponseValidator::validate( 405 | 'session-set', 406 | $this->getClient()->call('session-set', $arguments) 407 | ); 408 | } 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Stats/Session.php: -------------------------------------------------------------------------------- 1 | activeTorrentCount; 54 | } 55 | 56 | /** 57 | * Sets the value of activeTorrentCount. 58 | * 59 | * @param integer $activeTorrentCount the active torrent count 60 | */ 61 | public function setActiveTorrentCount($activeTorrentCount) 62 | { 63 | $this->activeTorrentCount = $activeTorrentCount; 64 | } 65 | 66 | /** 67 | * Gets the value of downloadSpeed. 68 | * 69 | * @return integer 70 | */ 71 | public function getDownloadSpeed() 72 | { 73 | return $this->downloadSpeed; 74 | } 75 | 76 | /** 77 | * Sets the value of downloadSpeed. 78 | * 79 | * @param integer $downloadSpeed the download speed 80 | */ 81 | public function setDownloadSpeed($downloadSpeed) 82 | { 83 | $this->downloadSpeed = $downloadSpeed; 84 | } 85 | 86 | /** 87 | * Gets the value of pausedTorrentCount. 88 | * 89 | * @return integer 90 | */ 91 | public function getPausedTorrentCount() 92 | { 93 | return $this->pausedTorrentCount; 94 | } 95 | 96 | /** 97 | * Sets the value of pausedTorrentCount. 98 | * 99 | * @param integer $pausedTorrentCount the paused torrent count 100 | */ 101 | public function setPausedTorrentCount($pausedTorrentCount) 102 | { 103 | $this->pausedTorrentCount = $pausedTorrentCount; 104 | } 105 | 106 | /** 107 | * Gets the value of torrentCount. 108 | * 109 | * @return integer 110 | */ 111 | public function getTorrentCount() 112 | { 113 | return $this->torrentCount; 114 | } 115 | 116 | /** 117 | * Sets the value of torrentCount. 118 | * 119 | * @param integer $torrentCount the torrent count 120 | */ 121 | public function setTorrentCount($torrentCount) 122 | { 123 | $this->torrentCount = $torrentCount; 124 | } 125 | 126 | /** 127 | * Gets the value of uploadSpeed. 128 | * 129 | * @return integer 130 | */ 131 | public function getUploadSpeed() 132 | { 133 | return $this->uploadSpeed; 134 | } 135 | 136 | /** 137 | * Sets the value of uploadSpeed. 138 | * 139 | * @param integer $uploadSpeed the upload speed 140 | */ 141 | public function setUploadSpeed($uploadSpeed) 142 | { 143 | $this->uploadSpeed = $uploadSpeed; 144 | } 145 | 146 | /** 147 | * Gets the value of cumulative. 148 | * 149 | * @return Stats 150 | */ 151 | public function getCumulative() 152 | { 153 | return $this->cumulative; 154 | } 155 | 156 | /** 157 | * Sets the value of cumulative. 158 | * 159 | * @param Stats $cumulative the cumulative 160 | */ 161 | public function setCumulative(Stats $cumulative) 162 | { 163 | $this->cumulative = $cumulative; 164 | } 165 | 166 | /** 167 | * Gets the value of current. 168 | * 169 | * @return Stats 170 | */ 171 | public function getCurrent() 172 | { 173 | return $this->current; 174 | } 175 | 176 | /** 177 | * Sets the value of current. 178 | * 179 | * @param Stats $current the current 180 | */ 181 | public function setCurrent(Stats $current) 182 | { 183 | $this->current = $current; 184 | } 185 | 186 | /** 187 | * {@inheritDoc} 188 | */ 189 | public static function getMapping() 190 | { 191 | return array( 192 | 'activeTorrentCount' => 'activeTorrentCount', 193 | 'downloadSpeed' => 'downloadSpeed', 194 | 'pausedTorrentCount' => 'pausedTorrentCount', 195 | 'torrentCount' => 'torrentCount', 196 | 'uploadSpeed' => 'uploadSpeed', 197 | 'cumulative-stats'=>'cumulative', 198 | 'current-stats' => 'current', 199 | ); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Stats/Stats.php: -------------------------------------------------------------------------------- 1 | downloadedBytes; 45 | } 46 | 47 | /** 48 | * Sets the value of downloadedBytes. 49 | * 50 | * @param integer $downloadedBytes the downloaded bytes 51 | */ 52 | public function setDownloadedBytes($downloadedBytes) 53 | { 54 | $this->downloadedBytes = $downloadedBytes; 55 | } 56 | 57 | /** 58 | * Gets the value of filesAdded. 59 | * 60 | * @return integer 61 | */ 62 | public function getFilesAdded() 63 | { 64 | return $this->filesAdded; 65 | } 66 | 67 | /** 68 | * Sets the value of filesAdded. 69 | * 70 | * @param integer $filesAdded the files added 71 | */ 72 | public function setFilesAdded($filesAdded) 73 | { 74 | $this->filesAdded = $filesAdded; 75 | } 76 | 77 | /** 78 | * Gets the value of secondsActive. 79 | * 80 | * @return integer 81 | */ 82 | public function getSecondsActive() 83 | { 84 | return $this->secondsActive; 85 | } 86 | 87 | /** 88 | * Sets the value of secondsActive. 89 | * 90 | * @param integer $secondsActive the seconds active 91 | */ 92 | public function setSecondsActive($secondsActive) 93 | { 94 | $this->secondsActive = $secondsActive; 95 | } 96 | 97 | /** 98 | * Gets the value of sessionCount. 99 | * 100 | * @return integer 101 | */ 102 | public function getSessionCount() 103 | { 104 | return $this->sessionCount; 105 | } 106 | 107 | /** 108 | * Sets the value of sessionCount. 109 | * 110 | * @param integer $sessionCount the session count 111 | */ 112 | public function setSessionCount($sessionCount) 113 | { 114 | $this->sessionCount = $sessionCount; 115 | } 116 | 117 | /** 118 | * Gets the value of uploadedBytes. 119 | * 120 | * @return integer 121 | */ 122 | public function getUploadedBytes() 123 | { 124 | return $this->uploadedBytes; 125 | } 126 | 127 | /** 128 | * Sets the value of uploadedBytes. 129 | * 130 | * @param integer $uploadedBytes the uploaded bytes 131 | */ 132 | public function setUploadedBytes($uploadedBytes) 133 | { 134 | $this->uploadedBytes = $uploadedBytes; 135 | } 136 | 137 | /** 138 | * {@inheritDoc} 139 | */ 140 | public static function getMapping() 141 | { 142 | return array( 143 | 'downloadedBytes' => 'downloadedBytes', 144 | 'filesAdded' => 'filesAdded', 145 | 'secondsActive' => 'secondsActive', 146 | 'sessionCount' => 'sessionCount', 147 | 'uploadedBytes' => 'uploadedBytes' 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Status.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class Status extends AbstractModel 8 | { 9 | /** 10 | * @var integer 11 | */ 12 | const STATUS_STOPPED = 0; 13 | 14 | /** 15 | * @var integer 16 | */ 17 | const STATUS_CHECK_WAIT = 1; 18 | 19 | /** 20 | * @var integer 21 | */ 22 | const STATUS_CHECK = 2; 23 | 24 | /** 25 | * @var integer 26 | */ 27 | const STATUS_DOWNLOAD_WAIT = 3; 28 | 29 | /** 30 | * @var integer 31 | */ 32 | const STATUS_DOWNLOAD = 4; 33 | 34 | /** 35 | * @var integer 36 | */ 37 | const STATUS_SEED_WAIT = 5; 38 | 39 | /** 40 | * @var integer 41 | */ 42 | const STATUS_SEED = 6; 43 | 44 | /** 45 | * @var integer 46 | */ 47 | protected $status; 48 | 49 | /** 50 | * @param integer|Status $status 51 | */ 52 | public function __construct($status) 53 | { 54 | if ($status instanceof self) { 55 | $this->status = $status->getValue(); 56 | } else { 57 | $this->status = (integer) $status; 58 | } 59 | } 60 | 61 | /** 62 | * @return integer 63 | */ 64 | public function getValue() 65 | { 66 | return $this->status; 67 | } 68 | 69 | /** 70 | * @return boolean 71 | */ 72 | public function isStopped() 73 | { 74 | return $this->status == self::STATUS_STOPPED; 75 | } 76 | 77 | /** 78 | * @return boolean 79 | */ 80 | public function isChecking() 81 | { 82 | return ($this->status == self::STATUS_CHECK || 83 | $this->status == self::STATUS_CHECK_WAIT); 84 | } 85 | 86 | /** 87 | * @return boolean 88 | */ 89 | public function isDownloading() 90 | { 91 | return ($this->status == self::STATUS_DOWNLOAD || 92 | $this->status == self::STATUS_DOWNLOAD_WAIT); 93 | } 94 | 95 | /** 96 | * @return boolean 97 | */ 98 | public function isSeeding() 99 | { 100 | return ($this->status == self::STATUS_SEED || 101 | $this->status == self::STATUS_SEED_WAIT); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Torrent.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class Torrent extends AbstractModel 10 | { 11 | /** 12 | * @var integer 13 | */ 14 | protected $id; 15 | 16 | /** 17 | * @var integer 18 | */ 19 | protected $eta; 20 | 21 | /** 22 | * @var integer 23 | */ 24 | protected $size; 25 | 26 | /** 27 | * @var string 28 | */ 29 | protected $name; 30 | 31 | /** 32 | * @var string 33 | */ 34 | protected $hash; 35 | 36 | /** 37 | * @var Status 38 | */ 39 | protected $status; 40 | 41 | /** 42 | * @var boolean 43 | */ 44 | protected $finished; 45 | 46 | /** 47 | * @var integer 48 | */ 49 | protected $startDate; 50 | 51 | /** 52 | * @var integer 53 | */ 54 | protected $uploadRate; 55 | 56 | /** 57 | * @var integer 58 | */ 59 | protected $downloadRate; 60 | 61 | /** 62 | * @var integer 63 | */ 64 | protected $peersConnected; 65 | 66 | /** 67 | * @var double 68 | */ 69 | protected $percentDone; 70 | 71 | /** 72 | * @var array 73 | */ 74 | protected $files = array(); 75 | 76 | /** 77 | * @var array 78 | */ 79 | protected $peers = array(); 80 | 81 | /** 82 | * @var array 83 | */ 84 | protected $trackers = array(); 85 | 86 | /** 87 | * @var array 88 | */ 89 | protected $trackerStats = array(); 90 | 91 | /** 92 | * @var double 93 | */ 94 | protected $uploadRatio; 95 | 96 | /** 97 | * @var string 98 | */ 99 | protected $downloadDir; 100 | 101 | /** 102 | * @var integer 103 | */ 104 | protected $downloadedEver; 105 | 106 | /** 107 | * @var integer 108 | */ 109 | protected $uploadedEver; 110 | 111 | /** 112 | * @param integer $id 113 | */ 114 | public function setId($id) 115 | { 116 | $this->id = (integer) $id; 117 | } 118 | 119 | /** 120 | * @return integer 121 | */ 122 | public function getId() 123 | { 124 | return $this->id; 125 | } 126 | 127 | /** 128 | * @param integer $eta 129 | */ 130 | public function setEta($eta) 131 | { 132 | $this->eta = (integer) $eta; 133 | } 134 | 135 | /** 136 | * @return integer 137 | */ 138 | public function getEta() 139 | { 140 | return $this->eta; 141 | } 142 | 143 | /** 144 | * @param integer $size 145 | */ 146 | public function setSize($size) 147 | { 148 | $this->size = (integer) $size; 149 | } 150 | 151 | /** 152 | * @return integer 153 | */ 154 | public function getSize() 155 | { 156 | return $this->size; 157 | } 158 | 159 | /** 160 | * @param string $name 161 | */ 162 | public function setName($name) 163 | { 164 | $this->name = (string) $name; 165 | } 166 | 167 | /** 168 | * @return string 169 | */ 170 | public function getName() 171 | { 172 | return $this->name; 173 | } 174 | 175 | /** 176 | * @param string $hash 177 | */ 178 | public function setHash($hash) 179 | { 180 | $this->hash = (string) $hash; 181 | } 182 | 183 | /** 184 | * @return string 185 | */ 186 | public function getHash() 187 | { 188 | return $this->hash; 189 | } 190 | 191 | /** 192 | * @param integer|Status $status 193 | */ 194 | public function setStatus($status) 195 | { 196 | $this->status = new Status($status); 197 | } 198 | 199 | /** 200 | * @return integer 201 | */ 202 | public function getStatus() 203 | { 204 | return $this->status->getValue(); 205 | } 206 | 207 | /** 208 | * @param boolean $finished 209 | */ 210 | public function setFinished($finished) 211 | { 212 | $this->finished = (boolean) $finished; 213 | } 214 | 215 | /** 216 | * @return boolean 217 | */ 218 | public function isFinished() 219 | { 220 | return ($this->finished || (int) $this->getPercentDone() == 100); 221 | } 222 | 223 | /** 224 | * @var integer $startDate 225 | */ 226 | public function setStartDate($startDate) 227 | { 228 | $this->startDate = (integer) $startDate; 229 | } 230 | 231 | /** 232 | * @return integer 233 | */ 234 | public function getStartDate() 235 | { 236 | return $this->startDate; 237 | } 238 | /** 239 | * @var integer $rate 240 | */ 241 | public function setUploadRate($rate) 242 | { 243 | $this->uploadRate = (integer) $rate; 244 | } 245 | 246 | /** 247 | * @return integer 248 | */ 249 | public function getUploadRate() 250 | { 251 | return $this->uploadRate; 252 | } 253 | 254 | /** 255 | * @param integer $rate 256 | */ 257 | public function setDownloadRate($rate) 258 | { 259 | $this->downloadRate = (integer) $rate; 260 | } 261 | 262 | /** 263 | * @param integer $peersConnected 264 | */ 265 | public function setPeersConnected($peersConnected) 266 | { 267 | $this->peersConnected = (integer) $peersConnected; 268 | } 269 | 270 | /** 271 | * @return integer 272 | */ 273 | public function getPeersConnected() 274 | { 275 | return $this->peersConnected; 276 | } 277 | 278 | /** 279 | * @return integer 280 | */ 281 | public function getDownloadRate() 282 | { 283 | return $this->downloadRate; 284 | } 285 | 286 | /** 287 | * @param double $done 288 | */ 289 | public function setPercentDone($done) 290 | { 291 | $this->percentDone = (double) $done; 292 | } 293 | 294 | /** 295 | * @return double 296 | */ 297 | public function getPercentDone() 298 | { 299 | return $this->percentDone * 100.0; 300 | } 301 | 302 | /** 303 | * @param array $files 304 | */ 305 | public function setFiles(array $files) 306 | { 307 | $this->files = array_map(function ($file) { 308 | return PropertyMapper::map(new File(), $file); 309 | }, $files); 310 | } 311 | 312 | /** 313 | * @return array 314 | */ 315 | public function getFiles() 316 | { 317 | return $this->files; 318 | } 319 | 320 | /** 321 | * @param array $peers 322 | */ 323 | public function setPeers(array $peers) 324 | { 325 | $this->peers = array_map(function ($peer) { 326 | return PropertyMapper::map(new Peer(), $peer); 327 | }, $peers); 328 | } 329 | 330 | /** 331 | * @return array 332 | */ 333 | public function getPeers() 334 | { 335 | return $this->peers; 336 | } 337 | /** 338 | * @param array $trackerStats 339 | */ 340 | public function setTrackerStats(array $trackerStats) 341 | { 342 | $this->trackerStats = array_map(function ($trackerStats) { 343 | return PropertyMapper::map(new TrackerStats(), $trackerStats); 344 | }, $trackerStats); 345 | } 346 | 347 | /** 348 | * @return array 349 | */ 350 | public function getTrackerStats() 351 | { 352 | return $this->trackerStats; 353 | } 354 | 355 | /** 356 | * @param array $trackers 357 | */ 358 | public function setTrackers(array $trackers) 359 | { 360 | $this->trackers = array_map(function ($tracker) { 361 | return PropertyMapper::map(new Tracker(), $tracker); 362 | }, $trackers); 363 | } 364 | 365 | /** 366 | * @return array 367 | */ 368 | public function getTrackers() 369 | { 370 | return $this->trackers; 371 | } 372 | 373 | /** 374 | * @param double $ratio 375 | */ 376 | public function setUploadRatio($ratio) 377 | { 378 | $this->uploadRatio = (double) $ratio; 379 | } 380 | 381 | /** 382 | * @return double 383 | */ 384 | public function getUploadRatio() 385 | { 386 | return $this->uploadRatio; 387 | } 388 | 389 | /** 390 | * @return boolean 391 | */ 392 | public function isStopped() 393 | { 394 | return $this->status->isStopped(); 395 | } 396 | 397 | /** 398 | * @return boolean 399 | */ 400 | public function isChecking() 401 | { 402 | return $this->status->isChecking(); 403 | } 404 | 405 | /** 406 | * @return boolean 407 | */ 408 | public function isDownloading() 409 | { 410 | return $this->status->isDownloading(); 411 | } 412 | 413 | /** 414 | * @return boolean 415 | */ 416 | public function isSeeding() 417 | { 418 | return $this->status->isSeeding(); 419 | } 420 | 421 | /** 422 | * @return string 423 | */ 424 | public function getDownloadDir() 425 | { 426 | return $this->downloadDir; 427 | } 428 | 429 | /** 430 | * @param string $downloadDir 431 | */ 432 | public function setDownloadDir($downloadDir) 433 | { 434 | $this->downloadDir = $downloadDir; 435 | } 436 | 437 | /** 438 | * @return int 439 | */ 440 | public function getDownloadedEver() { 441 | return $this->downloadedEver; 442 | } 443 | 444 | /** 445 | * @param int $downloadedEver 446 | */ 447 | public function setDownloadedEver($downloadedEver) { 448 | $this->downloadedEver = $downloadedEver; 449 | } 450 | 451 | /** 452 | * @return int 453 | */ 454 | public function getUploadedEver() { 455 | return $this->uploadedEver; 456 | } 457 | 458 | /** 459 | * @param int $uploadedEver 460 | */ 461 | public function setUploadedEver($uploadedEver) { 462 | $this->uploadedEver = $uploadedEver; 463 | } 464 | 465 | /** 466 | * {@inheritDoc} 467 | */ 468 | public static function getMapping() 469 | { 470 | return array( 471 | 'id' => 'id', 472 | 'eta' => 'eta', 473 | 'sizeWhenDone' => 'size', 474 | 'name' => 'name', 475 | 'status' => 'status', 476 | 'isFinished' => 'finished', 477 | 'rateUpload' => 'uploadRate', 478 | 'rateDownload' => 'downloadRate', 479 | 'percentDone' => 'percentDone', 480 | 'files' => 'files', 481 | 'peers' => 'peers', 482 | 'peersConnected' => 'peersConnected', 483 | 'trackers' => 'trackers', 484 | 'trackerStats' => 'trackerStats', 485 | 'startDate' => 'startDate', 486 | 'uploadRatio' => 'uploadRatio', 487 | 'hashString' => 'hash', 488 | 'downloadDir' => 'downloadDir', 489 | 'downloadedEver' => 'downloadedEver', 490 | 'uploadedEver' => 'uploadedEver' 491 | ); 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /lib/Transmission/Model/Tracker.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class Tracker extends AbstractModel 8 | { 9 | /** 10 | * @var integer 11 | */ 12 | protected $id; 13 | 14 | /** 15 | * @var integer 16 | */ 17 | protected $tier; 18 | 19 | /** 20 | * @var string 21 | */ 22 | protected $scrape; 23 | 24 | /** 25 | * @var string 26 | */ 27 | protected $announce; 28 | 29 | /** 30 | * @param integer $id 31 | */ 32 | public function setId($id) 33 | { 34 | $this->id = (integer) $id; 35 | } 36 | 37 | /** 38 | * @return integer 39 | */ 40 | public function getId() 41 | { 42 | return $this->id; 43 | } 44 | 45 | /** 46 | * @param integer $tier 47 | */ 48 | public function setTier($tier) 49 | { 50 | $this->tier = (integer) $tier; 51 | } 52 | 53 | /** 54 | * @return integer 55 | */ 56 | public function getTier() 57 | { 58 | return $this->tier; 59 | } 60 | 61 | /** 62 | * @param string $scrape 63 | */ 64 | public function setScrape($scrape) 65 | { 66 | $this->scrape = (string) $scrape; 67 | } 68 | 69 | /** 70 | * @return string 71 | */ 72 | public function getScrape() 73 | { 74 | return $this->scrape; 75 | } 76 | 77 | /** 78 | * @param string $announce 79 | */ 80 | public function setAnnounce($announce) 81 | { 82 | $this->announce = (string) $announce; 83 | } 84 | 85 | /** 86 | * @return string 87 | */ 88 | public function getAnnounce() 89 | { 90 | return $this->announce; 91 | } 92 | 93 | /** 94 | * {@inheritDoc} 95 | */ 96 | public static function getMapping() 97 | { 98 | return array( 99 | 'id' => 'id', 100 | 'tier' => 'tier', 101 | 'scrape' => 'scrape', 102 | 'announce' => 'announce' 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/Transmission/Model/TrackerStats.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class TrackerStats extends AbstractModel 8 | { 9 | /** 10 | * @var string 11 | */ 12 | protected $host; 13 | 14 | /** 15 | * @var integer 16 | */ 17 | protected $leecherCount; 18 | 19 | /** 20 | * @var integer 21 | */ 22 | protected $seederCount; 23 | 24 | /** 25 | * @var string 26 | */ 27 | protected $lastAnnounceResult; 28 | 29 | /** 30 | * @var string 31 | */ 32 | protected $lastScrapeResult; 33 | 34 | /** 35 | * @param string $host 36 | */ 37 | public function setHost($host) 38 | { 39 | $this->host = (string) $host; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getHost() 46 | { 47 | return $this->host; 48 | } 49 | 50 | /** 51 | * @param string $lastAnnounceResult 52 | */ 53 | public function setLastAnnounceResult($lastAnnounceResult) 54 | { 55 | $this->lastAnnounceResult = (string) $lastAnnounceResult; 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | public function getLastAnnounceResult() 62 | { 63 | return $this->lastAnnounceResult; 64 | } 65 | 66 | /** 67 | * @param string $lastScrapeResult 68 | */ 69 | public function setLastScrapeResult($lastScrapeResult) 70 | { 71 | $this->lastScrapeResult = (string) $lastScrapeResult; 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getLastScrapeResult() 78 | { 79 | return $this->lastScrapeResult; 80 | } 81 | 82 | /** 83 | * @param integer $seederCount 84 | */ 85 | public function setSeederCount($seederCount) 86 | { 87 | $this->seederCount = (integer) $seederCount; 88 | } 89 | 90 | /** 91 | * @return integer 92 | */ 93 | public function getSeederCount() 94 | { 95 | return $this->seederCount; 96 | } 97 | 98 | /** 99 | * @param integer $leecherCount 100 | */ 101 | public function setLeecherCount($leecherCount) 102 | { 103 | $this->leecherCount = (integer) $leecherCount; 104 | } 105 | 106 | /** 107 | * @return integer 108 | */ 109 | public function getLeecherCount() 110 | { 111 | return $this->leecherCount; 112 | } 113 | 114 | /** 115 | * {@inheritDoc} 116 | */ 117 | public static function getMapping() 118 | { 119 | return array( 120 | 'host' => 'host', 121 | 'leecherCount' => 'leecherCount', 122 | 'seederCount' => 'seederCount', 123 | 'lastScrapeResult' => 'lastScrapeResult', 124 | 'lastAnnounceResult' => 'lastAnnounceResult' 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/Transmission/Transmission.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Transmission 15 | { 16 | /** 17 | * @var Client 18 | */ 19 | protected $client; 20 | 21 | /** 22 | * @var ResponseValidator 23 | */ 24 | protected $validator; 25 | 26 | /** 27 | * @var PropertyMapper 28 | */ 29 | protected $mapper; 30 | 31 | /** 32 | * Constructor 33 | * 34 | * @param string $host 35 | * @param integer $port 36 | * @param string $path 37 | */ 38 | public function __construct($host = null, $port = null, $path = null) 39 | { 40 | $this->setClient(new Client($host, $port, $path)); 41 | $this->setMapper(new PropertyMapper()); 42 | $this->setValidator(new ResponseValidator()); 43 | } 44 | 45 | /** 46 | * Get all the torrents in the download queue 47 | * 48 | * @return Torrent[] 49 | */ 50 | public function all() 51 | { 52 | $client = $this->getClient(); 53 | $mapper = $this->getMapper(); 54 | $response = $this->getClient()->call( 55 | 'torrent-get', 56 | array('fields' => array_keys(Torrent::getMapping())) 57 | ); 58 | 59 | $torrents = array_map(function ($data) use ($mapper, $client) { 60 | return $mapper->map( 61 | new Torrent($client), 62 | $data 63 | ); 64 | }, $this->getValidator()->validate('torrent-get', $response)); 65 | 66 | return $torrents; 67 | } 68 | 69 | /** 70 | * Get a specific torrent from the download queue 71 | * 72 | * @param integer $id 73 | * @return Torrent 74 | * @throws \RuntimeException 75 | */ 76 | public function get($id) 77 | { 78 | $client = $this->getClient(); 79 | $mapper = $this->getMapper(); 80 | $response = $this->getClient()->call('torrent-get', array( 81 | 'fields' => array_keys(Torrent::getMapping()), 82 | 'ids' => array($id) 83 | )); 84 | 85 | $torrent = array_reduce( 86 | $this->getValidator()->validate('torrent-get', $response), 87 | function ($torrent, $data) use ($mapper, $client) { 88 | return $torrent ? $torrent : $mapper->map(new Torrent($client), $data); 89 | }); 90 | 91 | if (!$torrent instanceof Torrent) { 92 | throw new \RuntimeException(sprintf("Torrent with ID %s not found", $id)); 93 | } 94 | 95 | return $torrent; 96 | } 97 | 98 | /** 99 | * Get the Transmission session 100 | * 101 | * @return Session 102 | */ 103 | public function getSession() 104 | { 105 | $response = $this->getClient()->call( 106 | 'session-get', 107 | array() 108 | ); 109 | 110 | return $this->getMapper()->map( 111 | new Session($this->getClient()), 112 | $this->getValidator()->validate('session-get', $response) 113 | ); 114 | } 115 | 116 | /** 117 | * @return SessionStats 118 | */ 119 | public function getSessionStats() 120 | { 121 | $response = $this->getClient()->call( 122 | 'session-stats', 123 | array() 124 | ); 125 | 126 | return $this->getMapper()->map( 127 | new SessionStats(), 128 | $this->getValidator()->validate('session-stats', $response) 129 | ); 130 | } 131 | 132 | /** 133 | * Get Free space 134 | * @param string $path 135 | * @return FreeSpace 136 | */ 137 | public function getFreeSpace($path=null) 138 | { 139 | if (!$path) { 140 | $path = $this->getSession()->getDownloadDir(); 141 | } 142 | $response = $this->getClient()->call( 143 | 'free-space', 144 | array('path'=>$path) 145 | ); 146 | 147 | return $this->getMapper()->map( 148 | new FreeSpace(), 149 | $this->getValidator()->validate('free-space', $response) 150 | ); 151 | } 152 | 153 | /** 154 | * Add a torrent to the download queue 155 | * 156 | * @param string $torrent 157 | * @param boolean $metainfo 158 | * @param string $savepath 159 | * @return Torrent 160 | */ 161 | public function add($torrent, $metainfo = false, $savepath = null) 162 | { 163 | $parameters = array($metainfo ? 'metainfo' : 'filename' => $torrent); 164 | 165 | if ($savepath !== null) { 166 | $parameters['download-dir'] = (string) $savepath; 167 | } 168 | 169 | $response = $this->getClient()->call( 170 | 'torrent-add', 171 | $parameters 172 | ); 173 | 174 | return $this->getMapper()->map( 175 | new Torrent($this->getClient()), 176 | $this->getValidator()->validate('torrent-add', $response) 177 | ); 178 | } 179 | 180 | /** 181 | * Start the download of a torrent 182 | * 183 | * @param Torrent $torrent 184 | * @param bool $now 185 | */ 186 | public function start(Torrent $torrent, $now = false) 187 | { 188 | $this->getClient()->call( 189 | $now ? 'torrent-start-now' : 'torrent-start', 190 | array('ids' => array($torrent->getId())) 191 | ); 192 | } 193 | 194 | /** 195 | * Stop the download of a torrent 196 | * 197 | * @param Torrent $torrent 198 | */ 199 | public function stop(Torrent $torrent) 200 | { 201 | $this->getClient()->call( 202 | 'torrent-stop', 203 | array('ids' => array($torrent->getId())) 204 | ); 205 | } 206 | 207 | /** 208 | * Verify the download of a torrent 209 | * 210 | * @param Torrent $torrent 211 | */ 212 | public function verify(Torrent $torrent) 213 | { 214 | $this->getClient()->call( 215 | 'torrent-verify', 216 | array('ids' => array($torrent->getId())) 217 | ); 218 | } 219 | 220 | /** 221 | * Request a reannounce of a torrent 222 | * 223 | * @param Torrent $torrent 224 | */ 225 | public function reannounce(Torrent $torrent) 226 | { 227 | $this->getClient()->call( 228 | 'torrent-reannounce', 229 | array('ids' => array($torrent->getId())) 230 | ); 231 | } 232 | 233 | /** 234 | * Remove a torrent from the download queue 235 | * 236 | * @param Torrent $torrent 237 | */ 238 | public function remove(Torrent $torrent, $localData = false) 239 | { 240 | $arguments = array('ids' => array($torrent->getId())); 241 | 242 | if ($localData) { 243 | $arguments['delete-local-data'] = true; 244 | } 245 | 246 | $this->getClient()->call('torrent-remove', $arguments); 247 | } 248 | 249 | /** 250 | * Set the client used to connect to Transmission 251 | * 252 | * @param Client $client 253 | */ 254 | public function setClient(Client $client) 255 | { 256 | $this->client = $client; 257 | } 258 | 259 | /** 260 | * Get the client used to connect to Transmission 261 | * 262 | * @return Client 263 | */ 264 | public function getClient() 265 | { 266 | return $this->client; 267 | } 268 | 269 | /** 270 | * Set the hostname of the Transmission server 271 | * 272 | * @param string $host 273 | */ 274 | public function setHost($host) 275 | { 276 | $this->getClient()->setHost($host); 277 | } 278 | 279 | /** 280 | * Get the hostname of the Transmission server 281 | * 282 | * @return string 283 | */ 284 | public function getHost() 285 | { 286 | return $this->getClient()->getHost(); 287 | } 288 | 289 | /** 290 | * Set the port the Transmission server is listening on 291 | * 292 | * @param integer $port 293 | */ 294 | public function setPort($port) 295 | { 296 | $this->getClient()->setPort($port); 297 | } 298 | 299 | /** 300 | * Get the port the Transmission server is listening on 301 | * 302 | * @return integer 303 | */ 304 | public function getPort() 305 | { 306 | return $this->getClient()->getPort(); 307 | } 308 | 309 | /** 310 | * Set the mapper used to map responses from Transmission to models 311 | * 312 | * @param PropertyMapper $mapper 313 | */ 314 | public function setMapper(PropertyMapper $mapper) 315 | { 316 | $this->mapper = $mapper; 317 | } 318 | 319 | /** 320 | * Get the mapper used to map responses from Transmission to models 321 | * 322 | * @return PropertyMapper 323 | */ 324 | public function getMapper() 325 | { 326 | return $this->mapper; 327 | } 328 | 329 | /** 330 | * Set the validator used to validate Transmission responses 331 | * 332 | * @param ResponseValidator $validator 333 | */ 334 | public function setValidator(ResponseValidator $validator) 335 | { 336 | $this->validator = $validator; 337 | } 338 | 339 | /** 340 | * Get the validator used to validate Transmission responses 341 | * 342 | * @return ResponseValidator 343 | */ 344 | public function getValidator() 345 | { 346 | return $this->validator; 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /lib/Transmission/Util/PropertyMapper.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class PropertyMapper 13 | { 14 | /** 15 | * @param ModelInterface $model 16 | * @param \stdClass $dto 17 | * @return ModelInterface 18 | */ 19 | public static function map(ModelInterface $model, $dto) 20 | { 21 | $accessor = PropertyAccess::createPropertyAccessor(); 22 | 23 | $mapping = array_filter($model->getMapping(), function ($value) { 24 | return !is_null($value); 25 | }); 26 | 27 | foreach ($mapping as $source => $dest) { 28 | try { 29 | $accessor->setValue( 30 | $model, 31 | $dest, 32 | $accessor->getValue($dto, $source) 33 | ); 34 | } catch (\Exception $e) { 35 | continue; 36 | } 37 | } 38 | 39 | return $model; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Transmission/Util/ResponseValidator.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class ResponseValidator 8 | { 9 | /** 10 | * @param string $method 11 | * @param \stdClass $response 12 | * @return \stdClass 13 | */ 14 | public static function validate($method, \stdClass $response) 15 | { 16 | if (!isset($response->result)) { 17 | throw new \RuntimeException('Invalid response received from Transmission'); 18 | } 19 | 20 | if (!in_array($response->result, array('success', 'duplicate torrent'))) { 21 | throw new \RuntimeException(sprintf( 22 | 'An error occured: "%s"', $response->result 23 | )); 24 | } 25 | 26 | switch ($method) { 27 | case 'torrent-get': 28 | return self::validateGetResponse($response); 29 | case 'torrent-add': 30 | return self::validateAddResponse($response); 31 | case 'session-get': 32 | return self::validateSessionGetResponse($response); 33 | case 'session-stats': 34 | return self::validateSessionStatsGetResponse($response); 35 | case 'free-space': 36 | return self::validateFreeSpaceGetResponse($response); 37 | } 38 | } 39 | 40 | /** 41 | * @param \stdClass $response 42 | * @throws \RuntimeException 43 | */ 44 | public static function validateGetResponse(\stdClass $response) 45 | { 46 | if (!isset($response->arguments) || 47 | !isset($response->arguments->torrents)) { 48 | throw new \RuntimeException( 49 | 'Invalid response received from Transmission' 50 | ); 51 | } 52 | 53 | return $response->arguments->torrents; 54 | } 55 | 56 | /** 57 | * @param \stdClass $response 58 | * @throws \RuntimeException 59 | */ 60 | public static function validateAddResponse(\stdClass $response) 61 | { 62 | $fields = array('torrent-added', 'torrent-duplicate'); 63 | 64 | foreach ($fields as $field) { 65 | if (isset($response->arguments) && 66 | isset($response->arguments->$field) && 67 | count($response->arguments->$field)) { 68 | return $response->arguments->$field; 69 | } 70 | } 71 | 72 | throw new \RuntimeException('Invalid response received from Transmission'); 73 | } 74 | 75 | public static function validateSessionGetResponse(\stdClass $response) 76 | { 77 | if (!isset($response->arguments)) { 78 | throw new \RuntimeException( 79 | 'Invalid response received from Transmission' 80 | ); 81 | } 82 | 83 | return $response->arguments; 84 | } 85 | 86 | /** 87 | * @param \stdClass $response 88 | * @return \stdClass 89 | */ 90 | public static function validateSessionStatsGetResponse(\stdClass $response) 91 | { 92 | if (!isset($response->arguments)) { 93 | throw new \RuntimeException( 94 | 'Invalid response received from Transmission' 95 | ); 96 | } 97 | $class='Transmission\\Model\\Stats\\Stats'; 98 | foreach (array('cumulative-stats','current-stats') as $property) { 99 | if (property_exists($response->arguments,$property)) { 100 | $instance=self::map($response->arguments->$property,$class); 101 | $response->arguments->$property=$instance; 102 | } 103 | } 104 | 105 | return $response->arguments; 106 | } 107 | 108 | private static function map($object,$class) 109 | { 110 | return PropertyMapper::map(new $class(),$object); 111 | 112 | } 113 | 114 | public static function validateFreeSpaceGetResponse(\stdClass $response) 115 | { 116 | if (!isset($response->arguments)) { 117 | throw new \RuntimeException( 118 | 'Invalid response received from Transmission' 119 | ); 120 | } 121 | 122 | return $response->arguments; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./tests/Transmission/ 5 | 6 | 7 | 8 | 9 | 10 | ./lib/Transmission/ 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/Transmission/Mock/Model.php: -------------------------------------------------------------------------------- 1 | fo = $fo; 15 | } 16 | 17 | public function getFo() 18 | { 19 | return $this->fo; 20 | } 21 | 22 | public function setBar($bar) 23 | { 24 | $this->bar = $bar; 25 | } 26 | 27 | public function getBar() 28 | { 29 | return $this->bar; 30 | } 31 | 32 | public function setUnused($unused) 33 | { 34 | $this->unused = $unused; 35 | } 36 | 37 | public function getUnused() 38 | { 39 | return $this->unused; 40 | } 41 | 42 | public static function getMapping() 43 | { 44 | return array( 45 | 'foo' => 'fo', 46 | 'bar' => 'bar', 47 | 'unused' => null 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/ClientTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('localhost', $this->getClient()->getHost()); 16 | } 17 | 18 | /** 19 | * @test 20 | */ 21 | public function shouldHaveDefaultPort() 22 | { 23 | $this->assertEquals(9091, $this->getClient()->getPort()); 24 | } 25 | 26 | /** 27 | * @test 28 | */ 29 | public function shouldHaveNoTokenOnInstantiation() 30 | { 31 | $this->assertEmpty($this->getClient()->getToken()); 32 | } 33 | 34 | /** 35 | * @test 36 | */ 37 | public function shouldHaveDefaultClient() 38 | { 39 | $this->assertInstanceOf('Buzz\Client\Curl', $this->getClient()->getClient()); 40 | } 41 | 42 | /** 43 | * @test 44 | */ 45 | public function shouldGenerateDefaultUrl() 46 | { 47 | $this->assertEquals('http://localhost:9091', $this->getClient()->getUrl()); 48 | } 49 | 50 | /** 51 | * @test 52 | */ 53 | public function shouldMakeApiCall() 54 | { 55 | $test = $this; 56 | $client = $this->getMock('Buzz\Client\Curl'); 57 | $client->expects($this->once()) 58 | ->method('send') 59 | ->with( 60 | $this->isInstanceOf('Buzz\Message\Request'), 61 | $this->isInstanceOf('Buzz\Message\Response') 62 | ) 63 | ->will($this->returnCallback(function ($request, $response) use ($test) { 64 | $test->assertEquals('POST', $request->getMethod()); 65 | $test->assertEquals('/transmission/rpc', $request->getResource()); 66 | $test->assertEquals('http://localhost:9091', $request->getHost()); 67 | $test->assertEmpty($request->getHeader('X-Transmission-Session-Id')); 68 | $test->assertEquals('{"method":"foo","arguments":{"bar":"baz"}}', $request->getContent()); 69 | 70 | $response->addHeader('HTTP/1.1 200 OK'); 71 | $response->addHeader('Content-Type: application/json'); 72 | $response->addHeader('X-Transmission-Session-Id: foo'); 73 | $response->setContent('{"foo":"bar"}'); 74 | })); 75 | 76 | $this->getClient()->setClient($client); 77 | $response = $this->getClient()->call('foo', array('bar' => 'baz')); 78 | 79 | $this->assertInstanceOf('stdClass', $response); 80 | $this->assertObjectHasAttribute('foo', $response); 81 | $this->assertEquals('bar', $response->foo); 82 | } 83 | 84 | /** 85 | * @test 86 | */ 87 | public function shouldAuthenticate() 88 | { 89 | $test = $this; 90 | $client = $this->getMock('Buzz\Client\Curl'); 91 | $client->expects($this->once()) 92 | ->method('send') 93 | ->with( 94 | $this->isInstanceOf('Buzz\Message\Request'), 95 | $this->isInstanceOf('Buzz\Message\Response') 96 | ) 97 | ->will($this->returnCallback(function ($request, $response) use ($test) { 98 | $test->assertEquals('POST', $request->getMethod()); 99 | $test->assertEquals('/transmission/rpc', $request->getResource()); 100 | $test->assertEquals('http://localhost:9091', $request->getHost()); 101 | $test->assertEmpty($request->getHeader('X-Transmission-Session-Id')); 102 | $test->assertEquals('Basic '. base64_encode('foo:bar'), $request->getHeader('Authorization')); 103 | $test->assertEquals('{"method":"foo","arguments":{"bar":"baz"}}', $request->getContent()); 104 | 105 | $response->addHeader('HTTP/1.1 200 OK'); 106 | $response->addHeader('Content-Type: application/json'); 107 | $response->addHeader('X-Transmission-Session-Id: foo'); 108 | $response->setContent('{"foo":"bar"}'); 109 | })); 110 | 111 | $this->getClient()->authenticate('foo', 'bar'); 112 | $this->getClient()->setClient($client); 113 | $response = $this->getClient()->call('foo', array('bar' => 'baz')); 114 | 115 | $this->assertInstanceOf('stdClass', $response); 116 | $this->assertObjectHasAttribute('foo', $response); 117 | $this->assertEquals('bar', $response->foo); 118 | } 119 | 120 | /** 121 | * @test 122 | * @expectedException RuntimeException 123 | */ 124 | public function shouldThrowExceptionOnExceptionDuringApiCall() 125 | { 126 | $client = $this->getMock('Buzz\Client\Curl'); 127 | $client->expects($this->once()) 128 | ->method('send') 129 | ->will($this->throwException(new \Exception())); 130 | 131 | $this->getClient()->setClient($client); 132 | $this->getClient()->call('foo', array()); 133 | } 134 | 135 | /** 136 | * @test 137 | * @expectedException RuntimeException 138 | */ 139 | public function shouldThrowExceptionOnUnexpectedStatusCode() 140 | { 141 | $client = $this->getMock('Buzz\Client\Curl'); 142 | $client->expects($this->once()) 143 | ->method('send') 144 | ->will($this->returnCallback(function ($request, $response) { 145 | $response->addHeader('HTTP/1.1 500 Internal Server Error'); 146 | })); 147 | 148 | $this->getClient()->setClient($client); 149 | $this->getClient()->call('foo', array()); 150 | } 151 | 152 | /** 153 | * @test 154 | * @expectedException RuntimeException 155 | */ 156 | public function shouldThrowExceptionOnAccessDenied() 157 | { 158 | $client = $this->getMock('Buzz\Client\Curl'); 159 | $client->expects($this->once()) 160 | ->method('send') 161 | ->will($this->returnCallback(function ($request, $response) { 162 | $response->addHeader('HTTP/1.1 401 Access Denied'); 163 | })); 164 | 165 | $this->getClient()->setClient($client); 166 | $this->getClient()->call('foo', array()); 167 | } 168 | 169 | /** 170 | * @test 171 | */ 172 | public function shouldHandle409ResponseWhenMakingAnApiCall() 173 | { 174 | $test = $this; 175 | $client = $this->getMock('Buzz\Client\Curl'); 176 | $client->expects($this->at(0)) 177 | ->method('send') 178 | ->will($this->returnCallback(function ($request, $response) use ($test) { 179 | $test->assertEmpty($request->getHeader('X-Transmission-Session-Id')); 180 | 181 | $response->addHeader('HTTP/1.1 409 Conflict'); 182 | $response->addHeader('X-Transmission-Session-Id: foo'); 183 | })); 184 | 185 | $client->expects($this->at(1)) 186 | ->method('send') 187 | ->will($this->returnCallback(function ($request, $response) { 188 | $response->addHeader('HTTP/1.1 200 OK'); 189 | $response->addHeader('Content-Type: application/json'); 190 | $response->addHeader('X-Transmission-Session-Id: foo'); 191 | $response->setContent('{"foo":"bar"}'); 192 | })); 193 | 194 | $this->getClient()->setClient($client); 195 | $response = $this->getClient()->call('foo', array()); 196 | 197 | $this->assertEquals('foo', $this->getClient()->getToken()); 198 | $this->assertInstanceOf('stdClass', $response); 199 | $this->assertObjectHasAttribute('foo', $response); 200 | $this->assertEquals('bar', $response->foo); 201 | } 202 | 203 | public function setup() 204 | { 205 | $this->client = new Client(); 206 | } 207 | 208 | private function getClient() 209 | { 210 | return $this->client; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/AbstractModelTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getModel()); 14 | } 15 | 16 | /** 17 | * @test 18 | */ 19 | public function shouldHaveEmptyMappingByDefault() 20 | { 21 | $this->assertEmpty($this->getModel()->getMapping()); 22 | } 23 | 24 | /** 25 | * @test 26 | */ 27 | public function shouldHaveNoClientByDefault() 28 | { 29 | $this->assertNull($this->getModel()->getClient()); 30 | } 31 | 32 | /** 33 | * @test 34 | */ 35 | public function shouldHaveClientIfSetByUser() 36 | { 37 | $client = $this->getMock('Transmission\Client'); 38 | 39 | $this->getModel()->setClient($client); 40 | $this->assertEquals($client, $this->getModel()->getClient()); 41 | } 42 | 43 | public function setup() 44 | { 45 | $this->model = $this->getMockForAbstractClass('Transmission\Model\AbstractModel'); 46 | } 47 | 48 | private function getModel() 49 | { 50 | return $this->model; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/FileTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getFile()); 18 | } 19 | 20 | /** 21 | * @test 22 | */ 23 | public function shouldHaveNonEmptyMapping() 24 | { 25 | $this->assertNotEmpty($this->getFile()->getMapping()); 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function shouldBeCreatedFromMapping() 32 | { 33 | $source = (object) array( 34 | 'name' => 'foo', 35 | 'length' => 100, 36 | 'bytesCompleted' => 10 37 | ); 38 | 39 | PropertyMapper::map($this->getFile(), $source); 40 | 41 | $this->assertEquals('foo', $this->getFile()->getName()); 42 | $this->assertEquals(100, $this->getFile()->getSize()); 43 | $this->assertEquals(10, $this->getFile()->getCompleted()); 44 | $this->assertFalse($this->getFile()->isDone()); 45 | } 46 | 47 | /** 48 | * @test 49 | */ 50 | public function shouldConvertToString() 51 | { 52 | $this->getFile()->setName('foo'); 53 | 54 | $this->assertInternalType('string', (string) $this->getFile()); 55 | $this->assertEquals('foo', (string) $this->getFile()); 56 | } 57 | 58 | public function setup() 59 | { 60 | $this->file = new File(); 61 | } 62 | 63 | private function getFile() 64 | { 65 | return $this->file; 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/PeerTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getPeer()); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function shouldHaveNonEmptyMapping() 23 | { 24 | $this->assertNotEmpty($this->getPeer()->getMapping()); 25 | } 26 | 27 | /** 28 | * @test 29 | */ 30 | public function shouldBeCreatedFromMapping() 31 | { 32 | $source = (object) array( 33 | 'address' => 'foo', 34 | 'clientName' => 'foo', 35 | 'clientIsChoked' => false, 36 | 'clientIsInterested' => true, 37 | 'flagStr' => 'foo', 38 | 'isDownloadingFrom' => false, 39 | 'isEncrypted' => true, 40 | 'isIncoming' => false, 41 | 'isUploadingTo' => true, 42 | 'isUTP' => false, 43 | 'peerIsChoked' => true, 44 | 'peerIsInterested' => false, 45 | 'port' => 3000, 46 | 'progress' => 10.5, 47 | 'rateToClient' => 1000, 48 | 'rateFromClient' => 10000 49 | ); 50 | 51 | PropertyMapper::map($this->getPeer(), $source); 52 | 53 | $this->assertEquals('foo', $this->getPeer()->getAddress()); 54 | $this->assertEquals('foo', $this->getPeer()->getClientName()); 55 | $this->assertFalse($this->getPeer()->isClientChoked()); 56 | $this->assertTrue($this->getPeer()->isClientInterested()); 57 | $this->assertFalse($this->getPeer()->isDownloading()); 58 | $this->assertTrue($this->getPeer()->isEncrypted()); 59 | $this->assertFalse($this->getPeer()->isIncoming()); 60 | $this->assertTrue($this->getPeer()->isUploading()); 61 | $this->assertFalse($this->getPeer()->isUtp()); 62 | $this->assertTrue($this->getPeer()->isPeerChoked()); 63 | $this->assertFalse($this->getPeer()->isPeerInterested()); 64 | $this->assertEquals(3000, $this->getPeer()->getPort()); 65 | $this->assertEquals(10.5, $this->getPeer()->getProgress()); 66 | $this->assertEquals(1000, $this->getPeer()->getUploadRate()); 67 | $this->assertEquals(10000, $this->getPeer()->getDownloadRate()); 68 | } 69 | 70 | public function setup() 71 | { 72 | $this->peer = new Peer(); 73 | } 74 | 75 | public function getPeer() 76 | { 77 | return $this->peer; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/SessionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getSession()); 18 | } 19 | 20 | /** 21 | * @test 22 | */ 23 | public function shouldHaveNonEmptyMapping() 24 | { 25 | $this->assertNotEmpty($this->getSession()->getMapping()); 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function shouldBeCreatedFromMapping() 32 | { 33 | $source = (object) array( 34 | 'alt-speed-down' => 1, 35 | 'alt-speed-enabled' => true, 36 | 'download-dir' => 'foo', 37 | 'download-queue-enabled' => true, 38 | 'download-queue-size' => 5, 39 | 'incomplete-dir' => 'bar', 40 | 'incomplete-dir-enabled' => true, 41 | 'script-torrent-done-filename' => 'baz', 42 | 'script-torrent-done-enabled' => true, 43 | 'seedRatioLimit' => 3.14, 44 | 'seedRatioLimited' => true, 45 | 'seed-queue-size' => 5, 46 | 'seed-queue-enabled' => true, 47 | 'speed-limit-down' => 100, 48 | 'speed-limit-down-enabled' => true, 49 | 'speed-limit-up' => 100, 50 | 'speed-limit-up-enabled' => true 51 | ); 52 | 53 | PropertyMapper::map($this->getSession(), $source); 54 | 55 | $this->assertEquals(1, $this->getSession()->getAltSpeedDown()); 56 | $this->assertTrue($this->getSession()->isAltSpeedEnabled()); 57 | $this->assertEquals('foo', $this->getSession()->getDownloadDir()); 58 | $this->assertEquals(5, $this->getSession()->getDownloadQueueSize()); 59 | $this->assertTrue($this->getSession()->isDownloadQueueEnabled()); 60 | $this->assertEquals('bar', $this->getSession()->getIncompleteDir()); 61 | $this->assertTrue($this->getSession()->isIncompleteDirEnabled()); 62 | $this->assertEquals('baz', $this->getSession()->getTorrentDoneScript()); 63 | $this->assertTrue($this->getSession()->isTorrentDoneScriptEnabled()); 64 | $this->assertEquals(3.14, $this->getSession()->getSeedRatioLimit()); 65 | $this->assertTrue($this->getSession()->isSeedRatioLimited()); 66 | $this->assertEquals(5, $this->getSession()->getSeedQueueSize()); 67 | $this->assertTrue($this->getSession()->isSeedQueueEnabled()); 68 | $this->assertEquals(100, $this->getSession()->getDownloadSpeedLimit()); 69 | $this->assertTrue($this->getSession()->isDownloadSpeedLimitEnabled()); 70 | $this->assertEquals(100, $this->getSession()->getUploadSpeedLimit()); 71 | $this->assertTrue($this->getSession()->isUploadSpeedLimitEnabled()); 72 | } 73 | 74 | /** 75 | * @test 76 | */ 77 | public function shouldSave() 78 | { 79 | $expected = array( 80 | 'alt-speed-down' => 1, 81 | 'alt-speed-enabled' => true, 82 | 'download-dir' => 'foo', 83 | 'download-queue-enabled' => true, 84 | 'download-queue-size' => 5, 85 | 'incomplete-dir' => 'bar', 86 | 'incomplete-dir-enabled' => true, 87 | 'script-torrent-done-filename' => 'baz', 88 | 'script-torrent-done-enabled' => true, 89 | 'seedRatioLimit' => 3.14, 90 | 'seedRatioLimited' => true, 91 | 'seed-queue-size' => 5, 92 | 'seed-queue-enabled' => true, 93 | 'speed-limit-down' => 100, 94 | 'speed-limit-down-enabled' => true, 95 | 'speed-limit-up' => 100, 96 | 'speed-limit-up-enabled' => true 97 | ); 98 | 99 | PropertyMapper::map($this->getSession(), (object) $expected); 100 | 101 | $client = $this->getMock('Transmission\Client'); 102 | $client->expects($this->once()) 103 | ->method('call') 104 | ->with('session-set', $expected) 105 | ->will($this->returnCallback(function () { 106 | return (object) array( 107 | 'result' => 'success', 108 | ); 109 | })); 110 | 111 | $this->getSession()->setClient($client); 112 | $this->getSession()->save(); 113 | } 114 | 115 | /** 116 | * @test 117 | */ 118 | public function shouldNotSaveWithNoClient() 119 | { 120 | $this->getSession()->save(); 121 | } 122 | 123 | public function setup() 124 | { 125 | $this->session = new Session(); 126 | } 127 | 128 | protected function getSession() 129 | { 130 | return $this->session; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/StatusTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($status->isStopped()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/TorrentTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getTorrent()); 18 | } 19 | 20 | /** 21 | * @test 22 | */ 23 | public function shouldHaveNonEmptyMapping() 24 | { 25 | $this->assertNotEmpty($this->getTorrent()->getMapping()); 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function shouldBeCreatedFromMapping() 32 | { 33 | $source = (object) array( 34 | 'id' => 1, 35 | 'eta' => 10, 36 | 'sizeWhenDone' => 10000, 37 | 'name' => 'foo', 38 | 'hashString' => 'bar', 39 | 'status' => 0, 40 | 'isFinished' => false, 41 | 'rateUpload' => 10, 42 | 'rateDownload' => 100, 43 | 'downloadDir' => '/home/foo', 44 | 'downloadedEver' => 1024000000, 45 | 'uploadedEver' => 1024000000000, // 1 Tb 46 | 'files' => array( 47 | (object) array() 48 | ), 49 | 'peers' => array( 50 | (object) array(), 51 | (object) array() 52 | ), 53 | 'peersConnected' => 10, 54 | 'startDate' => 1427583510, 55 | 'trackers' => array( 56 | (object) array(), 57 | (object) array(), 58 | (object) array() 59 | ), 60 | 'trackerStats' => array( 61 | (object) array(), 62 | (object) array(), 63 | (object) array() 64 | ) 65 | ); 66 | 67 | PropertyMapper::map($this->getTorrent(), $source); 68 | 69 | $this->assertEquals(1, $this->getTorrent()->getId()); 70 | $this->assertEquals(10, $this->getTorrent()->getEta()); 71 | $this->assertEquals(10000, $this->getTorrent()->getSize()); 72 | $this->assertEquals('foo', $this->getTorrent()->getName()); 73 | $this->assertEquals('bar', $this->getTorrent()->getHash()); 74 | $this->assertEquals(0, $this->getTorrent()->getStatus()); 75 | $this->assertFalse($this->getTorrent()->isFinished()); 76 | $this->assertEquals(10, $this->getTorrent()->getUploadRate()); 77 | $this->assertEquals(100, $this->getTorrent()->getDownloadRate()); 78 | $this->assertEquals('/home/foo', $this->getTorrent()->getDownloadDir()); 79 | $this->assertEquals(1024000000, $this->getTorrent()->getDownloadedEver()); 80 | $this->assertEquals(1024000000000, $this->getTorrent()->getUploadedEver()); 81 | $this->assertCount(1, $this->getTorrent()->getFiles()); 82 | $this->assertCount(2, $this->getTorrent()->getPeers()); 83 | $this->assertCount(3, $this->getTorrent()->getTrackers()); 84 | } 85 | 86 | /** 87 | * @test 88 | */ 89 | public function shouldBeDoneWhenFinishedFlagIsSet() 90 | { 91 | $this->getTorrent()->setFinished(true); 92 | 93 | $this->assertTrue($this->getTorrent()->isFinished()); 94 | } 95 | 96 | /** 97 | * @test 98 | */ 99 | public function shouldBeDoneWhenPercentDoneIs100Percent() 100 | { 101 | $this->getTorrent()->setPercentDone(1); 102 | 103 | $this->assertTrue($this->getTorrent()->isFinished()); 104 | } 105 | 106 | /** 107 | * @test 108 | * @dataProvider statusProvider 109 | */ 110 | public function shouldHaveConvenienceMethods($status, $method) 111 | { 112 | $methods = array('stopped', 'checking', 'downloading', 'seeding'); 113 | $accessor = PropertyAccess::createPropertyAccessor(); 114 | $this->getTorrent()->setStatus($status); 115 | 116 | $methods = array_filter($methods, function ($value) use ($method) { 117 | return $method !== $value; 118 | }); 119 | 120 | $this->assertTrue($accessor->getValue($this->getTorrent(), $method)); 121 | foreach ($methods as $m) { 122 | $this->assertFalse($accessor->getValue($this->getTorrent(), $m), $m); 123 | } 124 | } 125 | 126 | public function statusProvider() 127 | { 128 | return array( 129 | array(0, 'stopped'), 130 | array(1, 'checking'), 131 | array(2, 'checking'), 132 | array(3, 'downloading'), 133 | array(4, 'downloading'), 134 | array(5, 'seeding'), 135 | array(6, 'seeding') 136 | ); 137 | } 138 | 139 | public function setup() 140 | { 141 | $this->torrent = new Torrent(); 142 | } 143 | 144 | public function getTorrent() 145 | { 146 | return $this->torrent; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/TrackerStatsTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getTrackerStats()); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function shouldHaveNonEmptyMapping() 23 | { 24 | $this->assertNotEmpty($this->getTrackerStats()->getMapping()); 25 | } 26 | 27 | /** 28 | * @test 29 | */ 30 | public function shouldBeCreatedFromMapping() 31 | { 32 | $source = (object) array( 33 | 'host' => 'test', 34 | 'leecherCount' => 1, 35 | 'seederCount' => 2, 36 | 'lastScrapeResult' => 'foo', 37 | 'lastAnnounceResult' => 'bar' 38 | ); 39 | 40 | PropertyMapper::map($this->getTrackerStats(), $source); 41 | 42 | $this->assertEquals('test', $this->getTrackerStats()->getHost()); 43 | $this->assertEquals(1, $this->getTrackerStats()->getLeecherCount()); 44 | $this->assertEquals(2, $this->getTrackerStats()->getSeederCount()); 45 | $this->assertEquals('foo', $this->getTrackerStats()->getLastScrapeResult()); 46 | $this->assertEquals('bar', $this->getTrackerStats()->getLastAnnounceResult()); 47 | } 48 | 49 | public function setup() 50 | { 51 | $this->trackerStats = new TrackerStats(); 52 | } 53 | 54 | private function getTrackerStats() 55 | { 56 | return $this->trackerStats; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Model/TrackerTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Transmission\Model\ModelInterface', $this->getTracker()); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function shouldHaveNonEmptyMapping() 23 | { 24 | $this->assertNotEmpty($this->getTracker()->getMapping()); 25 | } 26 | 27 | /** 28 | * @test 29 | */ 30 | public function shouldBeCreatedFromMapping() 31 | { 32 | $source = (object) array( 33 | 'id' => 1, 34 | 'tier' => 1, 35 | 'scrape' => 'foo', 36 | 'announce' => 'bar' 37 | ); 38 | 39 | PropertyMapper::map($this->getTracker(), $source); 40 | 41 | $this->assertEquals(1, $this->getTracker()->getId()); 42 | $this->assertEquals(1, $this->getTracker()->getTier()); 43 | $this->assertEquals('foo', $this->getTracker()->getScrape()); 44 | $this->assertEquals('bar', $this->getTracker()->getAnnounce()); 45 | } 46 | 47 | public function setup() 48 | { 49 | $this->tracker = new Tracker(); 50 | } 51 | 52 | private function getTracker() 53 | { 54 | return $this->tracker; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/TransmissionTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('localhost', $this->getTransmission()->getClient()->getHost()); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function shouldGetAllTorrentsInDownloadQueue() 23 | { 24 | $client = $this->getMock('Transmission\Client'); 25 | $client->expects($this->once()) 26 | ->method('call') 27 | ->with('torrent-get') 28 | ->will($this->returnCallback(function ($method, $arguments) { 29 | return (object) array( 30 | 'result' => 'success', 31 | 'arguments' => (object) array( 32 | 'torrents' => array( 33 | (object) array(), 34 | (object) array(), 35 | (object) array(), 36 | ) 37 | ) 38 | ); 39 | })); 40 | 41 | $this->getTransmission()->setClient($client); 42 | 43 | $torrents = $this->getTransmission()->all(); 44 | 45 | $this->assertCount(3, $torrents); 46 | } 47 | 48 | /** 49 | * @test 50 | */ 51 | public function shouldGetTorrentById() 52 | { 53 | $that = $this; 54 | $client = $this->getMock('Transmission\Client'); 55 | $client->expects($this->once()) 56 | ->method('call') 57 | ->with('torrent-get') 58 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 59 | $that->assertEquals(1, $arguments['ids'][0]); 60 | 61 | return (object) array( 62 | 'result' => 'success', 63 | 'arguments' => (object) array( 64 | 'torrents' => array( 65 | (object) array() 66 | ) 67 | ) 68 | ); 69 | })); 70 | 71 | $this->getTransmission()->setClient($client); 72 | 73 | $torrent = $this->getTransmission()->get(1); 74 | 75 | $this->assertInstanceOf('Transmission\Model\Torrent', $torrent); 76 | } 77 | 78 | /** 79 | * @test 80 | * @expectedException RuntimeException 81 | */ 82 | public function shouldThrowExceptionWhenTorrentIsNotFound() 83 | { 84 | $client = $this->getMock('Transmission\Client'); 85 | $client->expects($this->once()) 86 | ->method('call') 87 | ->with('torrent-get') 88 | ->will($this->returnCallback(function ($method, $arguments) { 89 | return (object) array( 90 | 'result' => 'success', 91 | 'arguments' => (object) array( 92 | 'torrents' => array() 93 | ) 94 | ); 95 | })); 96 | 97 | $this->getTransmission()->setClient($client); 98 | $this->getTransmission()->get(1); 99 | } 100 | 101 | /** 102 | * @test 103 | */ 104 | public function shouldAddTorrentByFilename() 105 | { 106 | $that = $this; 107 | $client = $this->getMock('Transmission\Client'); 108 | $client->expects($this->once()) 109 | ->method('call') 110 | ->with('torrent-add') 111 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 112 | $that->assertArrayHasKey('filename', $arguments); 113 | 114 | return (object) array( 115 | 'result' => 'success', 116 | 'arguments' => (object) array( 117 | 'torrent-added' => (object) array() 118 | ) 119 | ); 120 | })); 121 | 122 | $this->getTransmission()->setClient($client); 123 | 124 | $torrent = $this->getTransmission()->add('foo'); 125 | $this->assertInstanceOf('Transmission\Model\Torrent', $torrent); 126 | } 127 | 128 | /** 129 | * @test 130 | */ 131 | public function shouldAddTorrentByMetainfo() 132 | { 133 | $that = $this; 134 | $client = $this->getMock('Transmission\Client'); 135 | $client->expects($this->once()) 136 | ->method('call') 137 | ->with('torrent-add') 138 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 139 | $that->assertArrayHasKey('metainfo', $arguments); 140 | 141 | return (object) array( 142 | 'result' => 'success', 143 | 'arguments' => (object) array( 144 | 'torrent-added' => (object) array() 145 | ) 146 | ); 147 | })); 148 | 149 | $this->getTransmission()->setClient($client); 150 | 151 | $torrent = $this->getTransmission()->add('foo', true); 152 | $this->assertInstanceOf('Transmission\Model\Torrent', $torrent); 153 | } 154 | 155 | /** 156 | * @test 157 | */ 158 | public function shouldHandleDuplicateTorrent() 159 | { 160 | $that = $this; 161 | $client = $this->getMock('Transmission\Client'); 162 | $client->expects($this->once()) 163 | ->method('call') 164 | ->with('torrent-add') 165 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 166 | $that->assertArrayHasKey('metainfo', $arguments); 167 | 168 | return (object) array( 169 | 'result' => 'duplicate torrent', 170 | 'arguments' => (object) array( 171 | 'torrent-duplicate' => (object) array() 172 | ) 173 | ); 174 | })); 175 | 176 | $this->getTransmission()->setClient($client); 177 | 178 | $torrent = $this->getTransmission()->add('foo', true); 179 | $this->assertInstanceOf('Transmission\Model\Torrent', $torrent); 180 | } 181 | 182 | /** 183 | * @test 184 | */ 185 | public function shouldGetSession() 186 | { 187 | $that = $this; 188 | $client = $this->getMock('Transmission\Client'); 189 | $client->expects($this->once()) 190 | ->method('call') 191 | ->with('session-get') 192 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 193 | $that->assertEmpty($arguments); 194 | 195 | return (object) array( 196 | 'result' => 'success', 197 | 'arguments' => (object) array() 198 | ); 199 | })); 200 | 201 | $this->getTransmission()->setClient($client); 202 | $session = $this->getTransmission()->getSession(); 203 | 204 | $this->assertInstanceOf('Transmission\Model\Session', $session); 205 | } 206 | 207 | /** 208 | * @test 209 | */ 210 | public function shouldGetSessionStats() 211 | { 212 | $that = $this; 213 | $client = $this->getMock('Transmission\Client'); 214 | $client->expects($this->once()) 215 | ->method('call') 216 | ->with('session-stats') 217 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 218 | $that->assertEmpty($arguments); 219 | 220 | return (object) array( 221 | 'result' => 'success', 222 | 'arguments' => (object) array() 223 | ); 224 | })); 225 | 226 | $this->getTransmission()->setClient($client); 227 | $stats = $this->getTransmission()->getSessionStats(); 228 | 229 | $this->assertInstanceOf('Transmission\Model\Stats\Session', $stats); 230 | } 231 | 232 | /** 233 | * @test 234 | */ 235 | public function shouldGetFreeSpace() 236 | { 237 | $that = $this; 238 | $client = $this->getMock('Transmission\Client'); 239 | $client->expects($this->once()) 240 | ->method('call') 241 | ->with('free-space') 242 | ->will($this->returnCallback(function ($method, $arguments) use ($that) { 243 | $that->assertArrayHasKey('path', $arguments); 244 | 245 | return (object) array( 246 | 'result' => 'success', 247 | 'arguments' => (object) array() 248 | ); 249 | })); 250 | 251 | $this->getTransmission()->setClient($client); 252 | $freeSpace = $this->getTransmission()->getFreeSpace('/'); 253 | $this->assertInstanceOf('Transmission\Model\FreeSpace', $freeSpace); 254 | } 255 | 256 | /** 257 | * @test 258 | */ 259 | public function shouldStartDownload() 260 | { 261 | $client = $this->getMock('Transmission\Client'); 262 | $client->expects($this->once()) 263 | ->method('call') 264 | ->with('torrent-start', array('ids' => array(1))) 265 | ->will($this->returnCallback(function () { 266 | return (object) array( 267 | 'result' => 'success' 268 | ); 269 | })); 270 | 271 | $torrent = new Torrent(); 272 | $torrent->setId(1); 273 | 274 | $transmission = new Transmission(); 275 | $transmission->setClient($client); 276 | $transmission->start($torrent); 277 | } 278 | 279 | /** 280 | * @test 281 | */ 282 | public function shouldStartDownloadImmediately() 283 | { 284 | $client = $this->getMock('Transmission\Client'); 285 | $client->expects($this->once()) 286 | ->method('call') 287 | ->with('torrent-start-now', array('ids' => array(1))) 288 | ->will($this->returnCallback(function () { 289 | return (object) array( 290 | 'result' => 'success' 291 | ); 292 | })); 293 | 294 | $torrent = new Torrent(); 295 | $torrent->setId(1); 296 | 297 | $transmission = new Transmission(); 298 | $transmission->setClient($client); 299 | $transmission->start($torrent, true); 300 | } 301 | 302 | /** 303 | * @test 304 | */ 305 | public function shouldStopDownload() 306 | { 307 | $client = $this->getMock('Transmission\Client'); 308 | $client->expects($this->once()) 309 | ->method('call') 310 | ->with('torrent-stop', array('ids' => array(1))) 311 | ->will($this->returnCallback(function () { 312 | return (object) array( 313 | 'result' => 'success' 314 | ); 315 | })); 316 | 317 | $torrent = new Torrent(); 318 | $torrent->setId(1); 319 | 320 | $transmission = new Transmission(); 321 | $transmission->setClient($client); 322 | $transmission->stop($torrent); 323 | } 324 | 325 | /** 326 | * @test 327 | */ 328 | public function shouldVerifyDownload() 329 | { 330 | $client = $this->getMock('Transmission\Client'); 331 | $client->expects($this->once()) 332 | ->method('call') 333 | ->with('torrent-verify', array('ids' => array(1))) 334 | ->will($this->returnCallback(function () { 335 | return (object) array( 336 | 'result' => 'success' 337 | ); 338 | })); 339 | 340 | $torrent = new Torrent(); 341 | $torrent->setId(1); 342 | 343 | $transmission = new Transmission(); 344 | $transmission->setClient($client); 345 | $transmission->verify($torrent); 346 | } 347 | 348 | /** 349 | * @test 350 | */ 351 | public function shouldReannounceDownload() 352 | { 353 | $client = $this->getMock('Transmission\Client'); 354 | $client->expects($this->once()) 355 | ->method('call') 356 | ->with('torrent-reannounce', array('ids' => array(1))) 357 | ->will($this->returnCallback(function () { 358 | return (object) array( 359 | 'result' => 'success' 360 | ); 361 | })); 362 | 363 | $torrent = new Torrent(); 364 | $torrent->setId(1); 365 | 366 | $transmission = new Transmission(); 367 | $transmission->setClient($client); 368 | $transmission->reannounce($torrent); 369 | } 370 | 371 | /** 372 | * @test 373 | */ 374 | public function shouldRemoveDownloadWithoutRemovingLocalData() 375 | { 376 | $client = $this->getMock('Transmission\Client'); 377 | $client->expects($this->once()) 378 | ->method('call') 379 | ->with('torrent-remove', array('ids' => array(1))) 380 | ->will($this->returnCallback(function () { 381 | return (object) array( 382 | 'result' => 'success' 383 | ); 384 | })); 385 | 386 | $torrent = new Torrent(); 387 | $torrent->setId(1); 388 | 389 | $transmission = new Transmission(); 390 | $transmission->setClient($client); 391 | $transmission->remove($torrent); 392 | } 393 | 394 | /** 395 | * @test 396 | */ 397 | public function shouldRemoveDownloadWithRemovingLocalData() 398 | { 399 | $client = $this->getMock('Transmission\Client'); 400 | $client->expects($this->once()) 401 | ->method('call') 402 | ->with('torrent-remove', array('ids' => array(1), 'delete-local-data' => true)) 403 | ->will($this->returnCallback(function () { 404 | return (object) array( 405 | 'result' => 'success' 406 | ); 407 | })); 408 | 409 | $torrent = new Torrent(); 410 | $torrent->setId(1); 411 | 412 | $transmission = new Transmission(); 413 | $transmission->setClient($client); 414 | $transmission->remove($torrent, true); 415 | } 416 | 417 | /** 418 | * @test 419 | */ 420 | public function shouldHaveDefaultPort() 421 | { 422 | $this->assertEquals(9091, $this->getTransmission()->getClient()->getPort()); 423 | } 424 | 425 | /** 426 | * @test 427 | */ 428 | public function shouldProvideFacadeForClient() 429 | { 430 | $client = $this->getMock('Transmission\Client'); 431 | $client->expects($this->once()) 432 | ->method('setHost') 433 | ->with('example.org'); 434 | 435 | $client->expects($this->once()) 436 | ->method('getHost') 437 | ->will($this->returnValue('example.org')); 438 | 439 | $client->expects($this->once()) 440 | ->method('setPort') 441 | ->with(80); 442 | 443 | $client->expects($this->once()) 444 | ->method('getPort') 445 | ->will($this->returnValue(80)); 446 | 447 | $this->getTransmission()->setClient($client); 448 | $this->getTransmission()->setHost('example.org'); 449 | $this->getTransmission()->setPort(80); 450 | 451 | $this->assertEquals('example.org', $this->getTransmission()->getHost()); 452 | $this->assertEquals(80, $this->getTransmission()->getPort()); 453 | } 454 | 455 | public function setup() 456 | { 457 | $this->transmission = new Transmission(); 458 | } 459 | 460 | private function getTransmission() 461 | { 462 | return $this->transmission; 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Util/PropertyMapperTest.php: -------------------------------------------------------------------------------- 1 | 'this', 17 | 'bar' => 'that', 18 | 'ba' => 'thus', 19 | 'unused' => false 20 | ); 21 | 22 | $model = new \Transmission\Mock\Model(); 23 | 24 | $this->getMapper()->map($model, $source); 25 | 26 | $this->assertEquals('this', $model->getFo()); 27 | $this->assertEquals('that', $model->getBar()); 28 | $this->assertNull($model->getUnused()); 29 | } 30 | 31 | public function setup() 32 | { 33 | $this->mapper = new PropertyMapper(); 34 | } 35 | 36 | private function getMapper() 37 | { 38 | return $this->mapper; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Transmission/Tests/Util/ResponseValidatorTest.php: -------------------------------------------------------------------------------- 1 | getValidator()->validate('', $response); 19 | } 20 | 21 | /** 22 | * @test 23 | * @expectedException RuntimeException 24 | */ 25 | public function shouldThrowExceptionOnErrorResultField() 26 | { 27 | $response = (object) array('result' => 'error'); 28 | 29 | $this->getValidator()->validate('', $response); 30 | } 31 | 32 | /** 33 | * @test 34 | */ 35 | public function shouldThrowNoExceptionOnValidTorrentGetResponse() 36 | { 37 | $response = (object) array( 38 | 'result' => 'success', 39 | 'arguments' => (object) array( 40 | 'torrents' => array( 41 | (object) array('foo' => 'bar') 42 | ) 43 | ) 44 | ); 45 | 46 | $expected = array((object) array('foo' => 'bar')); 47 | $container = $this->getValidator()->validate('torrent-get', $response); 48 | $this->assertEquals($expected, $container); 49 | } 50 | 51 | /** 52 | * @test 53 | * @expectedException RuntimeException 54 | */ 55 | public function shouldThrowExceptionOnMissingArgumentsInTorrentGetResponse() 56 | { 57 | $response = (object) array('result' => 'success'); 58 | 59 | $this->getValidator()->validate('torrent-get', $response); 60 | } 61 | 62 | /** 63 | * @test 64 | * @expectedException RuntimeException 65 | */ 66 | public function shouldThrowExceptionOnMissingTorrentArgumentInTorrentGetResponse() 67 | { 68 | $response = (object) array('result' => 'success', 'arguments' => (object) array()); 69 | 70 | $this->getValidator()->validate('torrent-get', $response); 71 | } 72 | 73 | /** 74 | * @test 75 | */ 76 | public function shouldThrowNoExceptionOnValidTorrentAddResponse() 77 | { 78 | $response = (object) array( 79 | 'result' => 'success', 80 | 'arguments' => (object) array( 81 | 'torrent-added' => (object) array( 82 | 'foo' => 'bar' 83 | ) 84 | ) 85 | ); 86 | 87 | $expected = (object) array('foo' => 'bar'); 88 | $container = $this->getValidator()->validate('torrent-add', $response); 89 | $this->assertEquals($expected, $container); 90 | } 91 | 92 | /** 93 | * @test 94 | */ 95 | public function shouldThrowNoExceptionOnValidSessionGetResponse() 96 | { 97 | $response = (object) array( 98 | 'result' => 'success', 99 | 'arguments' => (object) array( 100 | 'foo' => 'bar' 101 | ) 102 | ); 103 | 104 | $expected = (object) array('foo' => 'bar'); 105 | $container = $this->getValidator()->validate('session-get', $response); 106 | $this->assertEquals($expected, $container); 107 | } 108 | 109 | /** 110 | * @test 111 | * @expectedException RuntimeException 112 | */ 113 | public function shouldThrowExceptionOnMissingArgumentsInSessionGetResponse() 114 | { 115 | $response = (object) array('result' => 'success'); 116 | 117 | $this->getValidator()->validate('session-get', $response); 118 | } 119 | 120 | /** 121 | * @test 122 | * @expectedException RuntimeException 123 | */ 124 | public function shouldThrowExceptionOnMissingArgumentsSessionGetResponse() 125 | { 126 | $response = (object) array('result' => 'success'); 127 | 128 | $this->getValidator()->validate('session-get', $response); 129 | } 130 | 131 | /** 132 | * @test 133 | * @expectedException RuntimeException 134 | */ 135 | public function shouldThrowExceptionOnMissingArgumentsInTorrentAddResponse() 136 | { 137 | $response = (object) array('result' => 'success'); 138 | 139 | $this->getValidator()->validate('torrent-add', $response); 140 | } 141 | 142 | /** 143 | * @test 144 | * @expectedException RuntimeException 145 | */ 146 | public function shouldThrowExceptionOnMissingTorrentFieldArgumentInTorrentAddResponse() 147 | { 148 | $response = (object) array('result' => 'success', 'arguments' => (object) array()); 149 | 150 | $this->getValidator()->validate('torrent-add', $response); 151 | } 152 | 153 | /** 154 | * @test 155 | * @expectedException RuntimeException 156 | */ 157 | public function shouldThrowExceptionOnEmptyTorrentFieldInTorrentAddResponse() 158 | { 159 | $response = (object) array('result' => 'success', 'arguments' => (object) array('torrent-added' => array())); 160 | 161 | $this->getValidator()->validate('torrent-add', $response); 162 | } 163 | 164 | /** 165 | * @test 166 | */ 167 | public function shouldThrowNoExceptionOnValidOtherResponses() 168 | { 169 | $response = (object) array('result' => 'success'); 170 | 171 | $container = $this->getValidator()->validate('torrent-remove', $response); 172 | $this->assertNull($container); 173 | } 174 | 175 | public function setup() 176 | { 177 | $this->validator = new ResponseValidator(); 178 | } 179 | 180 | private function getValidator() 181 | { 182 | return $this->validator; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('Transmission\Mock', __DIR__); 15 | $loader->add('Transmission\Tests', __DIR__); 16 | --------------------------------------------------------------------------------