PersistenceManager::onSubscriptionSeen()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 65
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 36
nc 7
nop 1
dl 0
loc 65
ccs 0
cts 43
cp 0
crap 56
rs 8.4106
c 2
b 0
f 0

How to fix   Long Method   

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
3
namespace Jalle19\StatusManager\Manager;
4
5
use Jalle19\StatusManager\Database;
6
use Jalle19\StatusManager\Database\Channel;
7
use Jalle19\StatusManager\Database\ChannelQuery;
8
use Jalle19\StatusManager\Database\Connection;
9
use Jalle19\StatusManager\Database\ConnectionQuery;
10
use Jalle19\StatusManager\Database\Input;
11
use Jalle19\StatusManager\Database\InputError;
12
use Jalle19\StatusManager\Database\InputQuery;
13
use Jalle19\StatusManager\Database\InstanceQuery;
14
use Jalle19\StatusManager\Database\Subscription;
15
use Jalle19\StatusManager\Database\SubscriptionQuery;
16
use Jalle19\StatusManager\Database\User;
17
use Jalle19\StatusManager\Database\UserQuery;
18
use Jalle19\StatusManager\Event\ConnectionSeenEvent;
19
use Jalle19\StatusManager\Event\Events;
20
use Jalle19\StatusManager\Event\InputSeenEvent;
21
use Jalle19\StatusManager\Event\InstanceSeenEvent;
22
use Jalle19\StatusManager\Event\PersistInputErrorEvent;
23
use Jalle19\StatusManager\Event\SubscriptionSeenEvent;
24
use Jalle19\StatusManager\Event\SubscriptionStateChangeEvent;
25
use Jalle19\StatusManager\Subscription\StateChange;
26
use Jalle19\tvheadend\model\SubscriptionStatus;
27
use Propel\Runtime\Exception\PropelException;
28
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
29
30
/**
31
 * Handles persisting of objects to the database
32
 *
33
 * @package   Jalle19\StatusManager\Manager
34
 * @copyright Copyright &copy; Sam Stenvall 2015-
35
 * @license   https://www.gnu.org/licenses/gpl.html The GNU General Public License v2.0
36
 */
37
class PersistenceManager extends AbstractManager implements EventSubscriberInterface
38
{
39
40
	/**
41
	 * @inheritdoc
42
	 */
43
	public static function getSubscribedEvents()
44
	{
45
		return [
46
			Events::MAIN_LOOP_STARTING        => 'onMainLoopStarted',
47
			Events::INSTANCE_SEEN             => 'onInstanceSeen',
48
			Events::CONNECTION_SEEN           => 'onConnectionSeen',
49
			Events::INPUT_SEEN                => 'onInputSeen',
50
			Events::SUBSCRIPTION_SEEN         => 'onSubscriptionSeen',
51
			Events::SUBSCRIPTION_STATE_CHANGE => 'onSubscriptionStateChange',
52
			Events::PERSIST_INPUT_ERROR       => 'onInputError',
53
		];
54
	}
55
56
57
	/**
58
	 * Removes stale subscriptions that haven't received a stop event
59
	 */
60
	public function onMainLoopStarted()
61
	{
62
		$numRemoved = SubscriptionQuery::create()->filterByStopped(null)->delete();
63
64
		$this->logger->info('Removed {numRemoved} stale subscriptions', [
65
			'numRemoved' => $numRemoved,
66
		]);
67
	}
68
69
70
	/**
71
	 * @param InstanceSeenEvent $event
72
	 *
73
	 * @throws PropelException
74
	 */
75
	public function onInstanceSeen(InstanceSeenEvent $event)
76
	{
77
		$instance = $event->getInstance();
78
79
		if (InstanceQuery::create()->hasInstance($instance))
80
			return;
81
82
		$instanceModel = new Database\Instance();
83
		$instanceModel->setPrimaryKey($instance->getName());
84
		$instanceModel->save();
85
86
		$this->logger->info('Stored new instance {instanceName}', [
87
			'instanceName' => $instance->getName(),
88
		]);
89
90
		// Create a special user for eventual DVR subscriptions
91
		$user = new User();
92
		$user->setInstance($instanceModel);
93
		$user->setName(User::NAME_DVR);
94
		$user->save();
95
96
		$this->logger
97
			->info('Stored new special user (instance: {instanceName}, user: {userName})', [
98
				'instanceName' => $instance->getName(),
99
				'userName'     => $user->getName(),
100
			]);
101
	}
102
103
104
	/**
105
	 * @param ConnectionSeenEvent $event
106
	 */
107
	public function onConnectionSeen(ConnectionSeenEvent $event)
108
	{
109
		$instanceName     = $event->getInstance();
110
		$connectionStatus = $event->getConnection();
111
112
		if (ConnectionQuery::create()->hasConnection($instanceName, $connectionStatus))
113
			return;
114
115
		$user = null;
116
117
		// Find the user object when applicable
118
		if (!$connectionStatus->isAnonymous())
119
		{
120
			$this->onUserSeen($instanceName, $connectionStatus->user);
121
122
			$user = UserQuery::create()->filterByInstanceName($instanceName)->filterByName($connectionStatus->user)
123
			                 ->findOne();
124
		}
125
126
		$connection = new Connection();
127
		$connection->setInstanceName($instanceName)->setPeer($connectionStatus->peer)
128
		           ->setUser($user)
129
		           ->setStarted($connectionStatus->started)->setType($connectionStatus->type)->save();
130
131
		$this->logger->info('Stored new connection (instance: {instanceName}, peer: {peer})', [
132
			'instanceName' => $instanceName,
133
			'peer'         => $connectionStatus->peer,
134
		]);
135
	}
136
137
138
	/**
139
	 * @param InputSeenEvent $event
140
	 */
141
	public function onInputSeen(InputSeenEvent $event)
142
	{
143
		$instanceName = $event->getInstance();
144
		$inputStatus  = $event->getInputStatus();
145
146
		// Only deal with inputs that are attached to a stream
147
		if ($inputStatus->stream === null)
148
		{
149
			return;
150
		}
151
152
		// Update the input and started fields for existing inputs
153
		if (InputQuery::create()->hasInput($inputStatus->uuid))
154
		{
155
			/** @var Input $input */
156
			$input = InputQuery::create()->findPk($inputStatus->uuid);
157
			$input->setStarted(new \DateTime())->setWeight($inputStatus->weight);
158
159
			return;
160
		}
161
162
		$input = new Input();
163
		$input->setInstanceName($instanceName)
164
		      ->setStarted(new \DateTime())
165
		      ->setFromInputStatus($inputStatus)->save();
166
167
		$this->logger
168
			->info('Stored new input (instance: {instanceName}, network: {network}, mux: {mux}, weight: {weight})',
169
				[
170
					'instanceName' => $instanceName,
171
					'network'      => $input->getNetwork(),
172
					'mux'          => $input->getMux(),
173
					'weight'       => $input->getWeight(),
174
				]);
175
	}
176
177
178
	/**
179
	 * @param SubscriptionSeenEvent $event
180
	 *
181
	 * @throws PropelException
182
	 */
183
	public function onSubscriptionSeen(SubscriptionSeenEvent $event)
184
	{
185
		$instanceName = $event->getInstance();
186
		$status       = $event->getSubscription();
187
188
		// Ignore certain subscriptions
189
		if (in_array($status->getType(), [
190
			SubscriptionStatus::TYPE_EPGGRAB,
191
			SubscriptionStatus::TYPE_SERVICE_OR_MUX,
192
			SubscriptionStatus::TYPE_SATIP,
193
		]))
194
		{
195
			return;
196
		}
197
198
		// Determine the username to store for the subscription
199
		$username = $status->username;
200
201
		switch ($status->getType())
202
		{
203
			case SubscriptionStatus::TYPE_RECORDING:
204
				$username = User::NAME_DVR;
205
				break;
206
		}
207
208
		// Get the instance, user and channel
209
		/** @var Database\Instance $instance */
210
		$instance = InstanceQuery::create()->findPk($instanceName);
211
		$user     = UserQuery::create()->filterByInstance($instance)->filterByName($username)->findOne();
212
213
		// Ensure the channel exists
214
		$this->onChannelSeen($instanceName, $status->channel);
215
		/** @var Channel $channel */
216
		$channel = ChannelQuery::create()->filterByInstance($instance)->filterByName($status->channel)->findOne();
217
218
		if (SubscriptionQuery::create()->hasSubscription($instance, $user, $channel, $status))
219
			return;
220
221
		// Try to determine which input is used by the subscription
222
		$input = InputQuery::create()->filterBySubscriptionStatus($instanceName, $event->getInstanceStatus(), $status)
223
		                   ->findOne();
224
225
		if ($input === null)
226
		{
227
			$this->logger
228
				->warning('Got subscription that cannot be tied to an input ({instanceName}, user: {userName}, channel: {channelName})',
229
					[
230
						'instanceName' => $instanceName,
231
						'userName'     => $user !== null ? $user->getName() : 'N/A',
232
						'channelName'  => $channel->getName(),
233
					]);
234
		}
235
236
		$subscription = new Subscription();
237
		$subscription->setInstance($instance)->setInput($input)->setUser($user)->setChannel($channel)
238
		             ->setSubscriptionId($status->id)->setStarted($status->start)->setTitle($status->title)
239
		             ->setService($status->service);
240
		$subscription->save();
241
242
		$this->logger
243
			->info('Stored new subscription (instance: {instanceName}, user: {userName}, channel: {channelName})',
244
				[
245
					'instanceName' => $instanceName,
246
					'userName'     => $user !== null ? $user->getName() : 'N/A',
247
					'channelName'  => $channel->getName(),
248
				]);
249
	}
250
251
252
	/**
253
	 * @param SubscriptionStateChangeEvent $event
254
	 */
255
	public function onSubscriptionStateChange(SubscriptionStateChangeEvent $event)
256
	{
257
		$instanceName = $event->getInstance();
258
		$stateChange  = $event->getStateChange();
259
260
		// We only need to persist subscription stops
261
		if ($stateChange->getState() === StateChange::STATE_SUBSCRIPTION_STARTED)
262
			return;
263
264
		// Find the latest matching subscription
265
		$subscription = SubscriptionQuery::create()
266
		                                 ->getNewestMatching($instanceName, $stateChange->getSubscriptionId());
267
268
		// EPG grab subscriptions are not stored so we don't want to log these with a high level
269
		if ($subscription === null)
270
		{
271
			$this->logger
272
				->debug('Got subscription stop without a matching start (instance: {instanceName}, subscription: {subscriptionId})',
273
					[
274
						'instanceName'   => $instanceName,
275
						'subscriptionId' => $stateChange->getSubscriptionId(),
276
					]);
277
278
			return;
279
		}
280
281
		$subscription->setStopped(new \DateTime());
282
		$subscription->save();
283
284
		$user    = $subscription->getUser();
285
		$channel = $subscription->getChannel();
286
287
		$this->logger
288
			->info('Stored subscription stop (instance: {instanceName}, user: {userName}, channel: {channelName})',
289
				[
290
					'instanceName' => $instanceName,
291
					'userName'     => $user !== null ? $user->getName() : 'N/A',
292
					'channelName'  => $channel->getName(),
293
				]);
294
	}
295
296
297
	/**
298
	 * @param PersistInputErrorEvent $event
299
	 */
300
	public function onInputError(PersistInputErrorEvent $event)
301
	{
302
		$input            = $event->getInput();
303
		$cumulativeErrors = $event->getCumulativeErrors();
304
305
		$inputError = new InputError();
306
		$inputError->setInput($input);
307
		$inputError->setFromInputErrorCumulative($cumulativeErrors);
308
		$inputError->save();
309
310
		$this->logger->debug('Persisted input errors (instance: {instanceName}, input: {friendlyName})', [
311
			'instanceName' => $input->getInstanceName(),
312
			'friendlyName' => $input->getFriendlyName(),
313
		]);
314
	}
315
316
317
	/**
318
	 * @param string $instanceName
319
	 * @param string $userName
320
	 *
321
	 * @throws PropelException
322
	 */
323
	private function onUserSeen($instanceName, $userName)
324
	{
325
		if (UserQuery::create()->hasUser($instanceName, $userName))
326
			return;
327
328
		$user = new User();
329
		$user->setInstanceName($instanceName)->setName($userName);
330
		$user->save();
331
332
		$this->logger->info('Stored new user (instance: {instanceName}, username: {userName})', [
333
			'instanceName' => $instanceName,
334
			'userName'     => $userName,
335
		]);
336
	}
337
338
339
	/**
340
	 * @param string $instanceName
341
	 * @param string $channelName
342
	 *
343
	 * @throws PropelException
344
	 */
345
	private function onChannelSeen($instanceName, $channelName)
346
	{
347
		if (ChannelQuery::create()->hasChannel($instanceName, $channelName))
348
			return;
349
350
		$channel = new Channel();
351
		$channel->setInstanceName($instanceName)->setName($channelName);
352
		$channel->save();
353
354
		$this->logger
355
			->info('Stored new channel (instance: {instanceName}, name: {channelName})', [
356
				'instanceName' => $instanceName,
357
				'channelName'  => $channelName,
358
			]);
359
	}
360
361
}
362