From 3143c9ad36e4cf1364205cf2be39c0c96f80fdd2 Mon Sep 17 00:00:00 2001 From: Yassine Doghri Date: Mon, 3 May 2021 17:39:58 +0000 Subject: [PATCH] feat: add remote_url alternative for transcript and chapters files --- .gitlab-ci.yml | 2 +- app/Config/Analytics.php | 4 +- app/Controllers/Admin/Episode.php | 108 +++++-- app/Controllers/Admin/PodcastImport.php | 2 +- .../2020-05-30-101500_add_podcasts.php | 2 +- .../2020-06-05-170000_add_episodes.php | 26 +- .../2020-12-25-120000_add_persons.php | 2 +- app/Entities/Episode.php | 236 ++++++++------- app/Entities/Person.php | 8 +- app/Entities/Podcast.php | 8 +- app/Helpers/id3_helper.php | 6 +- app/Helpers/media_helper.php | 14 +- app/Helpers/rss_helper.php | 21 +- app/Language/en/Common.php | 2 + app/Language/en/Episode.php | 14 +- app/Language/en/Page.php | 2 +- app/Language/fr/Common.php | 2 + app/Language/fr/Episode.php | 16 +- app/Libraries/Analytics/Config/Analytics.php | 6 +- .../EpisodeAnalyticsController.php | 4 +- .../Analytics/Helpers/analytics_helper.php | 38 +-- app/Libraries/Image.php | 10 +- app/Models/EpisodeModel.php | 24 +- app/Models/PersonModel.php | 4 +- app/Models/PodcastModel.php | 4 +- app/Views/_assets/styles/formInputTabs.css | 36 +++ app/Views/_assets/styles/index.css | 1 + app/Views/_assets/styles/tabs.css | 4 +- app/Views/admin/episode/create.php | 165 ++++++++-- app/Views/admin/episode/edit.php | 286 ++++++++++++------ app/Views/admin/episode/list.php | 8 +- app/Views/admin/episode/publish.php | 8 +- app/Views/admin/episode/publish_edit.php | 6 +- app/Views/admin/episode/soundbites.php | 36 +-- app/Views/admin/episode/view.php | 2 +- app/Views/embeddable_player.php | 4 +- app/Views/podcast/_partials/episode_card.php | 8 +- app/Views/podcast/episode.php | 10 +- app/Views/podcast/episode_authenticated.php | 10 +- app/Views/podcast/episodes.php | 9 +- app/Views/podcast/episodes_authenticated.php | 6 +- 41 files changed, 761 insertions(+), 403 deletions(-) create mode 100644 app/Views/_assets/styles/formInputTabs.css diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94f14a81..6e1cc393 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: php:7.2-fpm +image: php:7.3-fpm stages: - bundle diff --git a/app/Config/Analytics.php b/app/Config/Analytics.php index 4391703d..c3497252 100644 --- a/app/Config/Analytics.php +++ b/app/Config/Analytics.php @@ -26,10 +26,10 @@ class Analytics extends AnalyticsBase $this->gateway = config('App')->adminGateway . '/analytics'; } - public function getEnclosureUrl($enclosureUri) + public function getAudioFileUrl($audioFilePath) { helper('media'); - return media_base_url($enclosureUri); + return media_base_url($audioFilePath); } } diff --git a/app/Controllers/Admin/Episode.php b/app/Controllers/Admin/Episode.php index 01c8412f..619f420e 100644 --- a/app/Controllers/Admin/Episode.php +++ b/app/Controllers/Admin/Episode.php @@ -108,11 +108,12 @@ class Episode extends BaseController public function attemptCreate() { $rules = [ - 'enclosure' => 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]', + '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]', - 'transcript' => 'ext_in[transcript,txt,html,srt,json]|permit_empty', - 'chapters' => 'ext_in[chapters,json]|permit_empty', + 'transcript_file' => + 'ext_in[transcript,txt,html,srt,json]|permit_empty', + 'chapters_file' => 'ext_in[chapters,json]|permit_empty', ]; if (!$this->validate($rules)) { @@ -127,7 +128,7 @@ class Episode extends BaseController 'title' => $this->request->getPost('title'), 'slug' => $this->request->getPost('slug'), 'guid' => '', - 'enclosure' => $this->request->getFile('enclosure'), + 'audio_file' => $this->request->getFile('audio_file'), 'description_markdown' => $this->request->getPost('description'), 'image' => $this->request->getFile('image'), 'location' => $this->request->getPost('location_name'), @@ -151,6 +152,30 @@ class Episode extends BaseController 'published_at' => null, ]); + $transcriptChoice = $this->request->getPost('transcript-choice'); + if ( + $transcriptChoice === 'upload-file' && + ($transcriptFile = $this->request->getFile('transcript_file')) + ) { + $newEpisode->transcript_file = $transcriptFile; + } elseif ($transcriptChoice === 'remote-url') { + $newEpisode->transcript_file_remote_url = $this->request->getPost( + 'transcript_file_remote_url', + ); + } + + $chaptersChoice = $this->request->getPost('chapters-choice'); + if ( + $chaptersChoice === 'upload-file' && + ($chaptersFile = $this->request->getFile('chapters_file')) + ) { + $newEpisode->chapters_file = $chaptersFile; + } elseif ($chaptersChoice === 'remote-url') { + $newEpisode->chapters_file_remote_url = $this->request->getPost( + 'chapters_file_remote_url', + ); + } + $episodeModel = new EpisodeModel(); if (!($newEpisodeId = $episodeModel->insert($newEpisode, true))) { @@ -201,12 +226,13 @@ class Episode extends BaseController public function attemptEdit() { $rules = [ - 'enclosure' => - 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]|permit_empty', + '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]', - 'transcript' => 'ext_in[transcript,txt,html,srt,json]|permit_empty', - 'chapters' => 'ext_in[chapters,json]|permit_empty', + 'transcript_file' => + 'ext_in[transcript_file,txt,html,srt,json]|permit_empty', + 'chapters_file' => 'ext_in[chapters_file,json]|permit_empty', ]; if (!$this->validate($rules)) { @@ -240,21 +266,61 @@ class Episode extends BaseController $this->episode->updated_by = user()->id; - $enclosure = $this->request->getFile('enclosure'); - if ($enclosure->isValid()) { - $this->episode->enclosure = $enclosure; + $audioFile = $this->request->getFile('audio_file'); + if ($audioFile) { + $this->episode->audio_file = $audioFile; } $image = $this->request->getFile('image'); if ($image) { $this->episode->image = $image; } - $transcript = $this->request->getFile('transcript'); - if ($transcript->isValid()) { - $this->episode->transcript = $transcript; + + $transcriptChoice = $this->request->getPost('transcript-choice'); + if ($transcriptChoice === 'upload-file') { + $transcriptFile = $this->request->getFile('transcript_file'); + if ($transcriptFile->isValid()) { + $this->episode->transcript_file = $transcriptFile; + $this->episode->transcript_file_remote_url = null; + } + } elseif ($transcriptChoice === 'remote-url') { + if ( + $transcriptFileRemoteUrl = $this->request->getPost( + 'transcript_file_remote_url', + ) + ) { + if ( + ($transcriptFile = $this->episode->transcript_file) && + !empty($transcriptFile) + ) { + unlink($transcriptFile); + $this->episode->transcript_file_path = null; + } + } + $this->episode->transcript_file_remote_url = $transcriptFileRemoteUrl; } - $chapters = $this->request->getFile('chapters'); - if ($chapters->isValid()) { - $this->episode->chapters = $chapters; + + $chaptersChoice = $this->request->getPost('chapters-choice'); + if ($chaptersChoice === 'upload-file') { + $chaptersFile = $this->request->getFile('chapters_file'); + if ($chaptersFile->isValid()) { + $this->episode->chapters_file = $chaptersFile; + $this->episode->chapters_file_remote_url = null; + } + } elseif ($chaptersChoice === 'remote-url') { + if ( + $chaptersFileRemoteUrl = $this->request->getPost( + 'chapters_file_remote_url', + ) + ) { + if ( + ($chaptersFile = $this->episode->chapters_file) && + !empty($chaptersFile) + ) { + unlink($chaptersFile); + $this->episode->chapters_file_path = null; + } + } + $this->episode->chapters_file_remote_url = $chaptersFileRemoteUrl; } $episodeModel = new EpisodeModel(); @@ -289,8 +355,8 @@ class Episode extends BaseController public function transcriptDelete() { - unlink($this->episode->transcript); - $this->episode->transcript_uri = null; + unlink($this->episode->transcript_file); + $this->episode->transcript_file_path = null; $episodeModel = new EpisodeModel(); @@ -306,8 +372,8 @@ class Episode extends BaseController public function chaptersDelete() { - unlink($this->episode->chapters); - $this->episode->chapters_uri = null; + unlink($this->episode->chapters_file); + $this->episode->chapters_file_path = null; $episodeModel = new EpisodeModel(); diff --git a/app/Controllers/Admin/PodcastImport.php b/app/Controllers/Admin/PodcastImport.php index daad6c42..6a293261 100644 --- a/app/Controllers/Admin/PodcastImport.php +++ b/app/Controllers/Admin/PodcastImport.php @@ -345,7 +345,7 @@ class PodcastImport extends BaseController 'guid' => empty($item->guid) ? null : $item->guid, 'title' => $item->title, 'slug' => $slug, - 'enclosure' => download_file($item->enclosure->attributes()), + 'audio_file' => download_file($item->enclosure->attributes()), 'description_markdown' => $converter->convert( $itemDescriptionHtml, ), 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 d052c278..675fa83c 100644 --- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php +++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php @@ -41,7 +41,7 @@ class AddPodcasts extends Migration 'description_html' => [ 'type' => 'TEXT', ], - 'image_uri' => [ + 'image_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, ], 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 775e64c1..cafca237 100644 --- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php +++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php @@ -39,25 +39,25 @@ class AddEpisodes extends Migration 'type' => 'VARCHAR', 'constraint' => 191, ], - 'enclosure_uri' => [ + 'audio_file_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, ], - 'enclosure_duration' => [ + 'audio_file_duration' => [ 'type' => 'INT', 'unsigned' => true, 'comment' => 'Playtime in seconds', ], - 'enclosure_mimetype' => [ + 'audio_file_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 255, ], - 'enclosure_filesize' => [ + 'audio_file_size' => [ 'type' => 'INT', 'unsigned' => true, 'comment' => 'File size in bytes', ], - 'enclosure_headersize' => [ + 'audio_file_header_size' => [ 'type' => 'INT', 'unsigned' => true, 'comment' => 'Header size in bytes', @@ -68,7 +68,7 @@ class AddEpisodes extends Migration 'description_html' => [ 'type' => 'TEXT', ], - 'image_uri' => [ + 'image_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, @@ -80,16 +80,26 @@ class AddEpisodes extends Migration 'constraint' => 13, 'null' => true, ], - 'transcript_uri' => [ + 'transcript_file_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, ], - 'chapters_uri' => [ + 'transcript_file_remote_url' => [ + 'type' => 'VARCHAR', + 'constraint' => 512, + 'null' => true, + ], + 'chapters_file_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, ], + 'chapters_file_remote_url' => [ + 'type' => 'VARCHAR', + 'constraint' => 512, + 'null' => true, + ], 'parental_advisory' => [ 'type' => 'ENUM', 'constraint' => ['clean', 'explicit'], 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 f9933604..84b34ccf 100644 --- a/app/Database/Migrations/2020-12-25-120000_add_persons.php +++ b/app/Database/Migrations/2020-12-25-120000_add_persons.php @@ -41,7 +41,7 @@ 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_uri' => [ + 'image_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, ], diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index d0cdf642..338e9879 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -13,6 +13,8 @@ use App\Models\SoundbiteModel; use App\Models\EpisodePersonModel; use App\Models\NoteModel; use CodeIgniter\Entity; +use CodeIgniter\Files\Exceptions\FileNotFoundException; +use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\I18n\Time; use League\CommonMark\CommonMarkConverter; @@ -36,47 +38,37 @@ class Episode extends Entity /** * @var \CodeIgniter\Files\File */ - protected $enclosure; + protected $audioFile; /** * @var \CodeIgniter\Files\File */ - protected $transcript; + protected $transcript_file; /** * @var \CodeIgniter\Files\File */ - protected $chapters; + protected $chapters_file; /** * @var string */ - protected $enclosure_media_path; + protected $audio_file_url; /** * @var string */ - protected $enclosure_url; + protected $audio_file_analytics_url; /** * @var string */ - protected $enclosure_web_url; + protected $audio_file_web_url; /** * @var string */ - protected $enclosure_opengraph_url; - - /** - * @var string - */ - protected $transcript_url; - - /** - * @var string - */ - protected $chapters_url; + protected $audio_file_opengraph_url; /** * @var \App\Entities\EpisodePerson[] @@ -132,17 +124,19 @@ class Episode extends Entity 'guid' => 'string', 'slug' => 'string', 'title' => 'string', - 'enclosure_uri' => 'string', - 'enclosure_duration' => 'integer', - 'enclosure_mimetype' => 'string', - 'enclosure_filesize' => 'integer', - 'enclosure_headersize' => 'integer', + 'audio_file_path' => 'string', + 'audio_file_duration' => 'integer', + 'audio_file_mimetype' => 'string', + 'audio_file_size' => 'integer', + 'audio_file_header_size' => 'integer', 'description_markdown' => 'string', 'description_html' => 'string', - 'image_uri' => '?string', + 'image_path' => '?string', 'image_mimetype' => '?string', - 'transcript_uri' => '?string', - 'chapters_uri' => '?string', + 'transcript_file_path' => '?string', + 'transcript_file_remote_url' => '?string', + 'chapters_file_path' => '?string', + 'chapters_file_remote_url' => '?string', 'parental_advisory' => '?string', 'number' => '?integer', 'season_number' => '?integer', @@ -176,13 +170,13 @@ class Episode extends Entity // check whether the user has inputted an image and store $this->attributes['image_mimetype'] = $image->getMimeType(); - $this->attributes['image_uri'] = save_media( + $this->attributes['image_path'] = save_media( $image, 'podcasts/' . $this->getPodcast()->name, $this->attributes['slug'], ); $this->image = new \App\Libraries\Image( - $this->attributes['image_uri'], + $this->attributes['image_path'], $this->attributes['image_mimetype'], ); $this->image->saveSizes(); @@ -193,9 +187,9 @@ class Episode extends Entity public function getImage(): \App\Libraries\Image { - if ($image_uri = $this->attributes['image_uri']) { + if ($imagePath = $this->attributes['image_path']) { return new \App\Libraries\Image( - $image_uri, + $imagePath, $this->attributes['image_mimetype'], ); } @@ -203,58 +197,59 @@ class Episode extends Entity } /** - * Saves an enclosure + * Saves an audio file * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $enclosure + * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $audioFile * */ - public function setEnclosure($enclosure = null) + public function setAudioFile($audioFile = null) { if ( - !empty($enclosure) && - (!($enclosure instanceof \CodeIgniter\HTTP\Files\UploadedFile) || - $enclosure->isValid()) + !empty($audioFile) && + (!($audioFile instanceof \CodeIgniter\HTTP\Files\UploadedFile) || + $audioFile->isValid()) ) { helper(['media', 'id3']); - $enclosure_metadata = get_file_tags($enclosure); + $audio_metadata = get_file_tags($audioFile); - $this->attributes['enclosure_uri'] = save_media( - $enclosure, + $this->attributes['audio_file_path'] = save_media( + $audioFile, 'podcasts/' . $this->getPodcast()->name, $this->attributes['slug'], ); - $this->attributes['enclosure_duration'] = round( - $enclosure_metadata['playtime_seconds'], + $this->attributes['audio_file_duration'] = round( + $audio_metadata['playtime_seconds'], ); - $this->attributes['enclosure_mimetype'] = - $enclosure_metadata['mime_type']; - $this->attributes['enclosure_filesize'] = - $enclosure_metadata['filesize']; - $this->attributes['enclosure_headersize'] = - $enclosure_metadata['avdataoffset']; + $this->attributes['audio_file_mimetype'] = + $audio_metadata['mime_type']; + $this->attributes['audio_file_size'] = $audio_metadata['filesize']; + $this->attributes['audio_file_header_size'] = + $audio_metadata['avdataoffset']; return $this; } } /** - * Saves an episode transcript + * Saves an episode transcript file * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $transcript + * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $transcriptFile * */ - public function setTranscript($transcript) + public function setTranscriptFile($transcriptFile) { if ( - !empty($transcript) && - (!($transcript instanceof \CodeIgniter\HTTP\Files\UploadedFile) || - $transcript->isValid()) + !empty($transcriptFile) && + (!( + $transcriptFile instanceof \CodeIgniter\HTTP\Files\UploadedFile + ) || + $transcriptFile->isValid()) ) { helper('media'); - $this->attributes['transcript_uri'] = save_media( - $transcript, + $this->attributes['transcript_file_path'] = save_media( + $transcriptFile, $this->getPodcast()->name, $this->attributes['slug'] . '-transcript', ); @@ -264,22 +259,22 @@ class Episode extends Entity } /** - * Saves an episode chapters + * Saves an episode chapters file * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $chapters + * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $chaptersFile * */ - public function setChapters($chapters) + public function setChaptersFile($chaptersFile) { if ( - !empty($chapters) && - (!($chapters instanceof \CodeIgniter\HTTP\Files\UploadedFile) || - $chapters->isValid()) + !empty($chaptersFile) && + (!($chaptersFile instanceof \CodeIgniter\HTTP\Files\UploadedFile) || + $chaptersFile->isValid()) ) { helper('media'); - $this->attributes['chapters_uri'] = save_media( - $chapters, + $this->attributes['chapters_file_path'] = save_media( + $chaptersFile, $this->getPodcast()->name, $this->attributes['slug'] . '-chapters', ); @@ -288,87 +283,102 @@ class Episode extends Entity return $this; } - public function getEnclosure() - { - return new \CodeIgniter\Files\File($this->getEnclosureMediaPath()); - } - - public function getTranscript() - { - return $this->attributes['transcript_uri'] - ? new \CodeIgniter\Files\File($this->getTranscriptMediaPath()) - : null; - } - - public function getChapters() - { - return $this->attributes['chapters_uri'] - ? new \CodeIgniter\Files\File($this->getChaptersMediaPath()) - : null; - } - - public function getEnclosureMediaPath() + public function getAudioFile() { helper('media'); - return media_path($this->attributes['enclosure_uri']); + return new \CodeIgniter\Files\File(media_path($this->audio_file_path)); } - public function getTranscriptMediaPath() + public function getTranscriptFile() + { + if ($this->attributes['transcript_file_path']) { + helper('media'); + + return new \CodeIgniter\Files\File( + media_path($this->attributes['transcript_file_path']), + ); + } + + return null; + } + + public function getChaptersFile() + { + if ($this->attributes['chapters_file_path']) { + helper('media'); + + return new \CodeIgniter\Files\File( + media_path($this->attributes['chapters_file_path']), + ); + } + + return null; + } + + public function getAudioFileUrl() { helper('media'); - return $this->attributes['transcript_uri'] - ? media_path($this->attributes['transcript_uri']) - : null; + return media_url($this->audio_file_path); } - public function getChaptersMediaPath() - { - helper('media'); - - return $this->attributes['chapters_uri'] - ? media_path($this->attributes['chapters_uri']) - : null; - } - - public function getEnclosureUrl() + public function getAudioFileAnalyticsUrl() { helper('analytics'); return generate_episode_analytics_url( $this->podcast_id, $this->id, - $this->enclosure_uri, - $this->enclosure_duration, - $this->enclosure_filesize, - $this->enclosure_headersize, + $this->audio_file_path, + $this->audio_file_duration, + $this->audio_file_size, + $this->audio_file_header_size, $this->published_at, ); } - public function getEnclosureWebUrl() + public function getAudioFileWebUrl() { - return $this->getEnclosureUrl() . '?_from=-+Website+-'; + return $this->getAudioFileAnalyticsUrl() . '?_from=-+Website+-'; } - public function getEnclosureOpengraphUrl() + public function getAudioFileOpengraphUrl() { - return $this->getEnclosureUrl() . '?_from=-+Open+Graph+-'; + return $this->getAudioFileAnalyticsUrl() . '?_from=-+Open+Graph+-'; } - public function getTranscriptUrl() + /** + * Gets transcript url from transcript file uri if it exists + * or returns the transcript_file_remote_url which can be null. + * + * @return string|null + * @throws FileNotFoundException + * @throws HTTPException + */ + public function getTranscriptFileUrl() { - return $this->attributes['transcript_uri'] - ? base_url($this->getTranscriptMediaPath()) - : null; + if ($this->attributes['transcript_file_path']) { + return media_url($this->attributes['transcript_file_path']); + } else { + return $this->attributes['transcript_file_remote_url']; + } } - public function getChaptersUrl() + /** + * Gets chapters file url from chapters file uri if it exists + * or returns the chapters_file_remote_url which can be null. + * + * @return mixed + * @throws HTTPException + */ + public function getChaptersFileUrl() { - return $this->attributes['chapters_uri'] - ? base_url($this->getChaptersMediaPath()) - : null; + if ($this->attributes['chapters_file_path']) { + return media_url($this->attributes['chapters_file_path']); + } else { + return $this->attributes['chapters_file_remote_url']; + } } /** diff --git a/app/Entities/Person.php b/app/Entities/Person.php index 222aa56f..d6c9c50f 100644 --- a/app/Entities/Person.php +++ b/app/Entities/Person.php @@ -22,7 +22,7 @@ class Person extends Entity 'full_name' => 'string', 'unique_name' => 'string', 'information_url' => '?string', - 'image_uri' => 'string', + 'image_path' => 'string', 'image_mimetype' => 'string', 'created_by' => 'integer', 'updated_by' => 'integer', @@ -40,13 +40,13 @@ class Person extends Entity helper('media'); $this->attributes['image_mimetype'] = $image->getMimeType(); - $this->attributes['image_uri'] = save_media( + $this->attributes['image_path'] = save_media( $image, 'persons', $this->attributes['unique_name'], ); $this->image = new \App\Libraries\Image( - $this->attributes['image_uri'], + $this->attributes['image_path'], $this->attributes['image_mimetype'], ); $this->image->saveSizes(); @@ -58,7 +58,7 @@ class Person extends Entity public function getImage() { return new \App\Libraries\Image( - $this->attributes['image_uri'], + $this->attributes['image_path'], $this->attributes['image_mimetype'], ); } diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index db7af337..740a0134 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -100,7 +100,7 @@ class Podcast extends Entity 'title' => 'string', 'description_markdown' => 'string', 'description_html' => 'string', - 'image_uri' => 'string', + 'image_path' => 'string', 'image_mimetype' => 'string', 'language_code' => 'string', 'category_id' => 'integer', @@ -161,14 +161,14 @@ class Podcast extends Entity helper('media'); $this->attributes['image_mimetype'] = $image->getMimeType(); - $this->attributes['image_uri'] = save_media( + $this->attributes['image_path'] = save_media( $image, 'podcasts/' . $this->attributes['name'], 'cover', ); $this->image = new \App\Libraries\Image( - $this->attributes['image_uri'], + $this->attributes['image_path'], $this->attributes['image_mimetype'], ); $this->image->saveSizes(); @@ -180,7 +180,7 @@ class Podcast extends Entity public function getImage() { return new \App\Libraries\Image( - $this->attributes['image_uri'], + $this->attributes['image_path'], $this->attributes['image_mimetype'], ); } diff --git a/app/Helpers/id3_helper.php b/app/Helpers/id3_helper.php index 5046c72c..ab0a567c 100644 --- a/app/Helpers/id3_helper.php +++ b/app/Helpers/id3_helper.php @@ -36,13 +36,15 @@ function get_file_tags($file) * * @return UploadedFile */ -function write_enclosure_tags($episode) +function write_audio_file_tags($episode) { + helper('media'); + $TextEncoding = 'UTF-8'; // Initialize getID3 tag-writing module $tagwriter = new WriteTags(); - $tagwriter->filename = $episode->enclosure_media_path; + $tagwriter->filename = media_path($episode->audio_file_path); // set various options (optional) $tagwriter->tagformats = ['id3v2.4']; diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php index a7e2eb69..cd71e3e6 100644 --- a/app/Helpers/media_helper.php +++ b/app/Helpers/media_helper.php @@ -12,15 +12,15 @@ use CodeIgniter\HTTP\ResponseInterface; /** * Saves a file to the corresponding podcast folder in `public/media` * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $file - * @param string $podcast_name - * @param string $file_name + * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $filePath + * @param string $folder + * @param string $fileName * * @return string The episode's file path in media root */ -function save_media($file, $folder, $mediaName) +function save_media($filePath, $folder, $mediaName) { - $file_name = $mediaName . '.' . $file->getExtension(); + $fileName = $mediaName . '.' . $filePath->getExtension(); $mediaRoot = config('App')->mediaRoot . '/' . $folder; @@ -30,9 +30,9 @@ function save_media($file, $folder, $mediaName) } // move to media folder and overwrite file if already existing - $file->move($mediaRoot . '/', $file_name, true); + $filePath->move($mediaRoot . '/', $fileName, true); - return $folder . '/' . $file_name; + return $folder . '/' . $fileName; } /** diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php index 112b4648..a49562cc 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -258,13 +258,13 @@ function get_rss_feed($podcast, $serviceSlug = '') $enclosure->addAttribute( 'url', - $episode->enclosure_url . + $episode->audio_file_analytics_url . (empty($serviceSlug) ? '' : '?_from=' . urlencode($serviceSlug)), ); - $enclosure->addAttribute('length', $episode->enclosure_filesize); - $enclosure->addAttribute('type', $episode->enclosure_mimetype); + $enclosure->addAttribute('length', $episode->audio_file_size); + $enclosure->addAttribute('type', $episode->audio_file_mimetype); $item->addChild('guid', $episode->guid); $item->addChild( @@ -290,7 +290,7 @@ function get_rss_feed($podcast, $serviceSlug = '') ); $item->addChild( 'duration', - $episode->enclosure_duration, + $episode->audio_file_duration, $itunes_namespace, ); $item->addChild('link', $episode->link); @@ -318,17 +318,20 @@ function get_rss_feed($podcast, $serviceSlug = '') ); $item->addChild('episodeType', $episode->type, $itunes_namespace); - if ($episode->transcript) { + if ($episode->transcript_file_url) { $transcriptElement = $item->addChild( 'transcript', null, $podcast_namespace, ); - $transcriptElement->addAttribute('url', $episode->transcriptUrl); + $transcriptElement->addAttribute( + 'url', + $episode->transcript_file_url, + ); $transcriptElement->addAttribute( 'type', Mimes::guessTypeFromExtension( - pathinfo($episode->transcript_uri, PATHINFO_EXTENSION), + pathinfo($episode->transcript_file_url, PATHINFO_EXTENSION), ), ); $transcriptElement->addAttribute( @@ -337,13 +340,13 @@ function get_rss_feed($podcast, $serviceSlug = '') ); } - if ($episode->chapters) { + if ($episode->chapters_file_url) { $chaptersElement = $item->addChild( 'chapters', null, $podcast_namespace, ); - $chaptersElement->addAttribute('url', $episode->chaptersUrl); + $chaptersElement->addAttribute('url', $episode->chapters_file_url); $chaptersElement->addAttribute('type', 'application/json+chapters'); } diff --git a/app/Language/en/Common.php b/app/Language/en/Common.php index d2a5966c..da350b43 100644 --- a/app/Language/en/Common.php +++ b/app/Language/en/Common.php @@ -31,5 +31,7 @@ return [ ], '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/app/Language/en/Episode.php b/app/Language/en/Episode.php index 83210875..5eaa06ca 100644 --- a/app/Language/en/Episode.php +++ b/app/Language/en/Episode.php @@ -45,8 +45,8 @@ return [ 'form' => [ 'warning' => 'In case of fatal error, try increasing the `memory_limit`, `upload_max_filesize` and `post_max_size` values in your php configuration file then restart your web server.
These values must be higher than the audio file you wish to upload.', - 'enclosure' => 'Audio file', - 'enclosure_hint' => 'Choose an .mp3 or .m4a audio file.', + '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', @@ -90,10 +90,14 @@ return [ 'location_name_hint' => 'This can be a real or fictional location', 'transcript' => 'Transcript or closed captions', 'transcript_hint' => 'Allowed formats are txt, html, srt or json.', - 'transcript_delete' => 'Delete transcript', + 'transcript_file' => 'Transcript file', + 'transcript_file_remote_url' => 'Remote url for transcript', + 'transcript_file_delete' => 'Delete transcript file', 'chapters' => 'Chapters', - 'chapters_hint' => 'File should be in JSON Chapters Format.', - 'chapters_delete' => 'Delete chapters', + 'chapters_hint' => 'File must be in JSON Chapters format.', + 'chapters_file' => 'Chapters file', + 'chapters_file_remote_url' => 'Remote url for chapters file', + 'chapters_file_delete' => 'Delete chapters file', 'advanced_section_title' => 'Advanced Parameters', 'advanced_section_subtitle' => 'If you need RSS tags that Castopod does not handle, set them here.', diff --git a/app/Language/en/Page.php b/app/Language/en/Page.php index 4ddf9602..cafd8820 100644 --- a/app/Language/en/Page.php +++ b/app/Language/en/Page.php @@ -22,6 +22,6 @@ return [ 'submit_edit' => 'Save', ], 'messages' => [ - 'createSuccess' => 'The page "{pageTitle}" was created successfully!', + 'createSuccess' => 'The page “{pageTitle}” was created successfully!', ], ]; diff --git a/app/Language/fr/Common.php b/app/Language/fr/Common.php index 48b599e0..8d466bf2 100644 --- a/app/Language/fr/Common.php +++ b/app/Language/fr/Common.php @@ -31,5 +31,7 @@ return [ ], '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/app/Language/fr/Episode.php b/app/Language/fr/Episode.php index a2989f67..df4a5d90 100644 --- a/app/Language/fr/Episode.php +++ b/app/Language/fr/Episode.php @@ -45,8 +45,8 @@ return [ 'form' => [ 'warning' => 'En cas d’erreur fatale, essayez d’augmenter les valeurs de `memory_limit`, `upload_max_filesize` et `post_max_size` dans votre fichier de configuration php puis redémarrez votre serveur web.
Les valeurs doivent être plus grandes que le fichier audio que vous souhaitez téléverser.', - 'enclosure' => 'Fichier audio', - 'enclosure_hint' => 'Sélectionnez un fichier audio .mp3 ou .m4a.', + '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', @@ -91,10 +91,16 @@ return [ 'transcript' => 'Transcription ou sous-titrage', 'transcript_hint' => 'Les formats autorisés sont txt, html, srt ou json.', - 'transcript_delete' => 'Supprimer la transcription', + 'transcript_file' => 'Fichier de transcription', + 'transcript_file_remote_url' => + 'URL distante pour le fichier de transcription', + 'transcript_file_delete' => 'Supprimer le fichier de transcription', 'chapters' => 'Chapitrage', - 'chapters_hint' => 'Le fichier doit être en "JSON Chapters Format".', - 'chapters_delete' => 'Supprimer le chapitrage', + 'chapters_hint' => 'Le fichier doit être en format “JSON Chapters”.', + 'chapters_file' => 'Fichier de chapitrage', + 'chapters_file_remote_url' => + 'URL distante pour le fichier de chapitrage', + 'chapters_file_delete' => 'Supprimer le fichier de chapitrage', 'advanced_section_title' => 'Paramètres avancés', 'advanced_section_subtitle' => 'Si vous avez besoin d’une balise que Castopod ne couvre pas, définissez-la ici.', diff --git a/app/Libraries/Analytics/Config/Analytics.php b/app/Libraries/Analytics/Config/Analytics.php index fa5ff6a4..64a85b5d 100644 --- a/app/Libraries/Analytics/Config/Analytics.php +++ b/app/Libraries/Analytics/Config/Analytics.php @@ -26,13 +26,13 @@ class Analytics extends BaseConfig ]; /** - * get the full enclosure url + * get the full audio file url * * @param string $filename * @return string */ - public function getEnclosureUrl(string $enclosureUri) + public function getAudioFileUrl(string $audioFilePath) { - return base_url($enclosureUri); + return base_url($audioFilePath); } } diff --git a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php index 7e372d9a..b9a2cb43 100644 --- a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php +++ b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php @@ -50,7 +50,7 @@ class EpisodeAnalyticsController extends Controller } // Add one hit to this episode: - public function hit($base64EpisodeData, ...$enclosureUri) + public function hit($base64EpisodeData, ...$audioFilePath) { $session = \Config\Services::session(); $session->start(); @@ -78,6 +78,6 @@ class EpisodeAnalyticsController extends Controller $serviceName, ); - return redirect()->to($this->config->getEnclosureUrl($enclosureUri)); + return redirect()->to($this->config->getAudioFileUrl($audioFilePath)); } } diff --git a/app/Libraries/Analytics/Helpers/analytics_helper.php b/app/Libraries/Analytics/Helpers/analytics_helper.php index a9fc7c57..e18806a8 100644 --- a/app/Libraries/Analytics/Helpers/analytics_helper.php +++ b/app/Libraries/Analytics/Helpers/analytics_helper.php @@ -30,15 +30,15 @@ if (!function_exists('base64_url_decode')) { if (!function_exists('generate_episode_analytics_url')) { /** - * Builds the episode analytics url that redirects to the enclosure url + * Builds the episode analytics url that redirects to the audio file url * after analytics hit. * * @param int $podcastId * @param int $episodeId - * @param string $enclosureUri - * @param int $enclosureDuration - * @param int $enclosureFilesize - * @param int $enclosureHeadersize + * @param string $audioFilePath + * @param int $audioFileDuration + * @param int $audioFileSize + * @param int $audioFileHeaderSize * @param \CodeIgniter\I18n\Time $publicationDate * * @return string @@ -47,10 +47,10 @@ if (!function_exists('generate_episode_analytics_url')) { function generate_episode_analytics_url( $podcastId, $episodeId, - $enclosureUri, - $enclosureDuration, - $enclosureFilesize, - $enclosureHeadersize, + $audioFilePath, + $audioFileDuration, + $audioFileFilesize, + $audioFileHeaderSize, $publicationDate ) { return url_to( @@ -61,22 +61,22 @@ if (!function_exists('generate_episode_analytics_url')) { $podcastId, $episodeId, // bytes_threshold: number of bytes that must be downloaded for an episode to be counted in download analytics - // - if file is shorter than 60sec, then it's enclosure_filesize - // - if file is longer than 60 seconds then it's enclosure_headersize + 60 seconds - $enclosureDuration <= 60 - ? $enclosureFilesize - : $enclosureHeadersize + + // - if file is shorter than 60sec, then it's audio_file_size + // - if file is longer than 60 seconds then it's audio_file_header_size + 60 seconds + $audioFileDuration <= 60 + ? $audioFileFilesize + : $audioFileHeaderSize + floor( - (($enclosureFilesize - $enclosureHeadersize) / - $enclosureDuration) * + (($audioFileFilesize - $audioFileHeaderSize) / + $audioFileDuration) * 60, ), - $enclosureFilesize, - $enclosureDuration, + $audioFileFilesize, + $audioFileDuration, strtotime($publicationDate), ), ), - $enclosureUri, + $audioFilePath, ); } } diff --git a/app/Libraries/Image.php b/app/Libraries/Image.php index c7133937..78a5c2ac 100644 --- a/app/Libraries/Image.php +++ b/app/Libraries/Image.php @@ -70,17 +70,17 @@ class Image */ public $id3_path; - public function __construct($originalUri, $mimetype) + public function __construct($originalPath, $mimetype) { helper('media'); - $originalPath = media_path($originalUri); + $originalMediaPath = media_path($originalPath); [ 'filename' => $filename, 'dirname' => $dirname, 'extension' => $extension, - ] = pathinfo($originalPath); + ] = pathinfo($originalMediaPath); // load images extensions from config $this->config = config('Images'); @@ -100,8 +100,8 @@ class Image $feed = $dirname . '/' . $filename . $feedExtension . '.' . $extension; $id3 = $dirname . '/' . $filename . $id3Extension . '.' . $extension; - $this->original_path = $originalPath; - $this->original_url = media_url($originalUri); + $this->original_path = $originalMediaPath; + $this->original_url = media_url($originalMediaPath); $this->thumbnail_path = $thumbnail; $this->thumbnail_url = base_url($thumbnail); $this->medium_path = $medium; diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 8d36325b..9679d717 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -21,17 +21,19 @@ class EpisodeModel extends Model 'guid', 'title', 'slug', - 'enclosure_uri', - 'enclosure_duration', - 'enclosure_mimetype', - 'enclosure_filesize', - 'enclosure_headersize', + 'audio_file_path', + 'audio_file_duration', + 'audio_file_mimetype', + 'audio_file_size', + 'audio_file_header_size', 'description_markdown', 'description_html', - 'image_uri', + 'image_path', 'image_mimetype', - 'transcript_uri', - 'chapters_uri', + 'transcript_file_path', + 'transcript_file_remote_url', + 'chapters_file_path', + 'chapters_file_remote_url', 'parental_advisory', 'number', 'season_number', @@ -58,11 +60,13 @@ class EpisodeModel extends Model 'podcast_id' => 'required', 'title' => 'required', 'slug' => 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]', - 'enclosure_uri' => 'required', + 'audio_file_path' => 'required', 'description_markdown' => 'required', 'number' => 'is_natural_no_zero|permit_empty', 'season_number' => 'is_natural_no_zero|permit_empty', 'type' => 'required', + 'transcript_file_remote_url' => 'valid_url|permit_empty', + 'chapters_file_remote_url' => 'valid_url|permit_empty', 'published_at' => 'valid_date|permit_empty', 'created_by' => 'required', 'updated_by' => 'required', @@ -268,7 +272,7 @@ class EpisodeModel extends Model is_array($data['id']) ? $data['id'][0] : $data['id'], ); - write_enclosure_tags($episode); + write_audio_file_tags($episode); return $data; } diff --git a/app/Models/PersonModel.php b/app/Models/PersonModel.php index fc19b398..41dee4f2 100644 --- a/app/Models/PersonModel.php +++ b/app/Models/PersonModel.php @@ -20,7 +20,7 @@ class PersonModel extends Model 'full_name', 'unique_name', 'information_url', - 'image_uri', + 'image_path', 'image_mimetype', 'created_by', 'updated_by', @@ -35,7 +35,7 @@ class PersonModel extends Model 'full_name' => 'required', 'unique_name' => 'required|regex_match[/^[a-z0-9\-]{1,191}$/]|is_unique[persons.unique_name,id,{id}]', - 'image_uri' => 'required', + 'image_path' => 'required', 'created_by' => 'required', 'updated_by' => 'required', ]; diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php index a332f763..f8d79e00 100644 --- a/app/Models/PodcastModel.php +++ b/app/Models/PodcastModel.php @@ -25,7 +25,7 @@ class PodcastModel extends Model 'description_html', 'episode_description_footer_markdown', 'episode_description_footer_html', - 'image_uri', + 'image_path', 'image_mimetype', 'language_code', 'category_id', @@ -62,7 +62,7 @@ class PodcastModel extends Model 'name' => 'required|regex_match[/^[a-zA-Z0-9\_]{1,191}$/]|is_unique[podcasts.name,id,{id}]', 'description_markdown' => 'required', - 'image_uri' => 'required', + 'image_path' => 'required', 'language_code' => 'required', 'category_id' => 'required', 'owner_email' => 'required|valid_email', diff --git a/app/Views/_assets/styles/formInputTabs.css b/app/Views/_assets/styles/formInputTabs.css new file mode 100644 index 00000000..011d39a9 --- /dev/null +++ b/app/Views/_assets/styles/formInputTabs.css @@ -0,0 +1,36 @@ +@layer components { + .form-input-tabs > input[type="radio"] { + @apply absolute -left-full; + } + + .form-input-tabs .tab-panel { + @apply hidden; + } + + /* Logic for 2 tabs at most */ + .form-input-tabs + > input:first-child:checked + ~ .tab-panels + > .tab-panel:first-child, + .form-input-tabs + > input:nth-child(3):checked + ~ .tab-panels + > .tab-panel:nth-child(2) { + @apply block; + } + + /* Styling */ + .form-input-tabs > label { + @apply relative inline-block px-1 py-2 text-xs text-center cursor-pointer opacity-70 hover:opacity-100; + } + + .form-input-tabs > input:checked + label::after { + @apply absolute inset-x-0 bottom-0 w-full mx-auto bg-pine-700; + content: ""; + height: 0.2rem; + } + + .form-input-tabs > input:checked + label { + @apply font-semibold opacity-100 text-pine-700; + } +} diff --git a/app/Views/_assets/styles/index.css b/app/Views/_assets/styles/index.css index eb98956f..b4b795a5 100644 --- a/app/Views/_assets/styles/index.css +++ b/app/Views/_assets/styles/index.css @@ -10,3 +10,4 @@ @import "./note.css"; @import "./tabs.css"; @import "./radioToggler.css"; +@import "./formInputTabs.css"; diff --git a/app/Views/_assets/styles/tabs.css b/app/Views/_assets/styles/tabs.css index 4414c517..f0f279b4 100644 --- a/app/Views/_assets/styles/tabs.css +++ b/app/Views/_assets/styles/tabs.css @@ -7,7 +7,7 @@ @apply absolute -left-full; } - .tab-panel { + .tabset .tab-panel { @apply hidden; } @@ -31,7 +31,7 @@ @apply font-semibold opacity-100 text-pine-700; } - .tab-panels { + .tabset .tab-panels { @apply col-span-2 p-6; } } diff --git a/app/Views/admin/episode/create.php b/app/Views/admin/episode/create.php index 4d6061c9..883af9f7 100644 --- a/app/Views/admin/episode/create.php +++ b/app/Views/admin/episode/create.php @@ -29,14 +29,14 @@ ) ?> 'enclosure', - 'name' => 'enclosure', + 'id' => 'audio_file', + 'name' => 'audio_file', 'class' => 'form-input mb-4', 'required' => 'required', 'type' => 'file', @@ -263,34 +263,133 @@ lang('Episode.form.additional_files_section_title'), lang('Episode.form.additional_files_section_subtitle'), ) ?> - - 'transcript', - 'name' => 'transcript', - 'class' => 'form-input mb-4', - 'type' => 'file', - 'accept' => '.txt,.html,.srt,.json', -]) ?> - - 'chapters', - 'name' => 'chapters', - 'class' => 'form-input mb-4', - 'type' => 'file', - 'accept' => '.json', -]) ?> + + 'flex flex-col mb-4']) ?> + (' . + lang('Common.optional') . + ')' . + hint_tooltip(lang('Episode.form.transcript_hint'), 'ml-1') ?> +
+ /> + + + /> + + +
+
+ 'sr-only'], + lang('Episode.form.transcript_file'), + true, + ) ?> + 'transcript_file', + 'name' => 'transcript_file', + 'class' => 'form-input', + 'type' => 'file', + 'accept' => '.txt,.html,.srt,.json', + ]) ?> +
+
+ 'sr-only'], + lang('Episode.form.transcript_file_remote_url'), + true, + ) ?> + 'transcript_file_remote_url', + 'name' => 'transcript_file_remote_url', + 'class' => 'form-input w-full', + 'type' => 'url', + 'placeholder' => 'https://...', + 'value' => old('transcript_file_remote_url'), + ]) ?> +
+
+
+ + + 'flex flex-col mb-4']) ?> + (' . + lang('Common.optional') . + ')' . + hint_tooltip(lang('Episode.form.chapters_hint'), 'ml-1') ?> +
+ /> + + + /> + + +
+
+ 'sr-only'], + lang('Episode.form.chapters_file'), + true, + ) ?> + 'chapters_file', + 'name' => 'chapters_file', + 'class' => 'form-input', + 'type' => 'file', + 'accept' => '.json', + ]) ?> +
+
+ 'sr-only'], + lang('Episode.form.chapters_file_remote_url'), + true, + ) ?> + 'chapters_file_remote_url', + 'name' => 'chapters_file_remote_url', + 'class' => 'form-input w-full', + 'type' => 'url', + 'placeholder' => 'https://...', + 'value' => old('chapters_file_remote_url'), + ]) ?> +
+
+
+ + image->medium_url . + '" + alt="' . + $episode->title . + '" + class="w-48" +/>', ) ?> 'enclosure', - 'name' => 'enclosure', + 'id' => 'audio_file', + 'name' => 'audio_file', 'class' => 'form-input mb-4', 'type' => 'file', 'accept' => '.mp3,.m4a', @@ -48,11 +56,7 @@ lang('Episode.form.image_hint'), true, ) ?> -<?= $episode->title ?> + 'image', 'name' => 'image', @@ -272,86 +276,192 @@ '“podcast namespace”', ]), ) ?> -
- -transcript): ?> -
- transcriptUrl, - icon('file', 'mr-2') . $episode->transcript, - [ - 'class' => 'inline-flex items-center text-xs', - 'target' => '_blank', - 'rel' => 'noreferrer noopener', - ], - ) . - anchor( - route_to('transcript-delete', $podcast->id, $episode->id), - icon('delete-bin', 'mx-auto'), - [ - 'class' => - 'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900', - 'data-toggle' => 'tooltip', - 'data-placement' => 'bottom', - 'title' => lang('Episode.form.transcript_delete'), - ], + + 'flex flex-col mb-4']) ?> + (' . + lang('Common.optional') . + ')' . + hint_tooltip(lang('Episode.form.transcript_hint'), 'ml-1') ?> +
+ transcript_file_remote_url + ? 'checked' + : '' ?> /> + + + transcript_file_remote_url + ? 'checked' + : '' ?> /> + + +
+
+ transcript_file): ?> +
+ transcript_file_url, + icon('file', 'mr-2 text-gray-500') . + $episode->transcript_file, + [ + 'class' => 'inline-flex items-center text-xs', + 'target' => '_blank', + 'rel' => 'noreferrer noopener', + ], + ) . + anchor( + route_to( + 'transcript-delete', + $podcast->id, + $episode->id, + ), + icon('delete-bin', 'mx-auto'), + [ + 'class' => + 'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900', + 'data-toggle' => 'tooltip', + 'data-placement' => 'bottom', + 'title' => lang( + 'Episode.form.transcript_file_delete', + ), + ], + ) ?> +
+ + 'sr-only'], + lang('Episode.form.transcript_file'), + true, ) ?> -
- - 'transcript', - 'name' => 'transcript', - 'class' => 'form-input mb-4', - 'type' => 'file', - 'accept' => '.txt,.html,.srt,.json', -]) ?> -
-
- -chapters): ?> -
- chaptersUrl, - icon('file', 'mr-2') . $episode->chapters, - [ - 'class' => 'inline-flex items-center text-xs', - 'target' => '_blank', - 'rel' => 'noreferrer noopener', - ], - ) . - anchor( - route_to('chapters-delete', $podcast->id, $episode->id), - icon('delete-bin', 'mx-auto'), - [ - 'class' => - 'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900', - 'data-toggle' => 'tooltip', - 'data-placement' => 'bottom', - 'title' => lang('Episode.form.chapters_delete'), - ], + 'transcript_file', + 'name' => 'transcript_file', + 'class' => 'form-input', + 'type' => 'file', + 'accept' => '.txt,.html,.srt,.json', + ]) ?> + +
+ 'sr-only'], + lang('Episode.form.transcript_file_remote_url'), + true, ) ?> + 'transcript_file_remote_url', + 'name' => 'transcript_file_remote_url', + 'class' => 'form-input w-full', + 'type' => 'url', + 'placeholder' => 'https://...', + 'value' => old( + 'transcript_file_remote_url', + $episode->transcript_file_remote_url, + ), + ]) ?> +
+
- - 'chapters', - 'name' => 'chapters', - 'class' => 'form-input mb-4', - 'type' => 'file', - 'accept' => '.json', -]) ?> -
+ + + 'flex flex-col mb-4']) ?> + (' . + lang('Common.optional') . + ')' . + hint_tooltip(lang('Episode.form.chapters_hint'), 'ml-1') ?> +
+ chapters_file_remote_url + ? 'checked' + : '' ?> /> + + + chapters_file_remote_url + ? 'checked' + : '' ?> /> + + +
+
+ chapters_file): ?> +
+ chapters_file_url, + icon('file', 'mr-2') . $episode->chapters_file, + [ + 'class' => 'inline-flex items-center text-xs', + 'target' => '_blank', + 'rel' => 'noreferrer noopener', + ], + ) . + anchor( + route_to( + 'chapters-delete', + $podcast->id, + $episode->id, + ), + icon('delete-bin', 'mx-auto'), + [ + 'class' => + 'p-1 bg-red-200 rounded-full text-red-700 hover:text-red-900', + 'data-toggle' => 'tooltip', + 'data-placement' => 'bottom', + 'title' => lang( + 'Episode.form.chapters_file_delete', + ), + ], + ) ?> +
+ + 'sr-only'], + lang('Episode.form.chapters_file'), + true, + ) ?> + 'chapters_file', + 'name' => 'chapters_file', + 'class' => 'form-input', + 'type' => 'file', + 'accept' => '.json', + ]) ?> +
+
+ 'sr-only'], + lang('Episode.form.chapters_file_remote_url'), + true, + ) ?> + 'chapters_file_remote_url', + 'name' => 'chapters_file_remote_url', + 'class' => 'form-input w-full', + 'type' => 'url', + 'placeholder' => 'https://...', + 'value' => old( + 'chapters_file_remote_url', + $episode->chapters_file_remote_url, + ), + ]) ?> +
+
+
+ + publication_status, ) ?> -
diff --git a/app/Views/admin/episode/publish.php b/app/Views/admin/episode/publish.php index f491092b..4cc1d7c7 100644 --- a/app/Views/admin/episode/publish.php +++ b/app/Views/admin/episode/publish.php @@ -62,15 +62,15 @@ ) ?>
-
diff --git a/app/Views/admin/episode/publish_edit.php b/app/Views/admin/episode/publish_edit.php index 64246aef..973dc5d5 100644 --- a/app/Views/admin/episode/publish_edit.php +++ b/app/Views/admin/episode/publish_edit.php @@ -78,13 +78,13 @@ ]) ?> - - - - - -