Completed
Push — master ( 61695c...f1d104 )
by C
07:45
created

DownloadCommand::doWork()   D

Complexity

Conditions 30
Paths 20

Size

Total Lines 173
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 79
CRAP Score 30.0017

Importance

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