Skip to content
Snippets Groups Projects
EpisodeModel.php 8.99 KiB
Newer Older
  • Learn to ignore specific revisions
  • /**
     * @copyright  2020 Podlibre
     * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
     * @link       https://castopod.org/
     */
    
    use CodeIgniter\Model;
    
    class EpisodeModel extends Model
    {
    
         * @var array<string, array<string, string>>
         */
        public static $themes = [
            'light-transparent' => [
                'style' =>
                    'background-color: #fff; background-image: linear-gradient(45deg, #ccc 12.5%, transparent 12.5%, transparent 50%, #ccc 50%, #ccc 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
                'background' => 'transparent',
                'text' => '#000',
                'inverted' => '#fff',
            ],
            'light' => [
                'style' => 'background-color: #fff;',
                'background' => '#fff',
                'text' => '#000',
                'inverted' => '#fff',
            ],
            'dark-transparent' => [
                'style' =>
                    'background-color: #001f1a; background-image: linear-gradient(45deg, #888 12.5%, transparent 12.5%, transparent 50%, #888 50%, #888 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
                'background' => 'transparent',
                'text' => '#fff',
                'inverted' => '#000',
            ],
            'dark' => [
                'style' => 'background-color: #001f1a;',
                'background' => '#001f1a',
                'text' => '#fff',
                'inverted' => '#000',
            ],
        ];
    
            'audio_file_path',
            'audio_file_duration',
            'audio_file_mimetype',
            'audio_file_size',
            'audio_file_header_size',
    
            'description_markdown',
            'description_html',
    
            'transcript_file_path',
            'transcript_file_remote_url',
            'chapters_file_path',
            'chapters_file_remote_url',
    
            'location_name',
            'location_geo',
    
            'favourites_total',
            'reblogs_total',
            'notes_total',
    
            'published_at',
            'created_by',
            'updated_by',
    
        protected $useSoftDeletes = true;
    
        protected $validationRules = [
            'podcast_id' => 'required',
            'title' => 'required',
            'slug' => 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]',
    
            'audio_file_path' => 'required',
    
            'description_markdown' => 'required',
    
            'number' => 'is_natural_no_zero|permit_empty',
            'season_number' => 'is_natural_no_zero|permit_empty',
    
            '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',
    
        protected $afterInsert = ['writeEnclosureMetadata', 'clearCache'];
    
        protected $afterUpdate = ['clearCache', 'writeEnclosureMetadata'];
    
        public function getEpisodeBySlug(string $podcastName, string $episodeSlug): ?Episode
    
            $cacheName = "podcast-{$podcastName}_episode-{$episodeSlug}";
    
                $found = $this->select('episodes.*')
                    ->join('podcasts', 'podcasts.id = episodes.podcast_id')
    
                    ->where('podcasts.name', $podcastName)
                    ->where('`published_at` <= NOW()', null, false)
                    ->first();
    
                cache()
                    ->save($cacheName, $found, DECADE);
    
        public function getEpisodeById(int $episodeId): ?Episode
    
            // TODO: episode id should be a composite key. The cache should include podcast_id.
    
            $cacheName = "podcast_episode#{$episodeId}";
    
                cache()
                    ->save($cacheName, $found, DECADE);
    
        public function getPublishedEpisodeById(int $podcastId, int $episodeId): ?Episode
    
            $cacheName = "podcast#{$podcastId}_episode#{$episodeId}_published";
    
                ])
                    ->where('podcast_id', $podcastId)
                    ->where('`published_at` <= NOW()', null, false)
                    ->first();
    
                cache()
                    ->save($cacheName, $found, DECADE);
    
         * Gets all episodes for a podcast ordered according to podcast type Filtered depending on year or season
    
        public function getPodcastEpisodes(
            int $podcastId,
            string $podcastType,
            string $year = null,
            string $season = null
        ): array {
            $cacheName = implode(
                '_',
    
                array_filter(["podcast#{$podcastId}", $year, $season ? 'season' . $season : null, 'episodes']),
    
                if ($year) {
                    $where['YEAR(published_at)'] = $year;
                    $where['season_number'] = null;
                }
                if ($season) {
                    $where['season_number'] = $season;
                }
    
    
                    // podcast is serial
                    $found = $this->where($where)
    
                        ->orderBy('season_number DESC, number ASC')
                        ->findAll();
                } else {
                    $found = $this->where($where)
    
                        ->orderBy('published_at', 'DESC')
                        ->findAll();
                }
    
    
                $secondsToNextUnpublishedEpisode = $this->getSecondsToNextUnpublishedEpisode($podcastId);
    
                cache()
                    ->save(
                        $cacheName,
                        $found,
                        $secondsToNextUnpublishedEpisode
    
         * Returns the timestamp difference in seconds between the next episode to publish and the current timestamp Returns
         * false if there's no episode to publish
    
        public function getSecondsToNextUnpublishedEpisode(int $podcastId): int | false
    
            $result = $this->select('TIMESTAMPDIFF(SECOND, NOW(), `published_at`) as timestamp_diff',)
    
                ->where([
                    'podcast_id' => $podcastId,
                ])
                ->where('`published_at` > NOW()', null, false)
                ->orderBy('published_at', 'asc')
                ->get()
                ->getResultArray();
    
    
                ? (int) $result[0]['timestamp_diff']
                : false;
    
        public function clearCache(array $data): array
    
            $episode = (new self())->find(is_array($data['id']) ? $data['id'][0] : $data['id']);
    
                ->deleteMatching("podcast#{$episode->podcast_id}*");
    
                ->deleteMatching("podcast-{$episode->podcast->name}*");
    
            cache()
                ->delete("podcast_episode#{$episode->id}");
            cache()
    
                ->deleteMatching("page_podcast#{$episode->podcast_id}*");
    
            cache()
                ->deleteMatching('page_credits_*');
    
         * @return array<string, array<string|int, mixed>>
         */
        protected function writeEnclosureMetadata(array $data): array
        {
            helper('id3');
    
    
            $episode = (new self())->find(is_array($data['id']) ? $data['id'][0] : $data['id']);