*/ #[ ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistMedia::class, fetch: 'EXTRA_LAZY'), ORM\OrderBy(['weight' => 'ASC']) ] protected Collection $media_items; /** @var Collection */ #[ ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistFolder::class, fetch: 'EXTRA_LAZY') ] protected Collection $folders; /** @var Collection */ #[ OA\Property(type: "array", items: new OA\Items()), ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationSchedule::class, fetch: 'EXTRA_LAZY'), DeepNormalize(true), Serializer\MaxDepth(1) ] protected Collection $schedule_items; public function __construct(Station $station) { $this->station = $station; $this->type = PlaylistTypes::default()->value; $this->source = PlaylistSources::Songs->value; $this->order = PlaylistOrders::Shuffle->value; $this->remote_type = PlaylistRemoteTypes::Stream->value; $this->media_items = new ArrayCollection(); $this->folders = new ArrayCollection(); $this->schedule_items = new ArrayCollection(); } public function getStation(): Station { return $this->station; } public function setStation(Station $station): void { $this->station = $station; } public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $this->truncateString($name, 200); } public function getShortName(): string { return self::generateShortName($this->name); } public function getType(): string { return $this->type; } public function getTypeEnum(): PlaylistTypes { return PlaylistTypes::from($this->type); } public function setType(string $type): void { if (null === PlaylistTypes::tryFrom($type)) { throw new InvalidArgumentException('Invalid playlist type.'); } $this->type = $type; } public function getSource(): string { return $this->source; } public function getSourceEnum(): PlaylistSources { return PlaylistSources::from($this->source); } public function setSource(string $source): void { if (null === PlaylistSources::tryFrom($source)) { throw new InvalidArgumentException('Invalid playlist source.'); } $this->source = $source; } public function getOrder(): string { return $this->order; } public function getOrderEnum(): PlaylistOrders { return PlaylistOrders::from($this->order); } public function setOrder(string $order): void { if (null === PlaylistOrders::tryFrom($order)) { throw new InvalidArgumentException('Invalid playlist order.'); } $this->order = $order; } public function getRemoteUrl(): ?string { return $this->remote_url; } public function setRemoteUrl(?string $remote_url): void { $this->remote_url = $remote_url; } public function getRemoteType(): ?string { return $this->remote_type; } public function getRemoteTypeEnum(): ?PlaylistRemoteTypes { return PlaylistRemoteTypes::tryFrom($this->remote_type ?? ''); } public function setRemoteType(?string $remote_type): void { if (null !== $remote_type && null === PlaylistRemoteTypes::tryFrom($remote_type)) { throw new InvalidArgumentException('Invalid playlist remote type.'); } $this->remote_type = $remote_type; } public function getRemoteBuffer(): int { return $this->remote_buffer; } public function setRemoteBuffer(int $remote_buffer): void { $this->remote_buffer = $remote_buffer; } public function getIsEnabled(): bool { return $this->is_enabled; } public function setIsEnabled(bool $is_enabled): void { $this->is_enabled = $is_enabled; } public function getIsJingle(): bool { return $this->is_jingle; } public function setIsJingle(bool $is_jingle): void { $this->is_jingle = $is_jingle; } public function getWeight(): int { if ($this->weight < 1) { return self::DEFAULT_WEIGHT; } return $this->weight; } public function setWeight(int $weight): void { $this->weight = $weight; } public function getIncludeInRequests(): bool { return $this->include_in_requests; } public function setIncludeInRequests(bool $include_in_requests): void { $this->include_in_requests = $include_in_requests; } public function getIncludeInOnDemand(): bool { return $this->include_in_on_demand; } public function setIncludeInOnDemand(bool $include_in_on_demand): void { $this->include_in_on_demand = $include_in_on_demand; } /** * Indicates whether this playlist can be used as a valid source of requestable media. */ public function isRequestable(): bool { return ($this->is_enabled && $this->include_in_requests); } public function getIncludeInAutomation(): bool { return $this->include_in_automation; } public function setIncludeInAutomation(bool $include_in_automation): void { $this->include_in_automation = $include_in_automation; } public function getAvoidDuplicates(): bool { return $this->avoid_duplicates; } public function setAvoidDuplicates(bool $avoid_duplicates): void { $this->avoid_duplicates = $avoid_duplicates; } public function getPlayedAt(): int { return $this->played_at; } public function setPlayedAt(int $played_at): void { $this->played_at = $played_at; } public function getQueueResetAt(): int { return $this->queue_reset_at; } public function setQueueResetAt(int $queue_reset_at): void { $this->queue_reset_at = $queue_reset_at; } /** * @return Collection */ public function getMediaItems(): Collection { return $this->media_items; } /** * @return Collection */ public function getFolders(): Collection { return $this->folders; } /** * @return Collection */ public function getScheduleItems(): Collection { return $this->schedule_items; } /** * Indicates whether a playlist is enabled and has content which can be scheduled by an AutoDJ scheduler. * * @param bool $interrupting Whether determining "playability" for an interrupting queue or a regular one. */ public function isPlayable(bool $interrupting = false): bool { if (!$this->is_enabled) { return false; } if ($interrupting !== $this->backendInterruptOtherSongs()) { return false; } if (PlaylistSources::Songs === $this->getSourceEnum()) { return $this->media_items->count() > 0; } // Remote stream playlists aren't supported by the AzuraCast AutoDJ. return PlaylistRemoteTypes::Playlist === $this->getRemoteTypeEnum(); } /** * @return string[] */ public function getBackendOptions(): array { return explode(',', $this->backend_options ?? ''); } /** * @param array $backend_options */ public function setBackendOptions(array $backend_options): void { $this->backend_options = implode(',', $backend_options); } public function backendInterruptOtherSongs(): bool { $backend_options = $this->getBackendOptions(); return in_array(self::OPTION_INTERRUPT_OTHER_SONGS, $backend_options, true); } public function backendMerge(): bool { $backend_options = $this->getBackendOptions(); return in_array(self::OPTION_MERGE, $backend_options, true); } public function backendPlaySingleTrack(): bool { $backend_options = $this->getBackendOptions(); return in_array(self::OPTION_PLAY_SINGLE_TRACK, $backend_options, true); } 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; } public function getPlayPerSongs(): int { return $this->play_per_songs; } public function setPlayPerSongs(int $play_per_songs): void { $this->play_per_songs = $play_per_songs; } public function getPlayPerMinutes(): int { return $this->play_per_minutes; } public function setPlayPerMinutes( int $play_per_minutes ): void { $this->play_per_minutes = $play_per_minutes; } public function __clone() { $this->played_at = 0; $this->queue_reset_at = 0; } public function __toString(): string { return $this->getStation() . ' Playlist: ' . $this->getName(); } public static function generateShortName(string $str): string { $str = File::sanitizeFileName($str); return (is_numeric($str)) ? 'playlist_' . $str : $str; } }