aboutsummaryrefslogtreecommitdiff
path: root/src/routes
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-06-05 11:10:22 +0000
committerFuwn <[email protected]>2026-06-05 11:10:22 +0000
commit4b56194ee6807acb56abf0949394efadabf830d4 (patch)
tree5cb2074a8d012bf9b7c900e7e44cbdfd0e15123f /src/routes
parentfix(lists): tick count down when media leaves a list (diff)
downloaddue.moe-4b56194ee6807acb56abf0949394efadabf830d4.tar.xz
due.moe-4b56194ee6807acb56abf0949394efadabf830d4.zip
feat(airing): replace SubsPlease with AnimeSchedule (sub+dub)
Source both subbed and dubbed episode schedules from AnimeSchedule.net v3 (absolute timestamps, episode numbers, delay windows, streams), keyed to AniList shows by title. Removes SubsPlease and its ~650-line fuzzy matcher. Countdown source is now a setting (native|sub|dub) with a dub->sub->native fallback. Requires ANIMESCHEDULE_CLIENT_TOKEN.
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/+layout.svelte32
-rw-r--r--src/routes/api/animeschedule/+server.ts25
-rw-r--r--src/routes/api/subsplease/+server.ts18
-rw-r--r--src/routes/schedule/+page.svelte20
4 files changed, 33 insertions, 62 deletions
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index f58bff4d..db628da6 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -5,7 +5,6 @@ import { env } from "$env/dynamic/public";
import { userIdentity as getUserIdentity } from "$lib/Data/AniList/identity";
import HeadTitle from "$lib/Home/HeadTitle.svelte";
import Spacer from "$lib/Layout/Spacer.svelte";
-import type { SubsPleaseEpisode } from "$lib/Media/Anime/Airing/Subtitled/subsPlease";
import userIdentity from "$stores/identity";
import settings from "$stores/settings";
import "../app.css";
@@ -25,7 +24,6 @@ import { authActions } from "$lib/CommandPalette/authActions";
import CommandPalette from "$lib/CommandPalette/CommandPalette.svelte";
import { syncActions } from "$lib/CommandPalette/syncActions";
import { toggleActions } from "$lib/CommandPalette/toggleActions";
-import subtitles from "$lib/Data/Static/subtitles.json";
import { database as userDatabase } from "$lib/Database/IDB/user";
import Root from "$lib/Home/Root.svelte";
import Dropdown from "$lib/Layout/Dropdown.svelte";
@@ -38,10 +36,10 @@ import NotificationsProvider from "$lib/Notification/NotificationsProvider.svelt
import { toolsAsCommandPaletteActions } from "$lib/Tools/tools";
import { requestNotifications } from "$lib/Utility/notifications";
import root from "$lib/Utility/root";
+import airingSchedule from "$stores/airingSchedule";
import locale from "$stores/locale";
import settingsSyncPulled from "$stores/settingsSyncPulled";
import settingsSyncTimes from "$stores/settingsSyncTimes";
-import subsPlease from "$stores/subsPlease";
import "lenis/dist/lenis.css";
import lenisStore from "$stores/lenis";
import type { LayoutData } from "./$types";
@@ -286,32 +284,12 @@ $: {
(data.url === "/" ||
data.url === "/completed" ||
data.url === "/schedule") &&
- !$subsPlease
+ !$airingSchedule
)
- fetch(
- root(
- `/api/subsplease?tz=${Intl.DateTimeFormat().resolvedOptions().timeZone}`,
- ),
- )
+ fetch(root("/api/animeschedule"))
.then((r) => r.json())
- .then((r) => {
- for (const day in subtitles) {
- if (!r.schedule[day]) r.schedule[day] = [];
-
- (
- subtitles[day as keyof typeof subtitles] as SubsPleaseEpisode[]
- ).forEach((episode) => {
- r.schedule[day].push({
- title: episode.title,
- page: episode.page || "",
- image_url: episode.image_url || "",
- time: episode.time,
- });
- });
- }
-
- subsPlease.set(r);
- });
+ .then((r) => airingSchedule.set(r))
+ .catch(() => airingSchedule.set({ generatedAt: 0, sub: [], dub: [] }));
}
</script>
diff --git a/src/routes/api/animeschedule/+server.ts b/src/routes/api/animeschedule/+server.ts
new file mode 100644
index 00000000..c596bf41
--- /dev/null
+++ b/src/routes/api/animeschedule/+server.ts
@@ -0,0 +1,25 @@
+import { env } from "$env/dynamic/private";
+import { fetchTimetables } from "$lib/Media/Anime/Airing/animeSchedule";
+import { appOriginHeaders } from "$lib/Utility/appOrigin";
+
+export const GET = async () => {
+ const token = env.ANIMESCHEDULE_CLIENT_TOKEN;
+ const generatedAt = Math.floor(Date.now() / 1000);
+
+ if (!token)
+ return Response.json(
+ { generatedAt, sub: [], dub: [] },
+ { headers: appOriginHeaders({ "Cache-Control": "max-age=60" }) },
+ );
+
+ const { sub, dub } = await fetchTimetables(token);
+
+ return Response.json(
+ { generatedAt, sub, dub },
+ {
+ headers: appOriginHeaders({
+ "Cache-Control": "max-age=86400, s-maxage=86400",
+ }),
+ },
+ );
+};
diff --git a/src/routes/api/subsplease/+server.ts b/src/routes/api/subsplease/+server.ts
deleted file mode 100644
index 1f678d8c..00000000
--- a/src/routes/api/subsplease/+server.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { appOriginHeaders } from "$lib/Utility/appOrigin";
-
-export const GET = async ({ url }) => {
- const timezone = url.searchParams.get("tz") || "America/Los_Angeles";
-
- return Response.json(
- await (
- await fetch(
- `https://subsplease.org/api/?f=schedule&tz=${encodeURIComponent(timezone)}`,
- )
- ).json(),
- {
- headers: appOriginHeaders({
- "Cache-Control": "max-age=86400, s-maxage=86400",
- }),
- },
- );
-};
diff --git a/src/routes/schedule/+page.svelte b/src/routes/schedule/+page.svelte
index d6708a3e..da26e4e3 100644
--- a/src/routes/schedule/+page.svelte
+++ b/src/routes/schedule/+page.svelte
@@ -12,8 +12,8 @@ import "$lib/Schedule/container.css";
import Message from "$lib/Loading/Message.svelte";
import Skeleton from "$lib/Loading/Skeleton.svelte";
import Days from "$lib/Schedule/Days.svelte";
+import airingSchedule from "$stores/airingSchedule";
import locale from "$stores/locale";
-import subsPlease from "$stores/subsPlease";
import type { PageData } from "./$types";
export let data: PageData;
@@ -36,20 +36,6 @@ onMount(async () => {
<HeadTitle routeKey="schedule" path="/schedule" />
-<!-- <blockquote>
- <select
- bind:value={timeZone}
- onchange={() =>
- (subsPleasePromise = fetch(root(`/api/subsplease?tz=${timeZone}`)).then((r) => r.json()))}
- >
- {#each Intl.supportedValuesOf('timeZone') as zone}
- <option value={zone}>
- {zone.split('/').reverse().join(', ').replace(/_/g, ' ')}
- </option>
- {/each}
- </select>
-</blockquote> -->
-
<!-- <details bind:open={crunchyrollExpanded}>
<summary>
Crunchyroll Release Calender (Click to {crunchyrollExpanded ? 'collapse' : 'expand'})
@@ -62,7 +48,7 @@ onMount(async () => {
<Spacer /> -->
-{#if !$subsPlease}
+{#if !$airingSchedule}
<Message message={$locale().schedule?.loadingSubtitle} />
<Skeleton grid={true} count={7} height="15em" width="49.5%" />
@@ -74,7 +60,7 @@ onMount(async () => {
{:then scheduledMedia}
{#if scheduledMedia}
<div class="schedule-container" id="schedule">
- <Days subsPlease={$subsPlease} {scheduledMedia} {forceListMode} user={data.user} />
+ <Days schedule={$airingSchedule} {scheduledMedia} {forceListMode} user={data.user} />
</div>
{:else}
<Message message={$locale().schedule?.loadingSchedule} />