Passed
Pull Request — master (#1078)
by Pauli
05:42 queued 02:56
created

AlbumMapper::selectEntities()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
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 - 2023
13
 */
14
15
namespace OCA\Music\Db;
16
17
use OCA\Music\Utility\Util;
18
19
use OCP\IDBConnection;
20
21
/**
22
 * Type hint a base class methdo to help Scrutinizer
23
 * @method Album updateOrInsert(Album $album)
24
 * @phpstan-extends BaseMapper<Album>
25
 */
26
class AlbumMapper extends BaseMapper {
27
	public function __construct(IDBConnection $db) {
28
		parent::__construct($db, 'music_albums', Album::class, 'name', 'album_artist_id');
29
	}
30
31
	/**
32
	 * Override the base implementation to include data from multiple tables
33
	 *
34
	 * {@inheritdoc}
35
	 * @see BaseMapper::selectEntities()
36
	 */
37
	protected function selectEntities(string $condition, string $extension=null) : string {
38
		return "SELECT `*PREFIX*music_albums`.*, `artist`.`name` AS `album_artist_name`
39
				FROM `*PREFIX*music_albums`
40
				INNER JOIN `*PREFIX*music_artists` `artist`
41
				ON `*PREFIX*music_albums`.`album_artist_id` = `artist`.`id`
42
				WHERE $condition $extension";
43
	}
44
45
	/**
46
	 * Overridden from \OCA\Music\Db\BaseMapper to add support for sorting by artist.
47
	 *
48
	 * {@inheritdoc}
49
	 * @see BaseMapper::formatSortingClause()
50
	 */
51
	protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string {
52
		if ($sortBy === SortBy::Parent) {
53
			// Note: the alternative form "LOWER(`album_artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046
54
			$dir = $invertSort ? 'DESC' : 'ASC';
55
			return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`*PREFIX*music_albums`.`name`) $dir";
56
		} else {
57
			return parent::formatSortingClause($sortBy, $invertSort);
58
		}
59
	}
60
61
	/**
62
	 * returns artist IDs mapped to album IDs
63
	 * does not include album_artist_id
64
	 *
65
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
66
	 * @param string $userId the user ID
67
	 * @return array int => int[], keys are albums IDs and values are arrays of artist IDs
68
	 */
69
	public function getPerformingArtistsByAlbumId(?array $albumIds, string $userId) : array {
70
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`artist_id`
71
				FROM `*PREFIX*music_tracks` `track`
72
				WHERE `track`.`user_id` = ? ';
73
		$params = [$userId];
74
75
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
76
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
77
			$params = \array_merge($params, $albumIds);
78
		}
79
80
		$result = $this->execute($sql, $params);
81
		$artistIds = [];
82
		while ($row = $result->fetch()) {
83
			$artistIds[$row['album_id']][] = (int)$row['artist_id'];
84
		}
85
		return $artistIds;
86
	}
87
88
	/**
89
	 * returns release years mapped to album IDs
90
	 *
91
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
92
	 * @param string $userId the user ID
93
	 * @return array int => int[], keys are albums IDs and values are arrays of years
94
	 */
95
	public function getYearsByAlbumId(?array $albumIds, string $userId) : array {
96
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`year`
97
				FROM `*PREFIX*music_tracks` `track`
98
				WHERE `track`.`user_id` = ?
99
				AND `track`.`year` IS NOT NULL ';
100
		$params = [$userId];
101
102
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
103
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
104
			$params = \array_merge($params, $albumIds);
105
		}
106
107
		$result = $this->execute($sql, $params);
108
		$years = [];
109
		while ($row = $result->fetch()) {
110
			$years[$row['album_id']][] = (int)$row['year'];
111
		}
112
		return $years;
113
	}
114
115
	/**
116
	 * returns genres mapped to album IDs
117
	 *
118
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
119
	 * @param string $userId the user ID
120
	 * @return array int => Genre[], keys are albums IDs and values are arrays of *partial* Genre objects (only id and name properties set)
121
	 */
122
	public function getGenresByAlbumId(?array $albumIds, string $userId) : array {
123
		$sql = 'SELECT DISTINCT `album_id`, `genre_id`, `*PREFIX*music_genres`.`name` AS `genre_name`
124
				FROM `*PREFIX*music_tracks`
125
				LEFT JOIN `*PREFIX*music_genres`
126
				ON `genre_id` = `*PREFIX*music_genres`.`id`
127
				WHERE `*PREFIX*music_tracks`.`user_id` = ?
128
				AND `genre_id` IS NOT NULL ';
129
		$params = [$userId];
130
131
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
132
			$sql .= 'AND `album_id` IN ' . $this->questionMarks(\count($albumIds));
133
			$params = \array_merge($params, $albumIds);
134
		}
135
136
		$result = $this->execute($sql, $params);
137
		$genres = [];
138
		while ($row = $result->fetch()) {
139
			$genre = new Genre();
140
			$genre->setUserId($userId);
141
			$genre->setId((int)$row['genre_id']);
142
			$genre->setName($row['genre_name']);
143
			$genres[$row['album_id']][] = $genre;
144
		}
145
		return $genres;
146
	}
147
148
	/**
149
	 * returns number of disks per album ID
150
	 *
151
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
152
	 * @param string $userId the user ID
153
	 * @return array int => int, keys are albums IDs and values are disk counts
154
	 */
155
	public function getDiscCountByAlbumId(?array $albumIds, string $userId) : array {
156
		$sql = 'SELECT `album_id`, MAX(`disk`) AS `disc_count`
157
				FROM `*PREFIX*music_tracks`
158
				WHERE `user_id` = ?
159
				GROUP BY `album_id` ';
160
		$params = [$userId];
161
162
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
163
			$sql .= 'HAVING `album_id` IN ' . $this->questionMarks(\count($albumIds));
164
			$params = \array_merge($params, $albumIds);
165
		}
166
167
		$result = $this->execute($sql, $params);
168
		$diskCountByAlbum = [];
169
		while ($row = $result->fetch()) {
170
			$diskCountByAlbum[$row['album_id']] = (int)$row['disc_count'];
171
		}
172
		return $diskCountByAlbum;
173
	}
174
175
	/**
176
	 * returns summed track play counts of each album of the user, omittig albums which have never been played
177
	 *
178
	 * @return array [int => int], keys are album IDs and values are play count sums; ordered largest counts first
179
	 */
180
	public function getAlbumTracksPlayCount(string $userId, ?int $limit=null, ?int $offset=null) : array {
181
		$sql = 'SELECT `album_id`, SUM(`play_count`) AS `sum_count`
182
				FROM `*PREFIX*music_tracks`
183
				WHERE `user_id` = ? AND `play_count` > 0
184
				GROUP BY `album_id`
185
				ORDER BY `sum_count` DESC, `album_id`'; // the second criterion is just to make the order predictable on even counts
186
187
		$result = $this->execute($sql, [$userId], $limit, $offset);
188
		$playCountByAlbum = [];
189
		while ($row = $result->fetch()) {
190
			$playCountByAlbum[$row['album_id']] = (int)$row['sum_count'];
191
		}
192
		return $playCountByAlbum;
193
	}
194
195
	/**
196
	 * returns the latest play time of each album of the user, omittig albums which have never been played
197
	 *
198
	 * @return array [int => string], keys are album IDs and values are date-times; ordered latest times first
199
	 */
200
	public function getLatestAlbumPlayTimes(string $userId, ?int $limit=null, ?int $offset=null) : array {
201
		$sql = 'SELECT `album_id`, MAX(`last_played`) AS `latest_time`
202
				FROM `*PREFIX*music_tracks`
203
				WHERE `user_id` = ? AND `last_played` IS NOT NULL
204
				GROUP BY `album_id`
205
				ORDER BY `latest_time` DESC';
206
207
		$result = $this->execute($sql, [$userId], $limit, $offset);
208
		$latestTimeByAlbum = [];
209
		while ($row = $result->fetch()) {
210
			$latestTimeByAlbum[$row['album_id']] = $row['latest_time'];
211
		}
212
		return $latestTimeByAlbum;
213
	}
214
215
	/**
216
	 * returns the latest play time of each album of the user, including albums which have never been played
217
	 *
218
	 * @return array [int => ?string], keys are album IDs and values are date-times (or null for never played);
219
	 *									ordered furthest times first
220
	 */
221
	public function getFurthestAlbumPlayTimes(string $userId, ?int $limit=null, ?int $offset=null) : array {
222
		$sql = 'SELECT `album_id`, MAX(`last_played`) AS `latest_time`
223
				FROM `*PREFIX*music_tracks`
224
				WHERE `user_id` = ?
225
				GROUP BY `album_id`
226
				ORDER BY `latest_time` ASC';
227
228
		$result = $this->execute($sql, [$userId], $limit, $offset);
229
		$latestTimeByAlbum = [];
230
		while ($row = $result->fetch()) {
231
			$latestTimeByAlbum[$row['album_id']] = $row['latest_time'];
232
		}
233
		return $latestTimeByAlbum;
234
	}
235
236
	/**
237
	 * @return Album[]
238
	 */
239
	public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) : array {
240
		$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR
241
						LOWER(`*PREFIX*music_albums`.`name`) LIKE LOWER(?) )';
242
		$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
243
		$name = BaseMapper::prepareSubstringSearchPattern($name);
244
		$params = [$userId, $name, $name];
245
		return $this->findEntities($sql, $params, $limit, $offset);
246
	}
247
248
	/**
249
	 * returns albums of a specified artist
250
	 * The artist may be an album_artist or the artist of a track
251
	 *
252
	 * @param integer $artistId
253
	 * @return Album[]
254
	 */
255
	public function findAllByArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array {
256
		$sql = $this->selectEntities(
257
				'`*PREFIX*music_albums`.`id` IN (
258
					SELECT DISTINCT `album`.`id`
259
					FROM `*PREFIX*music_albums` `album`
260
					WHERE `album`.`album_artist_id` = ?
261
						UNION
262
					SELECT DISTINCT `track`.`album_id`
263
					FROM `*PREFIX*music_tracks` `track`
264
					WHERE `track`.`artist_id` = ?
265
				) AND `*PREFIX*music_albums`.`user_id` = ?',
266
				'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
267
		$params = [$artistId, $artistId, $userId];
268
		return $this->findEntities($sql, $params, $limit, $offset);
269
	}
270
271
	/**
272
	 * returns albums of a specified artists
273
	 * The artist must be album_artist on the album, artists of individual tracks are not considered
274
	 *
275
	 * @param int[] $artistIds
276
	 * @return Album[]
277
	 */
278
	public function findAllByAlbumArtist(array $artistIds, string $userId, ?int $limit=null, ?int $offset=null) : array {
279
		$sql = $this->selectUserEntities('`album_artist_id` IN ' . $this->questionMarks(\count($artistIds)));
280
		$params = \array_merge([$userId], $artistIds);
281
		return $this->findEntities($sql, $params, $limit, $offset);
282
	}
283
284
	/**
285
	 * @return Album[]
286
	 */
287
	public function findAllByGenre(int $genreId, string $userId, int $limit=null, int $offset=null) : array {
288
		$sql = $this->selectUserEntities('EXISTS '.
289
				'(SELECT 1 FROM `*PREFIX*music_tracks` `track`
290
				  WHERE `*PREFIX*music_albums`.`id` = `track`.`album_id`
291
				  AND `track`.`genre_id` = ?)');
292
293
		$params = [$userId, $genreId];
294
		return $this->findEntities($sql, $params, $limit, $offset);
295
	}
296
297
	/**
298
	 * @return boolean True if one or more albums were influenced
299
	 */
300
	public function updateFolderCover(int $coverFileId, int $folderId) : bool {
301
		$sql = 'SELECT DISTINCT `tracks`.`album_id`
302
				FROM `*PREFIX*music_tracks` `tracks`
303
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
304
				WHERE `files`.`parent` = ?';
305
		$params = [$folderId];
306
		$result = $this->execute($sql, $params);
307
308
		$updated = false;
309
		if ($result->rowCount()) {
310
			$sql = 'UPDATE `*PREFIX*music_albums`
311
					SET `cover_file_id` = ?
312
					WHERE `cover_file_id` IS NULL AND `id` IN (?)';
313
			$params = [$coverFileId, \join(",", $result->fetchAll(\PDO::FETCH_COLUMN))];
314
			$result = $this->execute($sql, $params);
315
			$updated = $result->rowCount() > 0;
316
		}
317
318
		return $updated;
319
	}
320
321
	/**
322
	 * Set file ID to be used as cover for an album
323
	 */
324
	public function setCover(?int $coverFileId, int $albumId) : void {
325
		$sql = 'UPDATE `*PREFIX*music_albums`
326
				SET `cover_file_id` = ?
327
				WHERE `id` = ?';
328
		$params = [$coverFileId, $albumId];
329
		$this->execute($sql, $params);
330
	}
331
332
	/**
333
	 * @param integer[] $coverFileIds
334
	 * @param string[]|null $userIds the users whose music library is targeted; all users are targeted if omitted
335
	 * @return Album[] albums which got modified (with incomplete data, only id and user are valid),
336
	 *         empty array if none
337
	 */
338
	public function removeCovers(array $coverFileIds, array $userIds=null) : array {
339
		// find albums using the given file as cover
340
		$sql = 'SELECT `id`, `user_id` FROM `*PREFIX*music_albums` WHERE `cover_file_id` IN ' .
341
			$this->questionMarks(\count($coverFileIds));
342
		$params = $coverFileIds;
343
		if ($userIds !== null) {
344
			$sql .= ' AND `user_id` IN ' . $this->questionMarks(\count($userIds));
345
			$params = \array_merge($params, $userIds);
346
		}
347
		$albums = $this->findEntities($sql, $params);
348
349
		// if any albums found, remove the cover from those
350
		$count = \count($albums);
351
		if ($count) {
352
			$sql = 'UPDATE `*PREFIX*music_albums`
353
				SET `cover_file_id` = NULL
354
				WHERE `id` IN ' . $this->questionMarks($count);
355
			$params = Util::extractIds($albums);
356
			$this->execute($sql, $params);
357
		}
358
359
		return $albums;
360
	}
361
362
	/**
363
	 * @param string|null $userId target user; omit to target all users
364
	 * @return array of dictionaries with keys [albumId, userId, parentFolderId]
365
	 */
366
	public function getAlbumsWithoutCover(string $userId = null) : array {
367
		$sql = 'SELECT DISTINCT `albums`.`id`, `albums`.`user_id`, `files`.`parent`
368
				FROM `*PREFIX*music_albums` `albums`
369
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
370
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
371
				WHERE `albums`.`cover_file_id` IS NULL';
372
		$params = [];
373
		if ($userId !== null) {
374
			$sql .= ' AND `albums`.`user_id` = ?';
375
			$params[] = $userId;
376
		}
377
		$result = $this->execute($sql, $params);
378
		$return = [];
379
		while ($row = $result->fetch()) {
380
			$return[] = [
381
				'albumId' => (int)$row['id'],
382
				'userId' => $row['user_id'],
383
				'parentFolderId' => (int)$row['parent']
384
			];
385
		}
386
		return $return;
387
	}
388
389
	/**
390
	 * @return boolean True if a cover image was found and added for the album
391
	 */
392
	public function findAlbumCover(int $albumId, int $parentFolderId) : bool {
393
		$return = false;
394
		$imagesSql = 'SELECT `fileid`, `name`
395
					FROM `*PREFIX*filecache`
396
					JOIN `*PREFIX*mimetypes` ON `*PREFIX*mimetypes`.`id` = `*PREFIX*filecache`.`mimetype`
397
					WHERE `parent` = ? AND `*PREFIX*mimetypes`.`mimetype` LIKE \'image%\'';
398
		$params = [$parentFolderId];
399
		$result = $this->execute($imagesSql, $params);
400
		$images = $result->fetchAll();
401
		if (\count($images) > 0) {
402
			$getImageRank = function($imageName) {
403
				$coverNames = ['cover', 'albumart', 'album', 'front', 'folder'];
404
				foreach ($coverNames as $i => $coverName) {
405
					if (Util::startsWith($imageName, $coverName, /*$ignoreCase=*/true)) {
406
						return $i;
407
					}
408
				}
409
				return \count($coverNames);
410
			};
411
412
			\usort($images, function ($imageA, $imageB) use ($getImageRank) {
413
				return $getImageRank($imageA['name']) <=> $getImageRank($imageB['name']);
414
			});
415
			$imageId = (int)$images[0]['fileid'];
416
			$this->setCover($imageId, $albumId);
417
			$return = true;
418
		}
419
		return $return;
420
	}
421
422
	/**
423
	 * Given an array of track IDs, find corresponding uniqu album IDs, including only
424
	 * those album which have a cover art set.
425
	 * @param int[] $trackIds
426
	 * @return Album[]
427
	 */
428
	public function findAlbumsWithCoversForTracks(array $trackIds, string $userId, int $limit) : array {
429
		$sql = 'SELECT DISTINCT `albums`.*
430
				FROM `*PREFIX*music_albums` `albums`
431
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
432
				WHERE `albums`.`cover_file_id` IS NOT NULL
433
				AND `albums`.`user_id` = ?
434
				AND `tracks`.`id` IN ' . $this->questionMarks(\count($trackIds));
435
		$params = \array_merge([$userId], $trackIds);
436
437
		return $this->findEntities($sql, $params, $limit);
438
	}
439
440
	/**
441
	 * Returns the count of albums where the given Artist is featured in
442
	 * @param integer $artistId
443
	 * @return integer
444
	 */
445
	public function countByArtist(int $artistId) : int {
446
		$sql = 'SELECT COUNT(*) AS count FROM (
447
					SELECT DISTINCT `track`.`album_id`
448
					FROM `*PREFIX*music_tracks` `track`
449
					WHERE `track`.`artist_id` = ?
450
						UNION
451
					SELECT `album`.`id`
452
					FROM `*PREFIX*music_albums` `album`
453
					WHERE `album`.`album_artist_id` = ?
454
				) tmp';
455
		$params = [$artistId, $artistId];
456
		$result = $this->execute($sql, $params);
457
		$row = $result->fetch();
458
		return (int)$row['count'];
459
	}
460
461
	/**
462
	 * Returns the count of albums where the given artist is the album artist
463
	 * @param integer $artistId
464
	 * @return integer
465
	 */
466
	public function countByAlbumArtist(int $artistId) : int {
467
		$sql = 'SELECT COUNT(*) AS count
468
				FROM `*PREFIX*music_albums` `album`
469
				WHERE `album`.`album_artist_id` = ?';
470
		$params = [$artistId];
471
		$result = $this->execute($sql, $params);
472
		$row = $result->fetch();
473
		return (int)$row['count'];
474
	}
475
476
	/**
477
	 * Overridden from the base implementation to provide support for table-specific rules
478
	 *
479
	 * {@inheritdoc}
480
	 * @see BaseMapper::advFormatSqlCondition()
481
	 */
482
	protected function advFormatSqlCondition(string $rule, string $sqlOp) : string {
483
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
484
		// In case of 'recent_played', the MySQL 5.5.62 errored with "1235 This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'" without the extra subquery.
485
		switch ($rule) {
486
			case 'artist':			return "LOWER(`artist`.`name`) $sqlOp LOWER(?)";
487
			case 'song_artist':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_artists` `ar` ON `t`.`artist_id` = `ar`.`id` WHERE LOWER(`ar`.`name`) $sqlOp LOWER(?))";
488
			case 'song':			return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE LOWER(`t`.`title`) $sqlOp LOWER(?))";
489
			case 'year':			// fall through, we only have one kind of year
490
			case 'original_year':	return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MIN(`year`) $sqlOp ?) mysqlhack)";
491
			case 'songrating':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE `t`.`rating` $sqlOp ?)";
492
			case 'artistrating':	return "`artist`.rating $sqlOp ?";
493
			case 'played_times':	return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING SUM(`play_count`) $sqlOp ?) mysqlhack)";
494
			case 'last_play':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MAX(`last_played`) $sqlOp ?) mysqlhack)";
495
			case 'played':			// fall through, we give no access to other people's data; not part of the API spec but Ample uses this
496
			case 'myplayed':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MAX(`last_played`) $sqlOp) mysqlhack)"; // operator "IS NULL" or "IS NOT NULL"
497
			case 'myplayedartist':	return "`album_artist_id` IN (SELECT * FROM (SELECT `artist_id` from `*PREFIX*music_tracks` GROUP BY `artist_id` HAVING MAX(`last_played`) $sqlOp) mysqlhack)"; // operator "IS NULL" or "IS NOT NULL"
498
			case 'song_count':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` GROUP BY `album_id` HAVING COUNT(`id`) $sqlOp ?) mysqlhack)";
499
			case 'time':			return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` GROUP BY `album_id` HAVING SUM(`length`) $sqlOp ?) mysqlhack)";
500
			case 'genre':			// fall through, alias
501
			case 'album_genre':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` GROUP BY `album_id` HAVING LOWER(GROUP_CONCAT(`g`.`name`)) $sqlOp LOWER(?)) mysqlhack)"; // GROUP_CONCAT not available on PostgreSQL
502
			case 'song_genre':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` WHERE LOWER(`g`.`name`) $sqlOp LOWER(?))";
503
			case 'no_genre':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` WHERE `g`.`name` " . (($sqlOp == 'IS NOT NULL') ? '=' : '!=') . ' "")';
504
			case 'playlist':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE $sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE CONCAT('%|',`t`.`id`, '|%')))";
505
			case 'playlist_name':	return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`name` $sqlOp ? AND `p`.`track_ids` LIKE CONCAT('%|',`t`.`id`, '|%')))";
506
			case 'file':			return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*filecache` `f` ON `t`.`file_id` = `f`.`fileid` WHERE LOWER(`f`.`name`) $sqlOp LOWER(?))";
507
			case 'recent_played':	return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM (SELECT `album_id`, MAX(`last_played`) FROM `*PREFIX*music_tracks` WHERE `user_id` = ? GROUP BY `album_id` ORDER BY MAX(`last_played`) DESC LIMIT $sqlOp) mysqlhack)";
508
			case 'mbid_album':		return parent::advFormatSqlCondition('mbid', $sqlOp); // alias
509
			case 'mbid_song':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE `t`.`mbid` $sqlOp ?)";
510
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
511
			case 'has_image':		return "`*PREFIX*music_albums`.`cover_file_id` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
512
			default:				return parent::advFormatSqlCondition($rule, $sqlOp);
513
		}
514
	}
515
516
	/**
517
	 * {@inheritdoc}
518
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
519
	 * @param Album $album
520
	 * @return Album
521
	 */
522
	protected function findUniqueEntity(Entity $album) : Entity {
523
		$sql = $this->selectUserEntities('`*PREFIX*music_albums`.`hash` = ?');
524
		return $this->findEntity($sql, [$album->getUserId(), $album->getHash()]);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findEntity...(), $album->getHash())) returns the type OCP\AppFramework\Db\Entity which includes types incompatible with the type-hinted return OCA\Music\Db\Entity.
Loading history...
Bug introduced by
The method getHash() does not exist on OCA\Music\Db\Entity. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

524
		return $this->findEntity($sql, [$album->getUserId(), $album->/** @scrutinizer ignore-call */ getHash()]);
Loading history...
525
	}
526
}
527