#6003 -- Update "play once per x songs" scheduling.

This commit is contained in:
Buster Neece 2023-01-18 22:05:51 -06:00
parent e772001fb0
commit b16f2a94ce
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
3 changed files with 23 additions and 84 deletions

View File

@ -68,42 +68,28 @@ final class StationQueueRepository extends AbstractStationBasedRepository
->execute();
}
/**
* @return int[]
*/
public function getRecentPlaylists(
Entity\Station $station,
int $rows
): array {
/*
* Explanation for why this is done in two queries:
* MariaDB won't apply indices if you order by one field ASC, then another DESC.
* The combiend query would order by is_played ASC, then timestamp_played DESC.
* This forces the use of indices at the expense of slightly more records being handled.
*/
$baseQueryBuilder = $this->em->createQueryBuilder()
->select('sq.timestamp_played, sq.is_visible, sq.playlist_id')
->from(Entity\StationQueue::class, 'sq')
->where('sq.station = :station')
->setParameter('station', $station)
->orderBy('sq.timestamp_played', 'DESC');
public function isPlaylistRecentlyPlayed(
Entity\StationPlaylist $playlist,
?int $playPerSongs = null
): bool {
$playPerSongs ??= $playlist->getPlayPerSongs();
$unplayedRows = (clone $baseQueryBuilder)
->andWhere('sq.is_played = 0')
->getQuery()
->getArrayResult();
$recentPlayedQuery = $this->em->createQuery(
<<<'DQL'
SELECT sq.playlist_id
FROM App\Entity\StationQueue sq
WHERE sq.station = :station
AND sq.playlist_id IS NOT NULL
AND (sq.playlist = :playlist OR sq.is_visible = 1)
ORDER BY sq.id DESC
DQL
)->setParameters([
'station' => $playlist->getStation(),
'playlist' => $playlist,
])->setMaxResults($playPerSongs);
$playedRows = (clone $baseQueryBuilder)
->andWhere('sq.is_played = 1')
->getQuery()
->setMaxResults($rows)
->getArrayResult();
return array_slice(
array_merge($unplayedRows, $playedRows),
0,
$rows
);
$recentPlayedPlaylists = $recentPlayedQuery->getSingleColumnResult();
return in_array($playlist->getIdRequired(), (array)$recentPlayedPlaylists, true);
}
/**

View File

@ -56,17 +56,11 @@ final class QueueBuilder implements EventSubscriberInterface
$expectedPlayTime = $event->getExpectedPlayTime();
$activePlaylistsByType = [];
$oncePerXSongHistoryCount = 15;
foreach ($station->getPlaylists() as $playlist) {
/** @var Entity\StationPlaylist $playlist */
if ($playlist->isPlayable($event->isInterrupting())) {
$type = $playlist->getType();
if (Entity\Enums\PlaylistTypes::OncePerXSongs === $playlist->getTypeEnum()) {
$oncePerXSongHistoryCount = max($oncePerXSongHistoryCount, $playlist->getPlayPerSongs());
}
$subType = ($playlist->getScheduleItems()->count() > 0) ? 'scheduled' : 'unscheduled';
$activePlaylistsByType[$type . '_' . $subType][$playlist->getId()] = $playlist;
}
@ -77,11 +71,6 @@ final class QueueBuilder implements EventSubscriberInterface
return;
}
$recentPlaylistHistory = $this->queueRepo->getRecentPlaylists(
$station,
$oncePerXSongHistoryCount
);
$recentSongHistoryForDuplicatePrevention = $this->queueRepo->getRecentlyPlayedByTimeRange(
$station,
$expectedPlayTime,
@ -91,7 +80,6 @@ final class QueueBuilder implements EventSubscriberInterface
$this->logger->debug(
'AutoDJ recent song playback history',
[
'history_once_per_x_songs' => $recentPlaylistHistory,
'history_duplicate_prevention' => $recentSongHistoryForDuplicatePrevention,
]
);
@ -117,7 +105,7 @@ final class QueueBuilder implements EventSubscriberInterface
$logPlaylists = [];
foreach ($activePlaylistsByType[$currentPlaylistType] as $playlistId => $playlist) {
/** @var Entity\StationPlaylist $playlist */
if (!$this->scheduler->shouldPlaylistPlayNow($playlist, $expectedPlayTime, $recentPlaylistHistory)) {
if (!$this->scheduler->shouldPlaylistPlayNow($playlist, $expectedPlayTime)) {
continue;
}

View File

@ -27,8 +27,7 @@ final class Scheduler
public function shouldPlaylistPlayNow(
Entity\StationPlaylist $playlist,
CarbonInterface $now = null,
array $recentPlaylistHistory = []
CarbonInterface $now = null
): bool {
$this->logger->pushProcessor(
function (LogRecord $record) use ($playlist) {
@ -66,7 +65,7 @@ final class Scheduler
case Entity\Enums\PlaylistTypes::OncePerXSongs:
$playPerSongs = $playlist->getPlayPerSongs();
$shouldPlay = !$this->wasPlaylistPlayedRecently($playlist, $recentPlaylistHistory, $playPerSongs);
$shouldPlay = !$this->queueRepo->isPlaylistRecentlyPlayed($playlist, $playPerSongs);
$this->logger->debug(
sprintf(
@ -154,40 +153,6 @@ final class Scheduler
return ($playedAt > $threshold);
}
private function wasPlaylistPlayedRecently(
Entity\StationPlaylist $playlist,
array $recentPlaylistHistory = [],
int $length = 15
): bool {
if (empty($recentPlaylistHistory)) {
return false;
}
$playlistId = $playlist->getIdRequired();
// Only consider playlists that are this playlist or are non-jingles.
$relevantSongHistory = array_slice(
array_filter(
$recentPlaylistHistory,
static function ($row) use ($playlistId) {
return $playlistId === $row['playlist_id']
? true
: $row['is_visible'];
}
),
0,
$length
);
foreach ($relevantSongHistory as $sh_row) {
if ($playlistId === (int)$sh_row['playlist_id']) {
return true;
}
}
return false;
}
/**
* Get the duration of scheduled play time in seconds (used for remote URLs of indeterminate length).
*