Loading app/Database/Migrations/2024-12-10-170000_drop_deprecated_podcasts_fields.php 0 → 100644 +86 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); /** * Class AddPodcastsMediumField adds medium field to podcast table in database * * @copyright 2020 Ad Aures * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ */ namespace App\Database\Migrations; use Override; class DropDeprecatedPodcastsFields extends BaseMigration { #[Override] public function up(): void { $this->forge->dropColumn( 'podcasts', 'episode_description_footer_markdown,episode_description_footer_html,is_owner_email_removed_from_feed,medium,payment_pointer,verify_txt,custom_rss,partner_id,partner_link_url,partner_image_url' ); } #[Override] public function down(): void { $fields = [ 'episode_description_footer_markdown' => [ 'type' => 'TEXT', 'null' => true, ], 'episode_description_footer_html' => [ 'type' => 'TEXT', 'null' => true, ], 'is_owner_email_removed_from_feed' => [ 'type' => 'BOOLEAN', 'null' => false, 'default' => 0, 'after' => 'owner_email', ], 'medium' => [ 'type' => "ENUM('podcast','music','audiobook')", 'null' => false, 'default' => 'podcast', 'after' => 'type', ], 'payment_pointer' => [ 'type' => 'VARCHAR', 'constraint' => 128, 'comment' => 'Wallet address for Web Monetization payments', 'null' => true, ], 'verify_txt' => [ 'type' => 'TEXT', 'null' => true, 'after' => 'location_osm', ], 'custom_rss' => [ 'type' => 'JSON', 'null' => true, ], 'partner_id' => [ 'type' => 'VARCHAR', 'constraint' => 32, 'null' => true, ], 'partner_link_url' => [ 'type' => 'VARCHAR', 'constraint' => 512, 'null' => true, ], 'partner_image_url' => [ 'type' => 'VARCHAR', 'constraint' => 512, 'null' => true, ], ]; $this->forge->addColumn('podcasts', $fields); } } app/Database/Migrations/2024-12-10-180000_drop_deprecated_episodes_fields.php 0 → 100644 +37 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); /** * Class AddPodcastsMediumField adds medium field to podcast table in database * * @copyright 2020 Ad Aures * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ */ namespace App\Database\Migrations; use Override; class DropDeprecatedEpisodesFields extends BaseMigration { #[Override] public function up(): void { $this->forge->dropColumn('episodes', 'custom_rss'); } #[Override] public function down(): void { $fields = [ 'custom_rss' => [ 'type' => 'JSON', 'null' => true, ], ]; $this->forge->addColumn('episodes', $fields); } } app/Entities/Episode.php +34 −168 Original line number Diff line number Diff line Loading @@ -11,7 +11,6 @@ declare(strict_types=1); namespace App\Entities; use App\Entities\Clip\Soundbite; use App\Libraries\SimpleRSSElement; use App\Models\ClipModel; use App\Models\EpisodeCommentModel; use App\Models\EpisodeModel; Loading @@ -29,14 +28,13 @@ use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension; use League\CommonMark\Extension\SmartPunct\SmartPunctExtension; use League\CommonMark\MarkdownConverter; use Modules\Analytics\OP3; use Modules\Media\Entities\Audio; use Modules\Media\Entities\Chapters; use Modules\Media\Entities\Image; use Modules\Media\Entities\Transcript; use Modules\Media\Models\MediaModel; use Override; use RuntimeException; use SimpleXMLElement; /** * @property int $id Loading Loading @@ -73,8 +71,6 @@ use SimpleXMLElement; * @property string|null $location_name * @property string|null $location_geo * @property string|null $location_osm * @property array|null $custom_rss * @property string $custom_rss_string * @property bool $is_published_on_hubs * @property int $posts_count * @property int $comments_count Loading @@ -94,19 +90,19 @@ use SimpleXMLElement; */ class Episode extends Entity { protected Podcast $podcast; public string $link = ''; protected string $link; public string $audio_url = ''; protected ?Audio $audio = null; public string $audio_web_url = ''; protected string $audio_url; public string $audio_opengraph_url = ''; protected string $audio_web_url; protected Podcast $podcast; protected string $audio_opengraph_url; protected ?Audio $audio = null; protected string $embed_url; protected string $embed_url = ''; protected ?Image $cover = null; Loading Loading @@ -140,8 +136,6 @@ class Episode extends Entity protected ?Location $location = null; protected string $custom_rss_string; protected ?string $publication_status = null; /** Loading Loading @@ -176,7 +170,6 @@ class Episode extends Entity 'location_name' => '?string', 'location_geo' => '?string', 'location_osm' => '?string', 'custom_rss' => '?json-array', 'is_published_on_hubs' => 'boolean', 'posts_count' => 'integer', 'comments_count' => 'integer', Loading @@ -185,6 +178,31 @@ class Episode extends Entity 'updated_by' => 'integer', ]; /** * @param array<string, mixed> $data */ #[Override] public function injectRawData(array $data): static { parent::injectRawData($data); $this->link = url_to('episode', esc($this->getPodcast()->handle, 'url'), esc($this->attributes['slug'], 'url')); $this->audio_url = url_to( 'episode-audio', $this->getPodcast() ->handle, $this->slug, $this->getAudio() ->file_extension ); $this->audio_opengraph_url = $this->audio_url . '?_from=-+Open+Graph+-'; $this->audio_web_url = $this->audio_url . '?_from=-+Website+-'; return $this; } public function setCover(UploadedFile | File $file = null): self { if (! $file instanceof File || ($file instanceof UploadedFile && ! $file->isValid())) { Loading Loading @@ -342,40 +360,6 @@ class Episode extends Entity return $this->chapters; } public function getAudioUrl(): string { $audioURL = url_to( 'episode-audio', $this->getPodcast() ->handle, $this->slug, $this->getAudio() ->file_extension ); // Wrap episode url with OP3 if episode is public and OP3 is enabled on this podcast if (! $this->is_premium && service('settings')->get( 'Analytics.enableOP3', 'podcast:' . $this->podcast_id )) { $op3 = new OP3(config('Analytics')->OP3); return $op3->wrap($audioURL, $this); } return $audioURL; } public function getAudioWebUrl(): string { return $this->getAudioUrl() . '?_from=-+Website+-'; } public function getAudioOpengraphUrl(): string { return $this->getAudioUrl() . '?_from=-+Open+Graph+-'; } /** * Gets transcript url from transcript file uri if it exists or returns the transcript_remote_url which can be null. */ Loading Loading @@ -468,11 +452,6 @@ class Episode extends Entity return $this->comments; } public function getLink(): string { return url_to('episode', esc($this->getPodcast()->handle), esc($this->attributes['slug'])); } public function getEmbedUrl(string $theme = null): string { return $theme Loading @@ -482,7 +461,7 @@ class Episode extends Entity public function setGuid(?string $guid = null): static { $this->attributes['guid'] = $guid ?? $this->getLink(); $this->attributes['guid'] = $guid ?? $this->link; return $this; } Loading Loading @@ -513,34 +492,6 @@ class Episode extends Entity return $this; } public function getDescriptionHtml(?string $serviceSlug = null): string { $descriptionHtml = ''; if ( $this->getPodcast() ->partner_id !== null && $this->getPodcast() ->partner_link_url !== null && $this->getPodcast() ->partner_image_url !== null ) { $descriptionHtml .= "<div><a href=\"{$this->getPartnerLink( $serviceSlug, )}\" rel=\"sponsored noopener noreferrer\" target=\"_blank\"><img src=\"{$this->getPartnerImageUrl( $serviceSlug, )}\" alt=\"Partner image\" /></a></div>"; } $descriptionHtml .= $this->attributes['description_html']; if ($this->getPodcast()->episode_description_footer_html) { $descriptionHtml .= "<footer>{$this->getPodcast() ->episode_description_footer_html}</footer>"; } return $descriptionHtml; } public function getDescription(): string { if ($this->description === null) { Loading Loading @@ -609,91 +560,6 @@ class Episode extends Entity return $this->location; } /** * Get custom rss tag as XML String */ public function getCustomRssString(): string { if ($this->custom_rss === null) { return ''; } helper('rss'); $xmlNode = (new SimpleRSSElement( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>', )) ->addChild('channel') ->addChild('item'); array_to_rss([ 'elements' => $this->custom_rss, ], $xmlNode); return str_replace(['<item>', '</item>'], '', (string) $xmlNode->asXML()); } /** * Saves custom rss tag into json */ public function setCustomRssString(?string $customRssString = null): static { if ($customRssString === '') { $this->attributes['custom_rss'] = null; return $this; } helper('rss'); $customXML = simplexml_load_string( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel><item>' . $customRssString . '</item></channel></rss>', ); if (! $customXML instanceof SimpleXMLElement) { // TODO: Failed to parse custom xml, should return error? return $this; } $customRssArray = rss_to_array($customXML)['elements'][0]['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 getPartnerLink(?string $serviceSlug = null): string { $partnerLink = rtrim((string) $this->getPodcast()->partner_link_url, '/') . '?pid=' . $this->getPodcast() ->partner_id . '&guid=' . urlencode((string) $this->attributes['guid']); if ($serviceSlug !== null) { $partnerLink .= '&_from=' . $serviceSlug; } return $partnerLink; } public function getPartnerImageUrl(string $serviceSlug = null): string { return rtrim((string) $this->getPodcast()->partner_image_url, '/') . '?pid=' . $this->getPodcast() ->partner_id . '&guid=' . urlencode((string) $this->attributes['guid']) . ($serviceSlug !== null ? '&_from=' . $serviceSlug : ''); } public function getPreviewLink(): string { if ($this->preview_id === null) { Loading app/Entities/Podcast.php +29 −148 Original line number Diff line number Diff line Loading @@ -10,7 +10,6 @@ declare(strict_types=1); namespace App\Entities; use App\Libraries\SimpleRSSElement; use App\Models\ActorModel; use App\Models\CategoryModel; use App\Models\EpisodeModel; Loading Loading @@ -62,12 +61,8 @@ use RuntimeException; * @property string|null $publisher * @property string $owner_name * @property string $owner_email * @property bool $is_owner_email_removed_from_feed * @property string $type * @property string $medium * @property string|null $copyright * @property string|null $episode_description_footer_markdown * @property string|null $episode_description_footer_html * @property bool $is_blocked * @property bool $is_completed * @property bool $is_locked Loading @@ -77,15 +72,7 @@ use RuntimeException; * @property string|null $location_name * @property string|null $location_geo * @property string|null $location_osm * @property string|null $payment_pointer * @property array|null $custom_rss * @property bool $is_op3_enabled * @property string $op3_url * @property string $custom_rss_string * @property bool $is_published_on_hubs * @property string|null $partner_id * @property string|null $partner_link_url * @property string|null $partner_image_url * @property int $created_by * @property int $updated_by * @property string $publication_status Loading Loading @@ -166,8 +153,6 @@ class Podcast extends Entity protected ?Location $location = null; protected string $custom_rss_string; protected ?string $publication_status = null; /** Loading Loading @@ -195,12 +180,8 @@ class Podcast extends Entity 'publisher' => '?string', 'owner_name' => 'string', 'owner_email' => 'string', 'is_owner_email_removed_from_feed' => 'boolean', 'type' => 'string', 'medium' => 'string', 'copyright' => '?string', 'episode_description_footer_markdown' => '?string', 'episode_description_footer_html' => '?string', 'is_blocked' => 'boolean', 'is_completed' => 'boolean', 'is_locked' => 'boolean', Loading @@ -210,12 +191,7 @@ class Podcast extends Entity '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', ]; Loading Loading @@ -454,42 +430,6 @@ class Podcast extends Entity 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) { Loading Loading @@ -638,68 +578,9 @@ class Podcast extends Entity 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( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>', ))->addChild('channel'); array_to_rss([ 'elements' => $this->custom_rss, ], $xmlNode); return str_replace(['<channel>', '</channel>'], '', (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( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel>' . $customRssString . '</channel></rss>', ), )['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); } public function getIsOp3Enabled(): bool { return service('settings')->get('Analytics.enableOP3', 'podcast:' . $this->id); } public function getOp3Url(): string { return 'https://op3.dev/show/' . $this->guid; } } app/Helpers/rss_helper.php +47 −102 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
app/Database/Migrations/2024-12-10-170000_drop_deprecated_podcasts_fields.php 0 → 100644 +86 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); /** * Class AddPodcastsMediumField adds medium field to podcast table in database * * @copyright 2020 Ad Aures * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ */ namespace App\Database\Migrations; use Override; class DropDeprecatedPodcastsFields extends BaseMigration { #[Override] public function up(): void { $this->forge->dropColumn( 'podcasts', 'episode_description_footer_markdown,episode_description_footer_html,is_owner_email_removed_from_feed,medium,payment_pointer,verify_txt,custom_rss,partner_id,partner_link_url,partner_image_url' ); } #[Override] public function down(): void { $fields = [ 'episode_description_footer_markdown' => [ 'type' => 'TEXT', 'null' => true, ], 'episode_description_footer_html' => [ 'type' => 'TEXT', 'null' => true, ], 'is_owner_email_removed_from_feed' => [ 'type' => 'BOOLEAN', 'null' => false, 'default' => 0, 'after' => 'owner_email', ], 'medium' => [ 'type' => "ENUM('podcast','music','audiobook')", 'null' => false, 'default' => 'podcast', 'after' => 'type', ], 'payment_pointer' => [ 'type' => 'VARCHAR', 'constraint' => 128, 'comment' => 'Wallet address for Web Monetization payments', 'null' => true, ], 'verify_txt' => [ 'type' => 'TEXT', 'null' => true, 'after' => 'location_osm', ], 'custom_rss' => [ 'type' => 'JSON', 'null' => true, ], 'partner_id' => [ 'type' => 'VARCHAR', 'constraint' => 32, 'null' => true, ], 'partner_link_url' => [ 'type' => 'VARCHAR', 'constraint' => 512, 'null' => true, ], 'partner_image_url' => [ 'type' => 'VARCHAR', 'constraint' => 512, 'null' => true, ], ]; $this->forge->addColumn('podcasts', $fields); } }
app/Database/Migrations/2024-12-10-180000_drop_deprecated_episodes_fields.php 0 → 100644 +37 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); /** * Class AddPodcastsMediumField adds medium field to podcast table in database * * @copyright 2020 Ad Aures * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ */ namespace App\Database\Migrations; use Override; class DropDeprecatedEpisodesFields extends BaseMigration { #[Override] public function up(): void { $this->forge->dropColumn('episodes', 'custom_rss'); } #[Override] public function down(): void { $fields = [ 'custom_rss' => [ 'type' => 'JSON', 'null' => true, ], ]; $this->forge->addColumn('episodes', $fields); } }
app/Entities/Episode.php +34 −168 Original line number Diff line number Diff line Loading @@ -11,7 +11,6 @@ declare(strict_types=1); namespace App\Entities; use App\Entities\Clip\Soundbite; use App\Libraries\SimpleRSSElement; use App\Models\ClipModel; use App\Models\EpisodeCommentModel; use App\Models\EpisodeModel; Loading @@ -29,14 +28,13 @@ use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension; use League\CommonMark\Extension\SmartPunct\SmartPunctExtension; use League\CommonMark\MarkdownConverter; use Modules\Analytics\OP3; use Modules\Media\Entities\Audio; use Modules\Media\Entities\Chapters; use Modules\Media\Entities\Image; use Modules\Media\Entities\Transcript; use Modules\Media\Models\MediaModel; use Override; use RuntimeException; use SimpleXMLElement; /** * @property int $id Loading Loading @@ -73,8 +71,6 @@ use SimpleXMLElement; * @property string|null $location_name * @property string|null $location_geo * @property string|null $location_osm * @property array|null $custom_rss * @property string $custom_rss_string * @property bool $is_published_on_hubs * @property int $posts_count * @property int $comments_count Loading @@ -94,19 +90,19 @@ use SimpleXMLElement; */ class Episode extends Entity { protected Podcast $podcast; public string $link = ''; protected string $link; public string $audio_url = ''; protected ?Audio $audio = null; public string $audio_web_url = ''; protected string $audio_url; public string $audio_opengraph_url = ''; protected string $audio_web_url; protected Podcast $podcast; protected string $audio_opengraph_url; protected ?Audio $audio = null; protected string $embed_url; protected string $embed_url = ''; protected ?Image $cover = null; Loading Loading @@ -140,8 +136,6 @@ class Episode extends Entity protected ?Location $location = null; protected string $custom_rss_string; protected ?string $publication_status = null; /** Loading Loading @@ -176,7 +170,6 @@ class Episode extends Entity 'location_name' => '?string', 'location_geo' => '?string', 'location_osm' => '?string', 'custom_rss' => '?json-array', 'is_published_on_hubs' => 'boolean', 'posts_count' => 'integer', 'comments_count' => 'integer', Loading @@ -185,6 +178,31 @@ class Episode extends Entity 'updated_by' => 'integer', ]; /** * @param array<string, mixed> $data */ #[Override] public function injectRawData(array $data): static { parent::injectRawData($data); $this->link = url_to('episode', esc($this->getPodcast()->handle, 'url'), esc($this->attributes['slug'], 'url')); $this->audio_url = url_to( 'episode-audio', $this->getPodcast() ->handle, $this->slug, $this->getAudio() ->file_extension ); $this->audio_opengraph_url = $this->audio_url . '?_from=-+Open+Graph+-'; $this->audio_web_url = $this->audio_url . '?_from=-+Website+-'; return $this; } public function setCover(UploadedFile | File $file = null): self { if (! $file instanceof File || ($file instanceof UploadedFile && ! $file->isValid())) { Loading Loading @@ -342,40 +360,6 @@ class Episode extends Entity return $this->chapters; } public function getAudioUrl(): string { $audioURL = url_to( 'episode-audio', $this->getPodcast() ->handle, $this->slug, $this->getAudio() ->file_extension ); // Wrap episode url with OP3 if episode is public and OP3 is enabled on this podcast if (! $this->is_premium && service('settings')->get( 'Analytics.enableOP3', 'podcast:' . $this->podcast_id )) { $op3 = new OP3(config('Analytics')->OP3); return $op3->wrap($audioURL, $this); } return $audioURL; } public function getAudioWebUrl(): string { return $this->getAudioUrl() . '?_from=-+Website+-'; } public function getAudioOpengraphUrl(): string { return $this->getAudioUrl() . '?_from=-+Open+Graph+-'; } /** * Gets transcript url from transcript file uri if it exists or returns the transcript_remote_url which can be null. */ Loading Loading @@ -468,11 +452,6 @@ class Episode extends Entity return $this->comments; } public function getLink(): string { return url_to('episode', esc($this->getPodcast()->handle), esc($this->attributes['slug'])); } public function getEmbedUrl(string $theme = null): string { return $theme Loading @@ -482,7 +461,7 @@ class Episode extends Entity public function setGuid(?string $guid = null): static { $this->attributes['guid'] = $guid ?? $this->getLink(); $this->attributes['guid'] = $guid ?? $this->link; return $this; } Loading Loading @@ -513,34 +492,6 @@ class Episode extends Entity return $this; } public function getDescriptionHtml(?string $serviceSlug = null): string { $descriptionHtml = ''; if ( $this->getPodcast() ->partner_id !== null && $this->getPodcast() ->partner_link_url !== null && $this->getPodcast() ->partner_image_url !== null ) { $descriptionHtml .= "<div><a href=\"{$this->getPartnerLink( $serviceSlug, )}\" rel=\"sponsored noopener noreferrer\" target=\"_blank\"><img src=\"{$this->getPartnerImageUrl( $serviceSlug, )}\" alt=\"Partner image\" /></a></div>"; } $descriptionHtml .= $this->attributes['description_html']; if ($this->getPodcast()->episode_description_footer_html) { $descriptionHtml .= "<footer>{$this->getPodcast() ->episode_description_footer_html}</footer>"; } return $descriptionHtml; } public function getDescription(): string { if ($this->description === null) { Loading Loading @@ -609,91 +560,6 @@ class Episode extends Entity return $this->location; } /** * Get custom rss tag as XML String */ public function getCustomRssString(): string { if ($this->custom_rss === null) { return ''; } helper('rss'); $xmlNode = (new SimpleRSSElement( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>', )) ->addChild('channel') ->addChild('item'); array_to_rss([ 'elements' => $this->custom_rss, ], $xmlNode); return str_replace(['<item>', '</item>'], '', (string) $xmlNode->asXML()); } /** * Saves custom rss tag into json */ public function setCustomRssString(?string $customRssString = null): static { if ($customRssString === '') { $this->attributes['custom_rss'] = null; return $this; } helper('rss'); $customXML = simplexml_load_string( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel><item>' . $customRssString . '</item></channel></rss>', ); if (! $customXML instanceof SimpleXMLElement) { // TODO: Failed to parse custom xml, should return error? return $this; } $customRssArray = rss_to_array($customXML)['elements'][0]['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 getPartnerLink(?string $serviceSlug = null): string { $partnerLink = rtrim((string) $this->getPodcast()->partner_link_url, '/') . '?pid=' . $this->getPodcast() ->partner_id . '&guid=' . urlencode((string) $this->attributes['guid']); if ($serviceSlug !== null) { $partnerLink .= '&_from=' . $serviceSlug; } return $partnerLink; } public function getPartnerImageUrl(string $serviceSlug = null): string { return rtrim((string) $this->getPodcast()->partner_image_url, '/') . '?pid=' . $this->getPodcast() ->partner_id . '&guid=' . urlencode((string) $this->attributes['guid']) . ($serviceSlug !== null ? '&_from=' . $serviceSlug : ''); } public function getPreviewLink(): string { if ($this->preview_id === null) { Loading
app/Entities/Podcast.php +29 −148 Original line number Diff line number Diff line Loading @@ -10,7 +10,6 @@ declare(strict_types=1); namespace App\Entities; use App\Libraries\SimpleRSSElement; use App\Models\ActorModel; use App\Models\CategoryModel; use App\Models\EpisodeModel; Loading Loading @@ -62,12 +61,8 @@ use RuntimeException; * @property string|null $publisher * @property string $owner_name * @property string $owner_email * @property bool $is_owner_email_removed_from_feed * @property string $type * @property string $medium * @property string|null $copyright * @property string|null $episode_description_footer_markdown * @property string|null $episode_description_footer_html * @property bool $is_blocked * @property bool $is_completed * @property bool $is_locked Loading @@ -77,15 +72,7 @@ use RuntimeException; * @property string|null $location_name * @property string|null $location_geo * @property string|null $location_osm * @property string|null $payment_pointer * @property array|null $custom_rss * @property bool $is_op3_enabled * @property string $op3_url * @property string $custom_rss_string * @property bool $is_published_on_hubs * @property string|null $partner_id * @property string|null $partner_link_url * @property string|null $partner_image_url * @property int $created_by * @property int $updated_by * @property string $publication_status Loading Loading @@ -166,8 +153,6 @@ class Podcast extends Entity protected ?Location $location = null; protected string $custom_rss_string; protected ?string $publication_status = null; /** Loading Loading @@ -195,12 +180,8 @@ class Podcast extends Entity 'publisher' => '?string', 'owner_name' => 'string', 'owner_email' => 'string', 'is_owner_email_removed_from_feed' => 'boolean', 'type' => 'string', 'medium' => 'string', 'copyright' => '?string', 'episode_description_footer_markdown' => '?string', 'episode_description_footer_html' => '?string', 'is_blocked' => 'boolean', 'is_completed' => 'boolean', 'is_locked' => 'boolean', Loading @@ -210,12 +191,7 @@ class Podcast extends Entity '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', ]; Loading Loading @@ -454,42 +430,6 @@ class Podcast extends Entity 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) { Loading Loading @@ -638,68 +578,9 @@ class Podcast extends Entity 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( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>', ))->addChild('channel'); array_to_rss([ 'elements' => $this->custom_rss, ], $xmlNode); return str_replace(['<channel>', '</channel>'], '', (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( '<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel>' . $customRssString . '</channel></rss>', ), )['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); } public function getIsOp3Enabled(): bool { return service('settings')->get('Analytics.enableOP3', 'podcast:' . $this->id); } public function getOp3Url(): string { return 'https://op3.dev/show/' . $this->guid; } }
app/Helpers/rss_helper.php +47 −102 File changed.Preview size limit exceeded, changes collapsed. Show changes