feat(media): clean media api + create an entity per media type
This commit is contained in:
parent
b09acf6c65
commit
fafaa7e689
|
@ -31,7 +31,7 @@ class AddMedia extends Migration
|
|||
'unsigned' => true,
|
||||
'comment' => 'File size in bytes',
|
||||
],
|
||||
'file_content_type' => [
|
||||
'file_mimetype' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 45,
|
||||
],
|
||||
|
@ -42,6 +42,7 @@ class AddMedia extends Migration
|
|||
'type' => [
|
||||
'type' => 'ENUM',
|
||||
'constraint' => ['image', 'audio', 'video', 'transcript', 'chapters', 'document'],
|
||||
'default' => 'document',
|
||||
],
|
||||
'description' => [
|
||||
'type' => 'TEXT',
|
||||
|
@ -71,6 +72,7 @@ class AddMedia extends Migration
|
|||
]);
|
||||
|
||||
$this->forge->addKey('id', true);
|
||||
$this->forge->addUniqueKey('file_path');
|
||||
$this->forge->addForeignKey('uploaded_by', 'users', 'id');
|
||||
$this->forge->addForeignKey('updated_by', 'users', 'id');
|
||||
$this->forge->createTable('media');
|
||||
|
|
|
@ -198,7 +198,7 @@ class AddPodcasts extends Migration
|
|||
$this->forge->addUniqueKey('actor_id');
|
||||
$this->forge->addForeignKey('actor_id', config('Fediverse')->tablesPrefix . 'actors', 'id', '', 'CASCADE');
|
||||
$this->forge->addForeignKey('cover_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('banner_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('banner_id', 'media', 'id', '', 'SET NULL');
|
||||
$this->forge->addForeignKey('category_id', 'categories', 'id');
|
||||
$this->forge->addForeignKey('language_code', 'languages', 'code');
|
||||
$this->forge->addForeignKey('created_by', 'users', 'id');
|
||||
|
|
|
@ -157,9 +157,9 @@ class AddEpisodes extends Migration
|
|||
$this->forge->addUniqueKey(['podcast_id', 'slug']);
|
||||
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id', '', 'CASCADE');
|
||||
$this->forge->addForeignKey('audio_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('cover_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('transcript_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('chapters_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('cover_id', 'media', 'id', '', 'SET NULL');
|
||||
$this->forge->addForeignKey('transcript_id', 'media', 'id', '', 'SET NULL');
|
||||
$this->forge->addForeignKey('chapters_id', 'media', 'id', '', 'SET NULL');
|
||||
$this->forge->addForeignKey('created_by', 'users', 'id');
|
||||
$this->forge->addForeignKey('updated_by', 'users', 'id');
|
||||
$this->forge->createTable('episodes');
|
||||
|
|
|
@ -64,7 +64,7 @@ class AddPersons extends Migration
|
|||
]);
|
||||
|
||||
$this->forge->addKey('id', true);
|
||||
$this->forge->addForeignKey('avatar_id', 'media', 'id');
|
||||
$this->forge->addForeignKey('avatar_id', 'media', 'id', '', 'SET NULL');
|
||||
$this->forge->addForeignKey('created_by', 'users', 'id');
|
||||
$this->forge->addForeignKey('updated_by', 'users', 'id');
|
||||
$this->forge->createTable('persons');
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
class BaseEntity extends Entity
|
||||
{
|
||||
}
|
|
@ -10,6 +10,10 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Entities;
|
||||
|
||||
use App\Entities\Media\Audio;
|
||||
use App\Entities\Media\Chapters;
|
||||
use App\Entities\Media\Image;
|
||||
use App\Entities\Media\Transcript;
|
||||
use App\Libraries\SimpleRSSElement;
|
||||
use App\Models\ClipsModel;
|
||||
use App\Models\EpisodeCommentModel;
|
||||
|
@ -19,6 +23,7 @@ use App\Models\PodcastModel;
|
|||
use App\Models\PostModel;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
use CodeIgniter\Files\File;
|
||||
use CodeIgniter\HTTP\Files\UploadedFile;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use RuntimeException;
|
||||
|
@ -42,10 +47,10 @@ use RuntimeException;
|
|||
* @property int $cover_id
|
||||
* @property Image $cover
|
||||
* @property int|null $transcript_id
|
||||
* @property Media|null $transcript
|
||||
* @property Transcript|null $transcript
|
||||
* @property string|null $transcript_remote_url
|
||||
* @property int|null $chapters_id
|
||||
* @property Media|null $chapters
|
||||
* @property Chapters|null $chapters
|
||||
* @property string|null $chapters_remote_url
|
||||
* @property string|null $parental_advisory
|
||||
* @property int $number
|
||||
|
@ -69,7 +74,7 @@ use RuntimeException;
|
|||
* @property Time|null $deleted_at;
|
||||
*
|
||||
* @property Person[] $persons;
|
||||
* @property Soundbite[] $soundbites;
|
||||
* @property Clip[] $clips;
|
||||
* @property string $embed_url;
|
||||
*/
|
||||
class Episode extends Entity
|
||||
|
@ -78,7 +83,7 @@ class Episode extends Entity
|
|||
|
||||
protected string $link;
|
||||
|
||||
protected Audio $audio;
|
||||
protected ?Audio $audio = null;
|
||||
|
||||
protected string $audio_url;
|
||||
|
||||
|
@ -90,13 +95,13 @@ class Episode extends Entity
|
|||
|
||||
protected string $embed_url;
|
||||
|
||||
protected Image $cover;
|
||||
protected ?Image $cover = null;
|
||||
|
||||
protected ?string $description = null;
|
||||
|
||||
protected ?Media $transcript;
|
||||
protected ?Transcript $transcript = null;
|
||||
|
||||
protected ?Media $chapters;
|
||||
protected ?Chapters $chapters = null;
|
||||
|
||||
/**
|
||||
* @var Person[]|null
|
||||
|
@ -161,25 +166,31 @@ class Episode extends Entity
|
|||
'updated_by' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Saves an episode cover
|
||||
*/
|
||||
public function setCover(?Image $cover = null): static
|
||||
public function setCover(?UploadedFile $file): self
|
||||
{
|
||||
if ($cover === null) {
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Save image
|
||||
$cover->saveImage(
|
||||
config('Images')
|
||||
->podcastCoverSizes,
|
||||
'podcasts/' . $this->getPodcast()->handle,
|
||||
$this->attributes['slug']
|
||||
);
|
||||
if (array_key_exists('cover_id', $this->attributes) && $this->attributes['cover_id'] !== null) {
|
||||
$this->getCover()
|
||||
->setFile($file);
|
||||
$this->getCover()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('image'))->updateMedia($this->getCover());
|
||||
} else {
|
||||
$cover = new Image([
|
||||
'file_name' => $this->attributes['slug'],
|
||||
'file_directory' => 'podcasts/' . $this->attributes['handle'],
|
||||
'sizes' => config('Images')
|
||||
->podcastCoverSizes,
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$cover->setFile($file);
|
||||
|
||||
$this->attributes['cover_mimetype'] = $cover->mimetype;
|
||||
$this->attributes['cover_path'] = $cover->path;
|
||||
$this->attributes['cover_id'] = (new MediaModel('image'))->saveMedia($cover);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -193,25 +204,106 @@ class Episode extends Entity
|
|||
return $this->cover;
|
||||
}
|
||||
|
||||
public function setAudio(?UploadedFile $file): self
|
||||
{
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->audio_id !== null) {
|
||||
$this->getAudio()
|
||||
->setFile($file);
|
||||
$this->getAudio()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('audio'))->updateMedia($this->getAudio());
|
||||
} else {
|
||||
$transcript = new Audio([
|
||||
'file_name' => $this->attributes['slug'],
|
||||
'file_directory' => 'podcasts/' . $this->attributes['handle'],
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$transcript->setFile($file);
|
||||
|
||||
$this->attributes['transcript_id'] = (new MediaModel())->saveMedia($transcript);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAudio(): Audio
|
||||
{
|
||||
if (! $this->audio) {
|
||||
if (! $this->audio instanceof Audio) {
|
||||
$this->audio = (new MediaModel('audio'))->getMediaById($this->audio_id);
|
||||
}
|
||||
|
||||
return $this->audio;
|
||||
}
|
||||
|
||||
public function getTranscript(): ?Media
|
||||
public function setTranscript(?UploadedFile $file): self
|
||||
{
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->getTranscript() !== null) {
|
||||
$this->getTranscript()
|
||||
->setFile($file);
|
||||
$this->getTranscript()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('transcript'))->updateMedia($this->getTranscript());
|
||||
} else {
|
||||
$transcript = new Transcript([
|
||||
'file_name' => $this->attributes['slug'] . '-transcript',
|
||||
'file_directory' => 'podcasts/' . $this->attributes['handle'],
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$transcript->setFile($file);
|
||||
|
||||
$this->attributes['transcript_id'] = (new MediaModel())->saveMedia($transcript);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTranscript(): ?Transcript
|
||||
{
|
||||
if ($this->transcript_id !== null && $this->transcript === null) {
|
||||
$this->transcript = (new MediaModel('document'))->getMediaById($this->transcript_id);
|
||||
$this->transcript = (new MediaModel('transcript'))->getMediaById($this->transcript_id);
|
||||
}
|
||||
|
||||
return $this->transcript;
|
||||
}
|
||||
|
||||
public function getChaptersFile(): ?Media
|
||||
public function setChapters(?UploadedFile $file): self
|
||||
{
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->getChapters() !== null) {
|
||||
$this->getChapters()
|
||||
->setFile($file);
|
||||
$this->getChapters()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('chapters'))->updateMedia($this->getChapters());
|
||||
} else {
|
||||
$chapters = new Chapters([
|
||||
'file_name' => $this->attributes['slug'] . '-chapters',
|
||||
'file_directory' => 'podcasts/' . $this->attributes['handle'],
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$chapters->setFile($file);
|
||||
|
||||
$this->attributes['chapters_id'] = (new MediaModel())->saveMedia($chapters);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getChapters(): ?Chapters
|
||||
{
|
||||
if ($this->chapters_id !== null && $this->chapters === null) {
|
||||
$this->chapters = (new MediaModel('document'))->getMediaById($this->chapters_id);
|
||||
|
@ -261,7 +353,7 @@ class Episode extends Entity
|
|||
public function getTranscriptUrl(): ?string
|
||||
{
|
||||
if ($this->transcript !== null) {
|
||||
return $this->transcript->url;
|
||||
return $this->transcript->file_url;
|
||||
}
|
||||
return $this->transcript_remote_url;
|
||||
}
|
||||
|
@ -271,8 +363,8 @@ class Episode extends Entity
|
|||
*/
|
||||
public function getChaptersFileUrl(): ?string
|
||||
{
|
||||
if ($this->chapters) {
|
||||
return $this->chapters->url;
|
||||
if ($this->chapters !== null) {
|
||||
return $this->chapters->file_url;
|
||||
}
|
||||
|
||||
return $this->chapters_remote_url;
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Files\File;
|
||||
use Config\Images;
|
||||
|
||||
class Image extends Media
|
||||
{
|
||||
/**
|
||||
* @var array<string, array<string, int|string>>
|
||||
*/
|
||||
public array $sizes = [];
|
||||
|
||||
protected Images $config;
|
||||
|
||||
protected string $type = 'image';
|
||||
|
||||
public function __get($property)
|
||||
{
|
||||
if (str_ends_with($property, '_url') || str_ends_with($property, '_path') || str_ends_with(
|
||||
$property,
|
||||
'_mimetype'
|
||||
)) {
|
||||
$this->initSizeProperties();
|
||||
}
|
||||
|
||||
parent::__get($property);
|
||||
}
|
||||
|
||||
public function setFileMetadata(string $metadata): self
|
||||
{
|
||||
$this->attributes['file_metadata'] = $metadata;
|
||||
|
||||
$metadataArray = json_decode($metadata, true);
|
||||
if (! array_key_exists('sizes', $metadataArray)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->sizes = $metadataArray['sizes'];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initSizeProperties(): bool
|
||||
{
|
||||
if ($this->file_path === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->sizes === []) {
|
||||
$this->sizes = $this->file_metadata['sizes'];
|
||||
}
|
||||
|
||||
helper('media');
|
||||
|
||||
$extension = $this->file_extension;
|
||||
$mimetype = $this->mimetype;
|
||||
foreach ($this->sizes as $name => $size) {
|
||||
if (array_key_exists('extension', $size)) {
|
||||
$extension = $size['extension'];
|
||||
}
|
||||
if (array_key_exists('mimetype', $size)) {
|
||||
$mimetype = $size['mimetype'];
|
||||
}
|
||||
$this->{$name . '_path'} = $this->file_directory . '/' . $this->file_name . '_' . $name . '.' . $extension;
|
||||
$this->{$name . '_url'} = media_base_url($this->{$name . '_path'});
|
||||
$this->{$name . '_mimetype'} = $mimetype;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveInDisk(File $file, string $dirname, string $filename): void
|
||||
{
|
||||
// save original
|
||||
parent::saveInDisk($file, $dirname, $filename);
|
||||
|
||||
$this->initSizeProperties();
|
||||
|
||||
// save derived sizes
|
||||
$imageService = service('image');
|
||||
foreach ($this->sizes as $name => $size) {
|
||||
$pathProperty = $name . '_path';
|
||||
$imageService
|
||||
->withFile(media_path($this->file_path))
|
||||
->resize($size['width'], $size['height']);
|
||||
$imageService->save(media_path($this->{$pathProperty}));
|
||||
}
|
||||
}
|
||||
|
||||
public function injectFileData(File $file): void
|
||||
{
|
||||
$metadata = exif_read_data(media_path($this->file_path), null, true);
|
||||
|
||||
if ($metadata) {
|
||||
$metadata['sizes'] = $this->sizes;
|
||||
$this->file_size = $metadata['FILE']['FileSize'];
|
||||
$this->file_metadata = $metadata;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int[]> $sizes
|
||||
*/
|
||||
public function delete(array $sizes): void
|
||||
{
|
||||
helper('media');
|
||||
|
||||
foreach (array_keys($sizes) as $name) {
|
||||
$pathProperty = $name . '_path';
|
||||
unlink(media_path($this->{$pathProperty}));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ declare(strict_types=1);
|
|||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
namespace App\Entities\Media;
|
||||
|
||||
use CodeIgniter\Files\File;
|
||||
use JamesHeinrich\GetID3\GetID3;
|
||||
|
@ -17,7 +17,7 @@ use JamesHeinrich\GetID3\GetID3;
|
|||
* @property float $duration
|
||||
* @property int $header_size
|
||||
*/
|
||||
class Audio extends Media
|
||||
class Audio extends BaseMedia
|
||||
{
|
||||
protected string $type = 'audio';
|
||||
|
||||
|
@ -41,7 +41,7 @@ class Audio extends Media
|
|||
$getID3 = new GetID3();
|
||||
$audioMetadata = $getID3->analyze((string) $file);
|
||||
|
||||
$this->attributes['file_content_type'] = $audioMetadata['mimetype'];
|
||||
$this->attributes['file_mimetype'] = $audioMetadata['mimetype'];
|
||||
$this->attributes['file_size'] = $audioMetadata['filesize'];
|
||||
$this->attributes['description'] = $audioMetadata['comments']['comment'];
|
||||
$this->attributes['file_metadata'] = $audioMetadata;
|
|
@ -8,7 +8,7 @@ declare(strict_types=1);
|
|||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
namespace App\Entities\Media;
|
||||
|
||||
use CodeIgniter\Entity\Entity;
|
||||
use CodeIgniter\Files\File;
|
||||
|
@ -16,11 +16,12 @@ use CodeIgniter\Files\File;
|
|||
/**
|
||||
* @property int $id
|
||||
* @property string $file_path
|
||||
* @property string $file_url
|
||||
* @property string $file_directory
|
||||
* @property string $file_extension
|
||||
* @property string $file_name
|
||||
* @property int $file_size
|
||||
* @property string $file_content_type
|
||||
* @property string $file_mimetype
|
||||
* @property array $file_metadata
|
||||
* @property 'image'|'audio'|'video'|'document' $type
|
||||
* @property string $description
|
||||
|
@ -28,7 +29,7 @@ use CodeIgniter\Files\File;
|
|||
* @property int $uploaded_by
|
||||
* @property int $updated_by
|
||||
*/
|
||||
class Media extends Entity
|
||||
class BaseMedia extends Entity
|
||||
{
|
||||
protected File $file;
|
||||
|
||||
|
@ -44,9 +45,10 @@ class Media extends Entity
|
|||
*/
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'file_extension' => 'string',
|
||||
'file_path' => 'string',
|
||||
'file_size' => 'int',
|
||||
'file_content_type' => 'string',
|
||||
'file_mimetype' => 'string',
|
||||
'file_metadata' => 'json-array',
|
||||
'type' => 'string',
|
||||
'description' => 'string',
|
||||
|
@ -62,16 +64,23 @@ class Media extends Entity
|
|||
{
|
||||
parent::__construct($data);
|
||||
|
||||
if ($this->file_path) {
|
||||
$this->initFileProperties();
|
||||
}
|
||||
|
||||
public function initFileProperties(): void
|
||||
{
|
||||
if ($this->file_path !== '') {
|
||||
helper('media');
|
||||
[
|
||||
'filename' => $filename,
|
||||
'dirname' => $dirname,
|
||||
'extension' => $extension,
|
||||
] = pathinfo($this->file_path);
|
||||
|
||||
$this->file_name = $filename;
|
||||
$this->file_directory = $dirname;
|
||||
$this->file_extension = $extension;
|
||||
$this->attributes['file_url'] = media_base_url($this->file_path);
|
||||
$this->attributes['file_name'] = $filename;
|
||||
$this->attributes['file_directory'] = $dirname;
|
||||
$this->attributes['file_extension'] = $extension;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +88,7 @@ class Media extends Entity
|
|||
{
|
||||
helper('media');
|
||||
|
||||
$this->attributes['file_content_type'] = $file->getMimeType();
|
||||
$this->attributes['file_mimetype'] = $file->getMimeType();
|
||||
$this->attributes['file_metadata'] = json_encode(lstat((string) $file));
|
||||
$this->attributes['file_path'] = save_media(
|
||||
$file,
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities\Media;
|
||||
|
||||
class Chapters extends BaseMedia
|
||||
{
|
||||
protected string $type = 'chapters';
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities\Media;
|
||||
|
||||
class Document extends BaseMedia
|
||||
{
|
||||
protected string $type = 'document';
|
||||
}
|
|
@ -8,20 +8,20 @@ declare(strict_types=1);
|
|||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
namespace App\Entities\Media;
|
||||
|
||||
use CodeIgniter\Files\File;
|
||||
|
||||
class Image extends Media
|
||||
/**
|
||||
* @property array $sizes
|
||||
*/
|
||||
class Image extends BaseMedia
|
||||
{
|
||||
protected string $type = 'image';
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|null $data
|
||||
*/
|
||||
public function __construct(array $data = null)
|
||||
public function initFileProperties(): void
|
||||
{
|
||||
parent::__construct($data);
|
||||
parent::initFileProperties();
|
||||
|
||||
if ($this->file_path && $this->file_metadata) {
|
||||
$this->sizes = $this->file_metadata['sizes'];
|
||||
|
@ -34,7 +34,7 @@ class Image extends Media
|
|||
helper('media');
|
||||
|
||||
$extension = $this->file_extension;
|
||||
$mimetype = $this->mimetype;
|
||||
$mimetype = $this->file_mimetype;
|
||||
foreach ($this->sizes as $name => $size) {
|
||||
if (array_key_exists('extension', $size)) {
|
||||
$extension = $size['extension'];
|
||||
|
@ -62,6 +62,23 @@ class Image extends Media
|
|||
$this->attributes['file_metadata'] = json_encode($metadata);
|
||||
}
|
||||
|
||||
$this->initFileProperties();
|
||||
$this->saveSizes();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function deleteFile(): void
|
||||
{
|
||||
helper('media');
|
||||
|
||||
unlink(media_path($this->file_path));
|
||||
|
||||
$this->deleteSizes();
|
||||
}
|
||||
|
||||
private function saveSizes(): void
|
||||
{
|
||||
// save derived sizes
|
||||
$imageService = service('image');
|
||||
foreach ($this->sizes as $name => $size) {
|
||||
|
@ -71,7 +88,14 @@ class Image extends Media
|
|||
->resize($size['width'], $size['height']);
|
||||
$imageService->save(media_path($this->{$pathProperty}));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
private function deleteSizes(): void
|
||||
{
|
||||
// delete all derived sizes
|
||||
foreach (array_keys($this->sizes) as $name) {
|
||||
$pathProperty = $name . '_path';
|
||||
unlink(media_path($this->{$pathProperty}));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities\Media;
|
||||
|
||||
class Transcript extends BaseMedia
|
||||
{
|
||||
protected string $type = 'transcript';
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities\Media;
|
||||
|
||||
class Video extends BaseMedia
|
||||
{
|
||||
protected string $type = 'video';
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity\Entity;
|
||||
use CodeIgniter\Files\File;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $file_path
|
||||
* @property string $file_directory
|
||||
* @property string $file_extension
|
||||
* @property string $file_name
|
||||
* @property int $file_size
|
||||
* @property string $file_content_type
|
||||
* @property array $file_metadata
|
||||
* @property 'image'|'audio'|'video'|'document' $type
|
||||
* @property string $description
|
||||
* @property string|null $language_code
|
||||
* @property int $uploaded_by
|
||||
* @property int $updated_by
|
||||
*/
|
||||
class Media extends Entity
|
||||
{
|
||||
protected File $file;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $dates = ['uploaded_at', 'updated_at', 'deleted_at'];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'file_path' => 'string',
|
||||
'file_size' => 'string',
|
||||
'file_content_type' => 'string',
|
||||
'file_metadata' => 'json-array',
|
||||
'type' => 'string',
|
||||
'description' => 'string',
|
||||
'language_code' => '?string',
|
||||
'uploaded_by' => 'integer',
|
||||
'updated_by' => 'integer',
|
||||
];
|
||||
|
||||
public function setFilePath(string $path): self
|
||||
{
|
||||
$this->attributes['file_path'] = $path;
|
||||
|
||||
[
|
||||
'filename' => $filename,
|
||||
'dirname' => $dirname,
|
||||
'extension' => $extension,
|
||||
] = pathinfo($path);
|
||||
|
||||
$this->file_name = $filename;
|
||||
$this->file_directory = $dirname;
|
||||
$this->file_extension = $extension;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function saveInDisk(File $file, string $dirname, string $filename): void
|
||||
{
|
||||
helper('media');
|
||||
|
||||
$this->file_content_type = $file->getMimeType();
|
||||
|
||||
$filePath = save_media($file, $dirname, $filename);
|
||||
|
||||
$this->file_path = $filePath;
|
||||
}
|
||||
|
||||
public function injectFileData(File $file): void
|
||||
{
|
||||
$this->file_content_type = $file->getMimeType();
|
||||
$this->type = 'document';
|
||||
|
||||
if ($filesize = filesize(media_path($this->file_path))) {
|
||||
$this->file_size = $filesize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,8 +10,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Entities;
|
||||
|
||||
use App\Entities\Media\Image;
|
||||
use App\Models\MediaModel;
|
||||
use App\Models\PersonModel;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
use CodeIgniter\HTTP\Files\UploadedFile;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
|
@ -52,31 +55,52 @@ class Person extends Entity
|
|||
/**
|
||||
* Saves the person avatar in `public/media/persons/`
|
||||
*/
|
||||
public function setAvatar(?Image $avatar = null): static
|
||||
public function setAvatar(?UploadedFile $file = null): static
|
||||
{
|
||||
if ($avatar === null) {
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
helper('media');
|
||||
if (array_key_exists('cover_id', $this->attributes) && $this->attributes['cover_id'] !== null) {
|
||||
$this->getAvatar()
|
||||
->setFile($file);
|
||||
$this->getAvatar()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('image'))->updateMedia($this->getAvatar());
|
||||
} else {
|
||||
$cover = new Image([
|
||||
'file_name' => $this->attributes['unique_name'],
|
||||
'file_directory' => 'persons',
|
||||
'sizes' => config('Images')
|
||||
->personAvatarSizes,
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$cover->setFile($file);
|
||||
|
||||
$avatar->saveImage(config('Images')->personAvatarSizes, 'persons', $this->attributes['unique_name']);
|
||||
|
||||
$this->attributes['avatar_mimetype'] = $avatar->mimetype;
|
||||
$this->attributes['avatar_path'] = $avatar->path;
|
||||
$this->attributes['cover_id'] = (new MediaModel('image'))->saveMedia($cover);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAvatar(): Image
|
||||
{
|
||||
if ($this->attributes['avatar_path'] === null) {
|
||||
return new Image(null, '/castopod-avatar-default.jpg', 'image/jpeg', config('Images')->personAvatarSizes);
|
||||
if ($this->attributes['avatar_id'] === null) {
|
||||
helper('media');
|
||||
return new Image([
|
||||
'file_path' => media_path('castopod-avatar-default.jpg'),
|
||||
'file_mimetype' => 'image/jpeg',
|
||||
'sizes' => config('Images')
|
||||
->personAvatarSizes,
|
||||
]);
|
||||
}
|
||||
|
||||
return new Image(null, $this->attributes['avatar_path'], $this->attributes['avatar_mimetype'], config(
|
||||
'Images'
|
||||
)->personAvatarSizes);
|
||||
if ($this->avatar === null) {
|
||||
$this->avatar = (new MediaModel('image'))->getMediaById($this->avatar_id);
|
||||
}
|
||||
|
||||
return $this->avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Entities;
|
||||
|
||||
use App\Entities\Media\Image;
|
||||
use App\Libraries\SimpleRSSElement;
|
||||
use App\Models\CategoryModel;
|
||||
use App\Models\EpisodeModel;
|
||||
|
@ -18,6 +19,7 @@ use App\Models\PersonModel;
|
|||
use App\Models\PlatformModel;
|
||||
use App\Models\UserModel;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
use CodeIgniter\HTTP\Files\UploadedFile;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use Modules\Auth\Entities\User;
|
||||
|
@ -192,6 +194,35 @@ class Podcast extends Entity
|
|||
return $this->actor;
|
||||
}
|
||||
|
||||
public function setCover(?UploadedFile $file = null): self
|
||||
{
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (array_key_exists('cover_id', $this->attributes) && $this->attributes['cover_id'] !== null) {
|
||||
$this->getCover()
|
||||
->setFile($file);
|
||||
$this->getCover()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('image'))->updateMedia($this->getCover());
|
||||
} else {
|
||||
$cover = new Image([
|
||||
'file_name' => 'cover',
|
||||
'file_directory' => 'podcasts/' . $this->attributes['handle'],
|
||||
'sizes' => config('Images')
|
||||
->podcastCoverSizes,
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$cover->setFile($file);
|
||||
|
||||
$this->attributes['cover_id'] = (new MediaModel('image'))->saveMedia($cover);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCover(): Image
|
||||
{
|
||||
if (! $this->cover instanceof Image) {
|
||||
|
@ -201,6 +232,35 @@ class Podcast extends Entity
|
|||
return $this->cover;
|
||||
}
|
||||
|
||||
public function setBanner(?UploadedFile $file): self
|
||||
{
|
||||
if ($file === null || ! $file->isValid()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (array_key_exists('banner_id', $this->attributes) && $this->attributes['banner_id'] !== null) {
|
||||
$this->getBanner()
|
||||
->setFile($file);
|
||||
$this->getBanner()
|
||||
->updated_by = (int) user_id();
|
||||
(new MediaModel('image'))->updateMedia($this->getBanner());
|
||||
} else {
|
||||
$banner = new Image([
|
||||
'file_name' => 'banner',
|
||||
'file_directory' => 'podcasts/' . $this->attributes['handle'],
|
||||
'sizes' => config('Images')
|
||||
->podcastBannerSizes,
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$banner->setFile($file);
|
||||
|
||||
$this->attributes['banner_id'] = (new MediaModel('image'))->saveMedia($banner);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBanner(): Image
|
||||
{
|
||||
if ($this->banner_id === null) {
|
||||
|
|
|
@ -212,7 +212,7 @@ if (! function_exists('get_rss_feed')) {
|
|||
: '?_from=' . urlencode($serviceSlug)),
|
||||
);
|
||||
$enclosure->addAttribute('length', (string) $episode->audio->file_size);
|
||||
$enclosure->addAttribute('type', $episode->audio->file_content_type);
|
||||
$enclosure->addAttribute('type', $episode->audio->file_mimetype);
|
||||
|
||||
$item->addChild('guid', $episode->guid);
|
||||
$item->addChild('pubDate', $episode->published_at->format(DATE_RFC1123));
|
||||
|
@ -255,25 +255,25 @@ if (! function_exists('get_rss_feed')) {
|
|||
$comments->addAttribute('uri', url_to('episode-comments', $podcast->handle, $episode->slug));
|
||||
$comments->addAttribute('contentType', 'application/podcast-activity+json');
|
||||
|
||||
if ($episode->transcript->file_url) {
|
||||
if ($episode->transcript->file_url !== '') {
|
||||
$transcriptElement = $item->addChild('transcript', null, $podcastNamespace);
|
||||
$transcriptElement->addAttribute('url', $episode->transcript_file_url);
|
||||
$transcriptElement->addAttribute('url', $episode->transcript->file_url);
|
||||
$transcriptElement->addAttribute(
|
||||
'type',
|
||||
Mimes::guessTypeFromExtension(
|
||||
pathinfo($episode->transcript_file_url, PATHINFO_EXTENSION)
|
||||
pathinfo($episode->transcript->file_url, PATHINFO_EXTENSION)
|
||||
) ?? 'text/html',
|
||||
);
|
||||
$transcriptElement->addAttribute('language', $podcast->language_code);
|
||||
}
|
||||
|
||||
if ($episode->chapters->file_url) {
|
||||
if ($episode->chapters->file_url !== '') {
|
||||
$chaptersElement = $item->addChild('chapters', null, $podcastNamespace);
|
||||
$chaptersElement->addAttribute('url', $episode->chapters_file_url);
|
||||
$chaptersElement->addAttribute('url', $episode->chapters->file_url);
|
||||
$chaptersElement->addAttribute('type', 'application/json+chapters');
|
||||
}
|
||||
|
||||
foreach ($episode->clip as $clip) {
|
||||
foreach ($episode->clips as $clip) {
|
||||
// TODO: differentiate video from soundbites?
|
||||
$soundbiteElement = $item->addChild('soundbite', $clip->label, $podcastNamespace);
|
||||
$soundbiteElement->addAttribute('start_time', (string) $clip->start_time);
|
||||
|
|
|
@ -87,7 +87,7 @@ if (! function_exists('get_episode_metatags')) {
|
|||
->og('image:height', (string) config('Images')->podcastCoverSizes['large']['height'])
|
||||
->og('locale', $episode->podcast->language_code)
|
||||
->og('audio', $episode->audio_file_opengraph_url)
|
||||
->og('audio:type', $episode->audio->file_content_type)
|
||||
->og('audio:type', $episode->audio->file_mimetype)
|
||||
->meta('article:published_time', $episode->published_at->format(DATE_ISO8601))
|
||||
->meta('article:modified_time', $episode->updated_at->format(DATE_ISO8601))
|
||||
->twitter('audio:partner', $episode->podcast->publisher ?? '')
|
||||
|
|
|
@ -80,7 +80,7 @@ class VideoClip
|
|||
helper(['media']);
|
||||
|
||||
$this->audioInput = media_path($this->episode->audio->file_path);
|
||||
$this->episodeCoverPath = media_path($this->episode->cover->path);
|
||||
$this->episodeCoverPath = media_path($this->episode->cover->file_path);
|
||||
if ($this->episode->transcript !== null) {
|
||||
$this->subtitlesInput = media_path($this->episode->transcript->file_path);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ class PodcastEpisode extends ObjectType
|
|||
|
||||
$this->image = [
|
||||
'type' => 'Image',
|
||||
'mediaType' => $episode->cover->file_content_type,
|
||||
'mediaType' => $episode->cover->file_mimetype,
|
||||
'url' => $episode->cover->feed_url,
|
||||
];
|
||||
|
||||
|
@ -66,7 +66,7 @@ class PodcastEpisode extends ObjectType
|
|||
'url' => [
|
||||
'href' => $episode->audio->file_url,
|
||||
'type' => 'Link',
|
||||
'mediaType' => $episode->audio->file_content_type,
|
||||
'mediaType' => $episode->audio->file_mimetype,
|
||||
],
|
||||
'transcript' => $episode->transcript->file_url,
|
||||
'chapters' => $episode->chapters->file_url,
|
||||
|
|
|
@ -68,14 +68,14 @@ class EpisodeModel extends Model
|
|||
'guid',
|
||||
'title',
|
||||
'slug',
|
||||
'audio_file_id',
|
||||
'audio_id',
|
||||
'description_markdown',
|
||||
'description_html',
|
||||
'cover_id',
|
||||
'transcript_file_id',
|
||||
'transcript_file_remote_url',
|
||||
'chapters_file_id',
|
||||
'chapters_file_remote_url',
|
||||
'transcript_id',
|
||||
'transcript_remote_url',
|
||||
'chapters_id',
|
||||
'chapters_remote_url',
|
||||
'parental_advisory',
|
||||
'number',
|
||||
'season_number',
|
||||
|
@ -114,13 +114,13 @@ class EpisodeModel extends Model
|
|||
'podcast_id' => 'required',
|
||||
'title' => 'required',
|
||||
'slug' => 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]',
|
||||
'audio_file_id' => 'required',
|
||||
'audio_id' => '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',
|
||||
'transcript_remote_url' => 'valid_url|permit_empty',
|
||||
'chapters_remote_url' => 'valid_url|permit_empty',
|
||||
'published_at' => 'valid_date|permit_empty',
|
||||
'created_by' => 'required',
|
||||
'updated_by' => 'required',
|
||||
|
|
|
@ -10,9 +10,12 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Entities\Audio;
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Media;
|
||||
use App\Entities\Media\Audio;
|
||||
use App\Entities\Media\Chapters;
|
||||
use App\Entities\Media\Document;
|
||||
use App\Entities\Media\Image;
|
||||
use App\Entities\Media\Transcript;
|
||||
use App\Entities\Media\Video;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Model;
|
||||
use CodeIgniter\Validation\ValidationInterface;
|
||||
|
@ -27,7 +30,7 @@ class MediaModel extends Model
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $returnType = Media::class;
|
||||
protected $returnType = Document::class;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
|
@ -36,7 +39,7 @@ class MediaModel extends Model
|
|||
'id',
|
||||
'file_path',
|
||||
'file_size',
|
||||
'file_content_type',
|
||||
'file_mimetype',
|
||||
'file_metadata',
|
||||
'type',
|
||||
'description',
|
||||
|
@ -52,29 +55,36 @@ class MediaModel extends Model
|
|||
* @param ValidationInterface|null $validation Validation
|
||||
*/
|
||||
public function __construct(
|
||||
protected string $fileType,
|
||||
protected string $fileType = 'document',
|
||||
ConnectionInterface &$db = null,
|
||||
ValidationInterface $validation = null
|
||||
) {
|
||||
// @phpstan-ignore-next-line
|
||||
switch ($fileType) {
|
||||
case 'audio':
|
||||
$this->returnType = Audio::class;
|
||||
break;
|
||||
case 'video':
|
||||
$this->returnType = Video::class;
|
||||
break;
|
||||
case 'image':
|
||||
$this->returnType = Image::class;
|
||||
break;
|
||||
case 'transcript':
|
||||
$this->returnType = Transcript::class;
|
||||
break;
|
||||
case 'chapters':
|
||||
$this->returnType = Chapters::class;
|
||||
break;
|
||||
default:
|
||||
// do nothing, keep Media class as default
|
||||
// do nothing, keep Document class as default
|
||||
break;
|
||||
}
|
||||
|
||||
parent::__construct($db, $validation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Media|Image|Audio
|
||||
*/
|
||||
public function getMediaById(int $mediaId): object
|
||||
public function getMediaById(int $mediaId): Document | Audio | Video | Image | Transcript | Chapters
|
||||
{
|
||||
$cacheName = "media#{$mediaId}";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
|
@ -94,7 +104,9 @@ class MediaModel extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Media|Image|Audio $media
|
||||
* @param Document|Audio|Video|Image|Transcript|Chapters $media
|
||||
*
|
||||
* @noRector ReturnTypeDeclarationRector
|
||||
*/
|
||||
public function saveMedia(object $media): int | false
|
||||
{
|
||||
|
@ -103,7 +115,21 @@ class MediaModel extends Model
|
|||
return false;
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
return $mediaId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Document|Audio|Video|Image|Transcript|Chapters $media
|
||||
*
|
||||
* @noRector ReturnTypeDeclarationRector
|
||||
*/
|
||||
public function updateMedia(object $media): bool
|
||||
{
|
||||
return $this->update($media->id, $media);
|
||||
}
|
||||
|
||||
public function deleteMedia(int $mediaId): bool
|
||||
{
|
||||
return $this->delete($mediaId, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Entities\Audio;
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Media;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Model;
|
||||
use CodeIgniter\Validation\ValidationInterface;
|
||||
|
||||
class MediaModel extends Model
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'media';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $returnType = Media::class;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allowedFields = [
|
||||
'id',
|
||||
'file_path',
|
||||
'file_size',
|
||||
'file_content_type',
|
||||
'file_metadata',
|
||||
'type',
|
||||
'description',
|
||||
'language_code',
|
||||
'uploaded_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
/**
|
||||
* Model constructor.
|
||||
*
|
||||
* @param ConnectionInterface|null $db DB Connection
|
||||
* @param ValidationInterface|null $validation Validation
|
||||
*/
|
||||
public function __construct(
|
||||
protected string $fileType,
|
||||
ConnectionInterface &$db = null,
|
||||
ValidationInterface $validation = null
|
||||
) {
|
||||
switch ($fileType) {
|
||||
case 'audio':
|
||||
$this->returnType = Audio::class;
|
||||
break;
|
||||
case 'image':
|
||||
$this->returnType = Image::class;
|
||||
break;
|
||||
default:
|
||||
// do nothing, keep Media class as default
|
||||
break;
|
||||
}
|
||||
|
||||
parent::__construct($db, $validation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Media|Image|Audio
|
||||
*/
|
||||
public function getMediaById(int $mediaId): object
|
||||
{
|
||||
$cacheName = "media#{$mediaId}";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$builder = $this->where([
|
||||
'id' => $mediaId,
|
||||
]);
|
||||
|
||||
$found = $builder->first();
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Media|Image $media
|
||||
*/
|
||||
public function saveMedia(object $media): int | false
|
||||
{
|
||||
// insert record in database
|
||||
if (! $mediaId = $this->insert($media, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
return $mediaId;
|
||||
}
|
||||
|
||||
public function deleteFile(int $mediaId): void
|
||||
{
|
||||
// TODO: get file, delete it from disk & from database
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Person;
|
||||
use CodeIgniter\Database\Query;
|
||||
use CodeIgniter\Model;
|
||||
|
@ -196,7 +195,7 @@ class PersonModel extends Model
|
|||
'full_name' => $fullName,
|
||||
'unique_name' => slugify($fullName),
|
||||
'information_url' => $informationUrl,
|
||||
'image' => new Image(download_file($image)),
|
||||
'image' => download_file($image),
|
||||
'created_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
|
|
|
@ -484,9 +484,9 @@ class PodcastModel extends Model
|
|||
$actor->display_name = $podcast->title;
|
||||
$actor->summary = $podcast->description_html;
|
||||
$actor->avatar_image_url = $podcast->cover->federation_url;
|
||||
$actor->avatar_image_mimetype = $podcast->cover->mimetype;
|
||||
$actor->avatar_image_mimetype = $podcast->cover->file_mimetype;
|
||||
$actor->cover_image_url = $podcast->banner->federation_url;
|
||||
$actor->cover_image_mimetype = $podcast->banner->mimetype;
|
||||
$actor->cover_image_mimetype = $podcast->banner->file_mimetype;
|
||||
|
||||
if ($actor->hasChanged()) {
|
||||
$actorModel->update($actor->id, $actor);
|
||||
|
|
|
@ -12,15 +12,12 @@ namespace Modules\Admin\Controllers;
|
|||
|
||||
use App\Entities\Episode;
|
||||
use App\Entities\EpisodeComment;
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Location;
|
||||
use App\Entities\Media;
|
||||
use App\Entities\Podcast;
|
||||
use App\Entities\Post;
|
||||
use App\Models\ClipsModel;
|
||||
use App\Models\EpisodeCommentModel;
|
||||
use App\Models\EpisodeModel;
|
||||
use App\Models\MediaModel;
|
||||
use App\Models\PodcastModel;
|
||||
use App\Models\PostModel;
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
|
@ -133,7 +130,8 @@ class EpisodeController extends BaseController
|
|||
'title' => $this->request->getPost('title'),
|
||||
'slug' => $this->request->getPost('slug'),
|
||||
'guid' => null,
|
||||
'audio_file' => $this->request->getFile('audio_file'),
|
||||
'audio' => $this->request->getFile('audio_file'),
|
||||
'cover' => $this->request->getFile('cover'),
|
||||
'description_markdown' => $this->request->getPost('description'),
|
||||
'location' => $this->request->getPost('location_name') === '' ? null : new Location($this->request->getPost(
|
||||
'location_name'
|
||||
|
@ -161,69 +159,22 @@ class EpisodeController extends BaseController
|
|||
$db = db_connect();
|
||||
$db->transStart();
|
||||
|
||||
$coverFile = $this->request->getFile('cover');
|
||||
if ($coverFile !== null && $coverFile->isValid()) {
|
||||
$cover = new Image([
|
||||
'file_name' => $newEpisode->slug,
|
||||
'file_directory' => 'podcasts/' . $this->podcast->handle,
|
||||
'sizes' => config('Images')
|
||||
->podcastBannerSizes,
|
||||
'file' => $this->request->getFile('banner'),
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$mediaModel = new MediaModel('image');
|
||||
if (! ($newCoverId = $mediaModel->saveMedia($cover))) {
|
||||
$db->transRollback();
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $mediaModel->errors());
|
||||
}
|
||||
|
||||
$newEpisode->cover_id = $newCoverId;
|
||||
}
|
||||
|
||||
$transcriptChoice = $this->request->getPost('transcript-choice');
|
||||
if (
|
||||
$transcriptChoice === 'upload-file'
|
||||
&& ($transcriptFile = $this->request->getFile('transcript_file'))
|
||||
&& $transcriptFile->isValid()
|
||||
) {
|
||||
$transcript = new Media([
|
||||
'file_name' => $newEpisode->slug . '-transcript',
|
||||
'file_directory' => 'podcasts/' . $this->podcast->handle,
|
||||
'file' => $transcriptFile,
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$mediaModel = new MediaModel('image');
|
||||
if (! ($newTranscriptId = $mediaModel->saveMedia($transcript))) {
|
||||
$db->transRollback();
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $mediaModel->errors());
|
||||
}
|
||||
|
||||
$newEpisode->transcript_id = $newTranscriptId;
|
||||
if ($transcriptChoice === 'upload-file') {
|
||||
$newEpisode->setTranscript($this->request->getFile('transcript_file'));
|
||||
} elseif ($transcriptChoice === 'remote-url') {
|
||||
$newEpisode->transcript_remote_url = $this->request->getPost(
|
||||
'transcript_remote_url'
|
||||
) === '' ? null : $this->request->getPost('transcript_file_remote_url');
|
||||
) === '' ? null : $this->request->getPost('transcript_remote_url');
|
||||
}
|
||||
|
||||
$chaptersChoice = $this->request->getPost('chapters-choice');
|
||||
if (
|
||||
$chaptersChoice === 'upload-file'
|
||||
&& ($chaptersFile = $this->request->getFile('chapters_file'))
|
||||
&& $chaptersFile->isValid()
|
||||
) {
|
||||
$newEpisode->chapters_file = $chaptersFile;
|
||||
if ($chaptersChoice === 'upload-file') {
|
||||
$newEpisode->setChapters($this->request->getFile('chapters_file'));
|
||||
} elseif ($chaptersChoice === 'remote-url') {
|
||||
$newEpisode->chapters_file_remote_url = $this->request->getPost(
|
||||
'chapters_file_remote_url'
|
||||
) === '' ? null : $this->request->getPost('chapters_file_remote_url');
|
||||
$newEpisode->chapters_remote_url = $this->request->getPost(
|
||||
'chapters_remote_url'
|
||||
) === '' ? null : $this->request->getPost('chapters_remote_url');
|
||||
}
|
||||
|
||||
$episodeModel = new EpisodeModel();
|
||||
|
@ -310,51 +261,43 @@ class EpisodeController extends BaseController
|
|||
$this->episode->custom_rss_string = $this->request->getPost('custom_rss');
|
||||
|
||||
$this->episode->updated_by = (int) user_id();
|
||||
|
||||
$audioFile = $this->request->getFile('audio_file');
|
||||
if ($audioFile !== null && $audioFile->isValid()) {
|
||||
$this->episode->audio_file = $audioFile;
|
||||
}
|
||||
|
||||
$coverFile = $this->request->getFile('cover');
|
||||
if ($coverFile !== null && $coverFile->isValid()) {
|
||||
$this->episode->cover = new Image($coverFile);
|
||||
}
|
||||
$this->episode->setAudio($this->request->getFile('audio_file'));
|
||||
$this->episode->setCover($this->request->getFile('cover'));
|
||||
|
||||
$transcriptChoice = $this->request->getPost('transcript-choice');
|
||||
if ($transcriptChoice === 'upload-file') {
|
||||
$transcriptFile = $this->request->getFile('transcript_file');
|
||||
if ($transcriptFile !== null && $transcriptFile->isValid()) {
|
||||
$this->episode->transcript_file = $transcriptFile;
|
||||
$this->episode->transcript_file_remote_url = null;
|
||||
$this->episode->transcript_remote_url = null;
|
||||
}
|
||||
} elseif ($transcriptChoice === 'remote-url') {
|
||||
if (
|
||||
($transcriptFileRemoteUrl = $this->request->getPost('transcript_file_remote_url')) &&
|
||||
($transcriptFileRemoteUrl = $this->request->getPost('transcript_remote_url')) &&
|
||||
(($transcriptFile = $this->episode->transcript_file) !== null)
|
||||
) {
|
||||
unlink((string) $transcriptFile);
|
||||
$this->episode->transcript_file_path = null;
|
||||
$this->episode->transcript->file_path = null;
|
||||
}
|
||||
$this->episode->transcript_file_remote_url = $transcriptFileRemoteUrl === '' ? null : $transcriptFileRemoteUrl;
|
||||
$this->episode->transcript_remote_url = $transcriptFileRemoteUrl === '' ? null : $transcriptFileRemoteUrl;
|
||||
}
|
||||
|
||||
$chaptersChoice = $this->request->getPost('chapters-choice');
|
||||
if ($chaptersChoice === 'upload-file') {
|
||||
$chaptersFile = $this->request->getFile('chapters_file');
|
||||
if ($chaptersFile !== null && $chaptersFile->isValid()) {
|
||||
$this->episode->chapters_file = $chaptersFile;
|
||||
$this->episode->chapters_file_remote_url = null;
|
||||
$this->episode->chapters = $chaptersFile;
|
||||
$this->episode->chapters_remote_url = null;
|
||||
}
|
||||
} elseif ($chaptersChoice === 'remote-url') {
|
||||
if (
|
||||
($chaptersFileRemoteUrl = $this->request->getPost('chapters_file_remote_url')) &&
|
||||
($chaptersFileRemoteUrl = $this->request->getPost('chapters_remote_url')) &&
|
||||
(($chaptersFile = $this->episode->chapters_file) !== null)
|
||||
) {
|
||||
unlink((string) $chaptersFile);
|
||||
$this->episode->chapters_file_path = null;
|
||||
$this->episode->chapters->file_path = null;
|
||||
}
|
||||
$this->episode->chapters_file_remote_url = $chaptersFileRemoteUrl === '' ? null : $chaptersFileRemoteUrl;
|
||||
$this->episode->chapters_remote_url = $chaptersFileRemoteUrl === '' ? null : $chaptersFileRemoteUrl;
|
||||
}
|
||||
|
||||
$db = db_connect();
|
||||
|
@ -396,7 +339,7 @@ class EpisodeController extends BaseController
|
|||
public function transcriptDelete(): RedirectResponse
|
||||
{
|
||||
unlink((string) $this->episode->transcript_file);
|
||||
$this->episode->transcript_file_path = null;
|
||||
$this->episode->transcript->file_path = null;
|
||||
|
||||
$episodeModel = new EpisodeModel();
|
||||
|
||||
|
@ -413,7 +356,7 @@ class EpisodeController extends BaseController
|
|||
public function chaptersDelete(): RedirectResponse
|
||||
{
|
||||
unlink((string) $this->episode->chapters_file);
|
||||
$this->episode->chapters_file_path = null;
|
||||
$this->episode->chapters->file_path = null;
|
||||
|
||||
$episodeModel = new EpisodeModel();
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Modules\Admin\Controllers;
|
||||
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Person;
|
||||
use App\Models\PersonModel;
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
|
@ -78,6 +77,7 @@ class PersonController extends BaseController
|
|||
}
|
||||
|
||||
$person = new Person([
|
||||
'avatar' => $this->request->getFile('avatar'),
|
||||
'full_name' => $this->request->getPost('full_name'),
|
||||
'unique_name' => $this->request->getPost('unique_name'),
|
||||
'information_url' => $this->request->getPost('information_url'),
|
||||
|
@ -85,11 +85,6 @@ class PersonController extends BaseController
|
|||
'updated_by' => user_id(),
|
||||
]);
|
||||
|
||||
$avatarFile = $this->request->getFile('avatar');
|
||||
if ($avatarFile !== null && $avatarFile->isValid()) {
|
||||
$person->avatar = new Image($avatarFile);
|
||||
}
|
||||
|
||||
$personModel = new PersonModel();
|
||||
|
||||
if (! $personModel->insert($person)) {
|
||||
|
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Modules\Admin\Controllers;
|
||||
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Location;
|
||||
use App\Entities\Podcast;
|
||||
use App\Models\CategoryModel;
|
||||
|
@ -196,6 +195,8 @@ class PodcastController extends BaseController
|
|||
$newPodcast = new Podcast([
|
||||
'title' => $this->request->getPost('title'),
|
||||
'handle' => $this->request->getPost('handle'),
|
||||
'cover' => $this->request->getFile('cover'),
|
||||
'banner' => $this->request->getFile('banner'),
|
||||
'description_markdown' => $this->request->getPost('description'),
|
||||
'language_code' => $this->request->getPost('language'),
|
||||
'category_id' => $this->request->getPost('category'),
|
||||
|
@ -228,48 +229,6 @@ class PodcastController extends BaseController
|
|||
$db = db_connect();
|
||||
$db->transStart();
|
||||
|
||||
$cover = new Image([
|
||||
'file_name' => 'cover',
|
||||
'file_directory' => 'podcasts/' . $newPodcast->handle,
|
||||
'sizes' => config('Images')
|
||||
->podcastCoverSizes,
|
||||
'file' => $this->request->getFile('cover'),
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$mediaModel = new MediaModel('image');
|
||||
if (! ($newCoverId = $mediaModel->saveMedia($cover))) {
|
||||
$db->transRollback();
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $mediaModel->errors());
|
||||
}
|
||||
$newPodcast->cover_id = $newCoverId;
|
||||
|
||||
$bannerFile = $this->request->getFile('banner');
|
||||
if ($bannerFile !== null && $bannerFile->isValid()) {
|
||||
$banner = new Image([
|
||||
'file_name' => 'banner',
|
||||
'file_directory' => 'podcasts/' . $newPodcast->handle,
|
||||
'sizes' => config('Images')
|
||||
->podcastBannerSizes,
|
||||
'file' => $this->request->getFile('banner'),
|
||||
'uploaded_by' => user_id(),
|
||||
'updated_by' => user_id(),
|
||||
]);
|
||||
$mediaModel = new MediaModel('image');
|
||||
if (! ($newBannerId = $mediaModel->saveMedia($banner))) {
|
||||
$db->transRollback();
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $mediaModel->errors());
|
||||
}
|
||||
|
||||
$newPodcast->banner_id = $newBannerId;
|
||||
}
|
||||
|
||||
$podcastModel = new PodcastModel();
|
||||
if (! ($newPodcastId = $podcastModel->insert($newPodcast, true))) {
|
||||
$db->transRollback();
|
||||
|
@ -344,15 +303,9 @@ class PodcastController extends BaseController
|
|||
|
||||
$this->podcast->title = $this->request->getPost('title');
|
||||
$this->podcast->description_markdown = $this->request->getPost('description');
|
||||
$this->podcast->setCover($this->request->getFile('cover'));
|
||||
$this->podcast->setBanner($this->request->getFile('banner'));
|
||||
|
||||
$coverFile = $this->request->getFile('cover');
|
||||
if ($coverFile !== null && $coverFile->isValid()) {
|
||||
$this->podcast->cover->setFile($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');
|
||||
$this->podcast->parental_advisory =
|
||||
|
@ -381,6 +334,7 @@ class PodcastController extends BaseController
|
|||
$this->podcast->updated_by = (int) user_id();
|
||||
|
||||
$db = db_connect();
|
||||
|
||||
$db->transStart();
|
||||
|
||||
$podcastModel = new PodcastModel();
|
||||
|
@ -400,7 +354,7 @@ class PodcastController extends BaseController
|
|||
|
||||
$db->transComplete();
|
||||
|
||||
return redirect()->route('podcast-view', [$this->podcast->id]);
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function deleteBanner(): RedirectResponse
|
||||
|
@ -409,17 +363,14 @@ class PodcastController extends BaseController
|
|||
return redirect()->back();
|
||||
}
|
||||
|
||||
$this->podcast->banner->delete(config('Images')->podcastBannerSizes);
|
||||
$this->podcast->banner->deleteFile();
|
||||
|
||||
$this->podcast->banner_path = null;
|
||||
$this->podcast->banner_mimetype = null;
|
||||
|
||||
$podcastModel = new PodcastModel();
|
||||
if (! $podcastModel->update($this->podcast->id, $this->podcast)) {
|
||||
$mediaModel = new MediaModel();
|
||||
if (! $mediaModel->deleteMedia((int) $this->podcast->banner_id)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $podcastModel->errors());
|
||||
->with('errors', $mediaModel->errors());
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
|
|
@ -11,7 +11,6 @@ declare(strict_types=1);
|
|||
namespace Modules\Admin\Controllers;
|
||||
|
||||
use App\Entities\Episode;
|
||||
use App\Entities\Image;
|
||||
use App\Entities\Location;
|
||||
use App\Entities\Person;
|
||||
use App\Entities\Podcast;
|
||||
|
|
|
@ -91,12 +91,12 @@ return [
|
|||
'transcript' => 'Transcript or closed captions',
|
||||
'transcript_hint' => 'Allowed formats are txt, html, srt or json.',
|
||||
'transcript_file' => 'Transcript file',
|
||||
'transcript_file_remote_url' => 'Remote url for transcript',
|
||||
'transcript_remote_url' => 'Remote url for transcript',
|
||||
'transcript_file_delete' => 'Delete transcript file',
|
||||
'chapters' => 'Chapters',
|
||||
'chapters_hint' => 'File must be in JSON Chapters format.',
|
||||
'chapters_file' => 'Chapters file',
|
||||
'chapters_file_remote_url' => 'Remote url for chapters file',
|
||||
'chapters_remote_url' => 'Remote url for chapters file',
|
||||
'chapters_file_delete' => 'Delete chapters file',
|
||||
'advanced_section_title' => 'Advanced Parameters',
|
||||
'advanced_section_subtitle' =>
|
||||
|
|
|
@ -93,13 +93,13 @@ return [
|
|||
'transcript_hint' =>
|
||||
'Les formats autorisés sont txt, html, srt ou json.',
|
||||
'transcript_file' => 'Fichier de transcription',
|
||||
'transcript_file_remote_url' =>
|
||||
'transcript_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 format “JSON Chapters”.',
|
||||
'chapters_file' => 'Fichier de chapitrage',
|
||||
'chapters_file_remote_url' =>
|
||||
'chapters_remote_url' =>
|
||||
'URL distante pour le fichier de chapitrage',
|
||||
'chapters_file_delete' => 'Supprimer le fichier de chapitrage',
|
||||
'advanced_section_title' => 'Paramètres avancés',
|
||||
|
|
|
@ -59,8 +59,8 @@ 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 audio_file_size
|
||||
// - if file is longer than 60 seconds then it's audio_file_header_size + 60 seconds
|
||||
// - if audio is less than or equal to 60s, then take the audio file_size
|
||||
// - if audio is more than 60s, then take the audio file_header_size + 60s
|
||||
$audioFileDuration <= 60
|
||||
? $audioFileSize
|
||||
: $audioFileHeaderSize +
|
||||
|
|
|
@ -20,6 +20,7 @@ parameters:
|
|||
ignoreErrors:
|
||||
- '#This property type might be inlined to PHP. Do you have confidence it is correct\? Put it here#'
|
||||
- '#^Cognitive complexity for#'
|
||||
- '#^Class cognitive complexity#'
|
||||
- '#Do not use chained method calls. Put each on separated lines.#'
|
||||
- '#Do not inherit from abstract class, better use composition#'
|
||||
- '#Cannot access property [\$a-z_]+ on ((array\|)?object)#'
|
||||
|
@ -28,7 +29,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#'
|
||||
- '#^Access to an undefined property App\\Entities\\Media\\Image#'
|
||||
-
|
||||
message: '#Function "function_exists\(\)" cannot be used/left in the code#'
|
||||
paths:
|
||||
|
|
|
@ -156,8 +156,8 @@
|
|||
<Forms.Input class="w-full" name="transcript_file" type="file" accept=".txt,.html,.srt,.json" />
|
||||
</section>
|
||||
<section id="transcript-file-remote-url" class="tab-panel">
|
||||
<Forms.Label class="sr-only" for="transcript_file_remote_url" isOptional="true"><?= lang('Episode.form.transcript_file_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="transcript_file_remote_url" />
|
||||
<Forms.Label class="sr-only" for="transcript_remote_url" isOptional="true"><?= lang('Episode.form.transcript_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="transcript_remote_url" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -183,8 +183,8 @@
|
|||
<Forms.Input class="w-full" name="chapters_file" type="file" accept=".json" />
|
||||
</section>
|
||||
<section id="chapters-file-remote-url" class="tab-panel">
|
||||
<Forms.Label class="sr-only" for="chapters_file_remote_url" isOptional="true"><?= lang('Episode.form.chapters_file_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="chapters_file_remote_url" />
|
||||
<Forms.Label class="sr-only" for="chapters_remote_url" isOptional="true"><?= lang('Episode.form.chapters_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="chapters_remote_url" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -153,10 +153,10 @@
|
|||
')</small>' .
|
||||
hint_tooltip(lang('Episode.form.transcript_hint'), 'ml-1') ?></legend>
|
||||
<div class="form-input-tabs">
|
||||
<input type="radio" name="transcript-choice" id="transcript-file-upload-choice" aria-controls="transcript-file-upload-choice" value="upload-file" <?= $episode->transcript_file_remote_url ? '' : 'checked' ?> />
|
||||
<input type="radio" name="transcript-choice" id="transcript-file-upload-choice" aria-controls="transcript-file-upload-choice" value="upload-file" <?= $episode->transcript_remote_url ? '' : 'checked' ?> />
|
||||
<label for="transcript-file-upload-choice"><?= lang('Common.forms.upload_file') ?></label>
|
||||
|
||||
<input type="radio" name="transcript-choice" id="transcript-file-remote-url-choice" aria-controls="transcript-file-remote-url-choice" value="remote-url" <?= $episode->transcript_file_remote_url ? 'checked' : '' ?> />
|
||||
<input type="radio" name="transcript-choice" id="transcript-file-remote-url-choice" aria-controls="transcript-file-remote-url-choice" value="remote-url" <?= $episode->transcript_remote_url ? 'checked' : '' ?> />
|
||||
<label for="transcript-file-remote-url-choice"><?= lang('Common.forms.remote_url') ?></label>
|
||||
|
||||
<div class="py-2 tab-panels">
|
||||
|
@ -164,7 +164,7 @@
|
|||
<?php if ($episode->transcript_file) : ?>
|
||||
<div class="flex mb-1 gap-x-2">
|
||||
<?= anchor(
|
||||
$episode->transcript_file_url,
|
||||
$episode->transcript->file_url,
|
||||
icon('file', 'mr-2 text-skin-muted') .
|
||||
$episode->transcript_file,
|
||||
[
|
||||
|
@ -195,8 +195,8 @@
|
|||
<Forms.Input class="w-full" name="transcript_file" type="file" accept=".txt,.html,.srt,.json" />
|
||||
</section>
|
||||
<section id="transcript-file-remote-url" class="tab-panel">
|
||||
<Forms.Label class="sr-only" for="transcript_file_remote_url" isOptional="true"><?= lang('Episode.form.transcript_file_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="transcript_file_remote_url" value="<?= $episode->transcript_file_remote_url ?>" />
|
||||
<Forms.Label class="sr-only" for="transcript_remote_url" isOptional="true"><?= lang('Episode.form.transcript_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="transcript_remote_url" value="<?= $episode->transcript_remote_url ?>" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -210,10 +210,10 @@
|
|||
')</small>' .
|
||||
hint_tooltip(lang('Episode.form.chapters_hint'), 'ml-1') ?></legend>
|
||||
<div class="form-input-tabs">
|
||||
<input type="radio" name="chapters-choice" id="chapters-file-upload-choice" aria-controls="chapters-file-upload-choice" value="upload-file" <?= $episode->chapters_file_remote_url ? '' : 'checked' ?> />
|
||||
<input type="radio" name="chapters-choice" id="chapters-file-upload-choice" aria-controls="chapters-file-upload-choice" value="upload-file" <?= $episode->chapters_remote_url ? '' : 'checked' ?> />
|
||||
<label for="chapters-file-upload-choice"><?= lang('Common.forms.upload_file') ?></label>
|
||||
|
||||
<input type="radio" name="chapters-choice" id="chapters-file-remote-url-choice" aria-controls="chapters-file-remote-url-choice" value="remote-url" <?= $episode->chapters_file_remote_url ? 'checked' : '' ?> />
|
||||
<input type="radio" name="chapters-choice" id="chapters-file-remote-url-choice" aria-controls="chapters-file-remote-url-choice" value="remote-url" <?= $episode->chapters_remote_url ? 'checked' : '' ?> />
|
||||
<label for="chapters-file-remote-url-choice"><?= lang('Common.forms.remote_url') ?></label>
|
||||
|
||||
<div class="py-2 tab-panels">
|
||||
|
@ -221,7 +221,7 @@
|
|||
<?php if ($episode->chapters_file) : ?>
|
||||
<div class="flex mb-1 gap-x-2">
|
||||
<?= anchor(
|
||||
$episode->chapters_file_url,
|
||||
$episode->chapters->file_url,
|
||||
icon('file', 'mr-2') . $episode->chapters_file,
|
||||
[
|
||||
'class' => 'inline-flex items-center text-xs',
|
||||
|
@ -251,8 +251,8 @@
|
|||
<Forms.Input class="w-full" name="chapters_file" type="file" accept=".json" />
|
||||
</section>
|
||||
<section id="chapters-file-remote-url" class="tab-panel">
|
||||
<Forms.Label class="sr-only" for="chapters_file_remote_url" isOptional="true"><?= lang('Episode.form.chapters_file_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="chapters_file_remote_url" value="<?= $episode->chapters_file_remote_url ?>" />
|
||||
<Forms.Label class="sr-only" for="chapters_remote_url" isOptional="true"><?= lang('Episode.form.chapters_remote_url') ?></Forms.Label>
|
||||
<Forms.Input class="w-full" placeholder="https://…" name="chapters_remote_url" value="<?= $episode->chapters_remote_url ?>" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
</time>
|
||||
</div>
|
||||
</a>
|
||||
<?= audio_player($episode->audio->file_url, $episode->audio->file_content_type, 'mt-auto') ?>
|
||||
<?= audio_player($episode->audio->file_url, $episode->audio->file_mimetype, 'mt-auto') ?>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="flex justify-around px-6 py-3">
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
</time>
|
||||
</div>
|
||||
</a>
|
||||
<?= audio_player($episode->audio->file_url, $episode->audio->file_content_type, 'mt-auto') ?>
|
||||
<?= audio_player($episode->audio->file_url, $episode->audio->file_mimetype, 'mt-auto') ?>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="flex justify-around px-6 py-3">
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<div class="flex items-center gap-x-2">
|
||||
<audio controls preload="auto" class="flex-1 w-full">
|
||||
<source src="<?= $episode->audio->file_url ?>" type="<?= $episode->audio->file_content_type ?>">
|
||||
<source src="<?= $episode->audio->file_url ?>" type="<?= $episode->audio->file_mimetype ?>">
|
||||
Your browser does not support the audio tag.
|
||||
</audio>
|
||||
<IconButton glyph="timer" variant="info" data-type="get-soundbite" data-start-time-field-name="soundbites[0][start_time]" data-duration-field-name="soundbites[0][duration]" ><?= lang('Episode.soundbites_form.bookmark') ?></IconButton>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<?= $this->section('content') ?>
|
||||
|
||||
<div class="mb-12">
|
||||
<?= audio_player($episode->audio->file_url, $episode->audio->file_content_type) ?>
|
||||
<?= audio_player($episode->audio->file_url, $episode->audio->file_mimetype) ?>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<?php if ($podcast->banner_id !== null): ?>
|
||||
<a href="<?= route_to('podcast-banner-delete', $podcast->id) ?>" class="absolute p-1 text-red-700 bg-red-100 border-2 rounded-full hover:text-red-900 border-contrast focus:ring-accent top-2 right-2" title="<?= lang('Podcast.form.banner_delete') ?>" data-tooltip="bottom"><?= icon('delete-bin') ?></a>
|
||||
<?php endif; ?>
|
||||
<img src="<?= $podcast->banner->small_url ?>" alt="" class="object-cover w-full aspect-[3/1] bg-header" />
|
||||
<img src="<?= $podcast->banner->small_url ?>" alt="" class="w-full aspect-[3/1] bg-header" />
|
||||
<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-background-elevated aspect-square" />
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
? '?_from=' .
|
||||
parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)
|
||||
: '') ?>
|
||||
<source src="<?= $source ?>" type="<?= $episode->audio->file_content_type ?>" />
|
||||
<source src="<?= $source ?>" type="<?= $episode->audio->file_mimetype ?>" />
|
||||
</vm-audio>
|
||||
<vm-ui>
|
||||
<vm-icon-library name="castopod-icons"></vm-icon-library>
|
||||
|
|
|
@ -115,7 +115,7 @@
|
|||
title="<?= $episode->title ?>"
|
||||
podcast="<?= $episode->podcast->title ?>"
|
||||
src="<?= $episode->audio_file_web_url ?>"
|
||||
mediaType="<?= $episode->audio->file_content_type ?>"
|
||||
mediaType="<?= $episode->audio->file_mimetype ?>"
|
||||
playLabel="<?= lang('Common.play_episode_button.play') ?>"
|
||||
playingLabel="<?= lang('Common.play_episode_button.playing') ?>"></play-episode-button>
|
||||
<div class="text-xs">
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
title="<?= $episode->title ?>"
|
||||
podcast="<?= $episode->podcast->title ?>"
|
||||
src="<?= $episode->audio_file_web_url ?>"
|
||||
mediaType="<?= $episode->audio->file_content_type ?>"
|
||||
mediaType="<?= $episode->audio->file_mimetype ?>"
|
||||
playLabel="<?= lang('Common.play_episode_button.play') ?>"
|
||||
playingLabel="<?= lang('Common.play_episode_button.playing') ?>"></play-episode-button>
|
||||
</div>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
title="<?= $episode->title ?>"
|
||||
podcast="<?= $episode->podcast->title ?>"
|
||||
src="<?= $episode->audio_file_web_url ?>"
|
||||
mediaType="<?= $episode->audio->file_content_type ?>"
|
||||
mediaType="<?= $episode->audio->file_mimetype ?>"
|
||||
playLabel="<?= lang('Common.play_episode_button.play') ?>"
|
||||
playingLabel="<?= lang('Common.play_episode_button.playing') ?>"></play-episode-button>
|
||||
</div>
|
|
@ -38,7 +38,7 @@
|
|||
'Fediverse.follow.subtitle',
|
||||
) ?></h1>
|
||||
<div class="flex flex-col w-full max-w-xs -mt-24 overflow-hidden shadow bg-elevated rounded-xl">
|
||||
<img src="<?= $actor->podcast->banner->small_url ?>" alt="" class="object-cover w-full aspect-[3/1] bg-header" />
|
||||
<img src="<?= $actor->podcast->banner->small_url ?>" alt="" class="w-full aspect-[3/1] bg-header" />
|
||||
<div class="flex px-4 py-2">
|
||||
<img src="<?= $actor->avatar_image_url ?>" alt="<?= $actor->display_name ?>"
|
||||
class="w-16 h-16 mr-4 -mt-8 rounded-full ring-2 ring-background-elevated aspect-square" />
|
||||
|
|
Loading…
Reference in New Issue