Add images to anime

main
idylls 1 year ago
parent 7ffe5cbfe2
commit ac7bc2c8b4
Signed by: idylls
GPG Key ID: 8A7167CBC2CC9F0F

@ -169,3 +169,18 @@ table.work {
.hidden {
display: none;
}
.logs {
padding: 0 1rem;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
gap: 20px;
.log {
min-width: 400px;
min-height: 600px;
}
}

@ -8,9 +8,7 @@ const abort = (e: any) => {
const check = <T>(p: Promise<T>) => p.catch(abort);
const gen = async () => {
const bp = check(buildPages());
await bp;
await check(buildPages());
console.log("Generation done");
};

@ -25,23 +25,23 @@ const walkdir = async (path: string) => {
};
const RENDERERS = {
"x-meta": () => "",
"x-meta": async () => "",
"x-now": renderNow,
"x-logs": renderLogs,
"x-home-quote": renderHomeQuote,
"x-work": renderWork,
};
const renderElement = (el: HTMLElement) => {
} as Record<string, (el: HTMLElement) => Promise<string> | string>;
const renderElement = async (el: HTMLElement) => {
if (el.nodeType == NodeType.TEXT_NODE) {
return el.rawText;
}
const renderer = RENDERERS[el.tagName.toLowerCase()] ?? renderBasic;
return renderer(el);
return await renderer(el);
};
const renderBasic = (el: HTMLElement) => {
const renderBasic = async (el: HTMLElement) => {
const tag = el.tagName.toLowerCase();
let out = `<${tag}`;
@ -50,7 +50,9 @@ const renderBasic = (el: HTMLElement) => {
}
out += ">";
const children = el.childNodes.map(renderElement).join("");
const children = (await Promise.all(el.childNodes.map(renderElement))).join(
"",
);
out += children;
out += `</${tag}>`;
@ -76,7 +78,9 @@ const buildPage = async (pagePath: string) => {
};
})();
const els = parsed.childNodes.map(renderElement).join("");
const els = (await Promise.all(parsed.childNodes.map(renderElement))).join(
"",
);
const rendered = `
<!DOCTYPE html>
<html lang="en">

@ -1,4 +1,5 @@
import { isoDateStr } from "../util";
import { writeFile } from "node:fs/promises";
type LogBase = {
name: string;
@ -34,7 +35,14 @@ const byStatusSorted = (items: LogBase[]) => {
inProgress.sort((a, b) => +(a.started ?? 0) - +(b.started ?? 0));
const finished = items.filter((i) => status(i) == "finished");
finished.sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0));
finished.sort((a, b) => {
const r = (b.rating ?? 0) - (a.rating ?? 0);
if (r == 0) {
return a.name.localeCompare(b.name);
}
return r;
});
const wantTo = items.filter((i) => status(i) == "wantTo");
wantTo.sort((a, b) => a.name.localeCompare(b.name));
@ -47,6 +55,28 @@ const byStatusSorted = (items: LogBase[]) => {
type Show = LogBase & { season?: number };
const SHOWS: Show[] = [
{
name: "The Bear",
rating: 8,
},
{
name: "The Witcher",
rating: 8,
},
{
name: "Arcane",
rating: 10,
},
{
name: "Over the Garden Wall",
rating: 10,
},
{
name: "Castlevania",
rating: 10,
},
];
const ANIME: Show[] = [
{
name: "Kakegurui",
},
@ -63,7 +93,7 @@ const SHOWS: Show[] = [
name: "The Eminence in Shadow",
},
{
name: "Bluelock",
name: "Blue Lock",
},
{
name: "Violet Evergarden",
@ -88,97 +118,166 @@ const SHOWS: Show[] = [
{
name: "Bocchi the Rock!",
started: new Date("2023-01-12"),
season: 1,
},
{
name: "Blue Period",
started: new Date("2023-01-02"),
finished: new Date("2023-01-12"),
season: 1,
rating: 10,
},
{
name: "Chainsaw Man",
started: new Date("2022-12-01"),
finished: new Date("2023-01-04"),
season: 1,
rating: 7.5,
},
{
name: "Do it Yourself!!",
started: new Date("2022-12-01"),
season: 1,
},
{
name: "Cyberpunk: Edgerunners",
season: 1,
rating: 10,
},
{
name: "The Bear",
rating: 8,
},
{
name: "The Witcher",
rating: 8,
},
{
name: "Arcane",
rating: 10,
},
{
name: "Castlevania",
rating: 10,
},
{
name: "Over the Garden Wall",
rating: 10,
},
];
const renderAnime = () => {
const renderSection = (title: string, items: Show[]) => {
let out = `<section><h3>${title}</h3>`;
for (const a of items) {
out += `<div class="log anime"><h4>${a.name}`;
if (a.season) {
out += `, Season ${a.season}`;
const cacheAnimeArt = async (a: Pick<Show, "name">) => {
const query = `
query($name: String) {
Media(
search: $name,
) {
coverImage {
extraLarge
}
}
}
`;
const variables = {
name: a.name,
} as const;
const req = {
query,
variables,
} as const;
const f = await fetch("https://graphql.anilist.co/", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(req),
});
if (!f.ok) {
console.warn(`Failed to query anilist for ${a.name}`);
return null;
}
const resp = (await f.json()) as
| {
data: {
Media: {
coverImage: {
extraLarge: string;
};
};
};
}
| {
errors: unknown;
};
if ("errors" in resp) {
console.warn(`Invalid anilist query for ${a.name}`);
console.warn(resp);
return null;
}
const url = resp.data.Media.coverImage.extraLarge;
const f_ = await fetch(url);
if (!f.ok) {
console.warn(`Failed to fetch image for ${a.name}`);
console.warn(resp);
return null;
}
const resUrl = `/static/images/anime/${a.name
.toLowerCase()
.replaceAll(" ", "_")}.png`;
// IMPROVEMENT: can probably finagle writing directly in to a writable
// file stream somehow
await writeFile(`build${resUrl}`, await f_.arrayBuffer());
console.log(`Successfully fetched image for ${a.name}`);
return resUrl;
};
// IMPROVEMENT: separate out the async here
// IMPROVEMENT: keep anime and TV shows separate?
const renderShows = async () => {
const shows = [...SHOWS, ...ANIME];
const animeTitles = new Set(ANIME.map((a) => a.name));
const { wantTo, dropped, finished, inProgress } = byStatusSorted(shows);
const renderSection = async (title: string, items: Show[]) => {
let out = `<section>
<h3>${title}</h3>
<div class="full-bleed">
<div class="logs">`;
const sections = items.map(async (a) => {
let out_ = "";
out_ += `<div class="log anime">`;
if (animeTitles.has(a.name)) {
const picUrl = await cacheAnimeArt(a);
if (picUrl) {
out_ += `<img src="${picUrl}">`;
}
}
out += "</h4>";
out_ += `<div class="name">${a.name}</div>`;
if (a.started) {
out += `<div class="started">Started: ${isoDateStr(a.started)}</div>`;
out_ += `<div class="started">Started: ${isoDateStr(a.started)}</div>`;
}
if (a.finished) {
out += `<div class="finished">Finished: ${isoDateStr(
out_ += `<div class="finished">Finished: ${isoDateStr(
a.finished,
)}</div>`;
}
if (a.rating) {
out += `<div class="rating">Rating: ${a.rating} / 10</div>`;
out_ += `<div class="rating">Rating: ${a.rating} / 10</div>`;
}
out += "</div>";
}
out += "</section>";
out_ += "</div>";
return out_;
});
out += (await Promise.all(sections)).join("");
out += "</div></div></section>";
return out;
};
const { wantTo, dropped, finished, inProgress } = byStatusSorted(SHOWS);
return `
<section>
<h2>Anime / TV</h2>
${renderSection("Currently Watching", inProgress)}
${renderSection("Finished", finished)}
${renderSection("Dropped", dropped)}
${renderSection("Want to Watch", wantTo)}
${await renderSection("Currently Watching", inProgress)}
${await renderSection("Finished", finished)}
${await renderSection("Dropped", dropped)}
${await renderSection("Want to Watch", wantTo)}
</section>
`;
};
export const render = () => {
return renderAnime();
export const render = async () => {
return renderShows();
};

Loading…
Cancel
Save