diff --git a/app/Config/Images.php b/app/Config/Images.php index debc98f039c3207cd27aad8dc046ce544b72e5e2..680a19507f9fddb0a1fa325f6e14f9d7e7622231 100644 --- a/app/Config/Images.php +++ b/app/Config/Images.php @@ -32,42 +32,63 @@ class Images extends BaseConfig /* |-------------------------------------------------------------------------- - | Uploaded images resizing sizes (in px) + | Uploaded images sizes (in px) |-------------------------------------------------------------------------- | The sizes listed below determine the resizing of images when uploaded. - | All uploaded images are of 1:1 ratio (width and height are the same). */ - public int $thumbnailSize = 150; - - public int $mediumSize = 320; - - public int $largeSize = 1024; - /** - * Size of images linked in the rss feed (should be between 1400 and 3000) + * Podcast cover image sizes + * + * Uploaded podcast covers are of 1:1 ratio (width and height are the same). + * + * Size of images linked in the rss feed (should be between 1400 and 3000). Size for ID3 tag cover art (should be + * between 300 and 800) + * + * Array values are as follows: 'name' => [width, height] + * + * @var array<string, int[]> */ - public int $feedSize = 1400; + public array $podcastCoverSizes = [ + 'tiny' => [40, 40], + 'thumbnail' => [150, 150], + 'medium' => [320, 320], + 'large' => [1024, 1024], + 'feed' => [1400, 1400], + 'id3' => [500, 500], + ]; /** - * Size for ID3 tag cover art (should be between 300 and 800) + * Podcast header cover image + * + * Uploaded podcast header covers are of 3:1 ratio + * + * Array values are as follows: 'name' => [width, height] + * + * @var array<string, int[]> */ - public int $id3Size = 500; - - /* - |-------------------------------------------------------------------------- - | Uploaded images naming extensions - |-------------------------------------------------------------------------- - | The properties listed below set the name extensions for the resized images - */ - - public string $thumbnailSuffix = '_thumbnail'; - - public string $mediumSuffix = '_medium'; + public array $podcastBannerSizes = [ + 'small' => [320, 128], + 'medium' => [960, 320], + 'large' => [1500, 500], + ]; - public string $largeSuffix = '_large'; + public string $podcastBannerDefaultPath = 'castopod-banner-default.jpg'; - public string $feedSuffix = '_feed'; + public string $podcastBannerDefaultMimeType = 'image/jpeg'; - public string $id3Suffix = '_id3'; + /** + * Person image + * + * Uploaded person images are of 1:1 ratio (width and height are the same). + * + * Array values are as follows: 'name' => [width, height] + * + * @var array<string, int[]> + */ + public array $personAvatarSizes = [ + 'tiny' => [40, 40], + 'thumbnail' => [150, 150], + 'medium' => [320, 320], + ]; } diff --git a/app/Controllers/CreditsController.php b/app/Controllers/CreditsController.php index 8c1b5418bfd0c3a96d82ead5d9e0d5f08f3800a0..816653dd15006ec31fa9e9823a5a634add92a0c1 100644 --- a/app/Controllers/CreditsController.php +++ b/app/Controllers/CreditsController.php @@ -48,7 +48,7 @@ class CreditsController extends BaseController $personId => [ 'full_name' => $credit->person->full_name, 'thumbnail_url' => - $credit->person->image->thumbnail_url, + $credit->person->avatar->thumbnail_url, 'information_url' => $credit->person->information_url, 'roles' => [ @@ -87,7 +87,7 @@ class CreditsController extends BaseController $credits[$personGroup]['persons'][$personId] = [ 'full_name' => $credit->person->full_name, 'thumbnail_url' => - $credit->person->image->thumbnail_url, + $credit->person->avatar->thumbnail_url, 'information_url' => $credit->person->information_url, 'roles' => [ $personRole => [ diff --git a/app/Controllers/EpisodeController.php b/app/Controllers/EpisodeController.php index 0b439a61f672b22665d338c49c505441f35becdd..3a59f91a2788785875176dd89d608147091304c8 100644 --- a/app/Controllers/EpisodeController.php +++ b/app/Controllers/EpisodeController.php @@ -200,11 +200,11 @@ class EpisodeController extends BaseController '" width="100%" height="144" frameborder="0" scrolling="no"></iframe>', 'width' => 600, 'height' => 144, - 'thumbnail_url' => $this->episode->image->large_url, + 'thumbnail_url' => $this->episode->cover->large_url, 'thumbnail_width' => config('Images') - ->largeSize, + ->podcastCoverSizes['large'][0], 'thumbnail_height' => config('Images') - ->largeSize, + ->podcastCoverSizes['large'][1], ]); } @@ -219,9 +219,9 @@ class EpisodeController extends BaseController $oembed->addChild('provider_url', $this->podcast->link); $oembed->addChild('author_name', $this->podcast->title); $oembed->addChild('author_url', $this->podcast->link); - $oembed->addChild('thumbnail', $this->episode->image->large_url); - $oembed->addChild('thumbnail_width', config('Images')->largeSize); - $oembed->addChild('thumbnail_height', config('Images')->largeSize); + $oembed->addChild('thumbnail', $this->episode->cover->large_url); + $oembed->addChild('thumbnail_width', config('Images')->podcastCoverSizes['large'][0]); + $oembed->addChild('thumbnail_height', config('Images')->podcastCoverSizes['large'][1]); $oembed->addChild( 'html', htmlentities( diff --git a/app/Controllers/MapMarkerController.php b/app/Controllers/MapMarkerController.php index 6c6df82d80c520386fab0b6634bd179167520879..5ead95979baab6bca5533631523a1641ebcbaccb 100644 --- a/app/Controllers/MapMarkerController.php +++ b/app/Controllers/MapMarkerController.php @@ -46,7 +46,7 @@ class MapMarkerController extends BaseController 'location_url' => $episode->location->url, 'episode_link' => $episode->link, 'podcast_link' => $episode->podcast->link, - 'image_path' => $episode->image->thumbnail_url, + 'cover_path' => $episode->cover->thumbnail_url, 'podcast_title' => $episode->podcast->title, 'episode_title' => $episode->title, ]; diff --git a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php index 9243e7f386056fc7c1b0d22ce5e994d8926e8d1a..0663392a1b0f6dcfcb61923eea3ab69864723e03 100644 --- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php +++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php @@ -46,16 +46,28 @@ class AddPodcasts extends Migration 'description_html' => [ 'type' => 'TEXT', ], - 'image_path' => [ + 'cover_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, ], // constraint is 13 because the longest safe mimetype for images is image/svg+xml, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types - 'image_mimetype' => [ + 'cover_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 13, ], + 'banner_path' => [ + 'type' => 'VARCHAR', + 'constraint' => 255, + 'null' => true, + 'default' => null, + ], + 'banner_mimetype' => [ + 'type' => 'VARCHAR', + 'constraint' => 13, + 'null' => true, + 'default' => null, + ], 'language_code' => [ 'type' => 'VARCHAR', 'constraint' => 2, diff --git a/app/Database/Migrations/2020-06-05-170000_add_episodes.php b/app/Database/Migrations/2020-06-05-170000_add_episodes.php index 310b1b70a5dbe14d429561c03e871420d1e185d2..aea2a023c375ef0bd4b503a8f383b7cad938f8c3 100644 --- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php +++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php @@ -70,14 +70,14 @@ class AddEpisodes extends Migration 'description_html' => [ 'type' => 'TEXT', ], - 'image_path' => [ + 'cover_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, ], // constraint is 13 because the longest safe mimetype for images is image/svg+xml, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types - 'image_mimetype' => [ + 'cover_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 13, 'null' => true, diff --git a/app/Database/Migrations/2020-12-25-120000_add_persons.php b/app/Database/Migrations/2020-12-25-120000_add_persons.php index 9101ee772b71c9ecd45d263422cca57432509f64..58e26df3078b42ba57a05522de0b22fdb0219ee6 100644 --- a/app/Database/Migrations/2020-12-25-120000_add_persons.php +++ b/app/Database/Migrations/2020-12-25-120000_add_persons.php @@ -42,14 +42,14 @@ class AddPersons extends Migration 'The url to a relevant resource of information about the person, such as a homepage or third-party profile platform.', 'null' => true, ], - 'image_path' => [ + 'avatar_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, ], // constraint is 13 because the longest safe mimetype for images is image/svg+xml, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types - 'image_mimetype' => [ + 'avatar_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 13, 'null' => true, diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 5f2689b88afbe55d6778b31e77e1f945b8cae27e..fdc16d04bb5cfd3dc9b5dbe1ef9a2dd7ff13ea36 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -44,9 +44,9 @@ use RuntimeException; * @property string|null $description Holds text only description, striped of any markdown or html special characters * @property string $description_markdown * @property string $description_html - * @property Image $image - * @property string|null $image_path - * @property string|null $image_mimetype + * @property Image $cover + * @property string|null $cover_path + * @property string|null $cover_mimetype * @property File|null $transcript_file * @property string|null $transcript_file_url * @property string|null $transcript_file_path @@ -98,7 +98,7 @@ class Episode extends Entity protected string $embed_url; - protected Image $image; + protected Image $cover; protected ?string $description = null; @@ -153,8 +153,8 @@ class Episode extends Entity 'audio_file_header_size' => 'integer', 'description_markdown' => 'string', 'description_html' => 'string', - 'image_path' => '?string', - 'image_mimetype' => '?string', + 'cover_path' => '?string', + 'cover_mimetype' => '?string', 'transcript_file_path' => '?string', 'transcript_file_remote_url' => '?string', 'chapters_file_path' => '?string', @@ -175,31 +175,36 @@ class Episode extends Entity ]; /** - * Saves an episode image + * Saves an episode cover */ - public function setImage(?Image $image = null): static + public function setCover(?Image $cover = null): static { - if ($image === null) { + if ($cover === null) { return $this; } // Save image - $image->saveImage('podcasts/' . $this->getPodcast()->handle, $this->attributes['slug']); + $cover->saveImage( + config('Images') + ->podcastCoverSizes, + 'podcasts/' . $this->getPodcast()->handle, + $this->attributes['slug'] + ); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['cover_mimetype'] = $cover->mimetype; + $this->attributes['cover_path'] = $cover->path; return $this; } - public function getImage(): Image + public function getCover(): Image { - if ($imagePath = $this->attributes['image_path']) { - return new Image(null, $imagePath, $this->attributes['image_mimetype']); + if ($coverPath = $this->attributes['cover_path']) { + return new Image(null, $coverPath, $this->attributes['cover_mimetype']); } return $this->getPodcast() - ->image; + ->cover; } /** diff --git a/app/Entities/Image.php b/app/Entities/Image.php index 799840c8ea60fe06a6f03a1b83bdd156b1be69a3..78740d0c6620138e038714b34327848a369f481f 100644 --- a/app/Entities/Image.php +++ b/app/Entities/Image.php @@ -13,7 +13,6 @@ namespace App\Entities; use CodeIgniter\Entity\Entity; use CodeIgniter\Files\File; use Config\Images; -use Config\Services; use RuntimeException; /** @@ -24,16 +23,6 @@ use RuntimeException; * @property string $mimetype * @property string $path * @property string $url - * @property string $thumbnail_path - * @property string $thumbnail_url - * @property string $medium_path - * @property string $medium_url - * @property string $large_path - * @property string $large_url - * @property string $feed_path - * @property string $feed_url - * @property string $id3_path - * @property string $id3_url */ class Image extends Entity { @@ -47,14 +36,14 @@ class Image extends Entity protected string $extension; + protected string $mimetype; + public function __construct(?File $file, string $path = '', string $mimetype = '') { if ($file === null && $path === '') { throw new RuntimeException('File or path must be set to create an Image.'); } - $this->config = config('Images'); - $dirname = ''; $filename = ''; $extension = ''; @@ -81,152 +70,87 @@ class Image extends Entity $this->mimetype = $mimetype; } - public function getFile(): File + public function __get($property) { - if ($this->file === null) { - $this->file = new File($this->path); + // Convert to CamelCase for the method + $method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $property))); + + // if a get* method exists for this property, + // call that method to get this value. + // @phpstan-ignore-next-line + if (method_exists($this, $method)) { + return $this->{$method}(); } - return $this->file; - } - - public function getPath(): string - { - return $this->dirname . '/' . $this->filename . '.' . $this->extension; - } - - public function getUrl(): string - { - helper('media'); - - return media_base_url($this->path); - } - - public function getThumbnailPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->thumbnailSuffix . - '.' . - $this->extension; - } - - public function getThumbnailUrl(): string - { - helper('media'); + $fileSuffix = ''; + if ($lastUnderscorePosition = strrpos($property, '_')) { + $fileSuffix = '_' . substr($property, 0, $lastUnderscorePosition); + } - return media_base_url($this->thumbnail_path); - } + $path = ''; + if ($this->dirname !== '.') { + $path .= $this->dirname . '/'; + } + $path .= $this->filename . $fileSuffix . '.' . $this->extension; - public function getMediumPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->mediumSuffix . - '.' . - $this->extension; - } + if (str_ends_with($property, 'url')) { + helper('media'); - public function getMediumUrl(): string - { - helper('media'); + return media_base_url($path); + } - return media_base_url($this->medium_path); + if (str_ends_with($property, 'path')) { + return $path; + } } - public function getLargePath(): string + public function getMimetype(): string { - return $this->dirname . - '/' . - $this->filename . - $this->config->largeSuffix . - '.' . - $this->extension; + return $this->mimetype; } - public function getLargeUrl(): string + public function getFile(): File { - helper('media'); - - return media_base_url($this->large_path); - } + if ($this->file === null) { + $this->file = new File($this->path); + } - public function getFeedPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->feedSuffix . - '.' . - $this->extension; + return $this->file; } - public function getFeedUrl(): string + /** + * @param array<string, int[]> $sizes + */ + public function saveImage(array $sizes, string $dirname, string $filename): void { helper('media'); - return media_base_url($this->feed_path); - } + $this->dirname = $dirname; + $this->filename = $filename; - public function getId3Path(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->id3Suffix . - '.' . - $this->extension; - } + save_media($this->file, $this->dirname, $this->filename); - public function getId3Url(): string - { - helper('media'); + $imageService = service('image'); - return media_base_url($this->id3_path); + foreach ($sizes as $name => $size) { + $pathProperty = $name . '_path'; + $imageService + ->withFile(media_path($this->path)) + ->resize($size[0], $size[1]) + ->save(media_path($this->{$pathProperty})); + } } - public function saveImage(string $dirname, string $filename): void + /** + * @param array<string, int[]> $sizes + */ + public function delete(array $sizes): void { helper('media'); - $this->dirname = $dirname; - $this->filename = $filename; - - save_media($this->file, $this->dirname, $this->filename); - - $imageService = Services::image(); - - $thumbnailSize = $this->config->thumbnailSize; - $mediumSize = $this->config->mediumSize; - $largeSize = $this->config->largeSize; - $feedSize = $this->config->feedSize; - $id3Size = $this->config->id3Size; - - $imageService - ->withFile(media_path($this->path)) - ->resize($thumbnailSize, $thumbnailSize) - ->save(media_path($this->thumbnail_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($mediumSize, $mediumSize) - ->save(media_path($this->medium_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($largeSize, $largeSize) - ->save(media_path($this->large_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($feedSize, $feedSize) - ->save(media_path($this->feed_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($id3Size, $id3Size) - ->save(media_path($this->id3_path)); + foreach (array_keys($sizes) as $name) { + $pathProperty = $name . '_path'; + unlink(media_path($this->{$pathProperty})); + } } } diff --git a/app/Entities/Person.php b/app/Entities/Person.php index aa9fbff4ebfb4c74f36300ce0e0f3b66a4ed91a8..f860ecec3954c006d48b782e13f6cacda45cd49e 100644 --- a/app/Entities/Person.php +++ b/app/Entities/Person.php @@ -19,16 +19,16 @@ use RuntimeException; * @property string $full_name * @property string $unique_name * @property string|null $information_url - * @property Image $image - * @property string $image_path - * @property string $image_mimetype + * @property Image $avatar + * @property string $avatar_path + * @property string $avatar_mimetype * @property int $created_by * @property int $updated_by * @property object[]|null $roles */ class Person extends Entity { - protected Image $image; + protected Image $avatar; protected ?int $podcast_id = null; @@ -47,8 +47,8 @@ class Person extends Entity 'full_name' => 'string', 'unique_name' => 'string', 'information_url' => '?string', - 'image_path' => '?string', - 'image_mimetype' => '?string', + 'avatar_path' => '?string', + 'avatar_mimetype' => '?string', 'podcast_id' => '?integer', 'episode_id' => '?integer', 'created_by' => 'integer', @@ -56,32 +56,31 @@ class Person extends Entity ]; /** - * Saves a picture in `public/media/persons/` + * Saves the person avatar in `public/media/persons/` */ - public function setImage(?Image $image = null): static + public function setAvatar(?Image $avatar = null): static { - if ($image === null) { + if ($avatar === null) { return $this; } helper('media'); - // Save image - $image->saveImage('persons', $this->attributes['unique_name']); + $avatar->saveImage(config('Images')->personAvatarSizes, 'persons', $this->attributes['unique_name']); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['avatar_mimetype'] = $avatar->mimetype; + $this->attributes['avatar_path'] = $avatar->path; return $this; } - public function getImage(): Image + public function getAvatar(): Image { - if ($this->attributes['image_path'] === null) { + if ($this->attributes['avatar_path'] === null) { return new Image(null, '/castopod-avatar-default.jpg', 'image/jpeg'); } - return new Image(null, $this->attributes['image_path'], $this->attributes['image_mimetype']); + return new Image(null, $this->attributes['avatar_path'], $this->attributes['avatar_mimetype']); } /** diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index 4861fa371a3237ccfc6de91735ac842bff77322c..35c35093a8ab1f7640f995bcd1748a3d0c460295 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -34,9 +34,12 @@ use RuntimeException; * @property string|null $description Holds text only description, striped of any markdown or html special characters * @property string $description_markdown * @property string $description_html - * @property Image $image - * @property string $image_path - * @property string $image_mimetype + * @property Image $cover + * @property string $cover_path + * @property string $cover_mimetype + * @property Image|null $banner + * @property string|null $banner_path + * @property string|null $banner_mimetype * @property string $language_code * @property int $category_id * @property Category|null $category @@ -84,7 +87,9 @@ class Podcast extends Entity protected ?Actor $actor = null; - protected Image $image; + protected Image $cover; + + protected ?Image $banner; protected ?string $description = null; @@ -145,8 +150,10 @@ class Podcast extends Entity 'title' => 'string', 'description_markdown' => 'string', 'description_html' => 'string', - 'image_path' => 'string', - 'image_mimetype' => 'string', + 'cover_path' => 'string', + 'cover_mimetype' => 'string', + 'banner_path' => '?string', + 'banner_mimetype' => '?string', 'language_code' => 'string', 'category_id' => 'integer', 'parental_advisory' => '?string', @@ -189,22 +196,63 @@ class Podcast extends Entity } /** - * Saves a cover image to the corresponding podcast folder in `public/media/podcast_name/` + * Saves a podcast cover to the corresponding podcast folder in `public/media/podcast_name/` + */ + public function setCover(Image $cover): static + { + // Save image + $cover->saveImage(config('Images')->podcastCoverSizes, 'podcasts/' . $this->attributes['handle'], 'cover'); + + $this->attributes['cover_path'] = $cover->path; + $this->attributes['cover_mimetype'] = $cover->mimetype; + + return $this; + } + + public function getCover(): Image + { + return new Image(null, $this->cover_path, $this->cover_mimetype); + } + + /** + * Saves a podcast cover to the corresponding podcast folder in `public/media/podcast_name/` */ - public function setImage(Image $image): static + public function setBanner(?Image $banner): static { + if ($banner === null) { + $this->attributes['banner_path'] = null; + $this->attributes['banner_mimetype'] = null; + + return $this; + } + // Save image - $image->saveImage('podcasts/' . $this->attributes['handle'], 'cover'); + $banner->saveImage( + config('Images') + ->podcastBannerSizes, + 'podcasts/' . $this->attributes['handle'], + 'banner' + ); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['banner_path'] = $banner->path; + $this->attributes['banner_mimetype'] = $banner->mimetype; return $this; } - public function getImage(): Image + public function getBanner(): Image { - return new Image(null, $this->image_path, $this->image_mimetype); + if ($this->attributes['banner_path'] === null) { + return new Image( + null, + config('Images') + ->podcastBannerDefaultPath, + config('Images') + ->podcastBannerDefaultMimeType + ); + } + + return new Image(null, $this->banner_path, $this->banner_mimetype); } public function getLink(): string diff --git a/app/Helpers/id3_helper.php b/app/Helpers/id3_helper.php index e7232a1611cbd0ed5472edda3ace552108bbfda1..c975f54b8af05c65a1a082482da06db365b73eac 100644 --- a/app/Helpers/id3_helper.php +++ b/app/Helpers/id3_helper.php @@ -51,7 +51,7 @@ if (! function_exists('write_audio_file_tags')) { $tagwriter->tagformats = ['id3v2.4']; $tagwriter->tag_encoding = $TextEncoding; - $cover = new File(media_path($episode->image->id3_path)); + $cover = new File(media_path($episode->cover->id3_path)); $APICdata = file_get_contents($cover->getRealPath()); diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php index c23fab85216551c00244c7c2d21d5e422e08becf..adb7d3bd5de2d95957accb60c75ae0882abf7c5f 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -56,8 +56,7 @@ if (! function_exists('get_rss_feed')) { $itunesImage = $channel->addChild('image', null, $itunesNamespace); - // FIXME: This should be downsized to 1400x1400 - $itunesImage->addAttribute('href', $podcast->image->url); + $itunesImage->addAttribute('href', $podcast->cover->feed_url); $channel->addChild('language', $podcast->language_code); if ($podcast->location !== null) { @@ -134,7 +133,7 @@ if (! function_exists('get_rss_feed')) { $podcastNamespace, ); - $personElement->addAttribute('img', $person->image->medium_url); + $personElement->addAttribute('img', $person->avatar->medium_url); if ($person->information_url !== null) { $personElement->addAttribute('href', $person->information_url); @@ -190,7 +189,7 @@ if (! function_exists('get_rss_feed')) { } $image = $channel->addChild('image'); - $image->addChild('url', $podcast->image->feed_url); + $image->addChild('url', $podcast->cover->feed_url); $image->addChild('title', $podcast->title, null, false); $image->addChild('link', $podcast->link); @@ -234,7 +233,7 @@ if (! function_exists('get_rss_feed')) { $item->addChild('duration', (string) $episode->audio_file_duration, $itunesNamespace); $item->addChild('link', $episode->link); $episodeItunesImage = $item->addChild('image', null, $itunesNamespace); - $episodeItunesImage->addAttribute('href', $episode->image->feed_url); + $episodeItunesImage->addAttribute('href', $episode->cover->feed_url); $episode->parental_advisory && $item->addChild( @@ -300,7 +299,7 @@ if (! function_exists('get_rss_feed')) { htmlspecialchars(lang("PersonsTaxonomy.persons.{$role->group}.label", [], 'en')), ); - $personElement->addAttribute('img', $person->image->medium_url); + $personElement->addAttribute('img', $person->avatar->medium_url); if ($person->information_url !== null) { $personElement->addAttribute('href', $person->information_url); diff --git a/app/Libraries/PodcastEpisode.php b/app/Libraries/PodcastEpisode.php index b6c9f6e522034b79cb81b005a4939b203cf47310..11bc23eb46e1d210519e10308a2ed642e9f48932 100644 --- a/app/Libraries/PodcastEpisode.php +++ b/app/Libraries/PodcastEpisode.php @@ -52,8 +52,8 @@ class PodcastEpisode extends ObjectType $this->image = [ 'type' => 'Image', - 'mediaType' => $episode->image_mimetype, - 'url' => $episode->image->url, + 'mediaType' => $episode->cover_mimetype, + 'url' => $episode->cover->feed_url, ]; // add audio file diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index f3973d1c5b74663fbdadbafa9b25df6e8e7ffde5..c73886f32f2052ef11dc280ead27c0fd4348d794 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -75,8 +75,8 @@ class EpisodeModel extends Model 'audio_file_header_size', 'description_markdown', 'description_html', - 'image_path', - 'image_mimetype', + 'cover_path', + 'cover_mimetype', 'transcript_file_path', 'transcript_file_remote_url', 'chapters_file_path', diff --git a/app/Models/PersonModel.php b/app/Models/PersonModel.php index 36fba45ca1d6e3c93d616d4c6ec155a3e9c0edf6..77cc85c2fe64bcb81ac05fbe3c4a6ef0208be911 100644 --- a/app/Models/PersonModel.php +++ b/app/Models/PersonModel.php @@ -35,8 +35,8 @@ class PersonModel extends Model 'full_name', 'unique_name', 'information_url', - 'image_path', - 'image_mimetype', + 'avatar_path', + 'avatar_mimetype', 'created_by', 'updated_by', ]; diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php index 5e18549101ecb9da59ae59f5227df829d7ce9492..a342a6f9dc127b51c507c40a712cdbe34f16bbf2 100644 --- a/app/Models/PodcastModel.php +++ b/app/Models/PodcastModel.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace App\Models; -use App\Entities\Actor; use App\Entities\Podcast; use CodeIgniter\Database\Query; use CodeIgniter\HTTP\URI; @@ -41,8 +40,10 @@ class PodcastModel extends Model 'description_html', 'episode_description_footer_markdown', 'episode_description_footer_html', - 'image_path', - 'image_mimetype', + 'cover_path', + 'cover_mimetype', + 'banner_path', + 'banner_mimetype', 'language_code', 'category_id', 'parental_advisory', @@ -91,7 +92,7 @@ class PodcastModel extends Model 'handle' => 'required|regex_match[/^[a-zA-Z0-9\_]{1,32}$/]|is_unique[podcasts.handle,id,{id}]', 'description_markdown' => 'required', - 'image_path' => 'required', + 'cover_path' => 'required', 'language_code' => 'required', 'category_id' => 'required', 'owner_email' => 'required|valid_email', @@ -454,13 +455,16 @@ class PodcastModel extends Model { $podcast = (new self())->getPodcastById(is_array($data['id']) ? $data['id'][0] : $data['id']); - $podcastActor = (new ActorModel())->find($podcast->actor_id); - - $podcastActor->avatar_image_url = $podcast->image->thumbnail_url; - $podcastActor->avatar_image_mimetype = $podcast->image_mimetype; + if ($podcast !== null) { + $podcastActor = (new ActorModel())->find($podcast->actor_id); - (new ActorModel())->update($podcast->actor_id, $podcastActor); + if ($podcastActor) { + $podcastActor->avatar_image_url = $podcast->cover->thumbnail_url; + $podcastActor->avatar_image_mimetype = $podcast->cover_mimetype; + (new ActorModel())->update($podcast->actor_id, $podcastActor); + } + } return $data; } @@ -477,14 +481,18 @@ class PodcastModel extends Model $actorModel = new ActorModel(); $actor = $actorModel->getActorById($podcast->actor_id); - // update values - $actor->display_name = $podcast->title; - $actor->summary = $podcast->description_html; - $actor->avatar_image_url = $podcast->image->thumbnail_url; - $actor->avatar_image_mimetype = $podcast->image_mimetype; - - if ($actor->hasChanged()) { - $actorModel->update($actor->id, $actor); + if ($actor !== null) { + // update values + $actor->display_name = $podcast->title; + $actor->summary = $podcast->description_html; + $actor->avatar_image_url = $podcast->cover->thumbnail_url; + $actor->avatar_image_mimetype = $podcast->cover->mimetype; + $actor->cover_image_url = $podcast->banner->large_url; + $actor->cover_image_mimetype = $podcast->banner->mimetype; + + if ($actor->hasChanged()) { + $actorModel->update($actor->id, $actor); + } } } diff --git a/app/Resources/js/modules/EpisodesMap.ts b/app/Resources/js/modules/EpisodesMap.ts index 41b794c5f333ac156efc963db19e8c9ab7ac3251..0f3c33ce6360bac101d680752e13510c7afe862b 100644 --- a/app/Resources/js/modules/EpisodesMap.ts +++ b/app/Resources/js/modules/EpisodesMap.ts @@ -47,7 +47,7 @@ const drawEpisodesMap = async (mapDivId: string, dataUrl: string) => { data[i].longitude, ]).bindPopup( '<div class="flex min-w-max"><img src="' + - data[i].image_path + + data[i].cover_path + '" alt="' + data[i].episode_title + '" class="mr-2 rounded w-16 h-16" /><div class="flex flex-col"><h2 class="lg:text-base text-sm ! font-bold"><a href="' + diff --git a/app/Resources/styles/custom.css b/app/Resources/styles/custom.css index 8f5fabad550a360dfd606fd474a0775f3b9d4f9e..815c0d6aac557c66fb1ba18f5bf2fbb636ea5432 100644 --- a/app/Resources/styles/custom.css +++ b/app/Resources/styles/custom.css @@ -38,4 +38,19 @@ hsla(0, 0%, 0%, 0.8) 100% ); } + + .backdrop-gradient-pine { + background-image: linear-gradient( + 180deg, + hsla(172, 100%, 17%, 0) 0%, + hsla(172, 100%, 17%, 0.034375) 16.36%, + hsla(172, 100%, 17%, 0.125) 33.34%, + hsla(172, 100%, 17%, 0.253125) 50.1%, + hsla(172, 100%, 17%, 0.4) 65.75%, + hsla(172, 100%, 17%, 0.546875) 79.43%, + hsla(172, 100%, 17%, 0.675) 90.28%, + hsla(172, 100%, 17%, 0.765625) 97.43%, + hsla(172, 100%, 17%, 0.8) 100% + ); + } } diff --git a/app/Validation/FileRules.php b/app/Validation/FileRules.php index 6b6ed3f31f0f31ae2f5663338ac06ac3214f3292..e56fd0923ce3e7da0ed7ac908fcc76c77b6b8efd 100644 --- a/app/Validation/FileRules.php +++ b/app/Validation/FileRules.php @@ -57,9 +57,9 @@ class FileRules extends ValidationFileRules //-------------------------------------------------------------------- /** - * Checks an uploaded file to verify that the image ratio is of 1:1 + * Checks an uploaded image to verify that the ratio corresponds to the params */ - public function is_image_squared(string $blank = null, string $params): bool + public function is_image_ratio(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. @@ -79,12 +79,14 @@ class FileRules extends ValidationFileRules return true; } + // Get Parameter sizes + $x = $params[0] ?? 1; + $y = $params[1] ?? 1; + // Get uploaded image size - $info = getimagesize($file->getTempName()); - $fileWidth = $info[0]; - $fileHeight = $info[1]; + [0 => $fileWidth, 1 => $fileHeight] = getimagesize($file->getTempName()); - if ($fileWidth !== $fileHeight) { + if (($x / $y) !== ($fileWidth / $fileHeight)) { return false; } } diff --git a/app/Views/Components/Forms/Input.php b/app/Views/Components/Forms/Input.php index 31489e1a90ac1b356562f8a5d123acd97ae01c38..bbddfd819abcd089bc8ac3d21fd40c2ab647867f 100644 --- a/app/Views/Components/Forms/Input.php +++ b/app/Views/Components/Forms/Input.php @@ -10,7 +10,7 @@ class Input extends FormComponent public function render(): string { - $baseClass = 'bg-white border-black rounded-lg focus:border-black border-3 focus:ring-castopod focus-within:ring-castopod ' . $this->class; + $baseClass = 'w-full bg-white border-black rounded-lg focus:border-black border-3 focus:ring-castopod focus-within:ring-castopod ' . $this->class; $this->attributes['class'] = $baseClass; diff --git a/app/Views/Components/Forms/MarkdownEditor.php b/app/Views/Components/Forms/MarkdownEditor.php index 63f9276a6adf45726dd306d16578e58b791143c3..62b035a97ff8830479b34ceb233c7876b8789240 100644 --- a/app/Views/Components/Forms/MarkdownEditor.php +++ b/app/Views/Components/Forms/MarkdownEditor.php @@ -13,7 +13,7 @@ class MarkdownEditor extends FormComponent $this->attributes['class'] = 'border-none focus:border-none focus:outline-none focus:ring-0 w-full h-full'; $this->attributes['rows'] = 6; - $textarea = form_textarea($this->attributes, old($this->name, html_entity_decode($this->value), null)); + $textarea = form_textarea($this->attributes, old($this->name, html_entity_decode($this->value), false)); $icons = [ 'heading' => icon('heading'), 'bold' => icon('bold'), diff --git a/app/Views/Components/Forms/Textarea.php b/app/Views/Components/Forms/Textarea.php index 744861fef2d7aad61c2d5746ee0a9df57a4af00c..c1d7e20fda5984c1882c7118ccab5dd0872be605 100644 --- a/app/Views/Components/Forms/Textarea.php +++ b/app/Views/Components/Forms/Textarea.php @@ -17,7 +17,7 @@ class Textarea extends FormComponent { unset($this->attributes['value']); - $this->attributes['class'] = 'focus:border-black focus:ring-castopod rounded-lg border-3 border-black ' . $this->class; + $this->attributes['class'] = 'w-full focus:border-black focus:ring-castopod rounded-lg border-3 border-black ' . $this->class; $textarea = form_textarea( $this->attributes, diff --git a/app/Views/Components/Forms/Toggler.php b/app/Views/Components/Forms/Toggler.php index 15d101900cf63c8b3f0220a41a83894b291868a8..5bc0c583e06a435f714142b2d277816171d8abce 100644 --- a/app/Views/Components/Forms/Toggler.php +++ b/app/Views/Components/Forms/Toggler.php @@ -36,7 +36,7 @@ class Toggler extends FormComponent $this->attributes['class'] = 'form-switch'; - $checkbox = form_checkbox($this->attributes, $this->value, old($this->name, $this->checked)); + $checkbox = form_checkbox($this->attributes, $this->value, old($this->name) === 'yes' ? true : $this->checked); $hint = $this->hint === '' ? '' : hint_tooltip($this->hint, 'ml-1'); return <<<HTML <label class="relative inline-flex items-center {$wrapperClass}"> diff --git a/modules/Admin/Config/Routes.php b/modules/Admin/Config/Routes.php index edda5e81520efad7a2a998e9b9eb2bb2ac6663fe..210f9b31830ca07fb8996f3d3e291ef2259a02ae 100644 --- a/modules/Admin/Config/Routes.php +++ b/modules/Admin/Config/Routes.php @@ -98,6 +98,10 @@ $routes->group( $routes->post('edit', 'PodcastController::attemptEdit/$1', [ 'filter' => 'permission:podcast-edit', ]); + $routes->get('edit/delete-banner', 'PodcastController::deleteBanner/$1', [ + 'as' => 'podcast-banner-delete', + 'filter' => 'permission:podcast-edit', + ]); $routes->get('delete', 'PodcastController::delete/$1', [ 'as' => 'podcast-delete', 'filter' => 'permission:podcasts-delete', diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php index 312953217f0db7c388aa532f522e6d5c8a4a76ed..cd33cc4b213deb35083f1dcd4f017f9f6d59685d 100644 --- a/modules/Admin/Controllers/EpisodeController.php +++ b/modules/Admin/Controllers/EpisodeController.php @@ -112,8 +112,8 @@ class EpisodeController extends BaseController { $rules = [ 'audio_file' => 'uploaded[audio_file]|ext_in[audio_file,mp3,m4a]', - 'image' => - 'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', 'transcript_file' => 'ext_in[transcript,txt,html,srt,json]|permit_empty', 'chapters_file' => 'ext_in[chapters,json]|permit_empty', @@ -126,12 +126,6 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } - $image = null; - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $image = new Image($imageFile); - } - $newEpisode = new Episode([ 'podcast_id' => $this->podcast->id, 'title' => $this->request->getPost('title'), @@ -139,7 +133,6 @@ class EpisodeController extends BaseController 'guid' => null, 'audio_file' => $this->request->getFile('audio_file'), 'description_markdown' => $this->request->getPost('description'), - 'image' => $image, 'location' => $this->request->getPost('location_name') === '' ? null : new Location($this->request->getPost( 'location_name' )), @@ -163,6 +156,11 @@ class EpisodeController extends BaseController 'published_at' => null, ]); + $coverFile = $this->request->getFile('cover'); + if ($coverFile !== null && $coverFile->isValid()) { + $newEpisode->cover = new Image($coverFile); + } + $transcriptChoice = $this->request->getPost('transcript-choice'); if ( $transcriptChoice === 'upload-file' @@ -238,8 +236,8 @@ class EpisodeController extends BaseController $rules = [ 'audio_file' => 'uploaded[audio_file]|ext_in[audio_file,mp3,m4a]|permit_empty', - 'image' => - 'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', 'transcript_file' => 'ext_in[transcript_file,txt,html,srt,json]|permit_empty', 'chapters_file' => 'ext_in[chapters_file,json]|permit_empty', @@ -279,9 +277,9 @@ class EpisodeController extends BaseController $this->episode->audio_file = $audioFile; } - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $this->episode->image = new Image($imageFile); + $coverFile = $this->request->getFile('cover'); + if ($coverFile !== null && $coverFile->isValid()) { + $this->episode->cover = new Image($coverFile); } $transcriptChoice = $this->request->getPost('transcript-choice'); diff --git a/modules/Admin/Controllers/PersonController.php b/modules/Admin/Controllers/PersonController.php index 3d75828c52bcac3edb32914c1e3cb2112e6ef48b..7825e6f8d3ea7b8b08772c727b9d29025f0e6f28 100644 --- a/modules/Admin/Controllers/PersonController.php +++ b/modules/Admin/Controllers/PersonController.php @@ -66,8 +66,8 @@ class PersonController extends BaseController public function attemptCreate(): RedirectResponse { $rules = [ - 'image' => - 'is_image[image]|ext_in[image,jpg,jpeg,png]|min_dims[image,400,400]|is_image_squared[image]', + 'avatar' => + 'is_image[avatar]|ext_in[avatar,jpg,jpeg,png]|min_dims[avatar,400,400]|is_image_ratio[avatar,1,1]', ]; if (! $this->validate($rules)) { @@ -85,9 +85,9 @@ class PersonController extends BaseController 'updated_by' => user_id(), ]); - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $person->image = new Image($imageFile); + $avatarFile = $this->request->getFile('avatar'); + if ($avatarFile !== null && $avatarFile->isValid()) { + $person->avatar = new Image($avatarFile); } $personModel = new PersonModel(); @@ -119,8 +119,8 @@ class PersonController extends BaseController public function attemptEdit(): RedirectResponse { $rules = [ - 'image' => - 'is_image[image]|ext_in[image,jpg,jpeg,png]|min_dims[image,400,400]|is_image_squared[image]', + 'avatar' => + 'is_image[avatar]|ext_in[avatar,jpg,jpeg,png]|min_dims[avatar,400,400]|is_image_ratio[avatar,1,1]', ]; if (! $this->validate($rules)) { @@ -134,9 +134,9 @@ class PersonController extends BaseController $this->person->unique_name = $this->request->getPost('unique_name'); $this->person->information_url = $this->request->getPost('information_url'); - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $this->person->image = new Image($imageFile); + $avatarFile = $this->request->getFile('avatar'); + if ($avatarFile !== null && $avatarFile->isValid()) { + $this->person->avatar = new Image($avatarFile); } $this->person->updated_by = user_id(); diff --git a/modules/Admin/Controllers/PodcastController.php b/modules/Admin/Controllers/PodcastController.php index dfdfc57be3f992afc35d948a66e419751814a60f..d2cae11f7334df61660371f7981531e9c22d0fc7 100644 --- a/modules/Admin/Controllers/PodcastController.php +++ b/modules/Admin/Controllers/PodcastController.php @@ -171,8 +171,9 @@ class PodcastController extends BaseController public function attemptCreate(): RedirectResponse { $rules = [ - 'image' => - 'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'uploaded[cover]|is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', + 'banner' => 'is_image[banner]|ext_in[banner,jpg,png]|min_dims[banner,1500,500]|is_image_ratio[banner,3,1]', ]; if (! $this->validate($rules)) { @@ -195,7 +196,7 @@ class PodcastController extends BaseController 'title' => $this->request->getPost('title'), 'handle' => $this->request->getPost('handle'), 'description_markdown' => $this->request->getPost('description'), - 'image' => new Image($this->request->getFile('image')), + 'cover' => new Image($this->request->getFile('cover')), 'language_code' => $this->request->getPost('language'), 'category_id' => $this->request->getPost('category'), 'parental_advisory' => @@ -224,6 +225,11 @@ class PodcastController extends BaseController 'updated_by' => user_id(), ]); + $bannerFile = $this->request->getFile('banner'); + if ($bannerFile !== null && $bannerFile->isValid()) { + $podcast->banner = new Image($bannerFile); + } + $podcastModel = new PodcastModel(); $db = db_connect(); @@ -279,8 +285,9 @@ class PodcastController extends BaseController public function attemptEdit(): RedirectResponse { $rules = [ - 'image' => - 'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', + 'banner' => 'is_image[banner]|ext_in[banner,jpg,png]|min_dims[banner,1500,500]|is_image_ratio[banner,3,1]', ]; if (! $this->validate($rules)) { @@ -302,9 +309,13 @@ class PodcastController extends BaseController $this->podcast->title = $this->request->getPost('title'); $this->podcast->description_markdown = $this->request->getPost('description'); - $image = $this->request->getFile('image'); - if ($image !== null && $image->isValid()) { - $this->podcast->image = new Image($image); + $coverFile = $this->request->getFile('cover'); + if ($coverFile !== null && $coverFile->isValid()) { + $this->podcast->cover = new Image($coverFile); + } + $bannerFile = $this->request->getFile('banner'); + if ($bannerFile !== null && $bannerFile->isValid()) { + $this->podcast->banner = new Image($bannerFile); } $this->podcast->language_code = $this->request->getPost('language'); $this->podcast->category_id = $this->request->getPost('category'); @@ -356,6 +367,28 @@ class PodcastController extends BaseController return redirect()->route('podcast-view', [$this->podcast->id]); } + public function deleteBanner(): RedirectResponse + { + if ($this->podcast->banner === null) { + return redirect()->back(); + } + + $this->podcast->banner->delete(config('Images')->podcastBannerSizes); + + $this->podcast->banner_path = null; + $this->podcast->banner_mimetype = null; + + $podcastModel = new PodcastModel(); + if (! $podcastModel->update($this->podcast->id, $this->podcast)) { + return redirect() + ->back() + ->withInput() + ->with('errors', $podcastModel->errors()); + } + + return redirect()->back(); + } + public function latestEpisodes(int $limit, int $podcastId): string { $episodes = (new EpisodeModel()) diff --git a/modules/Admin/Controllers/PodcastImportController.php b/modules/Admin/Controllers/PodcastImportController.php index f3d7a4ebd7ab0245c8dcd72b5a311d54d877242e..cddb3989d07c8016a08a977ab598e31e8e805377 100644 --- a/modules/Admin/Controllers/PodcastImportController.php +++ b/modules/Admin/Controllers/PodcastImportController.php @@ -115,9 +115,9 @@ class PodcastImportController extends BaseController property_exists($nsItunes, 'image') && $nsItunes->image !== null && $nsItunes->image->attributes()['href'] !== null ) { - $imageFile = download_file((string) $nsItunes->image->attributes()['href']); + $coverFile = download_file((string) $nsItunes->image->attributes()['href']); } else { - $imageFile = download_file((string) $feed->channel[0]->image->url); + $coverFile = download_file((string) $feed->channel[0]->image->url); } $location = null; @@ -140,7 +140,8 @@ class PodcastImportController extends BaseController 'title' => (string) $feed->channel[0]->title, 'description_markdown' => $converter->convert($channelDescriptionHtml), 'description_html' => $channelDescriptionHtml, - 'image' => new Image($imageFile), + 'cover' => new Image($coverFile), + 'banner' => null, 'language_code' => $this->request->getPost('language'), 'category_id' => $this->request->getPost('category'), 'parental_advisory' => @@ -249,7 +250,7 @@ class PodcastImportController extends BaseController 'full_name' => $fullName, 'unique_name' => slugify($fullName), 'information_url' => $podcastPerson->attributes()['href'], - 'image' => new Image(download_file((string) $podcastPerson->attributes()['img'])), + 'avatar' => new Image(download_file((string) $podcastPerson->attributes()['img'])), 'created_by' => user_id(), 'updated_by' => user_id(), ]); @@ -326,9 +327,9 @@ class PodcastImportController extends BaseController property_exists($nsItunes, 'image') && $nsItunes->image !== null && $nsItunes->image->attributes()['href'] !== null ) { - $episodeImage = new Image(download_file((string) $nsItunes->image->attributes()['href'])); + $episodeCover = new Image(download_file((string) $nsItunes->image->attributes()['href'])); } else { - $episodeImage = null; + $episodeCover = null; } $location = null; @@ -351,7 +352,7 @@ class PodcastImportController extends BaseController ), 'description_markdown' => $converter->convert($itemDescriptionHtml), 'description_html' => $itemDescriptionHtml, - 'image' => $episodeImage, + 'cover' => $episodeCover, 'parental_advisory' => property_exists($nsItunes, 'explicit') && $nsItunes->explicit !== null ? (in_array((string) $nsItunes->explicit, ['yes', 'true'], true) @@ -402,7 +403,7 @@ class PodcastImportController extends BaseController 'full_name' => $fullName, 'unique_name' => slugify($fullName), 'information_url' => $episodePerson->attributes()['href'], - 'image' => new Image(download_file((string) $episodePerson->attributes()['img'])), + 'avatar' => new Image(download_file((string) $episodePerson->attributes()['img'])), 'created_by' => user_id(), 'updated_by' => user_id(), ]); diff --git a/modules/Admin/Controllers/SettingsController.php b/modules/Admin/Controllers/SettingsController.php index 1308df71f5e5531cf0c097b4e9b257ce86d4eb49..0362c8233f1a73c38df3a98fb14ed2d586ba11dd 100644 --- a/modules/Admin/Controllers/SettingsController.php +++ b/modules/Admin/Controllers/SettingsController.php @@ -25,7 +25,7 @@ class SettingsController extends BaseController { $rules = [ 'site_icon' => - 'is_image[site_icon]|ext_in[site_icon,png,jpeg]|is_image_squared[site_icon]|min_dims[image,512,512]|permit_empty', + 'is_image[site_icon]|ext_in[site_icon,png,jpeg]|is_image_ratio[site_icon,1,1]|min_dims[image,512,512]|permit_empty', ]; if (! $this->validate($rules)) { diff --git a/modules/Admin/Language/en/Common.php b/modules/Admin/Language/en/Common.php index f3a6a913645b70a88a65223795d1ba78a9751346..d105e76d0d9371f60ca4f9255fef0493b06527ae 100644 --- a/modules/Admin/Language/en/Common.php +++ b/modules/Admin/Language/en/Common.php @@ -38,8 +38,6 @@ return [ 'noChoicesText' => 'No choices to choose from', 'maxItemText' => 'Cannot add more items', ], - 'image_size_hint' => - 'Image must be squared with at least 1400px wide and tall.', 'upload_file' => 'Upload a file', 'remote_url' => 'Remote URL', ], diff --git a/modules/Admin/Language/en/Episode.php b/modules/Admin/Language/en/Episode.php index 073af0210a9d8747a8f2af251a33284272ce4649..87552b95ee25b8ca625db9d5c84dd983acdc386d 100644 --- a/modules/Admin/Language/en/Episode.php +++ b/modules/Admin/Language/en/Episode.php @@ -49,10 +49,10 @@ return [ 'audio_file' => 'Audio file', 'audio_file_hint' => 'Choose an .mp3 or .m4a audio file.', 'info_section_title' => 'Episode info', - 'info_section_subtitle' => '', - 'image' => 'Cover image', - 'image_hint' => - 'If you do not set an image, the podcast cover will be used instead.', + 'cover' => 'Episode cover', + 'cover_hint' => + 'If you do not set a cover, the podcast cover will be used instead.', + 'cover_size_hint' => 'Cover must be squared with at least 1400px wide and tall.', 'title' => 'Title', 'title_hint' => 'Should contain a clear and concise episode name. Do not specify the episode or season numbers here.', diff --git a/modules/Admin/Language/en/Person.php b/modules/Admin/Language/en/Person.php index 334e535154eb3bc113fb55420f36347b2febe12b..b9e122d2e40766f2a80ddabf3839f3b2ec5b97b9 100644 --- a/modules/Admin/Language/en/Person.php +++ b/modules/Admin/Language/en/Person.php @@ -17,9 +17,9 @@ return [ 'edit' => 'Edit person', 'delete' => 'Delete person', 'form' => [ - 'image' => 'Picture', - 'image_size_hint' => - 'Image must be squared with at least 400px wide and tall.', + 'avatar' => 'Avatar', + 'avatar_size_hint' => + 'Avatar must be squared with at least 400px wide and tall.', 'full_name' => 'Full name', 'full_name_hint' => 'This is the full name or alias of the person.', 'unique_name' => 'Unique name', diff --git a/modules/Admin/Language/en/Podcast.php b/modules/Admin/Language/en/Podcast.php index d2aaa10126b40522c520474f5181d2c2fb2bc0d0..2a40d5b7f81e886c477d48a94e505b5d001f5c49 100644 --- a/modules/Admin/Language/en/Podcast.php +++ b/modules/Admin/Language/en/Podcast.php @@ -25,7 +25,11 @@ return [ 'form' => [ 'identity_section_title' => 'Podcast identity', 'identity_section_subtitle' => 'These fields allow you to get noticed.', - 'image' => 'Cover image', + 'cover' => 'Podcast cover', + 'cover_size_hint' => 'Cover must be squared with at least 1400px wide and tall.', + 'banner' => 'Podcast banner', + 'banner_size_hint' => 'Banner must have a 3:1 ratio with at least 1500px wide.', + 'banner_delete' => 'Delete podcast banner', 'title' => 'Title', 'handle' => 'Handle', 'handle_hint' => diff --git a/modules/Admin/Language/en/Validation.php b/modules/Admin/Language/en/Validation.php index ae627c4b2122b0f3877da54c87c2ca7c0b5c10ba..eb04b85ff0ed2fdfd0f7f88f2821211ec239cfba 100644 --- a/modules/Admin/Language/en/Validation.php +++ b/modules/Admin/Language/en/Validation.php @@ -11,8 +11,8 @@ declare(strict_types=1); return [ 'min_dims' => '{field} is either not an image, or it is not wide or tall enough.', - 'is_image_squared' => - '{field} is either not an image, or it is not squared (width and height differ).', + 'is_image_ratio' => + '{field} is either not an image or not of the right ratio.', 'validate_url' => 'The {field} field must be a valid URL (eg. https://example.com/).', ]; diff --git a/modules/Admin/Language/fr/Common.php b/modules/Admin/Language/fr/Common.php index 8788589f73503eb75cb98ccd2946ef714362c3f8..f856cf907362fa5b5565cf524b7d47c47e245457 100644 --- a/modules/Admin/Language/fr/Common.php +++ b/modules/Admin/Language/fr/Common.php @@ -38,8 +38,6 @@ return [ 'noChoicesText' => 'Aucune sélection possible', 'maxItemText' => 'Impossible de rajouter un élément', ], - 'image_size_hint' => - 'L’image doit être carrée, avec au minimum 1400px de long et de large.', 'upload_file' => 'Téléversez un fichier', 'remote_url' => 'URL distante', ], diff --git a/modules/Admin/Language/fr/Episode.php b/modules/Admin/Language/fr/Episode.php index b0cc9c80e9ca2cadfb8cdb9e676c9b770073a90d..ad3cf9713f098bea38e1a8954527ef738f0b35b2 100644 --- a/modules/Admin/Language/fr/Episode.php +++ b/modules/Admin/Language/fr/Episode.php @@ -50,10 +50,10 @@ return [ 'audio_file' => 'Fichier audio', 'audio_file_hint' => 'Sélectionnez un fichier audio .mp3 ou .m4a.', 'info_section_title' => 'Informations épisode', - 'info_section_subtitle' => '', - 'image' => 'Image de couverture', - 'image_hint' => + 'cover' => 'Image de couverture', + 'cover_hint' => 'Si vous ne définissez pas d’image, celle du podcast sera utilisée à la place.', + 'cover_size_hint' => 'La couverture de l’épisode doit être carrée, avec au minimum 1400px de largeur et de hauteur.', 'title' => 'Titre', 'title_hint' => 'Doit contenir un titre d’épisode clair et concis. Ne précisez ici aucun numéro de saison ou d’épisode.', diff --git a/modules/Admin/Language/fr/Person.php b/modules/Admin/Language/fr/Person.php index db2e1ab4916dd9d3f70b36234977d8155316cfd6..aa77d8777a25f57dc7d032ad5db063549bdafb70 100644 --- a/modules/Admin/Language/fr/Person.php +++ b/modules/Admin/Language/fr/Person.php @@ -17,8 +17,8 @@ return [ 'edit' => 'Modifier l’intervenant', 'delete' => 'Supprimer l’intervenant', 'form' => [ - 'image' => 'Photo', - 'image_size_hint' => + 'avatar' => 'Avatar', + 'avatar_size_hint' => 'L’image doit être carrée et avoir au moins 400px de largeur et de hauteur.', 'full_name' => 'Nom complet', 'full_name_hint' => 'Le nom complet ou le pseudonyme de l’intervenant', diff --git a/modules/Admin/Language/fr/Podcast.php b/modules/Admin/Language/fr/Podcast.php index 0d332120f9e7ec2a15c58f4fcb911d16c250459a..268014e3ccdea51c318df842f04de00314e2cc6b 100644 --- a/modules/Admin/Language/fr/Podcast.php +++ b/modules/Admin/Language/fr/Podcast.php @@ -26,7 +26,11 @@ return [ 'identity_section_title' => 'Informations sur le Podcast', 'identity_section_subtitle' => 'Ces champs vous permettent de vous faire remarquer.', - 'image' => 'Image de couverture', + 'cover' => 'Couverture du podcast', + 'cover_size_hint' => 'La couverture du podcast doit être carrée, avec au minimum 1400px de largeur et de hauteur.', + 'banner' => 'Bannière du podcast', + 'banner_size_hint' => 'La bannière doit être au format 3/1, avec au minimum 1500px de largeur.', + 'banner_delete' => 'Supprimer la bannière du podcast', 'title' => 'Titre', 'handle' => 'Identifiant', 'handle_hint' => diff --git a/modules/Admin/Language/fr/Settings.php b/modules/Admin/Language/fr/Settings.php index e68c87c7a2f1f477a013da81f408c9204c21721b..08cb0a64ac275af43cb1604372aa519a5ca473ed 100644 --- a/modules/Admin/Language/fr/Settings.php +++ b/modules/Admin/Language/fr/Settings.php @@ -16,7 +16,7 @@ return [ 'site_icon' => 'Favicon du site', 'site_icon_delete' => 'Supprimer la favicon du site', 'site_icon_hint' => 'Les favicons sont ce que vous voyez sur les onglets de votre navigateur, dans votre barre de favoris, et lorsque vous ajoutez un site web en raccourci sur des appareils mobiles.', - 'site_icon_helper' => 'La favicon doit être carrée et avoir au moins 512px de largeur et de hauteur.', + 'site_icon_helper' => 'La favicon doit être carrée, avec au minimum 512px de largeur et de hauteur.', 'site_name' => 'Titre du site', 'site_description' => 'Description du site', 'submit' => 'Save', diff --git a/modules/Admin/Language/fr/Validation.php b/modules/Admin/Language/fr/Validation.php index 12b45319f92a4522f54f9c0bdba3ec39dbe8a00c..ec1d4f130d7e91be3c48682874b33b83577f1001 100644 --- a/modules/Admin/Language/fr/Validation.php +++ b/modules/Admin/Language/fr/Validation.php @@ -11,8 +11,8 @@ declare(strict_types=1); return [ 'min_dims' => '{field} n’est pas une image ou n’a pas la taille minimale requise.', - 'is_image_squared' => - '{field} n’est pas une image ou n’est pas carré (largeur et hauteur différentes).', + 'is_image_ratio' => + '{field} n’est pas une image ou n’est pas au bon format.', 'validate_url' => 'Le champs {field} doit être une adresse valide (par exemple https://exemple.com/).', ]; diff --git a/phpstan.neon b/phpstan.neon index 88e1055aef8620445610761e9c1b9d49eb196315..253465986b03f638d80ed7ab8b6c1c9800479d4e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -28,6 +28,7 @@ parameters: - '#Function \"preg_.*\(\)\" cannot be used/left in the code#' - '#Function "property_exists\(\)" cannot be used/left in the code#' - '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(<desired_type\>\)\)\-\>isSuperTypeOf\(<element_type\>\)" for static reflection to work#' + - '#^Access to an undefined property App\\Entities\\Image#' - message: '#Function "function_exists\(\)" cannot be used/left in the code#' paths: diff --git a/public/media/castopod-cover-default.jpg b/public/media/castopod-banner-default.jpg similarity index 100% rename from public/media/castopod-cover-default.jpg rename to public/media/castopod-banner-default.jpg diff --git a/public/media/castopod-banner-default_large.jpg b/public/media/castopod-banner-default_large.jpg new file mode 100644 index 0000000000000000000000000000000000000000..938e3bef51fb1a7eed49f0559a0e4e289093ab3c Binary files /dev/null and b/public/media/castopod-banner-default_large.jpg differ diff --git a/public/media/castopod-banner-default_medium.jpg b/public/media/castopod-banner-default_medium.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f951c7de456d82c5e95cd5dfe040b0f8151c32f2 Binary files /dev/null and b/public/media/castopod-banner-default_medium.jpg differ diff --git a/public/media/castopod-banner-default_small.jpg b/public/media/castopod-banner-default_small.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dd98b87d3883e724477ef000e2c78774d21ae0f0 Binary files /dev/null and b/public/media/castopod-banner-default_small.jpg differ diff --git a/themes/cp_admin/_partials/_nav_header.php b/themes/cp_admin/_partials/_nav_header.php index fc726afd5014e020cf058837c1ce9de56db473cb..e9a6f9dd21b6b9ceeeca8f2a41ebde385641a079 100644 --- a/themes/cp_admin/_partials/_nav_header.php +++ b/themes/cp_admin/_partials/_nav_header.php @@ -36,7 +36,7 @@ $interactButtons .= <<<CODE_SAMPLE <button class="inline-flex items-center w-full px-4 py-1 hover:bg-gray-100" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}"> - <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->image->thumbnail_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> + <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> </button> CODE_SAMPLE; } diff --git a/themes/cp_admin/episode/_card.php b/themes/cp_admin/episode/_card.php index a77f478b0003c21ff42461ffcfb150056e2e4efe..0dcee50e989224f1e445046666872e0214233035 100644 --- a/themes/cp_admin/episode/_card.php +++ b/themes/cp_admin/episode/_card.php @@ -2,7 +2,7 @@ <a href="<?= route_to('episode-view', $episode->podcast->id, $episode->id) ?>" class="flex flex-col justify-end w-full h-full text-white group"> <div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div> <div class="w-full h-full overflow-hidden"> - <img src="<?= $episode->image->medium_url ?>" alt="<?= $episode->title ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + <img src="<?= $episode->cover->medium_url ?>" alt="<?= $episode->title ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> </div> <?= publication_pill($episode->published_at, $episode->publication_status, 'absolute top-0 left-0 ml-2 mt-2 text-sm'); ?> <div class="absolute z-20 flex flex-col items-start px-4 py-2"> diff --git a/themes/cp_admin/episode/_sidebar.php b/themes/cp_admin/episode/_sidebar.php index 8dba09e1fac7116425ab2208fc0ca271ea967a2c..ee91ce6a7a514cc76fcf4ebce49917db84f6ade5 100644 --- a/themes/cp_admin/episode/_sidebar.php +++ b/themes/cp_admin/episode/_sidebar.php @@ -10,7 +10,7 @@ $podcastNavigation = [ <a href="<?= route_to('podcast-view', $podcast->id) ?>" class="flex items-center px-4 py-2 border-b border-pine-900 focus:ring-inset focus:ring-castopod"> <?= icon('arrow-left', 'mr-2') ?> <img - src="<?= $podcast->image->thumbnail_url ?>" + src="<?= $podcast->cover->tiny_url ?>" alt="<?= $podcast->title ?>" class="object-cover w-6 h-6 rounded" /> @@ -18,7 +18,7 @@ $podcastNavigation = [ </a> <div class="flex items-center px-4 py-2 border-b border-pine-900"> <img - src="<?= $episode->image->thumbnail_url ?>" + src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" class="object-cover w-16 h-16 rounded" /> diff --git a/themes/cp_admin/episode/create.php b/themes/cp_admin/episode/create.php index d97554583d6faf9a5a0bbf9ae990a35de869e0c5..41ac3e152cd0038b25a8fc4cb5e0c207f44e7483 100644 --- a/themes/cp_admin/episode/create.php +++ b/themes/cp_admin/episode/create.php @@ -28,10 +28,10 @@ required="true" /> <Forms.Field - name="image" - label="<?= lang('Episode.form.image') ?>" - hint="<?= lang('Episode.form.image_hint') ?>" - helper="<?= lang('Common.forms.image_size_hint', ) ?>" + name="cover" + label="<?= lang('Episode.form.cover') ?>" + hint="<?= lang('Episode.form.cover_hint') ?>" + helper="<?= lang('Episode.form.cover_size_hint', ) ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/episode/edit.php b/themes/cp_admin/episode/edit.php index 1f4d87f6268dd9f48857ac0852ab0618b355bca8..2bd9e46dc932b5f21af802ca598d1e1b113b2deb 100644 --- a/themes/cp_admin/episode/edit.php +++ b/themes/cp_admin/episode/edit.php @@ -31,10 +31,10 @@ accept=".mp3,.m4a" /> <Forms.Field - name="image" - label="<?= lang('Episode.form.image') ?>" - hint="<?= lang('Episode.form.image_hint') ?>" - helper="<?= lang('Common.forms.image_size_hint', ) ?>" + name="cover" + label="<?= lang('Episode.form.cover') ?>" + hint="<?= lang('Episode.form.cover_hint') ?>" + helper="<?= lang('Episode.form.cover_size_hint', ) ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php index 9f571ef53fa76d9c6c35e519e300285636e1ea5b..1e8d29df78e93eb68ab631ea16ba8186b3d22a42 100644 --- a/themes/cp_admin/episode/list.php +++ b/themes/cp_admin/episode/list.php @@ -34,7 +34,7 @@ $episode->audio_file_duration, ) . '</time>' . - '<img loading="lazy" src="' . $episode->image->thumbnail_url . '" alt="' . $episode->title . '" class="object-cover w-20 h-20 rounded-lg" />' . + '<img loading="lazy" src="' . $episode->cover->thumbnail_url . '" alt="' . $episode->title . '" class="object-cover w-20 h-20 rounded-lg" />' . '</div>' . '<a class="overflow-x-hidden text-sm hover:underline" href="' . route_to( 'episode-view', diff --git a/themes/cp_admin/episode/persons.php b/themes/cp_admin/episode/persons.php index 93ed8e400c9f3c5c449ba7974186b7fe62002569..214aeb6b5cc6dd7d70fef3f7cf1c3c63c18ee9ab 100644 --- a/themes/cp_admin/episode/persons.php +++ b/themes/cp_admin/episode/persons.php @@ -58,7 +58,7 @@ return '<div class="flex">' . '<a href="' . route_to('person-view', $person->id) . - "\"><img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . + "\"><img src=\"{$person->avatar->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . '<div class="flex flex-col ml-3">' . $person->full_name . implode( diff --git a/themes/cp_admin/episode/publish.php b/themes/cp_admin/episode/publish.php index 11af5a63fda9ff1d62fc368514fb6e71e55a5033..10b69950628e616f666b99170d42afae5c0d6f83 100644 --- a/themes/cp_admin/episode/publish.php +++ b/themes/cp_admin/episode/publish.php @@ -40,7 +40,7 @@ <Forms.Textarea name="message" placeholder="<?= lang('Episode.publish_form.message_placeholder') ?>" autofocus="" rows="2" /> </div> <div class="flex border-t border-b"> - <img src="<?= $episode->image + <img src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2"> diff --git a/themes/cp_admin/episode/publish_edit.php b/themes/cp_admin/episode/publish_edit.php index cd6de4b545fddbcda855431ab5f7232efd8832dc..d5127e062b74cce47f3fa30d6d8787a4bb4ca1f7 100644 --- a/themes/cp_admin/episode/publish_edit.php +++ b/themes/cp_admin/episode/publish_edit.php @@ -42,7 +42,7 @@ <Forms.Textarea name="message" placeholder="<?= lang('Episode.publish_form.message_placeholder') ?>" autofocus="" value="<?= $post->message ?>" rows="2" /> </div> <div class="flex border-t border-b"> - <img src="<?= $episode->image + <img src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2"> diff --git a/themes/cp_admin/person/_card.php b/themes/cp_admin/person/_card.php index c504982924920bc3bf921a87c5aaa8b19a5097bf..8301cc3e2cc2cf6b9771e98e3689f22bde6900cb 100644 --- a/themes/cp_admin/person/_card.php +++ b/themes/cp_admin/person/_card.php @@ -2,7 +2,7 @@ <a href="<?= route_to('person-view', $person->id) ?>" class="flex flex-col justify-end w-full h-full text-white group"> <div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div> <div class="w-full h-full overflow-hidden"> - <img alt="<?= $person->full_name ?>" src="<?= $person->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + <img alt="<?= $person->full_name ?>" src="<?= $person->avatar->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> </div> <div class="absolute z-20"> <h2 class="px-4 py-2 font-semibold leading-tight"><?= $person->full_name ?></h2> diff --git a/themes/cp_admin/person/create.php b/themes/cp_admin/person/create.php index 531603e51599b4dc9ee9b357ee7870d1682cf1d8..a7e2ec01931f56a793a46446623a40dfc1feeed0 100644 --- a/themes/cp_admin/person/create.php +++ b/themes/cp_admin/person/create.php @@ -15,9 +15,9 @@ <?= csrf_field() ?> <Forms.Field - name="image" - label="<?= lang('Person.form.image') ?>" - helper="<?= lang('Person.form.image_size_hint') ?>" + name="avatar" + label="<?= lang('Person.form.avatar') ?>" + helper="<?= lang('Person.form.avatar_size_hint') ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/person/edit.php b/themes/cp_admin/person/edit.php index 7ce8966e79db141d7e5271d3d80db5776e4d59a9..803bd98439a132c70109b82d9e5ccc4d45457b13 100644 --- a/themes/cp_admin/person/edit.php +++ b/themes/cp_admin/person/edit.php @@ -15,9 +15,9 @@ <?= csrf_field() ?> <Forms.Field - name="image" - label="<?= lang('Person.form.image') ?>" - helper="<?= lang('Person.form.image_size_hint') ?>" + name="avatar" + label="<?= lang('Person.form.avatar') ?>" + helper="<?= lang('Person.form.avatar_size_hint') ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/person/view.php b/themes/cp_admin/person/view.php index 395b6d4416efd540d522ed217bc53d2efac467e0..fa17865fe6f2ce31fa7d686eaab6a4a05ae7ba6c 100644 --- a/themes/cp_admin/person/view.php +++ b/themes/cp_admin/person/view.php @@ -18,7 +18,7 @@ <div class="flex flex-wrap"> <div class="w-full max-w-sm mb-6 md:mr-4"> <img - src="<?= $person->image->medium_url ?>" + src="<?= $person->avatar->medium_url ?>" alt="$person->full_name" class="object-cover w-full rounded" /> diff --git a/themes/cp_admin/podcast/_card.php b/themes/cp_admin/podcast/_card.php index 8db6030858e6ab32401e426b09e3f8ac194ff1ce..ea4d9b8879f6c63550e3aabeca1d99c96e97e9ab 100644 --- a/themes/cp_admin/podcast/_card.php +++ b/themes/cp_admin/podcast/_card.php @@ -4,7 +4,7 @@ <div class="w-full h-full overflow-hidden"> <img alt="<?= $podcast->title ?>" - src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + src="<?= $podcast->cover->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> </div> <div class="absolute z-20 px-4 pb-4 transition duration-75 ease-out translate-y-6 group-focus:translate-y-0 group-hover:translate-y-0"> <h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2> diff --git a/themes/cp_admin/podcast/_sidebar.php b/themes/cp_admin/podcast/_sidebar.php index 9c0a7c06150634cd1551cf77a822544dc698ef8b..21a54dbaf2ef4d49be542091eb8b50b371d22eeb 100644 --- a/themes/cp_admin/podcast/_sidebar.php +++ b/themes/cp_admin/podcast/_sidebar.php @@ -37,7 +37,7 @@ $podcastNavigation = [ <div class="flex items-center px-4 py-2 border-b border-pine-900"> <img - src="<?= $podcast->image->thumbnail_url ?>" + src="<?= $podcast->cover->thumbnail_url ?>" alt="<?= $podcast->title ?>" class="object-cover w-16 h-16 rounded" /> diff --git a/themes/cp_admin/podcast/create.php b/themes/cp_admin/podcast/create.php index 0ef31938e2dbc84f793843ac4d199bb1d5694d2c..3de000c407132bb1dc0f439f07c0329a56cc7115 100644 --- a/themes/cp_admin/podcast/create.php +++ b/themes/cp_admin/podcast/create.php @@ -14,22 +14,28 @@ <?= $this->section('content') ?> -<form action="<?= route_to('podcast-create') ?>" method="POST" enctype='multipart/form-data' class="flex flex-col"> +<form action="<?= route_to('podcast-create') ?>" method="POST" enctype='multipart/form-data' class="flex flex-col gap-y-6"> <?= csrf_field() ?> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.identity_section_title') ?>" subtitle="<?= lang('Podcast.form.identity_section_subtitle') ?>" > <Forms.Field - name="image" - label="<?= lang('Podcast.form.image') ?>" - helper="<?= lang('Common.forms.image_size_hint') ?>" + name="cover" + label="<?= lang('Podcast.form.cover') ?>" + helper="<?= lang('Podcast.form.cover_size_hint') ?>" type="file" required="true" accept=".jpg,.jpeg,.png" /> +<Forms.Field + name="banner" + label="<?= lang('Podcast.form.banner') ?>" + helper="<?= lang('Podcast.form.banner_size_hint') ?>" + type="file" + accept=".jpg,.jpeg,.png" /> + <Forms.Field name="title" label="<?= lang('Podcast.form.title') ?>" @@ -67,7 +73,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.classification_section_title') ?>" subtitle="<?= lang('Podcast.form.classification_section_subtitle') ?>" > @@ -114,7 +119,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.author_section_title') ?>" subtitle="<?= lang('Podcast.form.author_section_subtitle') ?>" > @@ -143,7 +147,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.location_section_title') ?>" subtitle="<?= lang('Podcast.form.location_section_subtitle') ?>" > @@ -155,7 +158,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.monetization_section_title') ?>" subtitle="<?= lang('Podcast.form.monetization_section_subtitle') ?>" > @@ -184,7 +186,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.advanced_section_title') ?>" subtitle="<?= lang('Podcast.form.advanced_section_subtitle') ?>" > @@ -197,7 +198,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.status_section_title') ?>" > <Forms.Toggler class="mb-2" name="lock" value="yes" checked="true" hint="<?= lang('Podcast.form.lock_hint') ?>"> <?= lang('Podcast.form.lock') ?> diff --git a/themes/cp_admin/podcast/edit.php b/themes/cp_admin/podcast/edit.php index 71245af11b96627e867273a78756c83f2e79d4b8..2f5874cc23237c3f66252006a8f117c1dd3d7f0b 100644 --- a/themes/cp_admin/podcast/edit.php +++ b/themes/cp_admin/podcast/edit.php @@ -18,18 +18,41 @@ <?= $this->section('content') ?> -<form id="podcast-edit-form" action="<?= route_to('podcast-edit', $podcast->id) ?>" method="POST" enctype='multipart/form-data' class="flex flex-col"> +<form id="podcast-edit-form" action="<?= route_to('podcast-edit', $podcast->id) ?>" method="POST" enctype='multipart/form-data' class="flex flex-row-reverse flex-wrap items-start justify-end gap-4"> <?= csrf_field() ?> +<div class="sticky z-40 flex flex-col w-full max-w-xs overflow-hidden bg-white shadow-sm border-3 border-pine-100 top-24 rounded-xl"> + <?php if ($podcast->banner_path !== null): ?> + <a href="<?= route_to('podcast-banner-delete', $podcast->id) ?>" class="absolute p-1 text-white bg-red-600 border-2 border-black rounded-full hover:bg-red-800 focus:ring-castopod top-2 right-2" title="<?= lang('Podcast.form.banner_delete') ?>"><?= icon('delete-bin') ?></a> + <?php endif; ?> + <img src="<?= $podcast->banner->small_url ?>" alt="" class="object-cover w-full aspect-[3/1] bg-pine-800" /> + <div class="flex px-4 py-2"> + <img src="<?= $podcast->cover->thumbnail_url ?>" alt="<?= $podcast->title ?>" + class="w-16 h-16 mr-4 -mt-8 rounded-full ring-2 ring-white" /> + <div class="flex flex-col"> + <p class="font-semibold leading-none"><?= $podcast->title ?></p> + <p class="text-sm text-gray-500">@<?= $podcast->handle ?></p> + </div> + </div> +</div> + +<div class="flex flex-col gap-y-6"> + <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.identity_section_title') ?>" subtitle="<?= lang('Podcast.form.identity_section_subtitle') ?>" > <Forms.Field - name="image" - label="<?= lang('Podcast.form.image') ?>" - helper="<?= lang('Common.forms.image_size_hint') ?>" + name="cover" + label="<?= lang('Podcast.form.cover') ?>" + helper="<?= lang('Podcast.form.cover_size_hint') ?>" + type="file" + accept=".jpg,.jpeg,.png" /> + +<Forms.Field + name="banner" + label="<?= lang('Podcast.form.banner') ?>" + helper="<?= lang('Podcast.form.banner_size_hint') ?>" type="file" accept=".jpg,.jpeg,.png" /> @@ -65,7 +88,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.classification_section_title') ?>" subtitle="<?= lang('Podcast.form.classification_section_subtitle') ?>" > @@ -114,7 +136,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.author_section_title') ?>" subtitle="<?= lang('Podcast.form.author_section_subtitle') ?>" > @@ -147,7 +168,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.location_section_title') ?>" subtitle="<?= lang('Podcast.form.location_section_subtitle') ?>" > @@ -160,7 +180,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.monetization_section_title') ?>" subtitle="<?= lang('Podcast.form.monetization_section_subtitle') ?>" > @@ -190,7 +209,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.advanced_section_title') ?>" subtitle="<?= lang('Podcast.form.advanced_section_subtitle') ?>" > @@ -204,7 +222,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.status_section_title') ?>" > <Forms.Toggler class="mb-2" name="lock" value="yes" checked="<?= $podcast->is_locked ? 'true' : 'false' ?>" hint="<?= lang('Podcast.form.lock_hint') ?>"> <?= lang('Podcast.form.lock') ?> @@ -217,6 +234,9 @@ </Forms.Toggler> </Forms.Section> +<Button variant="primary" type="submit" class="self-end"><?= lang('Podcast.form.submit_edit') ?></Button> +</div> + </form> <?= $this->endSection() ?> diff --git a/themes/cp_admin/podcast/latest_episodes.php b/themes/cp_admin/podcast/latest_episodes.php index 756b0a7051f42734c65e36539d55a1c1f4b199d4..a90cefbecd90227d826779c764734565fb588c02 100644 --- a/themes/cp_admin/podcast/latest_episodes.php +++ b/themes/cp_admin/podcast/latest_episodes.php @@ -1,5 +1,5 @@ <section class="flex flex-col"> - <header class="flex justify-between py-2"> + <header class="flex justify-between"> <Heading tagName="h2"><?= lang('Podcast.latest_episodes') ?></Heading> <a href="<?= route_to( 'episode-list', @@ -10,7 +10,7 @@ </a> </header> <?php if ($episodes): ?> - <div class="grid px-4 py-2 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity"> + <div class="grid px-4 pt-2 pb-5 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity"> <?php foreach ($episodes as $episode): ?> <?= view('episode/_card', [ 'episode' => $episode, diff --git a/themes/cp_admin/podcast/persons.php b/themes/cp_admin/podcast/persons.php index c75a46b1cf277cdcf0aa0d01e61e8f2c16492be0..1255253764aa792928e51a3546779550668a3c1b 100644 --- a/themes/cp_admin/podcast/persons.php +++ b/themes/cp_admin/podcast/persons.php @@ -55,7 +55,7 @@ return '<div class="flex">' . '<a href="' . route_to('person-view', $person->id) . - "\"><img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . + "\"><img src=\"{$person->avatar->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . '<div class="flex flex-col ml-3">' . $person->full_name . implode( diff --git a/themes/cp_admin/settings/general.php b/themes/cp_admin/settings/general.php index f48bc581365866c1b2e8eff88154d38c36e7b2e6..dc51fa4060ebc58ddb0ad47e8041363ee5d0a58e 100644 --- a/themes/cp_admin/settings/general.php +++ b/themes/cp_admin/settings/general.php @@ -10,7 +10,7 @@ <?= $this->section('content') ?> -<form action="<?= route_to('settings-instance') ?>" method="POST" class="flex flex-col max-w-sm gap-y-4" enctype="multipart/form-data"> +<form action="<?= route_to('settings-instance') ?>" method="POST" class="flex flex-col gap-y-4" enctype="multipart/form-data"> <?= csrf_field() ?> <Forms.Section diff --git a/themes/cp_app/_admin_navbar.php b/themes/cp_app/_admin_navbar.php index 69a32a1f40eee9deee94769bf99abd09c39bc869..da61c281287b4702733074d4e29e86a559e70ac7 100644 --- a/themes/cp_app/_admin_navbar.php +++ b/themes/cp_app/_admin_navbar.php @@ -31,7 +31,7 @@ $interactButtons .= <<<CODE_SAMPLE <button class="inline-flex items-center w-full px-4 py-1 hover:bg-gray-100" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}"> - <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->image->thumbnail_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> + <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> </button> CODE_SAMPLE; } diff --git a/themes/cp_app/_persons_modal.php b/themes/cp_app/_persons_modal.php index 6822ed011873c1e93664329e33f089bbb6fed963..59384b4c82635a1be6e3b7a830a7345131056e74 100644 --- a/themes/cp_app/_persons_modal.php +++ b/themes/cp_app/_persons_modal.php @@ -17,7 +17,7 @@ <div class="flex flex-col items-start p-4 gap-y-4"> <?php foreach ($persons as $person): ?> <div class="flex gap-x-2"> - <img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-10 h-10 rounded-full" /> + <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-10 h-10 rounded-full" /> <div class="flex flex-col"> <h4 class="text-sm font-semibold"> <?php if ($person->information_url): ?> diff --git a/themes/cp_app/embed.php b/themes/cp_app/embed.php index 612a5f1f039f191eb7884c62755bbc1adc6573d4..f3208bd443ddfbd7f4c61cbefe1b4a9b41cadfe4 100644 --- a/themes/cp_app/embed.php +++ b/themes/cp_app/embed.php @@ -20,7 +20,7 @@ </head> <body class="flex" style="background: <?= $themeData['background'] ?>; color: <?= $themeData['text'] ?>;"> - <img src="<?= $episode->image->thumbnail_url ?>" alt="<?= $episode->title ?>" class="flex-shrink w-36 h-36" /> + <img src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" class="flex-shrink w-36 h-36" /> <div class="flex flex-col items-start flex-1 min-w-0 px-4 pt-4 h-36"> <a href="https://castopod.org/" class="absolute top-0 right-0 mt-1 mr-2 text-2xl text-pine-500 hover:opacity-75" title="<?= lang('Common.powered_by', [ 'castopod' => 'Castopod', diff --git a/themes/cp_app/episode/_layout.php b/themes/cp_app/episode/_layout.php index 813c198387aa042ff1192878f1e4125291cb4d80..3fd93685ad8889649a55da38a2fb8782d1b61b4b 100644 --- a/themes/cp_app/episode/_layout.php +++ b/themes/cp_app/episode/_layout.php @@ -40,7 +40,7 @@ ]) ?>"> <?= icon('arrow-left', 'mr-2 text-lg') ?> <div class="inline-flex items-center gap-x-2"> - <img class="w-8 h-8 rounded-full" src="<?= $episode->podcast->image->thumbnail_url ?>" alt="<?= $episode->podcast->title ?>" /> + <img class="w-8 h-8 rounded-full" src="<?= $episode->podcast->cover->tiny_url ?>" alt="<?= $episode->podcast->title ?>" /> <div class="flex flex-col"> <span class="text-sm font-semibold leading-none"><?= $episode->podcast->title ?></span> <span class="text-xs"><?= lang('Podcast.followers', [ @@ -69,10 +69,10 @@ </div> </nav> <header class="relative z-50 flex flex-col col-start-2 px-8 pt-8 pb-4 overflow-hidden bg-pine-500 gap-y-4"> - <div class="absolute top-0 left-0 w-full h-full bg-center bg-no-repeat bg-cover blur-lg mix-blend-luminosity" style="background-image: url('<?= $episode->podcast->image->thumbnail_url ?>');"></div> + <div class="absolute top-0 left-0 w-full h-full bg-center bg-no-repeat bg-cover blur-lg mix-blend-soft-light" style="background-image: url('<?= $episode->podcast->banner->small_url ?>');"></div> <div class="absolute top-0 left-0 w-full h-full bg-gradient-to-t from-pine-800 to-transparent"></div> <div class="z-10 flex flex-col items-start gap-y-2 gap-x-4 sm:flex-row"> - <img src="<?= $episode->image->medium_url ?>" alt="<?= $episode->title ?>" loading="lazy" class="rounded-md h-36" /> + <img src="<?= $episode->cover->medium_url ?>" alt="<?= $episode->title ?>" loading="lazy" class="rounded-md h-36" /> <div class="flex flex-col items-start text-white"> <?= episode_numbering($episode->number, $episode->season_number, 'bg-pine-50 text-sm leading-none font-semibold text-gray-700 border !no-underline border-pine-100', true) ?> <h1 class="inline-flex items-baseline max-w-md mt-2 text-2xl font-bold leading-none sm:text-3xl font-display line-clamp-2"><?= $episode->title ?></h1> @@ -82,7 +82,7 @@ <div class="inline-flex flex-row-reverse"> <?php $i = 0; ?> <?php foreach ($episode->persons as $person): ?> - <img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> + <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> <?php $i++; if ($i === 3) { break; }?> @@ -102,7 +102,7 @@ <div class="z-10 inline-flex items-center text-white gap-x-4"> <play-episode-button id="<?= $episode->id ?>" - imageSrc="<?= $episode->image->thumbnail_url ?>" + imageSrc="<?= $episode->cover->thumbnail_url ?>" title="<?= $episode->title ?>" podcast="<?= $episode->podcast->title ?>" src="<?= $episode->audio_file_web_url ?>" diff --git a/themes/cp_app/episode/_partials/card.php b/themes/cp_app/episode/_partials/card.php index bb431559269419599b975bd779e7f5f08b298255..27b31811834b38df640102c802dd0b807f5cd9b9 100644 --- a/themes/cp_app/episode/_partials/card.php +++ b/themes/cp_app/episode/_partials/card.php @@ -3,7 +3,7 @@ <time class="absolute px-1 text-xs font-semibold text-white rounded bottom-2 right-2 bg-black/50" datetime="PT<?= $episode->audio_file_duration ?>S"> <?= format_duration($episode->audio_file_duration) ?> </time> - <img loading="lazy" src="<?= $episode->image + <img loading="lazy" src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="object-cover w-20 h-20 rounded-lg" /> </div> <div class="flex items-center flex-1 gap-x-4"> @@ -16,7 +16,7 @@ </div> <play-episode-button id="<?= $episode->id ?>" - imageSrc="<?= $episode->image->thumbnail_url ?>" + imageSrc="<?= $episode->cover->thumbnail_url ?>" title="<?= $episode->title ?>" podcast="<?= $episode->podcast->title ?>" src="<?= $episode->audio_file_web_url ?>" diff --git a/themes/cp_app/episode/_partials/preview_card.php b/themes/cp_app/episode/_partials/preview_card.php index a9dfe3b9cff08cfa84f861aa88d42cba53cbe202..04a91b5bcd472d65c9ee3e0e731f60f9505fd58b 100644 --- a/themes/cp_app/episode/_partials/preview_card.php +++ b/themes/cp_app/episode/_partials/preview_card.php @@ -4,7 +4,7 @@ <?= format_duration($episode->audio_file_duration) ?> </time> <img - src="<?= $episode->image->thumbnail_url ?>" + src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24"/> </div> <div class="flex flex-col flex-1 px-4 py-2"> @@ -17,7 +17,7 @@ <play-episode-button class="mr-4" id="<?= $index . '_' . $episode->id ?>" - imageSrc="<?= $episode->image->thumbnail_url ?>" + imageSrc="<?= $episode->cover->thumbnail_url ?>" title="<?= $episode->title ?>" podcast="<?= $episode->podcast->title ?>" src="<?= $episode->audio_file_web_url ?>" diff --git a/themes/cp_app/episode/activity.php b/themes/cp_app/episode/activity.php index 6c5ca3a125a08e55bf52442acf670d60d2b37c55..97a23ac5b8df9ff82a048758d6cdb9d6e4f7e739 100644 --- a/themes/cp_app/episode/activity.php +++ b/themes/cp_app/episode/activity.php @@ -9,11 +9,10 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $episode->image->large_url ?>" /> +<meta property="og:image" content="<?= $episode->cover->large_url ?>" /> <meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> + ->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta property="og:description" content="$description" /> <meta property="article:published_time" content="<?= $episode->published_at ?>" /> <meta property="article:modified_time" content="<?= $episode->updated_at ?>" /> @@ -23,7 +22,7 @@ <link rel="alternate" type="text/xml+oembed" href="<?= base_url(route_to('episode-oembed-xml', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed xml" /> <meta name="twitter:title" content="<?= $episode->title ?>" /> <meta name="twitter:description" content="<?= $episode->description ?>" /> -<meta name="twitter:image" content="<?= $episode->image->large_url ?>" /> +<meta name="twitter:image" content="<?= $episode->cover->large_url ?>" /> <meta name="twitter:card" content="player" /> <meta property="twitter:audio:partner" content="<?= $podcast->publisher ?>" /> <meta property="twitter:audio:artist_name" content="<?= $podcast->owner_name ?>" /> diff --git a/themes/cp_app/episode/comments.php b/themes/cp_app/episode/comments.php index 33e9c9b472fe14cbf954aa24076d42c86d62a10a..873e166874913915f05a5224ad6dbdb19af4272a 100644 --- a/themes/cp_app/episode/comments.php +++ b/themes/cp_app/episode/comments.php @@ -11,11 +11,10 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $episode->image->large_url ?>" /> +<meta property="og:image" content="<?= $episode->cover->large_url ?>" /> <meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> + ->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta property="og:description" content="$description" /> <meta property="article:published_time" content="<?= $episode->published_at ?>" /> <meta property="article:modified_time" content="<?= $episode->updated_at ?>" /> @@ -25,7 +24,7 @@ <link rel="alternate" type="text/xml+oembed" href="<?= base_url(route_to('episode-oembed-xml', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed xml" /> <meta name="twitter:title" content="<?= $episode->title ?>" /> <meta name="twitter:description" content="<?= $episode->description ?>" /> -<meta name="twitter:image" content="<?= $episode->image->large_url ?>" /> +<meta name="twitter:image" content="<?= $episode->cover->large_url ?>" /> <meta name="twitter:card" content="player" /> <meta property="twitter:audio:partner" content="<?= $podcast->publisher ?>" /> <meta property="twitter:audio:artist_name" content="<?= $podcast->owner_name ?>" /> diff --git a/themes/cp_app/home.php b/themes/cp_app/home.php index 0852e85c054c9fc29b0caf5f4f9382d097913c76..c4328a6733dd3c149b89ffac7d19384722b9fa10 100644 --- a/themes/cp_app/home.php +++ b/themes/cp_app/home.php @@ -52,7 +52,7 @@ <article class="text-white"> <div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div> <div class="w-full h-full overflow-hidden"> - <img alt="<?= $podcast->title ?>" src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + <img alt="<?= $podcast->title ?>" src="<?= $podcast->cover->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> </div> <div class="absolute bottom-0 left-0 z-20 px-4 pb-2"> <h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2> diff --git a/themes/cp_app/podcast/_layout.php b/themes/cp_app/podcast/_layout.php index 8a9c9eea162aa759385caa6f40ff57a65d920941..3162a727478b0616cf09bd330378386c5d714925 100644 --- a/themes/cp_app/podcast/_layout.php +++ b/themes/cp_app/podcast/_layout.php @@ -34,9 +34,10 @@ </div> <?php endif; ?> - <header class="z-50 flex flex-col-reverse justify-between w-full col-start-2 bg-top bg-no-repeat bg-cover sm:flex-row sm:items-end bg-pine-800 aspect-[3/1]" style="background-image: url('<?= $podcast->actor->cover_image_url ?>');"> - <div class="flex items-center pl-4 -mb-6 md:pl-8 md:-mb-8 gap-x-4"> - <img src="<?= $podcast->image->thumbnail_url ?>" alt="<?= $podcast->title ?>" loading="lazy" class="h-24 rounded-full md:h-28 ring-4 ring-white" /> + <header class="relative z-50 flex flex-col-reverse justify-between w-full col-start-2 bg-top bg-no-repeat bg-cover sm:flex-row sm:items-end bg-pine-800 aspect-[3/1]" style="background-image: url('<?= $podcast->banner->medium_url ?>');"> + <div class="absolute bottom-0 left-0 w-full h-full backdrop-gradient-pine"></div> + <div class="z-10 flex items-center pl-4 -mb-6 md:pl-8 md:-mb-8 gap-x-4"> + <img src="<?= $podcast->cover->thumbnail_url ?>" alt="<?= $podcast->title ?>" loading="lazy" class="h-24 rounded-full md:h-28 ring-4 ring-white" /> <div class="relative flex flex-col text-white -top-2"> <h1 class="text-lg font-bold leading-none line-clamp-2 md:leading-none md:text-2xl font-display"><?= $podcast->title ?><span class="ml-1 font-sans text-base font-normal">@<?= $podcast->handle ?></span></h1> <span class="text-xs"><?= lang('Podcast.followers', [ @@ -44,7 +45,7 @@ ]) ?></span> </div> </div> - <div class="inline-flex items-center self-end mt-2 mb-2 mr-2 gap-x-2"> + <div class="z-10 inline-flex items-center self-end mt-2 mr-2 sm:mb-4 sm:mr-4 gap-x-2"> <?php if (in_array(true, array_column($podcast->fundingPlatforms, 'is_visible'), true)): ?> <IconButton glyph="heart" variant="accent" data-toggle="funding-links" data-toggle-class="hidden"><?= lang('Podcast.sponsor') . lang('Podcast.sponsor_title') ?></IconButton> <?php endif; ?> diff --git a/themes/cp_app/podcast/about.php b/themes/cp_app/podcast/about.php index 1c80116ca4cf8c4e5dfc97375cead06956441255..5ffffba3e5896a6fecd5cff4b233aaa15b47c5b0 100644 --- a/themes/cp_app/podcast/about.php +++ b/themes/cp_app/podcast/about.php @@ -19,11 +19,9 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> +<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta name="twitter:card" content="summary_large_image" /> <?= service('vite') @@ -55,7 +53,7 @@ <div class="inline-flex flex-row-reverse"> <?php $i = 0; ?> <?php foreach ($podcast->persons as $person): ?> - <img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> + <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> <?php $i++; if ($i === 3) { break; }?> diff --git a/themes/cp_app/podcast/activity.php b/themes/cp_app/podcast/activity.php index 3743d8859b2b4dfdaee1c416182b32a6837f0bfc..c62f367452a77b6a5317c9414b5dc4fe9f7ce13e 100644 --- a/themes/cp_app/podcast/activity.php +++ b/themes/cp_app/podcast/activity.php @@ -17,11 +17,9 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> +<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta name="twitter:card" content="summary_large_image" /> <?= service('vite') diff --git a/themes/cp_app/podcast/episodes.php b/themes/cp_app/podcast/episodes.php index 8b6bae0fe4a8193a62302fda6bccf7e381b3b9fb..1bd6924c95daafd130ef605c27668fcd948f13b1 100644 --- a/themes/cp_app/podcast/episodes.php +++ b/themes/cp_app/podcast/episodes.php @@ -17,11 +17,9 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> +<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta name="twitter:card" content="summary_large_image" /> <?= service('vite')