Passed
Push — feature/playlist_improvements ( 417478...9b747b )
by Pauli
14:15
created

PlaylistApiController::parseFile()   A

Complexity

Conditions 2
Paths 4

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 12
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 15
rs 9.8666
ccs 0
cts 3
cp 0
crap 6
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\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...
20
21
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...
22
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...
23
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...
24
25
use \OCA\Music\AppFramework\BusinessLayer\BusinessLayerException;
26
use \OCA\Music\AppFramework\Core\Logger;
27
use \OCA\Music\BusinessLayer\AlbumBusinessLayer;
28
use \OCA\Music\BusinessLayer\ArtistBusinessLayer;
29
use \OCA\Music\BusinessLayer\PlaylistBusinessLayer;
30
use \OCA\Music\BusinessLayer\TrackBusinessLayer;
31
use \OCA\Music\Db\Playlist;
32
use \OCA\Music\Http\ErrorResponse;
33
use \OCA\Music\Utility\APISerializer;
34
use \OCA\Music\Utility\PlaylistFileService;
35
use \OCA\Music\Utility\Util;
36
37
class PlaylistApiController extends Controller {
38
	private $urlGenerator;
39
	private $playlistBusinessLayer;
40
	private $artistBusinessLayer;
41
	private $albumBusinessLayer;
42
	private $trackBusinessLayer;
43
	private $playlistFileService;
44
	private $userId;
45
	private $userFolder;
46
	private $l10n;
47
	private $logger;
48
49
	public function __construct($appname,
50
								IRequest $request,
51
								IURLGenerator $urlGenerator,
52
								PlaylistBusinessLayer $playlistBusinessLayer,
53
								ArtistBusinessLayer $artistBusinessLayer,
54
								AlbumBusinessLayer $albumBusinessLayer,
55
								TrackBusinessLayer $trackBusinessLayer,
56
								PlaylistFileService $playlistFileService,
57
								$userId,
58
								Folder $userFolder,
59
								$l10n,
60
								Logger $logger) {
61
		parent::__construct($appname, $request);
62
		$this->urlGenerator = $urlGenerator;
63
		$this->playlistBusinessLayer = $playlistBusinessLayer;
64
		$this->artistBusinessLayer = $artistBusinessLayer;
65
		$this->albumBusinessLayer = $albumBusinessLayer;
66
		$this->trackBusinessLayer = $trackBusinessLayer;
67
		$this->playlistFileService = $playlistFileService;
68
		$this->userId = $userId;
69
		$this->userFolder = $userFolder;
70
		$this->l10n = $l10n;
71
		$this->logger = $logger;
72
	}
73
74
	/**
75
	 * lists all playlists
76
	 *
77
	 * @NoAdminRequired
78
	 * @NoCSRFRequired
79
	 */
80
	public function getAll() {
81
		$playlists = $this->playlistBusinessLayer->findAll($this->userId);
82
		$serializer = new APISerializer();
83
84
		return $serializer->serialize($playlists);
85
	}
86
87
	/**
88
	 * creates a playlist
89
	 *
90
	 * @NoAdminRequired
91
	 * @NoCSRFRequired
92
	 */
93
	public function create($name, $trackIds) {
94
		$playlist = $this->playlistBusinessLayer->create($name, $this->userId);
95
96
		// add trackIds to the newly created playlist if provided
97
		if (!empty($trackIds)) {
98
			$playlist = $this->playlistBusinessLayer->addTracks(
99
					self::toIntArray($trackIds), $playlist->getId(), $this->userId);
100
		}
101
102
		return $playlist->toAPI();
103
	}
104
105
	/**
106
	 * deletes a playlist
107
	 * @param  int $id playlist ID
108
	 *
109
	 * @NoAdminRequired
110
	 * @NoCSRFRequired
111
	 */
112
	public function delete($id) {
113
		$this->playlistBusinessLayer->delete($id, $this->userId);
114
		return [];
115
	}
116
117
	/**
118
	 * lists a single playlist
119
	 * @param  int $id playlist ID
120
	 *
121
	 * @NoAdminRequired
122
	 * @NoCSRFRequired
123
	 */
124
	public function get($id, $fulltree) {
125
		try {
126
			$playlist = $this->playlistBusinessLayer->find($id, $this->userId);
127
128
			$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
129
			if ($fulltree) {
130
				return $this->toFullTree($playlist);
131
			} else {
132
				return $playlist->toAPI();
133
			}
134
		} catch (BusinessLayerException $ex) {
135
			return new ErrorResponse(Http::STATUS_NOT_FOUND, $ex->getMessage());
136
		}
137
	}
138
139
	private function toFullTree($playlist) {
140
		$songs = [];
141
142
		// Get all track information for all the tracks of the playlist
143
		foreach ($playlist->getTrackIdsAsArray() as $trackId) {
144
			$song = $this->trackBusinessLayer->find($trackId, $this->userId);
145
			$song->setAlbum($this->albumBusinessLayer->find($song->getAlbumId(), $this->userId));
146
			$song->setArtist($this->artistBusinessLayer->find($song->getArtistId(), $this->userId));
147
			$songs[] = $song->toAPI($this->urlGenerator);
148
		}
149
150
		$result = $playlist->toAPI();
151
		unset($result['trackIds']);
152
		$result['tracks'] = $songs;
153
154
		return $result;
155
	}
156
157
	/**
158
	 * update a playlist
159
	 * @param int $id playlist ID
160
	 * @param string|null $name
161
	 * @param string|null $comment
162
	 *
163
	 * @NoAdminRequired
164
	 * @NoCSRFRequired
165
	 */
166
	public function update($id, $name, $comment) {
167
		$result = null;
168
		if ($name !== null) {
169
			$result = $this->modifyPlaylist('rename', [$name, $id, $this->userId]); 
170
		}
171
		if ($comment !== null) {
172
			$result = $this->modifyPlaylist('setComment', [$comment, $id, $this->userId]);
173
		}
174
		if ($result === null) {
175
			$result = new ErrorResponse(Http::STATUS_BAD_REQUEST, "at least one of the args ['name', 'comment'] must be given");
176
		}
177
		return $result;
178
	}
179
180
	/**
181
	 * add tracks to a playlist
182
	 * @param  int $id playlist ID
183
	 *
184
	 * @NoAdminRequired
185
	 * @NoCSRFRequired
186
	 */
187
	public function addTracks($id, $trackIds) {
188
		return $this->modifyPlaylist('addTracks', [self::toIntArray($trackIds), $id, $this->userId]);
189
	}
190
191
	/**
192
	 * removes tracks from a playlist
193
	 * @param  int $id playlist ID
194
	 *
195
	 * @NoAdminRequired
196
	 * @NoCSRFRequired
197
	 */
198
	public function removeTracks($id, $indices) {
199
		return $this->modifyPlaylist('removeTracks', [self::toIntArray($indices), $id, $this->userId]);
200
	}
201
202
	/**
203
	 * moves single track on playlist to a new position
204
	 * @param  int $id playlist ID
205
	 *
206
	 * @NoAdminRequired
207
	 * @NoCSRFRequired
208
	 */
209
	public function reorder($id, $fromIndex, $toIndex) {
210
		return $this->modifyPlaylist('moveTrack',
211
				[$fromIndex, $toIndex, $id, $this->userId]);
212
	}
213
214
	/**
215
	 * export the playlist to a file
216
	 * @param int $id playlist ID
217
	 * @param string $path parent folder path
218
	 * @param string $oncollision action to take on file name collision,
219
	 *								supported values:
220
	 *								- 'overwrite' The existing file will be overwritten
221
	 *								- 'keepboth' The new file is named with a suffix to make it unique
222
	 *								- 'abort' (default) The operation will fail
223
	 *
224
	 * @NoAdminRequired
225
	 * @NoCSRFRequired
226
	 */
227
	public function exportToFile($id, $path, $oncollision) {
228
		try {
229
			$exportedFilePath = $this->playlistFileService->exportToFile($id, $path, $oncollision);
230
			return new JSONResponse(['wrote_to_file' => $exportedFilePath]);
231
		}
232
		catch (BusinessLayerException $ex) {
233
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'playlist not found');
234
		}
235
		catch (\OCP\Files\NotFoundException $ex) {
0 ignored issues
show
Bug introduced by
The type OCP\Files\NotFoundException 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...
236
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'folder not found');
237
		}
238
		catch (\RuntimeException $ex) {
239
			return new ErrorResponse(Http::STATUS_CONFLICT, $ex->getMessage());
240
		}
241
		catch (\OCP\Files\NotPermittedException $ex) {
0 ignored issues
show
Bug introduced by
The type OCP\Files\NotPermittedException 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...
242
			return new ErrorResponse(Http::STATUS_FORBIDDEN, 'user is not allowed to write to the target file');
243
		}
244
	}
245
246
	/**
247
	 * import playlist contents from a file
248
	 * @param int $id playlist ID
249
	 * @param string $filePath path of the file to import
250
	 *
251
	 * @NoAdminRequired
252
	 * @NoCSRFRequired
253
	 */
254
	public function importFromFile($id, $filePath) {
255
		try {
256
			$result = $this->playlistFileService->importFromFile($id, $filePath);
257
			$result['playlist'] = $result['playlist']->toAPI();
258
			return $result;
259
		}
260
		catch (BusinessLayerException $ex) {
261
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'playlist not found');
262
		}
263
		catch (\OCP\Files\NotFoundException $ex) {
264
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'playlist file not found');
265
		}
266
	}
267
268
	/**
269
	 * read and parse a playlist file
270
	 * @param int $fileId ID of the file to parse
271
	 *
272
	 * @NoAdminRequired
273
	 * @NoCSRFRequired
274
	 */
275
	public function parseFile($fileId) {
276
		try {
277
			$result = $this->playlistFileService->parseFile($fileId);
278
			$result['files'] = \array_map(function($file) {
279
				return [
280
					'id' => $file->getId(),
281
					'name' => $file->getName(),
282
					'path' => Util::urlEncodePath($this->userFolder->getRelativePath($file->getPath())),
283
					'mimetype' => $file->getMimeType()
284
				];
285
			}, $result['files']);
286
			return new JSONResponse($result);
287
		}
288
		catch (\OCP\Files\NotFoundException $ex) {
289
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'playlist file not found');
290
		}
291
	}
292
293
	/**
294
	 * Modify playlist by calling a supplied method from PlaylistBusinessLayer
295
	 * @param string $funcName  Name of a function to call from PlaylistBusinessLayer
296
	 * @param array $funcParams Parameters to pass to the function 'funcName'
297
	 * @return \OCP\AppFramework\Http\JSONResponse JSON representation of the modified playlist
298
	 */
299
	private function modifyPlaylist($funcName, $funcParams) {
300
		try {
301
			$playlist = \call_user_func_array([$this->playlistBusinessLayer, $funcName], $funcParams);
302
			return $playlist->toAPI();
303
		} catch (BusinessLayerException $ex) {
304
			return new ErrorResponse(Http::STATUS_NOT_FOUND, $ex->getMessage());
305
		}
306
	}
307
308
	/**
309
	 * Get integer array passed as parameter to the Playlist API
310
	 * @param string $listAsString Comma-separated integer values in string
311
	 * @return int[]
312
	 */
313
	private static function toIntArray($listAsString) {
314
		return \array_map('intval', \explode(',', $listAsString));
315
	}
316
}
317