Files
mtdb_movie/app/Actions/People/GetPersonCredits.php
maher 703f50a09d
Some checks failed
Build / run (push) Has been cancelled
first commit
2025-10-29 11:42:25 +01:00

284 lines
9.7 KiB
PHP
Executable File

<?php
namespace App\Actions\People;
use App\Models\Person;
use App\Models\Title;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
class GetPersonCredits
{
private ?int $titleId;
public function execute(Person $person, $options = []): array
{
$this->titleId = Arr::get($options, 'titleId');
$credits = $this->titleId ? [] : $this->getTitleCredits($person);
$seasonCredits = $this->getSeasonCredits($person);
$episodeCredits = $this->getEpisodeCredits($person);
$mergedCredits = $this->mergeCredits(
Arr::get($credits, 'all', []),
$seasonCredits,
$episodeCredits,
);
$mergedCredits = $this->separateSelfCreditsAndSort($mergedCredits);
return [
'credits' => $mergedCredits,
'knownFor' => Arr::get($credits, 'knownFor', []),
'total_credits_count' => array_reduce(
$mergedCredits,
fn($carry, $item) => $carry + count($item),
0,
),
];
}
private function mergeCredits($credits1, $credits2, $credits3): array
{
$mergedCredits = array_merge_recursive($credits1, $credits2, $credits3);
return array_map(function ($titles) {
// sort titles by year
usort($titles, fn($a, $b) => $b['year'] - $a['year']);
$unique = [];
// if this title already exists and existing
// title has episodes property, continue,
// otherwise push title into 'unique' array
foreach ($titles as $title) {
$existing = Arr::get($unique, $title['id']);
if ($existing) {
$existing['credited_episode_count'] =
Arr::get($existing, 'credited_episode_count', 0) +
Arr::get($title, 'credited_episode_count', 0);
$existing['episodes'] = collect(
array_merge(
Arr::get($existing, 'episodes', []),
Arr::get($title, 'episodes', []),
),
)
->unique('id')
->toArray();
if (!$this->titleId) {
$existing['episodes'] = array_slice(
$existing['episodes'],
0,
5,
);
}
$unique[$title['id']] = $existing;
} else {
$unique[$title['id']] = $title;
}
}
return array_values($unique);
}, $mergedCredits);
}
private function getTitleCredits(Person $person): array
{
$credits = $person->credits()->get();
// generate known for list for actors "known_for" department.
$allKnownFor = $credits
->filter(function (Title $credit) use ($person) {
$knownFor =
strtolower($person->known_for) === 'acting'
? 'actors'
: $person->known_for;
return $credit->pivot->department === strtolower($knownFor);
})
->unique();
$knownFor = $allKnownFor->where('pivot.order', '<', 10);
if ($knownFor->count() < 4) {
$knownFor = $allKnownFor;
}
// sort by person credit "order" for title as well as title popularity
$knownFor = $knownFor
->sortBy(function ($title) {
$order = $title->pivot->order;
$popularity = $title->popularity;
return $order - $popularity;
})
->slice(0, 6)
->values();
// cast to array, so poster/backdrop is not removed later.
$knownFor = $knownFor->load(['primaryVideo'])->toArray();
// remove any data not needed to render person filmography
$credits = $credits
->map(function (Title $credit) {
unset($credit['backdrop']);
return $credit;
})
->groupBy('pivot.department');
return ['all' => $credits->toArray(), 'knownFor' => $knownFor];
}
/**
* Get credits for all series seasons person is attached to.
*/
private function getSeasonCredits(Person $person): array
{
$seasons = $person
->seasonCredits($this->titleId)
->with([
'title' => fn($query) => $query->select(
'id',
'name',
'release_date',
'poster',
),
'episodes' => fn($query) => $query
->select(
'id',
'name',
'release_date',
'season_id',
'season_number',
'episode_number',
'title_id',
)
->orderBy('season_number', 'desc')
->orderBy('episode_number', 'desc'),
])
->get();
// group all seasons by department, for example "production"
$groupedSeasons = $seasons->groupBy('pivot.department');
return $groupedSeasons
->map(function (Collection $departmentGroup) {
$seasonsGroupedByTitle = $departmentGroup->groupBy('title.id');
// attach episodes from all seasons to title
return $seasonsGroupedByTitle
->map(function (Collection $titleSeasons) {
$title = $titleSeasons
->first(fn($s) => $s->title)
->title->toArray();
//get episodes from each season and move season "pivot" data to each episode
$episodesFromAllSeasons = $titleSeasons
->pluck('episodes')
->flatten()
->values()
->map(function ($episode) use ($titleSeasons) {
$episode->pivot = $titleSeasons
->first()
->pivot->toArray();
return $episode;
});
$title[
'credited_episode_count'
] = $episodesFromAllSeasons->count();
if (!$this->titleId) {
$episodesFromAllSeasons = $episodesFromAllSeasons->take(
5,
);
}
$title['episodes'] = $episodesFromAllSeasons->toArray();
return $title;
})
->values();
})
->toArray();
}
/**
* Get all individual episodes person is credited for.
*
* This will return array grouped by department, and
* series with all episodes person is credited for attached
* to that series.
*
* @param Person $person
* @return array
*/
private function getEpisodeCredits(Person $person)
{
$episodes = $person
->episodeCredits($this->titleId)
->with([
'title' => function (BelongsTo $query) {
$query->select('id', 'name', 'release_date', 'poster');
},
])
->get();
$groupedByDep = $episodes->groupBy('pivot.department');
return $groupedByDep
->map(
fn(Collection $episodes) => $episodes
->groupBy('title.id')
->map(function (Collection $episodes) {
if (!$episodes->first()->title) {
return null;
}
$title = $episodes->first()->title->toArray();
$episodes = $episodes->map(function ($episode) {
unset($episode->title);
return $episode;
});
$title['credited_episode_count'] = $episodes->count();
if (!$this->titleId) {
$episodes = $episodes->take(5);
}
$title['episodes'] = $episodes->toArray();
return $title;
})
->filter()
->values(),
)
->toArray();
}
private function separateSelfCreditsAndSort(array $credits): array
{
if (!isset($credits['cast'])) {
return $credits;
}
$cast = [];
$self = [];
foreach ($credits['cast'] as $credit) {
$char = isset($credit['pivot']['character'])
? strtolower($credit['pivot']['character'])
: null;
if (
$char &&
($char === 'self' || Str::contains($char, 'himself'))
) {
$self[] = $credit;
} else {
$cast[] = $credit;
}
}
$credits['cast'] = $cast;
// sort before adding "self" to array as that should be last always
uksort(
$credits,
fn($a, $b) => (is_countable($credits[$b])
? count($credits[$b])
: 0) - (is_countable($credits[$a]) ? count($credits[$a]) : 0),
);
$credits['self'] = $self;
return $credits;
}
}