Completed
Push — scalability_improvements ( 1f54dd...dcfdac )
by Pauli
19:38 queued 05:25
created

ApiController   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 388
Duplicated Lines 11.6 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 48
lcom 1
cbo 9
dl 45
loc 388
rs 8.4864
c 2
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 24 1
A getIdFromSlug() 0 5 1
A collection() 0 9 2
C buildCollectionJson() 0 46 7
C artists() 0 26 7
B artist() 22 22 4
B albums() 0 22 5
B album() 23 23 4
B tracks() 0 23 5
A track() 0 6 1
A trackByFileId() 0 7 1
A getScanState() 0 6 1
A scan() 0 19 2
B download() 0 28 3
B cover() 0 33 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ApiController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ApiController, and based on these observations, apply Extract Interface, too.

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
 * @copyright Morris Jobke 2013, 2014
11
 */
12
13
namespace OCA\Music\Controller;
14
15
use OCA\Music\Db\Artist;
16
use OCA\Music\Db\Cache;
17
use OCA\Music\Db\Track;
18
use \OCP\AppFramework\Controller;
19
use \OCP\AppFramework\Http;
20
use \OCP\AppFramework\Http\DataDisplayResponse;
21
use \OCP\AppFramework\Http\JSONResponse;
22
use \OCP\AppFramework\Http\Response;
23
use \OCP\Files\Folder;
24
use \OCP\IL10N;
25
use \OCP\IRequest;
26
use \OCP\IURLGenerator;
27
28
use \OCP\AppFramework\Db\DoesNotExistException;
29
30
use \OCA\Music\BusinessLayer\TrackBusinessLayer;
31
use \OCA\Music\BusinessLayer\ArtistBusinessLayer;
32
use \OCA\Music\BusinessLayer\AlbumBusinessLayer;
33
use \OCA\Music\Http\FileResponse;
34
use \OCA\Music\Utility\Scanner;
35
36
37
class ApiController extends Controller {
38
39
	/** @var IL10N */
40
	private $l10n;
41
	/** @var TrackBusinessLayer */
42
	private $trackBusinessLayer;
43
	/** @var ArtistBusinessLayer */
44
	private $artistBusinessLayer;
45
	/** @var AlbumBusinessLayer */
46
	private $albumBusinessLayer;
47
	/** @var Cache */
48
	private $cache;
49
	/** @var Scanner */
50
	private $scanner;
51
	/** @var string */
52
	private $userId;
53
	/** @var IURLGenerator */
54
	private $urlGenerator;
55
	/** @var Folder */
56
	private $userFolder;
57
	/** @var Logger */
58
	private $logger;
59
60
	public function __construct($appname,
61
								IRequest $request,
62
								IURLGenerator $urlGenerator,
63
								TrackBusinessLayer $trackbusinesslayer,
64
								ArtistBusinessLayer $artistbusinesslayer,
65
								AlbumBusinessLayer $albumbusinesslayer,
66
								Cache $cache,
67
								Scanner $scanner,
68
								$userId,
69
								$l10n,
70
								Folder $userFolder,
71
								Logger $logger){
72
		parent::__construct($appname, $request);
73
		$this->l10n = $l10n;
74
		$this->trackBusinessLayer = $trackbusinesslayer;
75
		$this->artistBusinessLayer = $artistbusinesslayer;
76
		$this->albumBusinessLayer = $albumbusinesslayer;
77
		$this->cache = $cache;
78
		$this->scanner = $scanner;
79
		$this->userId = $userId;
80
		$this->urlGenerator = $urlGenerator;
81
		$this->userFolder = $userFolder;
82
		$this->logger = $logger;
83
	}
84
85
	/**
86
	 * Extracts the id from an unique slug (id-slug)
87
	 * @param string $slug the slug
88
	 * @return string the id
89
	 */
90
	protected function getIdFromSlug($slug){
91
		$split = explode('-', $slug, 2);
92
93
		return $split[0];
94
	}
95
96
	/**
97
	 * @NoAdminRequired
98
	 * @NoCSRFRequired
99
	 */
100
	public function collection() {
101
		$collectionJson = $this->cache->get($this->userId, 'collection');
102
103
		if ($collectionJson == null) {
104
			$collectionJson = $this->buildCollectionJson();
105
			$this->cache->add($this->userId, 'collection', $collectionJson);
106
		}
107
		return new DataDisplayResponse($collectionJson);
108
	}
109
110
	private function buildCollectionJson() {
111
		/** @var Artist[] $allArtists */
112
		$allArtists = $this->artistBusinessLayer->findAll($this->userId);
113
		$allArtistsByIdAsObj = array();
114
		$allArtistsByIdAsArr = array();
115
		foreach ($allArtists as &$artist) {
116
			$allArtistsByIdAsObj[$artist->getId()] = $artist;
117
			$allArtistsByIdAsArr[$artist->getId()] = $artist->toCollection($this->l10n);
118
		}
119
		
120
		$allAlbums = $this->albumBusinessLayer->findAll($this->userId);
121
		$allAlbumsByIdAsObj = array();
122
		$allAlbumsByIdAsArr = array();
123
		foreach ($allAlbums as &$album) {
124
			$allAlbumsByIdAsObj[$album->getId()] = $album;
125
			$allAlbumsByIdAsArr[$album->getId()] = $album->toCollection($this->urlGenerator, $this->l10n);
126
		}
127
128
		/** @var Track[] $allTracks */
129
		$allTracks = $this->trackBusinessLayer->findAll($this->userId);
130
131
		$artists = array();
132
		foreach ($allTracks as $track) {
133
			$albumObj = $allAlbumsByIdAsObj[$track->getAlbumId()];
134
			$trackArtistObj = $allArtistsByIdAsObj[$track->getArtistId()];
135
			$track->setAlbum($albumObj);
136
			$track->setArtist($trackArtistObj);
137
138
			$albumArtist = &$allArtistsByIdAsArr[$albumObj->getAlbumArtistId()];
139
			if (!isset($albumArtist['albums'])) {
140
				$albumArtist['albums'] = array();
141
				$artists[] = &$albumArtist;
142
			}
143
			$album = &$allAlbumsByIdAsArr[$track->getAlbumId()];
144
			if (!isset($album['tracks'])) {
145
				$album['tracks'] = array();
146
				$albumArtist['albums'][] = &$album;
147
			}
148
			try {
149
				$album['tracks'][] = $track->toCollection($this->urlGenerator, $this->userFolder, $this->l10n);
150
			} catch (\OCP\Files\NotFoundException $e) {
151
				//ignore not found
152
			}
153
		}
154
		return json_encode($artists);
155
	}
156
157
	/**
158
	 * @NoAdminRequired
159
	 * @NoCSRFRequired
160
	 */
161
	public function artists() {
162
		$fulltree = filter_var($this->params('fulltree'), FILTER_VALIDATE_BOOLEAN);
163
		$includeAlbums = filter_var($this->params('albums'), FILTER_VALIDATE_BOOLEAN);
164
		/** @var Artist[] $artists */
165
		$artists = $this->artistBusinessLayer->findAll($this->userId);
166
		foreach($artists as &$artist) {
167
			$artist = $artist->toAPI($this->urlGenerator, $this->l10n);
168
			if($fulltree || $includeAlbums) {
169
				$artistId = $artist['id'];
170
				$albums = $this->albumBusinessLayer->findAllByArtist($artistId, $this->userId);
171
				foreach($albums as &$album) {
172
					$album = $album->toAPI($this->urlGenerator, $this->l10n);
173
					if($fulltree) {
174
						$albumId = $album['id'];
175
						$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId, $artistId);
176
						foreach($tracks as &$track) {
177
							$track = $track->toAPI($this->urlGenerator);
178
						}
179
						$album['tracks'] = $tracks;
180
					}
181
				}
182
				$artist['albums'] = $albums;
183
			}
184
		}
185
		return new JSONResponse($artists);
186
	}
187
188
	/**
189
	 * @NoAdminRequired
190
	 * @NoCSRFRequired
191
	 */
192 View Code Duplication
	public function artist() {
193
		$fulltree = filter_var($this->params('fulltree'), FILTER_VALIDATE_BOOLEAN);
194
		$artistId = $this->getIdFromSlug($this->params('artistIdOrSlug'));
195
		/** @var Artist $artist */
196
		$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
197
		$artist = $artist->toAPI($this->urlGenerator, $this->l10n);
198
		if($fulltree) {
199
			$artistId = $artist['id'];
200
			$albums = $this->albumBusinessLayer->findAllByArtist($artistId, $this->userId);
201
			foreach($albums as &$album) {
202
				$album = $album->toAPI($this->urlGenerator, $this->l10n);
203
				$albumId = $album['id'];
204
				$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId, $artistId);
205
				foreach($tracks as &$track) {
206
					$track = $track->toAPI($this->urlGenerator);
207
				}
208
				$album['tracks'] = $tracks;
209
			}
210
			$artist['albums'] = $albums;
211
		}
212
		return new JSONResponse($artist);
213
	}
214
215
	/**
216
	 * @NoAdminRequired
217
	 * @NoCSRFRequired
218
	 */
219
	public function albums() {
220
		$fulltree = filter_var($this->params('fulltree'), FILTER_VALIDATE_BOOLEAN);
221
		$albums = $this->albumBusinessLayer->findAll($this->userId);
222
		foreach($albums as &$album) {
223
			$artistIds = $album->getArtistIds();
224
			$album = $album->toAPI($this->urlGenerator, $this->l10n);
225
			if($fulltree) {
226
				$albumId = $album['id'];
227
				$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId);
228
				foreach($tracks as &$track) {
229
					$track = $track->toAPI($this->urlGenerator);
230
				}
231
				$album['tracks'] = $tracks;
232
				$artists = $this->artistBusinessLayer->findMultipleById($artistIds, $this->userId);
233
				foreach($artists as &$artist) {
234
					$artist = $artist->toAPI($this->urlGenerator, $this->l10n);
235
				}
236
				$album['artists'] = $artists;
237
			}
238
		}
239
		return new JSONResponse($albums);
240
	}
241
242
	/**
243
	 * @NoAdminRequired
244
	 * @NoCSRFRequired
245
	 */
246 View Code Duplication
	public function album() {
247
		$fulltree = filter_var($this->params('fulltree'), FILTER_VALIDATE_BOOLEAN);
248
		$albumId = $this->getIdFromSlug($this->params('albumIdOrSlug'));
249
		$album = $this->albumBusinessLayer->find($albumId, $this->userId);
250
251
		$artistIds = $album->getArtistIds();
252
		$album = $album->toAPI($this->urlGenerator, $this->l10n);
253
		if($fulltree) {
254
			$albumId = $album['id'];
255
			$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId);
256
			foreach($tracks as &$track) {
257
				$track = $track->toAPI($this->urlGenerator);
258
			}
259
			$album['tracks'] = $tracks;
260
			$artists = $this->artistBusinessLayer->findMultipleById($artistIds, $this->userId);
261
			foreach($artists as &$artist) {
262
				$artist = $artist->toAPI($this->urlGenerator, $this->l10n);
263
			}
264
			$album['artists'] = $artists;
265
		}
266
267
		return new JSONResponse($album);
268
	}
269
270
	/**
271
	 * @NoAdminRequired
272
	 * @NoCSRFRequired
273
	 */
274
	public function tracks() {
275
		$fulltree = filter_var($this->params('fulltree'), FILTER_VALIDATE_BOOLEAN);
276
		if($artistId = $this->params('artist')) {
277
			$tracks = $this->trackBusinessLayer->findAllByArtist($artistId, $this->userId);
278
		} elseif($albumId = $this->params('album')) {
279
			$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId);
280
		} else {
281
			$tracks = $this->trackBusinessLayer->findAll($this->userId);
282
		}
283
		foreach($tracks as &$track) {
284
			$artistId = $track->getArtistId();
285
			$albumId = $track->getAlbumId();
286
			$track = $track->toAPI($this->urlGenerator);
287
			if($fulltree) {
288
				/** @var Artist $artist */
289
				$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
290
				$track['artist'] = $artist->toAPI($this->urlGenerator, $this->l10n);
291
				$album = $this->albumBusinessLayer->find($albumId, $this->userId);
292
				$track['album'] = $album->toAPI($this->urlGenerator, $this->l10n);
293
			}
294
		}
295
		return new JSONResponse($tracks);
296
	}
297
298
	/**
299
	 * @NoAdminRequired
300
	 * @NoCSRFRequired
301
	 */
302
	public function track() {
303
		$trackId = $this->getIdFromSlug($this->params('trackIdOrSlug'));
304
		/** @var Track $track */
305
		$track = $this->trackBusinessLayer->find($trackId, $this->userId);
306
		return new JSONResponse($track->toAPI($this->urlGenerator));
307
	}
308
309
	/**
310
	 * @NoAdminRequired
311
	 * @NoCSRFRequired
312
	 */
313
	public function trackByFileId() {
314
		$fileId = $this->params('fileId');
315
		$track = $this->trackBusinessLayer->findByFileId($fileId, $this->userId);
316
		$track->setAlbum($this->albumBusinessLayer->find($track->getAlbumId(), $this->userId));
317
		$track->setArtist($this->artistBusinessLayer->find($track->getArtistId(), $this->userId));
318
		return new JSONResponse($track->toCollection($this->urlGenerator, $this->userFolder, $this->l10n));
319
	}
320
321
	/**
322
	 * @NoAdminRequired
323
	 */
324
	public function getScanState() {
325
		return new JSONResponse([
326
			'unscannedFiles' => $this->scanner->getUnscannedMusicFileIds($this->userId, $this->userFolder),
327
			'scannedCount' => count($this->scanner->getScannedFiles($this->userId))
328
		]);
329
	}
330
331
	/**
332
	 * @NoAdminRequired
333
	 */
334
	public function scan() {
335
		// extract the parameters
336
		$fileIds = array_map('intval', explode(',', $this->params('files')));
337
		$finalize = filter_var($this->params('finalize'), FILTER_VALIDATE_BOOLEAN);
338
339
		$filesScanned = $this->scanner->scanFiles($this->userId, $this->userFolder, $fileIds);
340
341
		$coversUpdated = false;
342
		if ($finalize) {
343
			$coversUpdated = $this->scanner->findCovers();
344
			$totalCount = count($this->scanner->getScannedFiles($this->userId));
345
			$this->logger->log("Scanning finished, user $this->userId has $totalCount scanned tracks in total", 'info');
346
		}
347
348
		return new JSONResponse([
349
			'filesScanned' => $filesScanned,
350
			'coversUpdated' => $coversUpdated
351
		]);
352
	}
353
354
	/**
355
	 * @NoAdminRequired
356
	 * @NoCSRFRequired
357
	 */
358
	public function download() {
359
		// we no longer need the session to be kept open
360
		session_write_close();
361
362
		$fileId = $this->params('fileId');
363
364
		try {
365
			$track = $this->trackBusinessLayer->findByFileId($fileId, $this->userId);
366
		} catch(DoesNotExistException $e) {
367
			$r = new Response();
368
			$r->setStatus(Http::STATUS_NOT_FOUND);
369
			return $r;
370
		}
371
372
		$nodes = $this->userFolder->getById($track->getFileId());
373
		if(count($nodes) > 0 ) {
374
			// get the first valid node
375
			$node = $nodes[0];
376
377
			$mime = $node->getMimeType();
378
			$content = $node->getContent();
379
			return new FileResponse(array('mimetype' => $mime, 'content' => $content));
380
		}
381
382
		$r = new Response();
383
		$r->setStatus(Http::STATUS_NOT_FOUND);
384
		return $r;
385
	}
386
387
	/**
388
	 * @NoAdminRequired
389
	 * @NoCSRFRequired
390
	 */
391
	public function cover() {
392
		// we no longer need the session to be kept open
393
		session_write_close();
394
395
		$albumId = $this->getIdFromSlug($this->params('albumIdOrSlug'));
396
		$album = $this->albumBusinessLayer->find($albumId, $this->userId);
397
398
		$nodes = $this->userFolder->getById($album->getCoverFileId());
399
		if(count($nodes) > 0 ) {
400
			// get the first valid node
401
			$node = $nodes[0];
402
			$mime = $node->getMimeType();
403
404
			if (0 === strpos($mime, 'audio')) { // embedded cover image
405
				$cover = $this->scanner->parseEmbeddedCoverArt($node);
406
407
				if($cover != null) {
408
					return new FileResponse(array(
409
							'mimetype' => $cover["image_mime"],
410
							'content' => $cover["data"]
411
					));
412
				}
413
			}
414
			else { // separate image file
415
				$content = $node->getContent();
416
				return new FileResponse(array('mimetype' => $mime, 'content' => $content));
417
			}
418
		}
419
420
		$r = new Response();
421
		$r->setStatus(Http::STATUS_NOT_FOUND);
422
		return $r;
423
	}
424
}
425