#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(); ->execute();
} }
/** public function isPlaylistRecentlyPlayed(
* @return int[] Entity\StationPlaylist $playlist,
*/ ?int $playPerSongs = null
public function getRecentPlaylists( ): bool {
Entity\Station $station, $playPerSongs ??= $playlist->getPlayPerSongs();
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');
$unplayedRows = (clone $baseQueryBuilder) $recentPlayedQuery = $this->em->createQuery(
->andWhere('sq.is_played = 0') <<<'DQL'
->getQuery() SELECT sq.playlist_id
->getArrayResult(); 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) $recentPlayedPlaylists = $recentPlayedQuery->getSingleColumnResult();
->andWhere('sq.is_played = 1') return in_array($playlist->getIdRequired(), (array)$recentPlayedPlaylists, true);
->getQuery()
->setMaxResults($rows)
->getArrayResult();
return array_slice(
array_merge($unplayedRows, $playedRows),
0,
$rows
);
} }
/** /**

View File

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

View File

@ -27,8 +27,7 @@ final class Scheduler
public function shouldPlaylistPlayNow( public function shouldPlaylistPlayNow(
Entity\StationPlaylist $playlist, Entity\StationPlaylist $playlist,
CarbonInterface $now = null, CarbonInterface $now = null
array $recentPlaylistHistory = []
): bool { ): bool {
$this->logger->pushProcessor( $this->logger->pushProcessor(
function (LogRecord $record) use ($playlist) { function (LogRecord $record) use ($playlist) {
@ -66,7 +65,7 @@ final class Scheduler
case Entity\Enums\PlaylistTypes::OncePerXSongs: case Entity\Enums\PlaylistTypes::OncePerXSongs:
$playPerSongs = $playlist->getPlayPerSongs(); $playPerSongs = $playlist->getPlayPerSongs();
$shouldPlay = !$this->wasPlaylistPlayedRecently($playlist, $recentPlaylistHistory, $playPerSongs); $shouldPlay = !$this->queueRepo->isPlaylistRecentlyPlayed($playlist, $playPerSongs);
$this->logger->debug( $this->logger->debug(
sprintf( sprintf(
@ -154,40 +153,6 @@ final class Scheduler
return ($playedAt > $threshold); 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). * Get the duration of scheduled play time in seconds (used for remote URLs of indeterminate length).
* *