2016-05-04 00:03:28 +00:00
|
|
|
<?php
|
2018-08-04 22:05:14 +00:00
|
|
|
namespace App\Entity;
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
use App\Annotations\AuditLog;
|
2020-02-06 02:35:13 +00:00
|
|
|
use App\Normalizer\Annotation\DeepNormalize;
|
2019-08-07 04:33:55 +00:00
|
|
|
use Cake\Chronos\Chronos;
|
2019-09-04 18:00:51 +00:00
|
|
|
use DateTimeZone;
|
2017-01-24 00:35:16 +00:00
|
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
2017-08-17 18:28:48 +00:00
|
|
|
use Doctrine\Common\Collections\Collection;
|
2018-12-22 00:01:04 +00:00
|
|
|
use Doctrine\ORM\Mapping as ORM;
|
2019-04-13 03:27:58 +00:00
|
|
|
use OpenApi\Annotations as OA;
|
2019-11-02 18:50:21 +00:00
|
|
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
2019-04-13 03:27:58 +00:00
|
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
2018-12-22 00:01:04 +00:00
|
|
|
|
2016-05-04 00:03:28 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Table(name="station_playlists")
|
|
|
|
* @ORM\Entity
|
2018-12-22 00:01:04 +00:00
|
|
|
* @ORM\HasLifecycleCallbacks
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
2019-08-14 23:50:53 +00:00
|
|
|
* @AuditLog\Auditable
|
|
|
|
*
|
2019-04-13 03:27:58 +00:00
|
|
|
* @OA\Schema(type="object")
|
2016-05-04 00:03:28 +00:00
|
|
|
*/
|
2017-08-17 18:28:48 +00:00
|
|
|
class StationPlaylist
|
2016-05-04 00:03:28 +00:00
|
|
|
{
|
2018-04-06 21:29:27 +00:00
|
|
|
use Traits\TruncateStrings;
|
|
|
|
|
2019-03-01 03:58:19 +00:00
|
|
|
public const DEFAULT_WEIGHT = 3;
|
2019-03-31 22:09:10 +00:00
|
|
|
public const DEFAULT_REMOTE_BUFFER = 20;
|
2019-03-01 03:58:19 +00:00
|
|
|
|
2018-04-12 22:43:58 +00:00
|
|
|
public const TYPE_DEFAULT = 'default';
|
|
|
|
public const TYPE_ONCE_PER_X_SONGS = 'once_per_x_songs';
|
|
|
|
public const TYPE_ONCE_PER_X_MINUTES = 'once_per_x_minutes';
|
2019-03-26 07:05:50 +00:00
|
|
|
public const TYPE_ONCE_PER_HOUR = 'once_per_hour';
|
2018-04-12 22:43:58 +00:00
|
|
|
public const TYPE_ADVANCED = 'custom';
|
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
public const SOURCE_SONGS = 'songs';
|
2019-09-04 18:00:51 +00:00
|
|
|
public const SOURCE_REMOTE_URL = 'remote_url';
|
2018-04-29 23:48:48 +00:00
|
|
|
|
2018-11-20 12:06:16 +00:00
|
|
|
public const REMOTE_TYPE_STREAM = 'stream';
|
|
|
|
public const REMOTE_TYPE_PLAYLIST = 'playlist';
|
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
public const ORDER_RANDOM = 'random';
|
2018-08-30 00:45:01 +00:00
|
|
|
public const ORDER_SHUFFLE = 'shuffle';
|
2018-04-29 23:48:48 +00:00
|
|
|
public const ORDER_SEQUENTIAL = 'sequential';
|
|
|
|
|
2019-04-29 05:20:23 +00:00
|
|
|
public const OPTION_INTERRUPT_OTHER_SONGS = 'interrupt';
|
|
|
|
public const OPTION_LOOP_PLAYLIST_ONCE = 'loop_once';
|
|
|
|
public const OPTION_PLAY_SINGLE_TRACK = 'single_track';
|
|
|
|
public const OPTION_MERGE = 'merge';
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="id", type="integer")
|
|
|
|
* @ORM\Id
|
|
|
|
* @ORM\GeneratedValue(strategy="IDENTITY")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=1)
|
2019-05-03 09:32:45 +00:00
|
|
|
* @var int|null
|
2017-08-17 18:28:48 +00:00
|
|
|
*/
|
|
|
|
protected $id;
|
|
|
|
|
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="station_id", type="integer")
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $station_id;
|
|
|
|
|
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\ManyToOne(targetEntity="Station", inversedBy="playlists")
|
2018-12-22 00:01:04 +00:00
|
|
|
* @ORM\JoinColumns({
|
|
|
|
* @ORM\JoinColumn(name="station_id", referencedColumnName="id", onDelete="CASCADE")
|
2017-08-17 18:28:48 +00:00
|
|
|
* })
|
|
|
|
* @var Station
|
|
|
|
*/
|
|
|
|
protected $station;
|
|
|
|
|
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="name", type="string", length=200)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @Assert\NotBlank()
|
|
|
|
* @OA\Property(example="Test Playlist")
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $name;
|
|
|
|
|
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="type", type="string", length=50)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
2019-11-02 18:50:21 +00:00
|
|
|
* @Assert\Choice(choices={"default", "once_per_x_songs", "once_per_x_minutes", "once_per_hour", "custom"})
|
2019-04-13 03:27:58 +00:00
|
|
|
* @OA\Property(example="default")
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var string
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $type = self::TYPE_DEFAULT;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
2018-04-12 22:43:58 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="source", type="string", length=50)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @Assert\Choice(choices={"songs", "remote_url"})
|
|
|
|
* @OA\Property(example="songs")
|
|
|
|
*
|
2018-04-12 22:43:58 +00:00
|
|
|
* @var string
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $source = self::SOURCE_SONGS;
|
2018-04-12 22:43:58 +00:00
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="playback_order", type="string", length=50)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @Assert\Choice(choices={"random", "shuffle", "sequential"})
|
|
|
|
* @OA\Property(example="shuffle")
|
|
|
|
*
|
2018-04-29 23:48:48 +00:00
|
|
|
* @var string
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $order = self::ORDER_SHUFFLE;
|
2018-04-29 23:48:48 +00:00
|
|
|
|
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="remote_url", type="string", length=255, nullable=true)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example="http://remote-url.example.com/stream.mp3")
|
|
|
|
*
|
2018-04-29 23:48:48 +00:00
|
|
|
* @var string|null
|
|
|
|
*/
|
|
|
|
protected $remote_url;
|
|
|
|
|
2018-11-20 12:06:16 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="remote_type", type="string", length=25, nullable=true)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @Assert\Choice(choices={"stream", "playlist"})
|
|
|
|
* @OA\Property(example="stream")
|
|
|
|
*
|
2018-11-20 12:06:16 +00:00
|
|
|
* @var string|null
|
|
|
|
*/
|
|
|
|
protected $remote_type = self::REMOTE_TYPE_STREAM;
|
|
|
|
|
2019-03-31 22:09:10 +00:00
|
|
|
/**
|
|
|
|
* @ORM\Column(name="remote_timeout", type="smallint")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=0)
|
|
|
|
*
|
2019-03-31 22:09:10 +00:00
|
|
|
* @var int The total time (in seconds) that Liquidsoap should buffer remote URL streams.
|
|
|
|
*/
|
|
|
|
protected $remote_buffer = 0;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="is_enabled", type="boolean")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=true)
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var bool
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $is_enabled = true;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
2019-03-15 02:22:09 +00:00
|
|
|
/**
|
|
|
|
* @ORM\Column(name="is_jingle", type="boolean")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=false)
|
|
|
|
*
|
2019-03-15 02:22:09 +00:00
|
|
|
* @var bool If yes, do not send jingle metadata to AutoDJ or trigger web hooks.
|
|
|
|
*/
|
|
|
|
protected $is_jingle = false;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="play_per_songs", type="smallint")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=5)
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var int
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $play_per_songs = 0;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="play_per_minutes", type="smallint")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=120)
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var int
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $play_per_minutes = 0;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
2019-03-26 07:05:50 +00:00
|
|
|
/**
|
|
|
|
* @ORM\Column(name="play_per_hour_minute", type="smallint")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=15)
|
|
|
|
*
|
2019-03-26 07:05:50 +00:00
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $play_per_hour_minute = 0;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="weight", type="smallint")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=3)
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var int
|
|
|
|
*/
|
2019-03-01 03:58:19 +00:00
|
|
|
protected $weight = self::DEFAULT_WEIGHT;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
2018-04-12 22:43:58 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="include_in_requests", type="boolean")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=true)
|
|
|
|
*
|
2018-04-12 22:43:58 +00:00
|
|
|
* @var bool
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $include_in_requests = true;
|
2018-04-12 22:43:58 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\Column(name="include_in_automation", type="boolean")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
|
|
|
* @OA\Property(example=false)
|
|
|
|
*
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var bool
|
|
|
|
*/
|
2018-11-20 12:06:16 +00:00
|
|
|
protected $include_in_automation = false;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
2019-03-26 07:05:50 +00:00
|
|
|
/**
|
2019-04-29 05:20:23 +00:00
|
|
|
* @ORM\Column(name="backend_options", type="string", length=255, nullable=true)
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
2019-04-29 05:20:23 +00:00
|
|
|
* @OA\Property(example="interrupt,loop_once,single_track,merge")
|
2019-04-13 03:27:58 +00:00
|
|
|
*
|
2019-04-29 05:20:23 +00:00
|
|
|
* @var string
|
2019-04-02 22:49:45 +00:00
|
|
|
*/
|
2019-04-30 14:43:00 +00:00
|
|
|
protected $backend_options = '';
|
2019-04-02 22:49:45 +00:00
|
|
|
|
2019-07-19 23:27:24 +00:00
|
|
|
/**
|
|
|
|
* @ORM\Column(name="played_at", type="integer")
|
2019-08-14 23:50:53 +00:00
|
|
|
* @AuditLog\AuditIgnore
|
|
|
|
*
|
2019-07-19 23:27:24 +00:00
|
|
|
* @var int The UNIX timestamp at which a track from this playlist was last played.
|
|
|
|
*/
|
|
|
|
protected $played_at = 0;
|
|
|
|
|
2019-08-18 01:42:59 +00:00
|
|
|
/**
|
|
|
|
* @ORM\Column(name="queue", type="array", nullable=true)
|
|
|
|
* @AuditLog\AuditIgnore
|
|
|
|
*
|
|
|
|
* @var array|null The current queue of unplayed songs for this playlist.
|
|
|
|
*/
|
|
|
|
protected $queue;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2018-12-20 11:33:49 +00:00
|
|
|
* @ORM\OneToMany(targetEntity="StationPlaylistMedia", mappedBy="playlist", fetch="EXTRA_LAZY")
|
2018-12-22 00:01:04 +00:00
|
|
|
* @ORM\OrderBy({"weight" = "ASC"})
|
2017-08-17 18:28:48 +00:00
|
|
|
* @var Collection
|
|
|
|
*/
|
2018-04-29 23:48:48 +00:00
|
|
|
protected $media_items;
|
2017-08-17 18:28:48 +00:00
|
|
|
|
2019-11-02 18:50:21 +00:00
|
|
|
/**
|
2020-02-18 04:32:14 +00:00
|
|
|
* @ORM\OneToMany(targetEntity="StationSchedule", mappedBy="playlist")
|
2019-11-02 18:50:21 +00:00
|
|
|
* @var Collection
|
|
|
|
*
|
|
|
|
* @DeepNormalize(true)
|
|
|
|
* @Serializer\MaxDepth(1)
|
|
|
|
* @OA\Property(
|
|
|
|
* @OA\Items()
|
|
|
|
* )
|
|
|
|
*/
|
|
|
|
protected $schedule_items;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
public function __construct(Station $station)
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
2017-08-17 18:28:48 +00:00
|
|
|
$this->station = $station;
|
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
$this->media_items = new ArrayCollection;
|
2019-11-02 18:50:21 +00:00
|
|
|
$this->schedule_items = new ArrayCollection;
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
2019-05-03 09:32:45 +00:00
|
|
|
public function getId(): ?int
|
2017-08-17 18:28:48 +00:00
|
|
|
{
|
|
|
|
return $this->id;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2020-04-18 16:46:39 +00:00
|
|
|
public function getStation(): Station
|
|
|
|
{
|
|
|
|
return $this->station;
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
2019-08-14 23:50:53 +00:00
|
|
|
* @AuditLog\AuditIdentifier
|
2017-08-17 18:28:48 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getName(): string
|
|
|
|
{
|
|
|
|
return $this->name;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function setName(string $name): void
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
2020-03-29 07:16:41 +00:00
|
|
|
$this->name = $this->truncateString($name, 200);
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function getShortName(): string
|
2017-08-17 18:28:48 +00:00
|
|
|
{
|
2019-12-02 22:12:27 +00:00
|
|
|
return Station::getStationShortName($this->name);
|
2017-08-17 18:28:48 +00:00
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
public function getType(): string
|
|
|
|
{
|
|
|
|
return $this->type;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2019-01-31 17:54:17 +00:00
|
|
|
public function setType(string $type): void
|
2017-08-17 18:28:48 +00:00
|
|
|
{
|
|
|
|
$this->type = $type;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2018-04-12 22:43:58 +00:00
|
|
|
public function getSource(): string
|
|
|
|
{
|
|
|
|
return $this->source;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setSource(string $source): void
|
|
|
|
{
|
2019-08-18 01:42:59 +00:00
|
|
|
// Reset the playback queue if source is changed.
|
|
|
|
if ($source !== $this->source) {
|
|
|
|
$this->queue = null;
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:43:58 +00:00
|
|
|
$this->source = $source;
|
|
|
|
}
|
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
public function getOrder(): string
|
|
|
|
{
|
|
|
|
return $this->order;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setOrder(string $order): void
|
|
|
|
{
|
2019-08-18 01:42:59 +00:00
|
|
|
// Reset the playback queue if order is changed.
|
|
|
|
if ($order !== $this->order) {
|
|
|
|
$this->queue = null;
|
|
|
|
}
|
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
$this->order = $order;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getRemoteUrl(): ?string
|
|
|
|
{
|
|
|
|
return $this->remote_url;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setRemoteUrl(?string $remote_url): void
|
|
|
|
{
|
|
|
|
$this->remote_url = $remote_url;
|
|
|
|
}
|
|
|
|
|
2018-11-20 12:06:16 +00:00
|
|
|
public function getRemoteType(): ?string
|
|
|
|
{
|
|
|
|
return $this->remote_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setRemoteType(?string $remote_type): void
|
|
|
|
{
|
|
|
|
$this->remote_type = $remote_type;
|
|
|
|
}
|
|
|
|
|
2019-03-31 22:09:10 +00:00
|
|
|
public function getRemoteBuffer(): int
|
|
|
|
{
|
|
|
|
return $this->remote_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setRemoteBuffer(int $remote_buffer): void
|
|
|
|
{
|
|
|
|
$this->remote_buffer = $remote_buffer;
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
public function getIsEnabled(): bool
|
|
|
|
{
|
|
|
|
return $this->is_enabled;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2019-01-31 17:54:17 +00:00
|
|
|
public function setIsEnabled(bool $is_enabled): void
|
2017-08-17 18:28:48 +00:00
|
|
|
{
|
|
|
|
$this->is_enabled = $is_enabled;
|
|
|
|
}
|
|
|
|
|
2019-03-15 02:22:09 +00:00
|
|
|
public function isJingle(): bool
|
|
|
|
{
|
|
|
|
return $this->is_jingle;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsJingle(bool $is_jingle): void
|
|
|
|
{
|
|
|
|
$this->is_jingle = $is_jingle;
|
|
|
|
}
|
|
|
|
|
2018-11-20 12:06:16 +00:00
|
|
|
/**
|
|
|
|
* @return int Get the duration of scheduled play time in seconds (used for remote URLs of indeterminate length).
|
|
|
|
*/
|
|
|
|
public function getScheduleDuration(): int
|
|
|
|
{
|
2019-11-02 18:50:21 +00:00
|
|
|
if ($this->schedule_items->count() > 0) {
|
|
|
|
$now = Chronos::now(new DateTimeZone($this->getStation()->getTimezone()));
|
2018-11-20 12:06:16 +00:00
|
|
|
|
2019-11-02 18:50:21 +00:00
|
|
|
foreach ($this->schedule_items as $scheduleItem) {
|
2020-02-18 04:32:14 +00:00
|
|
|
/** @var StationSchedule $scheduleItem */
|
2019-11-02 18:50:21 +00:00
|
|
|
if ($scheduleItem->shouldPlayNow($now)) {
|
|
|
|
return $scheduleItem->getDuration();
|
|
|
|
}
|
|
|
|
}
|
2018-11-20 12:06:16 +00:00
|
|
|
}
|
|
|
|
|
2019-11-02 18:50:21 +00:00
|
|
|
return 0;
|
2018-11-20 12:06:16 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 03:08:01 +00:00
|
|
|
public function getWeight(): int
|
2017-08-24 01:30:43 +00:00
|
|
|
{
|
2019-03-01 03:58:19 +00:00
|
|
|
if ($this->weight < 1) {
|
|
|
|
return self::DEFAULT_WEIGHT;
|
2019-02-19 03:08:01 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 03:58:19 +00:00
|
|
|
return $this->weight;
|
2017-08-17 18:28:48 +00:00
|
|
|
}
|
2016-08-30 06:28:56 +00:00
|
|
|
|
2019-01-31 17:54:17 +00:00
|
|
|
public function setWeight(int $weight): void
|
2017-08-17 18:28:48 +00:00
|
|
|
{
|
|
|
|
$this->weight = $weight;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2018-04-12 22:43:58 +00:00
|
|
|
public function getIncludeInRequests(): bool
|
|
|
|
{
|
|
|
|
return $this->include_in_requests;
|
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function setIncludeInRequests(bool $include_in_requests): void
|
2018-04-12 22:43:58 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
$this->include_in_requests = $include_in_requests;
|
2018-04-12 22:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-09-04 18:00:51 +00:00
|
|
|
* Indicates whether this playlist can be used as a valid source of requestable media.
|
|
|
|
*
|
|
|
|
* @return bool
|
2018-04-12 22:43:58 +00:00
|
|
|
*/
|
2019-09-04 18:00:51 +00:00
|
|
|
public function isRequestable(): bool
|
2018-04-12 22:43:58 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
return ($this->is_enabled && $this->include_in_requests);
|
2018-04-12 22:43:58 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
public function getIncludeInAutomation(): bool
|
|
|
|
{
|
|
|
|
return $this->include_in_automation;
|
|
|
|
}
|
|
|
|
|
2019-01-31 17:54:17 +00:00
|
|
|
public function setIncludeInAutomation(bool $include_in_automation): void
|
2017-08-17 18:28:48 +00:00
|
|
|
{
|
|
|
|
$this->include_in_automation = $include_in_automation;
|
|
|
|
}
|
|
|
|
|
2019-07-19 23:27:24 +00:00
|
|
|
public function getPlayedAt(): int
|
|
|
|
{
|
|
|
|
return $this->played_at;
|
|
|
|
}
|
|
|
|
|
2019-07-20 21:04:42 +00:00
|
|
|
public function setPlayedAt(int $played_at): void
|
|
|
|
{
|
|
|
|
$this->played_at = $played_at;
|
|
|
|
}
|
|
|
|
|
2019-08-18 01:42:59 +00:00
|
|
|
public function getQueue(): ?array
|
|
|
|
{
|
|
|
|
return $this->queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setQueue(?array $queue): void
|
|
|
|
{
|
|
|
|
$this->queue = $queue;
|
|
|
|
}
|
|
|
|
|
2019-02-19 03:08:01 +00:00
|
|
|
/**
|
2020-02-18 04:32:14 +00:00
|
|
|
* @return Collection|StationPlaylistMedia[]
|
2019-02-19 03:08:01 +00:00
|
|
|
*/
|
2019-03-26 07:05:50 +00:00
|
|
|
public function getMediaItems(): Collection
|
2019-02-19 03:08:01 +00:00
|
|
|
{
|
2019-03-26 07:05:50 +00:00
|
|
|
return $this->media_items;
|
2019-02-19 03:08:01 +00:00
|
|
|
}
|
|
|
|
|
2019-11-02 18:50:21 +00:00
|
|
|
/**
|
2020-02-18 04:32:14 +00:00
|
|
|
* @return Collection|StationSchedule[]
|
2019-11-02 18:50:21 +00:00
|
|
|
*/
|
|
|
|
public function getScheduleItems(): Collection
|
|
|
|
{
|
|
|
|
return $this->schedule_items;
|
|
|
|
}
|
|
|
|
|
2019-07-17 21:29:21 +00:00
|
|
|
/**
|
|
|
|
* Indicates whether a playlist is enabled and has content which can be scheduled by an AutoDJ scheduler.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function isPlayable(): bool
|
|
|
|
{
|
2019-07-19 23:55:44 +00:00
|
|
|
return ($this->is_enabled
|
|
|
|
&& (self::SOURCE_SONGS !== $this->source || $this->media_items->count() > 0)
|
|
|
|
&& !$this->backendInterruptOtherSongs()
|
|
|
|
&& !$this->backendMerge()
|
|
|
|
&& !$this->backendLoopPlaylistOnce());
|
2019-07-17 21:29:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function getBackendOptions(): array
|
|
|
|
{
|
|
|
|
return explode(',', $this->backend_options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $backend_options
|
|
|
|
*/
|
|
|
|
public function setBackendOptions($backend_options): void
|
|
|
|
{
|
|
|
|
$this->backend_options = implode(',', (array)$backend_options);
|
|
|
|
}
|
|
|
|
|
2019-11-02 18:50:21 +00:00
|
|
|
public function backendInterruptOtherSongs(): bool
|
|
|
|
{
|
|
|
|
$backend_options = $this->getBackendOptions();
|
|
|
|
return in_array(self::OPTION_INTERRUPT_OTHER_SONGS, $backend_options, true);
|
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function backendMerge(): bool
|
|
|
|
{
|
|
|
|
$backend_options = $this->getBackendOptions();
|
|
|
|
return in_array(self::OPTION_MERGE, $backend_options, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function backendLoopPlaylistOnce(): bool
|
|
|
|
{
|
|
|
|
$backend_options = $this->getBackendOptions();
|
|
|
|
return in_array(self::OPTION_LOOP_PLAYLIST_ONCE, $backend_options, true);
|
|
|
|
}
|
|
|
|
|
2019-11-02 18:50:21 +00:00
|
|
|
public function backendPlaySingleTrack(): bool
|
|
|
|
{
|
|
|
|
$backend_options = $this->getBackendOptions();
|
|
|
|
return in_array(self::OPTION_PLAY_SINGLE_TRACK, $backend_options, true);
|
|
|
|
}
|
|
|
|
|
2019-07-17 21:29:21 +00:00
|
|
|
/**
|
|
|
|
* Parent function for determining whether a playlist of any type can be played by the AutoDJ.
|
|
|
|
*
|
|
|
|
* @param Chronos|null $now
|
|
|
|
* @param array $recentSongHistory
|
2019-09-20 16:44:38 +00:00
|
|
|
*
|
2019-07-17 21:29:21 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function shouldPlayNow(Chronos $now = null, array $recentSongHistory = []): bool
|
|
|
|
{
|
|
|
|
if (null === $now) {
|
2019-09-04 18:00:51 +00:00
|
|
|
$now = Chronos::now(new DateTimeZone($this->getStation()->getTimezone()));
|
2019-07-17 21:29:21 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 16:46:39 +00:00
|
|
|
if (!$this->isScheduledToPlayNow($now)) {
|
|
|
|
return false;
|
2019-11-02 18:50:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
switch ($this->type) {
|
2019-07-17 21:29:21 +00:00
|
|
|
case self::TYPE_ONCE_PER_HOUR:
|
2019-07-19 23:27:24 +00:00
|
|
|
return $this->shouldPlayNowPerHour($now);
|
2019-07-17 21:29:21 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case self::TYPE_ONCE_PER_X_SONGS:
|
|
|
|
return !$this->wasPlayedRecently($recentSongHistory, $this->getPlayPerSongs());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case self::TYPE_ONCE_PER_X_MINUTES:
|
2019-07-19 23:27:24 +00:00
|
|
|
return $this->shouldPlayNowPerMinute($now);
|
2019-07-17 21:29:21 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case self::TYPE_ADVANCED:
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case self::TYPE_DEFAULT:
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-18 16:46:39 +00:00
|
|
|
protected function isScheduledToPlayNow(Chronos $now): bool
|
2019-09-04 18:00:51 +00:00
|
|
|
{
|
2020-04-18 16:46:39 +00:00
|
|
|
if (0 === $this->schedule_items->count()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->schedule_items as $scheduleItem) {
|
|
|
|
/** @var StationSchedule $scheduleItem */
|
|
|
|
if ($scheduleItem->shouldPlayNow($now)) {
|
|
|
|
$startTime = $scheduleItem->getStartTime();
|
|
|
|
$endTime = $scheduleItem->getEndTime();
|
|
|
|
|
|
|
|
if (
|
|
|
|
$startTime !== $endTime
|
2020-04-19 01:14:07 +00:00
|
|
|
|| ($startTime === $endTime && !$this->wasPlayedInLastXMinutes($now, 30))
|
2020-04-18 16:46:39 +00:00
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2019-09-04 18:00:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function shouldPlayNowPerHour(Chronos $now): bool
|
2019-07-17 21:29:21 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
$current_minute = (int)$now->minute;
|
|
|
|
$target_minute = $this->getPlayPerHourMinute();
|
2019-07-17 21:29:21 +00:00
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
if ($current_minute < $target_minute) {
|
2020-02-24 20:39:42 +00:00
|
|
|
$target_time = $now->subHour()->minute($target_minute);
|
2019-09-04 18:00:51 +00:00
|
|
|
} else {
|
|
|
|
$target_time = $now->minute($target_minute);
|
2019-07-17 21:29:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
$playlist_diff = $target_time->diffInMinutes($now, false);
|
|
|
|
|
|
|
|
if ($playlist_diff < 0 || $playlist_diff > 15) {
|
2019-07-17 21:29:21 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
return !$this->wasPlayedInLastXMinutes($now, 30);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPlayPerHourMinute(): int
|
|
|
|
{
|
|
|
|
return $this->play_per_hour_minute;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setPlayPerHourMinute(int $play_per_hour_minute): void
|
|
|
|
{
|
|
|
|
if ($play_per_hour_minute > 59 || $play_per_hour_minute < 0) {
|
|
|
|
$play_per_hour_minute = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->play_per_hour_minute = $play_per_hour_minute;
|
|
|
|
}
|
|
|
|
|
2019-12-12 04:04:44 +00:00
|
|
|
public function wasPlayedInLastXMinutes(Chronos $now, int $minutes): bool
|
2019-09-04 18:00:51 +00:00
|
|
|
{
|
|
|
|
if (0 === $this->played_at) {
|
2019-07-17 21:29:21 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-24 20:39:42 +00:00
|
|
|
$threshold = $now->subMinutes($minutes)->getTimestamp();
|
2019-09-04 18:00:51 +00:00
|
|
|
return ($this->played_at > $threshold);
|
2019-07-17 21:29:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
protected function wasPlayedRecently(array $songHistoryEntries = [], $length = 15): bool
|
2019-07-17 21:29:21 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
if (empty($songHistoryEntries)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if already played
|
|
|
|
$relevant_song_history = array_slice($songHistoryEntries, 0, $length);
|
|
|
|
|
|
|
|
$was_played = false;
|
|
|
|
foreach ($relevant_song_history as $sh_row) {
|
|
|
|
if ((int)$sh_row['playlist_id'] === $this->id) {
|
|
|
|
$was_played = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reset($songHistoryEntries);
|
|
|
|
return $was_played;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPlayPerSongs(): int
|
|
|
|
{
|
|
|
|
return $this->play_per_songs;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setPlayPerSongs(int $play_per_songs): void
|
|
|
|
{
|
|
|
|
$this->play_per_songs = $play_per_songs;
|
2019-07-17 21:29:21 +00:00
|
|
|
}
|
|
|
|
|
2019-07-20 21:04:42 +00:00
|
|
|
protected function shouldPlayNowPerMinute(Chronos $now): bool
|
2019-07-17 21:29:21 +00:00
|
|
|
{
|
2019-07-19 23:27:24 +00:00
|
|
|
return !$this->wasPlayedInLastXMinutes($now, $this->getPlayPerMinutes());
|
2019-07-17 21:29:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function getPlayPerMinutes(): int
|
2019-07-17 21:29:21 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
return $this->play_per_minutes;
|
|
|
|
}
|
2019-07-17 21:29:21 +00:00
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
public function setPlayPerMinutes(int $play_per_minutes): void
|
|
|
|
{
|
|
|
|
$this->play_per_minutes = $play_per_minutes;
|
|
|
|
}
|
2019-07-17 21:29:21 +00:00
|
|
|
|
2017-05-13 04:15:36 +00:00
|
|
|
/**
|
|
|
|
* Export the playlist into a reusable format.
|
|
|
|
*
|
|
|
|
* @param string $file_format
|
|
|
|
* @param bool $absolute_paths
|
2019-02-25 06:43:00 +00:00
|
|
|
* @param bool $with_annotations
|
2019-09-20 16:44:38 +00:00
|
|
|
*
|
2017-05-13 04:15:36 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2019-02-25 06:43:00 +00:00
|
|
|
public function export($file_format = 'pls', $absolute_paths = false, $with_annotations = false): string
|
2017-05-13 04:15:36 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
$media_path = ($absolute_paths) ? $this->station->getRadioMediaDir() . '/' : '';
|
2017-05-13 04:15:36 +00:00
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
switch ($file_format) {
|
2017-05-13 16:16:57 +00:00
|
|
|
case 'm3u':
|
|
|
|
$playlist_file = [];
|
2018-04-29 23:48:48 +00:00
|
|
|
foreach ($this->media_items as $media_item) {
|
|
|
|
$media_file = $media_item->getMedia();
|
2017-08-17 18:28:48 +00:00
|
|
|
$media_file_path = $media_path . $media_file->getPath();
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file[] = $media_file_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
return implode("\n", $playlist_file);
|
2017-08-24 01:30:43 +00:00
|
|
|
break;
|
2017-05-13 16:16:57 +00:00
|
|
|
|
2017-05-13 04:15:36 +00:00
|
|
|
case 'pls':
|
|
|
|
default:
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file = [
|
|
|
|
'[playlist]',
|
|
|
|
];
|
|
|
|
|
|
|
|
$i = 0;
|
2019-09-04 18:00:51 +00:00
|
|
|
foreach ($this->media_items as $media_item) {
|
2017-05-13 16:16:57 +00:00
|
|
|
$i++;
|
|
|
|
|
2018-04-29 23:48:48 +00:00
|
|
|
$media_file = $media_item->getMedia();
|
2017-08-17 18:28:48 +00:00
|
|
|
$media_file_path = $media_path . $media_file->getPath();
|
2019-09-04 18:00:51 +00:00
|
|
|
$playlist_file[] = 'File' . $i . '=' . $media_file_path;
|
|
|
|
$playlist_file[] = 'Title' . $i . '=' . $media_file->getArtist() . ' - ' . $media_file->getTitle();
|
|
|
|
$playlist_file[] = 'Length' . $i . '=' . $media_file->getLength();
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file[] = '';
|
|
|
|
}
|
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
$playlist_file[] = 'NumberOfEntries=' . $i;
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file[] = 'Version=2';
|
|
|
|
|
2017-05-13 04:15:36 +00:00
|
|
|
return implode("\n", $playlist_file);
|
2017-08-24 01:30:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-08-04 22:05:14 +00:00
|
|
|
}
|