Skip to content

Commit

Permalink
Reimplement playlist queue to be attached to StationPlaylistMedia tab…
Browse files Browse the repository at this point in the history
…le. (#4031)
  • Loading branch information
BusterNeece committed Apr 17, 2021
1 parent 35cfd67 commit 4e6f1d7
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 281 deletions.
6 changes: 2 additions & 4 deletions src/Controller/Api/Stations/Playlists/DeleteQueueAction.php
Expand Up @@ -12,14 +12,12 @@ class DeleteQueueAction extends AbstractPlaylistsAction
public function __invoke(
ServerRequest $request,
Response $response,
Entity\Repository\StationPlaylistMediaRepository $spmRepo,
$id
): ResponseInterface {
$record = $this->requireRecord($request->getStation(), $id);

$record->setQueue(null);
$this->em->persist($record);

$this->em->flush();
$spmRepo->resetQueue($record);

return $response->withJson(
new Entity\Api\Status(
Expand Down
3 changes: 2 additions & 1 deletion src/Controller/Api/Stations/Playlists/GetQueueAction.php
Expand Up @@ -13,6 +13,7 @@ class GetQueueAction extends AbstractPlaylistsAction
public function __invoke(
ServerRequest $request,
Response $response,
Entity\Repository\StationPlaylistMediaRepository $spmRepo,
$id
): ResponseInterface {
$record = $this->requireRecord($request->getStation(), $id);
Expand All @@ -25,7 +26,7 @@ public function __invoke(
throw new \InvalidArgumentException('This playlist is always shuffled and has no visible queue.');
}

$queue = (array)$record->getQueue();
$queue = $spmRepo->getQueue($record);
$paginator = Paginator::fromArray($queue, $request);

return $paginator->write($response);
Expand Down
12 changes: 7 additions & 5 deletions src/Controller/Api/Stations/Playlists/ReshuffleAction.php
Expand Up @@ -9,13 +9,15 @@

class ReshuffleAction extends AbstractPlaylistsAction
{
public function __invoke(ServerRequest $request, Response $response, $id): ResponseInterface
{
public function __invoke(
ServerRequest $request,
Response $response,
Entity\Repository\StationPlaylistMediaRepository $spmRepo,
$id
): ResponseInterface {
$record = $this->requireRecord($request->getStation(), $id);

$record->setQueue(null);
$this->em->persist($record);
$this->em->flush();
$spmRepo->resetQueue($record);

return $response->withJson(
new Entity\Api\Status(
Expand Down
46 changes: 46 additions & 0 deletions src/Entity/Api/StationPlaylistQueue.php
@@ -0,0 +1,46 @@
<?php

namespace App\Entity\Api;

class StationPlaylistQueue
{
/**
* ID of the StationPlaylistMedia record associating this track with the playlist
*
* @OA\Property(example=1)
* @var int|null
*/
public ?int $spm_id = null;

/**
* ID of the StationPlaylistMedia record associating this track with the playlist
*
* @OA\Property(example=1)
* @var int
*/
public int $media_id;

/**
* The song's 32-character unique identifier hash
*
* @OA\Property(example="9f33bbc912c19603e51be8e0987d076b")
* @var string
*/
public string $song_id;

/**
* The song artist.
*
* @OA\Property(example="Chet Porter")
* @var string
*/
public string $artist = '';

/**
* The song title.
*
* @OA\Property(example="Aluko River")
* @var string
*/
public string $title = '';
}
33 changes: 33 additions & 0 deletions src/Entity/Migration/Version20210416214621.php
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace App\Entity\Migration;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210416214621 extends AbstractMigration
{
public function getDescription(): string
{
return 'Move playlist queue to station_playlist_media table.';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE station_playlist_media ADD is_queued TINYINT(1) NOT NULL');
$this->addSql('ALTER TABLE station_playlists DROP queue');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE station_playlist_media DROP is_queued');
$this->addSql('ALTER TABLE station_playlists ADD queue LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci` COMMENT \'(DC2Type:array)\'');
}
}
126 changes: 92 additions & 34 deletions src/Entity/Repository/StationPlaylistMediaRepository.php
Expand Up @@ -72,10 +72,6 @@ public function addMediaToPlaylist(
$record = new Entity\StationPlaylistMedia($playlist, $media);
$record->setWeight($weight);
$this->em->persist($record);

// Add the newly added song into the cached queue.
$playlist->addToQueue($media);
$this->em->persist($playlist);
}

return $weight;
Expand Down Expand Up @@ -124,10 +120,6 @@ function (Entity\StationPlaylistMedia $spm) use ($station) {

foreach ($playlists as $spmRow) {
$playlist = $spmRow->getPlaylist();

$playlist->removeFromQueue($media);
$this->em->persist($playlist);

$affectedPlaylists[$playlist->getId()] = $playlist;

$this->queueRepo->clearForMediaAndPlaylist($media, $playlist);
Expand Down Expand Up @@ -159,43 +151,109 @@ public function setMediaOrder(Entity\StationPlaylist $playlist, $mapping): void
DQL
)->setParameter('playlist_id', $playlist->getId());

foreach ($mapping as $id => $weight) {
$update_query->setParameter('id', $id)
->setParameter('weight', $weight)
$this->em->transactional(
function () use ($update_query, $mapping): void {
foreach ($mapping as $id => $weight) {
$update_query->setParameter('id', $id)
->setParameter('weight', $weight)
->execute();
}
}
);
}

/**
* @return Entity\Api\StationPlaylistQueue[]
*/
public function resetQueue(StationPlaylist $playlist): array
{
if ($playlist::SOURCE_SONGS !== $playlist->getSource()) {
throw new \InvalidArgumentException('Playlist must contain songs.');
}

if ($playlist::ORDER_SEQUENTIAL === $playlist->getOrder()) {
$this->em->createQuery(
<<<'DQL'
UPDATE App\Entity\StationPlaylistMedia spm
SET spm.is_queued = 1
WHERE spm.playlist = :playlist
DQL
)->setParameter('playlist', $playlist)
->execute();
} elseif ($playlist::ORDER_SHUFFLE === $playlist->getOrder()) {
$this->em->transactional(
function () use ($playlist): void {
$allSpmRecordsQuery = $this->em->createQuery(
<<<'DQL'
SELECT spm.id
FROM App\Entity\StationPlaylistMedia spm
WHERE spm.playlist = :playlist
ORDER BY RAND()
DQL
)->setParameter('playlist', $playlist);

$updateSpmWeightQuery = $this->em->createQuery(
<<<'DQL'
UPDATE App\Entity\StationPlaylistMedia spm
SET spm.weight=:weight, spm.is_queued=1
WHERE spm.id = :id
DQL
);

$allSpmRecords = $allSpmRecordsQuery->toIterable([], $allSpmRecordsQuery::HYDRATE_SCALAR);
$weight = 1;

foreach ($allSpmRecords as $spmId) {
$updateSpmWeightQuery->setParameter('id', $spmId)
->setParameter('weight', $weight)
->execute();

$weight++;
}
}
);
}

// Clear the playback queue.
$playlist->setQueue($this->getPlayableMedia($playlist));
$this->em->persist($playlist);
$this->em->flush();
return $this->getQueue($playlist);
}

/**
* @return mixed[]
* @return Entity\Api\StationPlaylistQueue[]
*/
public function getPlayableMedia(Entity\StationPlaylist $playlist): array
public function getQueue(StationPlaylist $playlist): array
{
$all_media = $this->em->createQuery(
<<<'DQL'
SELECT sm.id, sm.song_id, sm.artist, sm.title
FROM App\Entity\StationMedia sm
JOIN sm.playlists spm
WHERE spm.playlist_id = :playlist_id
ORDER BY spm.weight ASC
DQL
)->setParameter('playlist_id', $playlist->getId())
->getArrayResult();

if ($playlist->getOrder() !== Entity\StationPlaylist::ORDER_SEQUENTIAL) {
shuffle($all_media);
if ($playlist::SOURCE_SONGS !== $playlist->getSource()) {
throw new \InvalidArgumentException('Playlist must contain songs.');
}

$media_queue = [];
foreach ($all_media as $media_row) {
$media_queue[$media_row['id']] = $media_row;
$queuedMediaQuery = $this->em->createQueryBuilder()
->select(['spm.id AS spm_id', 'sm.id', 'sm.song_id', 'sm.artist', 'sm.title'])
->from(Entity\StationMedia::class, 'sm')
->join('sm.playlists', 'spm')
->where('spm.playlist = :playlist')
->setParameter('playlist', $playlist);

if ($playlist::ORDER_RANDOM === $playlist->getOrder()) {
$queuedMediaQuery = $queuedMediaQuery->orderBy('RAND()');
} else {
$queuedMediaQuery = $queuedMediaQuery->andWhere('spm.is_queued = 1')
->orderBy('spm.weight', 'ASC');
}

return $media_queue;
$queuedMedia = $queuedMediaQuery->getQuery()->getArrayResult();

return array_map(
function ($val): Entity\Api\StationPlaylistQueue {
$record = new Entity\Api\StationPlaylistQueue();
$record->spm_id = $val['spm_id'];
$record->media_id = $val['id'];
$record->song_id = $val['song_id'];
$record->artist = $val['artist'] ?? '';
$record->title = $val['title'] ?? '';

return $record;
},
$queuedMedia
);
}
}
14 changes: 6 additions & 8 deletions src/Entity/Repository/StationRequestRepository.php
Expand Up @@ -208,15 +208,13 @@ public function checkRecentPlay(Entity\StationMedia $media, Entity\Station $stat
->setParameter('threshold', $lastPlayThreshold)
->getArrayResult();

$eligibleTrack = new Entity\Api\StationPlaylistQueue();
$eligibleTrack->media_id = $media->getId();
$eligibleTrack->song_id = $media->getSongId();
$eligibleTrack->title = $media->getTitle() ?? '';
$eligibleTrack->artist = $media->getArtist() ?? '';

$eligibleTracks = [
$media->getId() => [
'title' => $media->getTitle(),
'artist' => $media->getArtist(),
],
];

$isDuplicate = (null === AutoDJ\Queue::getDistinctTrack($eligibleTracks, $recentTracks));
$isDuplicate = (null === AutoDJ\Queue::getDistinctTrack([$eligibleTrack], $recentTracks));

if ($isDuplicate) {
throw new Exception(
Expand Down
16 changes: 0 additions & 16 deletions src/Entity/StationMedia.php
Expand Up @@ -460,22 +460,6 @@ public function setArtUpdatedAt(int $art_updated_at): void
$this->art_updated_at = $art_updated_at;
}

public function getItemForPlaylist(StationPlaylist $playlist): ?StationPlaylistMedia
{
$item = $this->playlists->filter(
function ($spm) use ($playlist) {
/** @var StationPlaylistMedia $spm */
return $spm->getPlaylist()->getId() === $playlist->getId();
}
);

$firstItem = $item->first();

return ($firstItem instanceof StationPlaylistMedia)
? $firstItem
: null;
}

public function getCustomFields(): Collection
{
return $this->custom_fields;
Expand Down

0 comments on commit 4e6f1d7

Please sign in to comment.