diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 96b8425e..81521306 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -189,7 +189,8 @@ "loadingList": "Loading...", "loadingError": "Error loading season", "emptyState": "There are no episodes in this season, check back later!", - "episodeBadge": "E{{episode}}" + "episodeBadge": "E{{episode}}", + "unairedEpisodes": "One or more episodes in this season are disabled because they are not out yet." }, "sources": { "title": "Sources", diff --git a/src/backend/metadata/tmdb.ts b/src/backend/metadata/tmdb.ts index 3bd8c3e7..2bae75d1 100644 --- a/src/backend/metadata/tmdb.ts +++ b/src/backend/metadata/tmdb.ts @@ -71,7 +71,7 @@ export function formatTMDBMeta( type, seasons: seasons as any, seasonData: season - ? ({ + ? { id: season.id.toString(), number: season.season_number, title: season.title, @@ -81,8 +81,9 @@ export function formatTMDBMeta( id: v.id.toString(), number: v.episode_number, title: v.title, + air_date: v.air_date, })), - } as any) + } : (undefined as any), }; } @@ -227,6 +228,7 @@ export async function getEpisodes( id: e.id, episode_number: e.episode_number, title: e.name, + air_date: e.air_date, })); } diff --git a/src/backend/metadata/types/mw.ts b/src/backend/metadata/types/mw.ts index 3f0f452f..7589c834 100644 --- a/src/backend/metadata/types/mw.ts +++ b/src/backend/metadata/types/mw.ts @@ -18,6 +18,7 @@ export type MWSeasonWithEpisodeMeta = { id: string; number: number; title: string; + air_date: string; }[]; }; diff --git a/src/backend/metadata/types/tmdb.ts b/src/backend/metadata/types/tmdb.ts index 19a85c54..1071d96c 100644 --- a/src/backend/metadata/types/tmdb.ts +++ b/src/backend/metadata/types/tmdb.ts @@ -13,6 +13,7 @@ export type TMDBEpisodeShort = { title: string; id: number; episode_number: number; + air_date: string; }; export type TMDBMediaResult = { diff --git a/src/components/player/atoms/Episodes.tsx b/src/components/player/atoms/Episodes.tsx index d19590ef..b4bc7f06 100644 --- a/src/components/player/atoms/Episodes.tsx +++ b/src/components/player/atoms/Episodes.tsx @@ -19,6 +19,8 @@ import { PlayerMeta } from "@/stores/player/slices/source"; import { usePlayerStore } from "@/stores/player/store"; import { useProgressStore } from "@/stores/progress"; +import { hasAired } from "../utils/aired"; + function CenteredText(props: { children: React.ReactNode }) { return (
@@ -135,6 +137,9 @@ function EpisodesView({ {t("player.menus.episodes.loadingList")} ); else if (loadingState.value) { + const hasUnairedEpisodes = loadingState.value.season.episodes.some( + (ep) => !hasAired(ep.air_date), + ); content = ( {loadingState.value.season.episodes.length === 0 ? ( @@ -163,19 +168,31 @@ function EpisodesView({ return ( playEpisode(ep.id)} + onClick={() => + hasAired(ep.air_date) ? playEpisode(ep.id) : null + } active={ep.id === meta?.episode?.tmdbId} - clickable + clickable={hasAired(ep.air_date)} rightSide={rightSide} > -
+
{t("player.menus.episodes.episodeBadge", { @@ -188,6 +205,9 @@ function EpisodesView({ ); })} + {hasUnairedEpisodes ? ( +

{t("player.menus.episodes.unairedEpisodes")}

+ ) : null} ); } diff --git a/src/components/player/utils/aired.ts b/src/components/player/utils/aired.ts new file mode 100644 index 00000000..26011316 --- /dev/null +++ b/src/components/player/utils/aired.ts @@ -0,0 +1,11 @@ +const hasAiredCache: { [key: string]: boolean } = {}; + +export function hasAired(date: string) { + if (hasAiredCache[date]) return hasAiredCache[date]; + + const now = new Date(); + const airDate = new Date(date); + + hasAiredCache[date] = airDate < now; + return hasAiredCache[date]; +}