Completed
Pull Request — master (#777)
by Pauli
14:15 queued 12:07
created

ApiController::album()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

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