blob: bd49bbf8559d0735cfee8567c9013dc6a07c3f42 (
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
<script lang="ts">
import { onMount } from 'svelte';
import Message from '$lib/Loading/Message.svelte';
import Skeleton from '$lib/Loading/Skeleton.svelte';
interface ParseResult {
lives: {
time: Date;
link: string;
videoId: string;
streamer: string;
livePreviewImage: string;
guests: string[];
streaming: boolean;
}[];
dict: Record<string, string>;
}
let schedulePromise: Promise<Response>;
onMount(() => (schedulePromise = fetch('/api/hololive')));
const typeSchedule = (schedule: any) => schedule as ParseResult;
</script>
{#await schedulePromise}
<Message message="Loading schedule ..." />
<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{:then scheduleResponse}
{#if scheduleResponse}
{#await scheduleResponse.json()}
<Message message="Parsing schedule ..." />
<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{:then untypedSchedule}
{@const schedule = typeSchedule(untypedSchedule)}
{#if schedule.lives.length === 0}
<Message message="No upcoming streams." />
{/if}
<div class="container">
{#each schedule.lives
.filter((live) => {
const time = new Date(live.time);
return time.getTime() > Date.now() - 12 * 60 * 60 * 1000 || time.getTime() > Date.now() || live.streaming;
})
.sort((a, b) => {
if (a.streaming && !b.streaming) return -1;
if (!a.streaming && b.streaming) return 1;
return new Date(a.time).getTime() - new Date(b.time).getTime();
}) as live}
<div class="stream card">
<p>
[{#if live.streaming}
<b class="live">LIVE</b>{:else}
<span class="upcoming">Upcoming</span>{/if}]
<b>{live.streamer}</b> <span class="opaque">|</span>
{new Date(live.time).toLocaleString()}
{#if live.guests.length > 0}
<br />
<small>
With {live.guests.join(', ').replace(/, ([^,]+)$/, ', & $1')}
</small>
{/if}
</p>
<a href={live.link} class="preview">
<img src={live.livePreviewImage} alt="Stream Preview" />
</a>
</div>
{/each}
</div>
{:catch}
<Message message="Could not parse schedule. Please try again later." loader="ripple" />
{/await}
{:else}
<Message message="Loading schedule ..." />
<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{/if}
{:catch}
<Message message="Could not load schedule. Please try again later." loader="ripple" />
{/await}
<style lang="scss">
.preview {
// margin: 0.15rem;
img {
border-radius: 8px;
height: 20vh;
transition: transform 0.3s ease;
}
&:hover {
img {
position: relative;
z-index: 2;
transition: transform 0.3s ease;
transform: scale(1.05);
}
}
display: flex;
justify-content: center;
align-items: center;
}
.live {
color: var(--base0F);
}
.upcoming {
color: var(--base0C);
}
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(22.5em, 1fr));
gap: 0.5em;
}
</style>
|