Completed
Push — master ( 5de613...7fed73 )
by Stanislav
05:37 queued 02:33
created

WeburgDownload::getTorrentsUrls()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 8.3786
c 0
b 0
f 0
cc 7
nc 16
nop 6
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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