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

PlaylistApiController::parseFile()   A

Complexity

Conditions 3
Paths 7

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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