diff --git a/app/Models/Episode.php b/app/Models/Episode.php new file mode 100644 index 0000000..306f130 --- /dev/null +++ b/app/Models/Episode.php @@ -0,0 +1,30 @@ +showsApi = $showsApi; + } public function process($item, $job) { - // TODO: Implement process() method. + $methodName = "get".ucfirst($job); + + if (!method_exists($this, $methodName)) { + // @todo maybe raise an exception here + return; + } + + $this->$methodName($item); } + private function getDetails($item) + { + $tmdbShow = $this->showsApi->getTvshow($item->tmdb_id); + $tmdbShow['tmdb_id'] = $tmdbShow['id']; + $show = Show::where('tmdb_id', $item->tmdb_id)->first(); + + if ($show) { + return; + } + + $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; + + $show = Show::create($tmdbShow); + + if (!empty($tmdbShow['seasons'])) { + $this->processSeasons($show, $tmdbShow['seasons']); + } + + // @todo remove jobs from db + } + + private function getAlternativeTitles() + { + + } + + private function processSeasons(Show $show, array $seasons) + { + foreach ($seasons as $season) { + $showSeason = Season::where('tmdb_id', $season['id']) + ->where('show_id', $show->id) + ->first(); + + if (!$showSeason) { + $showSeason = Season::create($season); + + $tmdbSeasonDetails = $this->showsApi->getSeasonDetails($show->tmdb_id, $season['season_number']); + + $this->processEpisodes($show, $showSeason, $tmdbSeasonDetails['episodes'] ?? []); + } + } + } + + private function processEpisodes($show, $season, array $tmdbEpisodes) + { + if (empty($tmdbEpisodes)) { + return; + } + + foreach ($tmdbEpisodes as $tmdbEpisode) { + $episode = Episode::where('show_id', $show->id) + ->where('season_id', $season->id) + ->where('tmdb_id', $tmdbEpisode['id']) + ->first(); + + if (!$episode) { + $tmdbEpisode['tmdb_id'] = $tmdbEpisode['id']; + $tmdbEpisode['slug'] = Str::slug($tmdbEpisode['name']); + $tmdbEpisode['tmdb_show_id'] = $tmdbEpisode['show_id']; + $tmdbEpisode['show_id'] = $show->id; + $tmdbEpisode['tmdb_id'] = $season->id; + + Episode::create($tmdbEpisode); + } + } + } } diff --git a/app/Service/TMDBApi/ShowsApi.php b/app/Service/TMDBApi/ShowsApi.php new file mode 100644 index 0000000..d6be424 --- /dev/null +++ b/app/Service/TMDBApi/ShowsApi.php @@ -0,0 +1,299 @@ +get('tv/' . $tvshow_id, $parameters, $headers); + } + + /** + * Get the cast & crew information about a TV series. + * Just like the website, we pull this information from the last season of the series. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getCredits($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/credits', $parameters, $headers); + } + + /** + * Get the content ratings for a specific TV show id. + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getContentRatings($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/content_ratings', $parameters, $headers); + } + + /** + * Get the external ids that we have stored for a TV series. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getExternalIds($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/external_ids', $parameters, $headers); + } + + /** + * Get the images (posters and backdrops) for a TV series. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getImages($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/images', $parameters, $headers); + } + + /** + * Get the list of popular TV shows. This list refreshes every day. + * + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getPopular(array $parameters = [], array $headers = []) + { + return $this->get('tv/popular', $parameters, $headers); + } + + /** + * Get the list of top rated TV shows. + * + * By default, this list will only include TV shows that have 2 or more votes. + * This list refreshes every day. + * + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getTopRated(array $parameters = [], array $headers = []) + { + return $this->get('tv/top_rated', $parameters, $headers); + } + + /** + * Get the list of translations that exist for a TV series. + * + * These translations cascade down to the episode level. + * + * @param int $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getTranslations($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/translations', $parameters, $headers); + } + + /** + * Get the list of TV shows that are currently on the air. + * + * This query looks for any TV show that has an episode with an air date in the next 7 days. + * + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getOnTheAir(array $parameters = [], array $headers = []) + { + return $this->get('tv/on_the_air', $parameters, $headers); + } + + /** + * Get the list of TV shows that air today. + * + * Without a specified timezone, this query defaults to EST (Eastern Time UTC-05:00). + * + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getAiringToday(array $parameters = [], array $headers = []) + { + return $this->get('tv/airing_today', $parameters, $headers); + } + + /** + * Get the videos that have been added to a TV series (trailers, opening credits, etc...) + * + * @param int $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getVideos($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/videos', $parameters, $headers); + } + + /** + * Get the watch providers (by region) for a specific movie id. + * + * @param $movie_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getWatchProviders($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/watch/providers', $parameters, $headers); + } + + /** + * Get the changes for a specific TV show id. + * + * Changes are grouped by key, and ordered by date in descending order. + * By default, only the last 24 hours of changes are returned. + * The maximum number of days that can be returned in a single request is 14. + * The language is present on fields that are translatable. + * + * TV changes are different than movie changes in that there are some edits on seasons and episodes + * that will create a change entry at the show level. They can be found under the season and episode keys. + * These keys will contain a series_id and episode_id. + * + * You can use the /tv/season/{id}/changes and /tv/episode/{id}/changes methods to look up these specific changes. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getChanges($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/changes', $parameters, $headers); + } + + /** + * Get the latest TV show id. + * + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getLatest(array $parameters = [], array $headers = []) + { + return $this->get('tv/latest', $parameters, $headers); + } + + /** + * Get the plot keywords for a specific TV show id. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getKeywords($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/keywords', $parameters, $headers); + } + + /** + * Get the similar TV shows for a specific tv id. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getSimilar($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/similar', $parameters, $headers); + } + + /** + * Get the recommended TV shows for a specific tv id. + * + * @param $tvshow_id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getRecommendations($tvshow_id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $tvshow_id . '/recommendations', $parameters, $headers); + } + + /** + * This method lets users get the status of whether or not the TV show has been rated + * or added to their favourite or watch lists. + * + * A valid session id is required. + * + * @param integer $id + * @return mixed + */ + public function getAccountStates($id) + { + return $this->get('tv/' . $id . '/account_states'); + } + + /** + * This method lets users rate a TV show. + * + * A valid session id or guest session id is required. + * + * @param integer $id + * @param double $rating + * @return mixed + */ + public function rateTvShow($id, $rating) + { + return $this->postJson('tv/' . $id . '/rating', ['value' => (float)$rating]); + } + + /** + * Get the alternative titles for a specific show ID. + * + * @param integer $id + * @param array $parameters + * @param array $headers + * @return mixed + */ + public function getAlternativeTitles($id, array $parameters = [], array $headers = []) + { + return $this->get('tv/' . $id . '/alternative_titles', $parameters, $headers); + } + + /** + * Get the alternative titles for a specific show ID. + * + * @param integer $id + * @return mixed + */ + public function getEpisodeGroups($id) + { + return $this->get('tv/' . $id . '/episode_groups'); + } + + public function getSeasonDetails($seriesTmdbId, $seasonNumber) + { + return $this->get('tv/' . $seriesTmdbId . '/season/'.$seasonNumber); + } +} diff --git a/database/migrations/2025_01_04_003059_create_seasons_table.php b/database/migrations/2025_01_04_003059_create_seasons_table.php new file mode 100644 index 0000000..afb4419 --- /dev/null +++ b/database/migrations/2025_01_04_003059_create_seasons_table.php @@ -0,0 +1,37 @@ +id(); + $table->date('air_date')->nullable(); + $table->integer('episode_count')->nullable(); + $table->bigInteger('tmdb_id'); + $table->string('name')->nullable(); + $table->string('slug')->nullable(); + $table->text('overview')->nullable(); + $table->string('poster_path', 500)->nullable(); + $table->integer('season_number')->nullable(); + $table->decimal('vote_average', 10, 2)->nullable(); + $table->unsignedBigInteger('show_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('seasons'); + } +}; diff --git a/database/migrations/2025_01_04_010206_create_episodes_table.php b/database/migrations/2025_01_04_010206_create_episodes_table.php new file mode 100644 index 0000000..344d9c7 --- /dev/null +++ b/database/migrations/2025_01_04_010206_create_episodes_table.php @@ -0,0 +1,47 @@ +id(); + $table->date('air_date')->nullable(); + $table->integer('episode_number')->nullable(); + $table->string('episode_type')->nullable(); + $table->integer('tmdb_id'); + $table->string('name', 500)->nullable(); + $table->string('slug', 500)->nullable(); + $table->text('overview')->nullable(); + $table->string('production_code')->nullable(); + $table->integer('runtime')->nullable(); + $table->integer('season_number')->nullable(); + $table->show_id('tmdb_show_id')->nullable(); + $table->string('still_path', 500)->nullable(); + $table->decimal('vote_average', 10, 2)->nullable(); + $table->integer('vote_count')->nullable(); + $table->json('crew')->nullable(); + $table->json('guest_stars')->nullable(); + + $table->unsignedBigInteger('show_id'); + $table->unsignedBigInteger('season_id'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('episodes'); + } +}; diff --git a/storage/tv_series_01_03_2025.json.gz b/storage/tv_series_01_03_2025.json.gz new file mode 100644 index 0000000..1600efd Binary files /dev/null and b/storage/tv_series_01_03_2025.json.gz differ