Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion css/topnav.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions css/zoostyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,9 @@ a:hover {
.playlistHdr {
border-bottom: 1px solid gray;
}
.playlistHdr th {
text-align: left;
}
.playlistHdr td {
min-width: 30px;
}
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions engine/IPlaylist.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason <jmason@ibinx.com>
* @copyright Copyright (C) 1997-2023 Jim Mason <jmason@ibinx.com>
* @copyright Copyright (C) 1997-2024 Jim Mason <jmason@ibinx.com>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -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);
Expand Down
56 changes: 36 additions & 20 deletions engine/impl/Playlist.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason <jmason@ibinx.com>
* @copyright Copyright (C) 1997-2023 Jim Mason <jmason@ibinx.com>
* @copyright Copyright (C) 1997-2024 Jim Mason <jmason@ibinx.com>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
23 changes: 21 additions & 2 deletions ui/Playlists.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason <jmason@ibinx.com>
* @copyright Copyright (C) 1997-2023 Jim Mason <jmason@ibinx.com>
* @copyright Copyright (C) 1997-2024 Jim Mason <jmason@ibinx.com>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -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" ],
];

Expand Down Expand Up @@ -266,7 +267,7 @@ private function getDJAirNames() {
// make header for edit & view playlist
private function makePlaylistHeader($isEditMode) {
$editCol = $isEditMode ? "<TD />" : "";
$header = "<TR class='playlistHdr' ALIGN=LEFT>" . $editCol . "<TH WIDTH='64px'>Time</TH><TH WIDTH='25%'>" .
$header = "<TR class='playlistHdr'>" . $editCol . "<TH WIDTH='64px'>Time</TH><TH WIDTH='25%'>" .
"Artist</TH><TH WIDTH='25%'>Track</TH><TH></TH><TH>Album/Label</TH></TR>";
return $header;
}
Expand Down Expand Up @@ -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";
Expand Down
58 changes: 58 additions & 0 deletions ui/templates/default/airplay.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% set odays = [7, 14, 30] %}
<div class='top-airplay' style='display: none'>
<form action="?" method="POST" autocomplete="off">
<input type="hidden" name="action" value="{{ app.request.action }}">
<input type="hidden" name="subaction" value="{{ app.request.subaction }}">
<input type="hidden" name="limit" value="{{ limit }}">
<h2>Top airplay for the last
<select name="days">
{% for oday in odays %}
<option value="{{ oday }}" {{~ oday == days ? " selected" }}>{{ oday }}</option>
{% endfor %}
</select>
days
</h2>
</form>
{% if topPlays | length %}
<table class='playlistTable'>
<thead>
<tr class='playlistHdr'><th></th><th>Artist</th><th></th><th>Album/Label</th></tr>
</thead>
<tbody>
{%~ for index, entry in topPlays %}
<tr class='songRow'>
<td>{{ index + 1 }}.</td>
<td>{{ entry.iscoll ? "Various Artists" : entry.artist }}</td>
<td style='width: 15px'>{{ entry.reviewed ? "<div class='albumReview'></div>" }}</td>
<td>
{%~ if entry.album | length %}
{%~ if entry.tag %}
<a class='nav' href='?action=search&amp;s=byAlbumKey&amp;n={{ entry.tag }}'>{{ entry.album }}</a>
{%~ else %}
{{ entry.album }}
{%~ endif %}
{%~ if entry.label | length %}
<span class='songLabel'> / {{ entry.label }}</span>
{%~ endif %}
{%~ endif %}
</td>
</tr>
{%~ endfor %}
</tbody>
</table>
<h3>For complete album charting, see our <a href='?action=viewChart'><b>Airplay Charts</b></a></h3>
{% else %}
<h3>No top airplay is available</h3>
{% endif %}
</div>
<script><!--
$().ready(function() {
$("div.top-airplay select").selectmenu({width: 'auto'})
.on('change selectmenuchange', function() {
this.form.submit();
})
.closest('div').css('display', 'block')
.find('select').selectmenu('widget').trigger('focus');
});
// -->
</script>