106 lines
3.2 KiB
Svelte
106 lines
3.2 KiB
Svelte
<script lang="ts">
|
|
import type { MediaItem } from "$lib/api/types";
|
|
import { auth } from "$lib/stores/auth";
|
|
import EpisodeRow from "./EpisodeRow.svelte";
|
|
import SeasonDownloadButton from "./SeasonDownloadButton.svelte";
|
|
|
|
interface Props {
|
|
season: MediaItem;
|
|
episodes: MediaItem[];
|
|
focusedEpisodeId?: string;
|
|
onEpisodeClick?: (episode: MediaItem) => void;
|
|
}
|
|
|
|
let { season, episodes, focusedEpisodeId, onEpisodeClick }: Props = $props();
|
|
|
|
function getImageUrl(): string {
|
|
try {
|
|
const repo = auth.getRepository();
|
|
return repo.getImageUrl(season.id, "Primary", {
|
|
maxWidth: 200,
|
|
tag: season.primaryImageTag,
|
|
});
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
const imageUrl = $derived(getImageUrl());
|
|
const episodeCount = $derived(episodes.length);
|
|
const seasonNumber = $derived(season.indexNumber || season.parentIndexNumber);
|
|
const seasonName = $derived(
|
|
season.name || (seasonNumber ? `Season ${seasonNumber}` : "Unknown Season")
|
|
);
|
|
</script>
|
|
|
|
<section class="space-y-4">
|
|
<!-- Season header -->
|
|
<div class="flex gap-4 p-4 bg-[var(--color-surface)] rounded-xl">
|
|
<!-- Season poster -->
|
|
<div class="flex-shrink-0 w-20 aspect-[2/3] rounded-lg overflow-hidden bg-[var(--color-background)]">
|
|
{#if imageUrl}
|
|
<img
|
|
src={imageUrl}
|
|
alt={seasonName}
|
|
class="w-full h-full object-cover"
|
|
loading="lazy"
|
|
/>
|
|
{:else}
|
|
<div class="w-full h-full flex items-center justify-center text-gray-600">
|
|
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 12.5v-9l6 4.5-6 4.5z"/>
|
|
</svg>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Season info -->
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex items-start justify-between gap-4">
|
|
<div class="flex-1 min-w-0">
|
|
<h2 class="text-xl font-bold text-white">
|
|
{seasonName}
|
|
</h2>
|
|
|
|
<div class="flex items-center gap-3 mt-1 text-sm text-gray-400">
|
|
<span>{episodeCount} {episodeCount === 1 ? "Episode" : "Episodes"}</span>
|
|
{#if season.productionYear}
|
|
<span>•</span>
|
|
<span>{season.productionYear}</span>
|
|
{/if}
|
|
</div>
|
|
|
|
{#if season.overview}
|
|
<p class="text-gray-400 text-sm mt-3 line-clamp-3">
|
|
{season.overview}
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Download Season Button -->
|
|
<div class="flex-shrink-0">
|
|
<SeasonDownloadButton
|
|
seasonId={season.id}
|
|
seriesName={season.seriesName || ""}
|
|
seasonName={seasonName}
|
|
seasonNumber={season.indexNumber || season.parentIndexNumber || 0}
|
|
{episodeCount}
|
|
size="sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Episode list -->
|
|
<div class="space-y-1 pl-2">
|
|
{#each episodes as episode (episode.id)}
|
|
<EpisodeRow
|
|
{episode}
|
|
focused={episode.id === focusedEpisodeId}
|
|
onclick={() => onEpisodeClick?.(episode)}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
</section>
|