Files
2026-09-25 14:21:54 -04:00

214 lines
11 KiB
Plaintext

@page
@model IndexModel
@{
ViewData["Title"] = "Airing Soon Anime";
}
<div class="airing-container">
<div class="d-flex flex-wrap align-items-center justify-content-between mb-2">
<h1 class="display-5 mb-0 d-flex align-items-center flex-wrap">
<span>🗓️ Airing Soon Anime</span>
<span class="badge rounded-pill bg-dark text-white ms-2 fs-6">@(Model.AiringShows == null ? "No data" : (Model.AiringShows.Count + (Model.AiringShows.Count == 1 ? " show" : " shows")))</span>
</h1>
</div>
<p class="lead text-secondary mb-4">Anime with an upcoming or recent latest episode, drawn from the TMDB and AniList caches. Sorted by air date (<strong>soonest first</strong>).</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger shadow-sm" role="alert">
<strong>Error:</strong> @Model.ErrorMessage
</div>
}
@if (Model.AiringShows == null || Model.AiringShows.Count == 0)
{
<div class="text-center py-5">
<div class="mb-3">
<span class="display-1">📺</span>
</div>
<h3>No airing soon shows found</h3>
<p class="text-secondary">The Redis cache either returned no entries or the airing soon list is empty.</p>
</div>
}
else
{
<div class="airing-list">
@foreach (var show in Model.AiringShows)
{
<article class="airing-row @(show.IsFuture ? "is-upcoming" : "is-aired")">
@if (!string.IsNullOrWhiteSpace(show.HeroImageUrl))
{
<div class="airing-backdrop" style="background-image: url('@show.HeroImageUrl');" aria-hidden="true"></div>
}
<div class="airing-scrim" aria-hidden="true"></div>
<div class="airing-content">
<div class="airing-poster">
@if (!string.IsNullOrWhiteSpace(show.PosterUrl))
{
<img src="@show.PosterUrl" alt="@show.Title poster" title="@show.Title" loading="lazy" />
}
else
{
<div class="airing-poster-placeholder"><span>🗂️</span></div>
}
@if (show.LatestSeasonNumber.HasValue && show.LatestSeasonNumber > 0)
{
<span class="airing-poster-tag">Season @show.LatestSeasonNumber</span>
}
</div>
<div class="airing-body">
<header class="airing-header">
<div class="airing-titles">
<h2 class="airing-title">@show.Title</h2>
@if (show.AlternateTitles.Count > 0)
{
<p class="airing-alt-titles">@string.Join(" · ", show.AlternateTitles)</p>
}
</div>
<div class="airing-scores">
@if (show.VoteAverage.HasValue && show.VoteAverage > 0)
{
<span class="score-chip score-tmdb" title="TMDB user score">
<span class="score-value">@string.Format("{0:0.0}", show.VoteAverage)</span>
<span class="score-source">TMDB</span>
</span>
}
@if (show.AnilistScore.HasValue && show.AnilistScore > 0)
{
<span class="score-chip score-anilist" title="AniList average score">
<span class="score-value">@string.Format("{0:0.0}", show.AnilistScore)</span>
<span class="score-source">AniList</span>
</span>
}
</div>
</header>
<div class="airing-meta">
@if (show.LatestEpisodeNumber > 0)
{
<span class="meta-chip">
@if (show.TotalEpisodes.HasValue && show.TotalEpisodes > 0)
{
<text>Episode @show.LatestEpisodeNumber of @show.TotalEpisodes</text>
}
else
{
<text>Episode @show.LatestEpisodeNumber</text>
}
</span>
}
@if (show.LatestOrdinalEpisodeNumber.HasValue && show.LatestOrdinalEpisodeNumber > 0)
{
<span class="meta-chip">@show.LatestOrdinalEpisodeNumber episodes overall</span>
}
@if (!string.IsNullOrWhiteSpace(show.AnilistStatus))
{
<span class="meta-chip">@show.AnilistStatus</span>
}
@if (show.SeasonYear.HasValue)
{
<span class="meta-chip">@show.SeasonYear</span>
}
@if (show.IsFinaleNext && show.IsFuture)
{
<span class="meta-chip meta-chip-finale">🏁 Season finale</span>
}
</div>
@if (show.Genres.Count > 0 || show.Tags.Count > 0)
{
<div class="airing-taxonomy">
@foreach (var genre in show.Genres)
{
<span class="genre-chip">@genre</span>
}
@foreach (var tag in show.VisibleTags)
{
<span class="tag-chip">#@tag</span>
}
@if (show.OverflowTags.Count > 0)
{
<details class="tag-overflow">
<summary class="tag-chip tag-more">+@show.OverflowTags.Count more</summary>
@foreach (var tag in show.OverflowTags)
{
<span class="tag-chip">#@tag</span>
}
</details>
}
</div>
}
@if (show.EpisodeProgressPercent.HasValue)
{
<div class="airing-progress" title="@show.LatestEpisodeNumber of @show.TotalEpisodes episodes aired">
<div class="airing-progress-bar" style="width: @(show.EpisodeProgressPercent)%;"></div>
</div>
}
@if (!string.IsNullOrWhiteSpace(show.Overview))
{
<p class="airing-overview">
@if (show.OverviewIsSeasonSpecific && show.LatestSeasonNumber > 0)
{
<span class="overview-label">Season @show.LatestSeasonNumber —</span>
}
@show.Overview
</p>
}
else
{
<p class="airing-overview text-secondary fst-italic">No overview available.</p>
}
<footer class="airing-footer">
<a href="https://www.themoviedb.org/tv/@show.ShowID" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-outline-light">TMDB</a>
@if (!string.IsNullOrWhiteSpace(show.AnilistUrl))
{
<a href="@show.AnilistUrl" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-outline-info">AniList</a>
}
@if (show.SecondaryImages.Count > 0)
{
<div class="airing-gallery">
@foreach (var image in show.SecondaryImages)
{
<a href="@image.Url" target="_blank" rel="noopener noreferrer"
class="gallery-thumb" title="@image.Label (@image.Source)">
<img src="@image.Url" alt="@show.Title — @image.Label" loading="lazy" />
</a>
}
</div>
}
</footer>
</div>
<aside class="airing-countdown">
@if (show.EffectiveLastAirDate.HasValue)
{
@if (show.IsFuture)
{
<span class="countdown-number">@Math.Max(show.DaysUntil, 1)</span>
<span class="countdown-unit">@(Math.Max(show.DaysUntil, 1) == 1 ? "day" : "days") away</span>
}
else
{
<span class="countdown-number">@Math.Abs(show.DaysUntil)</span>
<span class="countdown-unit">@(Math.Abs(show.DaysUntil) == 1 ? "day" : "days") ago</span>
}
<span class="countdown-date" title="@show.AirDateLongFormatted">@show.AirDateFormatted</span>
}
else
{
<span class="countdown-unit">Air date unknown</span>
}
</aside>
</div>
</article>
}
</div>
}
</div>