add webp thumbnail support

This commit is contained in:
2024-10-24 12:48:39 +08:00
parent aec2505786
commit da291d234c
9 changed files with 72 additions and 14 deletions

View File

@@ -25,22 +25,32 @@ RUN apt-get update && apt-get install -y \
&& rm -rf /var/lib/apt/lists/*
RUN cd ~ && \
curl -L "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.0.1.tar.gz" -o ffmpeg.tar.gz && \
curl -L "https://github.com/webmproject/libwebp/archive/refs/tags/v1.4.0.tar.gz" -o libwebp.tar.gz && \
tar -xzvf libwebp.tar.gz && \
cd libwebp-1.4.0 && \
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \
-DWEBP_LINK_STATIC=OFF -DCMAKE_INSTALL_PREFIX=/clib ../ && \
make -j$(grep -c ^processor /proc/cpuinfo) && make install && \
cd ~ && rm -rf libwebp-1.4.0 libwebp.tar.gz
RUN cd ~ && \
curl -L "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.1.tar.gz" -o ffmpeg.tar.gz && \
tar -xzvf ffmpeg.tar.gz && \
cd FFmpeg-n7.0.1 && \
cd FFmpeg-n7.1 && \
./configure --enable-pic --prefix=/clib --enable-shared --disable-static \
--enable-gpl --enable-version3 --disable-doc --disable-ffplay \
--disable-network --disable-autodetect --enable-zlib \
--disable-encoders --enable-encoder=mjpeg \
--disable-encoders --enable-encoder=mjpeg,libwebp \
--disable-muxers --enable-muxer=image2,image2pipe \
--disable-decoders --enable-decoder=mjpeg,png,gif \
--disable-demuxers --enable-demuxer=image_jpeg_pipe,image_png_pipe,image_gif_pipe \
--disable-parsers --enable-parser=h264,png,gif \
--disable-bsfs --enable-bsf=dts2pts,null \
--disable-protocols --enable-protocol=async,concat,concatf,data,fd,file,md5,pipe,subfile \
--disable-devices --disable-filters --enable-filter=crop,pad,scale && \
--disable-devices --disable-filters --enable-filter=crop,pad,scale \
--enable-libwebp && \
make -j$(grep -c ^processor /proc/cpuinfo) && make install && \
cd ~ && rm -rf FFmpeg-n7.0.1 ffmpeg.tar.gz
cd ~ && rm -rf FFmpeg-n7.1 ffmpeg.tar.gz
RUN cd ~ && \
curl -L "https://github.com/curl/curl/releases/download/curl-8_8_0/curl-8.8.0.tar.gz" -o curl-8.8.0.tar.gz && \

13
api.yml
View File

@@ -188,6 +188,8 @@ components:
enable_server_timing:
type: boolean
description: Whether to enable server time tracking
thumbnail_format:
$ref: "#/components/schemas/ThumbnailFormat"
Config:
allOf:
- $ref: "#/components/schemas/ConfigOptional"
@@ -223,6 +225,7 @@ components:
- import_method
- max_import_img_count
- enable_server_timing
- thumbnail_format
ConfigUpdated:
description: result of updateConfig
type: object
@@ -1004,6 +1007,16 @@ components:
const: 4
- title: UpdateTagTranslation
const: 5
ThumbnailFormat:
description: Thumbnail format
type: integer
oneOf:
- title: JPEG
const: 0
description: MJPEG
- title: WEBP
const: 1
description: WEBP
ThumbnailMethod:
description: Thumbnail method
type: integer

View File

@@ -40,6 +40,7 @@ export type ConfigType = {
import_method: ImportMethod;
max_import_img_count: number;
enable_server_timing: boolean;
thumbnail_format: ThumbnailFormat;
};
export enum ThumbnailMethod {
@@ -47,6 +48,11 @@ export enum ThumbnailMethod {
FFMPEG_API,
}
export enum ThumbnailFormat {
JPEG,
WEBP,
}
export enum ImportMethod {
Copy,
CopyThenDelete,
@@ -173,6 +179,11 @@ export class Config {
get thumbnail_dir() {
return this._return_string("thumbnail_dir") || "./thumbnails";
}
get thumbnail_format() {
const n = this._return_number("thumbnail_format") || 0;
if (n < 0 || n > 1) return ThumbnailFormat.JPEG;
return n as ThumbnailFormat;
}
get remove_previous_gallery() {
return this._return_bool("remove_previous_gallery") || false;
}
@@ -282,6 +293,7 @@ export class Config {
import_method: this.import_method,
max_import_img_count: this.max_import_img_count,
enable_server_timing: this.enable_server_timing,
thumbnail_format: this.thumbnail_format,
};
}
}

View File

@@ -11,7 +11,7 @@ import {
ThumbnailGenMethod,
} from "../../../thumbnail/base.ts";
import { compareNum, isNumNaN, parseBigInt, sure_dir } from "../../../utils.ts";
import { ThumbnailMethod } from "../../../config.ts";
import { ThumbnailFormat, ThumbnailMethod } from "../../../config.ts";
import { fb_generate_thumbnail } from "../../../thumbnail/ffmpeg_binary.ts";
import {
get_file_response,
@@ -130,7 +130,11 @@ export const handler: Handlers = {
);
}
}
const output = generate_filename(b, f, cfg);
let fmt = m.cfg.thumbnail_format;
if (method == ThumbnailMethod.FFMPEG_API) {
fmt = ThumbnailFormat.JPEG;
}
const output = generate_filename(b, f, cfg, fmt);
if (!(await exists(output))) {
if (method === ThumbnailMethod.FFMPEG_BINARY) {
cfg.input = {
@@ -142,6 +146,7 @@ export const handler: Handlers = {
f.path,
output,
cfg,
fmt,
);
if (!re) {
return new Response("Failed to generate thumbnail.", {
@@ -171,7 +176,7 @@ export const handler: Handlers = {
const verify = u.searchParams.get("verify");
if (verify === null) {
const bs = new SortableURLSearchParams(
gen_thumbnail_config_params(cfg),
gen_thumbnail_config_params(cfg, fmt),
["verify"],
);
const tverify = encode(
@@ -188,8 +193,9 @@ export const handler: Handlers = {
const b = new URLSearchParams(bs.toString());
b.append("verify", tverify);
if (m.cfg.use_path_based_img_url) {
const ext = fmt == ThumbnailFormat.WEBP ? "webp" : "jpg";
return Response.redirect(
`${get_host(req)}/thumbnail/${b}/${f.id}.jpg`,
`${get_host(req)}/thumbnail/${b}/${f.id}.${ext}`,
);
}
return Response.redirect(

View File

@@ -47,6 +47,7 @@ export const handler: Handlers = {
const quality = await parse_int(u.searchParams.get("quality"), null);
const method = await parse_int(u.searchParams.get("method"), null);
const align = await parse_int(u.searchParams.get("align"), null);
const fmt = await parse_int(u.searchParams.get("fmt"), 0);
if (
width === null || height === null || quality === null ||
method === null || align === null
@@ -64,7 +65,7 @@ export const handler: Handlers = {
method,
align,
};
const output = generate_filename(b, f, cfg);
const output = generate_filename(b, f, cfg, fmt);
if (!(await exists(output))) {
return new Response("file not exists.", { status: 500 });
}

View File

@@ -49,6 +49,7 @@ export const handler: Handlers = {
const quality = await parse_int(search.get("quality"), null);
const method = await parse_int(search.get("method"), null);
const align = await parse_int(search.get("align"), null);
const fmt = await parse_int(search.get("fmt"), 0);
if (
width === null || height === null || quality === null ||
method === null || align === null
@@ -66,7 +67,7 @@ export const handler: Handlers = {
method,
align,
};
const output = generate_filename(b, f, cfg);
const output = generate_filename(b, f, cfg, fmt);
if (!(await exists(output))) {
return new Response("file not exists.", { status: 500 });
}

View File

@@ -37,7 +37,10 @@ export async function update_tag_translation(
}
sendEvent();
const file = isDocker() ? "/tmp/utt.lock" : "./utt.lock";
await Deno.writeTextFile(file, "", { create: true, signal: manager.aborts });
await Deno.writeTextFile(file, "", {
create: true,
signal: manager.aborts,
});
for (const d of f.data) {
await asyncForEach(Object.getOwnPropertyNames(d.data), async (name) => {
const tag = `${d.namespace}:${name}`;

View File

@@ -1,6 +1,7 @@
import { join } from "@std/path";
import { filterFilename } from "../utils.ts";
import type { EhFile } from "../db.ts";
import { ThumbnailFormat } from "../config.ts";
export enum ThumbnailGenMethod {
Unknown,
@@ -26,13 +27,17 @@ export type ThumbnailConfig = {
input?: { width: number; height: number };
};
export function gen_thumbnail_config_params(cfg: ThumbnailConfig) {
export function gen_thumbnail_config_params(
cfg: ThumbnailConfig,
fmt: ThumbnailFormat,
) {
return {
width: cfg.width.toString(),
height: cfg.height.toString(),
quality: cfg.quality.toString(),
method: cfg.method.toString(),
align: cfg.align.toString(),
fmt: fmt.toString(),
};
}
@@ -69,10 +74,12 @@ export function generate_filename(
base: string,
f: EhFile,
cfg: ThumbnailConfig,
fmt: ThumbnailFormat,
) {
let method = "";
let balign = "";
let align = "";
const ext = fmt == ThumbnailFormat.JPEG ? "jpg" : "webp";
switch (cfg.align) {
case ThumbnailAlign.Left:
balign = "-left";
@@ -100,7 +107,7 @@ export function generate_filename(
return join(
base,
filterFilename(
`${f.id}-${f.token}-${cfg.width}x${cfg.height}-q${cfg.quality}${method}${align}.jpg`,
`${f.id}-${f.token}-${cfg.width}x${cfg.height}-q${cfg.quality}${method}${align}.${ext}`,
),
);
}

View File

@@ -1,3 +1,4 @@
import { ThumbnailFormat } from "../config.ts";
import { ThumbnailAlign } from "./base.ts";
import { type ThumbnailConfig, ThumbnailGenMethod } from "./base.ts";
@@ -45,8 +46,10 @@ export async function fb_generate_thumbnail(
i: string,
o: string,
cfg: ThumbnailConfig,
fmt: ThumbnailFormat,
) {
let add = "";
const codec = fmt == ThumbnailFormat.WEBP ? "libwebp" : "mjpeg";
if (cfg.method == ThumbnailGenMethod.Cover) {
const size = cfg.input ?? await fb_get_size(i);
if (!size) return false;
@@ -90,6 +93,8 @@ export async function fb_generate_thumbnail(
"-n",
"-i",
i,
"-c",
codec,
"-vf",
add,
"-qmin",