diff --git a/public/locales/zh-CN/dashboard.json b/public/locales/zh-CN/dashboard.json
index 5d6400c..01848dc 100644
--- a/public/locales/zh-CN/dashboard.json
+++ b/public/locales/zh-CN/dashboard.json
@@ -213,7 +213,32 @@
"officePreviewServiceSrcB64Des": " Base64 编码后的文件 URL",
"officePreviewServiceName": "文件名",
"thumbnails": "缩略图",
- "localOnlyInfo": "以下设置只针对本机存储策略有效。",
+ "thumbnailDoc": "有关配置缩略图的更多信息,请参阅 <0>官方文档0>。",
+ "thumbnailDocLink":"https://docs.cloudreve.org/use/thumbnails",
+ "thumbnailBasic": "基本设置",
+ "generators": "生成器",
+ "thumbMaxSize": "最大原始文件尺寸",
+ "thumbMaxSizeDes": "可生成缩略图的最大原始文件的大小,超出此大小的文件不会生成缩略图",
+ "generatorProxyWarning": "默认情况下,非本机存储策略只会使用“存储策略原生”生成器。你可以通过开启“生成器代理”功能扩展第三方存储策略的缩略图能力。",
+ "policyBuiltin": "存储策略原生",
+ "policyBuiltinDes": "使用存储提供方原生的图像处理接口。对于本机和 S3 策略,这一生成器不可用,将会自动顺沿其他生成器。对于其他存储策略,支持的原始图像格式和大小限制请参考 Cloudreve 文档。",
+ "cloudreveBuiltin":"Cloudreve 内置",
+ "cloudreveBuiltinDes": "使用 Cloudreve 内置的图像处理能力,仅支持 PNG、JPEG、GIF 格式的图片。",
+ "libreOffice": "LibreOffice",
+ "libreOfficeDes": "使用 LibreOffice 生成 Office 文档的缩略图。这一生成器依赖于任一其他图像生成器(Cloudreve 内置 或 VIPS)。",
+ "vips": "VIPS",
+ "vipsDes": "使用 libvips 处理缩略图图像,支持更多图像格式,资源消耗更低。",
+ "thumbDependencyWarning": "LibreOffice 生成器依赖于 Cloudreve 内置 或 VIPS 生成器,请开启其中任一生成器。",
+ "ffmpeg": "FFmpeg",
+ "ffmpegDes": "使用 FFmpeg 生成视频缩略图。",
+ "executable": "可执行文件",
+ "executableDes": "第三方生成器可执行文件的地址或命令",
+ "executableTest": "测试",
+ "executableTestSuccess": "生成器正常,版本:{{version}}",
+ "generatorExts": "可用扩展名",
+ "generatorExtsDes": "此生成器可用的文件扩展名列表,多个请使用半角逗号 , 隔开",
+ "ffmpegSeek": "缩略图截取位置",
+ "ffmpegSeekDes": "定义缩略图截取的时间,推荐选择较小值以加速生成过程。如果超出视频实际长度,会导致缩略图截取失败。",
"thumbWidth": "缩略图宽度",
"thumbHeight": "缩略图高度",
"thumbSuffix": "缩略图文件后缀",
diff --git a/src/component/Admin/Setting/Image.js b/src/component/Admin/Setting/Image.js
index d39c785..3b1dee0 100644
--- a/src/component/Admin/Setting/Image.js
+++ b/src/component/Admin/Setting/Image.js
@@ -15,6 +15,7 @@ import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
import { Trans, useTranslation } from "react-i18next";
import Link from "@material-ui/core/Link";
+import ThumbGenerators from "./ThumbGenerators";
const useStyles = makeStyles((theme) => ({
root: {
@@ -58,6 +59,20 @@ export default function ImageSetting() {
wopi_enabled: "0",
wopi_endpoint: "",
wopi_session_timeout: "0",
+ thumb_builtin_enabled: "0",
+ thumb_vips_enabled: "0",
+ thumb_vips_exts: "",
+ thumb_ffmpeg_enabled: "0",
+ thumb_vips_path: "",
+ thumb_ffmpeg_path: "",
+ thumb_ffmpeg_exts: "",
+ thumb_ffmpeg_seek: "",
+ thumb_libreoffice_path: "",
+ thumb_libreoffice_enabled: "0",
+ thumb_libreoffice_exts: "",
+ thumb_proxy_enabled: "0",
+ thumb_proxy_policy: "[]",
+ thumb_max_src_size: "",
});
const handleChange = (name) => (event) => {
@@ -388,12 +403,26 @@ export default function ImageSetting() {
{t("thumbnails")}
+
+
+ {t("thumbnailBasic")}
+
-
-
@@ -510,6 +539,26 @@ export default function ImageSetting() {
+
+
+ {options.thumb_max_src_size !== "" && (
+
+ )}
+
+ {t("thumbMaxSizeDes")}
+
+
+
+
+
+
+ {t("generators")}
+
+
+
+
+ {t("generatorProxyWarning")}
+
+
+
+
+
+
+
diff --git a/src/component/Admin/Setting/Mail.js b/src/component/Admin/Setting/Mail.js
index 8d7e10f..1df2443 100644
--- a/src/component/Admin/Setting/Mail.js
+++ b/src/component/Admin/Setting/Mail.js
@@ -103,7 +103,7 @@ export default function Mail() {
const sendTestMail = () => {
setLoading(true);
- API.post("/admin/mailTest", {
+ API.post("/admin/test/mail", {
to: tesInput,
})
.then(() => {
diff --git a/src/component/Admin/Setting/ThumbGenerators.js b/src/component/Admin/Setting/ThumbGenerators.js
new file mode 100644
index 0000000..d0a72a0
--- /dev/null
+++ b/src/component/Admin/Setting/ThumbGenerators.js
@@ -0,0 +1,248 @@
+import React, { useCallback, useState } from "react";
+import { makeStyles } from "@material-ui/core/styles";
+import Accordion from "@material-ui/core/Accordion";
+import AccordionSummary from "@material-ui/core/AccordionSummary";
+import AccordionDetails from "@material-ui/core/AccordionDetails";
+import Checkbox from "@material-ui/core/Checkbox";
+import FormControlLabel from "@material-ui/core/FormControlLabel";
+import Typography from "@material-ui/core/Typography";
+import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
+import { useTranslation } from "react-i18next";
+import { useDispatch } from "react-redux";
+import { toggleSnackbar } from "../../../redux/explorer";
+import FormHelperText from "@material-ui/core/FormHelperText";
+import FormControl from "@material-ui/core/FormControl";
+import { Button, TextField } from "@material-ui/core";
+import InputAdornment from "@material-ui/core/InputAdornment";
+import API from "../../../middleware/Api";
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ width: "100%",
+ },
+ secondaryHeading: {
+ fontSize: theme.typography.pxToRem(15),
+ color: theme.palette.text.secondary,
+ },
+ column: {
+ flexBasis: "33.33%",
+ },
+ details: {
+ display: "block",
+ },
+}));
+
+const generators = [
+ {
+ name: "policyBuiltin",
+ des: "policyBuiltinDes",
+ readOnly: true,
+ },
+ {
+ name: "libreOffice",
+ des: "libreOfficeDes",
+ enableFlag: "thumb_libreoffice_enabled",
+ executableSetting: "thumb_libreoffice_path",
+ inputs: [
+ {
+ name: "thumb_libreoffice_exts",
+ label: "generatorExts",
+ des: "generatorExtsDes",
+ },
+ ],
+ },
+ {
+ name: "vips",
+ des: "vipsDes",
+ enableFlag: "thumb_vips_enabled",
+ executableSetting: "thumb_vips_path",
+ inputs: [
+ {
+ name: "thumb_vips_exts",
+ label: "generatorExts",
+ des: "generatorExtsDes",
+ },
+ ],
+ },
+ {
+ name: "ffmpeg",
+ des: "ffmpegDes",
+ enableFlag: "thumb_ffmpeg_enabled",
+ executableSetting: "thumb_ffmpeg_path",
+ inputs: [
+ {
+ name: "thumb_ffmpeg_exts",
+ label: "generatorExts",
+ des: "generatorExtsDes",
+ },
+ {
+ name: "thumb_ffmpeg_seek",
+ label: "ffmpegSeek",
+ des: "ffmpegSeekDes",
+ required: true,
+ },
+ ],
+ },
+ {
+ name: "cloudreveBuiltin",
+ des: "cloudreveBuiltinDes",
+ enableFlag: "thumb_builtin_enabled",
+ },
+];
+
+export default function ThumbGenerators({ options, setOptions }) {
+ const classes = useStyles();
+ const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
+ const [loading, setLoading] = useState(false);
+ const dispatch = useDispatch();
+ const ToggleSnackbar = useCallback(
+ (vertical, horizontal, msg, color) =>
+ dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
+ [dispatch]
+ );
+
+ const handleChange = (name) => (event) => {
+ setOptions({
+ ...options,
+ [name]: event.target.value,
+ });
+ };
+
+ const testExecutable = (name, executable) => {
+ setLoading(true);
+ API.post("/admin/test/thumb", {
+ name,
+ executable,
+ })
+ .then((response) => {
+ ToggleSnackbar(
+ "top",
+ "right",
+ t("executableTestSuccess", { version: response.data }),
+ "success"
+ );
+ })
+ .catch((error) => {
+ ToggleSnackbar("top", "right", error.message, "error");
+ })
+ .then(() => {
+ setLoading(false);
+ });
+ };
+
+ const handleEnableChange = (name) => (event) => {
+ const newOpts = {
+ ...options,
+ [name]: event.target.checked ? "1" : "0",
+ };
+ setOptions(newOpts);
+
+ if (
+ newOpts["thumb_libreoffice_enabled"] === "1" &&
+ newOpts["thumb_builtin_enabled"] === "0" &&
+ newOpts["thumb_vips_enabled"] === "0"
+ ) {
+ ToggleSnackbar(
+ "top",
+ "center",
+ t("thumbDependencyWarning"),
+ "warning"
+ );
+ }
+ };
+
+ return (
+
+ {generators.map((generator) => (
+
+ }
+ aria-label="Expand"
+ aria-controls="additional-actions1-content"
+ id="additional-actions1-header"
+ >
+ event.stopPropagation()}
+ onFocus={(event) => event.stopPropagation()}
+ control={
+
+ }
+ label={t(generator.name)}
+ disabled={generator.readOnly}
+ />
+
+
+
+ {t(generator.des)}
+
+ {generator.executableSetting && (
+
+
+
+
+ ),
+ }}
+ required
+ />
+
+ {t("executableDes")}
+
+
+ )}
+ {generator.inputs &&
+ generator.inputs.map((input) => (
+
+
+
+ {t(input.des)}
+
+
+ ))}
+
+
+ ))}
+
+ );
+}