<?php
namespace App\Controller;
use App\Entity\Event;
use App\Entity\EventTag;
use App\Entity\History;
use App\Entity\Song;
use App\Entity\SongStation;
use App\Entity\Station;
use App\Form\StationType;
use App\Lib\AzuraCast;
use App\Repository\HistoryRepository;
use App\Repository\SongRepository;
use AzuraCast\Api\Dto\MediaFileDto;
use AzuraCast\Api\Dto\NowPlayingDto;
use AzuraCast\Api\Dto\SongHistoryDto;
use AzuraCast\Api\Dto\UploadFileDto;
use AzuraCast\Api\Exception\AccessDeniedException;
use AzuraCast\Api\Exception\ClientRequestException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Exception;
use Psr\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use function GuzzleHttp\Promise\all;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
/**
* @Route("/station")
*/
class StationController extends AbstractController
{
/**
* @var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $nextSongLogger)
{
$this->logger = $nextSongLogger;
}
/**
* @Route("/action", name="station_action")
* @param Request $request
* @param EntityManagerInterface $em
* @param AzuraCast $azuraCast
* @return JsonResponse
*/
public function action(Request $request, EntityManagerInterface $em, AzuraCast $azuraCast, KernelInterface $kernel)
{
$stationRepository = $em->getRepository(Station::class);
$stationId = (int) $request->get('stationId', null);
/** @var Station $station */
$station = $stationRepository->findOneBy(['remoteId' => $stationId]);
if (NULL === $station) {
return $this->json(['success' => false, 'message' => 'Can\'t find station ' . $stationId], Response::HTTP_NOT_FOUND);
}
$this->initAllStations($kernel);
try {
$stationDto = $azuraCast->station($station->getId());
$action = $request->get('action', 'restart');
if ($action === 'restart') {
$stationDto->restart();
} else {
$stationDto->frontend($action);
$stationDto->backend($action);
}
} catch (AccessDeniedException $e) {
return $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_UNAUTHORIZED);
} catch (ClientRequestException $e) {
return $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
}
return $this->json(['success' => true, 'stationId' => $stationId]);
}
/**
* @Route("/restart-all", name="station_restartall")
* @param Request $request
* @param EntityManagerInterface $em
* @param AzuraCast $azuraCast
* @return JsonResponse
*/
public function restartAll(Request $request, EntityManagerInterface $entityManager, AzuraCast $azuraCast)
{
$stationRepository = $entityManager->getRepository(Station::class);
$stations = $stationRepository->findAll();
$force = $request->get('force', 'no') === "yes";
/** @var Station $station */
foreach ($stations as $station) {
$stationDto = $azuraCast->station($station->getId());
$nowPlayingData = $stationDto->nowPlaying();
if (!$force && $stationDto->status()->getFrontendRunning() && $stationDto->status()->getBackendRunning()) {
$songName = $nowPlayingData->getCurrentSong()->getSong()->getText();
if (strstr($songName, '.mp3')) {
dump(sprintf('Station %s is currently playing %s', $station->getName(), $songName));
continue;
}
}
dump(sprintf('=== >>> Restart %s', $station->getName()));
$stationDto->restart();
}
exit;
}
private function initAllStations(KernelInterface $kernel)
{
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'initAllStations'
]);
$output = new BufferedOutput();
$application->run($input, $output);
$initResponse = $output->fetch();
}
/**
* @Route("/edit", name="station_edit")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @param AzuraCast $azuraCast
* @return JsonResponse
* @throws AccessDeniedException
* @throws ClientRequestException
*/
public function edit(Request $request, EntityManagerInterface $entityManager, AzuraCast $azuraCast, KernelInterface $kernel)
{
$stationRemoteId = $request->get('stationRemoteId', null);
if (NULL === $stationRemoteId) {
$station = new Station();
} else {
$stationRepository = $entityManager->getRepository(Station::class);
/** @var Station $station */
$station = $stationRepository->findOneBy(['remoteId' => $stationRemoteId]);
if (NULL === $station) {
return $this->json(['success' => false, 'message' => 'Can\'t find station.'], Response::HTTP_NOT_FOUND);
}
}
if ($request->getMethod() === 'DELETE') {
return $this->deleteStation($station, $entityManager, $azuraCast);
}
$form = $this->createForm(StationType::class, $station, [
'action' => $this->generateUrl('station_edit', ['stationRemoteId' => $stationRemoteId])
]);
if ($request->getMethod() === 'POST' && $request->get('station') !== NULL) {
return $this->editStation($station, $form, $request, $entityManager, $azuraCast, $kernel);
}
return $this->json([
'success' => true,
'view' => $this->renderView('station/edit.html.twig', [
'form' => $form->createView(),
'station' => $station
])
]);
}
/**
* @param Station $station
* @param FormInterface $form
* @param Request $request
* @param EntityManagerInterface $entityManager
* @param AzuraCast $azuraCast
* @return JsonResponse
* @throws AccessDeniedException
* @throws ClientRequestException
*/
private function editStation(Station $station, FormInterface $form, Request $request, EntityManagerInterface $entityManager, AzuraCast $azuraCast, KernelInterface $kernel)
{
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Handle file uploads
$logoFile = $form['channelLogo']->getData();
$videoFile = $form['channelVideo']->getData();
if ($logoFile) {
$logoDir = $this->getParameter('kernel.project_dir') . '/public/uploads/stations/logo/';
if (!is_dir($logoDir)) {
mkdir($logoDir, 0777, true);
}
$logoFilename = uniqid('logo_') . '.' . $logoFile->guessExtension();
$logoFile->move($logoDir, $logoFilename);
$station->setChannelLogo('uploads/stations/logo/' . $logoFilename);
}
if ($videoFile) {
$videoDir = $this->getParameter('kernel.project_dir') . '/public/uploads/stations/video/';
if (!is_dir($videoDir)) {
mkdir($videoDir, 0777, true);
}
$videoFilename = uniqid('video_') . '.' . $videoFile->guessExtension();
$videoFile->move($videoDir, $videoFilename);
$station->setChannelVideo('uploads/stations/video/' . $videoFilename);
}
$stationRepository = $entityManager->getRepository(Station::class);
$exists = $stationRepository->findOneBy(['name' => $station->getName()]);
$new = $station->getId() === null;
if (!empty($exists) && $exists !== $station) {
$error = 'A station with the same name already exists';
return $this->json(['success' => false, 'errors' => $error]);
} elseif ($station->getId() === null) {
$data = $azuraCast->createStation($station);
if ((int) @$data['id'] <= 0) {
return $this->json(['success' => false, 'errors' => 'Can \'t create station']);
}
} else {
$stationDto = $azuraCast->station($station->getId());
$stationObject = $stationDto->get();
$currentName = $stationObject->getName();
$changeName = $currentName != $station->getName();
$currentDescription = $stationObject->getDescription();
$changeDescription = $currentDescription != $station->getDescription();
if ($changeName || $changeDescription) {
$this->logger->info(sprintf('Updating station %s: name change %s, description change %s', $station->getId(), var_export($changeName, true), var_export($changeDescription, true)));
$data = $azuraCast->admin($station->getServer())
->request('PUT', '/api/admin/station/' . $station->getRemoteId(), [
'json' => [
'name' => $station->getName(),
'description' => $station->getDescription()
]
]);
}
}
$entityManager->persist($station);
$entityManager->flush();
if ($new) {
if ($station->getListenUrl() === 'temp') {
$azuraCast->resetStations();
}
sleep(2);
$this->initAllStations($kernel);
}
return $this->json([
'success' => true
]);
} else {
return $this->json(['success' => false, 'errors' => $form->getErrors()], Response::HTTP_BAD_REQUEST);
}
}
private function deleteStation(Station $station, EntityManagerInterface $entityManager, AzuraCast $azuraCast)
{
if (NULL === $station->getId()) {
return $this->json(['success' => false, 'message' => 'Can\'t find station.'], Response::HTTP_NOT_FOUND);
}
try {
$data = $azuraCast->admin($station->getServer())
->request('DELETE', '/api/admin/station/' . $station->getRemoteId());
} catch (Exception $e) {
if (!strstr($e->getMessage(), 'Record not found')) {
throw $e;
}
}
foreach ($station->getEvents() as $event) {
$station->removeEvent($event);
}
$entityManager->remove($station);
$entityManager->flush();
return $this->json(['success' => true]);
}
/**
* @Route("/history/{stationRemoteId}", name="station_history")
* @Entity("station", expr="repository.findOneByRemoteId(stationRemoteId)")
* @param Station $station
* @param AzuraCast $azuraCast
* @return Response
* @throws AccessDeniedException
* @throws ClientRequestException
*/
public function history(Station $station, AzuraCast $azuraCast)
{
$end = new \DateTime();
$start = clone $end;
//$start->modify('+1 hour');
$history = array_slice($azuraCast->station($station)->history($start, $end), 0, 500);
return $this->json([
'success' => true,
'view' => $this->renderView('station/history.html.twig', ['history' => $history])
]);
}
/**
* @Route("/webhook/{stationRemoteId}", name="station_webhook")
* @Entity("station", expr="repository.findOneByRemoteId(stationRemoteId)")
* @param Request $request
* @param Station $station
* @param AzuraCast $azuraCast
* @param EntityManagerInterface $entityManager
* @return Response
* @throws AccessDeniedException
* @throws ClientRequestException
*/
public function webhook(Request $request, Station $station, AzuraCast $azuraCast, EntityManagerInterface $entityManager)
{
file_put_contents(
'/code/var/log/webhook.log',
sprintf(
'[%s] %s: %s',
date('Y-m-d H:i:s'),
$station->getName(),
json_encode($_REQUEST)
) . "\n",
FILE_APPEND | LOCK_EX
);
$nowPlaying = $azuraCast->station($station)->nowPlaying();
$eventTagRepository = $entityManager->getRepository(EventTag::class);
$eventTag = $eventTagRepository->findByRemoteNameFromStation($station, $nowPlaying->getCurrentSong()->getPlaylist());
dump($eventTag);
return $this->json([
'success' => true,
'request' => var_export($request->query->all(), true),
'station' => $station->getId(),
'nowPlaying' => $nowPlaying
]);
}
/**
* @Route("/{stationRemoteId}/nowPlaying", name="station_nowPlaying")
*
* @param Request $request
* @param AzuraCast $azuraCast
* @param EntityManagerInterface $entityManager
* @param int $stationRemoteId
* @return Response
*/
public function nowPlaying(Request $request, AzuraCast $azuraCast, EntityManagerInterface $entityManager, int $stationRemoteId)
{
$stationRepository = $entityManager->getRepository(Station::class);
/** @var Station $station */
$station = $stationRepository->findOneBy(['remoteId' => $stationRemoteId]);
if (!$station instanceof Station) {
return $this->json(['text' => 'Invalid Station - Please try again', 'error' => true]);
}
$data = $azuraCast->station($station->getId())->nowPlaying();
return $this->json(['text' => $data->getCurrentSong()->getSong()->getText(), 'error' => false, 'for' => $stationRemoteId]);
}
/**
* @Route("/nowPlaying", name="station_nowPlaying_all")
*
* @param AzuraCast $azuraCast
* @return Response
*/
public function nowPlayingAll(AzuraCast $azuraCast)
{
$nowPlaying = array_map(function ($station) {
if (gettype($station) === 'array') {
return [$station['station']['id'], 'Stream Offline'];
}
return [$station->getStation()->getId(), $station->getCurrentSong()->getSong()->getText()];
}, $azuraCast->allStationsNowPlaying());
return $this->json($nowPlaying);
}
/**
* @Route("/nextsong", name="station_nextsong")
*
* @param Request $request
* @param AzuraCast $azuraCast
* @param EntityManagerInterface $entityManager
* @return Response
*/
public function nextsong(Request $request, AzuraCast $azuraCast, EntityManagerInterface $entityManager)
{
$stationRepository = $entityManager->getRepository(Station::class);
$stationRemoteId = $request->get('station');
/** @var Station $station */
$station = $stationRepository->findOneBy(['remoteId' => $stationRemoteId]);
set_time_limit(20);
if (empty($station)) {
return new Response(sprintf('annotate:title="No music",artist="Unconfigured Station (%s)",duration="30.",song_id="0",media_id="0",playlist_id="0":/usr/local/share/icecast/web/error.mp3', $stationRemoteId), Response::HTTP_OK, ['content-type' => 'text/plain']);
}
ini_set('memory_limit', '2G');
$file = $this->nextEventTag($request, $azuraCast, $entityManager, $station);
if (empty($file)) {
return new Response('annotate:title="No music",artist="Schedule Empty - ' . $stationRemoteId . '/' . $station->getId() . '",duration="30.",song_id="0",media_id="0",playlist_id="0":/usr/local/share/icecast/web/error.mp3', Response::HTTP_OK, ['content-type' => 'text/plain']);
//list($file, $playlistId) = $this->randomSongForStation($azuraCast, $station);
// $content = sprintf(
// 'annotate:title="No music",artist="Schedule Empty",duration="3.",song_id="0",media_id="0",playlist_id="0":%s',
// '/var/azuracast/media/error.mp3'
// );
} else {
$playlistId = $file[1];
$file = $file[0];
}
$rootPath =
$request->query->get('public') === "yes"
? sprintf(
'https://%s/songs/',
$request->getHost()
)
: '/var/azuracast/media/';
$content = sprintf(
'annotate:title="%s",artist="%s",duration="%s.",song_id="%s",media_id="%s",playlist_id="%s":%s%s',
$file->getTitle(),
$file->getArtist(),
(int) $file->getLength(),
$file->getSongId(),
$file->getId(),
$playlistId,
$rootPath,
$file->getPath()
);
return new Response($content, Response::HTTP_OK, ['content-type' => 'text/plain']);
}
private function getFileInfo(AzuraCast $azuraCast, Station $station, Request $request, SongStation $songToStation)
{
try {
return $azuraCast->station($station)->media()->request('GET', '/api/station/' . $station->getRemoteId() . '/file/' . $songToStation->getMediaId());
} catch (Exception $e) {
$this->debug($request, sprintf('Cannot get file info %s', $e->getMessage()));
}
return NULL;
}
/**
* @param Request $request
* @param AzuraCast $azuraCast
* @param EntityManagerInterface $entityManager
* @param Station $station
* @param int|null $allowedFrequency
* @param int|null $allowedTime
* @param int|null $testId
* @return array|null |null
*/
private function nextEventTag(Request $request, AzuraCast $azuraCast, EntityManagerInterface $entityManager, Station $station, ?int $allowedFrequency = NULL, ?int $allowedTime = NULL, ?int $testId = NULL)
{
ini_set('date.timezone', 'Europe/Bucharest');
if ($allowedFrequency === NULL) {
$allowedFrequency = [0, (int) $request->get('allowedFrequency', date('w'))];
} else {
$allowedFrequency = [0, $allowedFrequency];
}
if ($allowedTime === NULL) {
$allowedTime = (int) $request->get('allowedTime', date('H'));
}
if ($request->get('dev') === 'yes' && $request->get('testId') != 'null') {
$testId = (int) $request->get('testId');
}
$this->debug($request, sprintf('Find event for %s [Freq %s/%s]', $station->getName(), $allowedFrequency[1], $allowedTime));
/** @var HistoryRepository $historyRepository */
$historyRepository = $entityManager->getRepository(History::class);
/** @var SongRepository $songRepository */
$songsRepository = $entityManager->getRepository(Song::class);
/** @var History $lastPlayed */
$lastPlayed = $historyRepository->getLastEventForStation($station);
/** @var EventTag $lastEventTag */
$lastEventTag = $lastPlayed === NULL ? NULL : $lastPlayed->getEventTag();
$this->debug($request, sprintf('Last event tag %s', $lastEventTag ? $lastEventTag->getEvent()->getName() : 'NONE'));
/** @var HistoryRepository $historyRepository */
$historyRepository = $entityManager->getRepository(History::class);
$currentEvent = NULL;
foreach ($station->getEventsInOrder() as $event) {
if (!in_array($event->getFrequency(), $allowedFrequency)) {
continue; //just events that play every day and this day of the week will be taken into account
}
$time = (int) $event->getTime();
if ($time !== 0 && $time > $allowedTime) {
continue; //this event is for later
}
if ($event->getType() === Event::TYPE_PLAY) {
$plays = $historyRepository->countEventToday($station, $event, $testId);
if ($plays < $event->getEventTags()->count()) {
$currentEvent = $event;
} else {
$this->debug($request, sprintf('Skip play %s as it already played %s', $event->getName(), $plays));
}
} else {
$currentEvent = $event;
}
}
if (empty($currentEvent)) { //Get the next correct event
$events = $station->getEvents()->toArray();
foreach ($events as $event) {
if ($event->getType() === Event::TYPE_PLAY) {
if ($event->getTime() > $allowedTime) {
$this->debug($request, sprintf('Skip event %s', $event->getName()));
continue; //This play event is for later
}
$plays = $entityManager->getRepository(History::class)->countEventToday($station, $event, $testId);
if ($plays < $event->getEventTags()->count()) {
$currentEvent = $event;
}
}
}
if (empty($currentEvent)) {
$first = $station->getFirstEventInADay();
$last = $station->getLastEventInADay();
if (!empty($first) && !empty($last)) {
$currentEvent = $first->getTimeInt() > $allowedTime ? $last : $first;
}
}
}
if (empty($currentEvent)) { //If there are no events.. there are no events
$this->debug($request, sprintf('No events for station'));
return NULL;
}
$eventTagToPlay = NULL;
$next = false;
foreach ($currentEvent->getEventTags() as $eventTag) {
if (empty($eventTag->getTag())) continue;
$tag = $eventTag->getTag();
if (count($tag->getSongs()->toArray()) < 1) {
$this->debug($request, sprintf('No songs available on %s -> %s', $station->getName(), $tag->getName()));
continue;
}
if ($eventTag === $lastEventTag) {
$next = true;
$this->debug($request, sprintf('Current eventTag %s', $eventTag->getEvent()->getName()));
} elseif ($next || empty($lastEventTag)) {
$eventTagToPlay = $eventTag;
$this->debug($request, sprintf('Next eventTag %s', $eventTag->getEvent()->getName()));
break;
}
}
$songToPlay = NULL;
if (empty($eventTagToPlay)) {
/** @var EventTag $eventTagToPlay */
$eventTagToPlay = $currentEvent->getEventTags()->first();
$this->debug($request, sprintf(
'Empty eventTag => eventTag %s/%s',
$eventTagToPlay->getEvent()->getName(),
$eventTagToPlay->getTag() ?? 'deleted tag'
));
} else {
$this->debug($request, sprintf(
'Event tag to play %s/%s',
$eventTagToPlay->getEvent()->getName(),
$eventTagToPlay->getTag() ?? 'deleted tag'
));
}
if (!empty($eventTagToPlay)) {
$tag = $eventTagToPlay->getTag();
$songs = $tag->getSongs()->toArray();
#$eventTagToPlay = $entityManager->getRepository(EventTag::class)->find(49);
$songToPlay = $this->getNextBestSong($songsRepository, $request, $station, $eventTagToPlay, $testId);
$this->debug($request, sprintf('Songs available %s', count($songs)));
if ($testId !== NULL) { //We do not need to sync
$history = new History();
$history->setEventTag($eventTagToPlay);
$history->setSong($songToPlay);
$history->setTestId($testId ?? 0);
$entityManager->persist($history);
$entityManager->flush();
$text = $path = $title = $songToPlay->getName();
$tagName = $eventTagToPlay->getTag()->getName();
$artist = implode('/', array_map(function ($element) use ($tagName) {
return $tagName === $element[1] ? ('<strong style="color: brown">' . $element[1] . '</strong>') : $element[1];
}, $songToPlay->getTagJson()));
$file = [
"id" => 10922,
"album" => null,
"lyrics" => null,
"isrc" => null,
"length" => $songToPlay->getLength(),
"length_text" => $songToPlay->getPlaytime(),
"path" => $path . ".mp3",
"mtime" => 1603730441,
"art_updated_at" => 0,
"playlists" => [],
"unique_id" => "f61b1f9930b6db4ab54135f8",
"song_id" => "268bfa008999e6b0e94b3e4962a8226f",
"text" => $text,
"artist" => $artist,
"custom_fields" => [],
"title" => $title
];
$mediaFileDto = MediaFileDto::fromArray($file);
return [$mediaFileDto, !empty($eventTagToPlay) ? $eventTagToPlay->getId() : '-'];
}
} else {
$this->debug($request, sprintf('No event tag'));
return NULL;
}
if (empty($songToPlay)) {
$this->debug($request, sprintf('No song to play'));
return NULL;
}
$songStationRepository = $entityManager->getRepository(SongStation::class);
$tries = 0;
$historyTry = 5;
$songToStation = NULL;
while (empty($songToStation) && $tries < $historyTry) {
$tries++;
/** @var SongStation $songToStation */
$songToStation = $songStationRepository->findOneBy(['song' => $songToPlay, 'station' => $station]);
if (!empty($songToStation)) {
$file = $this->getFileInfo($azuraCast, $station, $request, $songToStation);
if ($file === NULL) {
$songToStation = NULL;
} else {
if ($file['path'] !== $songToPlay->getNameWithExtension()) {
$this->debug($request, sprintf(
'Invalid song to station link. %s (RefId: %s) to %s',
$songToPlay->getNameWithExtension(),
$songToPlay->getId(),
var_export($file, true)
));
$station->removeSongStation($songToStation);
$entityManager->persist($station);
$entityManager->remove($songToStation);
$entityManager->flush();
$file = NULL;
$songToStation = NULL;
}
}
}
if (empty($songToStation)) { //Sync song to station
ini_set('memory_limit', '2g');
$this->debug($request, sprintf('Sync song to station %s', $songToPlay->getNameWithExtension()));
$uploadFile = new UploadFileDto($songToPlay->getNameWithExtension(), file_get_contents($songToPlay->getFullPath()));
try {
$response = $azuraCast->station($station)->media()->upload($uploadFile);
$songToStation = new SongStation();
$songToStation->setStation($station);
$songToStation->setSong($songToPlay);
$songToStation->setMediaId($response->getId());
$entityManager->persist($songToStation);
$entityManager->flush();
} catch (Exception $e) {
$this->debug($request, sprintf('Unable to sync song %s: %s', $songToPlay->getNameWithExtension(), $e->getMessage()));
$songs = array_filter($songs, function ($song) use ($songToPlay) { //Remove song from list as it has issues
return $song->getId() !== $songToPlay->getId();
});
$songToStation = NULL;
}
} else {
$this->debug($request, sprintf('Song already synced %s', $songToStation->getSong()->getName()));
}
if (empty($songToStation)) {
/** @var Song $songToPlay */
$songToPlay = $this->getNextBestSong($songsRepository, $request, $station, $eventTagToPlay, $testId);
} else {
$file = $this->getFileInfo($azuraCast, $station, $request, $songToStation);
if ($file === NULL) {
$songToStation = NULL;
}
}
}
if (empty($songToStation)) { //There is an error syncing this media => will try and use another song
$this->debug($request, sprintf('Error sync media'));
return NULL;
}
$history = new History();
$history->setEventTag($eventTagToPlay);
$history->setSong($songToPlay);
$history->setTestId($testId ?? 0);
$entityManager->persist($history);
$entityManager->flush();
$songTags = array_map(function ($element) {
return $element[1];
}, $songToPlay->getTagJson());
$this->debug($request, sprintf(
'Will play [%s] with tags [%s] - current tag [%s]',
$songToPlay->getName(),
implode('/', $songTags),
$eventTagToPlay
));
$file['title'] .= ' ||| ' . $eventTagToPlay . ' ||| ' . implode('/', $songTags) . ' ||| ' . $file['path'];
$mediaFileDto = MediaFileDto::fromArray($file);
return [$mediaFileDto, !empty($eventTagToPlay) ? $eventTagToPlay->getRemoteId() : '-'];
}
private function getNextBestSong(SongRepository $songRepo, Request $request, Station $station, EventTag $eventTagToPlay, ?int $testId = NULL)
{
while (true) {
$songs = $songRepo->getSongsHistoryForEventTag($eventTagToPlay);
$maxSongs = count($songs);
$top10Pgr = intval(round(0.1 * $maxSongs, 0));
if ($top10Pgr < 1 && $maxSongs > 0) {
$top10Pgr = 1;
}
$top10PgrSongs = array_slice($songs, 0, $top10Pgr);
$this->debug($request, sprintf('YEY available %s %s', count($songs), count($top10PgrSongs)));
if (empty($top10PgrSongs)) {
return NULL;
}
$song = $top10PgrSongs[array_rand($top10PgrSongs)];
if (file_exists($song->getFullPath())) {
return $song;
}
//mark song as deleted
$song->setDeleted(true);
$em = $this->getDoctrine()->getManager();
$em->persist($song);
$em->flush();
if (empty($top10PgrSongs)) {
return NULL;
}
}
}
private function debug(Request $request, string $message)
{
if ($request->get('dev') === 'yes') {
dump($message);
}
$function = isset(debug_backtrace()[1]) ? debug_backtrace()[1]['function'] : 'undefined';
$this->logger->debug(sprintf(
'STATION%s - %s',
$request->get('station') ?? '?',
$message
), [get_called_class(), $function]);
}
/**
* @Route("/{stationRemoteId}/preview", name="station_preview")
* @Entity("station", expr="repository.findOneByRemoteId(stationRemoteId)")
* @param Request $request
* @param Station $station
* @param AzuraCast $azuraCast
* @param EntityManagerInterface $entityManager
* @return Response
* @throws AccessDeniedException
*/
public function preview(Request $request, Station $station, AzuraCast $azuraCast, EntityManagerInterface $entityManager)
{
ini_set('date.timezone', 'Europe/Bucharest');
$eventTagRepository = $entityManager->getRepository(EventTag::class);
$historyRepository = $entityManager->getRepository(History::class);
$today = (int) date('d');
$timestamp = strtotime('today midnight');
$testId = rand(1, 99999999);
set_time_limit(120);
$return = [];
$total = 0;
while ((int) date('d', $timestamp) === $today) {
$total++;
$file = $this->nextEventTag(
$request,
$azuraCast,
$entityManager,
$station,
(int) date('w', $timestamp),
(int) date('H', $timestamp),
$testId
);
if ($file === NULL) {
throw new \Exception('No songs available');
//list($song, $eventTag) = $this->randomSongForStation($azuraCast, $station);
} else {
$eventTag = $eventTagRepository->find($file[1]);
/** @var MediaFileDto $song */
$song = $file[0];
}
$event = $eventTag === '-' ? NULL : $eventTag->getEvent();
$tag = $eventTag === '-' ? NULL : $eventTag->getTag();
$return[] = [
'when' => date('Y-m-d H:i:s', $timestamp),
'title' => $song->getTitle(),
'artist' => $song->getArtist(),
'length' => $song->getLengthText(),
'event' => $event ? $event->getName() . " - " . $event->getTimeName() : '-',
'frequency' => $event ? $event->getFrequencyName() : '-',
'type' => $event ? $event->getTypeName() : '-',
'tag' => $tag ? ($tag->getName() . ' (' . $tag->getSongs()->count() . ')') : '-'
];
$timestamp += $song->getLength();
}
foreach ($historyRepository->findBy(['testId' => $testId]) as $history) {
$entityManager->remove($history);
}
$entityManager->flush();
return $this->json([
'success' => true,
'view' => $this->renderView('station/preview.html.twig', ['preview' => $return, 'total' => $total, 'station' => $station])
]);
}
/**
* @param AzuraCast $azuraCast
* @param Station $station
* @return array
* @throws AccessDeniedException
*/
private function randomSongForStation(AzuraCast $azuraCast, Station $station)
{
$cacheFS = new FilesystemAdapter('station.randomsong', 60);
$cache = $cacheFS->getItem('media.list.' . $station->getId());
if (!$cache->isHit()) {
try {
$files = $azuraCast->station($station)->media()->list();
} catch (ClientRequestException $e) {
try {
$files = $azuraCast->station($station)->media()->list();
} catch (ClientRequestException $e) {
try {
$files = $azuraCast->station($station)->media()->list();
} catch (ClientRequestException $e) {
$files = NULL;
}
}
}
if ($files === NULL) {
return [];
}
$cache->set($files);
$cacheFS->save($cache);
} else {
$files = $cache->get();
}
$file = $files[array_rand($files)];
$playlistId = '-';
return [$file, $playlistId];
}
}