Completed
Push — scalability_improvements ( 393404...b3721c )
by Pauli
11:38 queued 10s
created

ApiController   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 364
Duplicated Lines 12.36 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 47
c 2
b 0
f 0
lcom 1
cbo 9
dl 45
loc 364
rs 8.439

14 Methods

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