1<?php 2/* 3 * vim:set softtabstop=4 shiftwidth=4 expandtab: 4 * 5 * LICENSE: GNU Affero General Public License, version 3 (AGPL-3.0-or-later) 6 * Copyright 2001 - 2020 Ampache.org 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22declare(strict_types=0); 23 24namespace Ampache\Repository\Model; 25 26use Ampache\Module\Authorization\Access; 27use Ampache\Repository\Model\Metadata\Repository\MetadataField; 28use Ampache\Module\System\Dba; 29use Ampache\Config\AmpConfig; 30use Ampache\Module\System\Core; 31use Ampache\Repository\LicenseRepositoryInterface; 32use Ampache\Repository\UserRepositoryInterface; 33 34/** 35 * Search-related voodoo. Beware tentacles. 36 */ 37class Search extends playlist_object 38{ 39 protected const DB_TABLENAME = 'search'; 40 41 public $searchtype; 42 public $rules = array(); 43 public $logic_operator = 'AND'; 44 public $type = 'public'; 45 public $random = 0; 46 public $limit = 0; 47 public $last_count = 0; 48 public $last_duration = 0; 49 public $date = 0; 50 51 public $basetypes; 52 public $types; 53 54 public $link; 55 public $f_link; 56 57 public $search_user; 58 59 private $stars; 60 private $order_by; 61 62 /** 63 * constructor 64 * @param integer $search_id 65 * @param string $searchtype 66 * @param User|null $user 67 */ 68 public function __construct($search_id = 0, $searchtype = 'song', ?User $user = null) 69 { 70 if ($user->id) { 71 $this->search_user = $user; 72 } else { 73 $this->search_user = Core::get_global('user'); 74 } 75 $this->searchtype = $searchtype; 76 if ($search_id > 0) { 77 $info = $this->get_info($search_id); 78 foreach ($info as $key => $value) { 79 $this->$key = $value; 80 } 81 $this->rules = json_decode((string)$this->rules, true); 82 } 83 $this->date = time(); 84 85 $this->stars = array( 86 T_('0 Stars'), 87 T_('1 Star'), 88 T_('2 Stars'), 89 T_('3 Stars'), 90 T_('4 Stars'), 91 T_('5 Stars') 92 ); 93 94 // Define our basetypes 95 $this->set_basetypes(); 96 97 $this->types = array(); 98 switch ($searchtype) { 99 case 'song': 100 $this->song_types(); 101 $this->order_by = '`song`.`file`'; 102 break; 103 case 'album': 104 $this->album_types(); 105 $this->order_by = (AmpConfig::get('album_group')) ? '`album`.`name`' : '`album`.`name`, `album`.`disk`'; 106 break; 107 case 'video': 108 $this->video_types(); 109 $this->order_by = '`video`.`file`'; 110 break; 111 case 'artist': 112 $this->artist_types(); 113 $this->order_by = '`artist`.`name`'; 114 break; 115 case 'playlist': 116 $this->playlist_types(); 117 $this->order_by = '`playlist`.`name`'; 118 break; 119 case 'label': 120 $this->label_types(); 121 $this->order_by = '`label`.`name`'; 122 break; 123 case 'user': 124 $this->user_types(); 125 $this->order_by = '`user`.`username`'; 126 break; 127 case 'tag': 128 case 'genre': 129 $this->tag_types(); 130 $this->order_by = '`tag`.`name`'; 131 break; 132 } // end switch on searchtype 133 } // end constructor 134 135 /** 136 * set_basetypes 137 * 138 * Function called during construction to set the different types and rules for search 139 */ 140 private function set_basetypes() 141 { 142 $this->basetypes['numeric'][] = array( 143 'name' => 'gte', 144 'description' => T_('is greater than or equal to'), 145 'sql' => '>=' 146 ); 147 148 $this->basetypes['numeric'][] = array( 149 'name' => 'lte', 150 'description' => T_('is less than or equal to'), 151 'sql' => '<=' 152 ); 153 154 $this->basetypes['numeric'][] = array( 155 'name' => 'equal', 156 'description' => T_('equals'), 157 'sql' => '<=>' 158 ); 159 160 $this->basetypes['numeric'][] = array( 161 'name' => 'ne', 162 'description' => T_('does not equal'), 163 'sql' => '<>' 164 ); 165 166 $this->basetypes['numeric'][] = array( 167 'name' => 'gt', 168 'description' => T_('is greater than'), 169 'sql' => '>' 170 ); 171 172 $this->basetypes['numeric'][] = array( 173 'name' => 'lt', 174 'description' => T_('is less than'), 175 'sql' => '<' 176 ); 177 178 $this->basetypes['is_true'][] = array( 179 'name' => 'true', 180 'description' => T_('is true'), 181 'sql' => '1' 182 ); 183 184 $this->basetypes['boolean'][] = array( 185 'name' => 'true', 186 'description' => T_('is true'), 187 'sql' => '1' 188 ); 189 190 $this->basetypes['boolean'][] = array( 191 'name' => 'false', 192 'description' => T_('is false'), 193 'sql' => '0' 194 ); 195 196 $this->basetypes['text'][] = array( 197 'name' => 'contain', 198 'description' => T_('contains'), 199 'sql' => 'LIKE', 200 'preg_match' => array('/^/', '/$/'), 201 'preg_replace' => array('%', '%') 202 ); 203 204 $this->basetypes['text'][] = array( 205 'name' => 'notcontain', 206 'description' => T_('does not contain'), 207 'sql' => 'NOT LIKE', 208 'preg_match' => array('/^/', '/$/'), 209 'preg_replace' => array('%', '%') 210 ); 211 212 $this->basetypes['text'][] = array( 213 'name' => 'start', 214 'description' => T_('starts with'), 215 'sql' => 'LIKE', 216 'preg_match' => '/$/', 217 'preg_replace' => '%' 218 ); 219 220 $this->basetypes['text'][] = array( 221 'name' => 'end', 222 'description' => T_('ends with'), 223 'sql' => 'LIKE', 224 'preg_match' => '/^/', 225 'preg_replace' => '%' 226 ); 227 228 $this->basetypes['text'][] = array( 229 'name' => 'equal', 230 'description' => T_('is'), 231 'sql' => '=' 232 ); 233 234 $this->basetypes['text'][] = array( 235 'name' => 'not equal', 236 'description' => T_('is not'), 237 'sql' => '!=' 238 ); 239 240 $this->basetypes['text'][] = array( 241 'name' => 'sounds', 242 'description' => T_('sounds like'), 243 'sql' => 'SOUNDS LIKE' 244 ); 245 246 $this->basetypes['text'][] = array( 247 'name' => 'notsounds', 248 'description' => T_('does not sound like'), 249 'sql' => 'NOT SOUNDS LIKE' 250 ); 251 252 $this->basetypes['text'][] = array( 253 'name' => 'regexp', 254 'description' => T_('matches regular expression'), 255 'sql' => 'REGEXP' 256 ); 257 258 $this->basetypes['text'][] = array( 259 'name' => 'notregexp', 260 'description' => T_('does not match regular expression'), 261 'sql' => 'NOT REGEXP' 262 ); 263 264 $this->basetypes['tags'][] = array( 265 'name' => 'contain', 266 'description' => T_('contains'), 267 'sql' => 'LIKE', 268 'preg_match' => array('/^/', '/$/'), 269 'preg_replace' => array('%', '%') 270 ); 271 272 $this->basetypes['tags'][] = array( 273 'name' => 'notcontain', 274 'description' => T_('does not contain'), 275 'sql' => 'NOT LIKE', 276 'preg_match' => array('/^/', '/$/'), 277 'preg_replace' => array('%', '%') 278 ); 279 280 $this->basetypes['tags'][] = array( 281 'name' => 'start', 282 'description' => T_('starts with'), 283 'sql' => 'LIKE', 284 'preg_match' => '/$/', 285 'preg_replace' => '%' 286 ); 287 288 $this->basetypes['tags'][] = array( 289 'name' => 'end', 290 'description' => T_('ends with'), 291 'sql' => 'LIKE', 292 'preg_match' => '/^/', 293 'preg_replace' => '%' 294 ); 295 296 $this->basetypes['tags'][] = array( 297 'name' => 'equal', 298 'description' => T_('is'), 299 'sql' => '>' 300 ); 301 302 $this->basetypes['tags'][] = array( 303 'name' => 'not equal', 304 'description' => T_('is not'), 305 'sql' => '=' 306 ); 307 308 $this->basetypes['boolean_numeric'][] = array( 309 'name' => 'equal', 310 'description' => T_('is'), 311 'sql' => '<=>' 312 ); 313 314 $this->basetypes['boolean_numeric'][] = array( 315 'name' => 'ne', 316 'description' => T_('is not'), 317 'sql' => '<>' 318 ); 319 320 $this->basetypes['boolean_subsearch'][] = array( 321 'name' => 'equal', 322 'description' => T_('is'), 323 'sql' => '' 324 ); 325 326 $this->basetypes['boolean_subsearch'][] = array( 327 'name' => 'ne', 328 'description' => T_('is not'), 329 'sql' => 'NOT' 330 ); 331 332 $this->basetypes['date'][] = array( 333 'name' => 'lt', 334 'description' => T_('before'), 335 'sql' => '<' 336 ); 337 338 $this->basetypes['date'][] = array( 339 'name' => 'gt', 340 'description' => T_('after'), 341 'sql' => '>' 342 ); 343 344 $this->basetypes['days'][] = array( 345 'name' => 'lt', 346 'description' => T_('before (x) days ago'), 347 'sql' => '<' 348 ); 349 350 $this->basetypes['days'][] = array( 351 'name' => 'gt', 352 'description' => T_('after (x) days ago'), 353 'sql' => '>' 354 ); 355 356 $this->basetypes['recent_played'][] = array( 357 'name' => 'ply', 358 'description' => T_('Limit'), 359 'sql' => '`date`' 360 ); 361 $this->basetypes['recent_added'][] = array( 362 'name' => 'add', 363 'description' => T_('Limit'), 364 'sql' => '`addition_time`' 365 ); 366 367 $this->basetypes['recent_updated'][] = array( 368 'name' => 'upd', 369 'description' => T_('Limit'), 370 'sql' => '`update_time`' 371 ); 372 373 $this->basetypes['user_numeric'][] = array( 374 'name' => 'love', 375 'description' => T_('has loved'), 376 'sql' => 'userflag' 377 ); 378 379 $this->basetypes['user_numeric'][] = array( 380 'name' => '5star', 381 'description' => T_('has rated 5 stars'), 382 'sql' => '`rating` = 5' 383 ); 384 385 $this->basetypes['user_numeric'][] = array( 386 'name' => '4star', 387 'description' => T_('has rated 4 stars'), 388 'sql' => '`rating` = 4' 389 ); 390 391 $this->basetypes['user_numeric'][] = array( 392 'name' => '3star', 393 'description' => T_('has rated 3 stars'), 394 'sql' => '`rating` = 3' 395 ); 396 397 $this->basetypes['user_numeric'][] = array( 398 'name' => '2star', 399 'description' => T_('has rated 2 stars'), 400 'sql' => '`rating` = 2' 401 ); 402 403 $this->basetypes['user_numeric'][] = array( 404 'name' => '1star', 405 'description' => T_('has rated 1 star'), 406 'sql' => '`rating` = 1' 407 ); 408 409 $this->basetypes['user_numeric'][] = array( 410 'name' => 'unrated', 411 'description' => T_('has not rated'), 412 'sql' => 'unrated' 413 ); 414 $this->basetypes['multiple'] = array_merge($this->basetypes['text'], $this->basetypes['numeric']); 415 } 416 417 /** 418 * type_numeric 419 * 420 * Generic integer searches rules 421 * @param string $name 422 * @param string $label 423 * @param string $type 424 */ 425 private function type_numeric($name, $label, $type = 'numeric') 426 { 427 $this->types[] = array( 428 'name' => $name, 429 'label' => $label, 430 'type' => $type, 431 'widget' => array('input', 'number') 432 ); 433 } 434 435 /** 436 * type_date 437 * 438 * Generic integer searches rules 439 * @param string $name 440 * @param string $label 441 */ 442 private function type_date($name, $label) 443 { 444 $this->types[] = array( 445 'name' => $name, 446 'label' => $label, 447 'type' => 'date', 448 'widget' => array('input', 'datetime-local') 449 ); 450 } 451 452 /** 453 * type_text 454 * 455 * Generic text rules 456 * @param string $name 457 * @param string $label 458 */ 459 private function type_text($name, $label) 460 { 461 $this->types[] = array( 462 'name' => $name, 463 'label' => $label, 464 'type' => 'text', 465 'widget' => array('input', 'text') 466 ); 467 } 468 469 /** 470 * type_select 471 * 472 * Generic rule to select from a list 473 * @param string $name 474 * @param string $label 475 * @param string $type 476 * @param array $array 477 */ 478 private function type_select($name, $label, $type, $array) 479 { 480 $this->types[] = array( 481 'name' => $name, 482 'label' => $label, 483 'type' => $type, 484 'widget' => array('select', $array) 485 ); 486 } 487 488 /** 489 * type_boolean 490 * 491 * True or false generic searches 492 * @param string $name 493 * @param string $label 494 * @param string $type 495 */ 496 private function type_boolean($name, $label, $type = 'boolean') 497 { 498 $this->types[] = array( 499 'name' => $name, 500 'label' => $label, 501 'type' => $type, 502 'widget' => array('input', 'hidden') 503 ); 504 } 505 506 /** 507 * songtypes 508 * 509 * this is where all the searchtypes for songs are defined 510 */ 511 private function song_types() 512 { 513 $this->type_text('anywhere', T_('Any searchable text')); 514 $this->type_text('title', T_('Title')); 515 $this->type_text('album', T_('Album')); 516 $this->type_text('artist', T_('Song Artist')); 517 $this->type_text('album_artist', T_('Album Artist')); 518 $this->type_text('composer', T_('Composer')); 519 520 $this->type_numeric('year', T_('Year')); 521 522 if (AmpConfig::get('ratings')) { 523 $this->type_select('myrating', T_('My Rating'), 'numeric', $this->stars); 524 $this->type_select('rating', T_('Rating (Average)'), 'numeric', $this->stars); 525 $this->type_select('albumrating', T_('My Rating (Album)'), 'numeric', $this->stars); 526 $this->type_select('artistrating', T_('My Rating (Artist)'), 'numeric', $this->stars); 527 } 528 if (AmpConfig::get('userflags')) { 529 $this->type_text('favorite', T_('Favorites')); 530 $this->type_text('favorite_album', T_('Favorites (Album)')); 531 $this->type_text('favorite_artist', T_('Favorites (Artist)')); 532 } 533 534 /* HINT: Number of times object has been played */ 535 $this->type_numeric('played_times', T_('# Played')); 536 /* HINT: Number of times object has been skipped */ 537 $this->type_numeric('skipped_times', T_('# Skipped')); 538 /* HINT: Number of times object has been played OR skipped */ 539 $this->type_numeric('played_or_skipped_times', T_('# Played or Skipped')); 540 /* HINT: Percentage of (Times Played / Times skipped) * 100 */ 541 $this->type_numeric('play_skip_ratio', T_('Played/Skipped ratio')); 542 $this->type_numeric('last_play', T_('My Last Play'), 'days'); 543 $this->type_numeric('last_skip', T_('My Last Skip'), 'days'); 544 $this->type_numeric('last_play_or_skip', T_('My Last Play or Skip'), 'days'); 545 $this->type_boolean('played', T_('Played')); 546 $this->type_boolean('myplayed', T_('Played by Me')); 547 $this->type_boolean('myplayedalbum', T_('Played by Me (Album)')); 548 $this->type_boolean('myplayedartist', T_('Played by Me (Artist)')); 549 $this->type_numeric('time', T_('Length (in minutes)')); 550 551 $this->type_text('tag', T_('Genre')); 552 $this->type_text('album_tag', T_('Album Genre')); 553 $this->type_text('artist_tag', T_('Artist Genre')); 554 555 $users = $this->getUserRepository()->getValidArray(); 556 $this->type_select('other_user', T_('Another User'), 'user_numeric', $users); 557 $this->type_select('other_user_album', T_('Another User (Album)'), 'user_numeric', $users); 558 $this->type_select('other_user_artist', T_('Another User (Artist)'), 'user_numeric', $users); 559 560 $this->type_text('label', T_('Label')); 561 if (AmpConfig::get('licensing')) { 562 $licenses = array(); 563 foreach ($this->getLicenseRepository()->getAll() as $license_id) { 564 $license = new License($license_id); 565 $licenses[$license_id] = $license->name; 566 } 567 $this->type_select('license', T_('Music License'), 'boolean_numeric', $licenses); 568 } 569 570 $playlists = Playlist::get_playlist_array($this->search_user->id); 571 if (!empty($playlists)) { 572 $this->type_select('playlist', T_('Playlist'), 'boolean_numeric', $playlists); 573 } 574 $playlists = self::get_search_array($this->search_user->id); 575 if (!empty($playlists)) { 576 $this->type_select('smartplaylist', T_('Smart Playlist'), 'boolean_subsearch', $playlists); 577 } 578 579 $this->type_text('playlist_name', T_('Playlist Name')); 580 581 $this->type_text('comment', T_('Comment')); 582 $this->type_text('lyrics', T_('Lyrics')); 583 $this->type_text('file', T_('Filename')); 584 $bitrate_array = array( 585 '32', 586 '40', 587 '48', 588 '56', 589 '64', 590 '80', 591 '96', 592 '112', 593 '128', 594 '160', 595 '192', 596 '224', 597 '256', 598 '320', 599 '640', 600 '1280' 601 ); 602 $this->type_select('bitrate', T_('Bitrate'), 'numeric', $bitrate_array); 603 $this->type_date('added', T_('Added')); 604 $this->type_date('updated', T_('Updated')); 605 606 $this->type_numeric('recent_played', T_('Recently played'), 'recent_played'); 607 $this->type_numeric('recent_added', T_('Recently added'), 'recent_added'); 608 $this->type_numeric('recent_updated', T_('Recently updated'), 'recent_updated'); 609 610 $catalogs = array(); 611 foreach (Catalog::get_catalogs('music', $this->search_user->id) as $catid) { 612 $catalog = Catalog::create_from_id($catid); 613 $catalog->format(); 614 $catalogs[$catid] = $catalog->f_name; 615 } 616 if (!empty($catalogs)) { 617 $this->type_select('catalog', T_('Catalog'), 'boolean_numeric', $catalogs); 618 } 619 620 $this->type_text('mbid', T_('MusicBrainz ID')); 621 $this->type_text('mbid_album', T_('MusicBrainz ID (Album)')); 622 $this->type_text('mbid_artist', T_('MusicBrainz ID (Artist)')); 623 $this->type_boolean('possible_duplicate', T_('Possible Duplicate'), 'is_true'); 624 625 if (AmpConfig::get('enable_custom_metadata')) { 626 $metadataFields = array(); 627 $metadataFieldRepository = new MetadataField(); 628 foreach ($metadataFieldRepository->findAll() as $metadata) { 629 $metadataFields[$metadata->getId()] = $metadata->getName(); 630 } 631 $this->types[] = array( 632 'name' => 'metadata', 633 'label' => T_('Metadata'), 634 'type' => 'multiple', 635 'subtypes' => $metadataFields, 636 'widget' => array('subtypes', array('input', 'text')) 637 ); 638 } 639 } 640 641 /** 642 * artisttypes 643 * 644 * this is where all the searchtypes for artists are defined 645 */ 646 private function artist_types() 647 { 648 $this->type_text('title', T_('Name')); 649 650 $this->type_numeric('yearformed', T_('Year')); 651 $this->type_text('placeformed', T_('Place')); 652 653 if (AmpConfig::get('ratings')) { 654 $this->type_select('myrating', T_('My Rating'), 'numeric', $this->stars); 655 $this->type_select('rating', T_('Rating (Average)'), 'numeric', $this->stars); 656 } 657 658 if (AmpConfig::get('userflags')) { 659 $this->type_text('favorite', T_('Favorites')); 660 } 661 662 /* HINT: Number of times object has been played */ 663 $this->type_numeric('played_times', T_('# Played')); 664 $this->type_numeric('last_play', T_('My Last Play'), 'days'); 665 $this->type_numeric('last_skip', T_('My Last Skip'), 'days'); 666 $this->type_numeric('last_play_or_skip', T_('My Last Play or Skip'), 'days'); 667 $this->type_boolean('played', T_('Played')); 668 $this->type_boolean('myplayed', T_('Played by Me')); 669 $this->type_numeric('time', T_('Length (in minutes)')); 670 671 $this->type_text('tag', T_('Genre')); 672 673 $users = $this->getUserRepository()->getValidArray(); 674 $this->type_select('other_user', T_('Another User'), 'user_numeric', $users); 675 676 $this->type_numeric('recent_played', T_('Recently played'), 'recent_played'); 677 678 $catalogs = array(); 679 foreach (Catalog::get_catalogs('music', $this->search_user->id) as $catid) { 680 $catalog = Catalog::create_from_id($catid); 681 $catalog->format(); 682 $catalogs[$catid] = $catalog->f_name; 683 } 684 if (!empty($catalogs)) { 685 $this->type_select('catalog', T_('Catalog'), 'boolean_numeric', $catalogs); 686 } 687 688 $this->type_text('mbid', T_('MusicBrainz ID')); 689 690 $this->type_boolean('has_image', T_('Local Image')); 691 $this->type_numeric('image_width', T_('Image Width')); 692 $this->type_numeric('image_height', T_('Image Height')); 693 $this->type_boolean('possible_duplicate', T_('Possible Duplicate'), 'is_true'); 694 $this->type_boolean('possible_duplicate_album', T_('Possible Duplicate Albums'), 'is_true'); 695 } // artisttypes 696 697 /** 698 * albumtypes 699 * 700 * this is where all the searchtypes for albums are defined 701 */ 702 private function album_types() 703 { 704 $this->type_text('title', T_('Title')); 705 $this->type_text('artist', T_('Album Artist')); 706 707 $this->type_numeric('year', T_('Year')); 708 $this->type_numeric('original_year', T_('Original Year')); 709 $this->type_text('release_type', T_('Release Type')); 710 $this->type_text('release_status', T_('Release Status')); 711 712 if (AmpConfig::get('ratings')) { 713 $this->type_select('myrating', T_('My Rating'), 'numeric', $this->stars); 714 $this->type_select('rating', T_('Rating (Average)'), 'numeric', $this->stars); 715 $this->type_select('artistrating', T_('My Rating (Artist)'), 'numeric', $this->stars); 716 } 717 if (AmpConfig::get('userflags')) { 718 $this->type_text('favorite', T_('Favorites')); 719 } 720 721 /* HINT: Number of times object has been played */ 722 $this->type_numeric('played_times', T_('# Played')); 723 $this->type_numeric('last_play', T_('My Last Play'), 'days'); 724 $this->type_numeric('last_skip', T_('My Last Skip'), 'days'); 725 $this->type_numeric('last_play_or_skip', T_('My Last Play or Skip'), 'days'); 726 $this->type_boolean('played', T_('Played')); 727 $this->type_boolean('myplayed', T_('Played by Me')); 728 $this->type_numeric('time', T_('Length (in minutes)')); 729 730 $this->type_text('tag', T_('Genre')); 731 732 $users = $this->getUserRepository()->getValidArray(); 733 $this->type_select('other_user', T_('Another User'), 'user_numeric', $users); 734 735 $this->type_numeric('recent_played', T_('Recently played'), 'recent_played'); 736 737 $catalogs = array(); 738 foreach (Catalog::get_catalogs('music', $this->search_user->id) as $catid) { 739 $catalog = Catalog::create_from_id($catid); 740 $catalog->format(); 741 $catalogs[$catid] = $catalog->f_name; 742 } 743 if (!empty($catalogs)) { 744 $this->type_select('catalog', T_('Catalog'), 'boolean_numeric', $catalogs); 745 } 746 747 $this->type_text('mbid', T_('MusicBrainz ID')); 748 749 $this->type_boolean('has_image', T_('Local Image')); 750 $this->type_numeric('image_width', T_('Image Width')); 751 $this->type_numeric('image_height', T_('Image Height')); 752 $this->type_boolean('possible_duplicate', T_('Possible Duplicate'), 'is_true'); 753 } // albumtypes 754 755 /** 756 * videotypes 757 * 758 * this is where all the searchtypes for videos are defined 759 */ 760 private function video_types() 761 { 762 $this->type_text('file', T_('Filename')); 763 } 764 765 /** 766 * playlisttypes 767 * 768 * this is where all the searchtypes for playlists are defined 769 */ 770 private function playlist_types() 771 { 772 $this->type_text('title', T_('Name')); 773 } 774 775 /** 776 * labeltypes 777 * 778 * this is where all the searchtypes for labels are defined 779 */ 780 private function label_types() 781 { 782 $this->type_text('title', T_('Name')); 783 $this->type_text('category', T_('Category')); 784 } 785 786 /** 787 * usertypes 788 * 789 * this is where all the searchtypes for users are defined 790 */ 791 private function user_types() 792 { 793 $this->type_text('username', T_('Username')); 794 } 795 796 /** 797 * tagtypes 798 * 799 * this is where all the searchtypes for Genres are defined 800 */ 801 private function tag_types() 802 { 803 $this->type_text('name', T_('Genre')); 804 } 805 806 /** 807 * clean_request 808 * 809 * Sanitizes raw search data 810 * @param array $data 811 * @return array 812 */ 813 public static function clean_request($data) 814 { 815 $request = array(); 816 foreach ($data as $key => $value) { 817 $prefix = substr($key, 0, 4); 818 $value = trim((string)$value); 819 820 if ($prefix == 'rule' && strlen((string)$value)) { 821 $request[$key] = Dba::escape(filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES)); 822 } 823 } 824 825 // Figure out if they want an AND based search or an OR based search 826 switch ($data['operator']) { 827 case 'or': 828 $request['operator'] = 'OR'; 829 break; 830 default: 831 $request['operator'] = 'AND'; 832 break; 833 } 834 835 // Verify the type 836 switch ($data['type']) { 837 case 'album': 838 case 'artist': 839 case 'video': 840 case 'song': 841 case 'tag': // for Genres 842 case 'playlist': 843 case 'label': 844 case 'user': 845 $request['type'] = $data['type']; 846 break; 847 default: 848 $request['type'] = 'song'; 849 break; 850 } 851 852 return $request; 853 } // end clean_request 854 855 /** 856 * get_name_byid 857 * 858 * Returns the name of the saved search corresponding to the given ID 859 * @param string $search_id 860 * @return string 861 */ 862 public static function get_name_byid($search_id) 863 { 864 $sql = "SELECT `name` FROM `search` WHERE `id` = '$search_id'"; 865 $db_results = Dba::read($sql); 866 $row = Dba::fetch_assoc($db_results); 867 868 return $row['name']; 869 } 870 871 /** 872 * get_searches 873 * 874 * Return the IDs of all saved searches accessible by the current user. 875 * @param integer $user_id 876 * @return array 877 */ 878 public static function get_searches($user_id = null) 879 { 880 $user_id = Core::get_global('user')->id; 881 $key = 'searches'; 882 if (parent::is_cached($key, $user_id)) { 883 return parent::get_from_cache($key, $user_id); 884 } 885 $is_admin = (Access::check('interface', 100, $user_id) || $user_id == -1); 886 $sql = "SELECT `id` FROM `search` "; 887 $params = array(); 888 889 if (!$is_admin) { 890 $sql .= "WHERE (`user` = ? OR `type` = 'public') "; 891 $params[] = $user_id; 892 } 893 $sql .= "ORDER BY `name`"; 894 895 $db_results = Dba::read($sql, $params); 896 $results = array(); 897 while ($row = Dba::fetch_assoc($db_results)) { 898 $results[$row['id']] = $row['name']; 899 } 900 901 parent::add_to_cache($key, $user_id, $results); 902 903 return $results; 904 } 905 906 /** 907 * get_search_array 908 * Returns a list of searches accessible by the user with formatted name. 909 * @param integer $user_id 910 * @return array 911 */ 912 public static function get_search_array($user_id = null) 913 { 914 if (!$user_id) { 915 $user_id = Core::get_global('user')->id; 916 } 917 $key = 'searcharray'; 918 if (parent::is_cached($key, $user_id)) { 919 return parent::get_from_cache($key, $user_id); 920 } 921 $is_admin = (Access::check('interface', 100, $user_id) || $user_id == -1); 922 $sql = "SELECT `id`, IF(`user` = ?, `name`, CONCAT(`name`, ' (', `username`, ')')) AS `name` FROM `search` "; 923 $params = array($user_id); 924 925 if (!$is_admin) { 926 $sql .= "WHERE (`user` = ? OR `type` = 'public') "; 927 $params[] = $user_id; 928 } 929 $sql .= "ORDER BY `name`"; 930 //debug_event(self::class, 'get_searches query: ' . $sql, 5); 931 932 $db_results = Dba::read($sql, $params); 933 $results = array(); 934 while ($row = Dba::fetch_assoc($db_results)) { 935 $results[$row['id']] = $row['name']; 936 } 937 938 parent::add_to_cache($key, $user_id, $results); 939 940 return $results; 941 } // get_smartlist_array 942 943 /** 944 * run 945 * 946 * This function actually runs the search and returns an array of the 947 * results. 948 * @param array $data 949 * @param User $user 950 * @return integer[] 951 */ 952 public static function run($data, $user = null) 953 { 954 $limit = (int)($data['limit']); 955 $offset = (int)($data['offset']); 956 $random = ((int)$data['random'] > 0) ? 1 : 0; 957 $data = self::clean_request($data); 958 $search = new Search(null, $data['type'], $user); 959 $search->parse_rules($data); 960 961 // Generate BASE SQL 962 $limit_sql = ""; 963 if ($limit > 0) { 964 $limit_sql = ' LIMIT '; 965 if ($offset) { 966 $limit_sql .= $offset . ","; 967 } 968 $limit_sql .= $limit; 969 } 970 971 $search_info = $search->to_sql(); 972 $sql = $search_info['base'] . ' ' . $search_info['table_sql']; 973 if (!empty($search_info['where_sql'])) { 974 $sql .= ' WHERE ' . $search_info['where_sql']; 975 } 976 if (!empty($search_info['group_sql'])) { 977 $sql .= ' GROUP BY ' . $search_info['group_sql']; 978 if (!empty($search_info['having_sql'])) { 979 $sql .= ' HAVING ' . $search_info['having_sql']; 980 } 981 } 982 $sql .= ($random > 0) ? " ORDER BY RAND()" : " ORDER BY " . $search->order_by; 983 $sql .= ' ' . $limit_sql; 984 $sql = trim((string)$sql); 985 986 //debug_event(self::class, 'SQL get_items: ' . $sql, 5); 987 $db_results = Dba::read($sql); 988 $results = array(); 989 while ($row = Dba::fetch_assoc($db_results)) { 990 $results[] = (int)$row['id']; 991 } 992 993 return $results; 994 } 995 996 /** 997 * delete 998 * 999 * Does what it says on the tin. 1000 * @return boolean 1001 */ 1002 public function delete() 1003 { 1004 $search_id = Dba::escape($this->id); 1005 $sql = "DELETE FROM `search` WHERE `id` = ?"; 1006 Dba::write($sql, array($search_id)); 1007 1008 return true; 1009 } 1010 1011 /** 1012 * format 1013 * Gussy up the data 1014 * @param boolean $details 1015 */ 1016 public function format($details = true) 1017 { 1018 parent::format(); 1019 1020 $this->link = AmpConfig::get('web_path') . '/smartplaylist.php?action=show_playlist&playlist_id=' . $this->id; 1021 $this->f_link = '<a href="' . $this->link . '">' . scrub_out($this->f_name) . '</a>'; 1022 } 1023 1024 /** 1025 * get_items 1026 * 1027 * Return an array of the items output by our search (part of the 1028 * playlist interface). 1029 * @return array 1030 */ 1031 public function get_items() 1032 { 1033 $results = array(); 1034 1035 $sqltbl = $this->to_sql(); 1036 $sql = $sqltbl['base'] . ' ' . $sqltbl['table_sql']; 1037 if (!empty($sqltbl['where_sql'])) { 1038 $sql .= ' WHERE ' . $sqltbl['where_sql']; 1039 } 1040 if (!empty($sqltbl['group_sql'])) { 1041 $sql .= ' GROUP BY ' . $sqltbl['group_sql']; 1042 } 1043 if (!empty($sqltbl['having_sql'])) { 1044 $sql .= ' HAVING ' . $sqltbl['having_sql']; 1045 } 1046 1047 $sql .= ($this->random > 0) ? " ORDER BY RAND()" : " ORDER BY " . $this->order_by; 1048 if ($this->limit > 0) { 1049 $sql .= " LIMIT " . (string)($this->limit); 1050 } 1051 //debug_event(self::class, 'SQL get_items: ' . $sql, 5); 1052 1053 $db_results = Dba::read($sql); 1054 while ($row = Dba::fetch_assoc($db_results)) { 1055 $results[] = array( 1056 'object_id' => $row['id'], 1057 'object_type' => $this->searchtype 1058 ); 1059 } 1060 $this->date = time(); 1061 $this->set_last(count($results), 'last_count'); 1062 $this->set_last(self::get_total_duration($results), 'last_duration'); 1063 1064 return $results; 1065 } 1066 1067 /** 1068 * set_last 1069 * 1070 * @param integer $count 1071 * @param string $column 1072 */ 1073 private function set_last($count, $column) 1074 { 1075 if (in_array($column, array('last_count', 'last_duration'))) { 1076 $search_id = Dba::escape($this->id); 1077 $sql = "UPDATE `search` SET `" . Dba::escape($column) . "` = " . $count . " WHERE `id` = ?"; 1078 Dba::write($sql, array($search_id)); 1079 } 1080 } 1081 1082 /** 1083 * get_random_items 1084 * 1085 * Returns a randomly sorted array (with an optional limit) of the items 1086 * output by our search (part of the playlist interface) 1087 * @param integer $limit 1088 * @return array 1089 */ 1090 public function get_random_items($limit = null) 1091 { 1092 $results = array(); 1093 1094 $sqltbl = $this->to_sql(); 1095 $sql = $sqltbl['base'] . ' ' . $sqltbl['table_sql']; 1096 if (!empty($sqltbl['where_sql'])) { 1097 $sql .= ' WHERE ' . $sqltbl['where_sql']; 1098 } 1099 $rating_filter = AmpConfig::get_rating_filter(); 1100 if ($rating_filter > 0 && $rating_filter <= 5 && Core::get_global('user')) { 1101 $user_id = Core::get_global('user')->id; 1102 if (empty($sqltbl['where_sql'])) { 1103 $sql .= " WHERE "; 1104 } else { 1105 $sql .= " AND "; 1106 } 1107 $sql .= "`" . $this->searchtype . "`.`id` NOT IN (SELECT `object_id` FROM `rating` WHERE `rating`.`object_type` = '" . $this->searchtype . "' AND `rating`.`rating` <=$rating_filter AND `rating`.`user` = $user_id)"; 1108 } 1109 if (!empty($sqltbl['group_sql'])) { 1110 $sql .= ' GROUP BY ' . $sqltbl['group_sql']; 1111 } 1112 if (!empty($sqltbl['having_sql'])) { 1113 $sql .= ' HAVING ' . $sqltbl['having_sql']; 1114 } 1115 1116 $sql .= " ORDER BY RAND()"; 1117 $sql .= ($limit) 1118 ? " LIMIT " . (string) ($limit) 1119 : ""; 1120 //debug_event(self::class, 'SQL get_random_items: ' . $sql, 5); 1121 1122 $db_results = Dba::read($sql); 1123 1124 while ($row = Dba::fetch_assoc($db_results)) { 1125 $results[] = array( 1126 'object_id' => $row['id'], 1127 'object_type' => $this->searchtype 1128 ); 1129 } 1130 1131 return $results; 1132 } 1133 1134 /** 1135 * get_total_duration 1136 * Get the total duration of all songs. 1137 * @param array $songs 1138 * @return integer 1139 */ 1140 public static function get_total_duration($songs) 1141 { 1142 $song_ids = array(); 1143 foreach ($songs as $objects) { 1144 $song_ids[] = (string)$objects['object_id']; 1145 } 1146 $idlist = '(' . implode(',', $song_ids) . ')'; 1147 if ($idlist == '()') { 1148 return 0; 1149 } 1150 $sql = "SELECT SUM(`time`) FROM `song` WHERE `id` IN $idlist"; 1151 1152 $db_results = Dba::read($sql); 1153 $results = Dba::fetch_row($db_results); 1154 1155 return (int)$results['0']; 1156 } // get_total_duration 1157 1158 /** 1159 * name_to_basetype 1160 * 1161 * Iterates over our array of types to find out the basetype for 1162 * the passed string. 1163 * @param string $name 1164 * @return string|false 1165 */ 1166 public function name_to_basetype($name) 1167 { 1168 foreach ($this->types as $type) { 1169 if ($type['name'] == $name) { 1170 return $type['type']; 1171 } 1172 } 1173 1174 return false; 1175 } 1176 1177 /** 1178 * parse_rules 1179 * 1180 * Takes an array of sanitized search data from the form and generates our real array from it. 1181 * @param array $data 1182 */ 1183 public function parse_rules($data) 1184 { 1185 // check that a limit or random flag have been sent 1186 $this->random = (isset($data['random'])) ? (int) $data['random'] : $this->random; 1187 $this->limit = (isset($data['limit'])) ? (int) $data['limit'] : $this->limit; 1188 // parse the remaining rule* keys 1189 $this->rules = array(); 1190 foreach ($data as $rule => $value) { 1191 if ((($this->searchtype == 'artist' && $value == 'artist') || $value == 'name') && preg_match('/^rule_[0123456789]*$/', $rule)) { 1192 $value = 'title'; 1193 } 1194 if (preg_match('/^rule_(\d+)$/', $rule, $ruleID)) { 1195 $ruleID = (string)$ruleID[1]; 1196 $input_rule = (string)$data['rule_' . $ruleID . '_input']; 1197 $operator = $this->basetypes[$this->name_to_basetype($value)][$data['rule_' . $ruleID . '_operator']]['name']; 1198 //keep vertical bar in regular expression 1199 if (in_array($operator, ['regexp', 'notregexp'])) { 1200 $input_rule = str_replace("|", "\0", $input_rule); 1201 } 1202 foreach (explode('|', $input_rule) as $input) { 1203 $this->rules[] = array( 1204 $value, 1205 $operator, 1206 in_array($operator, ['regexp', 'notregexp']) ? str_replace("\0", "|", $input) : $input, 1207 $data['rule_' . $ruleID . '_subtype'] 1208 ); 1209 } 1210 } 1211 } 1212 $this->logic_operator = $data['operator']; 1213 } 1214 1215 /** 1216 * save 1217 * 1218 * Save this search to the database for use as a smart playlist 1219 * @return string 1220 */ 1221 public function save() 1222 { 1223 $user = Core::get_global('user'); 1224 // Make sure we have a unique name 1225 if (!$this->name) { 1226 $this->name = $user->username . ' - ' . get_datetime(time()); 1227 } 1228 $sql = "SELECT `id` FROM `search` WHERE `name` = ? AND `user` = ? AND `type` = ?;"; 1229 $db_results = Dba::read($sql, array($this->name, $user->id, $this->type)); 1230 if (Dba::num_rows($db_results)) { 1231 $this->name .= uniqid('', true); 1232 } 1233 1234 $sql = "INSERT INTO `search` (`name`, `type`, `user`, `username`, `rules`, `logic_operator`, `random`, `limit`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; 1235 Dba::write($sql, array( 1236 $this->name, 1237 $this->type, 1238 $user->id, 1239 $user->username, 1240 json_encode($this->rules), 1241 $this->logic_operator, 1242 ($this->random > 0) ? 1 : 0, 1243 $this->limit 1244 )); 1245 $insert_id = Dba::insert_id(); 1246 $this->id = (int)$insert_id; 1247 1248 return $insert_id; 1249 } 1250 1251 /** 1252 * to_js 1253 * 1254 * Outputs the javascript necessary to re-show the current set of rules. 1255 * @return string 1256 */ 1257 public function to_js() 1258 { 1259 $javascript = ""; 1260 foreach ($this->rules as $rule) { 1261 $javascript .= '<script>' . 'SearchRow.add("' . $rule[0] . '","' . $rule[1] . '","' . $rule[2] . '", "' . $rule[3] . '"); </script>'; 1262 } 1263 1264 return $javascript; 1265 } 1266 1267 /** 1268 * to_sql 1269 * 1270 * Call the appropriate real function. 1271 * @return array 1272 */ 1273 public function to_sql() 1274 { 1275 return call_user_func(array($this, $this->searchtype . "_to_sql")); 1276 } 1277 1278 /** 1279 * update 1280 * 1281 * This function updates the saved version with the current settings. 1282 * @param array|null $data 1283 * @return integer 1284 */ 1285 public function update(array $data = null) 1286 { 1287 if ($data && is_array($data)) { 1288 $this->name = $data['name']; 1289 $this->type = $data['pl_type']; 1290 $this->user = $data['pl_user']; 1291 $this->random = ((int)$data['random'] > 0 || $this->random) ? 1 : 0; 1292 $this->limit = $data['limit']; 1293 } 1294 $this->username = User::get_username($this->user); 1295 1296 if (!$this->id) { 1297 return 0; 1298 } 1299 1300 $sql = "UPDATE `search` SET `name` = ?, `type` = ?, `username` = ?, `rules` = ?, `logic_operator` = ?, `random` = ?, `limit` = ? WHERE `id` = ?"; 1301 Dba::write($sql, array( 1302 $this->name, 1303 $this->type, 1304 $this->username, 1305 json_encode($this->rules), 1306 $this->logic_operator, 1307 $this->random, 1308 $this->limit, 1309 $this->id 1310 )); 1311 // reformat after an update 1312 $this->format(); 1313 1314 return $this->id; 1315 } 1316 1317 /** 1318 * _mangle_data 1319 * 1320 * Private convenience function. Mangles the input according to a set 1321 * of predefined rules so that we don't have to include this logic in 1322 * foo_to_sql. 1323 * @param array|string $data 1324 * @param string|false $type 1325 * @param array $operator 1326 * @return array|boolean|integer|string|string[]|null 1327 */ 1328 private function _mangle_data($data, $type, $operator) 1329 { 1330 if ($operator['preg_match']) { 1331 $data = preg_replace($operator['preg_match'], $operator['preg_replace'], $data); 1332 } 1333 1334 if ($type == 'numeric' || $type == 'days') { 1335 return (int)($data); 1336 } 1337 1338 if ($type == 'boolean') { 1339 return make_bool($data); 1340 } 1341 1342 return $data; 1343 } 1344 1345 /** 1346 * album_to_sql 1347 * 1348 * Handles the generation of the SQL for album searches. 1349 * @return array 1350 */ 1351 private function album_to_sql() 1352 { 1353 $sql_logic_operator = $this->logic_operator; 1354 $user_id = $this->search_user->id; 1355 $catalog_disable = AmpConfig::get('catalog_disable'); 1356 $catalog_filter = AmpConfig::get('catalog_filter'); 1357 1358 $where = array(); 1359 $table = array(); 1360 $join = array(); 1361 $group = array(); 1362 $having = array(); 1363 $join['tag'] = array(); 1364 $groupdisks = AmpConfig::get('album_group'); 1365 1366 foreach ($this->rules as $rule) { 1367 $type = $this->name_to_basetype($rule[0]); 1368 $operator = array(); 1369 if (!$type) { 1370 return array(); 1371 } 1372 foreach ($this->basetypes[$type] as $op) { 1373 if ($op['name'] == $rule[1]) { 1374 $operator = $op; 1375 break; 1376 } 1377 } 1378 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 1379 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 1380 $sql_match_operator = $operator['sql']; 1381 if ($groupdisks) { 1382 /* 'album_group' DEFAULT: 1383 * `album`.`prefix`, `album`.`name`, `album`.`album_artist`, `album`.`release_type`, `album`.`release_status`, `album`.`mbid`, `album`.`year`, `album`.`original_year` 1384 */ 1385 $group[] = "`album`.`prefix`"; 1386 $group[] = "`album`.`name`"; 1387 $group[] = "`album`.`album_artist`"; 1388 $group[] = "`album`.`release_type`"; 1389 $group[] = "`album`.`release_status`"; 1390 $group[] = "`album`.`mbid`"; 1391 $group[] = "`album`.`year`"; 1392 $group[] = "`album`.`original_year`"; 1393 } else { 1394 $group[] = "`album`.`id`"; 1395 $group[] = "`album`.`disk`"; 1396 } 1397 1398 switch ($rule[0]) { 1399 case 'title': 1400 $where[] = "(`album`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) $sql_match_operator '$input')"; 1401 break; 1402 case 'year': 1403 $where[] = "`album`.`" . $rule[0] . "` $sql_match_operator '$input'"; 1404 break; 1405 case 'original_year': 1406 $where[] = "(`album`.`original_year` $sql_match_operator '$input' OR " . 1407 "(`album`.`original_year` IS NULL AND `album`.`year` $sql_match_operator '$input'))"; 1408 break; 1409 case 'time': 1410 $input = $input * 60; 1411 $where[] = "`album`.`time` $sql_match_operator '$input'"; 1412 break; 1413 case 'rating': 1414 // average ratings only 1415 $where[] = "`average_rating`.`avg` $sql_match_operator '$input'"; 1416 $table['average'] = "LEFT JOIN (SELECT `object_id`, ROUND(AVG(IFNULL(`rating`.`rating`,0))) AS `avg` FROM `rating` WHERE `rating`.`object_type`='album' GROUP BY `object_id`) AS `average_rating` on `average_rating`.`object_id` = `album`.`id` "; 1417 break; 1418 case 'favorite': 1419 $where[] = "(`album`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) $sql_match_operator '$input') AND `favorite_album_$user_id`.`user` = $user_id AND `favorite_album_$user_id`.`object_type` = 'album'"; 1420 // flag once per user 1421 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_album_$user_id")) 1422 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $user_id) AS `favorite_album_$user_id` ON `album`.`id`=`favorite_album_$user_id`.`object_id` AND `favorite_album_$user_id`.`object_type` = 'album'" 1423 : ""; 1424 break; 1425 case 'myrating': 1426 case 'artistrating': 1427 // combine these as they all do the same thing just different tables 1428 $looking = str_replace('rating', '', $rule[0]); 1429 $column = ($looking == 'my') ? 'id' : 'album_artist'; 1430 $my_type = ($looking == 'my') ? 'album' : $looking; 1431 if ($input == 0 && $sql_match_operator == '>=') { 1432 break; 1433 } 1434 1435 if ($input == 0 && $sql_match_operator == '<') { 1436 $input = -1; 1437 $sql_match_operator = '<=>'; 1438 } 1439 if ($input == 0 && $sql_match_operator == '<>') { 1440 $input = 1; 1441 $sql_match_operator = '>='; 1442 } 1443 if (($input == 0 && $sql_match_operator != '>') || ($input == 1 && $sql_match_operator == '<')) { 1444 $where[] = "`rating_" . $my_type . "_" . $user_id . "`.`rating` IS NULL"; 1445 } elseif ($sql_match_operator == '<>' || $sql_match_operator == '<' || $sql_match_operator == '<=' || $sql_match_operator == '!=') { 1446 $where[] = "(`rating_" . $my_type . "_" . $user_id . "`.`rating` $sql_match_operator $input OR `rating_" . $my_type . "_" . $user_id . "`.`rating` IS NULL)"; 1447 } else { 1448 $where[] = "`rating_" . $my_type . "_" . $user_id . "`.`rating` $sql_match_operator $input"; 1449 } 1450 // rating once per user 1451 $table['rating'] .= (!strpos((string) $table['rating'], "rating_" . $my_type . "_" . $user_id)) 1452 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `rating` FROM `rating` WHERE `user` = $user_id AND `object_type`='$my_type') AS `rating_" . $my_type . "_" . $user_id . "` ON `rating_" . $my_type . "_" . $user_id . "`.`object_id`=`album`.`$column`" 1453 : ""; 1454 break; 1455 case 'myplayed': 1456 $column = 'id'; 1457 $my_type = 'album'; 1458 $operator_sql = ((int)$sql_match_operator == 0) ? 'IS NULL' : 'IS NOT NULL'; 1459 // played once per user 1460 $table['myplayed'] .= (!strpos((string) $table['myplayed'], "myplayed_" . $my_type . "_" . $user_id)) 1461 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'stream' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `myplayed_" . $my_type . "_" . $user_id . "` ON `album`.`$column`=`myplayed_" . $my_type . "_" . $user_id . "`.`object_id` AND `myplayed_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1462 : ""; 1463 $where[] = "`myplayed_" . $my_type . "_" . $user_id . "`.`object_id` $operator_sql"; 1464 break; 1465 case 'last_play': 1466 $my_type = 'album'; 1467 $table['last_play'] .= (!strpos((string) $table['last_play'], "last_play_" . $my_type . "_" . $user_id)) 1468 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'stream' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_play_" . $my_type . "_" . $user_id . "` ON `album`.`id`=`last_play_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_play_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1469 : ""; 1470 $where[] = "`last_play_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1471 break; 1472 case 'last_skip': 1473 $my_type = 'album'; 1474 $table['last_skip'] .= (!strpos((string) $table['last_skip'], "last_skip_" . $my_type . "_" . $user_id)) 1475 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` = 'skip' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_skip_" . $my_type . "_" . $user_id . "` ON `song`.`id`=`last_skip_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_skip_" . $my_type . "_" . $user_id . "`.`object_type` = 'song'" 1476 : ""; 1477 $where[] = "`last_skip_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1478 $join['song'] = true; 1479 break; 1480 case 'last_play_or_skip': 1481 $my_type = 'album'; 1482 $table['last_play_or_skip'] .= (!strpos((string) $table['last_play_or_skip'], "last_play_or_skip_" . $my_type . "_" . $user_id)) 1483 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` IN ('stream', 'skip') AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_play_or_skip_" . $my_type . "_" . $user_id . "` ON `song`.`id`=`last_play_or_skip_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_play_or_skip_" . $my_type . "_" . $user_id . "`.`object_type` = 'song'" 1484 : ""; 1485 $where[] = "`last_play_or_skip_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1486 $join['song'] = true; 1487 break; 1488 case 'played_times': 1489 $where[] = "`album`.`id` IN (SELECT `object_count`.`object_id` FROM `object_count` WHERE `object_count`.`object_type` = 'album' AND `object_count`.`count_type` = 'stream' GROUP BY `object_count`.`object_id` HAVING COUNT(*) $sql_match_operator '$input')"; 1490 break; 1491 case 'release_type': 1492 $where[] = "`album`.`release_type` $sql_match_operator '$input'"; 1493 break; 1494 case 'release_status': 1495 $where[] = "`album`.`release_status` $sql_match_operator '$input'"; 1496 break; 1497 case 'other_user': 1498 $other_userid = $input; 1499 if ($sql_match_operator == 'userflag') { 1500 $where[] = "`favorite_album_$other_userid`.`user` = $other_userid AND `favorite_album_$other_userid`.`object_type` = 'album'"; 1501 // flag once per user 1502 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_album_$other_userid")) 1503 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $other_userid) AS `favorite_album_$other_userid` ON `song`.`album`=`favorite_album_$other_userid`.`object_id` AND `favorite_album_$other_userid`.`object_type` = 'album'" 1504 : ""; 1505 } else { 1506 $column = 'id'; 1507 $my_type = 'album'; 1508 $where[] = "`rating_album_" . $other_userid . '`.' . $sql_match_operator . " AND `rating_album_$other_userid`.`user` = $other_userid AND `rating_album_$other_userid`.`object_type` = 'album'"; 1509 // rating once per user 1510 $table['rating'] .= (!strpos((string) $table['rating'], "rating_" . $my_type . "_" . $user_id)) 1511 ? "LEFT JOIN `rating` AS `rating_" . $my_type . "_" . $user_id . "` ON `rating_" . $my_type . "_" . $user_id . "`.`object_type`='$my_type' AND `rating_" . $my_type . "_" . $user_id . "`.`object_id`=`$my_type`.`$column` AND `rating_" . $my_type . "_" . $user_id . "`.`user` = $user_id " 1512 : ""; 1513 } 1514 break; 1515 case 'recent_played': 1516 $key = md5($input . $sql_match_operator); 1517 $where[] = "`played_$key`.`object_id` IS NOT NULL"; 1518 $table['played_' . $key] = "LEFT JOIN (SELECT `object_id` FROM `object_count` WHERE `object_type` = 'album' ORDER BY $sql_match_operator DESC LIMIT $input) as `played_$key` ON `album`.`id` = `played_$key`.`object_id`"; 1519 break; 1520 case 'catalog': 1521 $where[] = "`album`.`catalog` $sql_match_operator '$input'"; 1522 break; 1523 case 'tag': 1524 $where[] = "`album`.`id` IN (SELECT `tag_map`.`object_id` FROM `tag_map` WHERE `tag_map`.`object_type`='album' AND `tag_map`.`tag_id` IN (SELECT `tag`.`id` FROM `tag` WHERE `tag`.`is_hidden` = 0 AND `tag`.`name` $sql_match_operator '$input'))"; 1525 break; 1526 case 'has_image': 1527 $where[] = ($sql_match_operator == '1') ? "`has_image`.`object_id` IS NOT NULL" : "`has_image`.`object_id` IS NULL"; 1528 $table['has_image'] = "LEFT JOIN (SELECT `object_id` FROM `image` WHERE `object_type` = 'album') as `has_image` ON `album`.`id` = `has_image`.`object_id`"; 1529 break; 1530 case 'image_height': 1531 case 'image_width': 1532 $looking = strpos($rule[0], "image_") ? str_replace('image_', '', $rule[0]) : str_replace('image ', '', $rule[0]); 1533 $where[] = "`image`.`$looking` $sql_match_operator '$input'"; 1534 $join['image'] = true; 1535 break; 1536 case 'artist': 1537 $where[] = "(`artist`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) $sql_match_operator '$input')"; 1538 $table['artist'] = "LEFT JOIN `artist` ON `album`.`album_artist`=`artist`.`id`"; 1539 break; 1540 case 'mbid': 1541 $where[] = "`album`.`mbid` $sql_match_operator '$input'"; 1542 break; 1543 case 'possible_duplicate': 1544 $where[] = "(`dupe_search1`.`dupe_id1` IS NOT NULL OR `dupe_search2`.`dupe_id2` IS NOT NULL)"; 1545 $table['dupe_search1'] = "LEFT JOIN (SELECT MIN(`id`) AS `dupe_id1`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) AS `fullname`, COUNT(LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`))) AS `Counting` FROM `album` GROUP BY `album_artist`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `disk`, `year`, `release_type`, `release_status` HAVING `Counting` > 1) AS `dupe_search1` ON `album`.`id` = `dupe_search1`.`dupe_id1`"; 1546 $table['dupe_search2'] = "LEFT JOIN (SELECT MAX(`id`) AS `dupe_id2`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) AS `fullname`, COUNT(LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`))) AS `Counting` FROM `album` GROUP BY `album_artist`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `disk`, `year`, `release_type`, `release_status` HAVING `Counting` > 1) AS `dupe_search2` ON `album`.`id` = `dupe_search2`.`dupe_id2`"; 1547 break; 1548 default: 1549 break; 1550 } // switch on ruletype album 1551 } // foreach rule 1552 1553 $join['song'] = $join['song'] || $catalog_disable || $catalog_filter; 1554 $join['catalog'] = $catalog_disable || $catalog_filter; 1555 $join['catalog_map'] = $catalog_filter; 1556 1557 $where_sql = implode(" $sql_logic_operator ", $where); 1558 1559 if ($join['song']) { 1560 $table['0_song'] = "LEFT JOIN `song` ON `song`.`album`=`album`.`id`"; 1561 } 1562 if ($join['catalog']) { 1563 $table['1_catalog'] = "LEFT JOIN `catalog` AS `catalog_se` ON `catalog_se`.`id`=`song`.`catalog`"; 1564 if (!empty($where_sql)) { 1565 $where_sql .= " AND `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 1566 } else { 1567 $where_sql .= " `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 1568 } 1569 } 1570 if ($join['catalog_map']) { 1571 $table['2_catalog_map'] = "LEFT JOIN `catalog_map` AS `catalog_map_album` ON `catalog_map_album`.`object_id`=`album`.`id` AND `catalog_map_album`.`object_type` = 'album' AND `catalog_map_album`.`catalog_id` = `catalog_se`.`id`"; 1572 if (!empty($where_sql)) { 1573 $where_sql .= " AND `catalog_se`.`filter_user` IN (0, $user_id)"; 1574 } else { 1575 $where_sql .= " `catalog_se`.`filter_user` IN (0, $user_id)"; 1576 } 1577 } 1578 if ($join['count']) { 1579 $table['object_count'] = "LEFT JOIN (SELECT `object_count`.`object_id`, MAX(`object_count`.`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = 'album' AND `object_count`.`user`='" . $user_id . "' AND `object_count`.`count_type` = 'stream' GROUP BY `object_count`.`object_id`) AS `object_count` ON `object_count`.`object_id`=`album`.`id`"; 1580 } 1581 if ($join['image']) { 1582 $table['0_song'] = "LEFT JOIN `song` ON `song`.`album`=`album`.`id` LEFT JOIN `image` ON `image`.`object_id`=`album`.`id`"; 1583 $where_sql .= " AND `image`.`object_type`='album'"; 1584 $where_sql .= " AND `image`.`size`='original'"; 1585 } 1586 ksort($table); 1587 $table_sql = implode(' ', $table); 1588 $group_sql = implode(',', $group); 1589 $having_sql = implode(" $sql_logic_operator ", $having); 1590 1591 return array( 1592 'base' => ($groupdisks) ? 'SELECT MIN(`album`.`id`) AS `id` FROM `album`' : 'SELECT MIN(`album`.`id`) AS `id`, MAX(`album`.`disk`) AS `disk` FROM `album`', 1593 'join' => $join, 1594 'where' => $where, 1595 'where_sql' => $where_sql, 1596 'table' => $table, 1597 'table_sql' => $table_sql, 1598 'group_sql' => $group_sql, 1599 'having_sql' => $having_sql 1600 ); 1601 } 1602 1603 /** 1604 * artist_to_sql 1605 * 1606 * Handles the generation of the SQL for artist searches. 1607 * @return array 1608 */ 1609 private function artist_to_sql() 1610 { 1611 $sql_logic_operator = $this->logic_operator; 1612 $user_id = $this->search_user->id; 1613 $catalog_disable = AmpConfig::get('catalog_disable'); 1614 $catalog_filter = AmpConfig::get('catalog_filter'); 1615 1616 $where = array(); 1617 $table = array(); 1618 $join = array(); 1619 $group = array(); 1620 $having = array(); 1621 1622 foreach ($this->rules as $rule) { 1623 $type = $this->name_to_basetype($rule[0]); 1624 $operator = array(); 1625 if (!$type) { 1626 return array(); 1627 } 1628 foreach ($this->basetypes[$type] as $op) { 1629 if ($op['name'] == $rule[1]) { 1630 $operator = $op; 1631 break; 1632 } 1633 } 1634 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 1635 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 1636 $sql_match_operator = $operator['sql']; 1637 1638 switch ($rule[0]) { 1639 case 'title': 1640 case 'name': 1641 $where[] = "(`artist`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) $sql_match_operator '$input')"; 1642 break; 1643 case 'yearformed': 1644 $where[] = "`artist`.`yearformed` $sql_match_operator '$input'"; 1645 break; 1646 case 'placeformed': 1647 $where[] = "`artist`.`placeformed` $sql_match_operator '$input'"; 1648 break; 1649 case 'time': 1650 $input = $input * 60; 1651 $where[] = "`artist`.`time` $sql_match_operator '$input'"; 1652 break; 1653 case 'tag': 1654 $where[] = "`artist`.`id` IN (SELECT `tag_map`.`object_id` FROM `tag_map` WHERE `tag_map`.`object_type`='artist' AND `tag_map`.`tag_id` IN (SELECT `tag`.`id` FROM `tag` WHERE `tag`.`is_hidden` = 0 AND `tag`.`name` $sql_match_operator '$input'))"; 1655 break; 1656 case 'rating': 1657 // average ratings only 1658 $where[] = "`average_rating`.`avg` $sql_match_operator '$input'"; 1659 $table['average'] = "LEFT JOIN (SELECT `object_id`, ROUND(AVG(IFNULL(`rating`.`rating`,0))) AS `avg` FROM `rating` WHERE `rating`.`object_type`='artist' GROUP BY `object_id`) AS `average_rating` on `average_rating`.`object_id` = `artist`.`id` "; 1660 break; 1661 case 'favorite': 1662 $where[] = "(`artist`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) $sql_match_operator '$input') AND `favorite_artist_$user_id`.`user` = $user_id AND `favorite_artist_$user_id`.`object_type` = 'artist'"; 1663 // flag once per user 1664 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_artist_$user_id")) 1665 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $user_id) AS `favorite_artist_$user_id` ON `artist`.`id`=`favorite_artist_$user_id`.`object_id` AND `favorite_artist_$user_id`.`object_type` = 'artist'" 1666 : ""; 1667 break; 1668 case 'has_image': 1669 $where[] = ($sql_match_operator == '1') ? "`has_image`.`object_id` IS NOT NULL" : "`has_image`.`object_id` IS NULL"; 1670 $table['has_image'] = "LEFT JOIN (SELECT `object_id` FROM `image` WHERE `object_type` = 'artist') as `has_image` ON `artist`.`id` = `has_image`.`object_id`"; 1671 break; 1672 case 'image_height': 1673 case 'image_width': 1674 $looking = strpos($rule[0], "image_") ? str_replace('image_', '', $rule[0]) : str_replace('image ', '', $rule[0]); 1675 $where[] = "`image`.`$looking` $sql_match_operator '$input'"; 1676 $join['image'] = true; 1677 break; 1678 case 'myrating': 1679 // combine these as they all do the same thing just different tables 1680 $column = 'id'; 1681 $my_type = 'artist'; 1682 if ($input == 0 && $sql_match_operator == '>=') { 1683 break; 1684 } 1685 if ($input == 0 && $sql_match_operator == '<') { 1686 $input = -1; 1687 $sql_match_operator = '='; 1688 } 1689 if ($input == 0 && $sql_match_operator == '<>') { 1690 $input = 1; 1691 $sql_match_operator = '>='; 1692 } 1693 if (($input == 0 && $sql_match_operator != '>') || ($input == 1 && $sql_match_operator == '<')) { 1694 $where[] = "`rating_" . $my_type . "_" . $user_id . "`.`rating` IS NULL"; 1695 } elseif ($sql_match_operator == '<>' || $sql_match_operator == '<' || $sql_match_operator == '<=' || $sql_match_operator == '!=') { 1696 $where[] = "(`rating_" . $my_type . "_" . $user_id . "`.`rating` $sql_match_operator $input OR `rating_" . $my_type . "_" . $user_id . "`.`rating` IS NULL)"; 1697 } else { 1698 $where[] = "`rating_" . $my_type . "_" . $user_id . "`.`rating` $sql_match_operator $input"; 1699 } 1700 // rating once per user 1701 $table['rating'] .= (!strpos((string) $table['rating'], "rating_" . $my_type . "_" . $user_id)) 1702 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `rating` FROM `rating` WHERE `user` = $user_id AND `object_type`='$my_type') AS `rating_" . $my_type . "_" . $user_id . "` ON `rating_" . $my_type . "_" . $user_id . "`.`object_id`=`artist`.`$column`" 1703 : ""; 1704 break; 1705 case 'myplayed': 1706 $column = 'id'; 1707 $my_type = 'artist'; 1708 $operator_sql = ((int)$sql_match_operator == 0) ? 'IS NULL' : 'IS NOT NULL'; 1709 // played once per user 1710 $table['myplayed'] .= (!strpos((string) $table['myplayed'], "myplayed_" . $my_type . "_" . $user_id)) 1711 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'stream' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `myplayed_" . $my_type . "_" . $user_id . "` ON `artist`.`$column`=`myplayed_" . $my_type . "_" . $user_id . "`.`object_id` AND `myplayed_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1712 : ""; 1713 $where[] = "`myplayed_" . $my_type . "_" . $user_id . "`.`object_id` $operator_sql"; 1714 break; 1715 case 'last_play': 1716 $my_type = 'artist'; 1717 $table['last_play'] .= (!strpos((string) $table['last_play'], "last_play_" . $my_type . "_" . $user_id)) 1718 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'stream' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_play_" . $my_type . "_" . $user_id . "` ON `artist`.`id`=`last_play_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_play_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1719 : ""; 1720 $where[] = "`last_play_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1721 break; 1722 case 'last_skip': 1723 $my_type = 'artist'; 1724 $table['last_skip'] .= (!strpos((string) $table['last_skip'], "last_skip_" . $my_type . "_" . $user_id)) 1725 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` = 'skip' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_skip_" . $my_type . "_" . $user_id . "` ON `song`.`id`=`last_skip_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_skip_" . $my_type . "_" . $user_id . "`.`object_type` = 'song'" 1726 : ""; 1727 $where[] = "`last_skip_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1728 $join['song'] = true; 1729 break; 1730 case 'last_play_or_skip': 1731 $my_type = 'artist'; 1732 $table['last_play_or_skip'] .= (!strpos((string) $table['last_play_or_skip'], "last_play_or_skip_" . $my_type . "_" . $user_id)) 1733 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` IN ('stream', 'skip') AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_play_or_skip_" . $my_type . "_" . $user_id . "` ON `song`.`id`=`last_play_or_skip_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_play_or_skip_" . $my_type . "_" . $user_id . "`.`object_type` = 'song'" 1734 : ""; 1735 $where[] = "`last_play_or_skip_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1736 $join['song'] = true; 1737 break; 1738 case 'played_times': 1739 $where[] = "`artist`.`id` IN (SELECT `object_count`.`object_id` FROM `object_count` WHERE `object_count`.`object_type` = 'artist' AND `object_count`.`count_type` = 'stream' GROUP BY `object_count`.`object_id` HAVING COUNT(*) $sql_match_operator '$input')"; 1740 break; 1741 case 'other_user': 1742 $other_userid = $input; 1743 if ($sql_match_operator == 'userflag') { 1744 $where[] = "`favorite_artist_$other_userid`.`user` = $other_userid AND `favorite_artist_$other_userid`.`object_type` = 'artist'"; 1745 // flag once per user 1746 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_artist_$other_userid")) 1747 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $other_userid) AS `favorite_artist_$other_userid` ON `song`.`artist`=`favorite_artist_$other_userid`.`object_id` AND `favorite_artist_$other_userid`.`object_type` = 'artist'" 1748 : ""; 1749 } else { 1750 $column = 'id'; 1751 $my_type = 'artist'; 1752 $where[] = "`rating_artist_" . $other_userid . '`.' . $sql_match_operator . " AND `rating_artist_$other_userid`.`user` = $other_userid AND `rating_artist_$other_userid`.`object_type` = 'artist'"; 1753 // rating once per user 1754 $table['rating'] .= (!strpos((string) $table['rating'], "rating_" . $my_type . "_" . $user_id)) 1755 ? "LEFT JOIN `rating` AS `rating_" . $my_type . "_" . $user_id . "` ON `rating_" . $my_type . "_" . $user_id . "`.`object_type`='$my_type' AND `rating_" . $my_type . "_" . $user_id . "`.`object_id`=`$my_type`.`$column` AND `rating_" . $my_type . "_" . $user_id . "`.`user` = $user_id " 1756 : ""; 1757 } 1758 break; 1759 case 'recent_played': 1760 $key = md5($input . $sql_match_operator); 1761 $where[] = "`played_$key`.`object_id` IS NOT NULL"; 1762 $table['played_' . $key] = "LEFT JOIN (SELECT `object_id` FROM `object_count` WHERE `object_type` = 'artist' ORDER BY $sql_match_operator DESC LIMIT $input) as `played_$key` ON `artist`.`id` = `played_$key`.`object_id`"; 1763 break; 1764 case 'catalog': 1765 $where[] = "`artist_catalog`.`catalog_id` $sql_match_operator '$input'"; 1766 $join['artist_catalog'] = true; 1767 break; 1768 case 'mbid': 1769 $where[] = "`artist`.`mbid` $sql_match_operator '$input'"; 1770 break; 1771 case 'possible_duplicate': 1772 $where[] = "(`dupe_search1`.`dupe_id1` IS NOT NULL OR `dupe_search2`.`dupe_id2` IS NOT NULL)"; 1773 $table['dupe_search1'] = "LEFT JOIN (SELECT MIN(`id`) AS `dupe_id1`, LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) AS `fullname`, COUNT(LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`))) AS `Counting` FROM `artist` GROUP BY `fullname` HAVING `Counting` > 1) AS `dupe_search1` ON `artist`.`id` = `dupe_search1`.`dupe_id1`"; 1774 $table['dupe_search2'] = "LEFT JOIN (SELECT MAX(`id`) AS `dupe_id2`, LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) AS `fullname`, COUNT(LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`))) AS `Counting` FROM `artist` GROUP BY `fullname` HAVING `Counting` > 1) AS `dupe_search2` ON `artist`.`id` = `dupe_search2`.`dupe_id2`"; 1775 break; 1776 case 'possible_duplicate_album': 1777 $where[] = "(`dupe_album_search1`.`dupe_album_id1` IS NOT NULL OR `dupe_album_search2`.`dupe_album_id2` IS NOT NULL)"; 1778 $table['dupe_album_search1'] = "LEFT JOIN (SELECT album_artist, MIN(`id`) AS `dupe_album_id1`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) AS `fullname`, COUNT(LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`))) AS `Counting` FROM `album` GROUP BY `album_artist`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `disk`, `year`, `release_type`, `release_status` HAVING `Counting` > 1) AS `dupe_album_search1` ON `artist`.`id` = `dupe_album_search1`.`album_artist`"; 1779 $table['dupe_album_search2'] = "LEFT JOIN (SELECT album_artist, MAX(`id`) AS `dupe_album_id2`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) AS `fullname`, COUNT(LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`))) AS `Counting` FROM `album` GROUP BY `album_artist`, LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `disk`, `year`, `release_type`, `release_status` HAVING `Counting` > 1) AS `dupe_album_search2` ON `artist`.`id` = `dupe_album_search2`.`album_artist`"; 1780 break; 1781 default: 1782 break; 1783 } // switch on ruletype artist 1784 } // foreach rule 1785 1786 $join['song'] = $join['song'] || $catalog_disable || $catalog_filter; 1787 $join['catalog'] = $catalog_disable || $catalog_filter; 1788 $join['catalog_map'] = $catalog_filter; 1789 1790 $where_sql = implode(" $sql_logic_operator ", $where); 1791 1792 if ($join['song']) { 1793 $table['0_song'] = "LEFT JOIN `song` ON `song`.`artist`=`artist`.`id`"; 1794 } 1795 if ($join['catalog']) { 1796 $table['1_catalog'] = "LEFT JOIN `catalog` AS `catalog_se` ON `catalog_se`.`id`=`song`.`catalog`"; 1797 if (!empty($where_sql)) { 1798 $where_sql .= " AND `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 1799 } else { 1800 $where_sql .= " `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 1801 } 1802 } 1803 if ($join['catalog_map']) { 1804 $table['2_catalog_map'] = "LEFT JOIN `catalog_map` AS `catalog_map_artist` ON `catalog_map_artist`.`object_id`=`artist`.`id` AND `catalog_map_artist`.`object_type` = 'artist' AND `catalog_map_artist`.`catalog_id` = `catalog_se`.`id`"; 1805 if (!empty($where_sql)) { 1806 $where_sql .= " AND `catalog_se`.`filter_user` IN (0, $user_id)"; 1807 } else { 1808 $where_sql .= " `catalog_se`.`filter_user` IN (0, $user_id)"; 1809 } 1810 } 1811 if ($join['count']) { 1812 $table['object_count'] = "LEFT JOIN (SELECT `object_count`.`object_id`, MAX(`object_count`.`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = 'artist' AND `object_count`.`user`='" . $user_id . "' AND `object_count`.`count_type` = 'stream' GROUP BY `object_count`.`object_id`) AS `object_count` ON `object_count`.`object_id`=`artist`.`id`"; 1813 } 1814 if ($join['image']) { 1815 $table['0_song'] = "LEFT JOIN `song` ON `song`.`artist`=`artist`.`id` LEFT JOIN `image` ON `image`.`object_id`=`artist`.`id`"; 1816 $where_sql .= " AND `image`.`object_type`='artist'"; 1817 $where_sql .= " AND `image`.`size`='original'"; 1818 } 1819 if ($join['artist_catalog']) { 1820 $table['catalog_map'] = "LEFT JOIN `catalog_map` AS `artist_catalog` ON `artist_catalog`.`object_type` = 'artist' AND `artist_catalog`.`object_id`=`artist`.`id`"; 1821 } 1822 ksort($table); 1823 $table_sql = implode(' ', $table); 1824 $group_sql = implode(',', $group); 1825 $having_sql = implode(" $sql_logic_operator ", $having); 1826 1827 return array( 1828 'base' => "SELECT DISTINCT(`artist`.`id`), `artist`.`name` FROM `artist`", 1829 'join' => $join, 1830 'where' => $where, 1831 'where_sql' => $where_sql, 1832 'table' => $table, 1833 'table_sql' => $table_sql, 1834 'group_sql' => $group_sql, 1835 'having_sql' => $having_sql 1836 ); 1837 } 1838 1839 /** 1840 * song_to_sql 1841 * Handles the generation of the SQL for song searches. 1842 * @return array 1843 */ 1844 private function song_to_sql() 1845 { 1846 $sql_logic_operator = $this->logic_operator; 1847 $user_id = $this->search_user->id; 1848 $catalog_disable = AmpConfig::get('catalog_disable'); 1849 $catalog_filter = AmpConfig::get('catalog_filter'); 1850 1851 $where = array(); 1852 $table = array(); 1853 $join = array(); 1854 $group = array(); 1855 $having = array(); 1856 $metadata = array(); 1857 1858 foreach ($this->rules as $rule) { 1859 $type = $this->name_to_basetype($rule[0]); 1860 $operator = array(); 1861 if (!$type) { 1862 return array(); 1863 } 1864 foreach ($this->basetypes[$type] as $op) { 1865 if ($op['name'] == $rule[1]) { 1866 $operator = $op; 1867 break; 1868 } 1869 } 1870 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 1871 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 1872 $sql_match_operator = $operator['sql']; 1873 1874 switch ($rule[0]) { 1875 case 'anywhere': 1876 $tag_string = "`song`.`id` IN (SELECT `tag_map`.`object_id` FROM `tag_map` WHERE `tag_map`.`object_type`='song' AND `tag_map`.`tag_id` IN (SELECT `tag`.`id` FROM `tag` WHERE `tag`.`is_hidden` = 0 AND `tag`.`name` $sql_match_operator '$input'))"; 1877 // we want AND NOT and like for this query to really exclude them 1878 if ($sql_match_operator == 'NOT LIKE' || $sql_match_operator == 'NOT' || $sql_match_operator == '!=') { 1879 $where[] = "NOT ((`artist`.`name` LIKE '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) LIKE '$input') OR (`album`.`name` LIKE '$input' OR LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) LIKE '$input') OR `song_data`.`comment` LIKE '$input' OR `song_data`.`label` LIKE '$input' OR `song`.`file` LIKE '$input' OR `song`.`title` LIKE '$input' OR NOT " . $tag_string . ')'; 1880 } else { 1881 $where[] = "((`artist`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) $sql_match_operator '$input') OR (`album`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) $sql_match_operator '$input') OR `song_data`.`comment` $sql_match_operator '$input' OR `song_data`.`label` $sql_match_operator '$input' OR `song`.`file` $sql_match_operator '$input' OR `song`.`title` $sql_match_operator '$input' OR " . $tag_string . ')'; 1882 } 1883 // join it all up 1884 $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`"; 1885 $table['artist'] = "LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id`"; 1886 $join['song_data'] = true; 1887 break; 1888 case 'tag': 1889 $where[] = "`song`.`id` IN (SELECT `tag_map`.`object_id` FROM `tag_map` WHERE `tag_map`.`object_type`='song' AND `tag_map`.`tag_id` IN (SELECT `tag`.`id` FROM `tag` WHERE `tag`.`is_hidden` = 0 AND `tag`.`name` $sql_match_operator '$input'))"; 1890 break; 1891 case 'album_tag': 1892 $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`"; 1893 $where[] = "`album`.`id` IN (SELECT `tag_map`.`object_id` FROM `tag_map` WHERE `tag_map`.`object_type`='album' AND `tag_map`.`tag_id` IN (SELECT `tag`.`id` FROM `tag` WHERE `tag`.`is_hidden` = 0 AND `tag`.`name` $sql_match_operator '$input'))"; 1894 break; 1895 case 'artist_tag': 1896 $table['artist'] = "LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id`"; 1897 $where[] = "`artist`.`id` IN (SELECT `tag_map`.`object_id` FROM `tag_map` WHERE `tag_map`.`object_type`='artist' AND `tag_map`.`tag_id` IN (SELECT `tag`.`id` FROM `tag` WHERE `tag`.`is_hidden` = 0 AND `tag`.`name` $sql_match_operator '$input'))"; 1898 break; 1899 case 'title': 1900 $where[] = "`song`.`title` $sql_match_operator '$input'"; 1901 break; 1902 case 'album': 1903 $where[] = "(`album`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) $sql_match_operator '$input')"; 1904 $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`"; 1905 break; 1906 case 'artist': 1907 $where[] = "(`artist`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) $sql_match_operator '$input')"; 1908 $table['artist'] = "LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id`"; 1909 break; 1910 case 'album_artist': 1911 $where[] = "(`album_artist`.`name` $sql_match_operator '$input' " . 1912 " OR LTRIM(CONCAT(COALESCE(`album_artist`.`prefix`, ''), " . 1913 "' ', `album_artist`.`name`)) $sql_match_operator '$input')"; 1914 $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`"; 1915 $table['album_artist'] = "LEFT JOIN `artist` AS `album_artist` ON `album`.`album_artist`=`album_artist`.`id`"; 1916 break; 1917 case 'composer': 1918 $where[] = "`song`.`composer` $sql_match_operator '$input'"; 1919 break; 1920 case 'time': 1921 $input = $input * 60; 1922 $where[] = "`song`.`time` $sql_match_operator '$input'"; 1923 break; 1924 case 'file': 1925 $where[] = "`song`.`file` $sql_match_operator '$input'"; 1926 break; 1927 case 'year': 1928 $where[] = "`song`.`year` $sql_match_operator '$input'"; 1929 break; 1930 case 'comment': 1931 $where[] = "`song_data`.`comment` $sql_match_operator '$input'"; 1932 $join['song_data'] = true; 1933 break; 1934 case 'label': 1935 $where[] = "`song_data`.`label` $sql_match_operator '$input'"; 1936 $join['song_data'] = true; 1937 break; 1938 case 'lyrics': 1939 $where[] = "`song_data`.`lyrics` $sql_match_operator '$input'"; 1940 $join['song_data'] = true; 1941 break; 1942 case 'played': 1943 $where[] = "`song`.`played` = '$sql_match_operator'"; 1944 break; 1945 case 'last_play': 1946 $my_type = 'song'; 1947 $table['last_play'] .= (!strpos((string) $table['last_play'], "last_play_" . $my_type . "_" . $user_id)) 1948 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'stream' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_play_" . $my_type . "_" . $user_id . "` ON `song`.`id`=`last_play_" . $my_type . "_" . $user_id . "`.`object_id` AND `last_play_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1949 : ""; 1950 $where[] = "`last_play_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1951 break; 1952 case 'last_skip': 1953 $my_type = 'song'; 1954 $table['last_skip'] .= (!strpos((string) $table['last_skip'], "last_skip_" . $my_type . "_" . $user_id)) 1955 ? 1956 "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` " . 1957 "WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'skip' " . 1958 "AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `last_skip_" . $my_type . "_" . $user_id . "` " . 1959 "ON `song`.`id`=`last_skip_" . $my_type . "_" . $user_id . "`.`object_id` " . 1960 "AND `last_skip_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type' " : ""; 1961 $where[] = "`last_skip_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1962 break; 1963 case 'last_play_or_skip': 1964 $my_type = 'song'; 1965 $table['last_play_or_skip'] .= (!strpos((string) $table['play_or_skip'], "play_or_skip_" . $my_type . "_" . $user_id)) 1966 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user`, MAX(`date`) AS `date` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` IN ('stream', 'skip') AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `play_or_skip_" . $my_type . "_" . $user_id . "` ON `song`.`id`=`play_or_skip_" . $my_type . "_" . $user_id . "`.`object_id` AND `play_or_skip_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1967 : ""; 1968 $where[] = "`play_or_skip_" . $my_type . "_" . $user_id . "`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1969 break; 1970 case 'played_times': 1971 $where[] = "`song`.`id` IN (SELECT `object_count`.`object_id` FROM `object_count` WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` = 'stream' GROUP BY `object_count`.`object_id` HAVING COUNT(*) $sql_match_operator '$input')"; 1972 break; 1973 case 'skipped_times': 1974 $where[] = "`song`.`id` IN (SELECT `object_count`.`object_id` FROM `object_count` WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` = 'skip' GROUP BY `object_count`.`object_id` HAVING COUNT(*) $sql_match_operator '$input')"; 1975 break; 1976 case 'played_or_skipped_times': 1977 $where[] = "`song`.`id` IN (SELECT `object_count`.`object_id` FROM `object_count` " . 1978 "WHERE `object_count`.`object_type` = 'song' AND `object_count`.`count_type` IN ('stream', 'skip') " . 1979 "GROUP BY `object_count`.`object_id` HAVING COUNT(*) $sql_match_operator '$input')"; 1980 break; 1981 case 'play_skip_ratio': 1982 $where[] = "`song`.`id` IN (SELECT `song`.`id` FROM `song` LEFT JOIN (SELECT COUNT(`object_id`) AS `counting`, `object_id`, `count_type` FROM `object_count` WHERE `object_type` = 'song' AND `count_type` = 'stream' GROUP BY `object_id`, `count_type`) AS `stream_count` on `song`.`id` = `stream_count`.`object_id` LEFT JOIN (SELECT COUNT(`object_id`) AS `counting`, `object_id`, `count_type` FROM `object_count` WHERE `object_type` = 'song' AND `count_type` = 'skip' GROUP BY `object_id`, `count_type`) AS `skip_count` on `song`.`id` = `skip_count`.`object_id` WHERE ((IFNULL(`stream_count`.`counting`, 0)/IFNULL(`skip_count`.`counting`, 0)) * 100) $sql_match_operator '$input' GROUP BY `song`.`id`)"; 1983 break; 1984 case 'myplayed': 1985 case 'myplayedalbum': 1986 case 'myplayedartist': 1987 // combine these as they all do the same thing just different tables 1988 $looking = str_replace('myplayed', '', $rule[0]); 1989 $column = ($looking == '') ? 'id' : $looking; 1990 $my_type = ($looking == '') ? 'song' : $looking; 1991 $operator_sql = ((int) $sql_match_operator == 0) ? 'IS NULL' : 'IS NOT NULL'; 1992 // played once per user 1993 $table['myplayed'] .= (!strpos((string) $table['myplayed'], "myplayed_" . $my_type . "_" . $user_id)) 1994 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `object_count` WHERE `object_count`.`object_type` = '$my_type' AND `object_count`.`count_type` = 'stream' AND `object_count`.`user`=$user_id GROUP BY `object_id`, `object_type`, `user`) AS `myplayed_" . $my_type . "_" . $user_id . "` ON `song`.`$column`=`myplayed_" . $my_type . "_" . $user_id . "`.`object_id` AND `myplayed_" . $my_type . "_" . $user_id . "`.`object_type` = '$my_type'" 1995 : ""; 1996 $where[] = "`myplayed_" . $my_type . "_" . $user_id . "`.`object_id` $operator_sql"; 1997 break; 1998 case 'bitrate': 1999 $input = $input * 1000; 2000 $where[] = "`song`.`bitrate` $sql_match_operator '$input'"; 2001 break; 2002 case 'rating': 2003 // average ratings only 2004 $where[] = "`average_rating`.`avg` $sql_match_operator '$input'"; 2005 $table['average'] = "LEFT JOIN (SELECT `object_id`, ROUND(AVG(IFNULL(`rating`.`rating`,0))) AS `avg` FROM `rating` WHERE `rating`.`object_type`='song' GROUP BY `object_id`) AS `average_rating` on `average_rating`.`object_id` = `song`.`id` "; 2006 break; 2007 case 'favorite': 2008 $where[] = "`song`.`title` $sql_match_operator '$input' AND `favorite_song_$user_id`.`user` = $user_id AND `favorite_song_$user_id`.`object_type` = 'song'"; 2009 // flag once per user 2010 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_song_$user_id")) 2011 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $user_id) AS `favorite_song_$user_id` ON `song`.`id`=`favorite_song_$user_id`.`object_id` AND `favorite_song_$user_id`.`object_type` = 'song'" 2012 : ""; 2013 break; 2014 case 'favorite_album': 2015 $where[] = "(`album`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)) $sql_match_operator '$input') AND `favorite_album_$user_id`.`user` = $user_id AND `favorite_album_$user_id`.`object_type` = 'album'"; 2016 // flag once per user 2017 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_album_$user_id")) 2018 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $user_id) AS `favorite_album_$user_id` ON `album`.`id`=`favorite_album_$user_id`.`object_id` AND `favorite_album_$user_id`.`object_type` = 'album'" 2019 : ""; 2020 $join['album'] = true; 2021 break; 2022 case 'favorite_artist': 2023 $where[] = "(`artist`.`name` $sql_match_operator '$input' OR LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)) $sql_match_operator '$input') AND `favorite_artist_$user_id`.`user` = $user_id AND `favorite_artist_$user_id`.`object_type` = 'artist'"; 2024 // flag once per user 2025 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_artist_$user_id")) 2026 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $user_id) AS `favorite_artist_$user_id` ON `artist`.`id`=`favorite_artist_$user_id`.`object_id` AND `favorite_artist_$user_id`.`object_type` = 'artist'" 2027 : ""; 2028 $join['artist'] = true; 2029 break; 2030 case 'myrating': 2031 case 'albumrating': 2032 case 'artistrating': 2033 // combine these as they all do the same thing just different tables 2034 $looking = str_replace('rating', '', $rule[0]); 2035 $column = ($looking == 'my') ? 'id' : $looking; 2036 $my_type = ($looking == 'my') ? 'song' : $looking; 2037 if ($input == 0 && $sql_match_operator == '>=') { 2038 break; 2039 } 2040 if ($input == 0 && $sql_match_operator == '<') { 2041 $input = -1; 2042 $sql_match_operator = '='; 2043 } 2044 if ($input == 0 && $sql_match_operator == '<>') { 2045 $input = 1; 2046 $sql_match_operator = '>='; 2047 } 2048 if (($input == 0 && $sql_match_operator != '>') || ($input == 1 && $sql_match_operator == '<')) { 2049 $where[] = "`rating_" . $my_type . "_" . $user_id . "`.`rating` IS NULL"; 2050 } elseif ($sql_match_operator == '<>' || $sql_match_operator == '<' || $sql_match_operator == '<=' || $sql_match_operator == '!=') { 2051 $where[] = "(`rating_" . $my_type . "_" . $user_id . "`.`rating` $sql_match_operator $input OR `rating_" . $my_type . "_" . $user_id . "`.`rating` IS NULL)"; 2052 } else { 2053 $where[] = "`rating_" . $my_type . "_" . $user_id . "`.`rating` $sql_match_operator $input"; 2054 } 2055 // rating once per user 2056 $table['rating'] .= (!strpos((string) $table['rating'], "rating_" . $my_type . "_" . $user_id)) 2057 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `rating` FROM `rating` WHERE `user` = $user_id AND `object_type`='$my_type') AS `rating_" . $my_type . "_" . $user_id . "` ON `rating_" . $my_type . "_" . $user_id . "`.`object_id`=`song`.`$column`" 2058 : ""; 2059 break; 2060 case 'catalog': 2061 $where[] = "`song`.`catalog` $sql_match_operator '$input'"; 2062 break; 2063 case 'other_user': 2064 case 'other_user_album': 2065 case 'other_user_artist': 2066 // combine these as they all do the same thing just different tables 2067 $looking = str_replace('other_user_', '', $rule[0]); 2068 $column = ($looking == 'other_user') ? 'id' : $looking; 2069 $my_type = ($looking == 'other_user') ? 'song' : $looking; 2070 $other_userid = $input; 2071 if ($sql_match_operator == 'userflag') { 2072 $where[] = "`favorite_" . $my_type . "_" . $other_userid . "`.`user` = $other_userid AND `favorite_" . $my_type . "_" . $other_userid . "`.`object_type` = '$my_type'"; 2073 // flag once per user 2074 $table['favorite'] .= (!strpos((string) $table['favorite'], "favorite_" . $my_type . "_" . $other_userid . "")) 2075 ? "LEFT JOIN (SELECT `object_id`, `object_type`, `user` FROM `user_flag` WHERE `user` = $other_userid) AS `favorite_" . $my_type . "_" . $other_userid . "` ON `song`.`$column`=`favorite_" . $my_type . "_" . $other_userid . "`.`object_id` AND `favorite_" . $my_type . "_" . $other_userid . "`.`object_type` = '$my_type'" 2076 : ""; 2077 } else { 2078 $unrated = ($sql_match_operator == 'unrated'); 2079 $where[] = ($unrated) ? "`song`.`$column` NOT IN (SELECT `object_id` FROM `rating` WHERE `object_type` = '$my_type' AND `user` = $other_userid)" : "`rating_" . $my_type . "_" . $other_userid . "`.$sql_match_operator AND `rating_" . $my_type . "_" . $other_userid . "`.`user` = $other_userid AND `rating_" . $my_type . "_" . $other_userid . "`.`object_type` = '$my_type'"; 2080 // rating once per user 2081 $table['rating'] .= (!strpos((string) $table['rating'], "rating_" . $my_type . "_" . $other_userid)) 2082 ? "LEFT JOIN `rating` AS `rating_" . $my_type . "_" . $other_userid . "` ON `rating_" . $my_type . "_" . $other_userid . "`.`object_type`='$my_type' AND `rating_" . $my_type . "_" . $other_userid . "`.`object_id`=`song`.`$column` AND `rating_" . $my_type . "_" . $other_userid . "`.`user` = $other_userid " 2083 : ""; 2084 } 2085 break; 2086 case 'playlist_name': 2087 $join['playlist'] = true; 2088 $join['playlist_data'] = true; 2089 $where[] = "`playlist`.`name` $sql_match_operator '$input'"; 2090 break; 2091 case 'playlist': 2092 $join['playlist_data'] = true; 2093 $where[] = "`playlist_data`.`playlist` $sql_match_operator '$input'"; 2094 break; 2095 case 'smartplaylist': 2096 $subsearch = new Search($input, 'song', $this->search_user); 2097 $results = $subsearch->get_items(); 2098 $itemstring = ''; 2099 if (count($results) > 0) { 2100 foreach ($results as $item) { 2101 $itemstring .= ' ' . $item['object_id'] . ','; 2102 } 2103 $where[] = "`song`.`id` $sql_match_operator IN (" . substr($itemstring, 0, -1) . ")"; 2104 } 2105 break; 2106 case 'license': 2107 $where[] = "`song`.`license` $sql_match_operator '$input'"; 2108 break; 2109 case 'added': 2110 $input = strtotime((string) $input); 2111 $where[] = "`song`.`addition_time` $sql_match_operator $input"; 2112 break; 2113 case 'updated': 2114 $input = strtotime((string) $input); 2115 $where[] = "`song`.`update_time` $sql_match_operator $input"; 2116 break; 2117 case 'recent_played': 2118 $key = md5($input . $sql_match_operator); 2119 $where[] = "`played_$key`.`object_id` IS NOT NULL"; 2120 $table['played_' . $key] = "LEFT JOIN (SELECT `object_id` FROM `object_count` WHERE `object_type` = 'song' ORDER BY $sql_match_operator DESC LIMIT $input) as `played_$key` ON `song`.`id` = `played_$key`.`object_id`"; 2121 break; 2122 case 'recent_added': 2123 $key = md5($input . $sql_match_operator); 2124 $where[] = "`addition_time_$key`.`id` IS NOT NULL"; 2125 $table['addition_' . $key] = "LEFT JOIN (SELECT `id` FROM `song` ORDER BY $sql_match_operator DESC LIMIT $input) as `addition_time_$key` ON `song`.`id` = `addition_time_$key`.`id`"; 2126 break; 2127 case 'recent_updated': 2128 $key = md5($input . $sql_match_operator); 2129 $where[] = "`update_time_$key`.`id` IS NOT NULL"; 2130 $table['update_' . $key] = "LEFT JOIN (SELECT `id` FROM `song` ORDER BY $sql_match_operator DESC LIMIT $input) as `update_time_$key` ON `song`.`id` = `update_time_$key`.`id`"; 2131 break; 2132 case 'mbid': 2133 $where[] = "`song`.`mbid` $sql_match_operator '$input'"; 2134 break; 2135 case 'mbid_album': 2136 $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`"; 2137 $where[] = "`album`.`mbid` $sql_match_operator '$input'"; 2138 break; 2139 case 'mbid_artist': 2140 $table['artist'] = "LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id`"; 2141 $where[] = "`artist`.`mbid` $sql_match_operator '$input'"; 2142 break; 2143 case 'possible_duplicate': 2144 $where[] = "(`dupe_search1`.`dupe_id1` IS NOT NULL OR `dupe_search2`.`dupe_id2` IS NOT NULL)"; 2145 $table['dupe_search1'] = "LEFT JOIN (SELECT MIN(`song`.`id`) AS `dupe_id1`, CONCAT(LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)), LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `album`.`disk`, `song`.`title`) AS `fullname`, COUNT(CONCAT(LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)), LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `album`.`disk`, `song`.`title`)) AS `counting` FROM `song` LEFT JOIN `album` on `song`.`album` = `album`.`id` LEFT JOIN `artist` ON `song`.`artist` = `artist`.`id` GROUP BY `fullname` HAVING `Counting` > 1) AS `dupe_search1` ON `song`.`id` = `dupe_search1`.`dupe_id1`"; 2146 $table['dupe_search2'] = "LEFT JOIN (SELECT MAX(`song`.`id`) AS `dupe_id2`, CONCAT(LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)), LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `album`.`disk`, `song`.`title`) AS `fullname`, COUNT(CONCAT(LTRIM(CONCAT(COALESCE(`artist`.`prefix`, ''), ' ', `artist`.`name`)), LTRIM(CONCAT(COALESCE(`album`.`prefix`, ''), ' ', `album`.`name`)), `album`.`disk`, `song`.`title`)) AS `counting` FROM `song` LEFT JOIN `album` on `song`.`album` = `album`.`id` LEFT JOIN `artist` ON `song`.`artist` = `artist`.`id` GROUP BY `fullname` HAVING `Counting` > 1) AS `dupe_search2` ON `song`.`id` = `dupe_search2`.`dupe_id2`"; 2147 break; 2148 case 'metadata': 2149 $field = (int)$rule[3]; 2150 if ($sql_match_operator === '=' && strlen($input) == 0) { 2151 $where[] = "NOT EXISTS (SELECT NULL FROM `metadata` WHERE `metadata`.`object_id` = `song`.`id` AND `metadata`.`field` = {$field})"; 2152 } else { 2153 $parsedInput = is_numeric($input) ? $input : '"' . $input . '"'; 2154 if (!array_key_exists($field, $metadata)) { 2155 $metadata[$field] = array(); 2156 } 2157 $metadata[$field][] = "`metadata`.`data` $sql_match_operator $parsedInput"; 2158 } 2159 break; 2160 default: 2161 break; 2162 } // switch on ruletype song 2163 } // foreach over rules 2164 2165 // translate metadata queries into sql for each field 2166 foreach ($metadata as $metadata_field => $metadata_queries) { 2167 $metadata_sql = "EXISTS (SELECT NULL FROM `metadata` WHERE `metadata`.`object_id` = `song`.`id` AND `metadata`.`field` = {$metadata_field} AND ("; 2168 $metadata_sql .= implode(" $sql_logic_operator ", $metadata_queries); 2169 $where[] = $metadata_sql . '))'; 2170 } 2171 2172 $join['catalog_map'] = $catalog_filter; 2173 $join['catalog'] = $catalog_disable || $catalog_filter; 2174 2175 $where_sql = implode(" $sql_logic_operator ", $where); 2176 2177 // now that we know which things we want to JOIN... 2178 if ($join['song_data']) { 2179 $table['song_data'] = "LEFT JOIN `song_data` ON `song`.`id`=`song_data`.`song_id`"; 2180 } 2181 if ($join['playlist_data']) { 2182 $table['playlist_data'] = "LEFT JOIN `playlist_data` ON `song`.`id`=`playlist_data`.`object_id` AND `playlist_data`.`object_type`='song'"; 2183 if ($join['playlist']) { 2184 $table['playlist'] = "LEFT JOIN `playlist` ON `playlist_data`.`playlist`=`playlist`.`id`"; 2185 } 2186 } 2187 if ($join['catalog']) { 2188 $table['1_catalog'] = "LEFT JOIN `catalog` AS `catalog_se` ON `catalog_se`.`id`=`song`.`catalog`"; 2189 if (!empty($where_sql)) { 2190 $where_sql .= " AND `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 2191 } else { 2192 $where_sql .= " `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 2193 } 2194 } 2195 if ($join['catalog_map']) { 2196 $table['2_catalog_map'] = "LEFT JOIN `catalog_map` AS `catalog_map_song` ON `catalog_map_song`.`object_id`=`song`.`id` AND `catalog_map_song`.`object_type` = 'song' AND `catalog_map_song`.`catalog_id` = `catalog_se`.`id`"; 2197 if (!empty($where_sql)) { 2198 $where_sql .= " AND `catalog_se`.`filter_user` IN (0, $user_id)"; 2199 } else { 2200 $where_sql .= " `catalog_se`.`filter_user` IN (0, $user_id)"; 2201 } 2202 } 2203 if ($join['album']) { 2204 $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`"; 2205 } 2206 if ($join['artist']) { 2207 $table['artist'] = "LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id`"; 2208 } 2209 ksort($table); 2210 $table_sql = implode(' ', $table); 2211 $group_sql = implode(',', $group); 2212 $having_sql = implode(" $sql_logic_operator ", $having); 2213 2214 return array( 2215 'base' => 'SELECT DISTINCT(`song`.`id`), `song`.`file` FROM `song`', 2216 'join' => $join, 2217 'where' => $where, 2218 'where_sql' => $where_sql, 2219 'table' => $table, 2220 'table_sql' => $table_sql, 2221 'group_sql' => $group_sql, 2222 'having_sql' => $having_sql 2223 ); 2224 } 2225 2226 /** 2227 * video_to_sql 2228 * 2229 * Handles the generation of the SQL for video searches. 2230 * @return array 2231 */ 2232 private function video_to_sql() 2233 { 2234 $sql_logic_operator = $this->logic_operator; 2235 $user_id = $this->search_user->id; 2236 $catalog_disable = AmpConfig::get('catalog_disable'); 2237 $catalog_filter = AmpConfig::get('catalog_filter'); 2238 2239 $where = array(); 2240 $table = array(); 2241 $join = array(); 2242 $group = array(); 2243 $having = array(); 2244 2245 foreach ($this->rules as $rule) { 2246 $type = $this->name_to_basetype($rule[0]); 2247 $operator = array(); 2248 if (!$type) { 2249 return array(); 2250 } 2251 foreach ($this->basetypes[$type] as $op) { 2252 if ($op['name'] == $rule[1]) { 2253 $operator = $op; 2254 break; 2255 } 2256 } 2257 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 2258 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 2259 $sql_match_operator = $operator['sql']; 2260 2261 switch ($rule[0]) { 2262 case 'file': 2263 $where[] = "`video`.`file` $sql_match_operator '$input'"; 2264 break; 2265 default: 2266 break; 2267 } // switch on ruletype 2268 } // foreach rule 2269 2270 $join['catalog_map'] = $catalog_filter; 2271 $join['catalog'] = $catalog_disable || $catalog_filter; 2272 2273 $where_sql = implode(" $sql_logic_operator ", $where); 2274 2275 if ($join['catalog']) { 2276 $table['1_catalog'] = "LEFT JOIN `catalog` AS `catalog_se` ON `catalog_se`.`id`=`video`.`catalog`"; 2277 if (!empty($where_sql)) { 2278 $where_sql .= " AND `catalog_se`.`enabled` = '1' AND `video`.`enabled` = 1"; 2279 } else { 2280 $where_sql .= " `catalog_se`.`enabled` = '1' AND `video`.`enabled` = 1"; 2281 } 2282 } 2283 if ($join['catalog_map']) { 2284 $table['2_catalog_map'] = "LEFT JOIN `catalog_map` AS `catalog_map_video` ON `catalog_map_video`.`object_id`=`video`.`id` AND `catalog_map_video`.`object_type` = 'video' AND `catalog_map_video`.`catalog_id` = `catalog_se`.`id`"; 2285 if (!empty($where_sql)) { 2286 $where_sql .= " AND `catalog_se`.`filter_user` IN (0, $user_id)"; 2287 } else { 2288 $where_sql .= " `catalog_se`.`filter_user` IN (0, $user_id)"; 2289 } 2290 } 2291 ksort($table); 2292 $table_sql = implode(' ', $table); 2293 $group_sql = implode(',', $group); 2294 $having_sql = implode(" $sql_logic_operator ", $having); 2295 2296 return array( 2297 'base' => 'SELECT DISTINCT(`video`.`id`), `video`.`file` FROM `video`', 2298 'join' => $join, 2299 'where' => $where, 2300 'where_sql' => $where_sql, 2301 'table' => $table, 2302 'table_sql' => $table_sql, 2303 'group_sql' => $group_sql, 2304 'having_sql' => $having_sql 2305 ); 2306 } 2307 2308 /** 2309 * playlist_to_sql 2310 * 2311 * Handles the generation of the SQL for playlist searches. 2312 * @return array 2313 */ 2314 private function playlist_to_sql() 2315 { 2316 $sql_logic_operator = $this->logic_operator; 2317 $user_id = $this->search_user->id; 2318 $catalog_disable = AmpConfig::get('catalog_disable'); 2319 $catalog_filter = AmpConfig::get('catalog_filter'); 2320 2321 $where = array(); 2322 $table = array(); 2323 $join = array(); 2324 $group = array(); 2325 $having = array(); 2326 2327 foreach ($this->rules as $rule) { 2328 $type = $this->name_to_basetype($rule[0]); 2329 $operator = array(); 2330 if (!$type) { 2331 return array(); 2332 } 2333 foreach ($this->basetypes[$type] as $op) { 2334 if ($op['name'] == $rule[1]) { 2335 $operator = $op; 2336 break; 2337 } 2338 } 2339 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 2340 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 2341 $sql_match_operator = $operator['sql']; 2342 2343 $where[] = "(`playlist`.`type` = 'public' OR `playlist`.`user`=" . $user_id . ")"; 2344 2345 switch ($rule[0]) { 2346 case 'title': 2347 case 'name': 2348 $where[] = "`playlist`.`name` $sql_match_operator '$input'"; 2349 break; 2350 default: 2351 break; 2352 } // switch on ruletype 2353 } // foreach rule 2354 2355 $join['playlist_data'] = true; 2356 $join['song'] = $join['song'] || $catalog_disable || $catalog_filter; 2357 $join['catalog'] = $catalog_disable || $catalog_filter; 2358 $join['catalog_map'] = $catalog_filter; 2359 2360 $where_sql = implode(" $sql_logic_operator ", $where); 2361 2362 if ($join['playlist_data']) { 2363 $table['0_playlist_data'] = "LEFT JOIN `playlist_data` ON `playlist_data`.`playlist` = `playlist`.`id`"; 2364 } 2365 if ($join['song']) { 2366 $table['0_song'] = "LEFT JOIN `song` ON `song`.`id`=`playlist_data`.`object_id`"; 2367 $where_sql .= " AND `playlist_data`.`object_type` = 'song'"; 2368 } 2369 if ($join['catalog']) { 2370 $table['1_catalog'] = "LEFT JOIN `catalog` AS `catalog_se` ON `catalog_se`.`id`=`song`.`catalog`"; 2371 if ($catalog_disable) { 2372 if (!empty($where_sql)) { 2373 $where_sql .= " AND `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 2374 } else { 2375 $where_sql .= " `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 2376 } 2377 } 2378 } 2379 if ($join['catalog_map']) { 2380 if (!empty($where_sql)) { 2381 $where_sql .= " AND `catalog_se`.`filter_user` IN (0, $user_id)"; 2382 } else { 2383 $where_sql .= " `catalog_se`.`filter_user` IN (0, $user_id)"; 2384 } 2385 } 2386 ksort($table); 2387 $table_sql = implode(' ', $table); 2388 $group_sql = implode(',', $group); 2389 $having_sql = implode(" $sql_logic_operator ", $having); 2390 2391 return array( 2392 'base' => 'SELECT DISTINCT(`playlist`.`id`), `playlist`.`name` FROM `playlist`', 2393 'join' => $join, 2394 'where' => $where, 2395 'where_sql' => $where_sql, 2396 'table' => $table, 2397 'table_sql' => $table_sql, 2398 'group_sql' => $group_sql, 2399 'having_sql' => $having_sql 2400 ); 2401 } 2402 2403 /** 2404 * label_to_sql 2405 * 2406 * Handles the generation of the SQL for label searches. 2407 * @return array 2408 */ 2409 private function label_to_sql() 2410 { 2411 $sql_logic_operator = $this->logic_operator; 2412 $user_id = $this->search_user->id; 2413 $catalog_disable = AmpConfig::get('catalog_disable'); 2414 $catalog_filter = AmpConfig::get('catalog_filter'); 2415 2416 $where = array(); 2417 $table = array(); 2418 $join = array(); 2419 2420 foreach ($this->rules as $rule) { 2421 $type = $this->name_to_basetype($rule[0]); 2422 $operator = array(); 2423 if (!$type) { 2424 return array(); 2425 } 2426 foreach ($this->basetypes[$type] as $op) { 2427 if ($op['name'] == $rule[1]) { 2428 $operator = $op; 2429 break; 2430 } 2431 } 2432 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 2433 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 2434 $sql_match_operator = $operator['sql']; 2435 2436 switch ($rule[0]) { 2437 case 'title': 2438 case 'name': 2439 $where[] = "`label`.`name` $sql_match_operator '$input'"; 2440 break; 2441 case 'category': 2442 $where[] = "`label`.`category` $sql_match_operator '$input'"; 2443 break; 2444 default: 2445 break; 2446 } // switch on ruletype 2447 } // foreach rule 2448 2449 $join['catalog_map'] = $catalog_filter; 2450 $join['catalog'] = $catalog_disable || $catalog_filter; 2451 2452 $where_sql = implode(" $sql_logic_operator ", $where); 2453 2454 if ($catalog_disable || $catalog_filter) { 2455 $table['0_label_asso'] = "LEFT JOIN `label_asso` ON `label_asso`.`label` = `label`.`id`"; 2456 $table['1_artist'] = "LEFT JOIN `artist` ON `label_asso`.`artist` = `artist`.`id`"; 2457 $table['2_catalog_map'] = "LEFT JOIN `catalog_map` AS `catalog_map_artist` ON `catalog_map_artist`.`object_id`=`artist`.`id` AND `catalog_map_artist`.`object_type` = 'artist'"; 2458 } 2459 2460 if ($join['catalog_map']) { 2461 if (!empty($where_sql)) { 2462 $where_sql .= " AND `catalog_map_artist`.`object_type` = 'artist' AND `catalog_se`.`filter_user` IN (0, $user_id)"; 2463 } else { 2464 $where_sql .= " `catalog_map_artist`.`object_type` = 'artist' AND `catalog_se`.`filter_user` IN (0, $user_id)"; 2465 } 2466 } 2467 if ($join['catalog']) { 2468 $table['3_catalog'] = "LEFT JOIN `catalog`AS `catalog_se` ON `catalog_map_artist`.`catalog_id` = `catalog_se`.`id`"; 2469 if ($catalog_disable) { 2470 if (!empty($where_sql)) { 2471 $where_sql .= " AND `catalog_se`.`enabled` = '1'"; 2472 } else { 2473 $where_sql .= " `catalog_se`.`enabled` = '1'"; 2474 } 2475 } 2476 } 2477 $table_sql = implode(' ', $table); 2478 2479 return array( 2480 'base' => 'SELECT DISTINCT(`label`.`id`), `label`.`name` FROM `label`', 2481 'join' => $join, 2482 'where' => $where, 2483 'where_sql' => $where_sql, 2484 'table' => $table, 2485 'table_sql' => $table_sql, 2486 'group_sql' => '', 2487 'having_sql' => '' 2488 ); 2489 } 2490 2491 /** 2492 * tag_to_sql 2493 * 2494 * Handles the generation of the SQL for tag (genre) searches. 2495 * @return array 2496 */ 2497 2498 private function tag_to_sql() 2499 { 2500 $sql_logic_operator = $this->logic_operator; 2501 $user_id = $this->search_user->id; 2502 $catalog_disable = AmpConfig::get('catalog_disable'); 2503 $catalog_filter = AmpConfig::get('catalog_filter'); 2504 2505 $where = array(); 2506 $table = array(); 2507 $join = array(); 2508 2509 foreach ($this->rules as $rule) { 2510 $type = $this->name_to_basetype($rule[0]); 2511 $operator = array(); 2512 if (!$type) { 2513 return array(); 2514 } 2515 foreach ($this->basetypes[$type] as $op) { 2516 if ($op['name'] == $rule[1]) { 2517 $operator = $op; 2518 break; 2519 } 2520 } 2521 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 2522 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 2523 $sql_match_operator = $operator['sql']; 2524 2525 switch ($rule[0]) { 2526 case 'title': 2527 case 'name': 2528 $where[] = "`tag`.`name` $sql_match_operator '$input'"; 2529 break; 2530 case 'category': 2531 $where[] = "`tag`.`category` $sql_match_operator '$input'"; 2532 break; 2533 default: 2534 break; 2535 } // switch on ruletype 2536 } // foreach rule 2537 2538 $join['catalog_map'] = $catalog_filter; 2539 $join['catalog'] = $catalog_disable || $catalog_filter; 2540 2541 $where_sql = implode(" $sql_logic_operator ", $where); 2542 2543 if ($join['catalog']) { 2544 $table['1_catalog'] = "LEFT JOIN `catalog` AS `catalog_se` ON `catalog_se`.`id`=`song`.`catalog`"; 2545 if (!empty($where_sql)) { 2546 $where_sql .= " AND `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 2547 } else { 2548 $where_sql .= " `catalog_se`.`enabled` = '1' AND `song`.`enabled` = 1"; 2549 } 2550 } 2551 if ($join['catalog_map']) { 2552 $table['2_catalog_map'] = "LEFT JOIN `catalog_map` AS `catalog_map_album` ON `catalog_map_album`.`object_id`=`album`.`id` AND `catalog_map_album`.`object_type` = 'album' AND `catalog_map_album`.`catalog_id` = `catalog_se`.`id`"; 2553 if (!empty($where_sql)) { 2554 $where_sql .= " AND `catalog_se`.`filter_user` IN (0, $user_id)"; 2555 } else { 2556 $where_sql .= " `catalog_se`.`filter_user` IN (0, $user_id)"; 2557 } 2558 } 2559 2560 return array( 2561 'base' => 'SELECT DISTINCT(`tag`.`id`) FROM `tag`', 2562 'join' => $join, 2563 'where' => $where, 2564 'where_sql' => $where_sql, 2565 'table' => $table, 2566 'table_sql' => '', 2567 'group_sql' => '', 2568 'having_sql' => '' 2569 ); 2570 } 2571 2572 /** 2573 * user_to_sql 2574 * 2575 * Handles the generation of the SQL for user searches. 2576 * @return array 2577 */ 2578 private function user_to_sql() 2579 { 2580 $sql_logic_operator = $this->logic_operator; 2581 2582 $where = array(); 2583 $table = array(); 2584 $join = array(); 2585 2586 foreach ($this->rules as $rule) { 2587 $type = $this->name_to_basetype($rule[0]); 2588 $operator = array(); 2589 if (!$type) { 2590 return array(); 2591 } 2592 foreach ($this->basetypes[$type] as $op) { 2593 if ($op['name'] == $rule[1]) { 2594 $operator = $op; 2595 break; 2596 } 2597 } 2598 $raw_input = $this->_mangle_data($rule[2], $type, $operator); 2599 $input = filter_var($raw_input, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); 2600 $sql_match_operator = $operator['sql']; 2601 2602 switch ($rule[0]) { 2603 case 'username': 2604 $where[] = "`user`.`username` $sql_match_operator '$input'"; 2605 break; 2606 default: 2607 break; 2608 } // switch on ruletype 2609 } // foreach rule 2610 2611 $where_sql = implode(" $sql_logic_operator ", $where); 2612 ksort($table); 2613 2614 return array( 2615 'base' => 'SELECT DISTINCT(`user`.`id`), `user`.`username` FROM `user`', 2616 'join' => $join, 2617 'where' => $where, 2618 'where_sql' => $where_sql, 2619 'table' => $table, 2620 'table_sql' => '', 2621 'group_sql' => '', 2622 'having_sql' => '' 2623 ); 2624 } 2625 2626 /** 2627 * year_search 2628 * 2629 * Build search rules for year -> year searching. 2630 * @param $fromYear 2631 * @param $toYear 2632 * @param $size 2633 * @param $offset 2634 * @return array 2635 */ 2636 public static function year_search($fromYear, $toYear, $size, $offset) 2637 { 2638 $search = array(); 2639 $search['limit'] = $size; 2640 $search['offset'] = $offset; 2641 $search['type'] = "album"; 2642 $count = 0; 2643 if ($fromYear) { 2644 $search['rule_' . $count . '_input'] = $fromYear; 2645 $search['rule_' . $count . '_operator'] = 0; 2646 $search['rule_' . $count . ''] = "original_year"; 2647 ++$count; 2648 } 2649 if ($toYear) { 2650 $search['rule_' . $count . '_input'] = $toYear; 2651 $search['rule_' . $count . '_operator'] = 1; 2652 $search['rule_' . $count . ''] = "original_year"; 2653 ++$count; 2654 } 2655 2656 return $search; 2657 } 2658 2659 /** 2660 * @deprecated 2661 */ 2662 private function getLicenseRepository(): LicenseRepositoryInterface 2663 { 2664 global $dic; 2665 2666 return $dic->get(LicenseRepositoryInterface::class); 2667 } 2668 2669 /** 2670 * @deprecated inject dependency 2671 */ 2672 private function getUserRepository(): UserRepositoryInterface 2673 { 2674 global $dic; 2675 2676 return $dic->get(UserRepositoryInterface::class); 2677 } 2678} 2679