This commit is contained in:
2025-02-10 01:58:12 +02:00
parent 25d99e7b72
commit 87bbfc33d3
242 changed files with 38662 additions and 99 deletions
@@ -6,6 +6,8 @@ use App\Models\ItemToFetch;
use App\Models\ItemToFetchJobs;
use App\Models\Movie;
use App\Service\MediaProcessing\FactoryService;
use App\Service\MediaProcessing\MovieProcessingService;
use App\Service\MediaProcessing\ShowProcessingService;
use Illuminate\Console\Command;
class FetchItemDataCommand extends Command
@@ -15,7 +17,7 @@ class FetchItemDataCommand extends Command
*
* @var string
*/
protected $signature = 'processing:fetch-item-data {type=movie} {batch=50} {job=Details}';
protected $signature = 'processing:fetch-item-data {type=movie} {batch=50} {job=Details} {tmdbId?}';
/**
* The console command description.
@@ -36,42 +38,47 @@ class FetchItemDataCommand extends Command
*/
public function handle()
{
$this->info("Start");
$type = $this->argument('type');
$batch = $this->argument('batch');
$job = $this->argument('job');
$tmdbId = $this->argument('tmdbId');
$items = $this->getBatch($type, $batch, $job);
$items = $this->getBatch($type, $batch, $job, $tmdbId);
$this->processItems($items, $job);
$this->info("Processing ".count($items)." items of type `{$type}`.");
$this->processItems($items, $job, $type);
$this->info("Fin.");
}
private function getBatch(string $type, int $batch, string $job)
private function getBatch(string $type, int $batch, string $job, int $tmdbId = null)
{
// if job <> Details the movie in movie table should exist
$query = ItemToFetch::whereHas('jobs', function($query) use ($job){
return $query->where('jobs', '<>', null)->whereRaw('FIND_IN_SET(?, jobs)', [$job]);
})->with(['jobs']);
if ($job !== ItemToFetchJobs::JOB_DETAILS) {
$query = $query->join('movies', 'movies.tmdb_id', '=', 'items_to_fetch.tmdb_id');
switch ($type){
case ItemToFetch::TYPE_MOVIE:
return MovieProcessingService::getBatch($batch, $job);
case ItemToFetch::TYPE_TV_SERIES:
return ShowProcessingService::getBatch($batch, $job, $tmdbId);
}
return $query
->where('type', $type)
->take($batch)
->get();
}
private function processItems(\Illuminate\Database\Eloquent\Collection $items, $job)
private function processItems(\Illuminate\Database\Eloquent\Collection $items, $job, $type)
{
$bar = $this->output->createProgressBar(count($items));
foreach ($items as $item) {
$this->processItem($item, $job);
$this->processItem($item, $job, $type);
$bar->advance();
}
$bar->finish();
}
private function processItem($item, $job)
private function processItem($item, $job, $type)
{
$this->factoryService->process($item, $job);
$this->factoryService->process($item, $job, $type);
}
@@ -76,6 +76,7 @@ class RegisterController extends Controller
$this->createMediaList('Collection', $user);
$this->createMediaList('Watched', $user);
$this->createMediaList('Liked', $user);
$this->createMediaList('Favourite', $user);
return $user;
}
+28 -1
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Person;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class CelebrityController extends Controller
{
@@ -18,6 +19,32 @@ class CelebrityController extends Controller
{
$celebrity = Person::where('slug', $slug)->first();
return view('celebrity.show', compact('celebrity'));
// movies by job
$movies = DB::table('movies')
->leftJoin('movie_cast', 'movie_cast.movie_id', '=', 'movies.id')
->where('movie_cast.person_id', '=', $celebrity->id)
->get();
// movies crew
$moviesByDepartments = [];
foreach ($movies as $movie) {
$moviesByDepartments[$movie->known_for_department][]= $movie;
}
$showsByDepartment = [];
$shows = DB::table('shows')
->leftJoin('show_cast', 'show_cast.show_id', '=', 'shows.id')
->where('show_cast.person_id', '=', $celebrity->id)
->get();
foreach ($shows as $show) {
$showsByDepartment[$show->known_for_department][]= $show;
}
return view('celebrity.show', compact('celebrity', 'moviesByDepartments', 'showsByDepartment'));
}
}
@@ -3,12 +3,22 @@
namespace App\Http\Controllers\Forum;
use App\Http\Controllers\Controller;
use App\Models\Forum\Category;
use App\Models\Forum\Thread;
use App\Models\Forum\Topic;
use Illuminate\Http\Request;
class ThreadsController extends Controller
{
public function show(string $threadSlug)
public function show(string $categorySlug, string $threadSlug)
{
$category = Category::where('slug', $categorySlug)->first();
$thread = Thread::where('slug', $threadSlug)->first();
$topics = Topic::where('category_id', $category->id)
->where('thread_id', $thread->id)
->paginate(15);
return view('forum.thread', compact('category', 'thread', 'topics'));
}
}
@@ -3,9 +3,13 @@
namespace App\Http\Controllers\Forum;
use App\Http\Controllers\Controller;
use App\Http\Requests\Forum\TopicRequest;
use Illuminate\Http\Request;
class TopicsController extends Controller
{
//
public function create(TopicRequest $request)
{
return view('forum.topics.create');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers;
use App\Models\Show;
use Illuminate\Http\Request;
class ShowController extends Controller
{
public function index()
{
$shows = Show::paginate(48);
return view('show.index', compact('shows'));
}
public function show(string $slug)
{
$show = Show::with('posters', 'backdrops', 'externalIds', 'networks', 'genres', 'productionCompanies')->where('slug', $slug)->first();
$comments = [];
return view('show.show', compact('show', 'comments'));
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests\Forum;
use Illuminate\Foundation\Http\FormRequest;
class TopicRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Livewire\ShowPage;
use App\Models\Show;
use App\Models\ShowRecommendation;
use Livewire\Component;
class Recommended extends Component
{
public $show;
public $recommendedShows;
public function render()
{
return view('livewire.show-page.recommended');
}
public function mount()
{
$this->recommendedShows = Show::whereIn('id', ShowRecommendation::where('show_id', $this->show->id)->pluck('recommended_show_id'))
->get();
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Livewire\ShowPage;
use App\Models\Show;
use App\Models\ShowSimilar;
use Livewire\Component;
class Similar extends Component
{
public $show;
public $similarShows;
public function render()
{
return view('livewire.show-page.similar');
}
public function mount()
{
$this->similarShows = Show::whereIn('id', ShowSimilar::where('show_id', $this->show->id)->pluck('similar_show_id'))
->get();
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Livewire\ShowPage\Tabs;
use App\Models\Person;
use App\Models\Show;
use Livewire\Component;
class Cast extends Component
{
public $show;
public $peopleByDepartment;
public function render()
{
return view('livewire.show-page.tabs.cast');
}
public function mount()
{
$this->show = Show::with('castList')->where('id', $this->show->id)->first();
$peopleByDepartment = [];
foreach ($this->show->castList as $castMember) {
if (!isset($peopleByDepartment[$castMember->pivot->known_for_department])) {
$peopleByDepartment[$castMember->pivot->known_for_department] = [];
}
$peopleByDepartment[$castMember->pivot->known_for_department][] = [
Person::find($castMember->pivot->person_id),
$castMember->pivot->character,
];
}
$this->peopleByDepartment = $peopleByDepartment;
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Livewire\ShowPage\Tabs;
use App\Models\Person;
use App\Models\Show;
use Livewire\Component;
class Crew extends Component
{
public $show;
public $peopleByDepartment;
public function render()
{
return view('livewire.show-page.tabs.crew');
}
public function mount()
{
$this->show = Show::with('castList')->where('id', $this->show->id)->first();
$peopleByDepartment = [];
foreach ($this->show->crewList as $castMember) {
if (!isset($peopleByDepartment[$castMember->pivot->department])) {
$peopleByDepartment[$castMember->pivot->department] = [];
}
$peopleByDepartment[$castMember->pivot->department][] = [
Person::find($castMember->pivot->person_id),
$castMember->pivot->job,
];
}
$this->peopleByDepartment = $peopleByDepartment;
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace App\Livewire\ShowPage\Tabs;
use Livewire\Component;
class Details extends Component
{
public $show;
public function render()
{
return view('livewire.show-page.tabs.details');
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Livewire\ShowPage\Tabs;
use Livewire\Component;
class Releases extends Component
{
public function render()
{
return view('livewire.show-page.tabs.releases');
}
}
+10 -1
View File
@@ -6,5 +6,14 @@ use Illuminate\Database\Eloquent\Model;
class Topic extends Model
{
//
protected $table = 'forum_topics';
protected $fillable = [
'name',
'slug',
'status',
'category_id',
'thread_id',
'user_id',
];
}
+3
View File
@@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
public const TYPE_POSTER = 'posters';
public const TYPE_BACKDROP = 'backdrops';
protected $fillable = [
'aspect_ratio',
'height',
-8
View File
@@ -75,14 +75,6 @@ class ItemToFetchJobs extends Model
'tmdb_id',
'item_id',
'type',
'remaining_jobs',
'jobs',
];
protected function casts(): array
{
return [
'remaining_jobs' => 'array',
];
}
}
+58 -2
View File
@@ -36,6 +36,44 @@ class Show extends Model
'vote_count',
];
protected $casts = [
'first_air_date' => 'date',
'last_air_date' => 'date',
'languages' => 'array',
];
public function wasListedBy(string $listSlug, User $user)
{
$mediaList = MediaList::where('slug', $listSlug)->where('user_id', $user->id)->first();
if ($mediaList) {
$mediaListItem = MediaListItem::where('media_list_id', $mediaList->id)
->where('item_id', $this->id)
->where('item_type', 'show')
->first();
return !empty($mediaListItem);
}
return false;
}
public function ratingBy(User $user)
{
$rating = Rate::where('user_id', $user->id)
->where('item_id', $this->id)
->where('item_type', 'show')
->first();
return $rating ? $rating->rate : null;
}
public function externalIds()
{
return $this->hasOne(ExternalId::class, 'entity_id', 'id')
->where('external_ids.entity_type', 'show');
}
public function castList()
{
return $this->belongsToMany(Person::class, 'show_cast', 'show_id', 'person_id');
@@ -46,9 +84,27 @@ class Show extends Model
return $this->belongsToMany(Person::class, 'show_crew', 'show_id', 'person_id');
}
public function images()
public function images(string $type = 'posters')
{
return $this->belongsToMany(Image::class, 'show_images', 'show_id', 'image_id');
if ($type === Image::TYPE_POSTER) {
return $this->belongsToMany(Image::class, 'show_images', 'show_id', 'image_id')
->where('type', Image::TYPE_POSTER);
}
return $this->belongsToMany(Image::class, 'show_images', 'show_id', 'image_id')
->where('type', '=', Image::TYPE_BACKDROP);
}
public function posters()
{
return $this->belongsToMany(Image::class, 'show_images', 'show_id', 'image_id')
->where('type', Image::TYPE_POSTER);
}
public function backdrops()
{
return $this->belongsToMany(Image::class, 'show_images', 'show_id', 'image_id')
->where('type', '=', Image::TYPE_BACKDROP);
}
public function videos()
@@ -17,9 +17,9 @@ class FactoryService
$this->showProcessingService = $showProcessingService;
}
public function process($item, string $job)
public function process($item, string $job, $type)
{
switch ($item->type) {
switch ($type) {
case ItemToFetch::TYPE_MOVIE:
return $this->movieProcessingService->process($item, $job);
case ItemToFetch::TYPE_TV_SERIES:
@@ -46,6 +46,29 @@ class MovieProcessingService implements ProcessingInterface
$this->listApi = $listApi;
}
public static function getBatch(int $batch, string $job)
{
if ($job === ItemToFetchJobs::JOB_DETAILS) {
return ItemToFetchJobs::leftJoin('movies', function ($join) {
$join->on('movies.tmdb_id', '=', 'item_to_fetch_jobs.tmdb_id');
})
->whereRaw("FIND_IN_SET('".ItemToFetchJobs::JOB_DETAILS."', item_to_fetch_jobs.jobs)")
->where('item_to_fetch_jobs.type', 'movies')
->whereNull('movies.id')
->take($batch)
->get();
}
return ItemToFetchJobs::leftJoin('movies', function ($join) {
$join->on('movies.tmdb_id', '=', 'item_to_fetch_jobs.tmdb_id');
})
->whereRaw("FIND_IN_SET('".$job."', item_to_fetch_jobs.jobs)")
->where('item_to_fetch_jobs.type', 'tv_series')
->whereNotNull('movies.id')
->take($batch)
->get();
}
public function process($item, $job)
{
$methodName = "get".ucfirst($job);
@@ -26,6 +26,7 @@ use App\Models\Video;
use App\Models\WatchProvider;
use App\Service\TMDBApi\PersonApi;
use App\Service\TMDBApi\ShowsApi;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use function PHPUnit\Framework\containsIdentical;
@@ -42,13 +43,40 @@ class ShowProcessingService implements ProcessingInterface
$this->personApi = $personApi;
}
public static function getBatch(int $batch, string $job, int $tmdbId = null)
{
$query = ItemToFetchJobs::leftJoin('shows', function ($join) {
$join->on('shows.tmdb_id', '=', 'item_to_fetch_jobs.tmdb_id');
})
->select('item_to_fetch_jobs.*');
if ($tmdbId) {
$query = $query->where('item_to_fetch_jobs.tmdb_id', $tmdbId);
}
if ($job === ItemToFetchJobs::JOB_DETAILS) {
return $query
->whereRaw("FIND_IN_SET('".ItemToFetchJobs::JOB_DETAILS."', item_to_fetch_jobs.jobs)")
->where('item_to_fetch_jobs.type', 'tv_series')
->whereNull('shows.id')
->take($batch)
->get();
}
return $query
->whereRaw("FIND_IN_SET('".$job."', item_to_fetch_jobs.jobs)")
->where('item_to_fetch_jobs.type', 'tv_series')
->whereNotNull('shows.id')
->take($batch)
->get();
}
public function process($item, $job)
{
$methodName = "get".ucfirst($job);
if (!method_exists($this, $methodName)) {
// @todo maybe raise an exception here
return;
throw new \Exception("No method named $methodName\n");
}
$this->$methodName($item);
@@ -62,10 +90,10 @@ class ShowProcessingService implements ProcessingInterface
$show = Show::where('tmdb_id', $item->tmdb_id)->first();
if ($show) {
echo "+";
// echo "+";
return;
}
echo ".";
// echo ".";
$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;
@@ -140,11 +168,13 @@ class ShowProcessingService implements ProcessingInterface
$tmdbCredits = $this->showsApi->getCredits($item->tmdb_id);
if (empty($tmdbCredits)) {
echo "no credits\n";
return;
}
$show = Show::where('tmdb_id', $item->tmdb_id)->first();
echo $item->tmdb_id."\n";
$this->processCast($show, $tmdbCredits['cast'] ?? []);
$this->processCrew($show, $tmdbCredits['crew'] ?? []);
@@ -177,7 +207,27 @@ class ShowProcessingService implements ProcessingInterface
$externalIds['entity_id'] = $show->id;
$externalIds['entity_type'] = ExternalId::TYPE_SHOW;
$externalId['imdb_id'] = $tmdbExternalIds['imdb_id'] ?? null;
$externalId['wikidata_id'] = $tmdbExternalIds['wikidata_id'] ?? null;
$externalId['facebook_id'] = $tmdbExternalIds['facebook_id'] ?? null;
$externalId['instagram_id'] = $tmdbExternalIds['instagram_id'] ?? null;
$externalId['twitter_id'] = $tmdbExternalIds['twitter_id'] ?? null;
$externalId['tvdb_id'] = $tmdbExternalIds['tvdb_id'] ?? null;
$externalId['freebase_id'] = $tmdbExternalIds['freebase_id'] ?? null;
$externalId['tvrage_id'] = $tmdbExternalIds['tvrage_id'] ?? null;
ExternalId::create($externalIds);
} else {
$externalId->imdb_id = $tmdbExternalIds['imdb_id'] ?? null;
$externalId->wikidata_id = $tmdbExternalIds['wikidata_id'] ?? null;
$externalId->facebook_id = $tmdbExternalIds['facebook_id'] ?? null;
$externalId->instagram_id = $tmdbExternalIds['instagram_id'] ?? null;
$externalId->twitter_id = $tmdbExternalIds['twitter_id'] ?? null;
$externalId->tvdb_id = $tmdbExternalIds['tvdb_id'] ?? null;
$externalId->freebase_id = $tmdbExternalIds['freebase_id'] ?? null;
$externalId->tvrage_id = $tmdbExternalIds['tvrage_id'] ?? null;
$externalId->save();
}
$this->removeElementFromRemainingJobs($item->tmdb_id, ItemToFetchJobs::JOB_EXTERNAL_IDS);
@@ -250,12 +300,17 @@ class ShowProcessingService implements ProcessingInterface
]);
}
if ($show->whereHas('videos', function ($query) use ($show) {
$query->where('show_id', $show->id);
})->exists()) {
return;
} else {
$show->videos()->attach($videoExists);
$videoIsAttached = DB::table('show_videos')
->where('show_id', $show->id)
->where('video_id', $videoExists->id)
->first();
if (!$videoIsAttached) {
DB::table('show_videos')
->insert([
'show_id' => $show->id,
'video_id' => $videoExists->id
]);
}
}
@@ -282,12 +337,17 @@ class ShowProcessingService implements ProcessingInterface
]);
}
if ($show->whereHas('keywords', function ($query) use ($existingKeyword) {
$query->where('tmdb_id', $existingKeyword->tmdb_id);
})->exists()) {
return;
} else {
$show->keywords()->attach($existingKeyword);
$keywordIsAttached = DB::table('show_keywords')
->where('show_id', $show->id)
->where('keyword_id', $existingKeyword->id)
->first();
if (!$keywordIsAttached) {
DB::table('show_keywords')
->insert([
'show_id' => $show->id,
'keyword_id' => $existingKeyword->id,
]);
}
}
@@ -597,12 +657,25 @@ class ShowProcessingService implements ProcessingInterface
private function removeElementFromRemainingJobs(int $tmdbId, string $job)
{
$itemToFetchJobs = ItemToFetchJobs::where('tmdb_id', $tmdbId)->first();
$itemToFetchJobs = ItemToFetchJobs::where('tmdb_id', $tmdbId)
->where('type', 'tv_series')
->first();
$remainingJobs = $itemToFetchJobs->remaining_jobs;
if (!$itemToFetchJobs) {
echo "no job for ".$tmdbId."\n";
return;
}
$remainingJobs = $itemToFetchJobs->jobs;
if (is_string($remainingJobs)) {
$remainingJobs = json_decode($remainingJobs, true);
$remainingJobs = explode(',', $remainingJobs);
} else if ($itemToFetchJobs->jobs == null) {
$this->removeItemToFetch($tmdbId);
return;
} else {
dd("Not string", $remainingJobs, $itemToFetchJobs);
return;
}
$remainingJobKey = array_search($job, $remainingJobs);
@@ -610,20 +683,42 @@ class ShowProcessingService implements ProcessingInterface
unset($remainingJobs[$remainingJobKey]);
if (empty($remainingJobs)) {
$itemToFetchJobs->delete();
$this->removeItemToFetch($tmdbId);
return;
}
$itemToFetchJobs->remaining_jobs = $remainingJobs;
$itemToFetchJobs->jobs = implode(',', $remainingJobs);
$itemToFetchJobs->save();
}
private function removeItemToFetch($tmdbId)
{
$itemToFetchJobs = ItemToFetchJobs::where('tmdb_id', $tmdbId)
->where('type', 'tv_series')
->first();
$itemToFetch = ItemToFetch::where('tmdb_id', $tmdbId)
->where('type', 'tv_series')
->first();
if ($itemToFetch) {
$itemToFetch->delete();
}
if ($itemToFetchJobs) {
$itemToFetchJobs->delete();
}
}
private function processCast($show, array $credits)
{
if (empty($credits)) {
if (empty($credits) || $show === null) {
echo "+";
return;
}
foreach ($credits as $castPerson) {
echo "Searching for person ".$castPerson['id']."\n";
$person = Person::where('tmdb_id', $castPerson['id'])->first();
if (!$person) {
@@ -652,27 +747,33 @@ class ShowProcessingService implements ProcessingInterface
]);
}
echo "Creating person ".$castPerson['id']."\n";
$person = Person::create($personData);
}
if ($show->whereHas('castList', function ($query) use ($person) {
$query->where('tmdb_id', '=', $person->tmdb_id);
})->exists()) {
return;
} else {
$show->castList()->attach($person, [
'known_for_department' => $castPerson['known_for_department'],
'character' => $castPerson['character'],
'tmdb_cast_id' => $castPerson['cast_id'],
'order' => $castPerson['order'],
]);
$castExists = DB::table('show_cast')
->where('show_id', $show->id)
->where('person_id', $person->id)
->first();
if (!$castExists) {
DB::table('show_cast')
->insert([
'show_id' => $show->id,
'person_id' => $person->id,
'known_for_department' => trim($castPerson['known_for_department'], '"'),
'character' => $castPerson['character'],
'tmdb_cast_id' => $castPerson['credit_id'],
'order' => $castPerson['order'],
]);
}
}
}
private function processCrew($show, array $credits)
{
if (empty($credits)) {
if (empty($credits) || $show === null) {
echo "+";
return;
}
@@ -708,17 +809,21 @@ class ShowProcessingService implements ProcessingInterface
$person = Person::create($personData);
}
if ($show->whereHas('crewList', function ($query) use ($person) {
$query->where('tmdb_id', '=', $person->tmdb_id);
})->exists()) {
return;
} else {
$show->crewList()->attach($person, [
'known_for_department' => json_encode($crewPerson['known_for_department']),
'job' => $crewPerson['job'],
'department' => $crewPerson['department'],
'tmdb_cast_id' => $crewPerson['credit_id'],
]);
$crewExists = DB::table('show_crew')
->where('show_id', $show->id)
->where('person_id', $person->id)
->first();
if (!$crewExists) {
DB::table('show_crew')
->insert([
'show_id' => $show->id,
'person_id' => $person->id,
'known_for_department' => trim($crewPerson['known_for_department'], '"'),
'job' => $crewPerson['job'],
'department' => $crewPerson['department'],
'tmdb_cast_id' => $crewPerson['credit_id'],
]);
}
}
}