Passed
Push — feature/909_Ampache_API_improv... ( cb99df...a84036 )
by Pauli
02:41
created

TrackMapper::formatAdvSearchAnywhereCond()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 12
nc 1
nop 1
dl 0
loc 16
rs 9.8666
c 1
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 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
	 * Returns all tracks of the given artist (both album and track artists are considered)
71
	 * @return Track[]
72
	 */
73
	public function findAllByArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array {
74
		$sql = $this->selectUserEntities(
75
				'`artist_id` = ? OR `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` = ?) ',
76
				'ORDER BY LOWER(`title`)');
77
		$params = [$userId, $artistId, $artistId];
78
		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

78
		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...
79
	}
80
81
	/**
82
	 * @return Track[]
83
	 */
84
	public function findAllByAlbum(int $albumId, string $userId, ?int $artistId=null, ?int $limit=null, ?int $offset=null) : array {
85
		$condition = '`album_id` = ?';
86
		$params = [$userId, $albumId];
87
88
		if ($artistId !== null) {
89
			$condition .= ' AND `artist_id` = ? ';
90
			$params[] = $artistId;
91
		}
92
93
		$sql = $this->selectUserEntities($condition,
94
				'ORDER BY `*PREFIX*music_tracks`.`disk`, `number`, LOWER(`title`)');
95
		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

95
		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...
96
	}
97
98
	/**
99
	 * @return Track[]
100
	 */
101
	public function findAllByFolder(int $folderId, string $userId, ?int $limit=null, ?int $offset=null) : array {
102
		$sql = $this->selectUserEntities('`file`.`parent` = ?', 'ORDER BY LOWER(`title`)');
103
		$params = [$userId, $folderId];
104
		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

104
		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...
105
	}
106
107
	/**
108
	 * @return Track[]
109
	 */
110
	public function findAllByGenre(int $genreId, string $userId, ?int $limit=null, ?int $offset=null) : array {
111
		$sql = $this->selectUserEntities('`genre_id` = ?', 'ORDER BY LOWER(`title`)');
112
		$params = [$userId, $genreId];
113
		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

113
		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...
114
	}
115
116
	/**
117
	 * @param string $userId
118
	 * @return int[]
119
	 */
120
	public function findAllFileIds(string $userId) : array {
121
		$sql = 'SELECT `file_id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ?';
122
		$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

122
		$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...
123
124
		return \array_map(function ($i) {
125
			return (int)$i['file_id'];
126
		}, $result->fetchAll());
127
	}
128
129
	/**
130
	 * Find a track of user matching a file ID
131
	 * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
132
	 */
133
	public function findByFileId(int $fileId, string $userId) : Track {
134
		$sql = $this->selectUserEntities('`file_id` = ?');
135
		$params = [$userId, $fileId];
136
		return $this->findEntity($sql, $params);
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

136
		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...
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...
137
	}
138
139
	/**
140
	 * Find tracks of user with multiple file IDs
141
	 * @param integer[] $fileIds
142
	 * @param string[] $userIds
143
	 * @return Track[]
144
	 */
145
	public function findByFileIds(array $fileIds, array $userIds) : array {
146
		$sql = $this->selectEntities(
147
				'`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) .
148
				' AND `file_id` IN '. $this->questionMarks(\count($fileIds)));
149
		$params = \array_merge($userIds, $fileIds);
150
		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

150
		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...
151
	}
152
153
	/**
154
	 * Finds tracks of all users matching one or multiple file IDs
155
	 * @param integer[] $fileIds
156
	 * @return Track[]
157
	 */
158
	public function findAllByFileIds(array $fileIds) : array {
159
		$sql = $this->selectEntities('`file_id` IN '.
160
				$this->questionMarks(\count($fileIds)));
161
		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

161
		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...
162
	}
163
164
	public function countByArtist(int $artistId) : int {
165
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
166
		$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

166
		$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...
167
		$row = $result->fetch();
168
		return (int)$row['count'];
169
	}
170
171
	public function countByAlbum(int $albumId) : int {
172
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
173
		$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

173
		$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...
174
		$row = $result->fetch();
175
		return (int)$row['count'];
176
	}
177
178
	/**
179
	 * @return integer Duration in seconds
180
	 */
181
	public function totalDurationOfAlbum(int $albumId) : int {
182
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
183
		$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

183
		$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...
184
		$row = $result->fetch();
185
		return (int)$row['duration'];
186
	}
187
188
	/**
189
	 * @return integer Duration in seconds
190
	 */
191
	public function totalDurationByArtist(int $artistId) : int {
192
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
193
		$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

193
		$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...
194
		$row = $result->fetch();
195
		return (int)$row['duration'];
196
	}
197
198
	/**
199
	 * Get durations of the given tracks.
200
	 * @param integer[] $trackIds
201
	 * @return array {int => int} where keys are track IDs and values are corresponding durations
202
	 */
203
	public function getDurations(array $trackIds) : array {
204
		$result = [];
205
206
		if (!empty($trackIds)) {
207
			$sql = 'SELECT `id`, `length` FROM `*PREFIX*music_tracks` WHERE `id` IN ' .
208
						$this->questionMarks(\count($trackIds));
209
			$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

209
			$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...
210
			foreach ($rows as $row) {
211
				$result[$row['id']] = (int)$row['length'];
212
			}
213
		}
214
		return $result;
215
	}
216
217
	/**
218
	 * @return Track[]
219
	 */
220
	public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) {
221
		$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR
222
						LOWER(`album`.`name`) LIKE LOWER(?) OR
223
						LOWER(`title`) LIKE LOWER(?) )';
224
		$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`title`)');
225
		$name = BaseMapper::prepareSubstringSearchPattern($name);
226
		$params = [$userId, $name, $name, $name];
227
		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

227
		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...
228
	}
229
230
	/**
231
	 * Returns all tracks specified by name and/or artist name
232
	 * @param string|null $name the name of the track
233
	 * @param string|null $artistName the name of the artist
234
	 * @param string $userId the name of the user
235
	 * @return Track[] Tracks matching the criteria
236
	 */
237
	public function findAllByNameAndArtistName(?string $name, ?string $artistName, string $userId) : array {
238
		$sqlConditions = [];
239
		$params = [$userId];
240
241
		if (!empty($name)) {
242
			$sqlConditions[] = '`title` = ?';
243
			$params[] = $name;
244
		}
245
246
		if (!empty($artistName)) {
247
			$sqlConditions[] = '`artist`.`name` = ?';
248
			$params[] = $artistName;
249
		}
250
251
		// at least one condition has to be given, otherwise return an empty set
252
		if (\count($sqlConditions) > 0) {
253
			$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions));
254
			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

254
			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...
255
		} else {
256
			return [];
257
		}
258
	}
259
260
	/**
261
	 * Returns all tracks specified by various criteria, all of which are optional
262
	 * @param int[] $genres Array of genre IDs
263
	 * @param int[] $artists Array of artist IDs
264
	 * @param int|null $fromYear Earliest release year to include
265
	 * @param int|null $toYear Latest release year to include
266
	 * @param int $sortBy Sorting rule as defined in the class SortBy
267
	 * @param string $userId the name of the user
268
	 * @return Track[] Tracks matching the criteria
269
	 */
270
	public function findAllByCriteria(
271
			array $genres, array $artists, ?int $fromYear, ?int $toYear,
272
			int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array {
273
274
		$sqlConditions = [];
275
		$params = [$userId];
276
277
		if (!empty($genres)) {
278
			$sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres));
279
			$params = \array_merge($params, $genres);
280
		}
281
282
		if (!empty($artists)) {
283
			$sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists));
284
			$params = \array_merge($params, $artists);
285
		}
286
287
		if (!empty($fromYear)) {
288
			$sqlConditions[] = '`year` >= ?';
289
			$params[] = $fromYear;
290
		}
291
292
		if (!empty($toYear)) {
293
			$sqlConditions[] = '`year` <= ?';
294
			$params[] = $toYear;
295
		}
296
297
		$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort));
298
		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

298
		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...
299
	}
300
301
	/**
302
	 * Find most frequently played tracks
303
	 * @return Track[]
304
	 */
305
	public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
306
		$sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)');
307
		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

307
		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...
308
	}
309
310
	/**
311
	 * Find most recently played tracks
312
	 * @return Track[]
313
	 */
314
	public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
315
		$sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC');
316
		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

316
		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...
317
	}
318
319
	/**
320
	 * Find least recently played tracks
321
	 * @return Track[]
322
	 */
323
	public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
324
		$sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC');
325
		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

325
		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...
326
	}
327
328
	/**
329
	 * Finds all track IDs of the user along with the parent folder ID of each track
330
	 * @return array where keys are folder IDs and values are arrays of track IDs
331
	 */
332
	public function findTrackAndFolderIds(string $userId) : array {
333
		$sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS parent
334
				FROM `*PREFIX*music_tracks` `track`
335
				JOIN `*PREFIX*filecache` `file`
336
				ON `track`.`file_id` = `file`.`fileid`
337
				WHERE `track`.`user_id` = ?';
338
339
		$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

339
		$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...
340
341
		// Sort the results according the file names. This can't be made using ORDERBY in the
342
		// SQL query because then we couldn't use the "natural order" comparison algorithm
343
		\usort($rows, function ($a, $b) {
344
			return \strnatcasecmp($a['filename'], $b['filename']);
345
		});
346
347
		// group the files to parent folder "buckets"
348
		$result = [];
349
		foreach ($rows as $row) {
350
			$result[(int)$row['parent']][] = (int)$row['id'];
351
		}
352
353
		return $result;
354
	}
355
356
	/**
357
	 * Find names and parents of the file system nodes with given IDs within the given storage
358
	 * @param int[] $nodeIds
359
	 * @param string $storageId
360
	 * @return array where keys are the node IDs and values are associative arrays
361
	 *         like { 'name' => string, 'parent' => int };
362
	 */
363
	public function findNodeNamesAndParents(array $nodeIds, string $storageId) : array {
364
		$result = [];
365
366
		if (!empty($nodeIds)) {
367
			$sql = 'SELECT `fileid`, `name`, `parent` '.
368
					'FROM `*PREFIX*filecache` `filecache` '.
369
					'JOIN `*PREFIX*storages` `storages` '.
370
					'ON `filecache`.`storage` = `storages`.`numeric_id` '.
371
					'WHERE `storages`.`id` = ? '.
372
					'AND `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds));
373
374
			$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

374
			$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...
375
376
			foreach ($rows as $row) {
377
				$result[$row['fileid']] = [
378
					'name' => $row['name'],
379
					'parent' => (int)$row['parent']
380
				];
381
			}
382
		}
383
384
		return $result;
385
	}
386
387
	/**
388
	 * Returns all genre IDs associated with the given artist
389
	 * @return int[]
390
	 */
391
	public function getGenresByArtistId(int $artistId, string $userId) : array {
392
		$sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE
393
				`genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?';
394
		$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

394
		$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...
395
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
396
	}
397
398
	/**
399
	 * Returns all tracks IDs of the user, organized by the genre_id.
400
	 * @return array where keys are genre IDs and values are arrays of track IDs
401
	 */
402
	public function mapGenreIdsToTrackIds(string $userId) : array {
403
		$sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks`
404
				WHERE `genre_id` IS NOT NULL and `user_id` = ?';
405
		$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

405
		$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...
406
407
		$result = [];
408
		foreach ($rows as $row) {
409
			$result[(int)$row['genre_id']][] = (int)$row['id'];
410
		}
411
412
		return $result;
413
	}
414
415
	/**
416
	 * Returns file IDs of the tracks which do not have genre scanned. This is not the same
417
	 * thing as unknown genre, which means that the genre has been scanned but was not found
418
	 * from the track metadata.
419
	 * @return int[]
420
	 */
421
	public function findFilesWithoutScannedGenre(string $userId) : array {
422
		$sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track`
423
				INNER JOIN `*PREFIX*filecache` `file`
424
				ON `track`.`file_id` = `file`.`fileid`
425
				WHERE `genre_id` IS NULL and `user_id` = ?';
426
		$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

426
		$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...
427
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
428
	}
429
430
	/**
431
	 * Update "last played" timestamp and increment the total play count of the track.
432
	 * The DB row is updated *without* updating the `updated` column.
433
	 * @return bool true if the track was found and updated, false otherwise
434
	 */
435
	public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool {
436
		$sql = 'UPDATE `*PREFIX*music_tracks`
437
				SET `last_played` = ?, `play_count` = `play_count` + 1
438
				WHERE `user_id` = ? AND `id` = ?';
439
		$params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId];
440
		$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

440
		$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...
441
		return ($result->rowCount() > 0);
442
	}
443
444
	/**
445
	 * Overridden from the base implementation to provide support for table-specific rules
446
	 *
447
	 * {@inheritdoc}
448
	 * @see BaseMapper::advFormatSqlCondition()
449
	 */
450
	protected function advFormatSqlCondition(string $rule, string $sqlOp) : string {
451
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
452
		switch ($rule) {
453
			case 'anywhere':		return self::formatAdvSearchAnywhereCond($sqlOp); 
454
			case 'album':			return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE LOWER(`al`.`name`) $sqlOp LOWER(?))";
455
			case 'artist':			return "LOWER(`artist`.`name`) $sqlOp LOWER(?)";
456
			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(?))";
457
			case 'track':			return "`number` $sqlOp ?";
458
			case 'year':			return "`year` $sqlOp ?";
459
			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)";
460
			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)";
461
			case 'played_times':	return "`play_count` $sqlOp ?";
462
			case 'last_play':		return "`last_played` $sqlOp ?";
463
			case 'played':			// fall through, we give no access to other people's data
464
			case 'myplayed':		return "`last_played` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
465
			case 'myplayedalbum':	return "`album_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"
466
			case 'myplayedartist':	return "`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"
467
			case 'time':			return "`length` $sqlOp ?";
468
			case 'genre':			// fall through
469
			case 'song_genre':		return "LOWER(`genre`.`name`) $sqlOp LOWER(?)";
470
			case 'album_genre':		return "`album_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
471
			case 'artist_genre':	return "`artist_id` IN (SELECT * FROM (SELECT `artist_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` GROUP BY `artist_id` HAVING LOWER(GROUP_CONCAT(`g`.`name`)) $sqlOp LOWER(?)) mysqlhack)"; // GROUP_CONCAT not available on PostgreSQL
472
			case 'no_genre':		return ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""';
473
			case 'playlist':		return "$sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE CONCAT('%|',`*PREFIX*music_tracks`.`id`, '|%'))";
474
			case 'playlist_name':	return "EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`name` $sqlOp ? AND `p`.`track_ids` LIKE CONCAT('%|',`*PREFIX*music_tracks`.`id`, '|%'))";
475
			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)";
476
			case 'file':			return "LOWER(`file`.`name`) $sqlOp LOWER(?)";
477
			case 'mbid_song':		return parent::advFormatSqlCondition('mbid', $sqlOp); // alias
478
			case 'mbid_album':		return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE `al`.`mbid` $sqlOp ?)";
479
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
480
			default:				return parent::advFormatSqlCondition($rule, $sqlOp);
481
		}
482
	}
483
484
	private static function formatAdvSearchAnywhereCond(string $sqlOp) : string {
485
		$fields = [
486
			"`*PREFIX*music_tracks`.`title`",
487
			"`file`.`name`",
488
			"`artist`.`name`",
489
			"`album`.`name`",
490
			"`genre`.`name`"
491
		];
492
		$parts = \array_map(function(string $field) use ($sqlOp) {
493
			return "LOWER($field) $sqlOp LOWER(?)";
494
		}, $fields);
495
496
		$negativeOp = \in_array($sqlOp, ['NOT LIKE', '!=', 'NOT SOUNDS LIKE', 'NOT REGEXP']);
497
		$cond = \implode($negativeOp ? ' AND ' : ' OR ', $parts);
498
499
		return "($cond)";
500
	}
501
502
	/**
503
	 * {@inheritdoc}
504
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
505
	 * @param Track $track
506
	 * @return Track
507
	 */
508
	protected function findUniqueEntity(Entity $track) : Entity {
509
		return $this->findByFileId($track->getFileId(), $track->getUserId());
0 ignored issues
show
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

509
		return $this->findByFileId($track->/** @scrutinizer ignore-call */ getFileId(), $track->getUserId());
Loading history...
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

509
		return $this->findByFileId(/** @scrutinizer ignore-type */ $track->getFileId(), $track->getUserId());
Loading history...
510
	}
511
}
512