belongsTo(Studios::class, 'studios_id'); } /** * Get the hentai for the episode. */ public function hentai(): BelongsTo { return $this->belongsTo(Hentai::class, 'hentai_id'); } /** * Get the subtitles for the episode. */ public function subtitles(): HasMany { return $this->hasMany(EpisodeSubtitle::class); } /** * Has a Gallery. */ public function gallery(): HasMany { return $this->hasMany(Gallery::class); } /** * Has many Downloads. */ public function downloads(): HasMany { return $this->hasMany(Downloads::class); } /** * Increment View Count. */ public function incrementViewCount(): bool { $this->view_count++; return $this->save(); } /** * Increment Popular Count. */ public function incrementPopularCount(): void { PopularDaily::create(['episode_id' => $this->id]); PopularWeekly::create(['episode_id' => $this->id]); PopularMonthly::create(['episode_id' => $this->id]); } /** * Get cached view count */ public function viewCount(): int { return cache()->remember('episodeViews' . $this->id, 300, fn() => $this->view_count); } /** * Get view count in a human readable way */ public function viewCountFormatted(): string { if ($this->viewCount() < 1000) { return $this->viewCount(); } $units = ['k', 'M']; $index = floor(log($this->viewCount(), 1000)); $shortNumber = $this->viewCount() / pow(1000, $index); return round($shortNumber, 0) . $units[$index - 1]; } /** * Get cached like count */ public function likeCount(): int { return cache()->remember('episodeLikes' . $this->id, 300, fn() => $this->likes->count()); } /** * Get cached comment count */ public function commentCount(): int { return cache()->remember('episodeComments' . $this->id, 300, fn() => $this->comments->count()); } public function getProblematicTags(): string { $problematicTags = ['Gore', 'Scat', 'Horror']; $problematicResults = ''; foreach ($problematicTags as $pTag) { if (! $this->tags->contains('name', $pTag)) { continue; } if (! empty($problematicResults)) { $problematicResults .= ' + '; } $problematicResults .= $pTag; } return $problematicResults; } /** * Check if episode contains loli / shota tag */ public function isLoliOrShota(): bool { $problematicTags = ['Loli', 'Shota']; return Cache::remember( "episode:{$this->id}:has_problematic_tags", now()->addMinutes(1440), fn () => $this->tags->pluck('name')->intersect($problematicTags)->isNotEmpty() ); } /** * If episode has machine translated subtitles */ public function hasAutoTrans(): bool { return cache()->remember('mt' . $this->id, 900, fn() => $this->subtitles()->exists()); } public function is48Fps(): bool { return cache()->remember('48fps' . $this->id, 900, fn() => $this->interpolated); } public function isUHD48Fps(): bool { return cache()->remember('48fpsUHD' . $this->id, 900, fn() => $this->interpolated_uhd); } public function getResolution(): string { if ($this->isUHD48Fps()) { return '4k | 4k 48fps | FHD 48fps'; } return $this->is48Fps() ? '4k | FHD 48fps' : '4k'; } public function userWatched(int $user_id): bool { return cache()->remember('user' . $user_id . 'watched' . $this->id, 300, fn() => Watched::where('user_id', $user_id)->where('episode_id', $this->id)->exists()); } public function watched(): HasMany { return $this->hasMany(Watched::class); } public function getDownloadByType(string $type): Downloads | null { $cacheKey = "episode_{$this->id}_download_{$type}"; return Cache::remember($cacheKey, now()->addMinutes(10), function () use ($type) { return $this->downloads()->where('type', $type)->first(); }); } public function toSitemapTag(): Url | string | array { return Url::create(route('hentai.index', $this->slug)) ->setLastModificationDate(Carbon::create($this->created_at)); } }