Passed
Push — master ( 9f6f8b...b68386 )
by Pauli
03:11 queued 16s
created

ApiController::collection()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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