diff --git a/components/Stars.tsx b/components/Stars.tsx index 3f4949b..27f0ccd 100644 --- a/components/Stars.tsx +++ b/components/Stars.tsx @@ -4,7 +4,6 @@ import IconStarFilled from "https://deno.land/x/tabler_icons_tsx@0.0.3/tsx/star- export const Star = ( { max = 5, rating = 3 }: { max?: number; rating: number }, ) => { - console.log({ max, rating }); return (
= { body: JSON.stringify({ tmdbId: m.id }), }); const j = await res.json(); - console.log("Selected", { movie, m, j }); state.visible.value = false; state.activeState.value = "normal"; + window.location.reload(); }, })), }; diff --git a/lib/cache/cache.ts b/lib/cache/cache.ts index c5aa77d..bbf3f29 100644 --- a/lib/cache/cache.ts +++ b/lib/cache/cache.ts @@ -30,13 +30,20 @@ const cache = await createCache(); export async function get(id: string, binary = false) { if (binary && !(cache instanceof Map)) { - return await cache.sendCommand("GET", [id], { + const cacheHit = await cache.sendCommand("GET", [id], { returnUint8Arrays: true, }) as T; + if (cacheHit) console.log("[cache] HIT ", { id }); + else console.log("[cache] MISS", { id }); + return cacheHit; } - return await cache.get(id) as T; + const cacheHit = await cache.get(id) as T; + if (cacheHit) console.log("[cache] HIT ", { id }); + else console.log("[cache] MISS", { id }); + return cacheHit; } export async function set(id: string, content: T) { + console.log("[cache] storing ", { id }); return await cache.set(id, content); } diff --git a/lib/documents.ts b/lib/documents.ts index fd68ee6..9c93906 100644 --- a/lib/documents.ts +++ b/lib/documents.ts @@ -1,9 +1,12 @@ import { unified } from "npm:unified"; import remarkParse from "npm:remark-parse"; -import remarkFrontmatter from "https://esm.sh/remark-frontmatter@4"; -import remarkRehype from "https://esm.sh/remark-rehype"; -import rehypeSanitize from "https://esm.sh/rehype-sanitize"; -import rehypeStringify from "https://esm.sh/rehype-stringify"; +import remarkStringify from "https://esm.sh/remark-stringify@10.0.3"; +import remarkFrontmatter, { + Root, +} from "https://esm.sh/remark-frontmatter@4.0.1"; +import remarkRehype from "https://esm.sh/remark-rehype@10.1.0"; +import rehypeSanitize from "https://esm.sh/rehype-sanitize@5.0.1"; +import rehypeStringify from "https://esm.sh/rehype-stringify@9.0.3"; import { parse } from "https://deno.land/std@0.194.0/yaml/mod.ts"; import * as cache from "@lib/cache/documents.ts"; @@ -38,7 +41,7 @@ export async function getDocuments(): Promise { return documents; } -export async function createDocument( +export function createDocument( name: string, content: string | ArrayBuffer, mediaType?: string, @@ -49,13 +52,11 @@ export async function createDocument( headers.append("Content-Type", mediaType); } - const response = await fetch(SILVERBULLET_SERVER + "/" + name, { + return fetch(SILVERBULLET_SERVER + "/" + name, { body: content, method: "PUT", headers, }); - - return response; } export async function getDocument(name: string): Promise { @@ -70,6 +71,33 @@ export async function getDocument(name: string): Promise { return text; } +export function transformDocument(input: string, cb: (r: Root) => Root) { + const out = unified() + .use(remarkParse) + .use(remarkFrontmatter, ["yaml"]) + .use(() => (tree) => { + return cb(tree); + }) + .use(remarkStringify) + .processSync(input); + + return String(out) + .replace("***\n", "---") + .replace("----------------", "---") + .replace("\n---", "---") + .replace(/^(date:[^'\n]*)'|'/gm, (match, p1, p2) => { + if (p1) { + // This is a line starting with date: followed by single quotes + return p1.replace(/'/gm, ""); + } else if (p2) { + return ""; + } else { + // This is a line with single quotes, but not starting with date: + return match; + } + }); +} + export function parseDocument(doc: string) { return unified() .use(remarkParse).use(remarkFrontmatter, ["yaml", "toml"]) diff --git a/lib/string.ts b/lib/string.ts new file mode 100644 index 0000000..1c94956 --- /dev/null +++ b/lib/string.ts @@ -0,0 +1,4 @@ +export function formatDate(date: Date): string { + const options = { year: "numeric", month: "long", day: "numeric" } as const; + return new Intl.DateTimeFormat("en-US", options).format(date); +} diff --git a/main.ts b/main.ts index 5ce3ec1..22525bb 100644 --- a/main.ts +++ b/main.ts @@ -11,6 +11,7 @@ import manifest from "./fresh.gen.ts"; import twindPlugin from "$fresh/plugins/twind.ts"; import twindConfig from "./twind.config.ts"; + await start(manifest, { plugins: [twindPlugin(twindConfig)], }); diff --git a/routes/api/movies/[name].ts b/routes/api/movies/[name].ts index c2f8442..683ae36 100644 --- a/routes/api/movies/[name].ts +++ b/routes/api/movies/[name].ts @@ -1,8 +1,14 @@ import { HandlerContext } from "$fresh/server.ts"; -import { createDocument, getDocument } from "@lib/documents.ts"; -import { fileExtension } from "https://deno.land/x/file_extension/mod.ts"; -import { parseMovie } from "@lib/movies.ts"; +import { + createDocument, + getDocument, + transformDocument, +} from "@lib/documents.ts"; +import { fileExtension } from "https://deno.land/x/file_extension@v2.1.0/mod.ts"; +import { type Movie, parseMovie } from "@lib/movies.ts"; import * as tmdb from "@lib/tmdb.ts"; +import { parse, stringify } from "https://deno.land/std@0.194.0/yaml/mod.ts"; +import { formatDate } from "@lib/string.ts"; function safeFileName(inputString: string): string { // Convert the string to lowercase @@ -25,6 +31,45 @@ export async function getMovie(name: string) { return movie; } +async function updateMovieMetadata( + name: string, + metadata: Partial, +) { + const docId = `Media/movies/${name}.md`; + + const currentDoc = await getDocument(docId); + if (!currentDoc) return; + + const newDoc = transformDocument(currentDoc, (root) => { + const frontmatterNode = root.children.find((c) => c.type === "yaml"); + + const frontmatter = frontmatterNode?.value as string; + + if (frontmatter) { + const value = parse(frontmatter) as Movie["meta"]; + + if (metadata.author && !value.author) { + value.author = metadata.author; + } + + if (metadata.image && !value.image) { + value.image = metadata.image; + } + + if (metadata.date && !value.date) { + value.date = formatDate(metadata.date); + } + frontmatterNode.value = stringify(value); + } + + return root; + }); + + const response = await createDocument(docId, newDoc); + + return response; +} + export const handler = async ( _req: Request, _ctx: HandlerContext, @@ -32,8 +77,9 @@ export const handler = async ( const headers = new Headers(); headers.append("Content-Type", "application/json"); + const movie = await getMovie(_ctx.params.name); + if (_req.method === "GET") { - const movie = await getMovie(_ctx.params.name); return new Response(JSON.stringify(movie)); } @@ -46,25 +92,41 @@ export const handler = async ( status: 400, }); } + const movieDetails = await tmdb.getMovie(tmdbId); - const movieCredits = await tmdb.getMovieCredits(tmdbId); + const movieCredits = !movie.meta.author && + await tmdb.getMovieCredits(tmdbId); const releaseDate = movieDetails.release_date; const posterPath = movieDetails.poster_path; const director = movieCredits?.crew?.filter?.((person) => person.job === "Director" - ); + )[0]; - if (posterPath) { + let finalPath = ""; + if (posterPath && !movie.meta.image) { const poster = await tmdb.getMoviePoster(posterPath); const extension = fileExtension(posterPath); - const finalPath = `Media/movies/images/${ + + finalPath = `Media/movies/images/${ safeFileName(name) }_cover.${extension}`; await createDocument(finalPath, poster); } - console.log({ releaseDate, director, posterPath }); + const metadata = {} as Movie["meta"]; + if (releaseDate) { + metadata.date = new Date(releaseDate); + } + if (finalPath) { + metadata.image = finalPath; + } + if (director) { + metadata.author = director.name; + } + + await updateMovieMetadata(name, metadata); + return new Response(JSON.stringify(movieCredits), { headers, }); diff --git a/routes/api/tmdb/credits/[id].ts b/routes/api/tmdb/credits/[id].ts index ba21fff..d1920e5 100644 --- a/routes/api/tmdb/credits/[id].ts +++ b/routes/api/tmdb/credits/[id].ts @@ -24,6 +24,8 @@ export const handler = async ( const headers = new Headers(); headers.append("Content-Type", "application/json"); + console.log("[api] getting movie credits"); + const cacheId = `/movie/credits/${id}`; const cachedResponse = await cache.get(cacheId); diff --git a/static/Frame 1(1).svg:Zone.Identifier b/static/Frame 1(1).svg:Zone.Identifier deleted file mode 100644 index 053d112..0000000 --- a/static/Frame 1(1).svg:Zone.Identifier +++ /dev/null @@ -1,3 +0,0 @@ -[ZoneTransfer] -ZoneId=3 -HostUrl=about:internet