diff --git a/backend/app/api/admin/settings.py b/backend/app/api/admin/settings.py index 29e15f6..b9c3265 100644 --- a/backend/app/api/admin/settings.py +++ b/backend/app/api/admin/settings.py @@ -12,7 +12,11 @@ from app.core.runtime_settings import ( runtime_config_from_values, set_runtime_setting, ) -from app.core.pximg_reverse_proxy import DEFAULT_PXIMG_MIRROR_HOST, normalize_pximg_mirror_host +from app.core.pximg_reverse_proxy import ( + DEFAULT_PXIMG_MIRROR_HOST, + normalize_pximg_custom_mirror_host, + normalize_pximg_mirror_host, +) router = APIRouter() @@ -255,6 +259,7 @@ async def get_settings( "image_proxy": { "use_pixiv_cat": bool(runtime.image_proxy_use_pixiv_cat), "pximg_mirror_host": str(runtime.image_proxy_pximg_mirror_host or DEFAULT_PXIMG_MIRROR_HOST), + "extra_pximg_mirror_hosts": list(getattr(runtime, "image_proxy_extra_pximg_mirror_hosts", []) or []), }, "random": random_defaults, "security": {"hide_origin_url_in_public_json": bool(runtime.hide_origin_url_in_public_json)}, @@ -371,6 +376,28 @@ async def update_settings( raise ApiError(code=ErrorCode.BAD_REQUEST, message="Invalid image_proxy.pximg_mirror_host", status_code=400) updates.append(("image_proxy.pximg_mirror_host", str(host))) + if "extra_pximg_mirror_hosts" in image_proxy: + raw = image_proxy.get("extra_pximg_mirror_hosts") + if raw is None: + updates.append(("image_proxy.extra_pximg_mirror_hosts", [])) + else: + if not isinstance(raw, list): + raise ApiError(code=ErrorCode.BAD_REQUEST, message="Invalid image_proxy.extra_pximg_mirror_hosts", status_code=400) + candidates = _as_str_list(raw) + if len(candidates) > 200: + raise ApiError(code=ErrorCode.BAD_REQUEST, message="Invalid image_proxy.extra_pximg_mirror_hosts", status_code=400) + normalized: list[str] = [] + seen: set[str] = set() + for item in candidates: + host = normalize_pximg_custom_mirror_host(item) + if host is None: + raise ApiError(code=ErrorCode.BAD_REQUEST, message="Invalid image_proxy.extra_pximg_mirror_hosts", status_code=400) + if host in seen: + continue + seen.add(host) + normalized.append(host) + updates.append(("image_proxy.extra_pximg_mirror_hosts", normalized)) + random = body.get("random") if random is not None: if not isinstance(random, dict): diff --git a/backend/app/api/public/docs_page.py b/backend/app/api/public/docs_page.py index 36ee5a3..e20927c 100644 --- a/backend/app/api/public/docs_page.py +++ b/backend/app/api/public/docs_page.py @@ -19,6 +19,7 @@ def _build_docs_html(*, base_url: str) -> str: examples = { "img_default": u("/random"), + "img_proxy_cat": u("/random?proxy=i-pixiv-cat"), "img_pixiv_cat": u("/random?pixiv_cat=1"), "img_pixiv_re": u("/random?pixiv_cat=1&pximg_mirror_host=re"), "img_redirect": u("/random?redirect=1"), @@ -303,7 +304,8 @@ def _build_docs_html(*, base_url: str) -> str:
/status 为公开仪表盘:展示 API 状态、图库概览、/random 请求统计;/status.json 为机器可读 JSON。
{examples["status"]}
{examples["status_json"]}
- /wtf 为瀑布流:参数与 /random 完全一致(例如 r18/标签/分辨率/热度等),页面内部会自动加上 adaptive=1 与 redirect=1 来做屏幕适配与稳定链接。
+/wtf 为瀑布流:支持 /random 的全部过滤参数(例如 r18/标签/分辨率/热度/排除标签等),默认会补上 adaptive=1 以更适合不同屏幕。
+/wtf 额外支持布局参数:view=single|masonry|tiles,以及 wtf_mcols/wtf_mgap/wtf_tcols/wtf_tgap/wtf_tratio(仅影响页面布局)。另有 wtf_gender=girls|boys 与概要标签 @male/@female(仅 /wtf 端展开,用于快速“只看女生/只看男生”)。
{examples["wtf"]}
{examples["wtf_r18"]}
@@ -332,6 +334,15 @@ def _build_docs_html(*, base_url: str) -> str:
quality 会先抽样 N 张候选再按热度/分辨率打分选图。
+ proxypixiv_cat=1),并覆盖地区自动选择。cat/re/nl,也支持 pixiv-cat/i-pixiv-cat 等写法。pixiv_cat示例:R18 + 插画 + 竖图 + 250万像素以上 + 收藏≥2 + 评论≥5 + 标签包含 loli(直接出图)。
{examples["complex"]}
+
+ 管理端支持从文件批量导入图片链接:.txt(每行一个 URL)或 PixivBatchDownloader 导出的 .json。
+