diff options
| author | Fuwn <[email protected]> | 2026-06-05 11:10:22 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-06-05 11:10:22 +0000 |
| commit | 4b56194ee6807acb56abf0949394efadabf830d4 (patch) | |
| tree | 5cb2074a8d012bf9b7c900e7e44cbdfd0e15123f /src/routes | |
| parent | fix(lists): tick count down when media leaves a list (diff) | |
| download | due.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.svelte | 32 | ||||
| -rw-r--r-- | src/routes/api/animeschedule/+server.ts | 25 | ||||
| -rw-r--r-- | src/routes/api/subsplease/+server.ts | 18 | ||||
| -rw-r--r-- | src/routes/schedule/+page.svelte | 20 |
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} /> |