#6003 -- Update "play once per x songs" scheduling.
This commit is contained in:
parent
e772001fb0
commit
b16f2a94ce
|
@ -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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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).
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue