Total Complexity | 60 |
Total Lines | 539 |
Duplicated Lines | 0 % |
Changes | 10 | ||
Bugs | 0 | Features | 0 |
Complex classes like TrackMapper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TrackMapper, and based on these observations, apply Extract Interface, too.
1 | <?php declare(strict_types=1); |
||
25 | class TrackMapper extends BaseMapper { |
||
26 | public function __construct(IDBConnection $db, IConfig $config) { |
||
27 | parent::__construct($db, $config, 'music_tracks', Track::class, 'title', ['file_id', 'user_id'], 'album_id'); |
||
28 | } |
||
29 | |||
30 | /** |
||
31 | * Override the base implementation to include data from multiple tables |
||
32 | * |
||
33 | * {@inheritdoc} |
||
34 | * @see BaseMapper::selectEntities() |
||
35 | */ |
||
36 | protected function selectEntities(string $condition, ?string $extension=null) : string { |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * Overridden from the base implementation to add support for sorting by artist, play_count, and last_played. |
||
53 | * |
||
54 | * {@inheritdoc} |
||
55 | * @see BaseMapper::formatSortingClause() |
||
56 | */ |
||
57 | protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string { |
||
58 | switch ($sortBy) { |
||
59 | case SortBy::Parent: |
||
60 | $dir = $invertSort ? 'DESC' : 'ASC'; |
||
61 | // Note: the alternative form "LOWER(`artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046 for a similar case |
||
62 | return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`title`) $dir"; |
||
63 | case SortBy::PlayCount: |
||
64 | $dir = $invertSort ? 'ASC' : 'DESC'; |
||
65 | return "ORDER BY `play_count` $dir"; |
||
66 | case SortBy::LastPlayed: |
||
67 | $dir = $invertSort ? 'ASC' : 'DESC'; |
||
68 | return "ORDER BY `last_played` $dir"; |
||
69 | default: |
||
70 | return parent::formatSortingClause($sortBy, $invertSort); |
||
71 | } |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Returns all tracks of the given artist (both album and track artists are considered) |
||
76 | * @param int[] $artistIds |
||
77 | * @return Track[] |
||
78 | */ |
||
79 | public function findAllByArtist(array $artistIds, string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
80 | $questionMarks = $this->questionMarks(\count($artistIds)); |
||
81 | $sql = $this->selectUserEntities( |
||
82 | "`artist_id` IN $questionMarks OR `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` IN $questionMarks)", |
||
83 | 'ORDER BY LOWER(`title`)'); |
||
84 | $params = \array_merge([$userId], $artistIds, $artistIds); |
||
85 | return $this->findEntities($sql, $params, $limit, $offset); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * @param int[] $albumIds |
||
90 | * @return Track[] |
||
91 | */ |
||
92 | public function findAllByAlbum(array $albumIds, string $userId, ?int $artistId=null, ?int $limit=null, ?int $offset=null) : array { |
||
93 | $condition = '`album_id` IN ' . $this->questionMarks(\count($albumIds)); |
||
94 | $params = \array_merge([$userId], $albumIds); |
||
95 | |||
96 | if ($artistId !== null) { |
||
97 | $condition .= ' AND `artist_id` = ? '; |
||
98 | $params[] = $artistId; |
||
99 | } |
||
100 | |||
101 | $sql = $this->selectUserEntities($condition, |
||
102 | 'ORDER BY `*PREFIX*music_tracks`.`disk`, `number`, LOWER(`file`.`name`)'); |
||
103 | return $this->findEntities($sql, $params, $limit, $offset); |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * @return Track[] |
||
108 | */ |
||
109 | public function findAllByFolder(int $folderId, string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
110 | $sql = $this->selectUserEntities('`file`.`parent` = ?', 'ORDER BY LOWER(`file`.`name`)'); |
||
111 | $params = [$userId, $folderId]; |
||
112 | return $this->findEntities($sql, $params, $limit, $offset); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * @return Track[] |
||
117 | */ |
||
118 | public function findAllByGenre(int $genreId, string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
122 | } |
||
123 | |||
124 | /** |
||
125 | * @return int[] |
||
126 | */ |
||
127 | public function findAllFileIds(string $userId) : array { |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * @return int[] |
||
135 | */ |
||
136 | public function findDirtyFileIds(string $userId) : array { |
||
137 | $updatedEpoch = $this->sqlDateToEpoch('`track`.`updated`'); |
||
138 | $sql = "SELECT `track`.`file_id` |
||
139 | FROM `*PREFIX*music_tracks` `track` |
||
140 | INNER JOIN `*PREFIX*filecache` `file` |
||
141 | ON `track`.`file_id` = `file`.`fileid` |
||
142 | WHERE `track`.`user_id` = ? |
||
143 | AND (`track`.`dirty` = '1' OR $updatedEpoch < `file`.`mtime`)"; |
||
144 | $result = $this->execute($sql, [$userId]); |
||
145 | return $result->fetchAll(\PDO::FETCH_COLUMN); |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Find a track of user matching a file ID |
||
150 | * @throws \OCP\AppFramework\Db\DoesNotExistException if not found |
||
151 | */ |
||
152 | public function findByFileId(int $fileId, string $userId) : Track { |
||
153 | $sql = $this->selectUserEntities('`file_id` = ?'); |
||
154 | $params = [$userId, $fileId]; |
||
155 | return $this->findEntity($sql, $params); |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Find tracks of user with multiple file IDs |
||
160 | * @param integer[] $fileIds |
||
161 | * @param string[] $userIds |
||
162 | * @return Track[] |
||
163 | */ |
||
164 | public function findByFileIds(array $fileIds, array $userIds) : array { |
||
165 | $sql = $this->selectEntities( |
||
166 | '`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) . |
||
167 | ' AND `file_id` IN '. $this->questionMarks(\count($fileIds))); |
||
168 | $params = \array_merge($userIds, $fileIds); |
||
169 | return $this->findEntities($sql, $params); |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Finds tracks of all users matching one or multiple file IDs |
||
174 | * @param integer[] $fileIds |
||
175 | * @return Track[] |
||
176 | */ |
||
177 | public function findAllByFileIds(array $fileIds) : array { |
||
178 | $sql = $this->selectEntities('`file_id` IN '. |
||
179 | $this->questionMarks(\count($fileIds))); |
||
180 | return $this->findEntities($sql, $fileIds); |
||
181 | } |
||
182 | |||
183 | public function countByArtist(int $artistId) : int { |
||
184 | $sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; |
||
185 | $result = $this->execute($sql, [$artistId]); |
||
186 | $row = $result->fetch(); |
||
187 | return (int)$row['count']; |
||
188 | } |
||
189 | |||
190 | public function countByAlbum(int $albumId) : int { |
||
191 | $sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; |
||
192 | $result = $this->execute($sql, [$albumId]); |
||
193 | $row = $result->fetch(); |
||
194 | return (int)$row['count']; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * @return integer Duration in seconds |
||
199 | */ |
||
200 | public function totalDurationOfAlbum(int $albumId) : int { |
||
201 | $sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; |
||
202 | $result = $this->execute($sql, [$albumId]); |
||
203 | $row = $result->fetch(); |
||
204 | return (int)$row['duration']; |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * @return integer Duration in seconds |
||
209 | */ |
||
210 | public function totalDurationByArtist(int $artistId) : int { |
||
211 | $sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; |
||
212 | $result = $this->execute($sql, [$artistId]); |
||
213 | $row = $result->fetch(); |
||
214 | return (int)$row['duration']; |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * Get durations of the given tracks. |
||
219 | * @param integer[] $trackIds |
||
220 | * @return array {int => int} where keys are track IDs and values are corresponding durations |
||
221 | */ |
||
222 | public function getDurations(array $trackIds) : array { |
||
223 | $result = []; |
||
224 | |||
225 | if (!empty($trackIds)) { |
||
226 | $sql = 'SELECT `id`, `length` FROM `*PREFIX*music_tracks` WHERE `id` IN ' . |
||
227 | $this->questionMarks(\count($trackIds)); |
||
228 | $rows = $this->execute($sql, $trackIds)->fetchAll(); |
||
229 | foreach ($rows as $row) { |
||
230 | $result[$row['id']] = (int)$row['length']; |
||
231 | } |
||
232 | } |
||
233 | return $result; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @return Track[] |
||
238 | */ |
||
239 | public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) { |
||
240 | $condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR |
||
241 | LOWER(`album`.`name`) LIKE LOWER(?) OR |
||
242 | LOWER(`title`) LIKE LOWER(?) )'; |
||
243 | $sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`title`)'); |
||
244 | $name = BaseMapper::prepareSubstringSearchPattern($name); |
||
245 | $params = [$userId, $name, $name, $name]; |
||
246 | return $this->findEntities($sql, $params, $limit, $offset); |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * Returns all tracks specified by name, artist name, and/or album name |
||
251 | * @param string|null $name the name of the track |
||
252 | * @param string|null $artistName the name of the artist |
||
253 | * @param string $userId the name of the user |
||
254 | * @return Track[] Tracks matching the criteria |
||
255 | */ |
||
256 | public function findAllByNameArtistOrAlbum(?string $name, ?string $artistName, ?string $albumName, string $userId) : array { |
||
257 | $sqlConditions = []; |
||
258 | $params = [$userId]; |
||
259 | |||
260 | if (!empty($name)) { |
||
261 | $sqlConditions[] = 'LOWER(`title`) = LOWER(?)'; |
||
262 | $params[] = $name; |
||
263 | } |
||
264 | |||
265 | if (!empty($artistName)) { |
||
266 | $sqlConditions[] = 'LOWER(`artist`.`name`) = LOWER(?)'; |
||
267 | $params[] = $artistName; |
||
268 | } |
||
269 | |||
270 | if (!empty($albumName)) { |
||
271 | $sqlConditions[] = 'LOWER(`album`.`name`) = LOWER(?)'; |
||
272 | $params[] = $albumName; |
||
273 | } |
||
274 | |||
275 | // at least one condition has to be given, otherwise return an empty set |
||
276 | if (\count($sqlConditions) > 0) { |
||
277 | $sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions)); |
||
278 | return $this->findEntities($sql, $params); |
||
279 | } else { |
||
280 | return []; |
||
281 | } |
||
282 | } |
||
283 | |||
284 | const FAVORITE_TRACK = 0x1; |
||
285 | const FAVORITE_ALBUM = 0x2; |
||
286 | const FAVORITE_ARTIST = 0x4; |
||
287 | |||
288 | /** |
||
289 | * Returns all tracks specified by various criteria, all of which are optional |
||
290 | * @param int[] $genres Array of genre IDs |
||
291 | * @param int[] $artists Array of artist IDs |
||
292 | * @param int|null $fromYear Earliest release year to include |
||
293 | * @param int|null $toYear Latest release year to include |
||
294 | * @param int|null $favorite Bit mask of FAVORITE_TRACK, FAVORITE_ALBUM, FAVORITE_ARTIST (given favorite types are ORed in the query) |
||
295 | * @param int $sortBy Sorting rule as defined in the class SortBy |
||
296 | * @param string $userId the name of the user |
||
297 | * @return Track[] Tracks matching the criteria |
||
298 | */ |
||
299 | public function findAllByCriteria( |
||
300 | array $genres, array $artists, ?int $fromYear, ?int $toYear, ?int $favorite, |
||
301 | int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
302 | |||
303 | $sqlConditions = []; |
||
304 | $params = [$userId]; |
||
305 | |||
306 | if (!empty($genres)) { |
||
307 | $sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres)); |
||
308 | $params = \array_merge($params, $genres); |
||
309 | } |
||
310 | |||
311 | if (!empty($artists)) { |
||
312 | $sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists)); |
||
313 | $params = \array_merge($params, $artists); |
||
314 | } |
||
315 | |||
316 | if (!empty($fromYear)) { |
||
317 | $sqlConditions[] = '`year` >= ?'; |
||
318 | $params[] = $fromYear; |
||
319 | } |
||
320 | |||
321 | if (!empty($toYear)) { |
||
322 | $sqlConditions[] = '`year` <= ?'; |
||
323 | $params[] = $toYear; |
||
324 | } |
||
325 | |||
326 | if (!empty($favorite)) { |
||
327 | $favConds = []; |
||
328 | if ($favorite & self::FAVORITE_TRACK) { |
||
329 | $favConds[] = '`*PREFIX*music_tracks`.`starred` IS NOT NULL'; |
||
330 | } |
||
331 | if ($favorite & self::FAVORITE_ALBUM) { |
||
332 | $favConds[] = '`album`.`starred` IS NOT NULL'; |
||
333 | } |
||
334 | if ($favorite & self::FAVORITE_ARTIST) { |
||
335 | $favConds[] = '`artist`.`starred` IS NOT NULL'; |
||
336 | } |
||
337 | $sqlConditions[] = '(' . \implode(' OR ', $favConds) . ')'; |
||
338 | } |
||
339 | |||
340 | $sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort)); |
||
341 | return $this->findEntities($sql, $params, $limit, $offset); |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * Find most frequently played tracks |
||
346 | * @return Track[] |
||
347 | */ |
||
348 | public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
349 | $sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)'); |
||
350 | return $this->findEntities($sql, [$userId], $limit, $offset); |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Find most recently played tracks |
||
355 | * @return Track[] |
||
356 | */ |
||
357 | public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
358 | $sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC'); |
||
359 | return $this->findEntities($sql, [$userId], $limit, $offset); |
||
360 | } |
||
361 | |||
362 | /** |
||
363 | * Find least recently played tracks |
||
364 | * @return Track[] |
||
365 | */ |
||
366 | public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
367 | $sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC'); |
||
368 | return $this->findEntities($sql, [$userId], $limit, $offset); |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Finds all track IDs of the user along with the parent folder ID of each track |
||
373 | * @return array where keys are folder IDs and values are arrays of track IDs |
||
374 | */ |
||
375 | public function findTrackAndFolderIds(string $userId) : array { |
||
376 | $sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS `parent` |
||
377 | FROM `*PREFIX*music_tracks` `track` |
||
378 | JOIN `*PREFIX*filecache` `file` |
||
379 | ON `track`.`file_id` = `file`.`fileid` |
||
380 | WHERE `track`.`user_id` = ?'; |
||
381 | |||
382 | $rows = $this->execute($sql, [$userId])->fetchAll(); |
||
383 | |||
384 | // Sort the results according the file names. This can't be made using ORDERBY in the |
||
385 | // SQL query because then we couldn't use the "natural order" comparison algorithm |
||
386 | \usort($rows, fn($a, $b) => \strnatcasecmp($a['filename'], $b['filename'])); |
||
387 | |||
388 | // group the files to parent folder "buckets" |
||
389 | $result = []; |
||
390 | foreach ($rows as $row) { |
||
391 | $result[(int)$row['parent']][] = (int)$row['id']; |
||
392 | } |
||
393 | |||
394 | return $result; |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * Find names and parents of the file system nodes with given IDs |
||
399 | * @param int[] $nodeIds |
||
400 | * @return array where keys are the node IDs and values are associative arrays like { 'name' => string, 'parent' => int }; |
||
401 | */ |
||
402 | public function findNodeNamesAndParents(array $nodeIds) : array { |
||
403 | $result = []; |
||
404 | |||
405 | if (!empty($nodeIds)) { |
||
406 | $sql = 'SELECT `fileid`, `name`, `parent` |
||
407 | FROM `*PREFIX*filecache` `filecache` |
||
408 | WHERE `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds)); |
||
409 | |||
410 | $rows = $this->execute($sql, $nodeIds)->fetchAll(); |
||
411 | |||
412 | foreach ($rows as $row) { |
||
413 | $result[$row['fileid']] = [ |
||
414 | 'name' => $row['name'], |
||
415 | 'parent' => (int)$row['parent'] |
||
416 | ]; |
||
417 | } |
||
418 | } |
||
419 | |||
420 | return $result; |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * Returns all genre IDs associated with the given artist |
||
425 | * @return int[] |
||
426 | */ |
||
427 | public function getGenresByArtistId(int $artistId, string $userId) : array { |
||
428 | $sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE |
||
429 | `genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?'; |
||
430 | $rows = $this->execute($sql, [$userId, $artistId]); |
||
431 | return $rows->fetchAll(\PDO::FETCH_COLUMN); |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * Returns all tracks IDs of the user, organized by the genre_id. |
||
436 | * @return array where keys are genre IDs and values are arrays of track IDs |
||
437 | */ |
||
438 | public function mapGenreIdsToTrackIds(string $userId) : array { |
||
439 | $sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks` |
||
440 | WHERE `genre_id` IS NOT NULL and `user_id` = ?'; |
||
441 | $rows = $this->execute($sql, [$userId])->fetchAll(); |
||
442 | |||
443 | $result = []; |
||
444 | foreach ($rows as $row) { |
||
445 | $result[(int)$row['genre_id']][] = (int)$row['id']; |
||
446 | } |
||
447 | |||
448 | return $result; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * Returns file IDs of the tracks which do not have genre scanned. This is not the same |
||
453 | * thing as unknown genre, which means that the genre has been scanned but was not found |
||
454 | * from the track metadata. |
||
455 | * @return int[] |
||
456 | */ |
||
457 | public function findFilesWithoutScannedGenre(string $userId) : array { |
||
458 | $sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track` |
||
459 | INNER JOIN `*PREFIX*filecache` `file` |
||
460 | ON `track`.`file_id` = `file`.`fileid` |
||
461 | WHERE `genre_id` IS NULL and `user_id` = ?'; |
||
462 | $rows = $this->execute($sql, [$userId]); |
||
463 | return $rows->fetchAll(\PDO::FETCH_COLUMN); |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * Update "last played" timestamp and increment the total play count of the track. |
||
468 | * The DB row is updated *without* updating the `updated` column. |
||
469 | * @return bool true if the track was found and updated, false otherwise |
||
470 | */ |
||
471 | public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool { |
||
472 | $sql = 'UPDATE `*PREFIX*music_tracks` |
||
473 | SET `last_played` = ?, `play_count` = `play_count` + 1 |
||
474 | WHERE `user_id` = ? AND `id` = ?'; |
||
475 | $params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId]; |
||
476 | $result = $this->execute($sql, $params); |
||
477 | return ($result->rowCount() > 0); |
||
478 | } |
||
479 | |||
480 | /** |
||
481 | * Marks tracks as dirty, ultimately requesting the user to rescan them |
||
482 | * @param int[] $fileIds file IDs of the tracks to mark as dirty |
||
483 | * @param string[]|null $userIds the target users; if omitted, the tracks matching the |
||
484 | * $fileIds are marked for all users |
||
485 | * @return int number of rows affected |
||
486 | */ |
||
487 | public function markTracksDirty(array $fileIds, ?array $userIds=null) : int { |
||
488 | $sql = 'UPDATE `*PREFIX*music_tracks` |
||
489 | SET `dirty` = 1 |
||
490 | WHERE `file_id` IN ' . $this->questionMarks(\count($fileIds)); |
||
491 | $params = $fileIds; |
||
492 | |||
493 | if (!empty($userIds)) { |
||
494 | $sql .= ' AND `user_id` IN ' . $this->questionMarks(\count($userIds)); |
||
495 | $params = \array_merge($params, $userIds); |
||
496 | } |
||
497 | |||
498 | $result = $this->execute($sql, $params); |
||
499 | return $result->rowCount(); |
||
500 | } |
||
501 | |||
502 | /** |
||
503 | * Overridden from the base implementation to provide support for table-specific rules |
||
504 | * |
||
505 | * {@inheritdoc} |
||
506 | * @see BaseMapper::advFormatSqlCondition() |
||
507 | */ |
||
508 | protected function advFormatSqlCondition(string $rule, string $sqlOp, string $conv) : string { |
||
509 | // The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL. |
||
510 | $condForRule = [ |
||
511 | 'anywhere' => self::formatAdvSearchAnywhereCond($sqlOp, $conv), |
||
512 | 'album' => "$conv(`album`.`name`) $sqlOp $conv(?)", |
||
513 | 'artist' => "$conv(`artist`.`name`) $sqlOp $conv(?)", |
||
514 | 'album_artist' => "`album_id` IN (SELECT `al`.`id` from `*PREFIX*music_albums` `al` JOIN `*PREFIX*music_artists` `ar` ON `al`.`album_artist_id` = `ar`.`id` WHERE $conv(`ar`.`name`) $sqlOp $conv(?))", |
||
515 | 'album_artist_id' => "$sqlOp `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` = ?)", // our own API extension |
||
516 | 'track' => "`number` $sqlOp ?", |
||
517 | 'year' => "`year` $sqlOp ?", |
||
518 | 'albumrating' => "`album`.`rating` $sqlOp ?", |
||
519 | 'artistrating' => "`artist`.`rating` $sqlOp ?", |
||
520 | 'favorite_album' => "$conv(`album`.`name`) $sqlOp $conv(?) AND `album`.`starred` IS NOT NULL", |
||
521 | 'favorite_artist' => "$conv(`artist`.`name`) $sqlOp $conv(?) AND `artist`.`starred` IS NOT NULL", |
||
522 | 'played_times' => "`play_count` $sqlOp ?", |
||
523 | 'last_play' => "`last_played` $sqlOp ?", |
||
524 | 'myplayed' => "`last_played` $sqlOp", // operator "IS NULL" or "IS NOT NULL" |
||
525 | 'myplayedalbum' => "`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" |
||
526 | 'myplayedartist' => "`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" |
||
527 | 'time' => "`length` $sqlOp ?", |
||
528 | 'bitrate' => "`bitrate` $sqlOp ?", |
||
529 | 'song_genre' => "$conv(`genre`.`name`) $sqlOp $conv(?)", |
||
530 | 'album_genre' => "`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 $conv(" . $this->sqlGroupConcat('`g`.`name`') . ") $sqlOp $conv(?)) mysqlhack)", |
||
531 | 'artist_genre' => "`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 $conv(" . $this->sqlGroupConcat('`g`.`name`') . ") $sqlOp $conv(?)) mysqlhack)", |
||
532 | 'no_genre' => ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""', |
||
533 | 'playlist' => "$sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE " . $this->sqlConcat("'%|'", "`*PREFIX*music_tracks`.`id`", "'|%'") . ')', |
||
534 | 'playlist_name' => "EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE $conv(`p`.`name`) $sqlOp $conv(?) AND `p`.`track_ids` LIKE " . $this->sqlConcat("'%|'", "`*PREFIX*music_tracks`.`id`", "'|%'") . ')', |
||
535 | 'recent_played' => "`*PREFIX*music_tracks`.`id` IN (SELECT * FROM (SELECT `id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ? ORDER BY `last_played` DESC LIMIT $sqlOp) mysqlhack)", |
||
536 | 'file' => "$conv(`file`.`name`) $sqlOp $conv(?)", |
||
537 | 'mbid_album' => "`album`.`mbid` $sqlOp ?", |
||
538 | 'mbid_artist' => "`artist`.`mbid` $sqlOp ?" |
||
539 | ]; |
||
540 | |||
541 | // Add alias rules |
||
542 | $condForRule['played'] = $condForRule['myplayed']; // we give no access to other people's data; not part of the API spec but Ample uses this |
||
543 | $condForRule['genre'] = $condForRule['song_genre']; |
||
544 | $condForRule['song'] = parent::advFormatSqlCondition('title', $sqlOp, $conv); |
||
545 | $condForRule['mbid_song'] = parent::advFormatSqlCondition('mbid', $sqlOp, $conv); |
||
546 | |||
547 | return $condForRule[$rule] ?? parent::advFormatSqlCondition($rule, $sqlOp, $conv); |
||
548 | } |
||
549 | |||
550 | private static function formatAdvSearchAnywhereCond(string $sqlOp, string $conv) : string { |
||
564 | } |
||
565 | } |
||
566 |