DownloadCommand::doWork()   D
last analyzed

Complexity

Conditions 31
Paths 40

Size

Total Lines 152
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 80
CRAP Score 31.0017

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 152
ccs 80
cts 81
cp 0.9877
rs 4.3983
cc 31
eloc 89
nc 40
nop 2
crap 31.0017

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Local\Console\Command;
3
4
use GuzzleHttp\Promise;
5
use Joomla\Registry\Registry;
6
use League\Flysystem\Adapter\Local;
7
use Monolog\Logger;
8
use Tartana\Component\Command\Command;
9
use Tartana\Domain\Command\SaveDownloads;
10
use Tartana\Domain\DownloadRepository;
11
use Tartana\Entity\Download;
12
use Tartana\Host\Common\Http;
13
use Tartana\Host\HostFactory;
14
use Tartana\Mixins\CommandBusAwareTrait;
15
use Tartana\Mixins\LoggerAwareTrait;
16
use Tartana\Util;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Input\InputOption;
19
use Symfony\Component\Console\Output\OutputInterface;
20
use Tartana\Console\Command\AbstractDaemonCommand;
21
use Tartana\Component\Command\Runner;
22
23
class DownloadCommand extends AbstractDaemonCommand
24
{
25
	use LoggerAwareTrait;
26
	use CommandBusAwareTrait;
27
28
	private $repository = null;
29
	private $factory = null;
30
	private $config = null;
31
32 16
	public function __construct(DownloadRepository $repository, HostFactory $factory, Runner $runner, Registry $config = null)
33
	{
34 16
		parent::__construct($runner);
35
36 16
		$this->repository = $repository;
37 16
		$this->factory    = $factory;
38 16
		$this->config     = $config;
39 16
	}
40
41 16
	protected function configure()
42
	{
43 16
		parent::configure();
44
45 16
		$this->setName('download');
46 16
		$this->setDescription('Downloads links from the database. This command is running in foreground!');
47
48 16
		$this->addOption('force', 'f', InputOption::VALUE_NONE, 'Should all downloads set back to not started.');
49 16
	}
50
51 15
	protected function doWork(InputInterface $input, OutputInterface $output)
52
	{
53 15
		$repository = $this->repository;
54
55 15
		$this->log('Started to download links from the database');
56
57
		// Loading configuration for the hosters if it exists
58 15
		$config = $this->config ? $this->config : new Registry();
59 15
		if (file_exists(TARTANA_PATH_ROOT . '/app/config/parameters.yml')) {
60 15
			$config->loadFile(TARTANA_PATH_ROOT . '/app/config/parameters.yml', 'yaml');
61
		}
62 15
		if (file_exists(TARTANA_PATH_ROOT . '/app/config/hosters.yml')) {
63 1
			$config->loadFile(TARTANA_PATH_ROOT . '/app/config/hosters.yml', 'yaml');
64
		}
65
66 15
		$force = (boolean)$input->getOption('force');
67
68 15
		while (true) {
69 15
			$resets     = $repository->findDownloads([
70 15
				Download::STATE_DOWNLOADING_STARTED,
71 15
				Download::STATE_DOWNLOADING_ERROR
72
			]);
73 15
			$hasChanged = false;
74 15
			if (!empty($resets)) {
75 5
				foreach ($resets as $resetDownload) {
76 5
					if ($resetDownload->getState() != Download::STATE_DOWNLOADING_STARTED && !$force) {
77
						// When not forcing only check for zombie downloads
78 2
						continue;
79
					}
80 4
					if ($resetDownload->getState() == Download::STATE_DOWNLOADING_STARTED &&
81 4
						$resetDownload->getPid() &&
82 4
						Util::isPidRunning($resetDownload->getPid())
83
					) {
84
						// There is an active process
85 2
						continue;
86
					}
87 3
					$resetDownload = Download::reset($resetDownload);
88 3
					$hasChanged    = true;
89
				}
90
			}
91 15
			if ($hasChanged) {
92 3
				$this->log('Restarting zombie downloads, error downloads will be ' . ($force ? '' : 'not') . ' restarted');
93 3
				$this->handleCommand(new SaveDownloads($resets));
94
			}
95
96 15
			$notStartedDownloads = $repository->findDownloads(Download::STATE_DOWNLOADING_NOT_STARTED);
97 15
			if (empty($notStartedDownloads)) {
98
				// Nothing to do anymore
99 15
				break;
100
			}
101
102 11
			$concurrentDownloads = 5;
103 11
			$counter             = count($repository->findDownloads(Download::STATE_DOWNLOADING_STARTED));
104
105
			// Set download speed limit
106 11
			if (isset($config->get('parameters')->{'tartana.local.downloads.speedlimit'}) &&
107 11
				$config->get('parameters')->{'tartana.local.downloads.speedlimit'} > 0
108
			) {
109 1
				$config->set('speedlimit', $config->get('parameters')->{'tartana.local.downloads.speedlimit'} / $concurrentDownloads);
110
			}
111
112
			// Check day limit
113 11
			if (isset($config->get('parameters')->{'tartana.local.downloads.daylimit'}) &&
114 11
				$config->get('parameters')->{'tartana.local.downloads.daylimit'} > 0
115
			) {
116 2
				$dayLimit = $config->get('parameters')->{'tartana.local.downloads.daylimit'} * 1000;
117
118 2
				$today = (new \DateTime())->format('D');
119 2
				foreach ($repository->findDownloads(Download::STATE_DOWNLOADING_COMPLETED) as $download) {
120 2
					if ($download->getFinishedAt() && $download->getFinishedAt()->format('D') != $today) {
121 1
						continue;
122
					}
123
124 2
					$dayLimit -= $download->getSize();
125
				}
126
127 2
				if ($dayLimit <= 0) {
128 1
					$this->log('Reached day limit, not starting any download');
129 1
					$counter = $concurrentDownloads;
130
				}
131
			}
132
133 11
			$this->log('Found ' . $counter . ' started downloads.');
134
135
			// Processing the downloads
136 11
			$promises         = [];
137 11
			$sharedClients    = [];
138 11
			$startedDownloads = [];
139 11
			foreach ($notStartedDownloads as $download) {
140 11
				if ($counter >= $concurrentDownloads) {
141 2
					break;
142
				}
143
144 9
				$downloader = $this->factory->createHostDownloader($download->getLink(), $config);
145 9
				if ($downloader == null) {
146 1
					$this->log('No downloader found for link ' . $download->getLink(), Logger::WARNING);
147 1
					continue;
148
				}
149
150 8
				$this->log('Started to download ' . $download->getLink() . ' with the class ' . get_class($downloader));
151
152 8
				$download = Download::reset($download);
153 8
				$download->setState(Download::STATE_DOWNLOADING_STARTED);
154 8
				$download->setPid(getmypid());
155 8
				$this->handleCommand(new SaveDownloads([
156 8
					$download
157
				]));
158
159 8
				if ($downloader instanceof Http) {
160 2
					$name = get_class($downloader);
161 2
					if (!key_exists($name, $sharedClients)) {
162 2
						$sharedClients[$name] = $downloader->getClient();
163
					} else {
164 1
						$downloader->setClient($sharedClients[$name]);
165
					}
166
				}
167
168 8
				$tmp                = $downloader->download([
169 8
					clone $download
170
				]);
171 8
				$startedDownloads[] = $download;
172
173 8
				$promises = array_merge($promises, $tmp ? $tmp : []);
174 8
				$counter++;
175
			}
176
177 11
			$this->log('Downloading ' . count($promises) . ' links');
178
179
			try {
180 11
				Promise\unwrap($promises);
181
			} catch (\Exception $e) {
182
				// @codeCoverageIgnoreStart
183
				$this->log('Exception happened, waiting for the downloads: ' . $e->getMessage(), Logger::ERROR);
184
				foreach ($startedDownloads as $download) {
185
					$download = Download::reset($download);
186
					$download->setState(Download::STATE_DOWNLOADING_ERROR);
187
					$download->setMessage($e->getMessage());
188
					$this->handleCommand(new SaveDownloads([
189
						$download
190
					]));
191
				}
192
				// @codeCoverageIgnoreEnd
193
			}
194
195
			// We sleep her as we are a daemon to relax the system a bit
196 11
			sleep($config->get('sleepTime', 10));
197
		}
198 15
		$this->log('Finished to download links from the database');
199
200 15
		$this->log('Calling default command for post processing');
201 15
		$this->getCommandRunner()->execute(Command::getAppCommand('default'));
202 15
	}
203
}
204