Passed
Push — feature/329_Subsonic_API ( a9dee6...9d1353 )
by Pauli
14:33
created

TrackBusinessLayer::findAllByFolder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
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 - 2019
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
27
class TrackBusinessLayer extends BusinessLayer {
28
	private $logger;
29
30
	public function __construct(TrackMapper $trackMapper, Logger $logger) {
31
		parent::__construct($trackMapper);
32
		$this->logger = $logger;
33
	}
34
35
	/**
36
	 * Returns all tracks filtered by artist
37
	 * @param string $artistId the id of the artist
38
	 * @param string $userId the name of the user
39
	 * @return array of tracks
40
	 */
41
	public function findAllByArtist($artistId, $userId) {
42
		return $this->mapper->findAllByArtist($artistId, $userId);
43
	}
44
45
	/**
46
	 * Returns all tracks filtered by album
47
	 * @param string $albumId the id of the album
48
	 * @param string $userId the name of the user
49
	 * @return \OCA\Music\Db\Track[] tracks
50
	 */
51
	public function findAllByAlbum($albumId, $userId, $artistId = null) {
52
		return $this->mapper->findAllByAlbum($albumId, $userId, $artistId);
53
	}
54
55
	/**
56
	 * Returns all tracks filtered by parent folder
57
	 * @param integer $folderId the id of the track
58
	 * @param string $userId the name of the user
59
	 * @return \OCA\Music\Db\Track[] tracks
60
	 */
61
	public function findAllByFolder($folderId, $userId) {
62
		return $this->mapper->findAllByFolder($folderId, $userId);
63
	}
64
65
	/**
66
	 * Returns all tracks filtered by name (of track/album/artist)
67
	 * @param string $name the name of the track/album/artist
68
	 * @param string $userId the name of the user
69
	 * @return \OCA\Music\Db\Track[] tracks
70
	 */
71
	public function findAllByNameRecursive($name, $userId) {
72
		return $this->mapper->findAllByNameRecursive($name, $userId);
73
	}
74
75
	/**
76
	 * Returns the track for a file id
77
	 * @param string $fileId the file id of the track
78
	 * @param string $userId the name of the user
79
	 * @return \OCA\Music\Db\Track|null track
80
	 */
81
	public function findByFileId($fileId, $userId) {
82
		try {
83
			return $this->mapper->findByFileId($fileId, $userId);
84
		} catch (DoesNotExistException $e) {
85
			return null;
86
		}
87
	}
88
89
	/**
90
	 * Returns file IDs of all indexed tracks of the user
91
	 * @param string $userId
92
	 * @return int[]
93
	 */
94
	public function findAllFileIds($userId) {
95
		return $this->mapper->findAllFileIds($userId);
96
	}
97
98
	/**
99
	 * Returns all folders of the user containing indexed tracks, along with the contained track IDs
100
	 * @param string $userId
101
	 * @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...
102
	 * @return array of entries like {id: int, name: string, path: string, trackIds: int[]}
103
	 */
104
	public function findAllFolders($userId, $userHome) {
105
		// All tracks of the user, grouped by their parent folders. Some of the parent folders
106
		// may be owned by other users and are invisible to this user (in case of shared files).
107
		$tracksByFolder = $this->mapper->findTrackAndFolderIds($userId);
108
109
		// Get the folder names and paths for ordinary local folders directly from the DB.
110
		// This is significantly more efficient than using the Files API because we need to
111
		// run only single DB query instead of one per folder.
112
		$folderNamesAndPaths = $this->mapper->findNodeNamesAndPaths(
113
				\array_keys($tracksByFolder), $userHome->getStorage()->getId());
114
115
		// root folder has to be handled as a special case because shared files from
116
		// many folders may be shown to this user mapped under the root folder
117
		$rootFolderTracks = [];
118
119
		// Build the final results. Use the previously fetched data for the ordinary
120
		// local folders and query the data through the Files API for the more special cases.
121
		$result = [];
122
		foreach ($tracksByFolder as $folderId => $trackIds) {
123
			if (isset($folderNamesAndPaths[$folderId])) {
124
				// normal folder within the user home storage
125
				$entry = $folderNamesAndPaths[$folderId];
126
				// remove the "files" from the beginning of the folder path
127
				$entry['path'] = \substr($entry['path'], 5);
128
				// special handling for the root folder
129
				if ($entry['path'] === '') {
130
					$entry = null;
131
				}
132
			}
133
			else {
134
				// shared folder or parent folder of a shared file or an externally mounted folder
135
				$folderNode = $userHome->getById($folderId);
136
				if (\count($folderNode) === 0) {
137
					// other user's folder with files shared with this user (mapped under root)
138
					$entry = null;
139
				}
140
				else {
141
					$entry = [
142
						'name' => $folderNode[0]->getName(),
143
						'path' => $userHome->getRelativePath($folderNode[0]->getPath())
144
					];
145
				}
146
			}
147
148
			if ($entry) {
149
				$entry['trackIds'] = $trackIds;
150
				$entry['id'] = $folderId;
151
				$result[] = $entry;
152
			} else {
153
				$rootFolderTracks = \array_merge($rootFolderTracks, $trackIds);
154
			}
155
		}
156
157
		// add the root folder if it contains any tracks
158
		if (!empty($rootFolderTracks)) {
159
			$result[] = [
160
				'name' => '',
161
				'path' => '/',
162
				'trackIds' => $rootFolderTracks,
163
				'id' => $userHome->getId()
164
			];
165
		}
166
167
		return $result;
168
	}
169
170
	/**
171
	 * @param integer $artistId
172
	 * @return integer
173
	 */
174
	public function countByArtist($artistId) {
175
		return $this->mapper->countByArtist($artistId);
176
	}
177
178
	/**
179
	 * @param integer $albumId
180
	 * @return integer
181
	 */
182
	public function countByAlbum($albumId) {
183
		return $this->mapper->countByAlbum($albumId);
184
	}
185
186
	/**
187
	 * Adds a track if it does not exist already or updates an existing track
188
	 * @param string $title the title of the track
189
	 * @param string $number the number of the track
190
	 * @param string $year the year of the release
191
	 * @param string $artistId the artist id of the track
192
	 * @param string $albumId the album id of the track
193
	 * @param string $fileId the file id of the track
194
	 * @param string $mimetype the mimetype of the track
195
	 * @param string $userId the name of the user
196
	 * @param int $length track length in seconds
197
	 * @param int $bitrate track bitrate in bits (not kbits)
198
	 * @return \OCA\Music\Db\Track The added/updated track
199
	 */
200
	public function addOrUpdateTrack(
201
			$title, $number, $year, $artistId, $albumId, $fileId,
202
			$mimetype, $userId, $length=null, $bitrate=null) {
203
		$track = new Track();
204
		$track->setTitle(Util::truncate($title, 256)); // some DB setups can't truncate automatically to column max size
205
		$track->setNumber($number);
0 ignored issues
show
Bug introduced by
$number of type string is incompatible with the type integer expected by parameter $number of OCA\Music\Db\Track::setNumber(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
		$track->setNumber(/** @scrutinizer ignore-type */ $number);
Loading history...
206
		$track->setYear($year);
0 ignored issues
show
Bug introduced by
$year of type string is incompatible with the type integer expected by parameter $year of OCA\Music\Db\Track::setYear(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
		$track->setYear(/** @scrutinizer ignore-type */ $year);
Loading history...
207
		$track->setArtistId($artistId);
0 ignored issues
show
Bug introduced by
$artistId of type string is incompatible with the type integer expected by parameter $artistId of OCA\Music\Db\Track::setArtistId(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
		$track->setArtistId(/** @scrutinizer ignore-type */ $artistId);
Loading history...
208
		$track->setAlbumId($albumId);
0 ignored issues
show
Bug introduced by
$albumId of type string is incompatible with the type integer expected by parameter $albumId of OCA\Music\Db\Track::setAlbumId(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

208
		$track->setAlbumId(/** @scrutinizer ignore-type */ $albumId);
Loading history...
209
		$track->setFileId($fileId);
0 ignored issues
show
Bug introduced by
$fileId of type string is incompatible with the type integer expected by parameter $fileId of OCA\Music\Db\Track::setFileId(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

209
		$track->setFileId(/** @scrutinizer ignore-type */ $fileId);
Loading history...
210
		$track->setMimetype($mimetype);
211
		$track->setUserId($userId);
212
		$track->setLength($length);
213
		$track->setBitrate($bitrate);
214
		return $this->mapper->insertOrUpdate($track);
215
	}
216
217
	/**
218
	 * Deletes a track
219
	 * @param int[] $fileIds file IDs of the tracks to delete
220
	 * @param string[]|null $userIds the target users; if omitted, the tracks matching the
221
	 *                      $fileIds are deleted from all users
222
	 * @return False if no such track was found; otherwise array of six arrays
223
	 *         (named 'deletedTracks', 'remainingAlbums', 'remainingArtists', 'obsoleteAlbums',
224
	 *         'obsoleteArtists', and 'affectedUsers'). These contain the track, album, artist, and
225
	 *         user IDs of the deleted tracks. The 'obsolete' entities are such which no longer
226
	 *         have any tracks while 'remaining' entities have some left.
227
	 */
228
	public function deleteTracks($fileIds, $userIds = null) {
229
		$tracks = ($userIds !== null)
230
			? $this->mapper->findByFileIds($fileIds, $userIds)
231
			: $this->mapper->findAllByFileIds($fileIds);
232
233
		if (\count($tracks) === 0) {
234
			$result = false;
235
		} else {
236
			// delete all the matching tracks
237
			$trackIds = Util::extractIds($tracks);
238
			$this->deleteById($trackIds);
239
240
			// find all distinct albums, artists, and users of the deleted tracks
241
			$artists = [];
242
			$albums = [];
243
			$users = [];
244
			foreach ($tracks as $track) {
245
				$artists[$track->getArtistId()] = 1;
246
				$albums[$track->getAlbumId()] = 1;
247
				$users[$track->getUserId()] = 1;
248
			}
249
			$artists = \array_keys($artists);
250
			$albums = \array_keys($albums);
251
			$users = \array_keys($users);
252
253
			// categorize each artist as 'remaining' or 'obsolete'
254
			$remainingArtists = [];
255
			$obsoleteArtists = [];
256
			foreach ($artists as $artistId) {
257
				$result = $this->mapper->countByArtist($artistId);
258
				if ($result === '0') {
259
					$obsoleteArtists[] = $artistId;
260
				} else {
261
					$remainingArtists[] = $artistId;
262
				}
263
			}
264
265
			// categorize each album as 'remaining' or 'obsolete'
266
			$remainingAlbums = [];
267
			$obsoleteAlbums = [];
268
			foreach ($albums as $albumId) {
269
				$result = $this->mapper->countByAlbum($albumId);
270
				if ($result === '0') {
271
					$obsoleteAlbums[] = $albumId;
272
				} else {
273
					$remainingAlbums[] = $albumId;
274
				}
275
			}
276
277
			$result = [
278
				'deletedTracks'    => $trackIds,
279
				'remainingAlbums'  => $remainingAlbums,
280
				'remainingArtists' => $remainingArtists,
281
				'obsoleteAlbums'   => $obsoleteAlbums,
282
				'obsoleteArtists'  => $obsoleteArtists,
283
				'affectedUsers'    => $users
284
			];
285
		}
286
287
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type array<string,array|mixed> which is incompatible with the documented return type false.
Loading history...
288
	}
289
}
290