feat: Add imagesDimensions field.
This commit is contained in:
parent
6166754636
commit
3fc91b60b7
|
|
@ -83,6 +83,7 @@
|
|||
"hast-util-to-html": "9.0.0",
|
||||
"i18next": "23.7.6",
|
||||
"i18next-resources-to-backend": "1.2.0",
|
||||
"image-size": "^1.0.2",
|
||||
"immer": "10.0.3",
|
||||
"ioredis": "5.3.2",
|
||||
"isomorphic-dompurify": "1.9.0",
|
||||
|
|
|
|||
|
|
@ -164,6 +164,9 @@ dependencies:
|
|||
i18next-resources-to-backend:
|
||||
specifier: 1.2.0
|
||||
version: 1.2.0
|
||||
image-size:
|
||||
specifier: ^1.0.2
|
||||
version: 1.0.2
|
||||
immer:
|
||||
specifier: 10.0.3
|
||||
version: 10.0.3
|
||||
|
|
@ -7564,6 +7567,14 @@ packages:
|
|||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/image-size@1.0.2:
|
||||
resolution: {integrity: sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
queue: 6.0.2
|
||||
dev: false
|
||||
|
||||
/immer@10.0.3:
|
||||
resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==}
|
||||
dev: false
|
||||
|
|
@ -10385,6 +10396,12 @@ packages:
|
|||
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
|
||||
dev: false
|
||||
|
||||
/queue@6.0.2:
|
||||
resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
dev: false
|
||||
|
||||
/quick-format-unescaped@4.0.4:
|
||||
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
|
||||
dev: false
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE "Metadata" ADD COLUMN "image_dimensions" JSONB;
|
||||
|
|
@ -18,6 +18,15 @@ model Metadata {
|
|||
ai_summary_ja String?
|
||||
ai_score Int?
|
||||
ai_score_reason String?
|
||||
// Expected JSON structure for the image_dimensions field:
|
||||
// {
|
||||
// "<uri>": {
|
||||
// "width": 123,
|
||||
// "height": 123
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
image_dimensions Json?
|
||||
@@id([uri])
|
||||
@@index([uri])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { getFeed } from "~/models/home.model"
|
||||
import { decoratePageForImageDimensions } from "~/queries/page.server"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const query = getQuery(req)
|
||||
|
|
@ -16,6 +17,11 @@ export async function GET(req: Request) {
|
|||
useHTML: false,
|
||||
topic: query.topic,
|
||||
})
|
||||
|
||||
for (const item of result.list) {
|
||||
await decoratePageForImageDimensions(item)
|
||||
}
|
||||
|
||||
const res = new NextServerResponse()
|
||||
return res.status(200).json(result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { PagesSortTypes, PageVisibilityEnum } from "~/lib/types"
|
||||
import { getPagesBySite } from "~/models/page.model"
|
||||
import { decoratePageForImageDimensions } from "~/queries/page.server"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const query = getQuery(req)
|
||||
|
|
@ -20,6 +21,11 @@ export async function GET(req: Request) {
|
|||
: [query.tags as string],
|
||||
}),
|
||||
})
|
||||
|
||||
for (const item of result.list) {
|
||||
await decoratePageForImageDimensions(item)
|
||||
}
|
||||
|
||||
const res = new NextServerResponse()
|
||||
return res.status(200).json(result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ export type ExpandedNote = NoteEntity & {
|
|||
summary?: string
|
||||
cover?: string
|
||||
images?: string[]
|
||||
imageDimensions?: Record<string, { width: number; height: number }>
|
||||
frontMatter?: Record<string, any>
|
||||
slug?: string
|
||||
audio?: string
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import https from "https"
|
||||
import url from "url"
|
||||
import AsyncLock from "async-lock"
|
||||
import sizeOf from "image-size"
|
||||
import { AnalyzeDocumentChain, loadSummarizationChain } from "langchain/chains"
|
||||
import { OpenAI } from "langchain/llms/openai"
|
||||
import { PromptTemplate } from "langchain/prompts"
|
||||
|
|
@ -7,7 +10,7 @@ import removeMarkdown from "remove-markdown"
|
|||
import { Metadata } from "@prisma/client"
|
||||
import { QueryClient } from "@tanstack/react-query"
|
||||
|
||||
import { toGateway } from "~/lib/ipfs-parser"
|
||||
import { toCid, toGateway } from "~/lib/ipfs-parser"
|
||||
import prisma from "~/lib/prisma.server"
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import * as pageModel from "~/models/page.model"
|
||||
|
|
@ -35,6 +38,145 @@ export const fetchGetPage = async (
|
|||
})
|
||||
}
|
||||
|
||||
export const getImageDimensionByUri = async (uri: string) => {
|
||||
return new Promise<{ width: number; height: number; uri: string }>(
|
||||
(resolve, reject) => {
|
||||
const options = url.parse(uri)
|
||||
|
||||
https.get(options, function (response) {
|
||||
const chunks: Buffer[] = []
|
||||
let totalLength = 0
|
||||
const MAX_BUFFER_LENGTH = 10240
|
||||
|
||||
response
|
||||
.on("data", function (chunk) {
|
||||
chunks.push(chunk)
|
||||
totalLength += chunk.length
|
||||
|
||||
if (totalLength > MAX_BUFFER_LENGTH) {
|
||||
response.destroy()
|
||||
const buffer = Buffer.concat(chunks)
|
||||
try {
|
||||
const dimension = sizeOf(buffer)
|
||||
if (dimension.width && dimension.height) {
|
||||
resolve({
|
||||
width: dimension.width,
|
||||
height: dimension.height,
|
||||
uri,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
reject("Not enough data for image size detection.")
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("end", function () {
|
||||
if (totalLength <= MAX_BUFFER_LENGTH) {
|
||||
const buffer = Buffer.concat(chunks)
|
||||
try {
|
||||
const dimension = sizeOf(buffer)
|
||||
if (dimension.width && dimension.height) {
|
||||
resolve({
|
||||
width: dimension.width,
|
||||
height: dimension.height,
|
||||
uri,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export const cacheCoverDimensions = async ({
|
||||
cid,
|
||||
uris,
|
||||
}: {
|
||||
cid: string
|
||||
uris: string[]
|
||||
}) => {
|
||||
if (!uris.length) return
|
||||
|
||||
const dimensions = (await cacheGet({
|
||||
key: ["coverDimensions", cid],
|
||||
allowEmpty: true,
|
||||
noUpdate: true,
|
||||
noExpire: true,
|
||||
getValueFun: async () => {
|
||||
let result
|
||||
|
||||
await lock.acquire(`coverDimensions_${cid}`, async () => {
|
||||
const meta = await prisma.metadata.findFirst({
|
||||
where: {
|
||||
uri: `ipfs://${cid}`,
|
||||
},
|
||||
})
|
||||
|
||||
const key: keyof Metadata = "image_dimensions"
|
||||
const dimensions = meta?.image_dimensions
|
||||
|
||||
if (dimensions) {
|
||||
result = dimensions
|
||||
} else {
|
||||
const newDimensions: Record<
|
||||
string,
|
||||
{ width: number; height: number }
|
||||
> = {}
|
||||
|
||||
console.time(`get image dimensions ${cid}`)
|
||||
for (const uri of uris) {
|
||||
const dimension = await getImageDimensionByUri(toGateway(uri))
|
||||
if (dimension) {
|
||||
newDimensions[uri] = dimension
|
||||
}
|
||||
}
|
||||
console.timeEnd(`get image dimensions ${cid}`)
|
||||
|
||||
if (meta) {
|
||||
await prisma.metadata.update({
|
||||
where: { uri: `ipfs://${cid}` },
|
||||
data: {
|
||||
[key]: newDimensions,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
await prisma.metadata.create({
|
||||
data: {
|
||||
uri: `ipfs://${cid}`,
|
||||
[key]: newDimensions,
|
||||
},
|
||||
})
|
||||
}
|
||||
result = newDimensions
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
},
|
||||
})) as Record<string, { width: number; height: number }> | undefined
|
||||
|
||||
return dimensions
|
||||
}
|
||||
|
||||
export const decoratePageForImageDimensions = async (
|
||||
page?: Awaited<ReturnType<typeof pageModel.getPage>> | null,
|
||||
) => {
|
||||
if (!page) return
|
||||
|
||||
const dimensions = await cacheCoverDimensions({
|
||||
cid: toCid(page?.metadata?.uri || ""),
|
||||
uris: page?.metadata?.content?.images || [],
|
||||
})
|
||||
|
||||
if (dimensions && Object.keys(dimensions).length && page?.metadata?.content) {
|
||||
page.metadata.content["imageDimensions"] = dimensions
|
||||
}
|
||||
}
|
||||
|
||||
export const prefetchGetPagesBySite = async (
|
||||
input: Parameters<typeof pageModel.getPagesBySite>[0],
|
||||
queryClient: QueryClient,
|
||||
|
|
|
|||
Loading…
Reference in New Issue