Passed
Push — feature/playlist_improvements ( 2a690a...faf9ee )
by Pauli
14:31
created

AlbumMapper::selectEntities()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 6
ccs 0
cts 3
cp 0
crap 2
rs 10
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 2016 - 2020
13
 */
14
15
namespace OCA\Music\Db;
16
17
use \OCA\Music\Utility\Util;
18
19
use OCP\IDBConnection;
0 ignored issues
show
Bug introduced by
The type OCP\IDBConnection 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
class AlbumMapper extends BaseMapper {
22
	public function __construct(IDBConnection $db) {
23
		parent::__construct($db, 'music_albums', '\OCA\Music\Db\Album', 'name');
24
	}
25
26
	/**
27
	 * Override the base implementation to include data from multiple tables
28
	 * @see \OCA\Music\Db\BaseMapper::selectEntities()
29
	 * @param string $condition
30
	 * @param string|null $extension
31
	 * @return string
32
	 */
33
	protected function selectEntities($condition, $extension=null) {
34
		return "SELECT `*PREFIX*music_albums`.*, `artist`.`name` AS `album_artist_name`
35
				FROM `*PREFIX*music_albums`
36
				INNER JOIN `*PREFIX*music_artists` `artist`
37
				ON `*PREFIX*music_albums`.`album_artist_id` = `artist`.`id`
38
				WHERE $condition $extension";
39
	}
40
41
	/**
42
	 * returns artist IDs mapped to album IDs
43
	 * does not include album_artist_id
44
	 *
45
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
46
	 * @param string $userId the user ID
47
	 * @return array int => int[], keys are albums IDs and values are arrays of artist IDs
48
	 */
49
	public function getPerformingArtistsByAlbumId($albumIds, $userId) {
50
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`artist_id`
51
				FROM `*PREFIX*music_tracks` `track`
52
				WHERE `track`.`user_id` = ? ';
53
		$params = [$userId];
54
55
		if ($albumIds !== null) {
56
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
57
			$params = \array_merge($params, $albumIds);
58
		}
59
60
		$result = $this->execute($sql, $params);
61
		return $result->fetchAll(\PDO::FETCH_COLUMN|\PDO::FETCH_GROUP);
62
	}
63
64
	/**
65
	 * returns release years mapped to album IDs
66
	 *
67
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
68
	 * @param string $userId the user ID
69
	 * @return array int => int[], keys are albums IDs and values are arrays of years
70
	 */
71
	public function getYearsByAlbumId($albumIds, $userId) {
72
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`year`
73
				FROM `*PREFIX*music_tracks` `track`
74
				WHERE `track`.`user_id` = ?
75
				AND `track`.`year` IS NOT NULL ';
76
		$params = [$userId];
77
78
		if ($albumIds !== null) {
79
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
80
			$params = \array_merge($params, $albumIds);
81
		}
82
83
		$result = $this->execute($sql, $params);
84
		return $result->fetchAll(\PDO::FETCH_COLUMN|\PDO::FETCH_GROUP);
85
	}
86
87
	/**
88
	 * returns genres mapped to album IDs
89
	 *
90
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
91
	 * @param string $userId the user ID
92
	 * @return array int => string[], keys are albums IDs and values are arrays of genres
93
	 */
94
	public function getGenresByAlbumId($albumIds, $userId) {
95
		$sql = 'SELECT DISTINCT `album_id`, `genre_id`
96
				FROM `*PREFIX*music_tracks`
97
				WHERE `user_id` = ?
98
				AND `genre_id` IS NOT NULL ';
99
		$params = [$userId];
100
101
		if ($albumIds !== null) {
102
			$sql .= 'AND `album_id` IN ' . $this->questionMarks(\count($albumIds));
103
			$params = \array_merge($params, $albumIds);
104
		}
105
106
		$result = $this->execute($sql, $params);
107
		return $result->fetchAll(\PDO::FETCH_COLUMN|\PDO::FETCH_GROUP);
108
	}
109
110
	/**
111
	 * returns number of disks per album ID
112
	 *
113
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
114
	 * @param string $userId the user ID
115
	 * @return array int => int, keys are albums IDs and values are disk counts
116
	 */
117
	public function getDiscCountByAlbumId($albumIds, $userId) {
118
		$sql = 'SELECT `album_id`, MAX(`disk`) AS `disc_count`
119
				FROM `*PREFIX*music_tracks`
120
				WHERE `user_id` = ?
121
				GROUP BY `album_id` ';
122
		$params = [$userId];
123
124
		if ($albumIds !== null) {
125
			$sql .= 'HAVING `album_id` IN ' . $this->questionMarks(\count($albumIds));
126
			$params = \array_merge($params, $albumIds);
127
		}
128
129
		$result = $this->execute($sql, $params);
130
		$diskCountByAlbum = [];
131
		while ($row = $result->fetch()) {
132
			$diskCountByAlbum[$row['album_id']] = $row['disc_count'];
133
		}
134
		return $diskCountByAlbum;
135
	}
136
137
	/**
138
	 * Find all user's entities
139
	 *
140
	 * Overridden from \OCA\Music\Db\BaseMapper to add support for sorting by artist.
141
	 *
142
	 * @param string $userId
143
	 * @param integer $sortBy sort order of the result set
144
	 * @param integer|null $limit
145
	 * @param integer|null $offset
146
	 * @return Entity[]
147
	 */
148
	public function findAll($userId, $sortBy=SortBy::None, $limit=null, $offset=null) {
149
		if ($sortBy === SortBy::Parent) {
150
			$sorting = 'ORDER BY LOWER(`album_artist_name`), LOWER(`*PREFIX*music_albums`.`name`)';
151
			$sql = $this->selectUserEntities('', $sorting);
152
			$params = [$userId];
153
			return $this->findEntities($sql, $params, $limit, $offset);
154
		} else {
155
			return parent::findAll($userId, $sortBy, $limit, $offset);
156
		}
157
	}
158
159
	/**
160
	 * returns albums of a specified artist
161
	 * The artist may be an album_artist or the artist of a track
162
	 *
163
	 * @param integer $artistId ID of the artist
164
	 * @param string $userId the user ID
165
	 * @return Album[]
166
	 */
167
	public function findAllByArtist($artistId, $userId) {
168
		$sql = $this->selectEntities(
169
				'`*PREFIX*music_albums`.`id` IN (
170
					SELECT DISTINCT `album`.`id`
171
					FROM `*PREFIX*music_albums` `album`
172
					WHERE `album`.`album_artist_id` = ? AND `album`.`user_id` = ?
173
						UNION
174
					SELECT DISTINCT `track`.`album_id`
175
					FROM `*PREFIX*music_tracks` `track`
176
					WHERE `track`.`artist_id` = ? AND `track`.`user_id` = ?
177
				)',
178
				'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
179
		$params = [$artistId, $userId, $artistId, $userId];
180
		return $this->findEntities($sql, $params);
181
	}
182
183
	/**
184
	 * returns albums of a specified artist
185
	 * The artist must album_artist on the album, artists of individual tracks are not considered
186
	 *
187
	 * @param integer $artistId ID of the artist
188
	 * @param string $userId the user ID
189
	 * @return Album[]
190
	 */
191
	public function findAllByAlbumArtist($artistId, $userId) {
192
		$sql = $this->selectUserEntities('`album_artist_id` = ?');
193
		$params = [$userId, $artistId];
194
		return $this->findEntities($sql, $params);
195
	}
196
197
	/**
198
	 * @param int $genreId
199
	 * @param string $userId
200
	 * @param int|null $limit
201
	 * @param int|null $offset
202
	 * @return Album[]
203
	 */
204
	public function findAllByGenre($genreId, $userId, $limit=null, $offset=null) {
205
		$sql = $this->selectUserEntities('EXISTS '.
206
				'(SELECT 1 FROM `*PREFIX*music_tracks` `track`
207
				  WHERE `*PREFIX*music_albums`.`id` = `track`.`album_id`
208
				  AND `track`.`genre_id` = ?)');
209
210
		$params = [$userId, $genreId];
211
		return $this->findEntities($sql, $params, $limit, $offset);
212
	}
213
214
	/**
215
	 * @param integer $coverFileId
216
	 * @param integer $folderId
217
	 * @return boolean True if one or more albums were influenced
218
	 */
219
	public function updateFolderCover($coverFileId, $folderId) {
220
		$sql = 'SELECT DISTINCT `tracks`.`album_id`
221
				FROM `*PREFIX*music_tracks` `tracks`
222
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
223
				WHERE `files`.`parent` = ?';
224
		$params = [$folderId];
225
		$result = $this->execute($sql, $params);
226
227
		$updated = false;
228
		if ($result->rowCount()) {
229
			$sql = 'UPDATE `*PREFIX*music_albums`
230
					SET `cover_file_id` = ?
231
					WHERE `cover_file_id` IS NULL AND `id` IN (?)';
232
			$params = [$coverFileId, \join(",", $result->fetchAll(\PDO::FETCH_COLUMN))];
233
			$result = $this->execute($sql, $params);
234
			$updated = $result->rowCount() > 0;
235
		}
236
237
		return $updated;
238
	}
239
240
	/**
241
	 * @param integer $coverFileId
242
	 * @param integer $albumId
243
	 */
244
	public function setCover($coverFileId, $albumId) {
245
		$sql = 'UPDATE `*PREFIX*music_albums`
246
				SET `cover_file_id` = ?
247
				WHERE `id` = ?';
248
		$params = [$coverFileId, $albumId];
249
		$this->execute($sql, $params);
250
	}
251
252
	/**
253
	 * @param integer[] $coverFileIds
254
	 * @param string[]|null $userIds the users whose music library is targeted; all users are targeted if omitted
255
	 * @return Album[] albums which got modified (with incomplete data, only id and user are valid),
256
	 *         empty array if none
257
	 */
258
	public function removeCovers($coverFileIds, $userIds=null) {
259
		// find albums using the given file as cover
260
		$sql = 'SELECT `id`, `user_id` FROM `*PREFIX*music_albums` WHERE `cover_file_id` IN ' .
261
			$this->questionMarks(\count($coverFileIds));
262
		$params = $coverFileIds;
263
		if ($userIds !== null) {
264
			$sql .= ' AND `user_id` IN ' . $this->questionMarks(\count($userIds));
265
			$params = \array_merge($params, $userIds);
266
		}
267
		$albums = $this->findEntities($sql, $params);
268
269
		// if any albums found, remove the cover from those
270
		$count = \count($albums);
271
		if ($count) {
272
			$sql = 'UPDATE `*PREFIX*music_albums`
273
				SET `cover_file_id` = NULL
274
				WHERE `id` IN ' . $this->questionMarks($count);
275
			$params = Util::extractIds($albums);
276
			$this->execute($sql, $params);
277
		}
278
279
		return $albums;
280
	}
281
282
	/**
283
	 * @param string|null $userId target user; omit to target all users
284
	 * @return array of dictionaries with keys [albumId, userId, parentFolderId]
285
	 */
286
	public function getAlbumsWithoutCover($userId = null) {
287
		$sql = 'SELECT DISTINCT `albums`.`id`, `albums`.`user_id`, `files`.`parent`
288
				FROM `*PREFIX*music_albums` `albums`
289
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
290
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
291
				WHERE `albums`.`cover_file_id` IS NULL';
292
		$params = [];
293
		if ($userId !== null) {
294
			$sql .= ' AND `albums`.`user_id` = ?';
295
			$params[] = $userId;
296
		}
297
		$result = $this->execute($sql, $params);
298
		$return = [];
299
		while ($row = $result->fetch()) {
300
			$return[] = [
301
				'albumId' => $row['id'],
302
				'userId' => $row['user_id'],
303
				'parentFolderId' => $row['parent']
304
			];
305
		}
306
		return $return;
307
	}
308
309
	/**
310
	 * @param integer $albumId
311
	 * @param integer $parentFolderId
312
	 * @return boolean True if a cover image was found and added for the album
313
	 */
314
	public function findAlbumCover($albumId, $parentFolderId) {
315
		$return = false;
316
		$coverNames = ['cover', 'albumart', 'front', 'folder'];
317
		$imagesSql = 'SELECT `fileid`, `name`
318
					FROM `*PREFIX*filecache`
319
					JOIN `*PREFIX*mimetypes` ON `*PREFIX*mimetypes`.`id` = `*PREFIX*filecache`.`mimetype`
320
					WHERE `parent` = ? AND `*PREFIX*mimetypes`.`mimetype` LIKE \'image%\'';
321
		$params = [$parentFolderId];
322
		$result = $this->execute($imagesSql, $params);
323
		$images = $result->fetchAll();
324
		if (\count($images)) {
325
			\usort($images, function ($imageA, $imageB) use ($coverNames) {
326
				$nameA = \strtolower($imageA['name']);
327
				$nameB = \strtolower($imageB['name']);
328
				$indexA = PHP_INT_MAX;
329
				$indexB = PHP_INT_MAX;
330
				foreach ($coverNames as $i => $coverName) {
331
					if ($indexA === PHP_INT_MAX && \strpos($nameA, $coverName) === 0) {
332
						$indexA = $i;
333
					}
334
					if ($indexB === PHP_INT_MAX && \strpos($nameB, $coverName) === 0) {
335
						$indexB = $i;
336
					}
337
					if ($indexA !== PHP_INT_MAX  && $indexB !== PHP_INT_MAX) {
338
						break;
339
					}
340
				}
341
				return $indexA > $indexB;
342
			});
343
			$imageId = $images[0]['fileid'];
344
			$this->setCover($imageId, $albumId);
345
			$return = true;
346
		}
347
		return $return;
348
	}
349
350
	/**
351
	 * Returns the count of albums an Artist is featured in
352
	 * @param integer $artistId
353
	 * @return integer
354
	 */
355
	public function countByArtist($artistId) {
356
		$sql = 'SELECT COUNT(*) AS count FROM (
357
					SELECT DISTINCT `track`.`album_id`
358
					FROM `*PREFIX*music_tracks` `track`
359
					WHERE `track`.`artist_id` = ?
360
						UNION 
361
					SELECT `album`.`id`
362
					FROM `*PREFIX*music_albums` `album`
363
					WHERE `album`.`album_artist_id` = ?
364
				) tmp';
365
		$params = [$artistId, $artistId];
366
		$result = $this->execute($sql, $params);
367
		$row = $result->fetch();
368
		return (int)$row['count'];
369
	}
370
371
	/**
372
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
373
	 * @param Album $album
374
	 * @return Album
375
	 */
376
	protected function findUniqueEntity($album) {
377
		$sql = $this->selectUserEntities('`*PREFIX*music_albums`.`hash` = ?');
378
		return $this->findEntity($sql, [$album->getUserId(), $album->getHash()]);
379
	}
380
}
381