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>官方文档。", + "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")} +
-
- {t("localOnlyInfo")} -
-
@@ -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)} + + + ))} + + + ))} +
+ ); +}