Skip to content

Commit b9703fc

Browse files
committed
Add TV Shows and Orphan Episodes tabs to metadata enrichment
Adds tabbed filtering (All / TV Shows / Orphan Episodes) to the metadata enrichment page. TV tab filters to TV content only with show_name column. Orphan tab detects episodes missing a parent series or show_name, with one-click linking that propagates to siblings. Review modal now shows episode-specific fields.
1 parent 9842a18 commit b9703fc

2 files changed

Lines changed: 274 additions & 4 deletions

File tree

app/Livewire/Movies/MovieMetadataEnrichment.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class MovieMetadataEnrichment extends Component
3939

4040
public int $batchLimit = 100;
4141

42+
public string $activeTab = 'all';
43+
44+
public array $orphanEpisodes = [];
45+
4246
protected const ENRICHABLE_FIELDS = ['description', 'poster_url', 'runtime_minutes', 'release_date', 'genres', 'director', 'show_name', 'season_number', 'episode_number'];
4347

4448
public function mount(): void
@@ -134,6 +138,8 @@ public function scanLibrary(): void
134138
->values()
135139
->toArray();
136140

141+
$this->scanOrphanEpisodes();
142+
137143
$this->hasScanned = true;
138144
$this->isScanning = false;
139145
}
@@ -486,6 +492,114 @@ public function isJobCompleted(): bool
486492
return $this->jobStatus && $this->jobStatus['status'] === 'completed';
487493
}
488494

495+
public function setActiveTab(string $tab): void
496+
{
497+
$this->activeTab = $tab;
498+
}
499+
500+
public function getFilteredMovies(): array
501+
{
502+
if ($this->activeTab === 'tv') {
503+
return collect($this->moviesNeedingEnrichment)
504+
->filter(fn ($m) => in_array($m['title_type'] ?? '', ['TV Series', 'TV Mini Series', 'TV Episode']))
505+
->values()
506+
->toArray();
507+
}
508+
509+
if ($this->activeTab === 'orphans') {
510+
return [];
511+
}
512+
513+
return $this->moviesNeedingEnrichment;
514+
}
515+
516+
public function getTvCount(): int
517+
{
518+
return collect($this->moviesNeedingEnrichment)
519+
->filter(fn ($m) => in_array($m['title_type'] ?? '', ['TV Series', 'TV Mini Series', 'TV Episode']))
520+
->count();
521+
}
522+
523+
public function scanOrphanEpisodes(): void
524+
{
525+
$userId = Auth::id();
526+
527+
// Get all TV Series / TV Mini Series titles for this user
528+
$seriesTitles = Movie::query()
529+
->where('user_id', $userId)
530+
->whereIn('title_type', ['TV Series', 'TV Mini Series'])
531+
->pluck('title')
532+
->toArray();
533+
534+
// Find TV Episodes with no show_name, or whose show_name doesn't match any series title
535+
$orphans = Movie::query()
536+
->where('user_id', $userId)
537+
->where('title_type', 'TV Episode')
538+
->where(function ($query) use ($seriesTitles) {
539+
$query->whereNull('show_name')
540+
->orWhere('show_name', '');
541+
if (! empty($seriesTitles)) {
542+
$query->orWhereNotIn('show_name', $seriesTitles);
543+
}
544+
})
545+
->orderBy('title')
546+
->get(['id', 'title', 'title_type', 'imdb_id', 'year', 'show_name', 'season_number', 'episode_number', 'poster_url'])
547+
->map(fn ($movie) => [
548+
'id' => $movie->id,
549+
'title' => $movie->title,
550+
'imdb_id' => $movie->imdb_id,
551+
'year' => $movie->year,
552+
'show_name' => $movie->show_name,
553+
'season_episode' => $movie->season_episode_label,
554+
'has_show_name' => ! empty($movie->show_name),
555+
])
556+
->toArray();
557+
558+
$this->orphanEpisodes = $orphans;
559+
}
560+
561+
public function linkOrphanToShow(int $movieId, string $showName): void
562+
{
563+
$movie = Movie::query()
564+
->where('user_id', Auth::id())
565+
->where('title_type', 'TV Episode')
566+
->find($movieId);
567+
568+
if (! $movie) {
569+
return;
570+
}
571+
572+
$movie->update(['show_name' => $showName]);
573+
574+
// Propagate to siblings with same title prefix
575+
$titlePrefix = str_contains($movie->title, ':')
576+
? trim(explode(':', $movie->title, 2)[0])
577+
: null;
578+
579+
$propagated = 0;
580+
if ($titlePrefix) {
581+
$propagated = Movie::query()
582+
->where('user_id', Auth::id())
583+
->where('title_type', 'TV Episode')
584+
->where(function ($q) use ($titlePrefix) {
585+
$q->where('title', 'like', $titlePrefix . ':%');
586+
})
587+
->where(function ($q) {
588+
$q->whereNull('show_name')->orWhere('show_name', '');
589+
})
590+
->update(['show_name' => $showName]);
591+
}
592+
593+
// Re-scan orphans to reflect changes
594+
$this->scanOrphanEpisodes();
595+
596+
$msg = "Linked episode to \"{$showName}\".";
597+
if ($propagated > 0) {
598+
$msg .= " Also linked {$propagated} sibling episode(s).";
599+
}
600+
session()->flash('message', $msg);
601+
}
602+
489603
public function render()
490604
{
491605
if ($this->jobStatus && $this->jobStatus['status'] === 'running') {

0 commit comments

Comments
 (0)