feat: add initial recommendation data
This commit is contained in:
@ -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;
|
||||
|
Reference in New Issue
Block a user