feat: add initial recommendation data
This commit is contained in:
parent
517b1ba23d
commit
cc112b7554
@ -20,48 +20,18 @@ export type Props = {
|
||||
export const MainLayout = (
|
||||
{ children, url, title, context, searchResults }: Props,
|
||||
) => {
|
||||
const hasSearch = url.search.includes("q=");
|
||||
const _url = typeof url === "string" ? new URL(url) : url;
|
||||
const hasSearch = _url.search.includes("q=");
|
||||
|
||||
return (
|
||||
<div
|
||||
class="md:grid mx-auto"
|
||||
style={{ gridTemplateColumns: "200px 1fr", maxWidth: "1024px" }}
|
||||
>
|
||||
<Head>
|
||||
<style>{CSS}</style>
|
||||
<style>{KATEX_CSS}</style>
|
||||
{title &&
|
||||
<title>{title}</title>}
|
||||
</Head>
|
||||
<aside class="p-4 hidden md:block">
|
||||
<nav class="min-h-fit rounded-3xl p-3 grid gap-3 fixed t-0">
|
||||
{Object.values(resources).map((m) => {
|
||||
return (
|
||||
<a
|
||||
href={m.link}
|
||||
class={`flex items-center gap-2 ${
|
||||
m.link === url.pathname ? "bg-white text-black" : "text-white"
|
||||
} p-3 text-xl w-full rounded-2xl`}
|
||||
>
|
||||
{<Emoji class="w-6 h-6" name={m.emoji} />} {m.name}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</aside>
|
||||
<main
|
||||
class="py-5"
|
||||
style={{ fontFamily: "Work Sans" }}
|
||||
>
|
||||
{hasSearch && (
|
||||
<Search
|
||||
q={url.searchParams.get("q")}
|
||||
{...context}
|
||||
results={searchResults}
|
||||
/>
|
||||
)}
|
||||
{!hasSearch && children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
if (hasSearch) {
|
||||
return (
|
||||
<Search
|
||||
q={_url.searchParams.get("q")}
|
||||
{...context}
|
||||
results={searchResults}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
@ -1,10 +1,11 @@
|
||||
{
|
||||
"lock": false,
|
||||
"tasks": {
|
||||
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
|
||||
"start": "deno run -A --watch=static/,routes/ dev.ts",
|
||||
"update": "deno run -A -r https://fresh.deno.dev/update .",
|
||||
"build": "deno run -A dev.ts build",
|
||||
"preview": "deno run -A main.ts"
|
||||
"preview": "deno run -A main.ts",
|
||||
"update": "deno run -A -r https://fresh.deno.dev/update ."
|
||||
},
|
||||
"lint": {
|
||||
"rules": {
|
||||
@ -27,7 +28,7 @@
|
||||
"@islands": "./islands",
|
||||
"@islands/": "./islands/",
|
||||
"zod": "https://deno.land/x/zod@v3.21.4/mod.ts",
|
||||
"$fresh/": "https://deno.land/x/fresh@1.4.2/",
|
||||
"$fresh/": "https://deno.land/x/fresh@1.4.3/",
|
||||
"preact": "https://esm.sh/preact@10.15.1",
|
||||
"preact/": "https://esm.sh/preact@10.15.1/",
|
||||
"preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.2.1",
|
||||
@ -46,4 +47,4 @@
|
||||
"_fresh"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
dev.ts
3
dev.ts
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env -S deno run -A --watch=static/,routes/
|
||||
|
||||
import dev from "$fresh/dev.ts";
|
||||
import config from "./fresh.config.ts";
|
||||
|
||||
await dev(import.meta.url, "./main.ts");
|
||||
await dev(import.meta.url, "./main.ts", config);
|
||||
|
6
fresh.config.ts
Normal file
6
fresh.config.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { defineConfig } from "$fresh/server.ts";
|
||||
import twindPlugin from "$fresh/plugins/twind.ts";
|
||||
import twindConfig from "./twind.config.ts";
|
||||
export default defineConfig({
|
||||
plugins: [twindPlugin(twindConfig)],
|
||||
});
|
146
fresh.gen.ts
146
fresh.gen.ts
@ -4,42 +4,43 @@
|
||||
|
||||
import * as $0 from "./routes/_404.tsx";
|
||||
import * as $1 from "./routes/_app.tsx";
|
||||
import * as $2 from "./routes/_middleware.ts";
|
||||
import * as $3 from "./routes/admin/log/index.tsx";
|
||||
import * as $4 from "./routes/admin/performance/index.tsx";
|
||||
import * as $5 from "./routes/api/articles/[name].ts";
|
||||
import * as $6 from "./routes/api/articles/create/index.ts";
|
||||
import * as $7 from "./routes/api/articles/index.ts";
|
||||
import * as $8 from "./routes/api/auth/callback.ts";
|
||||
import * as $9 from "./routes/api/auth/login.ts";
|
||||
import * as $10 from "./routes/api/auth/logout.ts";
|
||||
import * as $11 from "./routes/api/cache/index.ts";
|
||||
import * as $12 from "./routes/api/images/index.ts";
|
||||
import * as $13 from "./routes/api/index.ts";
|
||||
import * as $14 from "./routes/api/logs.ts";
|
||||
import * as $15 from "./routes/api/movies/[name].ts";
|
||||
import * as $16 from "./routes/api/movies/enhance/[name].ts";
|
||||
import * as $17 from "./routes/api/movies/index.ts";
|
||||
import * as $18 from "./routes/api/query/index.ts";
|
||||
import * as $19 from "./routes/api/query/sync.ts";
|
||||
import * as $20 from "./routes/api/recipes/[name].ts";
|
||||
import * as $21 from "./routes/api/recipes/index.ts";
|
||||
import * as $22 from "./routes/api/resources.ts";
|
||||
import * as $23 from "./routes/api/series/[name].ts";
|
||||
import * as $24 from "./routes/api/series/enhance/[name].ts";
|
||||
import * as $25 from "./routes/api/series/index.ts";
|
||||
import * as $26 from "./routes/api/tmdb/[id].ts";
|
||||
import * as $27 from "./routes/api/tmdb/credits/[id].ts";
|
||||
import * as $28 from "./routes/api/tmdb/query.ts";
|
||||
import * as $29 from "./routes/articles/[name].tsx";
|
||||
import * as $30 from "./routes/articles/index.tsx";
|
||||
import * as $31 from "./routes/index.tsx";
|
||||
import * as $32 from "./routes/movies/[name].tsx";
|
||||
import * as $33 from "./routes/movies/index.tsx";
|
||||
import * as $34 from "./routes/recipes/[name].tsx";
|
||||
import * as $35 from "./routes/recipes/index.tsx";
|
||||
import * as $36 from "./routes/series/[name].tsx";
|
||||
import * as $37 from "./routes/series/index.tsx";
|
||||
import * as $2 from "./routes/_layout.tsx";
|
||||
import * as $3 from "./routes/_middleware.ts";
|
||||
import * as $4 from "./routes/admin/log/index.tsx";
|
||||
import * as $5 from "./routes/admin/performance/index.tsx";
|
||||
import * as $6 from "./routes/api/articles/[name].ts";
|
||||
import * as $7 from "./routes/api/articles/create/index.ts";
|
||||
import * as $8 from "./routes/api/articles/index.ts";
|
||||
import * as $9 from "./routes/api/auth/callback.ts";
|
||||
import * as $10 from "./routes/api/auth/login.ts";
|
||||
import * as $11 from "./routes/api/auth/logout.ts";
|
||||
import * as $12 from "./routes/api/cache/index.ts";
|
||||
import * as $13 from "./routes/api/images/index.ts";
|
||||
import * as $14 from "./routes/api/index.ts";
|
||||
import * as $15 from "./routes/api/logs.ts";
|
||||
import * as $16 from "./routes/api/movies/[name].ts";
|
||||
import * as $17 from "./routes/api/movies/enhance/[name].ts";
|
||||
import * as $18 from "./routes/api/movies/index.ts";
|
||||
import * as $19 from "./routes/api/query/index.ts";
|
||||
import * as $20 from "./routes/api/query/sync.ts";
|
||||
import * as $21 from "./routes/api/recipes/[name].ts";
|
||||
import * as $22 from "./routes/api/recipes/index.ts";
|
||||
import * as $23 from "./routes/api/resources.ts";
|
||||
import * as $24 from "./routes/api/series/[name].ts";
|
||||
import * as $25 from "./routes/api/series/enhance/[name].ts";
|
||||
import * as $26 from "./routes/api/series/index.ts";
|
||||
import * as $27 from "./routes/api/tmdb/[id].ts";
|
||||
import * as $28 from "./routes/api/tmdb/credits/[id].ts";
|
||||
import * as $29 from "./routes/api/tmdb/query.ts";
|
||||
import * as $30 from "./routes/articles/[name].tsx";
|
||||
import * as $31 from "./routes/articles/index.tsx";
|
||||
import * as $32 from "./routes/index.tsx";
|
||||
import * as $33 from "./routes/movies/[name].tsx";
|
||||
import * as $34 from "./routes/movies/index.tsx";
|
||||
import * as $35 from "./routes/recipes/[name].tsx";
|
||||
import * as $36 from "./routes/recipes/index.tsx";
|
||||
import * as $37 from "./routes/series/[name].tsx";
|
||||
import * as $38 from "./routes/series/index.tsx";
|
||||
import * as $$0 from "./islands/Counter.tsx";
|
||||
import * as $$1 from "./islands/IngredientsList.tsx";
|
||||
import * as $$2 from "./islands/KMenu.tsx";
|
||||
@ -56,42 +57,43 @@ const manifest = {
|
||||
routes: {
|
||||
"./routes/_404.tsx": $0,
|
||||
"./routes/_app.tsx": $1,
|
||||
"./routes/_middleware.ts": $2,
|
||||
"./routes/admin/log/index.tsx": $3,
|
||||
"./routes/admin/performance/index.tsx": $4,
|
||||
"./routes/api/articles/[name].ts": $5,
|
||||
"./routes/api/articles/create/index.ts": $6,
|
||||
"./routes/api/articles/index.ts": $7,
|
||||
"./routes/api/auth/callback.ts": $8,
|
||||
"./routes/api/auth/login.ts": $9,
|
||||
"./routes/api/auth/logout.ts": $10,
|
||||
"./routes/api/cache/index.ts": $11,
|
||||
"./routes/api/images/index.ts": $12,
|
||||
"./routes/api/index.ts": $13,
|
||||
"./routes/api/logs.ts": $14,
|
||||
"./routes/api/movies/[name].ts": $15,
|
||||
"./routes/api/movies/enhance/[name].ts": $16,
|
||||
"./routes/api/movies/index.ts": $17,
|
||||
"./routes/api/query/index.ts": $18,
|
||||
"./routes/api/query/sync.ts": $19,
|
||||
"./routes/api/recipes/[name].ts": $20,
|
||||
"./routes/api/recipes/index.ts": $21,
|
||||
"./routes/api/resources.ts": $22,
|
||||
"./routes/api/series/[name].ts": $23,
|
||||
"./routes/api/series/enhance/[name].ts": $24,
|
||||
"./routes/api/series/index.ts": $25,
|
||||
"./routes/api/tmdb/[id].ts": $26,
|
||||
"./routes/api/tmdb/credits/[id].ts": $27,
|
||||
"./routes/api/tmdb/query.ts": $28,
|
||||
"./routes/articles/[name].tsx": $29,
|
||||
"./routes/articles/index.tsx": $30,
|
||||
"./routes/index.tsx": $31,
|
||||
"./routes/movies/[name].tsx": $32,
|
||||
"./routes/movies/index.tsx": $33,
|
||||
"./routes/recipes/[name].tsx": $34,
|
||||
"./routes/recipes/index.tsx": $35,
|
||||
"./routes/series/[name].tsx": $36,
|
||||
"./routes/series/index.tsx": $37,
|
||||
"./routes/_layout.tsx": $2,
|
||||
"./routes/_middleware.ts": $3,
|
||||
"./routes/admin/log/index.tsx": $4,
|
||||
"./routes/admin/performance/index.tsx": $5,
|
||||
"./routes/api/articles/[name].ts": $6,
|
||||
"./routes/api/articles/create/index.ts": $7,
|
||||
"./routes/api/articles/index.ts": $8,
|
||||
"./routes/api/auth/callback.ts": $9,
|
||||
"./routes/api/auth/login.ts": $10,
|
||||
"./routes/api/auth/logout.ts": $11,
|
||||
"./routes/api/cache/index.ts": $12,
|
||||
"./routes/api/images/index.ts": $13,
|
||||
"./routes/api/index.ts": $14,
|
||||
"./routes/api/logs.ts": $15,
|
||||
"./routes/api/movies/[name].ts": $16,
|
||||
"./routes/api/movies/enhance/[name].ts": $17,
|
||||
"./routes/api/movies/index.ts": $18,
|
||||
"./routes/api/query/index.ts": $19,
|
||||
"./routes/api/query/sync.ts": $20,
|
||||
"./routes/api/recipes/[name].ts": $21,
|
||||
"./routes/api/recipes/index.ts": $22,
|
||||
"./routes/api/resources.ts": $23,
|
||||
"./routes/api/series/[name].ts": $24,
|
||||
"./routes/api/series/enhance/[name].ts": $25,
|
||||
"./routes/api/series/index.ts": $26,
|
||||
"./routes/api/tmdb/[id].ts": $27,
|
||||
"./routes/api/tmdb/credits/[id].ts": $28,
|
||||
"./routes/api/tmdb/query.ts": $29,
|
||||
"./routes/articles/[name].tsx": $30,
|
||||
"./routes/articles/index.tsx": $31,
|
||||
"./routes/index.tsx": $32,
|
||||
"./routes/movies/[name].tsx": $33,
|
||||
"./routes/movies/index.tsx": $34,
|
||||
"./routes/recipes/[name].tsx": $35,
|
||||
"./routes/recipes/index.tsx": $36,
|
||||
"./routes/series/[name].tsx": $37,
|
||||
"./routes/series/index.tsx": $38,
|
||||
},
|
||||
islands: {
|
||||
"./islands/Counter.tsx": $$0,
|
||||
|
@ -71,7 +71,7 @@ export const SearchResultItem = (
|
||||
) => {
|
||||
const doc = item.document;
|
||||
const resourceType = resources[doc.type];
|
||||
const href = (resourceType) ? `${resourceType.link}/${doc.id}` : "";
|
||||
const href = resourceType ? `${resourceType.link}/${doc.id}` : "";
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { OpenAI } from "https://deno.land/x/openai/mod.ts";
|
||||
import { OpenAI } from "https://deno.land/x/openai@1.4.2/mod.ts";
|
||||
import { OPENAI_API_KEY } from "@lib/env.ts";
|
||||
|
||||
const openAI = OPENAI_API_KEY && new OpenAI(OPENAI_API_KEY);
|
||||
@ -63,6 +63,28 @@ export async function extractAuthorName(content: string) {
|
||||
return author;
|
||||
}
|
||||
|
||||
export async function createKeywords(type: string, description: string) {
|
||||
if (!openAI) return;
|
||||
const chatCompletion = await openAI.createChatCompletion({
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{
|
||||
"role": "system",
|
||||
"content":
|
||||
`you create some general vibey keywords to use in a recommendation system based on a ${type} description`,
|
||||
},
|
||||
{ "role": "user", "content": description.slice(0, 2000) },
|
||||
{
|
||||
"role": "user",
|
||||
"content": "return a list of keywords seperated by commas",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return chatCompletion.choices[0].message.content?.toLowerCase().split(", ")
|
||||
.map((v) => v.replaceAll(" ", "-"));
|
||||
}
|
||||
|
||||
export async function createTags(content: string) {
|
||||
if (!openAI) return;
|
||||
const chatCompletion = await openAI.createChatCompletion({
|
||||
|
53
lib/recommendation.ts
Normal file
53
lib/recommendation.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import * as cache from "@lib/cache/cache.ts";
|
||||
import * as openai from "@lib/openai.ts";
|
||||
import { GenericResource } from "@lib/types.ts";
|
||||
import { parseRating } from "@lib/helpers.ts";
|
||||
|
||||
type RecommendationResource = {
|
||||
id: string;
|
||||
type: string;
|
||||
rating: number;
|
||||
tags?: string[];
|
||||
keywords?: string[];
|
||||
author?: string;
|
||||
year?: number;
|
||||
};
|
||||
|
||||
export async function createRecommendationResource(
|
||||
res: GenericResource,
|
||||
description?: string,
|
||||
) {
|
||||
const cacheId = `recommendations:${res.type}:${res.id}`;
|
||||
const resource: RecommendationResource = await cache.get(cacheId) || {
|
||||
id: res.id,
|
||||
type: res.type,
|
||||
rating: -1,
|
||||
};
|
||||
if (description && !resource.keywords) {
|
||||
const keywords = await openai.createKeywords(res.type, description);
|
||||
if (keywords?.length) {
|
||||
resource.keywords = keywords;
|
||||
}
|
||||
}
|
||||
|
||||
const { author, date, rating } = res.meta || {};
|
||||
|
||||
if (res?.tags) {
|
||||
resource.tags = res.tags;
|
||||
}
|
||||
|
||||
if (typeof rating !== "undefined") {
|
||||
resource.rating = parseRating(rating);
|
||||
}
|
||||
|
||||
if (author) {
|
||||
resource.author = author;
|
||||
}
|
||||
|
||||
if (date) {
|
||||
const d = typeof date === "string" ? new Date(date) : date;
|
||||
resource.year = d.getFullYear();
|
||||
}
|
||||
|
||||
cache.set(cacheId, JSON.stringify(resource));
|
||||
}
|
@ -12,6 +12,8 @@ export type Movie = {
|
||||
tags: string[];
|
||||
meta: {
|
||||
date: Date;
|
||||
tmdbId?: number;
|
||||
keywords?: string[];
|
||||
image: string;
|
||||
thumbnail?: string;
|
||||
average?: string;
|
||||
@ -26,6 +28,11 @@ export function renderMovie(movie: Movie) {
|
||||
meta.date = formatDate(meta.date) as unknown as Date;
|
||||
}
|
||||
|
||||
delete meta.thumbnail;
|
||||
delete meta.average;
|
||||
|
||||
const movieImage = ``;
|
||||
|
||||
return fixRenderedMarkdown(`${
|
||||
meta
|
||||
? `---
|
||||
@ -35,7 +42,11 @@ ${stringify(meta)}
|
||||
---`
|
||||
}
|
||||
# ${movie.name}
|
||||
${movie.meta.image ? `` : ""}
|
||||
${
|
||||
// So we do not add a new image to the description everytime we render
|
||||
(movie.meta.image && !movie.description.includes(movieImage))
|
||||
? movieImage
|
||||
: ""}
|
||||
${movie.tags.map((t) => `#${t}`).join(" ")}
|
||||
${movie.description}
|
||||
`);
|
||||
@ -103,6 +114,10 @@ const crud = createCrud<Movie>({
|
||||
hasThumbnails: true,
|
||||
});
|
||||
|
||||
export const getMovie = crud.read;
|
||||
export const getMovie = async (id: string) => {
|
||||
const movie = await crud.read(id);
|
||||
return movie;
|
||||
};
|
||||
|
||||
export const getAllMovies = crud.readAll;
|
||||
export const createMovie = crud.create;
|
||||
|
@ -15,6 +15,7 @@ export type Series = {
|
||||
date: Date;
|
||||
image: string;
|
||||
author: string;
|
||||
tmdbId?: number;
|
||||
rating: number;
|
||||
average?: string;
|
||||
thumbnail?: string;
|
||||
@ -22,12 +23,17 @@ export type Series = {
|
||||
};
|
||||
};
|
||||
|
||||
function renderSeries(movie: Series) {
|
||||
const meta = movie.meta;
|
||||
function renderSeries(series: Series) {
|
||||
const meta = series.meta;
|
||||
if ("date" in meta) {
|
||||
meta.date = formatDate(meta.date);
|
||||
}
|
||||
|
||||
delete meta.thumbnail;
|
||||
delete meta.average;
|
||||
|
||||
const movieImage = ``;
|
||||
|
||||
return fixRenderedMarkdown(`${
|
||||
meta
|
||||
? `---
|
||||
@ -36,10 +42,14 @@ ${stringify(meta)}
|
||||
: `---
|
||||
---`
|
||||
}
|
||||
# ${movie.name}
|
||||
${movie.meta.image ? `` : ""}
|
||||
${movie.tags.map((t) => `#${t}`).join(" ")}
|
||||
${movie.description}
|
||||
# ${series.name}
|
||||
${
|
||||
// So we do not add a new image to the description everytime we render
|
||||
(series.meta.image && !series.description.includes(movieImage))
|
||||
? movieImage
|
||||
: ""}
|
||||
${series.tags.map((t) => `#${t}`).join(" ")}
|
||||
${series.description}
|
||||
`);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { BadRequestError } from "@lib/errors.ts";
|
||||
import { resources } from "@lib/resources.ts";
|
||||
import { SearchResult } from "@lib/types.ts";
|
||||
import { getTypeSenseClient } from "@lib/typesense.ts";
|
||||
@ -15,9 +14,9 @@ type SearchParams = {
|
||||
query_by?: string;
|
||||
};
|
||||
|
||||
export function parseResourceUrl(_url: string): SearchParams | undefined {
|
||||
export function parseResourceUrl(_url: string | URL): SearchParams | undefined {
|
||||
try {
|
||||
const url = new URL(_url);
|
||||
const url = typeof _url === "string" ? new URL(_url) : _url;
|
||||
let query = url.searchParams.get("q") || "*";
|
||||
if (!query) {
|
||||
return undefined;
|
||||
|
@ -37,6 +37,7 @@ export interface TMDBSeries {
|
||||
export type GenericResource = {
|
||||
name: string;
|
||||
id: string;
|
||||
tags?: string[];
|
||||
type: keyof typeof resources;
|
||||
meta?: {
|
||||
image?: string;
|
||||
|
5
main.ts
5
main.ts
@ -8,8 +8,7 @@ import "$std/dotenv/load.ts";
|
||||
|
||||
import { start } from "$fresh/server.ts";
|
||||
import manifest from "./fresh.gen.ts";
|
||||
import config from "./fresh.config.ts";
|
||||
|
||||
import twindPlugin from "$fresh/plugins/twind.ts";
|
||||
import twindConfig from "./twind.config.ts";
|
||||
await start(manifest, config);
|
||||
|
||||
await start(manifest, { plugins: [twindPlugin(twindConfig)] });
|
||||
|
41
routes/_layout.tsx
Normal file
41
routes/_layout.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { LayoutProps } from "$fresh/server.ts";
|
||||
import { resources } from "@lib/resources.ts";
|
||||
import { CSS, KATEX_CSS } from "https://deno.land/x/gfm@0.2.5/mod.ts";
|
||||
import { Head } from "$fresh/runtime.ts";
|
||||
import { Emoji } from "@components/Emoji.tsx";
|
||||
|
||||
export default function MyLayout({ Component, url }: LayoutProps) {
|
||||
return (
|
||||
<div
|
||||
class="md:grid mx-auto"
|
||||
style={{ gridTemplateColumns: "200px 1fr", maxWidth: "1024px" }}
|
||||
>
|
||||
<Head>
|
||||
<style>{CSS}</style>
|
||||
<style>{KATEX_CSS}</style>
|
||||
</Head>
|
||||
<aside class="p-4 hidden md:block">
|
||||
<nav class="min-h-fit rounded-3xl p-3 grid gap-3 fixed t-0">
|
||||
{Object.values(resources).map((m) => {
|
||||
return (
|
||||
<a
|
||||
href={m.link}
|
||||
class={`flex items-center gap-2 ${
|
||||
m.link === url.pathname ? "bg-white text-black" : "text-white"
|
||||
} p-3 text-xl w-full rounded-2xl`}
|
||||
>
|
||||
{<Emoji class="w-6 h-6" name={m.emoji} />} {m.name}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</aside>
|
||||
<main
|
||||
class="py-5"
|
||||
style={{ fontFamily: "Work Sans" }}
|
||||
>
|
||||
<Component />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -25,8 +25,11 @@ export const handler: Handlers = {
|
||||
|
||||
const releaseDate = movieDetails.release_date;
|
||||
const posterPath = movieDetails.poster_path;
|
||||
const director =
|
||||
movieCredits?.crew?.filter?.((person) => person.job === "Director")[0];
|
||||
const director = movieCredits?.crew?.filter?.((person) =>
|
||||
person.job === "Director"
|
||||
)[0];
|
||||
|
||||
movieDetails.overview;
|
||||
|
||||
let finalPath = "";
|
||||
const name = movieDetails.title || movieDetails.original_title ||
|
||||
@ -41,7 +44,9 @@ export const handler: Handlers = {
|
||||
await createDocument(finalPath, poster);
|
||||
}
|
||||
|
||||
const metadata = {} as Movie["meta"];
|
||||
const metadata = {
|
||||
tmdbId,
|
||||
} as Movie["meta"];
|
||||
if (releaseDate) {
|
||||
metadata.date = new Date(releaseDate);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
NotFoundError,
|
||||
} from "@lib/errors.ts";
|
||||
import * as cache from "@lib/cache/cache.ts";
|
||||
import { createRecommendationResource } from "@lib/recommendation.ts";
|
||||
|
||||
const POST = async (
|
||||
req: Request,
|
||||
@ -42,8 +43,9 @@ const POST = async (
|
||||
movie.meta.date = new Date(releaseDate);
|
||||
}
|
||||
|
||||
const director =
|
||||
movieCredits?.crew?.filter?.((person) => person.job === "Director")[0];
|
||||
const director = movieCredits?.crew?.filter?.((person) =>
|
||||
person.job === "Director"
|
||||
)[0];
|
||||
if (director && !movie.meta.author) {
|
||||
movie.meta.author = director.name;
|
||||
}
|
||||
@ -57,6 +59,10 @@ const POST = async (
|
||||
];
|
||||
}
|
||||
|
||||
if (!movie.meta.tmdbId) {
|
||||
movie.meta.tmdbId = tmdbId;
|
||||
}
|
||||
|
||||
let finalPath = "";
|
||||
const posterPath = movieDetails.poster_path;
|
||||
if (posterPath && !movie.meta.image) {
|
||||
@ -72,6 +78,8 @@ const POST = async (
|
||||
|
||||
cache.del(`documents:Media:movies:${name}.md`);
|
||||
|
||||
createRecommendationResource(movie, movieDetails.overview);
|
||||
|
||||
return json(movie);
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,7 @@ export const handler: Handlers = {
|
||||
await createDocument(finalPath, poster);
|
||||
}
|
||||
|
||||
const metadata = {} as Series["meta"];
|
||||
const metadata = { tmdbId } as Series["meta"];
|
||||
if (releaseDate) {
|
||||
metadata.date = new Date(releaseDate);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { Handlers, PageProps, RouteContext } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { getMovie, Movie } from "@lib/resource/movies.ts";
|
||||
import { RecipeHero } from "@components/RecipeHero.tsx";
|
||||
@ -7,17 +7,12 @@ import { renderMarkdown } from "@lib/documents.ts";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
|
||||
export const handler: Handlers<Movie | null> = {
|
||||
async GET(_, ctx) {
|
||||
const movie = await getMovie(ctx.params.name);
|
||||
return ctx.render({ movie, session: ctx.state.session });
|
||||
},
|
||||
};
|
||||
|
||||
export default function Greet(
|
||||
export default async function Greet(
|
||||
props: PageProps<{ movie: Movie; session: Record<string, string> }>,
|
||||
ctx: RouteContext,
|
||||
) {
|
||||
const { movie, session } = props.data;
|
||||
const movie = await getMovie(ctx.params.name);
|
||||
const session = ctx.state.session;
|
||||
|
||||
const { author = "", date = "" } = movie.meta;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { getAllMovies, Movie } from "@lib/resource/movies.ts";
|
||||
import { ResourceCard } from "@components/Card.tsx";
|
||||
@ -8,26 +7,18 @@ import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
import { SearchResult } from "@lib/types.ts";
|
||||
import { PageProps } from "$fresh/server.ts";
|
||||
|
||||
export const handler: Handlers<
|
||||
{ movies: Movie[] | null; searchResults?: SearchResult }
|
||||
> = {
|
||||
async GET(req, ctx) {
|
||||
const movies = await getAllMovies();
|
||||
const searchParams = parseResourceUrl(req.url);
|
||||
const searchResults = searchParams &&
|
||||
await searchResource({ ...searchParams, type: "movie" });
|
||||
return ctx.render({
|
||||
movies: movies.sort((a, b) => a?.meta?.rating > b?.meta?.rating ? -1 : 1),
|
||||
searchResults,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default function Greet(
|
||||
export default async function Greet(
|
||||
props: PageProps<{ movies: Movie[] | null; searchResults: SearchResult }>,
|
||||
) {
|
||||
const { movies, searchResults } = props.data;
|
||||
const allMovies = await getAllMovies();
|
||||
const searchParams = parseResourceUrl(props.url);
|
||||
const searchResults = searchParams &&
|
||||
await searchResource({ ...searchParams, type: "movie" });
|
||||
const movies = allMovies.sort((a, b) =>
|
||||
a?.meta?.rating > b?.meta?.rating ? -1 : 1
|
||||
);
|
||||
|
||||
return (
|
||||
<MainLayout
|
||||
|
Loading…
x
Reference in New Issue
Block a user