diff --git a/app/Models/Episode.php b/app/Models/Episode.php index 306f130..06affef 100644 --- a/app/Models/Episode.php +++ b/app/Models/Episode.php @@ -27,4 +27,9 @@ class Episode extends Model 'show_id', 'season_id', ]; + + protected $casts = [ + 'crew' => 'array', + 'guest_stars' => 'array', + ]; } diff --git a/app/Models/ExternalId.php b/app/Models/ExternalId.php index 5f162eb..b3e4ea6 100644 --- a/app/Models/ExternalId.php +++ b/app/Models/ExternalId.php @@ -18,5 +18,8 @@ class ExternalId extends Model 'facebook_id', 'instagram_id', 'twitter_id', + 'tvdb_id', + 'freebase_id', + 'tvrage_id', ]; } diff --git a/app/Models/Show.php b/app/Models/Show.php index 0d4994e..96b11e2 100644 --- a/app/Models/Show.php +++ b/app/Models/Show.php @@ -45,4 +45,14 @@ class Show extends Model { return $this->belongsToMany(Person::class, 'show_crew', 'show_id', 'person_id'); } + + public function images() + { + return $this->belongsToMany(Image::class, 'show_images', 'show_id', 'image_id'); + } + + public function videos() + { + return $this->belongsToMany(Video::class, 'show_videos', 'show_id', 'video_id'); + } } diff --git a/app/Service/MediaProcessing/ShowProcessingService.php b/app/Service/MediaProcessing/ShowProcessingService.php index d9f529f..3186f13 100644 --- a/app/Service/MediaProcessing/ShowProcessingService.php +++ b/app/Service/MediaProcessing/ShowProcessingService.php @@ -3,11 +3,14 @@ namespace App\Service\MediaProcessing; use App\Models\Episode; +use App\Models\ExternalId; +use App\Models\Image; use App\Models\ItemToFetchJobs; use App\Models\Person; use App\Models\Season; use App\Models\Show; use App\Models\ShowAlternativeTitle; +use App\Models\Video; use App\Service\TMDBApi\PersonApi; use App\Service\TMDBApi\ShowsApi; use Illuminate\Support\Str; @@ -48,10 +51,13 @@ class ShowProcessingService implements ProcessingInterface if ($show) { return; } - +//dd($tmdbShow); $tmdbShow['slug'] = Str::slug($tmdbShow['name'].'-'.$tmdbShow['tmdb_id']); $tmdbShow['first_air_date'] = $tmdbShow['first_air_date'] ? new \DateTime($tmdbShow['first_air_date']) : null; $tmdbShow['last_air_date'] = $tmdbShow['last_air_date'] ? new \DateTime($tmdbShow['last_air_date']) : null; + $tmdbShow['episode_run_time'] = reset($tmdbShow['episode_run_time']); + $tmdbShow['languages'] = json_encode($tmdbShow['languages']); + $tmdbShow['origin_country'] = reset($tmdbShow['origin_country']); $show = Show::create($tmdbShow); @@ -59,6 +65,11 @@ class ShowProcessingService implements ProcessingInterface $this->processSeasons($show, $tmdbShow['seasons']); } + // @todo process genres + // @todo process networks + // @todo process production countries + // @todo process production companies + $this->removeElementFromRemainingJobs($item->tmdb_id, ItemToFetchJobs::JOB_DETAILS); } @@ -106,6 +117,117 @@ class ShowProcessingService implements ProcessingInterface $this->removeElementFromRemainingJobs($item->tmdb_id, ItemToFetchJobs::JOB_CREDITS); } + private function getExternalIds($item) + { + $tmdbExternalIds = $this->showsApi->getExternalIds($item->tmdb_id); + + if (empty($tmdbExternalIds)) { + return; + } + + $show = Show::where('tmdb_id', $item->tmdb_id)->first(); + + if (!$show) { + return; + } + + $externalId = ExternalId::where('entity_id', $show->id) + ->where('entity_type', ExternalId::TYPE_SHOW) + ->first(); + + if (!$externalId) { + $externalIds['tmdb_id'] = $tmdbExternalIds['id']; + //$externalIds['show_id'] = $show->id; + unset($externalIds['id']); + + $externalIds['entity_id'] = $show->id; + $externalIds['entity_type'] = ExternalId::TYPE_SHOW; + + ExternalId::create($externalIds); + } + + $this->removeElementFromRemainingJobs($item->tmdb_id, ItemToFetchJobs::JOB_EXTERNAL_IDS); + } + + private function getImages($item) + { + $tmdbImages = $this->showsApi->getImages($item->tmdb_id); + + if (empty($tmdbImages)) { + return; + } + + $show = Show::where('tmdb_id', $item->tmdb_id)->first(); + + foreach ($tmdbImages as $type => $images) { + if (!is_array($images)) { + continue; + } + foreach ($images as $image) { + $imageExists = Image::where('type', $type) + ->where('file_path', $image['file_path']) + ->first(); + + if (!$imageExists) { + $image['iso'] = $image['iso_639_1']; + $image['type'] = $type; + $imageExists = Image::create($image); + } + + if ($show->whereHas('images', function ($query) use ($imageExists) { + $query->where('type', $imageExists->type) + ->where('file_path', $imageExists->file_path); + })->exists()) { + return; + } else { + $show->images()->attach($imageExists); + } + } + } + + $this->removeElementFromRemainingJobs($item->tmdb_id, ItemToFetchJobs::JOB_IMAGES); + } + + private function getVideos($item) + { + $show = Show::where('tmdb_id', $item->tmdb_id)->first(); + + $tmdbVideos = $this->showsApi->getVideos($item->tmdb_id); + + if (empty($tmdbVideos) || empty($tmdbVideos['results'])) { + return; + } + + foreach ($tmdbVideos['results'] as $tmdbVideo) { + $videoExists = Video::where('tmdb_id', $tmdbVideo['id'])->first(); + + if (!$videoExists) { + $videoExists = Video::create([ + 'iso_639' => $tmdbVideo['iso_639_1'], + 'iso_3166' => $tmdbVideo['iso_3166_1'], + 'name' => $tmdbVideo['name'], + 'key' => $tmdbVideo['key'], + 'site' => $tmdbVideo['site'], + 'size' => $tmdbVideo['size'], + 'type' => $tmdbVideo['type'], + 'official' => $tmdbVideo['official'], + 'published_at' => $tmdbVideo['published_at'] ? new \DateTime($tmdbVideo['published_at']) : null, + 'tmdb_id' => $tmdbVideo['id'], + ]); + } + + if ($show->whereHas('videos', function ($query) use ($show) { + $query->where('show_id', $show->id); + })->exists()) { + return; + } else { + $show->videos()->attach($videoExists); + } + } + + $this->removeElementFromRemainingJobs($item->tmdb_id, ItemToFetchJobs::JOB_VIDEOS); + } + private function processSeasons(Show $show, array $seasons) { foreach ($seasons as $season) { @@ -114,6 +236,7 @@ class ShowProcessingService implements ProcessingInterface ->first(); if (!$showSeason) { + $season['show_id'] = $show->id; $showSeason = Season::create($season); $tmdbSeasonDetails = $this->showsApi->getSeasonDetails($show->tmdb_id, $season['season_number']); @@ -139,6 +262,11 @@ class ShowProcessingService implements ProcessingInterface $tmdbEpisode['tmdb_id'] = $tmdbEpisode['id']; $tmdbEpisode['slug'] = Str::slug($tmdbEpisode['name']); $tmdbEpisode['tmdb_show_id'] = $tmdbEpisode['show_id']; + $tmdbEpisode['season_id'] = $season->id; +// $tmdbEpisode['crew'] = $tmdbEpisode['crew']; +// $tmdbEpisode['guest_stars'] = $tmdbEpisode['guest_stars']; + + $tmdbEpisode['show_id'] = $show->id; $tmdbEpisode['tmdb_id'] = $season->id; diff --git a/database/migrations/2024_12_30_010725_create_external_ids_table.php b/database/migrations/2024_12_30_010725_create_external_ids_table.php index 8ac7aee..5a9c823 100644 --- a/database/migrations/2024_12_30_010725_create_external_ids_table.php +++ b/database/migrations/2024_12_30_010725_create_external_ids_table.php @@ -21,6 +21,9 @@ return new class extends Migration $table->string('facebook_id')->nullable(); $table->string('instagram_id')->nullable(); $table->string('twitter_id')->nullable(); + $table->string('tvdb_id')->nullable(); + $table->string('freebase_id')->nullable(); + $table->string('tvrage_id')->nullable(); $table->timestamps(); }); } diff --git a/database/migrations/2025_01_04_003059_create_seasons_table.php b/database/migrations/2025_01_04_003059_create_seasons_table.php index afb4419..523bf9f 100644 --- a/database/migrations/2025_01_04_003059_create_seasons_table.php +++ b/database/migrations/2025_01_04_003059_create_seasons_table.php @@ -15,7 +15,7 @@ return new class extends Migration $table->id(); $table->date('air_date')->nullable(); $table->integer('episode_count')->nullable(); - $table->bigInteger('tmdb_id'); + $table->bigInteger('tmdb_id')->nullable(); $table->string('name')->nullable(); $table->string('slug')->nullable(); $table->text('overview')->nullable(); diff --git a/database/migrations/2025_01_04_010206_create_episodes_table.php b/database/migrations/2025_01_04_010206_create_episodes_table.php index 344d9c7..c4dfa26 100644 --- a/database/migrations/2025_01_04_010206_create_episodes_table.php +++ b/database/migrations/2025_01_04_010206_create_episodes_table.php @@ -23,7 +23,7 @@ return new class extends Migration $table->string('production_code')->nullable(); $table->integer('runtime')->nullable(); $table->integer('season_number')->nullable(); - $table->show_id('tmdb_show_id')->nullable(); + $table->bigInteger('tmdb_show_id')->nullable(); $table->string('still_path', 500)->nullable(); $table->decimal('vote_average', 10, 2)->nullable(); $table->integer('vote_count')->nullable(); diff --git a/database/migrations/2025_01_07_183836_create_show_images_table.php b/database/migrations/2025_01_07_183836_create_show_images_table.php new file mode 100644 index 0000000..6a2e13a --- /dev/null +++ b/database/migrations/2025_01_07_183836_create_show_images_table.php @@ -0,0 +1,29 @@ +id(); + $table->unsignedBigInteger('show_id'); + $table->unsignedBigInteger('image_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('show_images'); + } +}; diff --git a/database/migrations/2025_01_07_213451_create_show_videos_table.php b/database/migrations/2025_01_07_213451_create_show_videos_table.php new file mode 100644 index 0000000..e032fbc --- /dev/null +++ b/database/migrations/2025_01_07_213451_create_show_videos_table.php @@ -0,0 +1,29 @@ +id(); + $table->unsignedBigInteger('show_id'); + $table->unsignedBigInteger('video_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('show_videos'); + } +};