diff --git a/css/topnav.css b/css/topnav.css index 4c5975a44..ddeb1058f 100644 --- a/css/topnav.css +++ b/css/topnav.css @@ -196,7 +196,7 @@ nav .search-icon { position: absolute; display: block; left: 0; - margin-right: -150px;/* TBD fix me */ + margin-right: -200px;/* TBD fix me */ } .nav-items:not(.active) li.selected ul li { float: left; diff --git a/css/zoostyle.css b/css/zoostyle.css index c24d827b3..a9865b681 100644 --- a/css/zoostyle.css +++ b/css/zoostyle.css @@ -718,6 +718,9 @@ a:hover { .playlistHdr { border-bottom: 1px solid gray; } +.playlistHdr th { + text-align: left; +} .playlistHdr td { min-width: 30px; } @@ -858,6 +861,9 @@ li.ui-menu-item .ui-state-active, font-size: 14px; padding: 6px; } + h2 .ui-selectmenu-button.ui-button { + font-size: inherit; + } li.ui-menu-item { font-size: 16px; diff --git a/engine/IPlaylist.php b/engine/IPlaylist.php index adfeef3bd..d8ba72f4e 100644 --- a/engine/IPlaylist.php +++ b/engine/IPlaylist.php @@ -3,7 +3,7 @@ * Zookeeper Online * * @author Jim Mason - * @copyright Copyright (C) 1997-2023 Jim Mason + * @copyright Copyright (C) 1997-2024 Jim Mason * @link https://zookeeper.ibinx.com/ * @license GPL-3.0 * @@ -98,7 +98,7 @@ function updateTrack($playlistId, $id, $tag, $artist, $track, $album, $label, $d function insertTrackEntry($playlistId, PlaylistEntry $entry, &$status); function updateTrackEntry($playlist, PlaylistEntry $entry); function deleteTrack($id); - function getTopPlays($airname=0, $days=41, $count=10); + function getTopPlays($airname=0, $days=41, $count=10, $excludeAutomation=true, $excludeRebroadcasts=true); function getLastPlays($tag, $count=0, $excludeAutomation=true, $excludeRebroadcasts=true); function getRecentPlays($airname, $count); function getPlaysBefore($timestamp, $limit); diff --git a/engine/impl/Playlist.php b/engine/impl/Playlist.php index ea01ecb61..6d445744b 100644 --- a/engine/impl/Playlist.php +++ b/engine/impl/Playlist.php @@ -3,7 +3,7 @@ * Zookeeper Online * * @author Jim Mason - * @copyright Copyright (C) 1997-2023 Jim Mason + * @copyright Copyright (C) 1997-2024 Jim Mason * @link https://zookeeper.ibinx.com/ * @license GPL-3.0 * @@ -836,20 +836,36 @@ public function deleteTrack($id) { return $success; } - public function getTopPlays($airname=0, $days=41, $count=10) { + public function getTopPlays($airname=0, $days=41, $count=10, $excludeAutomation=true, $excludeRebroadcasts=true) { + // if constraining by airname, no need to exclude automation + $excludeAutomation &= !$airname; + $zootopia = $excludeAutomation ? $this->getZootopiaAirname() : null; + if($zootopia) { + if(!is_array($zootopia)) + $zootopia = [ $zootopia ]; + + $zootopiaSet = str_repeat("?,", count($zootopia) - 1) . "?"; + } + $over = $airname?"distinct t.list":"*"; - $query = "SELECT t.tag, count($over) plays, l.showdate, IFNULL(a.artist, t.artist) artist, t.album, t.label, count(*)" . - " FROM tracks t JOIN lists l ON t.list = l.id " . - " LEFT JOIN albumvol a ON a.tag = t.tag " . - " WHERE t.artist NOT LIKE '".IPlaylist::SPECIAL_TRACK."%' AND". - " t.album <> '' AND t.label <> '' AND"; + $query = "SELECT max(t.tag) tag, count($over) plays, l.showdate, IFNULL(a.artist, t.artist) artist, t.album, t.label, count(*), a.iscoll " . + "FROM tracks t JOIN lists l ON t.list = l.id " . + ($zootopia ? "LEFT JOIN airnames n ON l.airname = n.id " : "") . + "LEFT JOIN albumvol a ON a.tag = t.tag " . + "WHERE t.artist NOT LIKE '".IPlaylist::SPECIAL_TRACK."%' ". + "AND t.album <> '' AND t.label <> '' " . + ($excludeRebroadcasts ? "AND origin IS NULL " : "") . + ($zootopia ? "AND n.airname NOT IN ($zootopiaSet) " : "") ; if($airname) - $query .= " l.airname = ? AND"; + $query .= "AND l.airname = ? "; if($days) - $query .= " date_add(l.showdate, interval $days day) > now() "; - $query .= " GROUP BY t.album, t.label ORDER BY 2 DESC, 7 DESC, t.artist LIMIT ?"; + $query .= "AND date_add(l.showdate, interval $days day) > now() "; + $query .= "GROUP BY t.album, t.label ORDER BY 2 DESC, 7 DESC, t.artist LIMIT ?"; $stmt = $this->prepare($query); $p = 1; + if($zootopia) + foreach($zootopia as $zka) + $stmt->bindValue($p++, $zka); if($airname) $stmt->bindValue($p++, (int)$airname, \PDO::PARAM_INT); $stmt->bindValue($p++, (int)$count, \PDO::PARAM_INT); @@ -878,16 +894,16 @@ public function getLastPlays($tag, $count=0, $excludeAutomation=true, $excludeRe $zootopiaSet = str_repeat("?,", count($zootopia) - 1) . "?"; } - $query = "SELECT l.id, l.showdate, l.description, a.airname," . - " count(*) plays," . - " group_concat(t.track ORDER BY t.seq DESC, t.id DESC SEPARATOR 0x1e) tracks" . - " FROM tracks t" . - " JOIN lists l ON t.list = l.id " . - " LEFT JOIN airnames a ON l.airname = a.id" . - " WHERE t.tag = ? AND l.airname IS NOT NULL" . - ($excludeRebroadcasts ? " AND origin IS NULL" : "") . - ($zootopia ? " AND a.airname NOT IN ($zootopiaSet)" : "") . - " GROUP BY t.tag, l.id ORDER BY l.showdate DESC, l.showtime DESC"; + $query = "SELECT l.id, l.showdate, l.description, a.airname, " . + "count(*) plays, " . + "group_concat(t.track ORDER BY t.seq DESC, t.id DESC SEPARATOR 0x1e) tracks " . + "FROM tracks t " . + "JOIN lists l ON t.list = l.id " . + "LEFT JOIN airnames a ON l.airname = a.id " . + "WHERE t.tag = ? AND l.airname IS NOT NULL " . + ($excludeRebroadcasts ? "AND origin IS NULL " : "") . + ($zootopia ? "AND a.airname NOT IN ($zootopiaSet) " : "") . + "GROUP BY t.tag, l.id ORDER BY l.showdate DESC, l.showtime DESC"; if($count) $query .= " LIMIT ?"; $stmt = $this->prepare($query); diff --git a/ui/Playlists.php b/ui/Playlists.php index 1632fdf7b..3409b2529 100644 --- a/ui/Playlists.php +++ b/ui/Playlists.php @@ -3,7 +3,7 @@ * Zookeeper Online * * @author Jim Mason - * @copyright Copyright (C) 1997-2023 Jim Mason + * @copyright Copyright (C) 1997-2024 Jim Mason * @link https://zookeeper.ibinx.com/ * @license GPL-3.0 * @@ -55,6 +55,7 @@ class Playlists extends MenuItem { [ "u", "editListGetHint", 0, "listManagerGetHint" ], [ "u", "editListEditor", 0, "emitEditor" ], [ "a", "viewDJ", "By DJ", "emitViewDJ" ], + [ "a", "viewTop", "Top Plays", "emitTopPlays" ], [ "u", "import", "Import", "emitImportList" ], ]; @@ -266,7 +267,7 @@ private function getDJAirNames() { // make header for edit & view playlist private function makePlaylistHeader($isEditMode) { $editCol = $isEditMode ? "" : ""; - $header = "" . $editCol . "Time" . + $header = "" . $editCol . "Time" . "ArtistTrackAlbum/Label"; return $header; } @@ -1021,6 +1022,24 @@ public function emitViewDJ() { $this->emitViewDJMain(); } + + public function emitTopPlays() { + $days = min($_REQUEST['days'] ?? 7, 42); + $limit = min($_REQUEST['limit'] ?? 30, 100); + + $topPlays = Engine::api(IPlaylist::class)->getTopPlays(0, $days, $limit); + Engine::api(ILibrary::class)->markAlbumsReviewed($topPlays); + + foreach($topPlays as &$entry) { + if($entry['tag'] && !$entry['iscoll']) + $entry['artist'] = PlaylistEntry::swapNames($entry['artist']); + } + + $this->setTemplate('airplay.html'); + $this->addVar('days', $days); + $this->addVar('limit', $limit); + $this->addVar('topPlays', $topPlays); + } public function emitViewDJMain() { $viewAll = $this->subaction == "viewDJAll"; diff --git a/ui/templates/default/airplay.html b/ui/templates/default/airplay.html new file mode 100644 index 000000000..b1efb7289 --- /dev/null +++ b/ui/templates/default/airplay.html @@ -0,0 +1,58 @@ +{% set odays = [7, 14, 30] %} + +