1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Transmission; |
4
|
|
|
|
5
|
|
|
use Http\Client\Common\HttpMethodsClient; |
6
|
|
|
use Http\Client\Common\Plugin\AuthenticationPlugin; |
7
|
|
|
use Http\Client\Common\Plugin\HeaderDefaultsPlugin; |
8
|
|
|
use Http\Client\Common\Plugin\HistoryPlugin; |
9
|
|
|
use Http\Client\HttpClient; |
10
|
|
|
use Http\Message\Authentication\BasicAuth; |
11
|
|
|
use Illuminate\Support\Collection; |
12
|
|
|
use Psr\Http\Message\ResponseInterface; |
13
|
|
|
use Transmission\Exception\InvalidArgumentException; |
14
|
|
|
use Transmission\Exception\TransmissionException; |
15
|
|
|
use Transmission\HttpClient\Builder; |
16
|
|
|
use Transmission\HttpClient\Message\ParamBuilder; |
17
|
|
|
use Transmission\HttpClient\Message\ResponseMediator; |
18
|
|
|
use Transmission\HttpClient\Plugin\AuthSession; |
19
|
|
|
use Transmission\HttpClient\Plugin\ExceptionThrower; |
20
|
|
|
use Transmission\HttpClient\Plugin\History; |
21
|
|
|
use Transmission\Models\Torrent; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Transmission-RPC API SDK Client |
25
|
|
|
*/ |
26
|
|
|
class Client |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* SDK Version |
30
|
|
|
* |
31
|
|
|
* @var string |
32
|
|
|
*/ |
33
|
|
|
const VERSION = '1.1.0'; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Transmission-RPC Hostname |
37
|
|
|
* |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
public $hostname; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Transmission-RPC Port |
44
|
|
|
* |
45
|
|
|
* @var string |
46
|
|
|
*/ |
47
|
|
|
public $port; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Transmission-RPC Path |
51
|
|
|
* |
52
|
|
|
* @var string |
53
|
|
|
*/ |
54
|
|
|
public $path = '/transmission/rpc'; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var History |
58
|
|
|
*/ |
59
|
|
|
private $responseHistory; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var Builder |
63
|
|
|
*/ |
64
|
|
|
private $httpClientBuilder; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Instantiate a new Transmission Client. |
68
|
|
|
* |
69
|
|
|
* @param null|string $hostname |
70
|
|
|
* @param null|int $port |
71
|
|
|
* @param null|string $username |
72
|
|
|
* @param null|string $password |
73
|
|
|
* @param Builder|null $httpClientBuilder |
74
|
|
|
*/ |
75
|
|
|
public function __construct( |
76
|
|
|
string $hostname = null, |
77
|
|
|
int $port = null, |
78
|
|
|
string $username = null, |
79
|
|
|
string $password = null, |
80
|
|
|
Builder $httpClientBuilder = null |
81
|
|
|
) { |
82
|
|
|
$this->hostname = $hostname ?? env('TRANSMISSION_HOSTNAME', '127.0.0.1'); |
|
|
|
|
83
|
|
|
$this->port = $port ?? env('TRANSMISSION_PORT', 9091); |
|
|
|
|
84
|
|
|
|
85
|
|
|
$this->responseHistory = new History(); |
86
|
|
|
$this->httpClientBuilder = $httpClientBuilder ?? new Builder(); |
87
|
|
|
$this->httpClientBuilder->addPlugin(new ExceptionThrower()); |
88
|
|
|
$this->httpClientBuilder->addPlugin(new HistoryPlugin($this->responseHistory)); |
89
|
|
|
$this->httpClientBuilder->addPlugin(new HeaderDefaultsPlugin([ |
90
|
|
|
'User-Agent' => $this->defaultUserAgent(), |
91
|
|
|
])); |
92
|
|
|
|
93
|
|
|
$username = $username ?? env('TRANSMISSION_USERNAME'); |
94
|
|
|
$password = $password ?? env('TRANSMISSION_PASSWORD', ''); |
95
|
|
|
|
96
|
|
|
if (filled($username)) { |
97
|
|
|
$this->authenticate($username, $password); |
|
|
|
|
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Create a Transmission\Client. |
103
|
|
|
* |
104
|
|
|
* @param null|string $hostname |
105
|
|
|
* @param null|int $port |
106
|
|
|
* @param null|string $username |
107
|
|
|
* @param null|string $password |
108
|
|
|
* |
109
|
|
|
* @return Client |
110
|
|
|
*/ |
111
|
|
|
public static function create( |
112
|
|
|
string $hostname = null, |
113
|
|
|
int $port = null, |
114
|
|
|
string $username = null, |
115
|
|
|
string $password = null |
116
|
|
|
): self { |
117
|
|
|
$client = new static($hostname, $port, $username, $password); |
118
|
|
|
|
119
|
|
|
return $client; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Create a Transmission\Client using an HttpClient. |
124
|
|
|
* |
125
|
|
|
* @param HttpClient $httpClient |
126
|
|
|
* @param null|string $hostname |
127
|
|
|
* @param null|int $port |
128
|
|
|
* @param null|string $username |
129
|
|
|
* @param null|string $password |
130
|
|
|
* |
131
|
|
|
* @return Client |
132
|
|
|
*/ |
133
|
|
|
public static function createWithHttpClient( |
134
|
|
|
HttpClient $httpClient, |
135
|
|
|
string $hostname = null, |
136
|
|
|
int $port = null, |
137
|
|
|
string $username = null, |
138
|
|
|
string $password = null |
139
|
|
|
): self { |
140
|
|
|
$builder = new Builder($httpClient); |
141
|
|
|
|
142
|
|
|
return new static($hostname, $port, $username, $password, $builder); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Get Client Instance. |
147
|
|
|
* |
148
|
|
|
* @return Client |
149
|
|
|
*/ |
150
|
|
|
public function instance(): self |
151
|
|
|
{ |
152
|
|
|
return $this; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Authenticate the user for all next requests |
157
|
|
|
* |
158
|
|
|
* @param string $username |
159
|
|
|
* @param null|string $password |
160
|
|
|
* |
161
|
|
|
* @return Client |
162
|
|
|
*/ |
163
|
|
|
public function authenticate(string $username, string $password = ''): self |
164
|
|
|
{ |
165
|
|
|
$authentication = new BasicAuth($username, $password); |
166
|
|
|
|
167
|
|
|
$this->httpClientBuilder->removePlugin(AuthenticationPlugin::class); |
168
|
|
|
$this->httpClientBuilder->addPlugin(new AuthenticationPlugin($authentication)); |
169
|
|
|
|
170
|
|
|
return $this; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Set Session ID. |
175
|
|
|
* |
176
|
|
|
* @param string $sessionId |
177
|
|
|
* |
178
|
|
|
* @return Client |
179
|
|
|
*/ |
180
|
|
|
public function setSessionId(string $sessionId): self |
181
|
|
|
{ |
182
|
|
|
$this->httpClientBuilder->removePlugin(AuthSession::class); |
183
|
|
|
$this->httpClientBuilder->addPlugin(new AuthSession($sessionId)); |
184
|
|
|
|
185
|
|
|
return $this; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Start one or more torrents. |
190
|
|
|
* |
191
|
|
|
* @see https://git.io/transmission-rpc-specs Torrent Action Requests. |
192
|
|
|
* |
193
|
|
|
* @param mixed $ids One or more torrent ids, sha1 hash strings, or both OR "recently-active", for recently-active |
194
|
|
|
* torrents. All torrents are used if no value is given. |
195
|
|
|
* |
196
|
|
|
* @return bool |
197
|
|
|
*/ |
198
|
|
|
public function start($ids = null): bool |
199
|
|
|
{ |
200
|
|
|
$this->api('torrent-start', compact('ids')); |
201
|
|
|
|
202
|
|
|
return true; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Start Now one or more torrents. |
207
|
|
|
* |
208
|
|
|
* @see https://git.io/transmission-rpc-specs Torrent Action Requests. |
209
|
|
|
* |
210
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
211
|
|
|
* |
212
|
|
|
* @return bool |
213
|
|
|
*/ |
214
|
|
|
public function startNow($ids = null): bool |
215
|
|
|
{ |
216
|
|
|
$this->api('torrent-start-now', compact('ids')); |
217
|
|
|
|
218
|
|
|
return true; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Stop one or more torrents. |
223
|
|
|
* |
224
|
|
|
* @see https://git.io/transmission-rpc-specs Torrent Action Requests. |
225
|
|
|
* |
226
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
227
|
|
|
* |
228
|
|
|
* @return bool |
229
|
|
|
*/ |
230
|
|
|
public function stop($ids = null): bool |
231
|
|
|
{ |
232
|
|
|
$this->api('torrent-stop', compact('ids')); |
233
|
|
|
|
234
|
|
|
return true; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Verify one or more torrents. |
239
|
|
|
* |
240
|
|
|
* @see https://git.io/transmission-rpc-specs Torrent Action Requests. |
241
|
|
|
* |
242
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
243
|
|
|
* |
244
|
|
|
* @return bool |
245
|
|
|
*/ |
246
|
|
|
public function verify($ids = null): bool |
247
|
|
|
{ |
248
|
|
|
$this->api('torrent-verify', compact('ids')); |
249
|
|
|
|
250
|
|
|
return true; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Reannounce one or more torrents. |
255
|
|
|
* |
256
|
|
|
* @see https://git.io/transmission-rpc-specs Torrent Action Requests. |
257
|
|
|
* |
258
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
259
|
|
|
* |
260
|
|
|
* @return bool |
261
|
|
|
*/ |
262
|
|
|
public function reannounce($ids = null): bool |
263
|
|
|
{ |
264
|
|
|
$this->api('torrent-reannounce', compact('ids')); |
265
|
|
|
|
266
|
|
|
return true; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Set properties of one or more torrents. |
271
|
|
|
* |
272
|
|
|
* @see https://git.io/transmission-rpc-specs "torrent-set" for available arguments. |
273
|
|
|
* |
274
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
275
|
|
|
* @param array $arguments An associative array of arguments to set. |
276
|
|
|
* |
277
|
|
|
* @return bool |
278
|
|
|
*/ |
279
|
|
|
public function set($ids, array $arguments): bool |
280
|
|
|
{ |
281
|
|
|
$arguments['ids'] = $ids; |
282
|
|
|
$this->api('torrent-set', $arguments); |
283
|
|
|
|
284
|
|
|
return true; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Get information on torrents, if the ids parameter is |
289
|
|
|
* null all torrents will be returned. |
290
|
|
|
* |
291
|
|
|
* @see https://git.io/transmission-rpc-specs "torrent-get" for available fields. |
292
|
|
|
* |
293
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
294
|
|
|
* @param array $fields An array of return fields, no value will fallback to default fields. |
295
|
|
|
* |
296
|
|
|
* @return Collection |
297
|
|
|
*/ |
298
|
|
|
public function get($ids = null, array $fields = null): Collection |
299
|
|
|
{ |
300
|
|
|
$fields = $fields ?? Torrent::$fields['default']; |
301
|
|
|
$data = $this->api('torrent-get', compact('ids', 'fields')); |
302
|
|
|
|
303
|
|
|
$torrentsInfo = data_get($data, 'arguments.torrents', 0); |
304
|
|
|
|
305
|
|
|
if (blank($torrentsInfo)) { |
306
|
|
|
return collect(); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
$torrents = collect($torrentsInfo)->mapInto(Torrent::class); |
310
|
|
|
|
311
|
|
|
return $torrents->count() > 1 ? $torrents : $torrents->first(); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Add a torrent to the download queue |
316
|
|
|
* |
317
|
|
|
* @see https://git.io/transmission-rpc-specs "torrent-add" for available arguments. |
318
|
|
|
* |
319
|
|
|
* @param string $torrent Filename/URL of the .torrent file OR Magnet URI/.torrent content. |
320
|
|
|
* @param boolean $metainfo Is given torrent a metainfo? (default: false). |
321
|
|
|
* @param string $savepath Path to download the torrent to. |
322
|
|
|
* @param array $arguments Other optional arguments. |
323
|
|
|
* |
324
|
|
|
* @return Collection |
325
|
|
|
*/ |
326
|
|
|
public function add( |
327
|
|
|
string $torrent, |
328
|
|
|
bool $metainfo = false, |
329
|
|
|
string $savepath = null, |
330
|
|
|
array $arguments = [] |
331
|
|
|
): Collection { |
332
|
|
|
$arguments[$metainfo ? 'metainfo' : 'filename'] = $metainfo ? base64_encode($torrent) : $torrent; |
333
|
|
|
|
334
|
|
|
if ($savepath !== null) { |
335
|
|
|
$arguments['download-dir'] = (string)$savepath; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
$data = $this->api('torrent-add', $arguments); |
339
|
|
|
|
340
|
|
|
if (array_key_exists('torrent-duplicate', $data['arguments'])) { |
341
|
|
|
return $this->get($data['arguments']['torrent-duplicate']['id']); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
if (!array_key_exists('torrent-added', $data['arguments'])) { |
345
|
|
|
throw new InvalidArgumentException($data['result']); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
return collect($data['arguments']['torrent-added']); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Remove one or more torrents. |
353
|
|
|
* |
354
|
|
|
* @see https://git.io/transmission-rpc-specs "torrent-remove" for available arguments. |
355
|
|
|
* |
356
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
357
|
|
|
* @param bool $deleteLocalData Also remove local data? (default: false). |
358
|
|
|
* |
359
|
|
|
* @return bool |
360
|
|
|
*/ |
361
|
|
|
public function remove($ids, bool $deleteLocalData = false): bool |
362
|
|
|
{ |
363
|
|
|
$arguments = ['ids' => $ids, 'delete-local-data' => $deleteLocalData]; |
364
|
|
|
$this->api('torrent-remove', $arguments); |
365
|
|
|
|
366
|
|
|
return true; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Move one or more torrents to new location. |
371
|
|
|
* |
372
|
|
|
* @see https://git.io/transmission-rpc-specs "torrent-set-location" for available arguments. |
373
|
|
|
* |
374
|
|
|
* @param mixed $ids One or more torrent ids, as described in 3.1 of specs. |
375
|
|
|
* @param string $location The new torrent location. |
376
|
|
|
* @param bool $move Move from previous location or search "location" for files (default: true). |
377
|
|
|
* |
378
|
|
|
* @return bool |
379
|
|
|
*/ |
380
|
|
|
public function move($ids, string $location, bool $move = true): bool |
381
|
|
|
{ |
382
|
|
|
$this->api('torrent-set-location', compact('ids', 'location', 'move')); |
383
|
|
|
|
384
|
|
|
return true; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Rename a Torrent's Path. |
389
|
|
|
* |
390
|
|
|
* @see https://git.io/transmission-rpc-specs "torrent-rename-path" for available arguments. |
391
|
|
|
* |
392
|
|
|
* @param mixed $ids One torrent id, as described in 3.1 of specs. |
393
|
|
|
* @param string $path The path to the file or folder that will be renamed. |
394
|
|
|
* @param string $name The file or folder's new name. |
395
|
|
|
* |
396
|
|
|
* @return array |
397
|
|
|
*/ |
398
|
|
|
public function rename($ids, string $path, string $name): array |
399
|
|
|
{ |
400
|
|
|
return $this->api('torrent-rename-path', compact('ids', 'path', 'name')); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* Set the transmission settings. |
405
|
|
|
* |
406
|
|
|
* @see https://git.io/transmission-rpc-specs "session-set" for available arguments. |
407
|
|
|
* |
408
|
|
|
* @param array $arguments one or more of spec's arguments, except: "blocklist-size", |
409
|
|
|
* "config-dir", "rpc-version", "rpc-version-minimum", |
410
|
|
|
* "version", and "session-id" |
411
|
|
|
* |
412
|
|
|
* @return bool |
413
|
|
|
*/ |
414
|
|
|
public function setSettings(array $arguments): bool |
415
|
|
|
{ |
416
|
|
|
$this->api('session-set', $arguments); |
417
|
|
|
|
418
|
|
|
return true; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Get the transmission settings. |
423
|
|
|
* |
424
|
|
|
* @see https://git.io/transmission-rpc-specs "session-get" for available fields. |
425
|
|
|
* |
426
|
|
|
* @param array|null $fields |
427
|
|
|
* |
428
|
|
|
* @return array |
429
|
|
|
*/ |
430
|
|
|
public function getSettings(array $fields = null): array |
431
|
|
|
{ |
432
|
|
|
return $this->api('session-get', compact('fields')); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* Get Session Stats. |
437
|
|
|
* |
438
|
|
|
* @see https://git.io/transmission-rpc-specs "session-stats" for response arguments. |
439
|
|
|
* |
440
|
|
|
* @return array |
441
|
|
|
*/ |
442
|
|
|
public function sessionStats(): array |
443
|
|
|
{ |
444
|
|
|
return $this->api('session-stats'); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Trigger Blocklist Update. |
449
|
|
|
* |
450
|
|
|
* @see https://git.io/transmission-rpc-specs "blocklist-update" for response arguments. |
451
|
|
|
* |
452
|
|
|
* @return array |
453
|
|
|
*/ |
454
|
|
|
public function updateBlocklist(): array |
455
|
|
|
{ |
456
|
|
|
return $this->api('blocklist-update'); |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* Port Test: See if your incoming peer port is accessible from the outside world. |
461
|
|
|
* |
462
|
|
|
* @see https://git.io/transmission-rpc-specs "port-test" for response arguments. |
463
|
|
|
* |
464
|
|
|
* @return bool |
465
|
|
|
*/ |
466
|
|
|
public function portTest(): bool |
467
|
|
|
{ |
468
|
|
|
return $this->api('port-test')['arguments']['port-is-open']; |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
/** |
472
|
|
|
* Shutdown Transmission. |
473
|
|
|
* |
474
|
|
|
* @see https://git.io/transmission-rpc-specs "session-close". |
475
|
|
|
* |
476
|
|
|
* @return bool |
477
|
|
|
*/ |
478
|
|
|
public function close(): bool |
479
|
|
|
{ |
480
|
|
|
$this->api('session-close'); |
481
|
|
|
|
482
|
|
|
return true; |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Move one or more torrents to top in queue. |
487
|
|
|
* |
488
|
|
|
* @see https://git.io/transmission-rpc-specs Queue Movement Requests. |
489
|
|
|
* |
490
|
|
|
* @param mixed $ids One or more torrent ids, sha1 hash strings, or both OR "recently-active", for recently-active |
491
|
|
|
* torrents. All torrents are used if no value is given. |
492
|
|
|
* |
493
|
|
|
* @return bool |
494
|
|
|
*/ |
495
|
|
|
public function queueMoveTop($ids = null): bool |
496
|
|
|
{ |
497
|
|
|
$this->api('queue-move-top', compact('ids')); |
498
|
|
|
|
499
|
|
|
return true; |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* Move one or more torrents up in queue. |
504
|
|
|
* |
505
|
|
|
* @see https://git.io/transmission-rpc-specs Queue Movement Requests. |
506
|
|
|
* |
507
|
|
|
* @param mixed $ids One or more torrent ids, sha1 hash strings, or both OR "recently-active", for recently-active |
508
|
|
|
* torrents. All torrents are used if no value is given. |
509
|
|
|
* |
510
|
|
|
* @return bool |
511
|
|
|
*/ |
512
|
|
|
public function queueMoveUp($ids = null): bool |
513
|
|
|
{ |
514
|
|
|
$this->api('queue-move-top', compact('ids')); |
515
|
|
|
|
516
|
|
|
return true; |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
/** |
520
|
|
|
* Move one or more torrents down in queue. |
521
|
|
|
* |
522
|
|
|
* @see https://git.io/transmission-rpc-specs Queue Movement Requests. |
523
|
|
|
* |
524
|
|
|
* @param mixed $ids One or more torrent ids, sha1 hash strings, or both OR "recently-active", for recently-active |
525
|
|
|
* torrents. All torrents are used if no value is given. |
526
|
|
|
* |
527
|
|
|
* @return bool |
528
|
|
|
*/ |
529
|
|
|
public function queueMoveDown($ids = null): bool |
530
|
|
|
{ |
531
|
|
|
$this->api('queue-move-down', compact('ids')); |
532
|
|
|
|
533
|
|
|
return true; |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* Move one or more torrents to bottom in queue. |
538
|
|
|
* |
539
|
|
|
* @see https://git.io/transmission-rpc-specs Queue Movement Requests. |
540
|
|
|
* |
541
|
|
|
* @param mixed $ids One or more torrent ids, sha1 hash strings, or both OR "recently-active", for recently-active |
542
|
|
|
* torrents. All torrents are used if no value is given. |
543
|
|
|
* |
544
|
|
|
* @return bool |
545
|
|
|
*/ |
546
|
|
|
public function queueMoveBottom($ids = null): bool |
547
|
|
|
{ |
548
|
|
|
$this->api('queue-move-bottom', compact('ids')); |
549
|
|
|
|
550
|
|
|
return true; |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
/** |
554
|
|
|
* Free Space: Tests how much free space is available in a client-specified folder. |
555
|
|
|
* |
556
|
|
|
* @see https://git.io/transmission-rpc-specs "free-space" for arguments. |
557
|
|
|
* |
558
|
|
|
* @param null|string $path Path to check free space (default: download-dir). |
559
|
|
|
* |
560
|
|
|
* @return array |
561
|
|
|
*/ |
562
|
|
|
public function freeSpace(string $path = null): array |
563
|
|
|
{ |
564
|
|
|
if (blank($path)) { |
565
|
|
|
$path = $this->getSettings()['arguments']['download-dir']; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
return $this->api('free-space', compact('path'))['arguments']; |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
/** |
572
|
|
|
* Seed Ratio Limit. |
573
|
|
|
* |
574
|
|
|
* @return mixed |
575
|
|
|
*/ |
576
|
|
|
public function seedRatioLimit() |
577
|
|
|
{ |
578
|
|
|
$settings = $this->getSettings(['seedRatioLimited', 'seedRatioLimit'])['arguments']; |
579
|
|
|
|
580
|
|
|
if (isset($settings['seedRatioLimited'])) { |
581
|
|
|
return $settings['seedRatioLimit']; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
return -1; |
585
|
|
|
} |
586
|
|
|
|
587
|
|
|
/** |
588
|
|
|
* Update Download Dir. |
589
|
|
|
* |
590
|
|
|
* @param string $downloadDir Path to download torrents. |
591
|
|
|
* |
592
|
|
|
* @return bool |
593
|
|
|
*/ |
594
|
|
|
public function updateDownloadDir(string $downloadDir): bool |
595
|
|
|
{ |
596
|
|
|
$settings = [ |
597
|
|
|
'download-dir' => $downloadDir, |
598
|
|
|
]; |
599
|
|
|
|
600
|
|
|
return $this->setSettings($settings); |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
/** |
604
|
|
|
* Update & Enable Incomplete Dir. |
605
|
|
|
* |
606
|
|
|
* @param string $incompleteDir Path to store incomplete torrents. |
607
|
|
|
* @param bool $enableIncompleteDir Is incomplete dir enabled? (default: true). |
608
|
|
|
* |
609
|
|
|
* @return bool |
610
|
|
|
*/ |
611
|
|
|
public function updateIncompleteDir(string $incompleteDir, bool $enableIncompleteDir = true): bool |
612
|
|
|
{ |
613
|
|
|
$settings = [ |
614
|
|
|
'incomplete-dir-enabled' => $enableIncompleteDir, |
615
|
|
|
'incomplete-dir' => $incompleteDir, |
616
|
|
|
]; |
617
|
|
|
|
618
|
|
|
return $this->setSettings($settings); |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
/** |
622
|
|
|
* Request API. |
623
|
|
|
* |
624
|
|
|
* @param string $method |
625
|
|
|
* @param array $params |
626
|
|
|
* |
627
|
|
|
* @return mixed |
628
|
|
|
*/ |
629
|
|
|
protected function api(string $method, array $params = []) |
630
|
|
|
{ |
631
|
|
|
$arguments = ParamBuilder::build($params); |
632
|
|
|
|
633
|
|
|
$body = json_encode(compact('method', 'arguments')); |
634
|
|
|
|
635
|
|
|
$response = $this->getHttpClient() |
636
|
|
|
->post( |
637
|
|
|
$this->transmissionUrl(), |
638
|
|
|
['Content-Type' => 'application/json'], |
639
|
|
|
$body |
640
|
|
|
); |
641
|
|
|
|
642
|
|
|
if (ResponseMediator::isConflictError($response)) { |
643
|
|
|
$this->findAndSetSessionId($response); |
644
|
|
|
|
645
|
|
|
return $this->api($method, $params); |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
return ResponseMediator::getContent($response); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Find and Set Session ID from the response. |
653
|
|
|
* |
654
|
|
|
* @param ResponseInterface $response |
655
|
|
|
* |
656
|
|
|
* @return Client |
657
|
|
|
* @throws TransmissionException |
658
|
|
|
*/ |
659
|
|
|
protected function findAndSetSessionId(ResponseInterface $response): self |
660
|
|
|
{ |
661
|
|
|
$sessionId = $response->getHeaderLine('x-transmission-session-id'); |
662
|
|
|
|
663
|
|
|
if (blank($sessionId)) { |
664
|
|
|
throw new TransmissionException('Unable to retrieve X-Transmission-Session-Id'); |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
$this->setSessionId($sessionId); |
668
|
|
|
|
669
|
|
|
return $this; |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
/** |
673
|
|
|
* Transmission-RPC API URL. |
674
|
|
|
* |
675
|
|
|
* @return string |
676
|
|
|
*/ |
677
|
|
|
protected function transmissionUrl(): string |
678
|
|
|
{ |
679
|
|
|
return $this->hostname . ':' . $this->port . $this->path; |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
/** |
683
|
|
|
* Default User Agent for all HTTP Requests. |
684
|
|
|
* |
685
|
|
|
* @return string HTTP User Agent. |
686
|
|
|
*/ |
687
|
|
|
protected function defaultUserAgent(): string |
688
|
|
|
{ |
689
|
|
|
return 'PHP-Transmission-SDK/' . self::VERSION; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
/** |
693
|
|
|
* Get HTTP Client. |
694
|
|
|
* |
695
|
|
|
* @return HttpMethodsClient |
696
|
|
|
*/ |
697
|
|
|
public function getHttpClient(): HttpMethodsClient |
698
|
|
|
{ |
699
|
|
|
return $this->httpClientBuilder->getHttpClient(); |
700
|
|
|
} |
701
|
|
|
|
702
|
|
|
/** |
703
|
|
|
* @return History |
704
|
|
|
*/ |
705
|
|
|
public function getResponseHistory() |
706
|
|
|
{ |
707
|
|
|
return $this->responseHistory; |
708
|
|
|
} |
709
|
|
|
|
710
|
|
|
/** |
711
|
|
|
* @param $method |
712
|
|
|
* @param $arguments |
713
|
|
|
* |
714
|
|
|
* @return mixed |
715
|
|
|
*/ |
716
|
|
|
public function __call($method, $arguments) |
717
|
|
|
{ |
718
|
|
|
throw new \BadMethodCallException(sprintf( |
719
|
|
|
'Method %s::%s does not exist.', static::class, $method |
720
|
|
|
)); |
721
|
|
|
} |
722
|
|
|
} |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.