import { parseDocument } from "@lib/documents.ts"; import { parse, stringify } from "yaml"; import { createCrud } from "@lib/crud.ts"; import { extractHashTags, formatDate } from "@lib/string.ts"; import { fixRenderedMarkdown } from "@lib/helpers.ts"; import { getThumbhash } from "@lib/cache/image.ts"; export type Series = { id: string; name: string; description: string; type: "series"; tags: string[]; meta: { date: Date; image: string; author: string; rating: number; thumbnail?: string; status: "not-seen" | "watch-again" | "finished"; }; }; function renderSeries(movie: Series) { const meta = movie.meta; if ("date" in meta) { meta.date = formatDate(meta.date); } return fixRenderedMarkdown(`${ meta ? `--- ${stringify(meta)} ---` : `--- ---` } # ${movie.name} ${movie.meta.image ? `![](${movie.meta.image})` : ""} ${movie.tags.map((t) => `#${t}`).join(" ")} ${movie.description} `); } export function parseSeries(original: string, id: string): Series { const doc = parseDocument(original); let meta = {} as Series["meta"]; let name = ""; const range = [Infinity, -Infinity]; for (const child of doc.children) { if (child.type === "yaml") { try { meta = (parse(child.value) || {}) as Series["meta"]; } catch (_) { // ignore here } if (meta["rating"] && typeof meta["rating"] === "string") { meta.rating = [...meta.rating?.matchAll("⭐")].length; } continue; } if ( child.type === "heading" && child.depth === 1 && !name && child.children.length === 1 && child.children[0].type === "text" ) { name = child.children[0].value; continue; } if (name) { const start = child.position?.start.offset || Infinity; const end = child.position?.end.offset || -Infinity; if (start < range[0]) range[0] = start; if (end > range[1]) range[1] = end; } } let description = original.slice(range[0], range[1]); const tags = extractHashTags(description); for (const tag of tags) { description = description.replace("#" + tag, ""); } return { type: "series", id, name, tags, description, meta, }; } const crud = createCrud({ prefix: "Media/series/", parse: parseSeries, hasThumbnails: true, }); export const getSeries = (id: string) => crud.read(id).then(async (serie) => { const imageUrl = serie.meta?.image; if (!imageUrl) return serie; const thumbhash = await getThumbhash({ url: imageUrl }); if (!thumbhash) return serie; return { ...serie, meta: { ...serie.meta, thumbnail: btoa(String.fromCharCode(...thumbhash)), }, }; }); export const getAllSeries = crud.readAll; export const createSeries = (series: Series) => { const content = renderSeries(series); return crud.create(series.id, content); };