Passed
Push — master ( d5fa9e...eded98 )
by Pauli
01:56
created

TrackBusinessLayer::addOrUpdateTrack()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 12
dl 0
loc 17
ccs 15
cts 15
cp 1
crap 1
rs 9.7998
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
12
 * @copyright Pauli Järvinen 2016 - 2020
13
 */
14
15
namespace OCA\Music\BusinessLayer;
16
17
use \OCA\Music\AppFramework\BusinessLayer\BusinessLayer;
18
use \OCA\Music\AppFramework\Core\Logger;
19
20
use \OCA\Music\Db\TrackMapper;
21
use \OCA\Music\Db\Track;
22
23
use \OCA\Music\Utility\Util;
24
25
use \OCP\AppFramework\Db\DoesNotExistException;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Db\DoesNotExistException 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
use \OCP\AppFramework\Db\MultipleObjectsReturnedException;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Db\Mult...bjectsReturnedException 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...
27
28
class TrackBusinessLayer extends BusinessLayer {
29
	private $logger;
30
31 7
	public function __construct(TrackMapper $trackMapper, Logger $logger) {
32 7
		parent::__construct($trackMapper);
33 7
		$this->logger = $logger;
34 7
	}
35
36
	/**
37
	 * Returns all tracks filtered by artist
38
	 * @param string $artistId the id of the artist
39
	 * @param string $userId the name of the user
40
	 * @return array of tracks
41
	 */
42 1
	public function findAllByArtist($artistId, $userId) {
43 1
		return $this->mapper->findAllByArtist($artistId, $userId);
44
	}
45
46
	/**
47
	 * Returns all tracks filtered by album
48
	 * @param string $albumId the id of the album
49
	 * @param string $userId the name of the user
50
	 * @return \OCA\Music\Db\Track[] tracks
51
	 */
52 1
	public function findAllByAlbum($albumId, $userId, $artistId = null) {
53 1
		return $this->mapper->findAllByAlbum($albumId, $userId, $artistId);
54
	}
55
56
	/**
57
	 * Returns all tracks filtered by parent folder
58
	 * @param integer $folderId the id of the folder
59
	 * @param string $userId the name of the user
60
	 * @return \OCA\Music\Db\Track[] tracks
61
	 */
62
	public function findAllByFolder($folderId, $userId) {
63
		return $this->mapper->findAllByFolder($folderId, $userId);
64
	}
65
66
	/**
67
	 * Returns all tracks filtered by genre
68
	 * @param int $genreId the genre to include
69
	 * @param string $userId the name of the user
70
	 * @param int|null $limit
71
	 * @param int|null $offset
72
	 * @return \OCA\Music\Db\Track[] tracks
73
	 */
74
	public function findAllByGenre($genreId, $userId, $limit=null, $offset=null) {
75
		return $this->mapper->findAllByGenre($genreId, $userId, $limit, $offset);
76
	}
77
78
	/**
79
	 * Returns all tracks filtered by name (of track/album/artist)
80
	 * @param string $name the name of the track/album/artist
81
	 * @param string $userId the name of the user
82
	 * @return \OCA\Music\Db\Track[] tracks
83
	 */
84
	public function findAllByNameRecursive($name, $userId) {
85
		return $this->mapper->findAllByNameRecursive($name, $userId);
86
	}
87
88
	/**
89
	 * Returns all tracks specified by name and/or artist name
90
	 * @param string|null $name the name of the track
91
	 * @param string|null $artistName the name of the artist
92
	 * @param string $userId the name of the user
93
	 * @return \OCA\Music\Db\Track[] Tracks matching the criteria
94
	 */
95
	public function findAllByNameAndArtistName($name, $artistName, $userId) {
96
		// find exact matches first and then append fuzzy matches which are not exact matches
97
		$strictMatches = $this->mapper->findAllByNameAndArtistName($name, $artistName, false, $userId);
98
		$fuzzyMatches = $this->mapper->findAllByNameAndArtistName($name, $artistName, true, $userId);
99
100
		$matches = \array_merge($strictMatches, $fuzzyMatches);
101
		$matches = \array_unique($matches, SORT_REGULAR);
102
		return $matches;
103
	}
104
105
	/**
106
	 * Returns the track for a file id
107
	 * @param string $fileId the file id of the track
108
	 * @param string $userId the name of the user
109
	 * @return \OCA\Music\Db\Track|null track
110
	 */
111 1
	public function findByFileId($fileId, $userId) {
112
		try {
113 1
			return $this->mapper->findByFileId($fileId, $userId);
114
		} catch (DoesNotExistException $e) {
115
			return null;
116
		}
117
	}
118
119
	/**
120
	 * Returns file IDs of all indexed tracks of the user
121
	 * @param string $userId
122
	 * @return int[]
123
	 */
124
	public function findAllFileIds($userId) {
125
		return $this->mapper->findAllFileIds($userId);
126
	}
127
128
	/**
129
	 * Returns all folders of the user containing indexed tracks, along with the contained track IDs
130
	 * @param string $userId
131
	 * @param Folder $userHome
0 ignored issues
show
Bug introduced by
The type OCA\Music\BusinessLayer\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...
132
	 * @return array of entries like {id: int, name: string, path: string, trackIds: int[]}
133
	 */
134
	public function findAllFolders($userId, $userHome) {
135
		// All tracks of the user, grouped by their parent folders. Some of the parent folders
136
		// may be owned by other users and are invisible to this user (in case of shared files).
137
		$tracksByFolder = $this->mapper->findTrackAndFolderIds($userId);
138
139
		// Get the folder names and paths for ordinary local folders directly from the DB.
140
		// This is significantly more efficient than using the Files API because we need to
141
		// run only single DB query instead of one per folder.
142
		$folderNamesAndPaths = $this->mapper->findNodeNamesAndPaths(
143
				\array_keys($tracksByFolder), $userHome->getStorage()->getId());
144
145
		// root folder has to be handled as a special case because shared files from
146
		// many folders may be shown to this user mapped under the root folder
147
		$rootFolderTracks = [];
148
149
		// Build the final results. Use the previously fetched data for the ordinary
150
		// local folders and query the data through the Files API for the more special cases.
151
		$result = [];
152
		foreach ($tracksByFolder as $folderId => $trackIds) {
153
			if (isset($folderNamesAndPaths[$folderId])) {
154
				// normal folder within the user home storage
155
				$entry = $folderNamesAndPaths[$folderId];
156
				// remove the "files" from the beginning of the folder path
157
				$entry['path'] = \substr($entry['path'], 5);
158
				// special handling for the root folder
159
				if ($entry['path'] === '') {
160
					$entry = null;
161
				}
162
			}
163
			else {
164
				// shared folder or parent folder of a shared file or an externally mounted folder
165
				$folderNode = $userHome->getById($folderId);
166
				if (\count($folderNode) === 0) {
167
					// other user's folder with files shared with this user (mapped under root)
168
					$entry = null;
169
				}
170
				else {
171
					$entry = [
172
						'name' => $folderNode[0]->getName(),
173
						'path' => $userHome->getRelativePath($folderNode[0]->getPath())
174
					];
175
				}
176
			}
177
178
			if ($entry) {
179
				$entry['trackIds'] = $trackIds;
180
				$entry['id'] = $folderId;
181
				$result[] = $entry;
182
			} else {
183
				$rootFolderTracks = \array_merge($rootFolderTracks, $trackIds);
184
			}
185
		}
186
187
		// add the root folder if it contains any tracks
188
		if (!empty($rootFolderTracks)) {
189
			$result[] = [
190
				'name' => '',
191
				'path' => '/',
192
				'trackIds' => $rootFolderTracks,
193
				'id' => $userHome->getId()
194
			];
195
		}
196
197
		return $result;
198
	}
199
200
	/**
201
	 * Returns all genre IDs associated with the given artist
202
	 * @param int $artistId
203
	 * @param string $userId
204
	 * @return int[]
205
	 */
206
	public function getGenresByArtistId($artistId, $userId) {
207
		return $this->mapper->getGenresByArtistId($artistId, $userId);
208
	}
209
210
	/**
211
	 * Returns file IDs of the tracks which do not have genre scanned. This is not the same
212
	 * thing as unknown genre, which is stored as empty string and means that the genre has
213
	 * been scanned but was not found from the track metadata.
214
	 * @param string $userId
215
	 * @return int[]
216
	 */
217
	public function findFilesWithoutScannedGenre($userId) {
218
		return $this->mapper->findFilesWithoutScannedGenre($userId);
219
	}
220
221
	/**
222
	 * @param integer $artistId
223
	 * @return integer
224
	 */
225
	public function countByArtist($artistId) {
226
		return $this->mapper->countByArtist($artistId);
227
	}
228
229
	/**
230
	 * @param integer $albumId
231
	 * @return integer
232
	 */
233
	public function countByAlbum($albumId) {
234
		return $this->mapper->countByAlbum($albumId);
235
	}
236
237
	/**
238
	 * Adds a track if it does not exist already or updates an existing track
239
	 * @param string $title the title of the track
240
	 * @param int|null $number the number of the track
241
	 * @param int|null $discNumber the number of the disc
242
	 * @param int|null $year the year of the release
243
	 * @param int $genreId the genre id of the track
244
	 * @param int $artistId the artist id of the track
245
	 * @param int $albumId the album id of the track
246
	 * @param int $fileId the file id of the track
247
	 * @param string $mimetype the mimetype of the track
248
	 * @param string $userId the name of the user
249
	 * @param int $length track length in seconds
250
	 * @param int $bitrate track bitrate in bits (not kbits)
251
	 * @return \OCA\Music\Db\Track The added/updated track
252
	 */
253 1
	public function addOrUpdateTrack(
254
			$title, $number, $discNumber, $year, $genreId, $artistId, $albumId,
255
			$fileId, $mimetype, $userId, $length=null, $bitrate=null) {
256 1
		$track = new Track();
257 1
		$track->setTitle(Util::truncate($title, 256)); // some DB setups can't truncate automatically to column max size
258 1
		$track->setNumber($number);
259 1
		$track->setDisk($discNumber);
260 1
		$track->setYear($year);
261 1
		$track->setGenreId($genreId);
262 1
		$track->setArtistId($artistId);
263 1
		$track->setAlbumId($albumId);
264 1
		$track->setFileId($fileId);
265 1
		$track->setMimetype($mimetype);
266 1
		$track->setUserId($userId);
267 1
		$track->setLength($length);
268 1
		$track->setBitrate($bitrate);
269 1
		return $this->mapper->insertOrUpdate($track);
270
	}
271
272
	/**
273
	 * Deletes a track
274
	 * @param int[] $fileIds file IDs of the tracks to delete
275
	 * @param string[]|null $userIds the target users; if omitted, the tracks matching the
276
	 *                      $fileIds are deleted from all users
277
	 * @return array|false  False is returned if no such track was found; otherwise array of six arrays
278
	 *         (named 'deletedTracks', 'remainingAlbums', 'remainingArtists', 'obsoleteAlbums',
279
	 *         'obsoleteArtists', and 'affectedUsers'). These contain the track, album, artist, and
280
	 *         user IDs of the deleted tracks. The 'obsolete' entities are such which no longer
281
	 *         have any tracks while 'remaining' entities have some left.
282
	 */
283 3
	public function deleteTracks($fileIds, $userIds = null) {
284 3
		$tracks = ($userIds !== null)
285
			? $this->mapper->findByFileIds($fileIds, $userIds)
286 3
			: $this->mapper->findAllByFileIds($fileIds);
287
288 3
		if (\count($tracks) === 0) {
289 1
			$result = false;
290
		} else {
291
			// delete all the matching tracks
292 2
			$trackIds = Util::extractIds($tracks);
293 2
			$this->deleteById($trackIds);
294
295
			// find all distinct albums, artists, and users of the deleted tracks
296 2
			$artists = [];
297 2
			$albums = [];
298 2
			$users = [];
299 2
			foreach ($tracks as $track) {
300 2
				$artists[$track->getArtistId()] = 1;
301 2
				$albums[$track->getAlbumId()] = 1;
302 2
				$users[$track->getUserId()] = 1;
303
			}
304 2
			$artists = \array_keys($artists);
305 2
			$albums = \array_keys($albums);
306 2
			$users = \array_keys($users);
307
308
			// categorize each artist as 'remaining' or 'obsolete'
309 2
			$remainingArtists = [];
310 2
			$obsoleteArtists = [];
311 2
			foreach ($artists as $artistId) {
312 2
				$result = $this->mapper->countByArtist($artistId);
313 2
				if ($result === '0') {
314 1
					$obsoleteArtists[] = $artistId;
315
				} else {
316 2
					$remainingArtists[] = $artistId;
317
				}
318
			}
319
320
			// categorize each album as 'remaining' or 'obsolete'
321 2
			$remainingAlbums = [];
322 2
			$obsoleteAlbums = [];
323 2
			foreach ($albums as $albumId) {
324 2
				$result = $this->mapper->countByAlbum($albumId);
325 2
				if ($result === '0') {
326 1
					$obsoleteAlbums[] = $albumId;
327
				} else {
328 2
					$remainingAlbums[] = $albumId;
329
				}
330
			}
331
332
			$result = [
333 2
				'deletedTracks'    => $trackIds,
334 2
				'remainingAlbums'  => $remainingAlbums,
335 2
				'remainingArtists' => $remainingArtists,
336 2
				'obsoleteAlbums'   => $obsoleteAlbums,
337 2
				'obsoleteArtists'  => $obsoleteArtists,
338 2
				'affectedUsers'    => $users
339
			];
340
		}
341
342 3
		return $result;
343
	}
344
}
345