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

TrackMapper::findRecentPlay()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 3
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 OCP\IDBConnection;
18
19
/**
20
 * @phpstan-extends BaseMapper<Track>
21
 */
22
class TrackMapper extends BaseMapper {
23
	public function __construct(IDBConnection $db) {
24
		parent::__construct($db, 'music_tracks', Track::class, 'title');
25
	}
26
27
	/**
28
	 * Override the base implementation to include data from multiple tables
29
	 *
30
	 * {@inheritdoc}
31
	 * @see BaseMapper::selectEntities()
32
	 */
33
	protected function selectEntities(string $condition, string $extension=null) : string {
34
		return "SELECT `*PREFIX*music_tracks`.*, `file`.`name` AS `filename`, `file`.`size`, `file`.`mtime` AS `file_mod_time`,
35
						`album`.`name` AS `album_name`, `artist`.`name` AS `artist_name`, `genre`.`name` AS `genre_name`
36
				FROM `*PREFIX*music_tracks`
37
				INNER JOIN `*PREFIX*filecache` `file`
38
				ON `*PREFIX*music_tracks`.`file_id` = `file`.`fileid`
39
				INNER JOIN `*PREFIX*music_albums` `album`
40
				ON `*PREFIX*music_tracks`.`album_id` = `album`.`id`
41
				INNER JOIN `*PREFIX*music_artists` `artist`
42
				ON `*PREFIX*music_tracks`.`artist_id` = `artist`.`id`
43
				LEFT JOIN `*PREFIX*music_genres` `genre`
44
				ON `*PREFIX*music_tracks`.`genre_id` = `genre`.`id`
45
				WHERE $condition $extension";
46
	}
47
48
	/**
49
	 * Overridden from the base implementation to add support for sorting by artist, play_count, and last_played.
50
	 *
51
	 * {@inheritdoc}
52
	 * @see BaseMapper::formatSortingClause()
53
	 */
54
	protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string {
55
		$dir = $invertSort ? 'DESC' : 'ASC';
56
		switch ($sortBy) {
57
			case SortBy::Parent:
58
				// Note: the alternative form "LOWER(`artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046 for a similar case
59
				return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`title`) $dir";
60
			case SortBy::PlayCount:
61
				return "ORDER BY LOWER(`play_count`) $dir";
62
			case SortBy::LastPlayed:
63
				return "ORDER BY LOWER(`last_played`) $dir";
64
			default:
65
				return parent::formatSortingClause($sortBy, $invertSort);
66
		}
67
	}
68
69
	/**
70
	 * Overridden from the base implementation to provide support for table-specific rules
71
	 *
72
	 * {@inheritdoc}
73
	 * @see BaseMapper::advFormatSqlCondition()
74
	 */
75
	protected function advFormatSqlCondition(string $rule, string $sqlOp) : string {
76
		switch ($rule) {
77
			case 'album':			return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE LOWER(`al`.`name`) $sqlOp LOWER(?))";
78
			case 'artist':			return "LOWER(`artist`.`name`) $sqlOp LOWER(?)";
79
			case 'album_artist':	return "`album_id` IN (SELECT `al`.`id` from `*PREFIX*music_albums` `al` JOIN `*PREFIX*music_artists` `ar` ON `al`.`album_artist_id` = `ar`.`id` WHERE LOWER(`ar`.`name`) $sqlOp LOWER(?))";
80
			case 'track':			return "`number` $sqlOp ?";
81
			case 'year':			return "`year` $sqlOp ?";
82
			case 'favorite_album':	return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE LOWER(`al`.`name`) $sqlOp LOWER(?) AND `al`.`starred` IS NOT NULL)";
83
			case 'favorite_artist':	return "`artist_id` IN (SELECT `id` from `*PREFIX*music_artists` `ar` WHERE LOWER(`ar`.`name`) $sqlOp LOWER(?) AND `ar`.`starred` IS NOT NULL)";
84
			case 'played_times':	return "`play_count` $sqlOp ?";
85
			case 'last_play':		return "`last_played` $sqlOp ?";
86
			case 'played':			// fall through, we give no access to other people's data
87
			case 'myplayed':		return "`last_played` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
88
			//case 'myplayedalbum':
89
			//case 'myplayedartist':
90
			case 'time':			return "`length` $sqlOp ?";
91
			case 'genre':			// fall through
92
			case 'song_genre':		return "LOWER(`genre`.`name`) $sqlOp LOWER(?)";
93
			//case 'albumgenre':
94
			//case 'artistgenre':
95
			case 'no_genre':		return ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""';
96
			//case 'playlist':
97
			//case 'playlist_name':	TODO: pattern to match against playlist track_ids somethign like: ('%|' || CONVERT(varchar(10), `*PREFIX*music_tracks`.`id`) || '|%')
98
			case 'recent_played':	return "`*PREFIX*music_tracks`.`id` IN (SELECT * FROM (SELECT `id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ? ORDER BY `last_played` DESC LIMIT $sqlOp) mysqlhack)";
99
			case 'file':			return "LOWER(`file`.`name`) $sqlOp LOWER(?)";
100
			case 'mbid_song':		return parent::advFormatSqlCondition('mbid', $sqlOp); // alias
101
			case 'mbid_album':		return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE `al`.`mbid` $sqlOp ?)";
102
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
103
			default:				return parent::advFormatSqlCondition($rule, $sqlOp);
104
		}
105
	}
106
107
	/**
108
	 * Returns all tracks of the given artist (both album and track artists are considered)
109
	 * @return Track[]
110
	 */
111
	public function findAllByArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array {
112
		$sql = $this->selectUserEntities(
113
				'`artist_id` = ? OR `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` = ?) ',
114
				'ORDER BY LOWER(`title`)');
115
		$params = [$userId, $artistId, $artistId];
116
		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

116
		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...
117
	}
118
119
	/**
120
	 * @return Track[]
121
	 */
122
	public function findAllByAlbum(int $albumId, string $userId, ?int $artistId=null, ?int $limit=null, ?int $offset=null) : array {
123
		$condition = '`album_id` = ?';
124
		$params = [$userId, $albumId];
125
126
		if ($artistId !== null) {
127
			$condition .= ' AND `artist_id` = ? ';
128
			$params[] = $artistId;
129
		}
130
131
		$sql = $this->selectUserEntities($condition,
132
				'ORDER BY `*PREFIX*music_tracks`.`disk`, `number`, LOWER(`title`)');
133
		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

133
		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...
134
	}
135
136
	/**
137
	 * @return Track[]
138
	 */
139
	public function findAllByFolder(int $folderId, string $userId, ?int $limit=null, ?int $offset=null) : array {
140
		$sql = $this->selectUserEntities('`file`.`parent` = ?', 'ORDER BY LOWER(`title`)');
141
		$params = [$userId, $folderId];
142
		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

142
		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...
143
	}
144
145
	/**
146
	 * @return Track[]
147
	 */
148
	public function findAllByGenre(int $genreId, string $userId, ?int $limit=null, ?int $offset=null) : array {
149
		$sql = $this->selectUserEntities('`genre_id` = ?', 'ORDER BY LOWER(`title`)');
150
		$params = [$userId, $genreId];
151
		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

151
		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...
152
	}
153
154
	/**
155
	 * @param string $userId
156
	 * @return int[]
157
	 */
158
	public function findAllFileIds(string $userId) : array {
159
		$sql = 'SELECT `file_id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ?';
160
		$result = $this->execute($sql, [$userId]);
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

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

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...
161
162
		return \array_map(function ($i) {
163
			return (int)$i['file_id'];
164
		}, $result->fetchAll());
165
	}
166
167
	/**
168
	 * Find a track of user matching a file ID
169
	 * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
170
	 */
171
	public function findByFileId(int $fileId, string $userId) : Track {
172
		$sql = $this->selectUserEntities('`file_id` = ?');
173
		$params = [$userId, $fileId];
174
		return $this->findEntity($sql, $params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findEntity($sql, $params) returns the type OCP\AppFramework\Db\Entity which includes types incompatible with the type-hinted return OCA\Music\Db\Track.
Loading history...
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

174
		return /** @scrutinizer ignore-deprecated */ $this->findEntity($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...
175
	}
176
177
	/**
178
	 * Find tracks of user with multiple file IDs
179
	 * @param integer[] $fileIds
180
	 * @param string[] $userIds
181
	 * @return Track[]
182
	 */
183
	public function findByFileIds(array $fileIds, array $userIds) : array {
184
		$sql = $this->selectEntities(
185
				'`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) .
186
				' AND `file_id` IN '. $this->questionMarks(\count($fileIds)));
187
		$params = \array_merge($userIds, $fileIds);
188
		return $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

188
		return /** @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...
189
	}
190
191
	/**
192
	 * Finds tracks of all users matching one or multiple file IDs
193
	 * @param integer[] $fileIds
194
	 * @return Track[]
195
	 */
196
	public function findAllByFileIds(array $fileIds) : array {
197
		$sql = $this->selectEntities('`file_id` IN '.
198
				$this->questionMarks(\count($fileIds)));
199
		return $this->findEntities($sql, $fileIds);
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

199
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($sql, $fileIds);

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...
200
	}
201
202
	public function countByArtist(int $artistId) : int {
203
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
204
		$result = $this->execute($sql, [$artistId]);
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

204
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$artistId]);

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...
205
		$row = $result->fetch();
206
		return (int)$row['count'];
207
	}
208
209
	public function countByAlbum(int $albumId) : int {
210
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
211
		$result = $this->execute($sql, [$albumId]);
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

211
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$albumId]);

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...
212
		$row = $result->fetch();
213
		return (int)$row['count'];
214
	}
215
216
	/**
217
	 * @return integer Duration in seconds
218
	 */
219
	public function totalDurationOfAlbum(int $albumId) : int {
220
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
221
		$result = $this->execute($sql, [$albumId]);
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

221
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$albumId]);

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...
222
		$row = $result->fetch();
223
		return (int)$row['duration'];
224
	}
225
226
	/**
227
	 * @return integer Duration in seconds
228
	 */
229
	public function totalDurationByArtist(int $artistId) : int {
230
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
231
		$result = $this->execute($sql, [$artistId]);
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

231
		$result = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$artistId]);

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...
232
		$row = $result->fetch();
233
		return (int)$row['duration'];
234
	}
235
236
	/**
237
	 * Get durations of the given tracks.
238
	 * @param integer[] $trackIds
239
	 * @return array {int => int} where keys are track IDs and values are corresponding durations
240
	 */
241
	public function getDurations(array $trackIds) : array {
242
		$result = [];
243
244
		if (!empty($trackIds)) {
245
			$sql = 'SELECT `id`, `length` FROM `*PREFIX*music_tracks` WHERE `id` IN ' .
246
						$this->questionMarks(\count($trackIds));
247
			$rows = $this->execute($sql, $trackIds)->fetchAll();
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

247
			$rows = /** @scrutinizer ignore-deprecated */ $this->execute($sql, $trackIds)->fetchAll();

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...
248
			foreach ($rows as $row) {
249
				$result[$row['id']] = (int)$row['length'];
250
			}
251
		}
252
		return $result;
253
	}
254
255
	/**
256
	 * @return Track[]
257
	 */
258
	public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) {
259
		$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR
260
						LOWER(`album`.`name`) LIKE LOWER(?) OR
261
						LOWER(`title`) LIKE LOWER(?) )';
262
		$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`title`)');
263
		$name = BaseMapper::prepareSubstringSearchPattern($name);
264
		$params = [$userId, $name, $name, $name];
265
		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

265
		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...
266
	}
267
268
	/**
269
	 * Returns all tracks specified by name and/or artist name
270
	 * @param string|null $name the name of the track
271
	 * @param string|null $artistName the name of the artist
272
	 * @param string $userId the name of the user
273
	 * @return Track[] Tracks matching the criteria
274
	 */
275
	public function findAllByNameAndArtistName(?string $name, ?string $artistName, string $userId) : array {
276
		$sqlConditions = [];
277
		$params = [$userId];
278
279
		if (!empty($name)) {
280
			$sqlConditions[] = '`title` = ?';
281
			$params[] = $name;
282
		}
283
284
		if (!empty($artistName)) {
285
			$sqlConditions[] = '`artist`.`name` = ?';
286
			$params[] = $artistName;
287
		}
288
289
		// at least one condition has to be given, otherwise return an empty set
290
		if (\count($sqlConditions) > 0) {
291
			$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions));
292
			return $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

292
			return /** @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...
293
		} else {
294
			return [];
295
		}
296
	}
297
298
	/**
299
	 * Returns all tracks specified by various criteria, all of which are optional
300
	 * @param int[] $genres Array of genre IDs
301
	 * @param int[] $artists Array of artist IDs
302
	 * @param int|null $fromYear Earliest release year to include
303
	 * @param int|null $toYear Latest release year to include
304
	 * @param int $sortBy Sorting rule as defined in the class SortBy
305
	 * @param string $userId the name of the user
306
	 * @return Track[] Tracks matching the criteria
307
	 */
308
	public function findAllByCriteria(
309
			array $genres, array $artists, ?int $fromYear, ?int $toYear,
310
			int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array {
311
312
		$sqlConditions = [];
313
		$params = [$userId];
314
315
		if (!empty($genres)) {
316
			$sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres));
317
			$params = \array_merge($params, $genres);
318
		}
319
320
		if (!empty($artists)) {
321
			$sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists));
322
			$params = \array_merge($params, $artists);
323
		}
324
325
		if (!empty($fromYear)) {
326
			$sqlConditions[] = '`year` >= ?';
327
			$params[] = $fromYear;
328
		}
329
330
		if (!empty($toYear)) {
331
			$sqlConditions[] = '`year` <= ?';
332
			$params[] = $toYear;
333
		}
334
335
		$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort));
336
		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

336
		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...
337
	}
338
339
	/**
340
	 * Find most frequently played tracks
341
	 * @return Track[]
342
	 */
343
	public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
344
		$sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)');
345
		return $this->findEntities($sql, [$userId], $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

345
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($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...
346
	}
347
348
	/**
349
	 * Find most recently played tracks
350
	 * @return Track[]
351
	 */
352
	public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
353
		$sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC');
354
		return $this->findEntities($sql, [$userId], $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

354
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($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...
355
	}
356
357
	/**
358
	 * Find least recently played tracks
359
	 * @return Track[]
360
	 */
361
	public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
362
		$sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC');
363
		return $this->findEntities($sql, [$userId], $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

363
		return /** @scrutinizer ignore-deprecated */ $this->findEntities($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...
364
	}
365
366
	/**
367
	 * Finds all track IDs of the user along with the parent folder ID of each track
368
	 * @return array where keys are folder IDs and values are arrays of track IDs
369
	 */
370
	public function findTrackAndFolderIds(string $userId) : array {
371
		$sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS parent
372
				FROM `*PREFIX*music_tracks` `track`
373
				JOIN `*PREFIX*filecache` `file`
374
				ON `track`.`file_id` = `file`.`fileid`
375
				WHERE `track`.`user_id` = ?';
376
377
		$rows = $this->execute($sql, [$userId])->fetchAll();
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

377
		$rows = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId])->fetchAll();

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...
378
379
		// Sort the results according the file names. This can't be made using ORDERBY in the
380
		// SQL query because then we couldn't use the "natural order" comparison algorithm
381
		\usort($rows, function ($a, $b) {
382
			return \strnatcasecmp($a['filename'], $b['filename']);
383
		});
384
385
		// group the files to parent folder "buckets"
386
		$result = [];
387
		foreach ($rows as $row) {
388
			$result[(int)$row['parent']][] = (int)$row['id'];
389
		}
390
391
		return $result;
392
	}
393
394
	/**
395
	 * Find names and parents of the file system nodes with given IDs within the given storage
396
	 * @param int[] $nodeIds
397
	 * @param string $storageId
398
	 * @return array where keys are the node IDs and values are associative arrays
399
	 *         like { 'name' => string, 'parent' => int };
400
	 */
401
	public function findNodeNamesAndParents(array $nodeIds, string $storageId) : array {
402
		$result = [];
403
404
		if (!empty($nodeIds)) {
405
			$sql = 'SELECT `fileid`, `name`, `parent` '.
406
					'FROM `*PREFIX*filecache` `filecache` '.
407
					'JOIN `*PREFIX*storages` `storages` '.
408
					'ON `filecache`.`storage` = `storages`.`numeric_id` '.
409
					'WHERE `storages`.`id` = ? '.
410
					'AND `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds));
411
412
			$rows = $this->execute($sql, \array_merge([$storageId], $nodeIds))->fetchAll();
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

412
			$rows = /** @scrutinizer ignore-deprecated */ $this->execute($sql, \array_merge([$storageId], $nodeIds))->fetchAll();

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...
413
414
			foreach ($rows as $row) {
415
				$result[$row['fileid']] = [
416
					'name' => $row['name'],
417
					'parent' => (int)$row['parent']
418
				];
419
			}
420
		}
421
422
		return $result;
423
	}
424
425
	/**
426
	 * Returns all genre IDs associated with the given artist
427
	 * @return int[]
428
	 */
429
	public function getGenresByArtistId(int $artistId, string $userId) : array {
430
		$sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE
431
				`genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?';
432
		$rows = $this->execute($sql, [$userId, $artistId]);
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

432
		$rows = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId, $artistId]);

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...
433
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
434
	}
435
436
	/**
437
	 * Returns all tracks IDs of the user, organized by the genre_id.
438
	 * @return array where keys are genre IDs and values are arrays of track IDs
439
	 */
440
	public function mapGenreIdsToTrackIds(string $userId) : array {
441
		$sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks`
442
				WHERE `genre_id` IS NOT NULL and `user_id` = ?';
443
		$rows = $this->execute($sql, [$userId])->fetchAll();
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

443
		$rows = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId])->fetchAll();

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...
444
445
		$result = [];
446
		foreach ($rows as $row) {
447
			$result[(int)$row['genre_id']][] = (int)$row['id'];
448
		}
449
450
		return $result;
451
	}
452
453
	/**
454
	 * Returns file IDs of the tracks which do not have genre scanned. This is not the same
455
	 * thing as unknown genre, which means that the genre has been scanned but was not found
456
	 * from the track metadata.
457
	 * @return int[]
458
	 */
459
	public function findFilesWithoutScannedGenre(string $userId) : array {
460
		$sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track`
461
				INNER JOIN `*PREFIX*filecache` `file`
462
				ON `track`.`file_id` = `file`.`fileid`
463
				WHERE `genre_id` IS NULL and `user_id` = ?';
464
		$rows = $this->execute($sql, [$userId]);
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

464
		$rows = /** @scrutinizer ignore-deprecated */ $this->execute($sql, [$userId]);

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...
465
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
466
	}
467
468
	/**
469
	 * Update "last played" timestamp and increment the total play count of the track.
470
	 * The DB row is updated *without* updating the `updated` column.
471
	 * @return bool true if the track was found and updated, false otherwise
472
	 */
473
	public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool {
474
		$sql = 'UPDATE `*PREFIX*music_tracks`
475
				SET `last_played` = ?, `play_count` = `play_count` + 1
476
				WHERE `user_id` = ? AND `id` = ?';
477
		$params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId];
478
		$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

478
		$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...
479
		return ($result->rowCount() > 0);
480
	}
481
482
	/**
483
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
484
	 * @param Track $track
485
	 * @return Track
486
	 */
487
	protected function findUniqueEntity(Entity $track) : Entity {
488
		return $this->findByFileId($track->getFileId(), $track->getUserId());
0 ignored issues
show
Bug introduced by
It seems like $track->getFileId() can also be of type null; however, parameter $fileId of OCA\Music\Db\TrackMapper::findByFileId() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

488
		return $this->findByFileId(/** @scrutinizer ignore-type */ $track->getFileId(), $track->getUserId());
Loading history...
Bug introduced by
The method getFileId() 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

488
		return $this->findByFileId($track->/** @scrutinizer ignore-call */ getFileId(), $track->getUserId());
Loading history...
489
	}
490
}
491