| Total Complexity | 59 |
| Total Lines | 524 |
| Duplicated Lines | 0 % |
| Changes | 21 | ||
| 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 { |
||
| 37 | return "SELECT `*PREFIX*music_tracks`.*, `file`.`name` AS `filename`, `file`.`size`, `file`.`mtime` AS `file_mod_time`, `file`.`parent` AS `folder_id`, |
||
| 38 | `album`.`name` AS `album_name`, `artist`.`name` AS `artist_name`, `genre`.`name` AS `genre_name` |
||
| 39 | FROM `*PREFIX*music_tracks` |
||
| 40 | INNER JOIN `*PREFIX*filecache` `file` |
||
| 41 | ON `*PREFIX*music_tracks`.`file_id` = `file`.`fileid` |
||
| 42 | INNER JOIN `*PREFIX*music_albums` `album` |
||
| 43 | ON `*PREFIX*music_tracks`.`album_id` = `album`.`id` |
||
| 44 | INNER JOIN `*PREFIX*music_artists` `artist` |
||
| 45 | ON `*PREFIX*music_tracks`.`artist_id` = `artist`.`id` |
||
| 46 | LEFT JOIN `*PREFIX*music_genres` `genre` |
||
| 47 | ON `*PREFIX*music_tracks`.`genre_id` = `genre`.`id` |
||
| 48 | WHERE $condition $extension"; |
||
| 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 { |
||
| 119 | $sql = $this->selectUserEntities('`genre_id` = ?', 'ORDER BY LOWER(`title`)'); |
||
| 120 | $params = [$userId, $genreId]; |
||
| 121 | return $this->findEntities($sql, $params, $limit, $offset); |
||
| 122 | } |
||
| 123 | |||
| 124 | /** |
||
| 125 | * @return int[] |
||
| 126 | */ |
||
| 127 | public function findAllFileIds(string $userId) : array { |
||
| 128 | $sql = 'SELECT `file_id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ?'; |
||
| 129 | $result = $this->execute($sql, [$userId]); |
||
| 130 | return $result->fetchAll(\PDO::FETCH_COLUMN); |
||
| 131 | } |
||
| 132 | |||
| 133 | /** |
||
| 134 | * Find a track of user matching a file ID |
||
| 135 | * @throws \OCP\AppFramework\Db\DoesNotExistException if not found |
||
| 136 | */ |
||
| 137 | public function findByFileId(int $fileId, string $userId) : Track { |
||
| 138 | $sql = $this->selectUserEntities('`file_id` = ?'); |
||
| 139 | $params = [$userId, $fileId]; |
||
| 140 | return $this->findEntity($sql, $params); |
||
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Find tracks of user with multiple file IDs |
||
| 145 | * @param integer[] $fileIds |
||
| 146 | * @param string[] $userIds |
||
| 147 | * @return Track[] |
||
| 148 | */ |
||
| 149 | public function findByFileIds(array $fileIds, array $userIds) : array { |
||
| 150 | $sql = $this->selectEntities( |
||
| 151 | '`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) . |
||
| 152 | ' AND `file_id` IN '. $this->questionMarks(\count($fileIds))); |
||
| 153 | $params = \array_merge($userIds, $fileIds); |
||
| 154 | return $this->findEntities($sql, $params); |
||
| 155 | } |
||
| 156 | |||
| 157 | /** |
||
| 158 | * Finds tracks of all users matching one or multiple file IDs |
||
| 159 | * @param integer[] $fileIds |
||
| 160 | * @return Track[] |
||
| 161 | */ |
||
| 162 | public function findAllByFileIds(array $fileIds) : array { |
||
| 163 | $sql = $this->selectEntities('`file_id` IN '. |
||
| 164 | $this->questionMarks(\count($fileIds))); |
||
| 165 | return $this->findEntities($sql, $fileIds); |
||
| 166 | } |
||
| 167 | |||
| 168 | public function countByArtist(int $artistId) : int { |
||
| 169 | $sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; |
||
| 170 | $result = $this->execute($sql, [$artistId]); |
||
| 171 | $row = $result->fetch(); |
||
| 172 | return (int)$row['count']; |
||
| 173 | } |
||
| 174 | |||
| 175 | public function countByAlbum(int $albumId) : int { |
||
| 176 | $sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; |
||
| 177 | $result = $this->execute($sql, [$albumId]); |
||
| 178 | $row = $result->fetch(); |
||
| 179 | return (int)$row['count']; |
||
| 180 | } |
||
| 181 | |||
| 182 | /** |
||
| 183 | * @return integer Duration in seconds |
||
| 184 | */ |
||
| 185 | public function totalDurationOfAlbum(int $albumId) : int { |
||
| 186 | $sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; |
||
| 187 | $result = $this->execute($sql, [$albumId]); |
||
| 188 | $row = $result->fetch(); |
||
| 189 | return (int)$row['duration']; |
||
| 190 | } |
||
| 191 | |||
| 192 | /** |
||
| 193 | * @return integer Duration in seconds |
||
| 194 | */ |
||
| 195 | public function totalDurationByArtist(int $artistId) : int { |
||
| 196 | $sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; |
||
| 197 | $result = $this->execute($sql, [$artistId]); |
||
| 198 | $row = $result->fetch(); |
||
| 199 | return (int)$row['duration']; |
||
| 200 | } |
||
| 201 | |||
| 202 | /** |
||
| 203 | * Get durations of the given tracks. |
||
| 204 | * @param integer[] $trackIds |
||
| 205 | * @return array {int => int} where keys are track IDs and values are corresponding durations |
||
| 206 | */ |
||
| 207 | public function getDurations(array $trackIds) : array { |
||
| 219 | } |
||
| 220 | |||
| 221 | /** |
||
| 222 | * @return Track[] |
||
| 223 | */ |
||
| 224 | public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) { |
||
| 232 | } |
||
| 233 | |||
| 234 | /** |
||
| 235 | * Returns all tracks specified by name, artist name, and/or album name |
||
| 236 | * @param string|null $name the name of the track |
||
| 237 | * @param string|null $artistName the name of the artist |
||
| 238 | * @param string $userId the name of the user |
||
| 239 | * @return Track[] Tracks matching the criteria |
||
| 240 | */ |
||
| 241 | public function findAllByNameArtistOrAlbum(?string $name, ?string $artistName, ?string $albumName, string $userId) : array { |
||
| 242 | $sqlConditions = []; |
||
| 243 | $params = [$userId]; |
||
| 244 | |||
| 245 | if (!empty($name)) { |
||
| 246 | $sqlConditions[] = 'LOWER(`title`) = LOWER(?)'; |
||
| 247 | $params[] = $name; |
||
| 248 | } |
||
| 249 | |||
| 250 | if (!empty($artistName)) { |
||
| 251 | $sqlConditions[] = 'LOWER(`artist`.`name`) = LOWER(?)'; |
||
| 252 | $params[] = $artistName; |
||
| 253 | } |
||
| 254 | |||
| 255 | if (!empty($albumName)) { |
||
| 256 | $sqlConditions[] = 'LOWER(`album`.`name`) = LOWER(?)'; |
||
| 257 | $params[] = $albumName; |
||
| 258 | } |
||
| 259 | |||
| 260 | // at least one condition has to be given, otherwise return an empty set |
||
| 261 | if (\count($sqlConditions) > 0) { |
||
| 262 | $sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions)); |
||
| 263 | return $this->findEntities($sql, $params); |
||
| 264 | } else { |
||
| 265 | return []; |
||
| 266 | } |
||
| 267 | } |
||
| 268 | |||
| 269 | const FAVORITE_TRACK = 0x1; |
||
| 270 | const FAVORITE_ALBUM = 0x2; |
||
| 271 | const FAVORITE_ARTIST = 0x4; |
||
| 272 | |||
| 273 | /** |
||
| 274 | * Returns all tracks specified by various criteria, all of which are optional |
||
| 275 | * @param int[] $genres Array of genre IDs |
||
| 276 | * @param int[] $artists Array of artist IDs |
||
| 277 | * @param int|null $fromYear Earliest release year to include |
||
| 278 | * @param int|null $toYear Latest release year to include |
||
| 279 | * @param int|null $favorite Bit mask of FAVORITE_TRACK, FAVORITE_ALBUM, FAVORITE_ARTIST (given favorite types are ORed in the query) |
||
| 280 | * @param int $sortBy Sorting rule as defined in the class SortBy |
||
| 281 | * @param string $userId the name of the user |
||
| 282 | * @return Track[] Tracks matching the criteria |
||
| 283 | */ |
||
| 284 | public function findAllByCriteria( |
||
| 285 | array $genres, array $artists, ?int $fromYear, ?int $toYear, ?int $favorite, |
||
| 286 | int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
| 287 | |||
| 288 | $sqlConditions = []; |
||
| 289 | $params = [$userId]; |
||
| 290 | |||
| 291 | if (!empty($genres)) { |
||
| 292 | $sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres)); |
||
| 293 | $params = \array_merge($params, $genres); |
||
| 294 | } |
||
| 295 | |||
| 296 | if (!empty($artists)) { |
||
| 297 | $sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists)); |
||
| 298 | $params = \array_merge($params, $artists); |
||
| 299 | } |
||
| 300 | |||
| 301 | if (!empty($fromYear)) { |
||
| 302 | $sqlConditions[] = '`year` >= ?'; |
||
| 303 | $params[] = $fromYear; |
||
| 304 | } |
||
| 305 | |||
| 306 | if (!empty($toYear)) { |
||
| 307 | $sqlConditions[] = '`year` <= ?'; |
||
| 308 | $params[] = $toYear; |
||
| 309 | } |
||
| 310 | |||
| 311 | if (!empty($favorite)) { |
||
| 312 | $favConds = []; |
||
| 313 | if ($favorite & self::FAVORITE_TRACK) { |
||
| 314 | $favConds[] = '`*PREFIX*music_tracks`.`starred` IS NOT NULL'; |
||
| 315 | } |
||
| 316 | if ($favorite & self::FAVORITE_ALBUM) { |
||
| 317 | $favConds[] = '`album`.`starred` IS NOT NULL'; |
||
| 318 | } |
||
| 319 | if ($favorite & self::FAVORITE_ARTIST) { |
||
| 320 | $favConds[] = '`artist`.`starred` IS NOT NULL'; |
||
| 321 | } |
||
| 322 | $sqlConditions[] = '(' . \implode(' OR ', $favConds) . ')'; |
||
| 323 | } |
||
| 324 | |||
| 325 | $sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort)); |
||
| 326 | return $this->findEntities($sql, $params, $limit, $offset); |
||
| 327 | } |
||
| 328 | |||
| 329 | /** |
||
| 330 | * Find most frequently played tracks |
||
| 331 | * @return Track[] |
||
| 332 | */ |
||
| 333 | public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
| 336 | } |
||
| 337 | |||
| 338 | /** |
||
| 339 | * Find most recently played tracks |
||
| 340 | * @return Track[] |
||
| 341 | */ |
||
| 342 | public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
| 343 | $sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC'); |
||
| 344 | return $this->findEntities($sql, [$userId], $limit, $offset); |
||
| 345 | } |
||
| 346 | |||
| 347 | /** |
||
| 348 | * Find least recently played tracks |
||
| 349 | * @return Track[] |
||
| 350 | */ |
||
| 351 | public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
||
| 352 | $sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC'); |
||
| 353 | return $this->findEntities($sql, [$userId], $limit, $offset); |
||
| 354 | } |
||
| 355 | |||
| 356 | /** |
||
| 357 | * Finds all track IDs of the user along with the parent folder ID of each track |
||
| 358 | * @return array where keys are folder IDs and values are arrays of track IDs |
||
| 359 | */ |
||
| 360 | public function findTrackAndFolderIds(string $userId) : array { |
||
| 380 | } |
||
| 381 | |||
| 382 | /** |
||
| 383 | * Find names and parents of the file system nodes with given IDs |
||
| 384 | * @param int[] $nodeIds |
||
| 385 | * @return array where keys are the node IDs and values are associative arrays like { 'name' => string, 'parent' => int }; |
||
| 386 | */ |
||
| 387 | public function findNodeNamesAndParents(array $nodeIds) : array { |
||
| 388 | $result = []; |
||
| 389 | |||
| 390 | if (!empty($nodeIds)) { |
||
| 391 | $sql = 'SELECT `fileid`, `name`, `parent` |
||
| 392 | FROM `*PREFIX*filecache` `filecache` |
||
| 393 | WHERE `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds)); |
||
| 394 | |||
| 395 | $rows = $this->execute($sql, $nodeIds)->fetchAll(); |
||
| 396 | |||
| 397 | foreach ($rows as $row) { |
||
| 398 | $result[$row['fileid']] = [ |
||
| 399 | 'name' => $row['name'], |
||
| 400 | 'parent' => (int)$row['parent'] |
||
| 401 | ]; |
||
| 402 | } |
||
| 403 | } |
||
| 404 | |||
| 405 | return $result; |
||
| 406 | } |
||
| 407 | |||
| 408 | /** |
||
| 409 | * Returns all genre IDs associated with the given artist |
||
| 410 | * @return int[] |
||
| 411 | */ |
||
| 412 | public function getGenresByArtistId(int $artistId, string $userId) : array { |
||
| 413 | $sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE |
||
| 414 | `genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?'; |
||
| 415 | $rows = $this->execute($sql, [$userId, $artistId]); |
||
| 416 | return $rows->fetchAll(\PDO::FETCH_COLUMN); |
||
| 417 | } |
||
| 418 | |||
| 419 | /** |
||
| 420 | * Returns all tracks IDs of the user, organized by the genre_id. |
||
| 421 | * @return array where keys are genre IDs and values are arrays of track IDs |
||
| 422 | */ |
||
| 423 | public function mapGenreIdsToTrackIds(string $userId) : array { |
||
| 434 | } |
||
| 435 | |||
| 436 | /** |
||
| 437 | * Returns file IDs of the tracks which do not have genre scanned. This is not the same |
||
| 438 | * thing as unknown genre, which means that the genre has been scanned but was not found |
||
| 439 | * from the track metadata. |
||
| 440 | * @return int[] |
||
| 441 | */ |
||
| 442 | public function findFilesWithoutScannedGenre(string $userId) : array { |
||
| 443 | $sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track` |
||
| 444 | INNER JOIN `*PREFIX*filecache` `file` |
||
| 445 | ON `track`.`file_id` = `file`.`fileid` |
||
| 446 | WHERE `genre_id` IS NULL and `user_id` = ?'; |
||
| 447 | $rows = $this->execute($sql, [$userId]); |
||
| 448 | return $rows->fetchAll(\PDO::FETCH_COLUMN); |
||
| 449 | } |
||
| 450 | |||
| 451 | /** |
||
| 452 | * Update "last played" timestamp and increment the total play count of the track. |
||
| 453 | * The DB row is updated *without* updating the `updated` column. |
||
| 454 | * @return bool true if the track was found and updated, false otherwise |
||
| 455 | */ |
||
| 456 | public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool { |
||
| 457 | $sql = 'UPDATE `*PREFIX*music_tracks` |
||
| 458 | SET `last_played` = ?, `play_count` = `play_count` + 1 |
||
| 459 | WHERE `user_id` = ? AND `id` = ?'; |
||
| 460 | $params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId]; |
||
| 461 | $result = $this->execute($sql, $params); |
||
| 462 | return ($result->rowCount() > 0); |
||
| 463 | } |
||
| 464 | |||
| 465 | /** |
||
| 466 | * Marks tracks as dirty, ultimately requesting the user to rescan them |
||
| 467 | * @param int[] $fileIds file IDs of the tracks to mark as dirty |
||
| 468 | * @param string[]|null $userIds the target users; if omitted, the tracks matching the |
||
| 469 | * $fileIds are marked for all users |
||
| 470 | * @return int number of rows affected |
||
| 471 | */ |
||
| 472 | public function markTracksDirty(array $fileIds, ?array $userIds=null) : int { |
||
| 485 | } |
||
| 486 | |||
| 487 | /** |
||
| 488 | * Overridden from the base implementation to provide support for table-specific rules |
||
| 489 | * |
||
| 490 | * {@inheritdoc} |
||
| 491 | * @see BaseMapper::advFormatSqlCondition() |
||
| 492 | */ |
||
| 493 | protected function advFormatSqlCondition(string $rule, string $sqlOp, string $conv) : string { |
||
| 494 | // The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL. |
||
| 495 | $condForRule = [ |
||
| 496 | 'anywhere' => self::formatAdvSearchAnywhereCond($sqlOp, $conv), |
||
| 497 | 'album' => "$conv(`album`.`name`) $sqlOp $conv(?)", |
||
| 498 | 'artist' => "$conv(`artist`.`name`) $sqlOp $conv(?)", |
||
| 499 | '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(?))", |
||
| 500 | 'album_artist_id' => "$sqlOp `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` = ?)", // our own API extension |
||
| 501 | 'track' => "`number` $sqlOp ?", |
||
| 502 | 'year' => "`year` $sqlOp ?", |
||
| 503 | 'albumrating' => "`album`.`rating` $sqlOp ?", |
||
| 504 | 'artistrating' => "`artist`.`rating` $sqlOp ?", |
||
| 505 | 'favorite_album' => "$conv(`album`.`name`) $sqlOp $conv(?) AND `album`.`starred` IS NOT NULL", |
||
| 506 | 'favorite_artist' => "$conv(`artist`.`name`) $sqlOp $conv(?) AND `artist`.`starred` IS NOT NULL", |
||
| 507 | 'played_times' => "`play_count` $sqlOp ?", |
||
| 508 | 'last_play' => "`last_played` $sqlOp ?", |
||
| 509 | 'myplayed' => "`last_played` $sqlOp", // operator "IS NULL" or "IS NOT NULL" |
||
| 510 | '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" |
||
| 511 | '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" |
||
| 512 | 'time' => "`length` $sqlOp ?", |
||
| 513 | 'bitrate' => "`bitrate` $sqlOp ?", |
||
| 514 | 'song_genre' => "$conv(`genre`.`name`) $sqlOp $conv(?)", |
||
| 515 | '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)", |
||
| 516 | '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)", |
||
| 517 | 'no_genre' => ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""', |
||
| 518 | 'playlist' => "$sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE " . $this->sqlConcat("'%|'", "`*PREFIX*music_tracks`.`id`", "'|%'") . ')', |
||
| 519 | '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`", "'|%'") . ')', |
||
| 520 | '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)", |
||
| 521 | 'file' => "$conv(`file`.`name`) $sqlOp $conv(?)", |
||
| 522 | 'mbid_album' => "`album`.`mbid` $sqlOp ?", |
||
| 523 | 'mbid_artist' => "`artist`.`mbid` $sqlOp ?" |
||
| 524 | ]; |
||
| 525 | |||
| 526 | // Add alias rules |
||
| 527 | $condForRule['played'] = $condForRule['myplayed']; // we give no access to other people's data; not part of the API spec but Ample uses this |
||
| 528 | $condForRule['genre'] = $condForRule['song_genre']; |
||
| 529 | $condForRule['song'] = parent::advFormatSqlCondition('title', $sqlOp, $conv); |
||
| 530 | $condForRule['mbid_song'] = parent::advFormatSqlCondition('mbid', $sqlOp, $conv); |
||
| 531 | |||
| 532 | return $condForRule[$rule] ?? parent::advFormatSqlCondition($rule, $sqlOp, $conv); |
||
| 533 | } |
||
| 534 | |||
| 535 | private static function formatAdvSearchAnywhereCond(string $sqlOp, string $conv) : string { |
||
| 549 | } |
||
| 550 | } |
||
| 551 |