Skip to content

Commit 9eefb4d

Browse files
committed
Add the stupidest, most wonderful easter egg
1 parent 6b8aea6 commit 9eefb4d

File tree

1 file changed

+244
-0
lines changed
  • src/app/(stupid)/[artistSlug]/[year]/[month]

1 file changed

+244
-0
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import RelistenAPI from '@/lib/RelistenAPI';
2+
import { Metadata } from 'next';
3+
import Link from 'next/link';
4+
import { notFound } from 'next/navigation';
5+
import { format } from 'date-fns';
6+
7+
interface Props {
8+
params: Promise<{ artistSlug: string; year: string; month: string }>;
9+
}
10+
11+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
12+
const { artistSlug, year, month } = await params;
13+
const artists = await RelistenAPI.fetchArtists();
14+
const artist = artists?.find((a) => a.slug === artistSlug);
15+
16+
if (!artist) {
17+
return { title: 'Not Found' };
18+
}
19+
20+
const monthName = format(new Date(parseInt(year), parseInt(month) - 1, 1), 'MMMM');
21+
22+
return {
23+
title: `Index of /${artist.name}/${year}/${monthName}`,
24+
};
25+
}
26+
27+
function formatFileSize(seconds: number): string {
28+
const hours = Math.floor(seconds / 3600);
29+
const minutes = Math.floor((seconds % 3600) / 60);
30+
31+
if (hours > 0) {
32+
return `${hours}h ${minutes}m`;
33+
}
34+
return `${minutes}m`;
35+
}
36+
37+
function formatDate(date: string): string {
38+
return format(new Date(date), 'dd-MMM-yyyy HH:mm');
39+
}
40+
41+
export default async function MonthPage({ params }: Props) {
42+
const { artistSlug, year, month } = await params;
43+
44+
// Fetch artist and shows
45+
const [artists, artistShows] = await Promise.all([
46+
RelistenAPI.fetchArtists(),
47+
RelistenAPI.fetchShows(artistSlug, year),
48+
]);
49+
50+
const artist = artists?.find((a) => a.slug === artistSlug);
51+
if (!artist || !artistShows) {
52+
notFound();
53+
}
54+
55+
const shows = artistShows.shows || [];
56+
57+
// Filter shows for the specific month
58+
const monthShows = shows.filter((show) => {
59+
const showMonth = new Date(show.display_date || '').getMonth() + 1;
60+
return showMonth === parseInt(month, 10);
61+
});
62+
63+
// Sort shows by date
64+
monthShows.sort(
65+
(a, b) => new Date(a.display_date || '').getTime() - new Date(b.display_date || '').getTime()
66+
);
67+
68+
const monthName = format(new Date(parseInt(year), parseInt(month) - 1, 1), 'MMMM');
69+
70+
return (
71+
<div className="bg-white p-4 font-serif text-black">
72+
<h1 className="mb-4 text-2xl font-bold">
73+
Index of /{artist.slug}/{year}/{monthName}/
74+
</h1>
75+
<table className="border-collapse">
76+
<tbody>
77+
<tr>
78+
<th className="py-1 pr-2 align-top">
79+
<svg width="20" height="16" viewBox="0 0 20 16">
80+
<defs>
81+
<pattern id="halftone" patternUnits="userSpaceOnUse" width="2" height="2">
82+
<circle cx="1" cy="1" r="0.3" fill="#666" />
83+
</pattern>
84+
</defs>
85+
<rect
86+
x="2"
87+
y="2"
88+
width="16"
89+
height="12"
90+
fill="url(#halftone)"
91+
stroke="#333"
92+
strokeWidth="1"
93+
/>
94+
<rect x="4" y="4" width="4" height="1" fill="#333" />
95+
<rect x="4" y="6" width="6" height="1" fill="#333" />
96+
<rect x="4" y="8" width="5" height="1" fill="#333" />
97+
</svg>
98+
</th>
99+
<th className="py-1 pr-8 text-left">
100+
<a href="?C=N;O=D" className="text-black no-underline hover:underline">
101+
Name
102+
</a>
103+
</th>
104+
<th className="py-1 pr-8 text-left">
105+
<a href="?C=M;O=A" className="text-black no-underline hover:underline">
106+
Last modified
107+
</a>
108+
</th>
109+
<th className="py-1 pr-8 text-left">
110+
<a href="?C=S;O=A" className="text-black no-underline hover:underline">
111+
Size
112+
</a>
113+
</th>
114+
<th className="py-1 text-left">
115+
<a href="?C=D;O=A" className="text-black no-underline hover:underline">
116+
Description
117+
</a>
118+
</th>
119+
</tr>
120+
<tr>
121+
<th colSpan={5}>
122+
<hr className="border-0 border-t border-gray-400" />
123+
</th>
124+
</tr>
125+
<tr className="hover:bg-gray-50">
126+
<td className="py-1 pr-2 align-top">
127+
<svg width="20" height="16" viewBox="0 0 20 16">
128+
<defs>
129+
<pattern id="folderHalftone" patternUnits="userSpaceOnUse" width="3" height="3">
130+
<circle cx="0.5" cy="0.5" r="0.4" fill="#c49102" />
131+
<circle cx="2" cy="1.5" r="0.3" fill="#c49102" />
132+
<circle cx="1" cy="2.5" r="0.2" fill="#c49102" />
133+
</pattern>
134+
<pattern id="folderShadow" patternUnits="userSpaceOnUse" width="2" height="2">
135+
<circle cx="0.5" cy="0.5" r="0.2" fill="#8b6914" />
136+
</pattern>
137+
</defs>
138+
<path
139+
d="M1 2l1-1h4l2 2h10v1H1V2z"
140+
fill="url(#folderHalftone)"
141+
stroke="#8b6914"
142+
strokeWidth="0.5"
143+
/>
144+
<path
145+
d="M2 4h5l2-2h9v12H2V4z"
146+
fill="url(#folderHalftone)"
147+
stroke="#8b6914"
148+
strokeWidth="1"
149+
/>
150+
<path d="M3 6h3v1h-3z" fill="url(#folderShadow)" />
151+
<circle cx="4" cy="8" r="0.5" fill="#8b6914" />
152+
<circle cx="6" cy="10" r="0.3" fill="#8b6914" />
153+
</svg>
154+
</td>
155+
<td className="py-1 pr-8">
156+
<Link
157+
href={`/${artistSlug}/${year}`}
158+
className="text-blue-700 no-underline visited:text-purple-700 hover:underline"
159+
>
160+
Parent Directory
161+
</Link>
162+
</td>
163+
<td className="py-1 pr-8">&nbsp;</td>
164+
<td className="py-1 pr-8 text-right"> - </td>
165+
<td className="py-1">&nbsp;</td>
166+
</tr>
167+
168+
{monthShows.map((show) => {
169+
const showDate = new Date(show.display_date || '');
170+
const dayStr = showDate.getDate().toString().padStart(2, '0');
171+
const showPath = `${dayStr}/`;
172+
173+
return (
174+
<tr key={show.id} className="hover:bg-gray-50">
175+
<td className="py-1 pr-2 align-top">
176+
<svg width="20" height="16" viewBox="0 0 20 16">
177+
<defs>
178+
<pattern
179+
id="dirHalftone"
180+
patternUnits="userSpaceOnUse"
181+
width="2.5"
182+
height="2.5"
183+
>
184+
<circle cx="0.3" cy="0.3" r="0.3" fill="#1e40af" />
185+
<circle cx="1.5" cy="1" r="0.2" fill="#1e40af" />
186+
<circle cx="0.8" cy="2" r="0.25" fill="#1e40af" />
187+
</pattern>
188+
<pattern id="dirDots" patternUnits="userSpaceOnUse" width="4" height="4">
189+
<circle cx="1" cy="1" r="0.4" fill="#1d4ed8" />
190+
<circle cx="3" cy="3" r="0.3" fill="#1d4ed8" />
191+
</pattern>
192+
</defs>
193+
<path
194+
d="M2 4h5l2-2h9v12H2V4z"
195+
fill="url(#dirHalftone)"
196+
stroke="#1e3a8a"
197+
strokeWidth="1"
198+
/>
199+
<rect x="4" y="6" width="10" height="1" fill="url(#dirDots)" />
200+
<rect x="4" y="8" width="8" height="1" fill="url(#dirDots)" />
201+
<rect x="4" y="10" width="6" height="1" fill="url(#dirDots)" />
202+
<circle cx="15" cy="7" r="0.5" fill="#1e3a8a" />
203+
<circle cx="13" cy="9" r="0.3" fill="#1e3a8a" />
204+
</svg>
205+
</td>
206+
<td className="py-1 pr-8">
207+
<Link
208+
href={`/${artistSlug}/${year}/${month}/${dayStr}`}
209+
className="text-blue-700 no-underline visited:text-purple-700 hover:underline"
210+
>
211+
{showPath}
212+
</Link>
213+
</td>
214+
<td className="py-1 pr-8 text-center">{formatDate(show.display_date || '')}</td>
215+
<td className="py-1 pr-8 text-right">
216+
{show.avg_duration && show.avg_duration > 0
217+
? formatFileSize(show.avg_duration)
218+
: '-'}
219+
</td>
220+
<td className="py-1">
221+
{show.venue?.name || ''} {show.venue?.location ? `- ${show.venue.location}` : ''}
222+
</td>
223+
</tr>
224+
);
225+
})}
226+
227+
{monthShows.length === 0 && (
228+
<tr>
229+
<td colSpan={5} className="pt-4 pb-4 text-center">
230+
No shows found for {monthName} {year}
231+
</td>
232+
</tr>
233+
)}
234+
<tr>
235+
<th colSpan={5}>
236+
<hr className="border-0 border-t border-gray-400" />
237+
</th>
238+
</tr>
239+
</tbody>
240+
</table>
241+
<address className="m-2 italic">Apache/2.4.41 (Unix) Server at relisten.net Port 80</address>
242+
</div>
243+
);
244+
}

0 commit comments

Comments
 (0)