1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Popstas\Transmission\Console\Command; |
4
|
|
|
|
5
|
|
|
use Popstas\Transmission\Console\WeburgClient; |
6
|
|
|
use Symfony\Component\Console\Helper\ProgressBar; |
7
|
|
|
use Symfony\Component\Console\Input\ArrayInput; |
8
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
9
|
|
|
use Symfony\Component\Console\Input\InputOption; |
10
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
11
|
|
|
|
12
|
|
|
class WeburgDownload extends Command |
13
|
|
|
{ |
14
|
|
|
protected function configure() |
15
|
|
|
{ |
16
|
|
|
parent::configure(); |
17
|
|
|
$this |
18
|
|
|
->setName('weburg-download') |
19
|
|
|
->setAliases(['wd']) |
20
|
|
|
->setDescription('Download torrents from weburg.net') |
21
|
|
|
->addOption('download-torrents-dir', null, InputOption::VALUE_OPTIONAL, 'Torrents destination directory') |
22
|
|
|
->addOption('days', null, InputOption::VALUE_OPTIONAL, 'Max age of series torrent') |
23
|
|
|
->addOption('popular', null, InputOption::VALUE_NONE, 'Download only popular') |
24
|
|
|
->addOption('series', null, InputOption::VALUE_NONE, 'Download only tracked series') |
25
|
|
|
->addOption('query', null, InputOption::VALUE_OPTIONAL, 'Search and download movie from Weburg') |
26
|
|
|
->addOption('yes', 'y', InputOption::VALUE_NONE, 'Don\'t ask confirmation') |
27
|
|
|
->addArgument('movie-id', null, 'Movie ID or URL') |
28
|
|
|
->setHelp(<<<EOT |
29
|
|
|
## Download torrents from Weburg.net |
30
|
|
|
|
31
|
|
|
You can automatically download popular torrents from http://weburg.net/movies/new out of the box, use command: |
32
|
|
|
``` |
33
|
|
|
transmission-cli weburg-download --download-torrents-dir=/path/to/torrents/directory |
34
|
|
|
``` |
35
|
|
|
|
36
|
|
|
or define `download-torrents-dir` in config and just: |
37
|
|
|
``` |
38
|
|
|
transmission-cli weburg-download |
39
|
|
|
``` |
40
|
|
|
|
41
|
|
|
You can automatically download new series, for add series to tracked list see `transmission-cli weburg-series-add`. |
42
|
|
|
It is pretty simple: |
43
|
|
|
``` |
44
|
|
|
transmission-cli weburg-series-add http://weburg.net/series/info/12345 |
45
|
|
|
``` |
46
|
|
|
|
47
|
|
|
After that command `weburg-download` also will download series from list for last day. |
48
|
|
|
If you don't want to download popular torrents, but only new series, use command: |
49
|
|
|
``` |
50
|
|
|
transmission-cli weburg-download --download-torrents-dir=/path/to/torrents/directory --series |
51
|
|
|
``` |
52
|
|
|
|
53
|
|
|
## Add downloaded torrents to Transmission |
54
|
|
|
|
55
|
|
|
After download all torrents, command call `torrent-add` command for each transmission-host from config. |
56
|
|
|
If was defined `--transmission-host` option, then `torrent-add` will called only for this host. |
57
|
|
|
EOT |
58
|
|
|
); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
62
|
|
|
{ |
63
|
|
|
$config = $this->getApplication()->getConfig(); |
64
|
|
|
$weburgClient = $this->getApplication()->getWeburgClient(); |
65
|
|
|
|
66
|
|
|
try { |
67
|
|
|
list($torrentsDir, $downloadDir) = $this->getTorrentsDirectory($input); |
68
|
|
|
|
69
|
|
|
$daysMax = $config->overrideConfig($input, 'days', 'weburg-series-max-age'); |
70
|
|
|
$allowedMisses = $config->get('weburg-series-allowed-misses'); |
71
|
|
|
|
72
|
|
|
$movieArgument = $input->getArgument('movie-id'); |
73
|
|
|
if (isset($movieArgument)) { |
74
|
|
|
$torrentsUrls = $this->getMovieTorrentsUrls( |
75
|
|
|
$weburgClient, |
76
|
|
|
$movieArgument, |
77
|
|
|
$daysMax, |
78
|
|
|
$allowedMisses |
79
|
|
|
); |
80
|
|
|
} else { |
81
|
|
|
$torrentsUrls = $this->getTorrentsUrls( |
82
|
|
|
$input, |
83
|
|
|
$output, |
84
|
|
|
$weburgClient, |
85
|
|
|
$downloadDir, |
86
|
|
|
$daysMax, |
87
|
|
|
$allowedMisses |
88
|
|
|
); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
$this->dryRun($input, $output, function () use ( |
92
|
|
|
$input, |
93
|
|
|
$output, |
94
|
|
|
$weburgClient, |
95
|
|
|
$torrentsDir, |
96
|
|
|
$torrentsUrls |
97
|
|
|
) { |
98
|
|
|
if (empty($torrentsUrls)) { |
99
|
|
|
$output->writeln("\nNo torrents for download"); |
100
|
|
|
return; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
$downloadedFiles = []; |
104
|
|
|
foreach ($torrentsUrls as $torrentUrl) { |
105
|
|
|
$downloadedFiles[] = $weburgClient->downloadTorrent($torrentUrl, $torrentsDir); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
$downloadedFiles = $this->filterByLists($downloadedFiles); |
109
|
|
|
if (!empty($downloadedFiles)) { |
110
|
|
|
$this->addTorrents($input, $output, $downloadedFiles); |
111
|
|
|
} else { |
112
|
|
|
$output->writeln("\nAll torrents filtered by black/whitelists"); |
113
|
|
|
} |
114
|
|
|
}, 'dry-run, don\'t really download'); |
115
|
|
|
} catch (\RuntimeException $e) { |
116
|
|
|
$output->writeln($e->getMessage()); |
117
|
|
|
return 1; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return 0; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
private function getTorrentsUrls( |
124
|
|
|
InputInterface $input, |
125
|
|
|
OutputInterface $output, |
126
|
|
|
WeburgClient $weburgClient, |
127
|
|
|
$downloadDir, |
128
|
|
|
$daysMax, |
129
|
|
|
$allowedMisses |
130
|
|
|
) { |
131
|
|
|
$torrentsUrls = []; |
132
|
|
|
|
133
|
|
|
if ($input->getOption('query')) { |
134
|
|
|
$torrentsUrls = array_merge( |
135
|
|
|
$torrentsUrls, |
136
|
|
|
$this->getTorrentsUrlByQuery($output, $weburgClient, $downloadDir, $input->getOption('query')) |
137
|
|
|
); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
if (!$input->getOption('popular') && !$input->getOption('series') && !$input->getOption('query')) { |
141
|
|
|
$input->setOption('popular', true); |
142
|
|
|
$input->setOption('series', true); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
if ($input->getOption('popular')) { |
146
|
|
|
$torrentsUrls = array_merge( |
147
|
|
|
$torrentsUrls, |
148
|
|
|
$this->getPopularTorrentsUrls($output, $weburgClient, $downloadDir) |
149
|
|
|
); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
if ($input->getOption('series')) { |
153
|
|
|
$torrentsUrls = array_merge( |
154
|
|
|
$torrentsUrls, |
155
|
|
|
$this->getTrackedSeriesUrls($output, $weburgClient, $daysMax, $allowedMisses) |
156
|
|
|
); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
return $torrentsUrls; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
public function getTorrentsUrlByQuery(OutputInterface $output, WeburgClient $weburgClient, $downloadDir, $query) |
163
|
|
|
{ |
164
|
|
|
$torrentsUrls = []; |
165
|
|
|
|
166
|
|
|
$logger = $this->getApplication()->getLogger(); |
167
|
|
|
|
168
|
|
|
$movieId = $weburgClient->getMovieIdByQuery($query); |
169
|
|
|
if (!$movieId) { |
170
|
|
|
$output->writeln("\nNot found any for query $query"); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
$downloadedLogfile = $downloadDir . '/' . $movieId; |
174
|
|
|
|
175
|
|
|
$isDownloaded = file_exists($downloadedLogfile); |
176
|
|
|
if ($isDownloaded) { |
177
|
|
|
$output->writeln("\nMovie $query was downloaded before"); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
$movieInfo = $weburgClient->getMovieInfoById($movieId); |
181
|
|
View Code Duplication |
foreach (array_keys($movieInfo) as $infoField) { |
|
|
|
|
182
|
|
|
if (!isset($movieInfo[$infoField])) { |
183
|
|
|
$logger->warning('Cannot find ' . $infoField . ' in movie ' . $movieId); |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$movieUrls = $weburgClient->getMovieTorrentUrlsById($movieId); |
188
|
|
|
$torrentsUrls = array_merge($torrentsUrls, $movieUrls); |
189
|
|
|
$logger->info('Download movie ' . $movieId . ': ' . $movieInfo['title']); |
190
|
|
|
|
191
|
|
|
file_put_contents( |
192
|
|
|
$downloadedLogfile, |
193
|
|
|
date('Y-m-d H:i:s') . "\n" . implode("\n", $torrentsUrls) |
194
|
|
|
); |
195
|
|
|
|
196
|
|
|
return $torrentsUrls; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
public function getPopularTorrentsUrls(OutputInterface $output, WeburgClient $weburgClient, $downloadDir) |
200
|
|
|
{ |
201
|
|
|
$torrentsUrls = []; |
202
|
|
|
|
203
|
|
|
$config = $this->getApplication()->getConfig(); |
204
|
|
|
$logger = $this->getApplication()->getLogger(); |
205
|
|
|
|
206
|
|
|
$moviesIds = $weburgClient->getMoviesIds(); |
207
|
|
|
|
208
|
|
|
$output->writeln("\nDownloading popular torrents"); |
209
|
|
|
|
210
|
|
|
$progress = new ProgressBar($output, count($moviesIds)); |
211
|
|
|
$progress->start(); |
212
|
|
|
|
213
|
|
|
foreach ($moviesIds as $movieId) { |
214
|
|
|
$progress->setMessage('Check movie ' . $movieId . '...'); |
215
|
|
|
$progress->advance(); |
216
|
|
|
|
217
|
|
|
$downloadedLogfile = $downloadDir . '/' . $movieId; |
218
|
|
|
|
219
|
|
|
$isDownloaded = file_exists($downloadedLogfile); |
220
|
|
|
if ($isDownloaded) { |
221
|
|
|
continue; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
$movieInfo = $weburgClient->getMovieInfoById($movieId); |
225
|
|
View Code Duplication |
foreach (array_keys($movieInfo) as $infoField) { |
|
|
|
|
226
|
|
|
if (!isset($movieInfo[$infoField])) { |
227
|
|
|
$logger->warning('Cannot find ' . $infoField . ' in movie ' . $movieId); |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
$isTorrentPopular = $weburgClient->isTorrentPopular( |
232
|
|
|
$movieInfo, |
233
|
|
|
$config->get('download-comments-min'), |
234
|
|
|
$config->get('download-imdb-min'), |
235
|
|
|
$config->get('download-kinopoisk-min'), |
236
|
|
|
$config->get('download-votes-min') |
237
|
|
|
); |
238
|
|
|
|
239
|
|
|
if ($isTorrentPopular) { |
240
|
|
|
$progress->setMessage('Download movie ' . $movieId . '...'); |
241
|
|
|
|
242
|
|
|
$movieUrls = $weburgClient->getMovieTorrentUrlsById($movieId); |
243
|
|
|
$torrentsUrls = array_merge($torrentsUrls, $movieUrls); |
244
|
|
|
$logger->info('Download movie ' . $movieId . ': ' . $movieInfo['title']); |
245
|
|
|
|
246
|
|
|
file_put_contents( |
247
|
|
|
$downloadedLogfile, |
248
|
|
|
date('Y-m-d H:i:s') . "\n" . implode("\n", $torrentsUrls) |
249
|
|
|
); |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
$progress->finish(); |
254
|
|
|
|
255
|
|
|
return $torrentsUrls; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* @param OutputInterface $output |
260
|
|
|
* @param WeburgClient $weburgClient |
261
|
|
|
* @param $daysMax |
262
|
|
|
* @param $allowedMisses |
263
|
|
|
* @return array |
264
|
|
|
*/ |
265
|
|
|
public function getTrackedSeriesUrls(OutputInterface $output, WeburgClient $weburgClient, $daysMax, $allowedMisses) |
266
|
|
|
{ |
267
|
|
|
$torrentsUrls = []; |
268
|
|
|
|
269
|
|
|
$config = $this->getApplication()->getConfig(); |
270
|
|
|
|
271
|
|
|
$seriesList = $config->get('weburg-series-list'); |
272
|
|
|
if (!$seriesList) { |
273
|
|
|
return []; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
$output->writeln("\nDownloading tracked series"); |
277
|
|
|
|
278
|
|
|
$progress = new ProgressBar($output, count($seriesList)); |
279
|
|
|
$progress->start(); |
280
|
|
|
|
281
|
|
|
foreach ($seriesList as $seriesItem) { |
282
|
|
|
if (is_array($seriesItem)) { |
283
|
|
|
$seriesId = $seriesItem['id']; |
284
|
|
|
$seriesTitle = isset($seriesItem['title']) && $seriesItem['title'] ? $seriesItem['title'] : $seriesId; |
285
|
|
|
} else { |
286
|
|
|
$seriesId = $seriesTitle = $seriesItem; |
287
|
|
|
} |
288
|
|
|
$progress->setMessage('Check series ' . $seriesTitle . '...'); |
289
|
|
|
$progress->advance(); |
290
|
|
|
|
291
|
|
|
$movieInfo = $weburgClient->getMovieInfoById($seriesId); |
292
|
|
|
$seriesUrls = $weburgClient->getSeriesTorrents($seriesId, $movieInfo['hashes'], $daysMax, $allowedMisses); |
293
|
|
|
$torrentsUrls = array_merge($torrentsUrls, $seriesUrls); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
$progress->finish(); |
297
|
|
|
|
298
|
|
|
return $torrentsUrls; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* @param WeburgClient $weburgClient |
303
|
|
|
* @param $movieId |
304
|
|
|
* @param $daysMax |
305
|
|
|
* @param $allowedMisses |
306
|
|
|
* @return array |
307
|
|
|
*/ |
308
|
|
|
public function getMovieTorrentsUrls(WeburgClient $weburgClient, $movieId, $daysMax, $allowedMisses) |
309
|
|
|
{ |
310
|
|
|
$torrentsUrls = []; |
311
|
|
|
$logger = $this->getApplication()->getLogger(); |
312
|
|
|
|
313
|
|
|
$movieId = $weburgClient->cleanMovieId($movieId); |
314
|
|
|
if (!$movieId) { |
315
|
|
|
throw new \RuntimeException($movieId . ' seems not weburg movie ID or URL'); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
$movieInfo = $weburgClient->getMovieInfoById($movieId); |
319
|
|
|
$logger->info('Search series ' . $movieId); |
320
|
|
|
if (!empty($movieInfo['hashes'])) { |
321
|
|
|
$seriesUrls = $weburgClient->getSeriesTorrents($movieId, $movieInfo['hashes'], $daysMax, $allowedMisses); |
322
|
|
|
$torrentsUrls = array_merge($torrentsUrls, $seriesUrls); |
323
|
|
|
|
324
|
|
|
if (count($seriesUrls)) { |
325
|
|
|
$logger->info('Download series ' . $movieId . ': ' |
326
|
|
|
. $movieInfo['title'] . ' (' . count($seriesUrls) . ')'); |
327
|
|
|
} |
328
|
|
|
} else { |
329
|
|
|
$torrentsUrls = array_merge($torrentsUrls, $weburgClient->getMovieTorrentUrlsById($movieId)); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
return $torrentsUrls; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* @param InputInterface $input |
337
|
|
|
* @return array |
338
|
|
|
* @throws \RuntimeException |
339
|
|
|
*/ |
340
|
|
|
private function getTorrentsDirectory(InputInterface $input) |
341
|
|
|
{ |
342
|
|
|
$config = $this->getApplication()->getConfig(); |
343
|
|
|
|
344
|
|
|
$torrentsDir = $config->overrideConfig($input, 'download-torrents-dir'); |
345
|
|
|
if (!$torrentsDir) { |
346
|
|
|
throw new \RuntimeException('Destination directory not defined. ' |
347
|
|
|
. 'Use command with --download-torrents-dir=/path/to/dir parameter ' |
348
|
|
|
. 'or define destination directory \'download-torrents-dir\' in config file.'); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
if (!file_exists($torrentsDir)) { |
352
|
|
|
throw new \RuntimeException('Destination directory not exists: ' . $torrentsDir); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
$downloadDir = $torrentsDir . '/downloaded'; |
356
|
|
|
if (!file_exists($downloadDir)) { |
357
|
|
|
mkdir($downloadDir, 0777); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
return [$torrentsDir, $downloadDir]; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
private function filterByLists(array $torrentFiles) |
364
|
|
|
{ |
365
|
|
|
$config = $this->getApplication()->getConfig(); |
366
|
|
|
$logger = $this->getApplication()->getLogger(); |
367
|
|
|
|
368
|
|
|
$whitelist = $config->get('download-filename-whitelist'); |
369
|
|
|
$blacklist = $config->get('download-filename-blacklist'); |
370
|
|
|
|
371
|
|
|
$torrentFiles = array_filter($torrentFiles, function ($torrentFile) use ($whitelist, $blacklist, $logger) { |
372
|
|
|
if (!empty($whitelist)) { |
373
|
|
|
$matched = false; |
374
|
|
|
foreach ($whitelist as $white) { |
375
|
|
|
if (preg_match('/' . $white . '/i', $torrentFile)) { |
376
|
|
|
$logger->info($torrentFile . ' matched whitelist: ' . $white); |
377
|
|
|
$matched = true; |
378
|
|
|
} |
379
|
|
|
} |
380
|
|
|
if (!$matched) { |
381
|
|
|
$logger->info($torrentFile . ' not matched any whitelist: ' . implode(', ', $whitelist)); |
382
|
|
|
return false; |
383
|
|
|
} |
384
|
|
|
} |
385
|
|
|
if (!empty($blacklist)) { |
386
|
|
|
foreach ($blacklist as $black) { |
387
|
|
|
if (preg_match('/' . $black . '/i', $torrentFile)) { |
388
|
|
|
$logger->info($torrentFile . ' matched blacklist: ' . $black); |
389
|
|
|
return false; |
390
|
|
|
} |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
return true; |
394
|
|
|
}); |
395
|
|
|
return $torrentFiles; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
private function addTorrents(InputInterface $input, OutputInterface $output, array $torrentFiles) |
399
|
|
|
{ |
400
|
|
|
$config = $this->getApplication()->getConfig(); |
401
|
|
|
$hosts = []; |
402
|
|
|
|
403
|
|
|
if (empty($input->getOption('transmission-host'))) { |
404
|
|
|
$transmissionConnects = $config->get('transmission'); |
405
|
|
|
foreach ($transmissionConnects as $transmissionConnect) { |
406
|
|
|
$hosts[] = $transmissionConnect['host']; |
407
|
|
|
} |
408
|
|
|
} else { |
409
|
|
|
$hosts[] = $config->get('transmission-host'); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
foreach ($hosts as $host) { |
413
|
|
|
$command = $this->getApplication()->find('torrent-add'); |
414
|
|
|
$arguments = array( |
415
|
|
|
'command' => 'torrent-add', |
416
|
|
|
'torrent-files' => $torrentFiles, |
417
|
|
|
'--transmission-host' => $host, |
418
|
|
|
'--yes' => $input->getOption('yes'), |
419
|
|
|
'--dry-run' => $input->getOption('dry-run'), |
420
|
|
|
); |
421
|
|
|
|
422
|
|
|
$addInput = new ArrayInput($arguments); |
423
|
|
|
$output->writeln("\nAdd " . count($torrentFiles) . " torrents to " . $host); |
424
|
|
|
$command->run($addInput, $output); |
425
|
|
|
} |
426
|
|
|
} |
427
|
|
|
} |
428
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.