aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-01-01 21:59:25 -0800
committerFuwn <[email protected]>2024-01-01 21:59:25 -0800
commita35ec1a3f7b340faaaf716b4694f52ef721baec5 (patch)
tree62dc74f475f42fdc5973e368b76debc25c9e995a /src
parentfeat(oauth): add animeschedule back (diff)
downloaddue.moe-a35ec1a3f7b340faaaf716b4694f52ef721baec5.tar.xz
due.moe-a35ec1a3f7b340faaaf716b4694f52ef721baec5.zip
refactor(schedule): move to lib
Diffstat (limited to 'src')
-rw-r--r--src/lib/Tools/Schedule/Tool.svelte220
-rw-r--r--src/routes/schedule/+page.svelte221
2 files changed, 223 insertions, 218 deletions
diff --git a/src/lib/Tools/Schedule/Tool.svelte b/src/lib/Tools/Schedule/Tool.svelte
new file mode 100644
index 00000000..4d68d85b
--- /dev/null
+++ b/src/lib/Tools/Schedule/Tool.svelte
@@ -0,0 +1,220 @@
+<script lang="ts">
+ import Error from '$lib/Error/RateLimited.svelte';
+ import type { SubsPlease, SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
+ import { onMount } from 'svelte';
+ import settings from '$stores/settings';
+ import { parseOrDefault } from '$lib/Utility/parameters';
+ import { browser } from '$app/environment';
+ import type { Media } from '$lib/AniList/media';
+ import { scheduleMediaListCollection } from '$lib/AniList/schedule';
+ import { season } from '$lib/Media/Anime/season';
+ import { findClosestMedia } from '$lib/Media/Anime/Airing/Subtitled/match';
+ import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
+ import { outboundLink } from '$lib/Media/links';
+ import HeadTitle from '$lib/HeadTitle.svelte';
+ import { onMouseEnter, onMouseLeave, onMouseMove } from '$lib/Media/hoverCover';
+ import HoverCover from '$lib/Media/HoverCover.svelte';
+ import Crunchyroll from '$lib/Tools/Schedule/Crunchyroll.svelte';
+
+ let subsPleasePromise: Promise<SubsPlease>;
+ let scheduledMediaPromise: Promise<Partial<Media[]>>;
+ const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
+ let timeZone = parseOrDefault(
+ urlParameters,
+ 'tz',
+ Intl.DateTimeFormat().resolvedOptions().timeZone
+ );
+ let day: string | null = parseOrDefault(urlParameters, 'day', null);
+
+ onMount(async () => {
+ subsPleasePromise = fetch(`/api/subsplease?tz=${timeZone}`).then((r) => r.json());
+ scheduledMediaPromise = scheduleMediaListCollection(new Date().getFullYear(), season());
+ });
+
+ let hovering = false;
+ let hoveredItem: SubsPleaseEpisode | null = null;
+ let hoveredMedia: Media | null = null;
+ let imageStyle = '';
+
+ const shiftSubsPleaseSchedule = (schedule: SubsPlease['schedule']) => {
+ const shiftedSchedule: { [key: string]: SubsPleaseEpisode[] } = {};
+
+ if (day && Object.keys(schedule).includes(day)) {
+ shiftedSchedule[day] = schedule[
+ day as keyof typeof schedule
+ ] as unknown as SubsPleaseEpisode[];
+
+ return shiftedSchedule;
+ }
+
+ const days = Object.keys(schedule);
+ const currentDayIndex = days.indexOf(new Date().toLocaleString('en-us', { weekday: 'long' }));
+
+ days
+ .slice(currentDayIndex)
+ .concat(days.slice(0, currentDayIndex))
+ .forEach((day) => {
+ const scheduleEntry = schedule[day as keyof typeof schedule];
+ shiftedSchedule[day] = Array.isArray(scheduleEntry)
+ ? scheduleEntry
+ : ([scheduleEntry] as unknown as SubsPleaseEpisode[]);
+ });
+
+ return shiftedSchedule;
+ };
+
+ const associateMedia = (media: (Media | undefined)[], title: string) =>
+ findClosestMedia(media as Media[], title);
+
+ const titleSelect = (media: Media | null) =>
+ media ? media.title.english || media.title.romaji || media.title.native : null;
+</script>
+
+<HeadTitle route="Schedule" path="/schedule" />
+
+<blockquote>
+ <select
+ bind:value={timeZone}
+ on:change={() =>
+ (subsPleasePromise = fetch(`/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>
+
+{#await subsPleasePromise}
+ Loading subtitle release data ... 49.5%
+{:then subsPlease}
+ {#if subsPlease}
+ {#await scheduledMediaPromise}
+ Loading anime schedule ... 82.5%
+ {:then scheduledMedia}
+ {#if scheduledMedia}
+ {@const columnCount = Math.ceil(Object.keys(subsPlease.schedule).length / 2)}
+
+ <div id="list-container" style={`column-count: ${columnCount}`}>
+ {#each Object.entries(shiftSubsPleaseSchedule(subsPlease.schedule)) as [day, scheduleEntry]}
+ <details
+ open
+ class="list"
+ class:today={day === new Date().toLocaleString('en-us', { weekday: 'long' })}
+ >
+ <summary>{day}</summary>
+
+ <ul>
+ {#each Object.values(scheduleEntry) as entry}
+ {@const media = associateMedia(scheduledMedia, entry.title)}
+
+ <li
+ class="entry"
+ on:mouseenter={() => {
+ const response = onMouseEnter(media, entry);
+
+ hovering = response.hovering;
+ hoveredItem = response.item;
+ hoveredMedia = response.media;
+ }}
+ on:mouseleave={() => {
+ const response = onMouseLeave();
+
+ hovering = response.hovering;
+ hoveredItem = response.item;
+ hoveredMedia = response.media;
+ }}
+ on:mousemove={(e) => {
+ const response = onMouseMove(e);
+
+ imageStyle = response.style;
+ }}
+ >
+ <a
+ href={media
+ ? outboundLink(media, 'anime', $settings.displayOutboundLinksTo)
+ : outboundLink(
+ null,
+ 'anime',
+ $settings.displayOutboundLinksTo,
+ true,
+ titleSelect(media) || entry.title
+ )}
+ target="_blank"
+ >
+ {#if media}
+ <MediaTitleDisplay title={media.title} />
+ {:else}
+ {entry.title}
+ {/if}
+ </a>
+ {#if !$settings.displayCountdownRightAligned}
+ <span style="opacity: 50%;">|</span>
+ {/if}
+ <span class:countdown={$settings.displayCountdownRightAligned}>
+ {#if media && media.nextAiringEpisode}
+ <span style="opacity: 50%;">
+ &nbsp;
+ {media.nextAiringEpisode?.episode}{media.episodes
+ ? `/${media.episodes}`
+ : ''} at
+ </span>
+ {/if}
+ {entry.time}
+ </span>
+ </li>
+ {/each}
+ </ul>
+
+ <p />
+ </details>
+ {/each}
+ </div>
+ {:else}
+ Loading anime schedule ... 66%
+ {/if}
+ {:catch}
+ <Error type="Media" loginSessionError={false} />
+ {/await}
+ {:else}
+ Loading subtitle release data ... 33%
+ {/if}
+{:catch}
+ <Error type="Schedule" loginSessionError={false} />
+{/await}
+
+<p />
+
+<details>
+ <summary>Crunchyroll Simulcast Release Calender</summary>
+
+ <Crunchyroll />
+</details>
+
+<HoverCover {hoveredItem} {hoveredMedia} {imageStyle} {hovering} />
+
+<style>
+ #list-container {
+ column-width: 250px;
+ }
+
+ .list {
+ overflow-y: auto;
+ }
+
+ .entry::after {
+ content: '';
+ display: table;
+ clear: both;
+ }
+
+ .countdown {
+ white-space: nowrap;
+ float: right;
+ }
+
+ .today {
+ font-weight: bold;
+ }
+</style>
diff --git a/src/routes/schedule/+page.svelte b/src/routes/schedule/+page.svelte
index 4d68d85b..7d24c17c 100644
--- a/src/routes/schedule/+page.svelte
+++ b/src/routes/schedule/+page.svelte
@@ -1,220 +1,5 @@
-<script lang="ts">
- import Error from '$lib/Error/RateLimited.svelte';
- import type { SubsPlease, SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
- import { onMount } from 'svelte';
- import settings from '$stores/settings';
- import { parseOrDefault } from '$lib/Utility/parameters';
- import { browser } from '$app/environment';
- import type { Media } from '$lib/AniList/media';
- import { scheduleMediaListCollection } from '$lib/AniList/schedule';
- import { season } from '$lib/Media/Anime/season';
- import { findClosestMedia } from '$lib/Media/Anime/Airing/Subtitled/match';
- import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
- import { outboundLink } from '$lib/Media/links';
- import HeadTitle from '$lib/HeadTitle.svelte';
- import { onMouseEnter, onMouseLeave, onMouseMove } from '$lib/Media/hoverCover';
- import HoverCover from '$lib/Media/HoverCover.svelte';
- import Crunchyroll from '$lib/Tools/Schedule/Crunchyroll.svelte';
-
- let subsPleasePromise: Promise<SubsPlease>;
- let scheduledMediaPromise: Promise<Partial<Media[]>>;
- const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
- let timeZone = parseOrDefault(
- urlParameters,
- 'tz',
- Intl.DateTimeFormat().resolvedOptions().timeZone
- );
- let day: string | null = parseOrDefault(urlParameters, 'day', null);
-
- onMount(async () => {
- subsPleasePromise = fetch(`/api/subsplease?tz=${timeZone}`).then((r) => r.json());
- scheduledMediaPromise = scheduleMediaListCollection(new Date().getFullYear(), season());
- });
-
- let hovering = false;
- let hoveredItem: SubsPleaseEpisode | null = null;
- let hoveredMedia: Media | null = null;
- let imageStyle = '';
-
- const shiftSubsPleaseSchedule = (schedule: SubsPlease['schedule']) => {
- const shiftedSchedule: { [key: string]: SubsPleaseEpisode[] } = {};
-
- if (day && Object.keys(schedule).includes(day)) {
- shiftedSchedule[day] = schedule[
- day as keyof typeof schedule
- ] as unknown as SubsPleaseEpisode[];
-
- return shiftedSchedule;
- }
-
- const days = Object.keys(schedule);
- const currentDayIndex = days.indexOf(new Date().toLocaleString('en-us', { weekday: 'long' }));
-
- days
- .slice(currentDayIndex)
- .concat(days.slice(0, currentDayIndex))
- .forEach((day) => {
- const scheduleEntry = schedule[day as keyof typeof schedule];
- shiftedSchedule[day] = Array.isArray(scheduleEntry)
- ? scheduleEntry
- : ([scheduleEntry] as unknown as SubsPleaseEpisode[]);
- });
-
- return shiftedSchedule;
- };
-
- const associateMedia = (media: (Media | undefined)[], title: string) =>
- findClosestMedia(media as Media[], title);
-
- const titleSelect = (media: Media | null) =>
- media ? media.title.english || media.title.romaji || media.title.native : null;
+<script>
+ import Tool from '$lib/Tools/Schedule/Tool.svelte';
</script>
-<HeadTitle route="Schedule" path="/schedule" />
-
-<blockquote>
- <select
- bind:value={timeZone}
- on:change={() =>
- (subsPleasePromise = fetch(`/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>
-
-{#await subsPleasePromise}
- Loading subtitle release data ... 49.5%
-{:then subsPlease}
- {#if subsPlease}
- {#await scheduledMediaPromise}
- Loading anime schedule ... 82.5%
- {:then scheduledMedia}
- {#if scheduledMedia}
- {@const columnCount = Math.ceil(Object.keys(subsPlease.schedule).length / 2)}
-
- <div id="list-container" style={`column-count: ${columnCount}`}>
- {#each Object.entries(shiftSubsPleaseSchedule(subsPlease.schedule)) as [day, scheduleEntry]}
- <details
- open
- class="list"
- class:today={day === new Date().toLocaleString('en-us', { weekday: 'long' })}
- >
- <summary>{day}</summary>
-
- <ul>
- {#each Object.values(scheduleEntry) as entry}
- {@const media = associateMedia(scheduledMedia, entry.title)}
-
- <li
- class="entry"
- on:mouseenter={() => {
- const response = onMouseEnter(media, entry);
-
- hovering = response.hovering;
- hoveredItem = response.item;
- hoveredMedia = response.media;
- }}
- on:mouseleave={() => {
- const response = onMouseLeave();
-
- hovering = response.hovering;
- hoveredItem = response.item;
- hoveredMedia = response.media;
- }}
- on:mousemove={(e) => {
- const response = onMouseMove(e);
-
- imageStyle = response.style;
- }}
- >
- <a
- href={media
- ? outboundLink(media, 'anime', $settings.displayOutboundLinksTo)
- : outboundLink(
- null,
- 'anime',
- $settings.displayOutboundLinksTo,
- true,
- titleSelect(media) || entry.title
- )}
- target="_blank"
- >
- {#if media}
- <MediaTitleDisplay title={media.title} />
- {:else}
- {entry.title}
- {/if}
- </a>
- {#if !$settings.displayCountdownRightAligned}
- <span style="opacity: 50%;">|</span>
- {/if}
- <span class:countdown={$settings.displayCountdownRightAligned}>
- {#if media && media.nextAiringEpisode}
- <span style="opacity: 50%;">
- &nbsp;
- {media.nextAiringEpisode?.episode}{media.episodes
- ? `/${media.episodes}`
- : ''} at
- </span>
- {/if}
- {entry.time}
- </span>
- </li>
- {/each}
- </ul>
-
- <p />
- </details>
- {/each}
- </div>
- {:else}
- Loading anime schedule ... 66%
- {/if}
- {:catch}
- <Error type="Media" loginSessionError={false} />
- {/await}
- {:else}
- Loading subtitle release data ... 33%
- {/if}
-{:catch}
- <Error type="Schedule" loginSessionError={false} />
-{/await}
-
-<p />
-
-<details>
- <summary>Crunchyroll Simulcast Release Calender</summary>
-
- <Crunchyroll />
-</details>
-
-<HoverCover {hoveredItem} {hoveredMedia} {imageStyle} {hovering} />
-
-<style>
- #list-container {
- column-width: 250px;
- }
-
- .list {
- overflow-y: auto;
- }
-
- .entry::after {
- content: '';
- display: table;
- clear: both;
- }
-
- .countdown {
- white-space: nowrap;
- float: right;
- }
-
- .today {
- font-weight: bold;
- }
-</style>
+<Tool />