refactor: split tmdb recommendations into movies and tv

This commit is contained in:
Roardom
2025-04-07 15:51:15 +00:00
parent 3097949ad1
commit e3cbfd7d7f
10 changed files with 107 additions and 188 deletions
+1 -2
View File
@@ -22,7 +22,6 @@ use App\Models\TmdbCredit;
use App\Models\TmdbGenre;
use App\Models\TmdbMovie;
use App\Models\TmdbPerson;
use App\Models\TmdbRecommendation;
use App\Models\Torrent;
use App\Services\Tmdb\Client;
use Illuminate\Bus\Queueable;
@@ -108,7 +107,7 @@ class ProcessMovieJob implements ShouldQueue
// Recommendations
TmdbRecommendation::upsert($movieScraper->getRecommendations(), ['recommended_tmdb_movie_id', 'tmdb_movie_id']);
$movie->recommendedMovies()->sync(array_unique(array_column($movieScraper->getRecommendations(), 'recommended_tmdb_movie_id')));
Torrent::query()
->where('tmdb_movie_id', '=', $this->id)
+1 -2
View File
@@ -21,7 +21,6 @@ use App\Models\TmdbCredit;
use App\Models\TmdbGenre;
use App\Models\TmdbNetwork;
use App\Models\TmdbPerson;
use App\Models\TmdbRecommendation;
use App\Models\Torrent;
use App\Models\TmdbTv;
use App\Services\Tmdb\Client;
@@ -110,7 +109,7 @@ class ProcessTvJob implements ShouldQueue
// Recommendations
TmdbRecommendation::upsert($tvScraper->getRecommendations(), ['recommended_tmdb_tv_id', 'tmdb_tv_id']);
$tv->recommendedTv()->sync(array_unique(array_column($tvScraper->getRecommendations(), 'recommended_tmdb_tv_id')));
Torrent::query()
->where('tmdb_tv_id', '=', $this->id)
+1 -9
View File
@@ -116,20 +116,12 @@ class TmdbMovie extends Model
return $this->belongsToMany(TmdbCollection::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany<TmdbRecommendation, $this>
*/
public function recommendations(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(TmdbRecommendation::class, 'tmdb_movie_id', 'id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<TmdbMovie, $this>
*/
public function recommendedMovies(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(__CLASS__, TmdbRecommendation::class, 'tmdb_movie_id', 'recommended_tmdb_movie_id', 'id', 'id');
return $this->belongsToMany(__CLASS__, 'tmdb_recommended_movies', 'tmdb_movie_id', 'recommended_tmdb_movie_id', 'id', 'id');
}
/**
-60
View File
@@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* App\Models\TmdbRecommendation.
*
* @property int $id
* @property string $title
* @property string|null $poster
* @property string|null $vote_average
* @property string|null $release_date
* @property string|null $first_air_date
* @property int|null $tmdb_movie_id
* @property int|null $recommended_tmdb_movie_id
* @property int|null $tmdb_tv_id
* @property int|null $recommended_tmdb_tv_id
*/
class TmdbRecommendation extends Model
{
/** @use HasFactory<\Database\Factories\TmdbRecommendationFactory> */
use HasFactory;
protected $guarded = [];
public $timestamps = false;
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<TmdbMovie, $this>
*/
public function movie(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(TmdbMovie::class, 'tmdb_movie_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<TmdbTv, $this>
*/
public function tv(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(TmdbTv::class, 'tmdb_tv_id');
}
}
+1 -9
View File
@@ -137,20 +137,12 @@ class TmdbTv extends Model
return $this->belongsToMany(TmdbCompany::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany<TmdbRecommendation, $this>
*/
public function recommendations(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(TmdbRecommendation::class, 'tmdb_tv_id', 'id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<TmdbTv, $this>
*/
public function recommendedTv(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(__CLASS__, TmdbRecommendation::class, 'tmdb_tv_id', 'recommended_tmdb_tv_id', 'id', 'id');
return $this->belongsToMany(__CLASS__, 'tmdb_recommended_tv', 'tmdb_tv_id', 'recommended_tmdb_tv_id', 'id', 'id');
}
/**
+6 -17
View File
@@ -390,17 +390,10 @@ class Movie
}
/**
* @return array<
* int<0, max>,
* array{
* recommended_tmdb_movie_id: ?int,
* tmdb_movie_id: ?int,
* title: ?string,
* vote_average: ?float,
* poster: ?string,
* release_date: ?string,
* }
* >
* @return list<array{
* tmdb_movie_id: ?int,
* recommended_tmdb_movie_id: ?int,
* }>
*/
public function getRecommendations(): array
{
@@ -412,18 +405,14 @@ class Movie
$recommendations = [];
foreach ($this->data['recommendations']['results'] ?? [] as $recommendation) {
if ($recommendation === null || $recommendation['id'] === null) {
if ($recommendation === null || $recommendation['id'] === null || $this->data['id'] === null) {
continue;
}
if ($movie_ids->contains($recommendation['id'])) {
$recommendations[] = [
'tmdb_movie_id' => $this->data['id'],
'recommended_tmdb_movie_id' => $recommendation['id'],
'tmdb_movie_id' => $this->data['id'] ?? null,
'title' => $recommendation['title'] ?? null,
'vote_average' => $recommendation['vote_average'] ?? null,
'poster' => $this->tmdb->image('poster', $recommendation),
'release_date' => $recommendation['release_date'] ?? null,
];
}
}
+6 -17
View File
@@ -452,17 +452,10 @@ class TV
}
/**
* @return array<
* int<0, max>,
* array{
* recommended_tmdb_tv_id: ?int,
* tmdb_tv_id: ?int,
* title: ?string,
* vote_average: ?float,
* poster: ?string,
* first_air_date: ?string,
* }
* >
* @return list<array{
* tmdb_tv_id: ?int,
* recommended_tmdb_tv_id: ?int,
* }>
*/
public function getRecommendations(): array
{
@@ -474,18 +467,14 @@ class TV
$recommendations = [];
foreach ($this->data['recommendations']['results'] ?? [] as $recommendation) {
if ($recommendation === null || $recommendation['id'] === null) {
if ($recommendation === null || $recommendation['id'] === null || $this->data['id'] === null) {
continue;
}
if ($tv_ids->contains($recommendation['id'])) {
$recommendations[] = [
'recommended_tmdb_tv_id' => $recommendation['id'],
'tmdb_tv_id' => $this->data['id'],
'title' => $recommendation['name'],
'vote_average' => $recommendation['vote_average'],
'poster' => $this->tmdb->image('poster', $recommendation),
'first_air_date' => $recommendation['first_air_date'],
'recommended_tmdb_tv_id' => $recommendation['id'],
];
}
}
@@ -1,49 +0,0 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace Database\Factories;
use App\Models\TmdbMovie;
use App\Models\TmdbTv;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\TmdbRecommendation;
/** @extends Factory<TmdbRecommendation> */
class TmdbRecommendationFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*/
protected $model = TmdbRecommendation::class;
/**
* Define the model's default state.
*/
public function definition(): array
{
return [
'title' => $this->faker->sentence(),
'poster' => $this->faker->word(),
'vote_average' => $this->faker->word(),
'release_date' => $this->faker->date(),
'first_air_date' => $this->faker->date(),
'tmdb_movie_id' => TmdbMovie::factory(),
'recommended_tmdb_movie_id' => $this->faker->unique()->randomDigitNotNull(),
'tmdb_tv_id' => TmdbTv::factory(),
'recommended_tmdb_tv_id' => $this->faker->unique()->randomDigitNotNull(),
];
}
}
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tmdb_recommended_movies', function (Blueprint $table): void {
$table->unsignedInteger('tmdb_movie_id');
$table->unsignedInteger('recommended_tmdb_movie_id');
$table->primary(['tmdb_movie_id', 'recommended_tmdb_movie_id']);
$table->foreign('tmdb_movie_id')->references('id')->on('tmdb_movies')->cascadeOnUpdate()->cascadeOnDelete();
$table->foreign('recommended_tmdb_movie_id')->references('id')->on('tmdb_movies')->cascadeOnUpdate()->cascadeOnDelete();
});
Schema::create('tmdb_recommended_tv', function (Blueprint $table): void {
$table->unsignedInteger('tmdb_tv_id');
$table->unsignedInteger('recommended_tmdb_tv_id');
$table->primary(['tmdb_tv_id', 'recommended_tmdb_tv_id']);
$table->foreign('tmdb_tv_id')->references('id')->on('tmdb_tv')->cascadeOnUpdate()->cascadeOnDelete();
$table->foreign('recommended_tmdb_tv_id')->references('id')->on('tmdb_tv')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('tmdb_recommended_movies')->insertUsing(
['tmdb_movie_id', 'recommended_tmdb_movie_id'],
DB::table('tmdb_recommendations')
->select([
'tmdb_movie_id',
'recommended_tmdb_movie_id',
])
->whereNotNull('tmdb_movie_id')
->whereNotNull('recommended_tmdb_movie_id'),
);
DB::table('tmdb_recommended_tv')->insertUsing(
['tmdb_tv_id', 'recommended_tmdb_tv_id'],
DB::table('tmdb_recommendations')
->select([
'tmdb_tv_id',
'recommended_tmdb_tv_id',
])
->whereNotNull('tmdb_tv_id')
->whereNotNull('recommended_tmdb_tv_id'),
);
Schema::drop('tmdb_recommendations');
}
};
+21 -23
View File
@@ -1902,31 +1902,28 @@ CREATE TABLE `tmdb_people` (
KEY `person_name_index` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tmdb_recommendations`;
DROP TABLE IF EXISTS `tmdb_recommended_movies`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tmdb_recommendations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`poster` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`vote_average` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`release_date` date DEFAULT NULL,
`first_air_date` date DEFAULT NULL,
`tmdb_movie_id` int unsigned DEFAULT NULL,
`recommended_tmdb_movie_id` int unsigned DEFAULT NULL,
`tmdb_tv_id` int unsigned DEFAULT NULL,
`recommended_tmdb_tv_id` int unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `recommendations_movie_id_recommendation_movie_id_unique` (`tmdb_movie_id`,`recommended_tmdb_movie_id`),
UNIQUE KEY `recommendations_tv_id_recommendation_tv_id_unique` (`tmdb_tv_id`,`recommended_tmdb_tv_id`),
KEY `recommendations_movie_id_index` (`tmdb_movie_id`),
KEY `recommendations_recommendation_movie_id_index` (`recommended_tmdb_movie_id`),
KEY `recommendations_tv_id_index` (`tmdb_tv_id`),
KEY `recommendations_recommendation_tv_id_index` (`recommended_tmdb_tv_id`),
CONSTRAINT `recommendations_movie_id_foreign` FOREIGN KEY (`tmdb_movie_id`) REFERENCES `tmdb_movies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `recommendations_recommendation_movie_id_foreign` FOREIGN KEY (`recommended_tmdb_movie_id`) REFERENCES `tmdb_movies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `recommendations_recommendation_tv_id_foreign` FOREIGN KEY (`recommended_tmdb_tv_id`) REFERENCES `tmdb_tv` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `recommendations_tv_id_foreign` FOREIGN KEY (`tmdb_tv_id`) REFERENCES `tmdb_tv` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
CREATE TABLE `tmdb_recommended_movies` (
`tmdb_movie_id` int unsigned NOT NULL,
`recommended_tmdb_movie_id` int unsigned NOT NULL,
PRIMARY KEY (`tmdb_movie_id`,`recommended_tmdb_movie_id`),
KEY `tmdb_recommended_movies_recommended_tmdb_movie_id_foreign` (`recommended_tmdb_movie_id`),
CONSTRAINT `tmdb_recommended_movies_recommended_tmdb_movie_id_foreign` FOREIGN KEY (`recommended_tmdb_movie_id`) REFERENCES `tmdb_movies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `tmdb_recommended_movies_tmdb_movie_id_foreign` FOREIGN KEY (`tmdb_movie_id`) REFERENCES `tmdb_movies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tmdb_recommended_tv`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tmdb_recommended_tv` (
`tmdb_tv_id` int unsigned NOT NULL,
`recommended_tmdb_tv_id` int unsigned NOT NULL,
PRIMARY KEY (`tmdb_tv_id`,`recommended_tmdb_tv_id`),
KEY `tmdb_recommended_tv_recommended_tmdb_tv_id_foreign` (`recommended_tmdb_tv_id`),
CONSTRAINT `tmdb_recommended_tv_recommended_tmdb_tv_id_foreign` FOREIGN KEY (`recommended_tmdb_tv_id`) REFERENCES `tmdb_tv` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `tmdb_recommended_tv_tmdb_tv_id_foreign` FOREIGN KEY (`tmdb_tv_id`) REFERENCES `tmdb_tv` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tmdb_tv`;
@@ -2942,3 +2939,4 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (342,'2025_03_23_20
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (343,'2025_03_25_093436_update_metadata_id_default_to_null',5);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (344,'2025_03_29_215845_create_playlist_categories',6);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (345,'2025_04_03_085022_drop_season_and_episodes',7);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (346,'2025_04_07_152108_split_recommendations_into_movie_and_tv',8);