225 lines
12 KiB
Plaintext
225 lines
12 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 id="show-@show.ShowID" 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.Download)
|
|
{
|
|
<span class="btn btn-sm btn-success disabled" aria-disabled="true">✓ Marked for download</span>
|
|
}
|
|
else
|
|
{
|
|
<form method="post" asp-page-handler="Download" class="d-inline">
|
|
<input type="hidden" name="showId" value="@show.ShowID" />
|
|
<button type="submit" class="btn btn-sm btn-outline-success">⬇ Download</button>
|
|
</form>
|
|
}
|
|
|
|
@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>
|