mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-06-06 05:38:44 +08:00
Update /api/thumbnail
This commit is contained in:
@@ -123,7 +123,7 @@ export class Config {
|
|||||||
return this._return_string("ffmpeg_path") || "ffmpeg";
|
return this._return_string("ffmpeg_path") || "ffmpeg";
|
||||||
}
|
}
|
||||||
get thumbnail_method() {
|
get thumbnail_method() {
|
||||||
const n = this._return_number("thumbnail") || 0;
|
const n = this._return_number("thumbnail_method") || 0;
|
||||||
if (n < 0 || n > 1) return ThumbnailMethod.FFMPEG_BINARY;
|
if (n < 0 || n > 1) return ThumbnailMethod.FFMPEG_BINARY;
|
||||||
return n as ThumbnailMethod;
|
return n as ThumbnailMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,19 @@ import { get_task_manager } from "../../server.ts";
|
|||||||
import type { StatusData } from "../../server/status.ts";
|
import type { StatusData } from "../../server/status.ts";
|
||||||
import { return_data } from "../../server/utils.ts";
|
import { return_data } from "../../server/utils.ts";
|
||||||
import { check_ffmpeg_binary } from "../../thumbnail/ffmpeg_binary.ts";
|
import { check_ffmpeg_binary } from "../../thumbnail/ffmpeg_binary.ts";
|
||||||
|
import type * as FFMPEG_API from "../../thumbnail/ffmpeg_api.ts";
|
||||||
|
|
||||||
|
let ffmpeg_api: typeof FFMPEG_API | undefined;
|
||||||
|
|
||||||
|
async function check_ffmpeg_api() {
|
||||||
|
if (ffmpeg_api) return true;
|
||||||
|
try {
|
||||||
|
ffmpeg_api = await import("../../thumbnail/ffmpeg_api.ts");
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const handler: Handlers = {
|
export const handler: Handlers = {
|
||||||
async GET(_req, ctx) {
|
async GET(_req, ctx) {
|
||||||
@@ -12,6 +25,7 @@ export const handler: Handlers = {
|
|||||||
const ffmpeg_binary_enabled = await check_ffmpeg_binary(
|
const ffmpeg_binary_enabled = await check_ffmpeg_binary(
|
||||||
m.cfg.ffmpeg_path,
|
m.cfg.ffmpeg_path,
|
||||||
);
|
);
|
||||||
|
const ffmpeg_api_enabled = await check_ffmpeg_api();
|
||||||
const meilisearch_enabled = m.meilisearch !== undefined;
|
const meilisearch_enabled = m.meilisearch !== undefined;
|
||||||
const meilisearch =
|
const meilisearch =
|
||||||
is_authed && meilisearch_enabled && m.cfg.meili_host &&
|
is_authed && meilisearch_enabled && m.cfg.meili_host &&
|
||||||
@@ -24,6 +38,7 @@ export const handler: Handlers = {
|
|||||||
: undefined;
|
: undefined;
|
||||||
const no_user = m.db.get_user_count() === 0;
|
const no_user = m.db.get_user_count() === 0;
|
||||||
return return_data<StatusData>({
|
return return_data<StatusData>({
|
||||||
|
ffmpeg_api_enabled,
|
||||||
ffmpeg_binary_enabled,
|
ffmpeg_binary_enabled,
|
||||||
meilisearch_enabled,
|
meilisearch_enabled,
|
||||||
meilisearch,
|
meilisearch,
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import { exists } from "std/fs/exists.ts";
|
|||||||
import { get_task_manager } from "../../../server.ts";
|
import { get_task_manager } from "../../../server.ts";
|
||||||
import { parse_bool, parse_int } from "../../../server/parse_form.ts";
|
import { parse_bool, parse_int } from "../../../server/parse_form.ts";
|
||||||
import {
|
import {
|
||||||
|
gen_thumbnail_config_params,
|
||||||
generate_filename,
|
generate_filename,
|
||||||
|
parse_thumbnail_align,
|
||||||
|
parse_thumbnail_method,
|
||||||
ThumbnailConfig,
|
ThumbnailConfig,
|
||||||
ThumbnailGenMethod,
|
ThumbnailGenMethod,
|
||||||
} from "../../../thumbnail/base.ts";
|
} from "../../../thumbnail/base.ts";
|
||||||
@@ -18,6 +21,9 @@ import { get_host } from "../../../server/utils.ts";
|
|||||||
import pbkdf2Hmac from "pbkdf2-hmac";
|
import pbkdf2Hmac from "pbkdf2-hmac";
|
||||||
import { encode } from "std/encoding/base64.ts";
|
import { encode } from "std/encoding/base64.ts";
|
||||||
import { SortableURLSearchParams } from "../../../server/SortableURLSearchParams.ts";
|
import { SortableURLSearchParams } from "../../../server/SortableURLSearchParams.ts";
|
||||||
|
import type * as FFMPEG_API from "../../../thumbnail/ffmpeg_api.ts";
|
||||||
|
|
||||||
|
let ffmpeg_api: typeof FFMPEG_API | undefined;
|
||||||
|
|
||||||
export const handler: Handlers = {
|
export const handler: Handlers = {
|
||||||
async GET(req, ctx) {
|
async GET(req, ctx) {
|
||||||
@@ -39,11 +45,14 @@ export const handler: Handlers = {
|
|||||||
const height = await parse_int(u.searchParams.get("height"), null);
|
const height = await parse_int(u.searchParams.get("height"), null);
|
||||||
const quality = await parse_int(u.searchParams.get("quality"), 1);
|
const quality = await parse_int(u.searchParams.get("quality"), 1);
|
||||||
const force = await parse_bool(u.searchParams.get("force"), false);
|
const force = await parse_bool(u.searchParams.get("force"), false);
|
||||||
|
const tmethod = parse_thumbnail_method(u.searchParams.get("method"));
|
||||||
|
const align = parse_thumbnail_align(u.searchParams.get("align"));
|
||||||
const cfg: ThumbnailConfig = {
|
const cfg: ThumbnailConfig = {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
quality,
|
quality,
|
||||||
method: ThumbnailGenMethod.Unknown,
|
method: tmethod,
|
||||||
|
align: align,
|
||||||
};
|
};
|
||||||
if (width !== null && height !== null) {
|
if (width !== null && height !== null) {
|
||||||
cfg.width = width;
|
cfg.width = width;
|
||||||
@@ -51,16 +60,20 @@ export const handler: Handlers = {
|
|||||||
} else if (width !== null) {
|
} else if (width !== null) {
|
||||||
cfg.width = width;
|
cfg.width = width;
|
||||||
cfg.height = Math.floor(f.height / f.width * width);
|
cfg.height = Math.floor(f.height / f.width * width);
|
||||||
|
cfg.method = ThumbnailGenMethod.Unknown;
|
||||||
} else if (height !== null) {
|
} else if (height !== null) {
|
||||||
cfg.height = height;
|
cfg.height = height;
|
||||||
cfg.width = Math.floor(f.width / f.height * height);
|
cfg.width = Math.floor(f.width / f.height * height);
|
||||||
|
cfg.method = ThumbnailGenMethod.Unknown;
|
||||||
} else {
|
} else {
|
||||||
if (f.width > f.height) {
|
if (f.width > f.height) {
|
||||||
cfg.width = max;
|
cfg.width = max;
|
||||||
cfg.height = Math.floor(f.height / f.width * max);
|
cfg.height = Math.floor(f.height / f.width * max);
|
||||||
|
cfg.method = ThumbnailGenMethod.Unknown;
|
||||||
} else {
|
} else {
|
||||||
cfg.height = max;
|
cfg.height = max;
|
||||||
cfg.width = Math.floor(f.width / f.height * max);
|
cfg.width = Math.floor(f.width / f.height * max);
|
||||||
|
cfg.method = ThumbnailGenMethod.Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!force) {
|
if (!force) {
|
||||||
@@ -68,6 +81,9 @@ export const handler: Handlers = {
|
|||||||
return Response.redirect(`${get_host(req)}/api/file/${f.id}`);
|
return Response.redirect(`${get_host(req)}/api/file/${f.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (method === ThumbnailMethod.FFMPEG_BINARY) {
|
||||||
|
cfg.method = ThumbnailGenMethod.Unknown;
|
||||||
|
}
|
||||||
const output = generate_filename(b, f, cfg);
|
const output = generate_filename(b, f, cfg);
|
||||||
if (!(await exists(output))) {
|
if (!(await exists(output))) {
|
||||||
if (method === ThumbnailMethod.FFMPEG_BINARY) {
|
if (method === ThumbnailMethod.FFMPEG_BINARY) {
|
||||||
@@ -82,13 +98,32 @@ export const handler: Handlers = {
|
|||||||
status: 500,
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (method === ThumbnailMethod.FFMPEG_API) {
|
||||||
|
if (!ffmpeg_api) {
|
||||||
|
ffmpeg_api = await import(
|
||||||
|
"../../../thumbnail/ffmpeg_api.ts"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const re = await ffmpeg_api.fa_generate_thumbnail(
|
||||||
|
f.path,
|
||||||
|
output,
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
if (!re) {
|
||||||
|
return new Response("Failed to generate thumbnail.", {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const opts: GetFileResponseOptions = {};
|
const opts: GetFileResponseOptions = {};
|
||||||
if (m.cfg.img_verify_secret) {
|
if (m.cfg.img_verify_secret) {
|
||||||
const verify = u.searchParams.get("verify");
|
const verify = u.searchParams.get("verify");
|
||||||
if (verify === null) {
|
if (verify === null) {
|
||||||
const bs = new SortableURLSearchParams(u.search, ["verify"]);
|
const bs = new SortableURLSearchParams(
|
||||||
|
gen_thumbnail_config_params(cfg),
|
||||||
|
["verify"],
|
||||||
|
);
|
||||||
const tverify = encode(
|
const tverify = encode(
|
||||||
new Uint8Array(
|
new Uint8Array(
|
||||||
await pbkdf2Hmac(
|
await pbkdf2Hmac(
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import { Handlers } from "$fresh/server.ts";
|
|||||||
import { exists } from "std/fs/exists.ts";
|
import { exists } from "std/fs/exists.ts";
|
||||||
import { get_task_manager } from "../../server.ts";
|
import { get_task_manager } from "../../server.ts";
|
||||||
import { parse_int } from "../../server/parse_form.ts";
|
import { parse_int } from "../../server/parse_form.ts";
|
||||||
import {
|
import { generate_filename, ThumbnailConfig } from "../../thumbnail/base.ts";
|
||||||
generate_filename,
|
|
||||||
ThumbnailConfig,
|
|
||||||
ThumbnailGenMethod,
|
|
||||||
} from "../../thumbnail/base.ts";
|
|
||||||
import { sure_dir } from "../../utils.ts";
|
import { sure_dir } from "../../utils.ts";
|
||||||
import {
|
import {
|
||||||
get_file_response,
|
get_file_response,
|
||||||
@@ -50,34 +46,24 @@ export const handler: Handlers = {
|
|||||||
if (verify !== tverify) {
|
if (verify !== tverify) {
|
||||||
return new Response("verify is invalid.", { status: 400 });
|
return new Response("verify is invalid.", { status: 400 });
|
||||||
}
|
}
|
||||||
const max = await parse_int(u.searchParams.get("max"), 1200);
|
|
||||||
const width = await parse_int(u.searchParams.get("width"), null);
|
const width = await parse_int(u.searchParams.get("width"), null);
|
||||||
const height = await parse_int(u.searchParams.get("height"), null);
|
const height = await parse_int(u.searchParams.get("height"), null);
|
||||||
const quality = await parse_int(u.searchParams.get("quality"), 1);
|
const quality = await parse_int(u.searchParams.get("quality"), null);
|
||||||
const cfg: ThumbnailConfig = {
|
const method = await parse_int(u.searchParams.get("method"), null);
|
||||||
width: 0,
|
const align = await parse_int(u.searchParams.get("align"), null);
|
||||||
height: 0,
|
if (
|
||||||
quality,
|
width === null || height === null || quality === null ||
|
||||||
method: ThumbnailGenMethod.Unknown,
|
method === null || align === null
|
||||||
};
|
) {
|
||||||
if (width !== null && height !== null) {
|
return new Response("params is missing", { status: 400 });
|
||||||
cfg.width = width;
|
|
||||||
cfg.height = height;
|
|
||||||
} else if (width !== null) {
|
|
||||||
cfg.width = width;
|
|
||||||
cfg.height = Math.floor(f.height / f.width * width);
|
|
||||||
} else if (height !== null) {
|
|
||||||
cfg.height = height;
|
|
||||||
cfg.width = Math.floor(f.width / f.height * height);
|
|
||||||
} else {
|
|
||||||
if (f.width > f.height) {
|
|
||||||
cfg.width = max;
|
|
||||||
cfg.height = Math.floor(f.height / f.width * max);
|
|
||||||
} else {
|
|
||||||
cfg.height = max;
|
|
||||||
cfg.width = Math.floor(f.width / f.height * max);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const cfg: ThumbnailConfig = {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
method,
|
||||||
|
align,
|
||||||
|
};
|
||||||
const output = generate_filename(b, f, cfg);
|
const output = generate_filename(b, f, cfg);
|
||||||
if (!(await exists(output))) {
|
if (!(await exists(output))) {
|
||||||
return new Response("file not exists.", { status: 500 });
|
return new Response("file not exists.", { status: 500 });
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export type StatusData = {
|
export type StatusData = {
|
||||||
|
ffmpeg_api_enabled: boolean;
|
||||||
ffmpeg_binary_enabled: boolean;
|
ffmpeg_binary_enabled: boolean;
|
||||||
meilisearch_enabled: boolean;
|
meilisearch_enabled: boolean;
|
||||||
meilisearch?: {
|
meilisearch?: {
|
||||||
|
|||||||
@@ -22,20 +22,75 @@ export type ThumbnailConfig = {
|
|||||||
height: number;
|
height: number;
|
||||||
quality: number;
|
quality: number;
|
||||||
method: ThumbnailGenMethod;
|
method: ThumbnailGenMethod;
|
||||||
|
align: ThumbnailAlign;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function gen_thumbnail_config_params(cfg: ThumbnailConfig) {
|
||||||
|
return {
|
||||||
|
width: cfg.width.toString(),
|
||||||
|
height: cfg.height.toString(),
|
||||||
|
quality: cfg.quality.toString(),
|
||||||
|
method: cfg.method.toString(),
|
||||||
|
align: cfg.align.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse_thumbnail_method(s: string | null) {
|
||||||
|
if (s === null) return ThumbnailGenMethod.Unknown;
|
||||||
|
const t = s.toLowerCase();
|
||||||
|
switch (t) {
|
||||||
|
case "cover":
|
||||||
|
return ThumbnailGenMethod.Cover;
|
||||||
|
case "contain":
|
||||||
|
return ThumbnailGenMethod.Contain;
|
||||||
|
case "fill":
|
||||||
|
return ThumbnailGenMethod.Fill;
|
||||||
|
default:
|
||||||
|
return ThumbnailGenMethod.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse_thumbnail_align(s: string | null) {
|
||||||
|
if (s === null) return ThumbnailAlign.Left;
|
||||||
|
const t = s.toLowerCase();
|
||||||
|
switch (t) {
|
||||||
|
case "center":
|
||||||
|
return ThumbnailAlign.Center;
|
||||||
|
case "right":
|
||||||
|
case "bottom":
|
||||||
|
return ThumbnailAlign.Right;
|
||||||
|
default:
|
||||||
|
return ThumbnailAlign.Left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function generate_filename(
|
export function generate_filename(
|
||||||
base: string,
|
base: string,
|
||||||
f: EhFile,
|
f: EhFile,
|
||||||
cfg: ThumbnailConfig,
|
cfg: ThumbnailConfig,
|
||||||
) {
|
) {
|
||||||
let method = "";
|
let method = "";
|
||||||
|
let balign = "";
|
||||||
|
let align = "";
|
||||||
|
switch (cfg.align) {
|
||||||
|
case ThumbnailAlign.Left:
|
||||||
|
balign = "-left";
|
||||||
|
break;
|
||||||
|
case ThumbnailAlign.Center:
|
||||||
|
balign = "-center";
|
||||||
|
break;
|
||||||
|
case ThumbnailAlign.Right:
|
||||||
|
balign = "-right";
|
||||||
|
break;
|
||||||
|
}
|
||||||
switch (cfg.method) {
|
switch (cfg.method) {
|
||||||
case ThumbnailGenMethod.Cover:
|
case ThumbnailGenMethod.Cover:
|
||||||
method = "-cover";
|
method = "-cover";
|
||||||
|
align = balign;
|
||||||
break;
|
break;
|
||||||
case ThumbnailGenMethod.Contain:
|
case ThumbnailGenMethod.Contain:
|
||||||
method = "-contain";
|
method = "-contain";
|
||||||
|
align = balign;
|
||||||
break;
|
break;
|
||||||
case ThumbnailGenMethod.Fill:
|
case ThumbnailGenMethod.Fill:
|
||||||
method = "-fill";
|
method = "-fill";
|
||||||
@@ -44,7 +99,7 @@ export function generate_filename(
|
|||||||
return join(
|
return join(
|
||||||
base,
|
base,
|
||||||
filterFilename(
|
filterFilename(
|
||||||
`${f.id}-${f.token}-${cfg.width}x${cfg.height}-q${cfg.quality}${method}.jpg`,
|
`${f.id}-${f.token}-${cfg.width}x${cfg.height}-q${cfg.quality}${method}${align}.jpg`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// <reference lib="deno.unstable" />
|
/// <reference lib="deno.unstable" />
|
||||||
import { Struct } from "pwn/mod.ts";
|
import { Struct } from "pwn/mod.ts";
|
||||||
import { ThumbnailAlign, ThumbnailGenMethod } from "./base.ts";
|
import { ThumbnailAlign, ThumbnailConfig, ThumbnailGenMethod } from "./base.ts";
|
||||||
|
|
||||||
let libSuffix = "";
|
let libSuffix = "";
|
||||||
let libPrefix = "lib";
|
let libPrefix = "lib";
|
||||||
@@ -78,3 +78,25 @@ export async function gen_thumbnail(
|
|||||||
if (re.e) return get_error(ore);
|
if (re.e) return get_error(ore);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fa_generate_thumbnail(
|
||||||
|
i: string,
|
||||||
|
o: string,
|
||||||
|
cfg: ThumbnailConfig,
|
||||||
|
) {
|
||||||
|
let method = cfg.method;
|
||||||
|
if (method === ThumbnailGenMethod.Unknown) method = ThumbnailGenMethod.Fill;
|
||||||
|
const re = await gen_thumbnail(
|
||||||
|
i,
|
||||||
|
o,
|
||||||
|
cfg.width,
|
||||||
|
cfg.height,
|
||||||
|
method,
|
||||||
|
cfg.align,
|
||||||
|
cfg.quality,
|
||||||
|
);
|
||||||
|
if (re) {
|
||||||
|
console.error(re);
|
||||||
|
}
|
||||||
|
return re === undefined;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user