Passed
Push — master ( d5fa9e...eded98 )
by Pauli
01:56
created

ApiController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 16
dl 0
loc 31
ccs 16
cts 16
cp 1
crap 1
rs 9.7666
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Morris Jobke <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Morris Jobke 2013, 2014
12
 * @copyright Pauli Järvinen 2017 - 2019
13
 */
14
15
namespace OCA\Music\Controller;
16
17
use \OCP\AppFramework\Controller;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Controller was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use \OCP\AppFramework\Http;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use \OCP\AppFramework\Http\DataDisplayResponse;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\DataDisplayResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use \OCP\AppFramework\Http\JSONResponse;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\JSONResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use \OCP\AppFramework\Http\RedirectResponse;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\RedirectResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use \OCP\Files\Folder;
0 ignored issues
show
Bug introduced by
The type OCP\Files\Folder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use \OCP\IL10N;
0 ignored issues
show
Bug introduced by
The type OCP\IL10N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use \OCP\IRequest;
0 ignored issues
show
Bug introduced by
The type OCP\IRequest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use \OCP\IURLGenerator;
0 ignored issues
show
Bug introduced by
The type OCP\IURLGenerator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
27
use \OCA\Music\AppFramework\Core\Logger;
28
use \OCA\Music\BusinessLayer\AlbumBusinessLayer;
29
use \OCA\Music\BusinessLayer\ArtistBusinessLayer;
30
use \OCA\Music\BusinessLayer\TrackBusinessLayer;
31
use \OCA\Music\Db\Album;
32
use \OCA\Music\Db\Artist;
33
use \OCA\Music\Db\Maintenance;
34
use \OCA\Music\Db\Track;
35
use \OCA\Music\Http\ErrorResponse;
36
use \OCA\Music\Http\FileResponse;
37
use \OCA\Music\Utility\CollectionHelper;
38
use \OCA\Music\Utility\CoverHelper;
39
use \OCA\Music\Utility\DetailsHelper;
40
use \OCA\Music\Utility\Scanner;
41
use OCA\Music\BusinessLayer\GenreBusinessLayer;
42
43
class ApiController extends Controller {
44
45
	/** @var IL10N */
46
	private $l10n;
47
	/** @var TrackBusinessLayer */
48
	private $trackBusinessLayer;
49
	/** @var ArtistBusinessLayer */
50
	private $artistBusinessLayer;
51
	/** @var AlbumBusinessLayer */
52
	private $albumBusinessLayer;
53
	/** @var GenreBusinessLayer */
54
	private $genreBusinessLayer;
55
	/** @var Scanner */
56
	private $scanner;
57
	/** @var CollectionHelper */
58
	private $collectionHelper;
59
	/** @var CoverHelper */
60
	private $coverHelper;
61
	/** @var DetailsHelper */
62
	private $detailsHelper;
63
	/** @var Maintenance */
64
	private $maintenance;
65
	/** @var string */
66
	private $userId;
67
	/** @var IURLGenerator */
68
	private $urlGenerator;
69
	/** @var Folder */
70
	private $userFolder;
71
	/** @var Logger */
72
	private $logger;
73
74 16
	public function __construct($appname,
75
								IRequest $request,
76
								IURLGenerator $urlGenerator,
77
								TrackBusinessLayer $trackbusinesslayer,
78
								ArtistBusinessLayer $artistbusinesslayer,
79
								AlbumBusinessLayer $albumbusinesslayer,
80
								GenreBusinessLayer $genreBusinessLayer,
81
								Scanner $scanner,
82
								CollectionHelper $collectionHelper,
83
								CoverHelper $coverHelper,
84
								DetailsHelper $detailsHelper,
85
								Maintenance $maintenance,
86
								$userId,
87
								IL10N $l10n,
88
								Folder $userFolder,
89
								Logger $logger) {
90 16
		parent::__construct($appname, $request);
91 16
		$this->l10n = $l10n;
92 16
		$this->trackBusinessLayer = $trackbusinesslayer;
93 16
		$this->artistBusinessLayer = $artistbusinesslayer;
94 16
		$this->albumBusinessLayer = $albumbusinesslayer;
95 16
		$this->genreBusinessLayer = $genreBusinessLayer;
96 16
		$this->scanner = $scanner;
97 16
		$this->collectionHelper = $collectionHelper;
98 16
		$this->coverHelper = $coverHelper;
99 16
		$this->detailsHelper = $detailsHelper;
100 16
		$this->maintenance = $maintenance;
101 16
		$this->userId = $userId;
102 16
		$this->urlGenerator = $urlGenerator;
103 16
		$this->userFolder = $userFolder;
104 16
		$this->logger = $logger;
105 16
	}
106
107
	/**
108
	 * Extracts the id from an unique slug (id-slug)
109
	 * @param string $slug the slug
110
	 * @return integer the id
111
	 */
112 5
	protected static function getIdFromSlug($slug) {
113 5
		$split = \explode('-', $slug, 2);
114
115 5
		return (int)$split[0];
116
	}
117
118
	/**
119
	 * @NoAdminRequired
120
	 * @NoCSRFRequired
121
	 */
122
	public function prepareCollection() {
123
		$hash = $this->collectionHelper->getCachedJsonHash();
124
		if ($hash === null) {
125
			// build the collection but ignore the data for now
126
			$this->collectionHelper->getJson();
127
			$hash = $this->collectionHelper->getCachedJsonHash();
128
		}
129
		return new JSONResponse(['hash' => $hash]);
130
	}
131
132
	/**
133
	 * @NoAdminRequired
134
	 * @NoCSRFRequired
135
	 */
136
	public function collection() {
137
138
		$collectionJson = $this->collectionHelper->getJson();
139
		$response = new DataDisplayResponse($collectionJson);
140
		$response->addHeader('Content-Type', 'application/json; charset=utf-8');
141
142
		// Instruct the client to cache the result in case it requested the collection with
143
		// the correct hash. The hash could be incorrect if the collection would have changed
144
		// between calls to prepareCollection() and colletion().
145
		$requestHash = $this->request->getParam('hash');
146
		$actualHash = $this->collectionHelper->getCachedJsonHash();
147
		if (!empty($actualHash) && $requestHash === $actualHash) {
148
			self::setClientCaching($response, 90); // cache for 3 months
149
		}
150
151
		return $response;
152
	}
153
154
	/**
155
	 * @NoAdminRequired
156
	 * @NoCSRFRequired
157
	 */
158
	public function folders() {
159
		$folders = $this->trackBusinessLayer->findAllFolders($this->userId, $this->userFolder);
160
		return new JSONResponse($folders);
161
	}
162
163
	/**
164
	 * @NoAdminRequired
165
	 * @NoCSRFRequired
166
	 */
167
	public function genres() {
168
		$genres = $this->genreBusinessLayer->findAllWithTrackIds($this->userId);
169
		$unscanned =  $this->trackBusinessLayer->findFilesWithoutScannedGenre($this->userId);
170
		return new JSONResponse([
171
			'genres' => \array_map(function($g) {return $g->toApi();}, $genres),
172
			'unscanned' => $unscanned
173
		]);
174
	}
175
176
	/**
177
	 * @NoAdminRequired
178
	 * @NoCSRFRequired
179
	 */
180 3
	public function artists($fulltree, $albums) {
181 3
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
182 3
		$includeAlbums = \filter_var($albums, FILTER_VALIDATE_BOOLEAN);
183
		/** @var Artist[] $artists */
184 3
		$artists = $this->artistBusinessLayer->findAll($this->userId);
185
186
		$artists = \array_map(function($a) use ($fulltree, $includeAlbums) {
187 3
			return $this->artistToApi($a, $includeAlbums || $fulltree, $fulltree);
188 3
		}, $artists);
189
190 3
		return new JSONResponse($artists);
191
	}
192
193
	/**
194
	 * @NoAdminRequired
195
	 * @NoCSRFRequired
196
	 */
197 2
	public function artist($artistIdOrSlug, $fulltree) {
198 2
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
199 2
		$artistId = $this->getIdFromSlug($artistIdOrSlug);
200
		/** @var Artist $artist */
201 2
		$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
202 2
		$artist = $this->artistToApi($artist, $fulltree, $fulltree);
203 2
		return new JSONResponse($artist);
204
	}
205
206
	/**
207
	 * Return given artist in Shia API format
208
	 * @param Artist $artist
209
	 * @param boolean $includeAlbums
210
	 * @param boolean $includeTracks (ignored if $includeAlbums==false)
211
	 * @return array
212
	 */
213 5
	private function artistToApi($artist, $includeAlbums, $includeTracks) {
214 5
		$artistInApi = $artist->toAPI($this->urlGenerator, $this->l10n);
215 5
		if ($includeAlbums) {
216 3
			$artistId = $artist->getId();
217 3
			$albums = $this->albumBusinessLayer->findAllByArtist($artistId, $this->userId);
218
219
			$artistInApi['albums'] = \array_map(function($a) use ($includeTracks) {
220 3
				return $this->albumToApi($a, $includeTracks, false);
221 3
			}, $albums);
222
		}
223 5
		return $artistInApi;
224
	}
225
226
	/**
227
	 * @NoAdminRequired
228
	 * @NoCSRFRequired
229
	 */
230 2
	public function albums($artist, $fulltree) {
231 2
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
232 2
		if ($artist) {
233
			$albums = $this->albumBusinessLayer->findAllByArtist($artist, $this->userId);
234
		} else {
235 2
			$albums = $this->albumBusinessLayer->findAll($this->userId);
236
		}
237
238
		$albums = \array_map(function($a) use ($fulltree) {
239 2
			return $this->albumToApi($a, $fulltree, $fulltree);
240 2
		}, $albums);
241
242 2
		return new JSONResponse($albums);
243
	}
244
245
	/**
246
	 * @NoAdminRequired
247
	 * @NoCSRFRequired
248
	 */
249 2
	public function album($albumIdOrSlug, $fulltree) {
250 2
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
251 2
		$albumId = $this->getIdFromSlug($albumIdOrSlug);
252 2
		$album = $this->albumBusinessLayer->find($albumId, $this->userId);
253 2
		$album = $this->albumToApi($album, $fulltree, $fulltree);
254 2
		return new JSONResponse($album);
255
	}
256
257
	/**
258
	 * Return given album in the Shiva API format
259
	 * @param Album $album
260
	 * @param boolean $includeTracks
261
	 * @param boolean $includeAlbums
262
	 * @return array
263
	 */
264 7
	private function albumToApi($album, $includeTracks, $includeArtists) {
265 7
		$albumInApi = $album->toAPI($this->urlGenerator, $this->l10n);
266
267 7
		if ($includeTracks) {
268 4
			$albumId = $album->getId();
269 4
			$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId);
270
			$albumInApi['tracks'] = \array_map(function($t) {
271 4
				return $t->toAPI($this->urlGenerator);
272 4
			}, $tracks);
273
		}
274
275 7
		if ($includeArtists) {
276 2
			$artistIds = $album->getArtistIds();
277 2
			$artists = $this->artistBusinessLayer->findById($artistIds, $this->userId);
278 2
			$albumInApi['artists'] = \array_map(function($a) {
279 2
				return $a->toAPI($this->urlGenerator, $this->l10n);
280 2
			}, $artists);
281
		}
282
283 7
		return $albumInApi;
284
	}
285
286
	/**
287
	 * @NoAdminRequired
288
	 * @NoCSRFRequired
289
	 */
290 4
	public function tracks($artist, $album, $fulltree) {
291 4
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
292 4
		if ($artist) {
293 1
			$tracks = $this->trackBusinessLayer->findAllByArtist($artist, $this->userId);
294 3
		} elseif ($album) {
295 1
			$tracks = $this->trackBusinessLayer->findAllByAlbum($album, $this->userId);
296
		} else {
297 2
			$tracks = $this->trackBusinessLayer->findAll($this->userId);
298
		}
299 4
		foreach ($tracks as &$track) {
300 4
			$artistId = $track->getArtistId();
301 4
			$albumId = $track->getAlbumId();
302 4
			$track = $track->toAPI($this->urlGenerator);
303 4
			if ($fulltree) {
304
				/** @var Artist $artist */
305 1
				$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
306 1
				$track['artist'] = $artist->toAPI($this->urlGenerator, $this->l10n);
307 1
				$album = $this->albumBusinessLayer->find($albumId, $this->userId);
308 4
				$track['album'] = $album->toAPI($this->urlGenerator, $this->l10n);
309
			}
310
		}
311 4
		return new JSONResponse($tracks);
312
	}
313
314
	/**
315
	 * @NoAdminRequired
316
	 * @NoCSRFRequired
317
	 */
318 1
	public function track($trackIdOrSlug) {
319 1
		$trackId = $this->getIdFromSlug($trackIdOrSlug);
320
		/** @var Track $track */
321 1
		$track = $this->trackBusinessLayer->find($trackId, $this->userId);
322 1
		return new JSONResponse($track->toAPI($this->urlGenerator));
323
	}
324
325
	/**
326
	 * @NoAdminRequired
327
	 * @NoCSRFRequired
328
	 */
329 1
	public function trackByFileId($fileId) {
330 1
		$track = $this->trackBusinessLayer->findByFileId($fileId, $this->userId);
331 1
		if ($track !== null) {
332 1
			$track->setAlbum($this->albumBusinessLayer->find($track->getAlbumId(), $this->userId));
333 1
			$track->setArtist($this->artistBusinessLayer->find($track->getArtistId(), $this->userId));
334 1
			return new JSONResponse($track->toCollection($this->l10n));
335
		} else {
336
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
337
		}
338
	}
339
340
	/**
341
	 * @NoAdminRequired
342
	 * @NoCSRFRequired
343
	 */
344
	public function getScanState() {
345
		return new JSONResponse([
346
			'unscannedFiles' => $this->scanner->getUnscannedMusicFileIds($this->userId),
347
			'scannedCount' => $this->trackBusinessLayer->count($this->userId)
348
		]);
349
	}
350
351
	/**
352
	 * @NoAdminRequired
353
	 * @UseSession to keep the session reserved while execution in progress
354
	 */
355
	public function scan($files, $finalize) {
356
		// extract the parameters
357
		$fileIds = \array_map('intval', \explode(',', $files));
358
		$finalize = \filter_var($finalize, FILTER_VALIDATE_BOOLEAN);
359
360
		$filesScanned = $this->scanner->scanFiles($this->userId, $this->userFolder, $fileIds);
361
362
		$coversUpdated = false;
363
		if ($finalize) {
364
			$coversUpdated = $this->scanner->findCovers($this->userId);
365
			$totalCount = $this->trackBusinessLayer->count($this->userId);
366
			$this->logger->log("Scanning finished, user $this->userId has $totalCount scanned tracks in total", 'info');
367
		}
368
369
		return new JSONResponse([
370
			'filesScanned' => $filesScanned,
371
			'coversUpdated' => $coversUpdated
372
		]);
373
	}
374
375
	/**
376
	 * @NoAdminRequired
377
	 * @UseSession to keep the session reserved while execution in progress
378
	 */
379
	public function resetScanned() {
380
		$this->maintenance->resetDb($this->userId);
381
		return new JSONResponse(['success' => true]);
382
	}
383
384
	/**
385
	 * @NoAdminRequired
386
	 * @NoCSRFRequired
387
	 */
388
	public function download($fileId) {
389
		$track = $this->trackBusinessLayer->findByFileId($fileId, $this->userId);
390
		if ($track === null) {
391
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'track not found');
392
		}
393
394
		$nodes = $this->userFolder->getById($track->getFileId());
395
		if (\count($nodes) > 0) {
396
			// get the first valid node
397
			$node = $nodes[0];
398
399
			$mime = $node->getMimeType();
400
			$content = $node->getContent();
401
			return new FileResponse(['mimetype' => $mime, 'content' => $content]);
402
		}
403
404
		return new ErrorResponse(Http::STATUS_NOT_FOUND, 'file not found');
405
	}
406
407
	/**
408
	 * @NoAdminRequired
409
	 * @NoCSRFRequired
410
	 */
411
	public function filePath($fileId) {
412
		$nodes = $this->userFolder->getById($fileId);
413
		if (\count($nodes) == 0) {
414
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
415
		} else {
416
			$node = $nodes[0];
417
			$path = $this->userFolder->getRelativePath($node->getPath());
418
			// URL encode each part of the file path
419
			$path = \join('/', \array_map('rawurlencode', \explode('/', $path)));
420
			return new JSONResponse(['path' => $path]);
421
		}
422
	}
423
424
	/**
425
	 * @NoAdminRequired
426
	 * @NoCSRFRequired
427
	 */
428
	public function fileInfo($fileId) {
429
		$info = $this->scanner->getFileInfo($fileId, $this->userId, $this->userFolder);
430
		if ($info) {
431
			return new JSONResponse($info);
432
		} else {
433
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
434
		}
435
	}
436
437
	/**
438
	 * @NoAdminRequired
439
	 * @NoCSRFRequired
440
	 */
441
	public function fileDetails($fileId) {
442
		$details = $this->detailsHelper->getDetails($fileId, $this->userFolder);
443
		if ($details) {
444
			return new JSONResponse($details);
445
		} else {
446
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
447
		}
448
	}
449
450
	/**
451
	 * @NoAdminRequired
452
	 * @NoCSRFRequired
453
	 */
454
	public function cover($albumIdOrSlug) {
455
		$albumId = $this->getIdFromSlug($albumIdOrSlug);
456
		$coverAndHash = $this->coverHelper->getCoverAndHash($albumId, $this->userId, $this->userFolder);
457
458
		if ($coverAndHash['hash'] !== null) {
459
			// Cover is in cache. Return a redirection response so that the client
460
			// will fetch the content through a cacheable route.
461
			$link = $this->urlGenerator->linkToRoute('music.api.cachedCover', ['hash' => $coverAndHash['hash']]);
462
			return new RedirectResponse($link);
463
		} else if ($coverAndHash['data'] !== null) {
464
			return new FileResponse($coverAndHash['data']);
465
		} else {
466
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
467
		}
468
	}
469
470
	/**
471
	 * @NoAdminRequired
472
	 * @NoCSRFRequired
473
	 */
474
	public function cachedCover($hash) {
475
		$coverData = $this->coverHelper->getCoverFromCache($hash, $this->userId);
476
477
		if ($coverData !== null) {
478
			$response =  new FileResponse($coverData);
479
			// instruct also the client-side to cache the result, this is safe
480
			// as the resource URI contains the image hash
481
			self::setClientCaching($response);
482
			return $response;
483
		} else {
484
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
485
		}
486
	}
487
488
	private static function setClientCaching(&$httpResponse, $days=365) {
489
		$httpResponse->cacheFor($days * 24 * 60 * 60);
490
		$httpResponse->addHeader('Pragma', 'cache');
491
	}
492
}
493