aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Media/Anime/Airing/classify.ts
blob: 9585fae9dcb9180be022215c5e75e34e09553a2d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import type { Media } from "$lib/Data/AniList/media";

export interface AnimeEpisodeState {
	progress: number;
	nextEpisode: number;
	airedEpisodes: number;
}

const hasAired = (airingAt: number | undefined, nowEpochSeconds: number) =>
	typeof airingAt === "number" && airingAt <= nowEpochSeconds;

export const getAnimeEpisodeState = (
	media: Media,
	nowEpochSeconds = Date.now() / 1000,
): AnimeEpisodeState => {
	const progress = media.mediaListEntry?.progress || 0;
	const nextEpisode =
		media.nextAiringEpisode?.nativeEpisode ||
		media.nextAiringEpisode?.episode ||
		0;

	if (nextEpisode <= 0) {
		return {
			progress,
			nextEpisode,
			airedEpisodes: 0,
		};
	}

	let airedEpisodes = Math.max(0, nextEpisode - 1);
	const airingAt = media.nextAiringEpisode?.airingAt;
	const nativeAiringAt = media.nextAiringEpisode?.nativeAiringAt;

	// If either source says the "next" episode already aired, treat it as released.
	if (
		hasAired(airingAt, nowEpochSeconds) ||
		hasAired(nativeAiringAt, nowEpochSeconds)
	)
		airedEpisodes = Math.max(airedEpisodes, nextEpisode);

	return {
		progress,
		nextEpisode,
		airedEpisodes,
	};
};

export const hasDueEpisodes = (
	media: Media,
	nowEpochSeconds = Date.now() / 1000,
) => {
	const episodeState = getAnimeEpisodeState(media, nowEpochSeconds);

	return episodeState.airedEpisodes > episodeState.progress;
};

export const hasNoAiredEpisodes = (
	media: Media,
	nowEpochSeconds = Date.now() / 1000,
) => {
	const episodeState = getAnimeEpisodeState(media, nowEpochSeconds);

	return episodeState.airedEpisodes <= 0;
};