*/ protected $casts = [ 'id' => 'integer', 'guid' => 'string', 'actor_id' => 'integer', 'handle' => 'string', 'title' => 'string', 'description_markdown' => 'string', 'description_html' => 'string', 'cover_id' => 'int', 'banner_id' => '?int', 'language_code' => 'string', 'category_id' => 'integer', 'parental_advisory' => '?string', 'publisher' => '?string', 'owner_name' => 'string', 'owner_email' => 'string', 'type' => 'string', 'copyright' => '?string', 'episode_description_footer_markdown' => '?string', 'episode_description_footer_html' => '?string', 'is_blocked' => 'boolean', 'is_completed' => 'boolean', 'is_locked' => 'boolean', 'is_premium_by_default' => 'boolean', 'imported_feed_url' => '?string', 'new_feed_url' => '?string', 'location_name' => '?string', 'location_geo' => '?string', 'location_osm' => '?string', 'payment_pointer' => '?string', 'custom_rss' => '?json-array', 'is_published_on_hubs' => 'boolean', 'partner_id' => '?string', 'partner_link_url' => '?string', 'partner_image_url' => '?string', 'created_by' => 'integer', 'updated_by' => 'integer', ]; public function getAtHandle(): string { return '@' . $this->handle; } public function getActor(): ?Actor { if ($this->actor_id === 0) { throw new RuntimeException('Podcast must have an actor_id before getting actor.'); } if (! $this->actor instanceof Actor) { $this->actor = model(ActorModel::class, false) ->getActorById($this->actor_id); } return $this->actor; } public function setCover(UploadedFile | File $file = null): self { if (! $file instanceof File || ($file instanceof UploadedFile && ! $file->isValid())) { return $this; } if (array_key_exists('cover_id', $this->attributes) && $this->attributes['cover_id'] !== null) { $this->getCover() ->setFile($file); $this->getCover() ->updated_by = (int) user_id(); (new MediaModel('image'))->updateMedia($this->getCover()); } else { $cover = new Image([ 'file_key' => 'podcasts/' . $this->attributes['handle'] . '/cover.' . $file->getExtension(), 'sizes' => config('Images') ->podcastCoverSizes, 'uploaded_by' => user_id(), 'updated_by' => user_id(), ]); $cover->setFile($file); $this->attributes['cover_id'] = (new MediaModel('image'))->saveMedia($cover); } return $this; } public function getCover(): Image { if (! $this->cover instanceof Image) { $this->cover = (new MediaModel('image'))->getMediaById($this->cover_id); } return $this->cover; } public function setBanner(UploadedFile | File $file = null): self { if (! $file instanceof File || ($file instanceof UploadedFile && ! $file->isValid())) { return $this; } if (array_key_exists('banner_id', $this->attributes) && $this->attributes['banner_id'] !== null) { $this->getBanner() ->setFile($file); $this->getBanner() ->updated_by = (int) user_id(); (new MediaModel('image'))->updateMedia($this->getBanner()); } else { $banner = new Image([ 'file_key' => 'podcasts/' . $this->attributes['handle'] . '/banner.' . $file->getExtension(), 'sizes' => config('Images') ->podcastBannerSizes, 'uploaded_by' => user_id(), 'updated_by' => user_id(), ]); $banner->setFile($file); $this->attributes['banner_id'] = (new MediaModel('image'))->saveMedia($banner); } return $this; } public function getBanner(): ?Image { if ($this->banner_id === null) { return null; } if (! $this->banner instanceof Image) { $this->banner = (new MediaModel('image'))->getMediaById($this->banner_id); } return $this->banner; } public function getLink(): string { return url_to('podcast-activity', $this->attributes['handle']); } public function getFeedUrl(): string { return url_to('podcast-rss-feed', $this->attributes['handle']); } /** * Returns the podcast's episodes * * @return Episode[] */ public function getEpisodes(): array { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting episodes.'); } if ($this->episodes === null) { $this->episodes = (new EpisodeModel())->getPodcastEpisodes($this->id, $this->type); } return $this->episodes; } /** * Returns the podcast's episodes count */ public function getEpisodesCount(): int|string { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting number of episodes.'); } return (new EpisodeModel())->getPodcastEpisodesCount($this->id); } /** * Returns the podcast's persons * * @return Person[] */ public function getPersons(): array { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting persons.'); } if ($this->persons === null) { $this->persons = (new PersonModel())->getPodcastPersons($this->id); } return $this->persons; } /** * Returns the podcast category entity */ public function getCategory(): ?Category { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting category.'); } if (! $this->category instanceof Category) { $this->category = (new CategoryModel())->getCategoryById($this->category_id); } return $this->category; } /** * Returns all podcast subscriptions * * @return Subscription[] */ public function getSubscriptions(): array { if ($this->id === null) { throw new RuntimeException('Podcasts must be created before getting subscriptions.'); } if ($this->subscriptions === null) { $this->subscriptions = (new SubscriptionModel())->getPodcastSubscriptions($this->id); } return $this->subscriptions; } /** * Returns all podcast contributors * * @return User[] */ public function getContributors(): array { if ($this->id === null) { throw new RuntimeException('Podcasts must be created before getting contributors.'); } if ($this->contributors === null) { $this->contributors = (new UserModel())->getPodcastContributors($this->id); } return $this->contributors; } public function setDescriptionMarkdown(string $descriptionMarkdown): static { $config = [ 'html_input' => 'escape', 'allow_unsafe_links' => false, ]; $environment = new Environment($config); $environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new AutolinkExtension()); $environment->addExtension(new SmartPunctExtension()); $environment->addExtension(new DisallowedRawHtmlExtension()); $converter = new MarkdownConverter($environment); $this->attributes['description_markdown'] = $descriptionMarkdown; $this->attributes['description_html'] = $converter->convert($descriptionMarkdown); return $this; } public function setEpisodeDescriptionFooterMarkdown(?string $episodeDescriptionFooterMarkdown = null): static { if ($episodeDescriptionFooterMarkdown === null || $episodeDescriptionFooterMarkdown === '') { $this->attributes[ 'episode_description_footer_markdown' ] = null; $this->attributes[ 'episode_description_footer_html' ] = null; return $this; } $config = [ 'html_input' => 'escape', 'allow_unsafe_links' => false, ]; $environment = new Environment($config); $environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new AutolinkExtension()); $environment->addExtension(new SmartPunctExtension()); $environment->addExtension(new DisallowedRawHtmlExtension()); $converter = new MarkdownConverter($environment); $this->attributes[ 'episode_description_footer_markdown' ] = $episodeDescriptionFooterMarkdown; $this->attributes[ 'episode_description_footer_html' ] = $converter->convert($episodeDescriptionFooterMarkdown); return $this; } public function getDescription(): string { if ($this->description === null) { $this->description = trim( (string) preg_replace('~\s+~', ' ', strip_tags((string) $this->attributes['description_html'])), ); } return $this->description; } public function getPublicationStatus(): string { if ($this->publication_status === null) { if (! $this->published_at instanceof Time) { $this->publication_status = 'not_published'; } elseif ($this->published_at->isBefore(Time::now())) { $this->publication_status = 'published'; } else { $this->publication_status = 'scheduled'; } } return $this->publication_status; } /** * Returns the podcast's podcasting platform links * * @return Platform[] */ public function getPodcastingPlatforms(): array { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting podcasting platform links.'); } if ($this->podcasting_platforms === null) { $this->podcasting_platforms = (new PlatformModel())->getPodcastPlatforms($this->id, 'podcasting'); } return $this->podcasting_platforms; } /** * Returns the podcast's social platform links * * @return Platform[] */ public function getSocialPlatforms(): array { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting social platform links.'); } if ($this->social_platforms === null) { $this->social_platforms = (new PlatformModel())->getPodcastPlatforms($this->id, 'social'); } return $this->social_platforms; } /** * Returns the podcast's funding platform links * * @return Platform[] */ public function getFundingPlatforms(): array { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting funding platform links.'); } if ($this->funding_platforms === null) { $this->funding_platforms = (new PlatformModel())->getPodcastPlatforms($this->id, 'funding'); } return $this->funding_platforms; } /** * @return Category[] */ public function getOtherCategories(): array { if ($this->id === null) { throw new RuntimeException('Podcast must be created before getting other categories.'); } if ($this->other_categories === null) { $this->other_categories = (new CategoryModel())->getPodcastCategories($this->id); } return $this->other_categories; } /** * @return int[]|string[] */ public function getOtherCategoriesIds(): array { if ($this->other_categories_ids === null) { $this->other_categories_ids = array_column($this->getOtherCategories(), 'id'); } return $this->other_categories_ids; } /** * Saves the location name and fetches OpenStreetMap info */ public function setLocation(?Location $location = null): static { if (! $location instanceof Location) { $this->attributes['location_name'] = null; $this->attributes['location_geo'] = null; $this->attributes['location_osm'] = null; return $this; } if ( ! isset($this->attributes['location_name']) || $this->attributes['location_name'] !== $location->name ) { $location->fetchOsmLocation(); $this->attributes['location_name'] = $location->name; $this->attributes['location_geo'] = $location->geo; $this->attributes['location_osm'] = $location->osm; } return $this; } public function getLocation(): ?Location { if ($this->location_name === null) { return null; } if (! $this->location instanceof Location) { $this->location = new Location($this->location_name, $this->location_geo, $this->location_osm); } return $this->location; } /** * Get custom rss tag as XML String */ public function getCustomRssString(): string { if ($this->attributes['custom_rss'] === null) { return ''; } helper('rss'); $xmlNode = (new SimpleRSSElement( '', ))->addChild('channel'); array_to_rss([ 'elements' => $this->custom_rss, ], $xmlNode); return str_replace(['', ''], '', (string) $xmlNode->asXML()); } /** * Saves custom rss tag into json */ public function setCustomRssString(string $customRssString): static { if ($customRssString === '') { $this->attributes['custom_rss'] = null; return $this; } helper('rss'); $customRssArray = rss_to_array( simplexml_load_string( '' . $customRssString . '', ), )['elements'][0]; if (array_key_exists('elements', $customRssArray)) { $this->attributes['custom_rss'] = json_encode($customRssArray['elements']); } else { $this->attributes['custom_rss'] = null; } return $this; } public function getIsPremium(): bool { // podcast is premium if at least one of its episodes is set as premium return (new EpisodeModel())->doesPodcastHavePremiumEpisodes($this->id); } }