mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-02 13:14:29 -04:00
Add image downloading test
This commit is contained in:
parent
0a729ccf75
commit
51558db1b2
1
api/.gitignore
vendored
1
api/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
**/*.bun
|
**/*.bun
|
||||||
|
images
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import { mkdir, writeFile } from "node:fs/promises";
|
import { mkdir, writeFile } from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { encode } from "blurhash";
|
import { encode } from "blurhash";
|
||||||
import { SQL, type SQLWrapper, eq, getTableName, sql } from "drizzle-orm";
|
import { type SQL, eq, is, sql } from "drizzle-orm";
|
||||||
import type { PgColumn, PgTable } from "drizzle-orm/pg-core";
|
import { PgColumn, type PgTable } from "drizzle-orm/pg-core";
|
||||||
import { version } from "package.json";
|
import { version } from "package.json";
|
||||||
import type { PoolClient } from "pg";
|
import type { PoolClient } from "pg";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { type Transaction, db } from "~/db";
|
import { type Transaction, db } from "~/db";
|
||||||
import { mqueue } from "~/db/schema/queue";
|
import { mqueue } from "~/db/schema/mqueue";
|
||||||
import type { Image } from "~/models/utils";
|
import type { Image } from "~/models/utils";
|
||||||
|
|
||||||
export const imageDir = process.env.IMAGES_PATH ?? "./images";
|
export const imageDir = process.env.IMAGES_PATH ?? "./images";
|
||||||
await mkdir(imageDir, { recursive: true });
|
await mkdir(imageDir, { recursive: true });
|
||||||
|
|
||||||
|
export const defaultBlurhash = "000000";
|
||||||
|
|
||||||
type ImageTask = {
|
type ImageTask = {
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -35,19 +37,34 @@ export const enqueueOptImage = async (
|
|||||||
hasher.update(img.url);
|
hasher.update(img.url);
|
||||||
const id = hasher.digest().toString("hex");
|
const id = hasher.digest().toString("hex");
|
||||||
|
|
||||||
|
const cleanupColumn = (column: SQL) =>
|
||||||
|
// @ts-expect-error dialect is private
|
||||||
|
db.dialect.sqlToQuery(
|
||||||
|
sql.join(
|
||||||
|
column.queryChunks.map((x) => {
|
||||||
|
if (is(x, PgColumn)) {
|
||||||
|
return sql.identifier(x.name);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).sql;
|
||||||
|
|
||||||
const message: ImageTask =
|
const message: ImageTask =
|
||||||
"table" in img
|
"table" in img
|
||||||
? {
|
? {
|
||||||
id,
|
id,
|
||||||
url: img.url,
|
url: img.url,
|
||||||
table: getTableName(img.table),
|
// @ts-expect-error dialect is private
|
||||||
column: db.execute(img.column).getQuery().sql,
|
table: db.dialect.sqlToQuery(sql`${img.table}`).sql,
|
||||||
|
column: cleanupColumn(img.column),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
id,
|
id,
|
||||||
url: img.url,
|
url: img.url,
|
||||||
table: getTableName(img.column.table),
|
// @ts-expect-error dialect is private
|
||||||
column: img.column.name,
|
table: db.dialect.sqlToQuery(sql`${img.column.table}`).sql,
|
||||||
|
column: sql.identifier(img.column.name).value,
|
||||||
};
|
};
|
||||||
await tx.insert(mqueue).values({
|
await tx.insert(mqueue).values({
|
||||||
kind: "image",
|
kind: "image",
|
||||||
@ -58,7 +75,7 @@ export const enqueueOptImage = async (
|
|||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
source: img.url,
|
source: img.url,
|
||||||
blurhash: "",
|
blurhash: defaultBlurhash,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,7 +100,7 @@ export const processImages = async () => {
|
|||||||
const column = sql.raw(img.column);
|
const column = sql.raw(img.column);
|
||||||
|
|
||||||
await tx.execute(sql`
|
await tx.execute(sql`
|
||||||
update ${table} set ${column} = ${ret} where ${column}->'id' = '${item.id}'
|
update ${table} set ${column} = ${ret} where ${column}->'id' = ${sql.raw(`'"${img.id}"'::jsonb`)}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
await tx.delete(mqueue).where(eq(mqueue.id, item.id));
|
await tx.delete(mqueue).where(eq(mqueue.id, item.id));
|
||||||
@ -112,6 +129,7 @@ export const processImages = async () => {
|
|||||||
|
|
||||||
// start processing old tasks
|
// start processing old tasks
|
||||||
await processAll();
|
await processAll();
|
||||||
|
return () => client.release(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function downloadImage(id: string, url: string): Promise<string> {
|
async function downloadImage(id: string, url: string): Promise<string> {
|
||||||
@ -144,6 +162,7 @@ async function downloadImage(id: string, url: string): Promise<string> {
|
|||||||
|
|
||||||
const { data, info } = await image
|
const { data, info } = await image
|
||||||
.resize(32, 32, { fit: "inside" })
|
.resize(32, 32, { fit: "inside" })
|
||||||
|
.ensureAlpha()
|
||||||
.raw()
|
.raw()
|
||||||
.toBuffer({ resolveWithObject: true });
|
.toBuffer({ resolveWithObject: true });
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { t } from "elysia";
|
import { t } from "elysia";
|
||||||
import type { SeedSerie } from "~/models/serie";
|
import type { SeedSerie } from "~/models/serie";
|
||||||
import { getYear } from "~/utils";
|
import { getYear } from "~/utils";
|
||||||
import { enqueueOptImage } from "./images";
|
|
||||||
import { insertCollection } from "./insert/collection";
|
import { insertCollection } from "./insert/collection";
|
||||||
import { insertEntries } from "./insert/entries";
|
import { insertEntries } from "./insert/entries";
|
||||||
import { insertSeasons } from "./insert/seasons";
|
import { insertSeasons } from "./insert/seasons";
|
||||||
|
@ -4,3 +4,4 @@ export * from "./shows";
|
|||||||
export * from "./studios";
|
export * from "./studios";
|
||||||
export * from "./staff";
|
export * from "./staff";
|
||||||
export * from "./videos";
|
export * from "./videos";
|
||||||
|
export * from "./mqueue";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import jwt from "@elysiajs/jwt";
|
import jwt from "@elysiajs/jwt";
|
||||||
import { swagger } from "@elysiajs/swagger";
|
import { swagger } from "@elysiajs/swagger";
|
||||||
|
import { processImages } from "./controllers/seed/images";
|
||||||
import { migrate } from "./db";
|
import { migrate } from "./db";
|
||||||
import { app } from "./elysia";
|
import { app } from "./elysia";
|
||||||
import { comment } from "./utils";
|
import { comment } from "./utils";
|
||||||
@ -23,6 +24,9 @@ if (!secret) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run image processor task in background
|
||||||
|
processImages();
|
||||||
|
|
||||||
app
|
app
|
||||||
.use(jwt({ secret }))
|
.use(jwt({ secret }))
|
||||||
.use(
|
.use(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { processImages } from "~/controllers/seed/images";
|
||||||
import { db, migrate } from "~/db";
|
import { db, migrate } from "~/db";
|
||||||
import { shows, videos } from "~/db/schema";
|
import { mqueue, shows, videos } from "~/db/schema";
|
||||||
import { madeInAbyss, madeInAbyssVideo } from "~/models/examples";
|
import { madeInAbyss, madeInAbyssVideo } from "~/models/examples";
|
||||||
import { createSerie, createVideo, getSerie } from "./helpers";
|
import { createSerie, createVideo, getSerie } from "./helpers";
|
||||||
|
|
||||||
@ -8,10 +9,16 @@ import { createSerie, createVideo, getSerie } from "./helpers";
|
|||||||
await migrate();
|
await migrate();
|
||||||
await db.delete(shows);
|
await db.delete(shows);
|
||||||
await db.delete(videos);
|
await db.delete(videos);
|
||||||
|
await db.delete(mqueue);
|
||||||
|
|
||||||
const [_, vid] = await createVideo(madeInAbyssVideo);
|
const [_, vid] = await createVideo(madeInAbyssVideo);
|
||||||
console.log(vid);
|
console.log(vid);
|
||||||
const [__, ser] = await createSerie(madeInAbyss);
|
const [__, ser] = await createSerie(madeInAbyss);
|
||||||
console.log(ser);
|
console.log(ser);
|
||||||
|
|
||||||
|
await processImages();
|
||||||
|
|
||||||
const [___, got] = await getSerie(madeInAbyss.slug, { with: ["translations"] });
|
const [___, got] = await getSerie(madeInAbyss.slug, { with: ["translations"] });
|
||||||
console.log(got);
|
console.log(got);
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
31
api/tests/misc/images.test.ts
Normal file
31
api/tests/misc/images.test.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { beforeAll, describe, expect, it } from "bun:test";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { defaultBlurhash, processImages } from "~/controllers/seed/images";
|
||||||
|
import { db } from "~/db";
|
||||||
|
import { mqueue, shows, staff, studios, videos } from "~/db/schema";
|
||||||
|
import { madeInAbyss } from "~/models/examples";
|
||||||
|
import { createSerie } from "../helpers";
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await db.delete(shows);
|
||||||
|
await db.delete(studios);
|
||||||
|
await db.delete(staff);
|
||||||
|
await db.delete(videos);
|
||||||
|
await db.delete(mqueue);
|
||||||
|
|
||||||
|
await createSerie(madeInAbyss);
|
||||||
|
const release = await processImages();
|
||||||
|
// remove notifications to prevent other images to be downloaded (do not curl 20000 images for nothing)
|
||||||
|
release();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("images", () => {
|
||||||
|
it("Create a serie download images", async () => {
|
||||||
|
const ret = await db.query.shows.findFirst({
|
||||||
|
where: eq(shows.slug, madeInAbyss.slug),
|
||||||
|
});
|
||||||
|
expect(ret!.slug).toBe(madeInAbyss.slug);
|
||||||
|
expect(ret!.original.poster!.blurhash).toBeString();
|
||||||
|
expect(ret!.original.poster!.blurhash).not.toBe(defaultBlurhash);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user