Passed
Pull Request — master (#1078)
by Pauli
06:30 queued 03:46
created

AlbumMapper::findAllByNameRecursive()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 4
dl 0
loc 7
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');
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

80
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

107
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

136
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

167
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

187
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId], $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

207
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId], $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

228
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId], $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...dMapper::findEntities() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

245
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $params, $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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 ID of the artist
253
	 * @param string $userId the user ID
254
	 * @return Album[]
255
	 */
256
	public function findAllByArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array {
257
		$sql = $this->selectEntities(
258
				'`*PREFIX*music_albums`.`id` IN (
259
					SELECT DISTINCT `album`.`id`
260
					FROM `*PREFIX*music_albums` `album`
261
					WHERE `album`.`album_artist_id` = ?
262
						UNION
263
					SELECT DISTINCT `track`.`album_id`
264
					FROM `*PREFIX*music_tracks` `track`
265
					WHERE `track`.`artist_id` = ?
266
				) AND `*PREFIX*music_albums`.`user_id` = ?',
267
				'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
268
		$params = [$artistId, $artistId, $userId];
269
		return $this->findEntities($sql, $params, $limit, $offset);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...dMapper::findEntities() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

269
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $params, $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
270
	}
271
272
	/**
273
	 * returns albums of a specified artist
274
	 * The artist must album_artist on the album, artists of individual tracks are not considered
275
	 *
276
	 * @param integer $artistId ID of the artist
277
	 * @param string $userId the user ID
278
	 * @return Album[]
279
	 */
280
	public function findAllByAlbumArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array {
281
		$sql = $this->selectUserEntities('`album_artist_id` = ?');
282
		$params = [$userId, $artistId];
283
		return $this->findEntities($sql, $params, $limit, $offset);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...dMapper::findEntities() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

283
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $params, $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
284
	}
285
286
	/**
287
	 * @return Album[]
288
	 */
289
	public function findAllByGenre(int $genreId, string $userId, int $limit=null, int $offset=null) : array {
290
		$sql = $this->selectUserEntities('EXISTS '.
291
				'(SELECT 1 FROM `*PREFIX*music_tracks` `track`
292
				  WHERE `*PREFIX*music_albums`.`id` = `track`.`album_id`
293
				  AND `track`.`genre_id` = ?)');
294
295
		$params = [$userId, $genreId];
296
		return $this->findEntities($sql, $params, $limit, $offset);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...dMapper::findEntities() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

296
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $params, $limit, $offset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
297
	}
298
299
	/**
300
	 * @return boolean True if one or more albums were influenced
301
	 */
302
	public function updateFolderCover(int $coverFileId, int $folderId) : bool {
303
		$sql = 'SELECT DISTINCT `tracks`.`album_id`
304
				FROM `*PREFIX*music_tracks` `tracks`
305
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
306
				WHERE `files`.`parent` = ?';
307
		$params = [$folderId];
308
		$result = $this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

308
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
309
310
		$updated = false;
311
		if ($result->rowCount()) {
312
			$sql = 'UPDATE `*PREFIX*music_albums`
313
					SET `cover_file_id` = ?
314
					WHERE `cover_file_id` IS NULL AND `id` IN (?)';
315
			$params = [$coverFileId, \join(",", $result->fetchAll(\PDO::FETCH_COLUMN))];
316
			$result = $this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

316
			$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
317
			$updated = $result->rowCount() > 0;
318
		}
319
320
		return $updated;
321
	}
322
323
	/**
324
	 * Set file ID to be used as cover for an album
325
	 */
326
	public function setCover(?int $coverFileId, int $albumId) : void {
327
		$sql = 'UPDATE `*PREFIX*music_albums`
328
				SET `cover_file_id` = ?
329
				WHERE `id` = ?';
330
		$params = [$coverFileId, $albumId];
331
		$this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

331
		/** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
332
	}
333
334
	/**
335
	 * @param integer[] $coverFileIds
336
	 * @param string[]|null $userIds the users whose music library is targeted; all users are targeted if omitted
337
	 * @return Album[] albums which got modified (with incomplete data, only id and user are valid),
338
	 *         empty array if none
339
	 */
340
	public function removeCovers(array $coverFileIds, array $userIds=null) : array {
341
		// find albums using the given file as cover
342
		$sql = 'SELECT `id`, `user_id` FROM `*PREFIX*music_albums` WHERE `cover_file_id` IN ' .
343
			$this->questionMarks(\count($coverFileIds));
344
		$params = $coverFileIds;
345
		if ($userIds !== null) {
346
			$sql .= ' AND `user_id` IN ' . $this->questionMarks(\count($userIds));
347
			$params = \array_merge($params, $userIds);
348
		}
349
		$albums = $this->findEntities($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...dMapper::findEntities() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

349
		$albums = /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
350
351
		// if any albums found, remove the cover from those
352
		$count = \count($albums);
353
		if ($count) {
354
			$sql = 'UPDATE `*PREFIX*music_albums`
355
				SET `cover_file_id` = NULL
356
				WHERE `id` IN ' . $this->questionMarks($count);
357
			$params = Util::extractIds($albums);
358
			$this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

358
			/** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
359
		}
360
361
		return $albums;
362
	}
363
364
	/**
365
	 * @param string|null $userId target user; omit to target all users
366
	 * @return array of dictionaries with keys [albumId, userId, parentFolderId]
367
	 */
368
	public function getAlbumsWithoutCover(string $userId = null) : array {
369
		$sql = 'SELECT DISTINCT `albums`.`id`, `albums`.`user_id`, `files`.`parent`
370
				FROM `*PREFIX*music_albums` `albums`
371
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
372
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
373
				WHERE `albums`.`cover_file_id` IS NULL';
374
		$params = [];
375
		if ($userId !== null) {
376
			$sql .= ' AND `albums`.`user_id` = ?';
377
			$params[] = $userId;
378
		}
379
		$result = $this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

379
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
380
		$return = [];
381
		while ($row = $result->fetch()) {
382
			$return[] = [
383
				'albumId' => (int)$row['id'],
384
				'userId' => $row['user_id'],
385
				'parentFolderId' => (int)$row['parent']
386
			];
387
		}
388
		return $return;
389
	}
390
391
	/**
392
	 * @return boolean True if a cover image was found and added for the album
393
	 */
394
	public function findAlbumCover(int $albumId, int $parentFolderId) : bool {
395
		$return = false;
396
		$imagesSql = 'SELECT `fileid`, `name`
397
					FROM `*PREFIX*filecache`
398
					JOIN `*PREFIX*mimetypes` ON `*PREFIX*mimetypes`.`id` = `*PREFIX*filecache`.`mimetype`
399
					WHERE `parent` = ? AND `*PREFIX*mimetypes`.`mimetype` LIKE \'image%\'';
400
		$params = [$parentFolderId];
401
		$result = $this->execute($imagesSql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

401
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($imagesSql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
402
		$images = $result->fetchAll();
403
		if (\count($images) > 0) {
404
			$getImageRank = function($imageName) {
405
				$coverNames = ['cover', 'albumart', 'album', 'front', 'folder'];
406
				foreach ($coverNames as $i => $coverName) {
407
					if (Util::startsWith($imageName, $coverName, /*$ignoreCase=*/true)) {
408
						return $i;
409
					}
410
				}
411
				return \count($coverNames);
412
			};
413
414
			\usort($images, function ($imageA, $imageB) use ($getImageRank) {
415
				return $getImageRank($imageA['name']) <=> $getImageRank($imageB['name']);
416
			});
417
			$imageId = (int)$images[0]['fileid'];
418
			$this->setCover($imageId, $albumId);
419
			$return = true;
420
		}
421
		return $return;
422
	}
423
424
	/**
425
	 * Given an array of track IDs, find corresponding uniqu album IDs, including only
426
	 * those album which have a cover art set.
427
	 * @param int[] $trackIds
428
	 * @return Album[]
429
	 */
430
	public function findAlbumsWithCoversForTracks(array $trackIds, string $userId, int $limit) : array {
431
		$sql = 'SELECT DISTINCT `albums`.*
432
				FROM `*PREFIX*music_albums` `albums`
433
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
434
				WHERE `albums`.`cover_file_id` IS NOT NULL
435
				AND `albums`.`user_id` = ?
436
				AND `tracks`.`id` IN ' . $this->questionMarks(\count($trackIds));
437
		$params = \array_merge([$userId], $trackIds);
438
439
		return $this->findEntities($sql, $params, $limit);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...dMapper::findEntities() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

439
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $params, $limit);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
440
	}
441
442
	/**
443
	 * Returns the count of albums where the given Artist is featured in
444
	 * @param integer $artistId
445
	 * @return integer
446
	 */
447
	public function countByArtist(int $artistId) : int {
448
		$sql = 'SELECT COUNT(*) AS count FROM (
449
					SELECT DISTINCT `track`.`album_id`
450
					FROM `*PREFIX*music_tracks` `track`
451
					WHERE `track`.`artist_id` = ?
452
						UNION
453
					SELECT `album`.`id`
454
					FROM `*PREFIX*music_albums` `album`
455
					WHERE `album`.`album_artist_id` = ?
456
				) tmp';
457
		$params = [$artistId, $artistId];
458
		$result = $this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

458
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
459
		$row = $result->fetch();
460
		return (int)$row['count'];
461
	}
462
463
	/**
464
	 * Returns the count of albums where the given artist is the album artist
465
	 * @param integer $artistId
466
	 * @return integer
467
	 */
468
	public function countByAlbumArtist(int $artistId) : int {
469
		$sql = 'SELECT COUNT(*) AS count
470
				FROM `*PREFIX*music_albums` `album`
471
				WHERE `album`.`album_artist_id` = ?';
472
		$params = [$artistId];
473
		$result = $this->execute($sql, $params);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...tcloudMapper::execute() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

473
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $params);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
474
		$row = $result->fetch();
475
		return (int)$row['count'];
476
	}
477
478
	/**
479
	 * Overridden from the base implementation to provide support for table-specific rules
480
	 *
481
	 * {@inheritdoc}
482
	 * @see BaseMapper::advFormatSqlCondition()
483
	 */
484
	protected function advFormatSqlCondition(string $rule, string $sqlOp) : string {
485
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
486
		// 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.
487
		switch ($rule) {
488
			case 'artist':			return "LOWER(`artist`.`name`) $sqlOp LOWER(?)";
489
			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(?))";
490
			case 'song':			return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE LOWER(`t`.`title`) $sqlOp LOWER(?))";
491
			case 'year':			// fall through, we only have one kind of year
492
			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)";
493
			case 'songrating':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE `t`.`rating` $sqlOp ?)";
494
			case 'artistrating':	return "`artist`.rating $sqlOp ?";
495
			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)";
496
			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)";
497
			case 'played':			// fall through, we give no access to other people's data; not part of the API spec but Ample uses this
498
			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"
499
			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"
500
			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)";
501
			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)";
502
			case 'genre':			// fall through, alias
503
			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
504
			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(?))";
505
			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') ? '=' : '!=') . ' "")';
506
			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`, '|%')))";
507
			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`, '|%')))";
508
			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(?))";
509
			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)";
510
			case 'mbid_album':		return parent::advFormatSqlCondition('mbid', $sqlOp); // alias
511
			case 'mbid_song':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE `t`.`mbid` $sqlOp ?)";
512
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
513
			case 'has_image':		return "`*PREFIX*music_albums`.`cover_file_id` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
514
			default:				return parent::advFormatSqlCondition($rule, $sqlOp);
515
		}
516
	}
517
518
	/**
519
	 * {@inheritdoc}
520
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
521
	 * @param Album $album
522
	 * @return Album
523
	 */
524
	protected function findUniqueEntity(Entity $album) : Entity {
525
		$sql = $this->selectUserEntities('`*PREFIX*music_albums`.`hash` = ?');
526
		return $this->findEntity($sql, [$album->getUserId(), $album->getHash()]);
0 ignored issues
show
Deprecated Code introduced by
The function OCA\Music\AppFramework\D...oudMapper::findEntity() has been deprecated: 14.0.0 Move over to QBMapper ( Ignorable by Annotation )

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

526
		return /** @scrutinizer ignore-deprecated */ $this->findEntity($sql, [$album->getUserId(), $album->getHash()]);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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

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