diff --git a/app.py b/app.py index a9fdeae..5bfdbac 100644 --- a/app.py +++ b/app.py @@ -44,7 +44,7 @@ def paths_check(paths): if os.path.isabs(path): abs_path = path else: - abs_path = os.path.join(os.getcwd(), path) + abs_path = os.path.normpath(os.path.join(os.getcwd(), path)) if not os.path.exists(abs_path): print(f"{tag} The path '{abs_path}' will be ignored (value: {path}).") @@ -75,7 +75,7 @@ class AppUtils: allow_cors=False, enable_shutdown=False, sd_webui_dir: Optional[str] = None, - base="", + base: Optional[str]=None, export_fe_fn=False, ): """ @@ -88,6 +88,8 @@ class AppUtils: self.allow_cors = allow_cors self.enable_shutdown = enable_shutdown self.sd_webui_dir = sd_webui_dir + if base and not base.startswith('/'): + base = '/' + base self.base = base self.export_fe_fn = export_fe_fn if sd_webui_dir: @@ -138,7 +140,7 @@ class AppUtils: allow_cors=self.allow_cors, enable_shutdown=self.enable_shutdown, launch_mode="server", - base=self.base, + base= self.base, export_fe_fn=self.export_fe_fn, ) @@ -151,7 +153,7 @@ class AppUtils: # 用于在首页显示 @app.get("/") def index(): - if self.base and self.base != DEFAULT_BASE: + if isinstance(self.base, str): with open(index_html_path, "r", encoding="utf-8") as file: content = file.read().replace(DEFAULT_BASE, self.base) return Response(content=content, media_type="text/html") diff --git a/javascript/index.js b/javascript/index.js index 9f7c6d9..e1d8336 100644 --- a/javascript/index.js +++ b/javascript/index.js @@ -23,7 +23,7 @@ Promise.resolve().then(async () => { -` +`.replace(/\/infinite_image_browsing/g, (window.location.pathname + '/infinite_image_browsing').replace(/\/\//g, '/')) let containerSelector = '#infinite_image_browsing_container_wrapper' let shouldMaximize = true diff --git a/scripts/iib/api.py b/scripts/iib/api.py index 3b89c1d..d4398c9 100644 --- a/scripts/iib/api.py +++ b/scripts/iib/api.py @@ -96,8 +96,9 @@ async def verify_secret(request: Request): DEFAULT_BASE = "/infinite_image_browsing" def infinite_image_browsing_api(app: FastAPI, **kwargs): - pre = "/infinite_image_browsing" - + api_base = kwargs.get("base") if isinstance(kwargs.get("base"), str) else DEFAULT_BASE + fe_public_path = kwargs.get("fe_public_path") if isinstance(kwargs.get("fe_public_path"), str) else api_base + # print(f"IIB api_base:{api_base} fe_public_path:{fe_public_path}") if IIB_DEBUG: @app.exception_handler(Exception) async def exception_handler(request: Request, exc: Exception): @@ -133,7 +134,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): f"An exception occurred while processing {request.method} {request.url}: {exc}" ) - pre = kwargs.get("base") or DEFAULT_BASE + if kwargs.get("allow_cors"): app.add_middleware( CORSMiddleware, @@ -237,22 +238,13 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): def filter_allowed_files(files: List[FileInfoDict]): return [x for x in files if is_path_trusted(x["fullpath"])] - static_dir = f"{cwd}/vue/dist" - @app.get(pre + "/fe-static/{file_path:path}") - async def serve_static_file(file_path: str): - file_full_path = f"{static_dir}/{file_path}" - if file_path.endswith(".js"): - with open(file_full_path, "r", encoding="utf-8") as file: - content = file.read().replace(DEFAULT_BASE, pre) - return Response(content=content, media_type="text/javascript") - else: - return FileResponse(file_full_path) - @app.get(f"{pre}/hello") + + @app.get(f"{api_base}/hello") async def greeting(): return "hello" - @app.get(f"{pre}/global_setting", dependencies=[Depends(verify_secret)]) + @app.get(f"{api_base}/global_setting", dependencies=[Depends(verify_secret)]) async def global_setting(): all_custom_tags = [] @@ -284,7 +276,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): file_paths: List[str] @app.post( - pre + "/delete_files", + api_base + "/delete_files", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def delete_files(req: DeleteFilesReq): @@ -325,7 +317,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): dest_folder: str @app.post( - pre + "/mkdirs", + api_base + "/mkdirs", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def create_folders(req: CreateFoldersReq): @@ -340,7 +332,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): create_dest_folder: Optional[bool] = False @app.post( - pre + "/copy_files", + api_base + "/copy_files", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def copy_files(req: MoveFilesReq): @@ -360,7 +352,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): raise HTTPException(400, detail=error_msg) @app.post( - pre + "/move_files", + api_base + "/move_files", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def move_files(req: MoveFilesReq): @@ -394,7 +386,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): ) raise HTTPException(400, detail=error_msg) - @app.get(pre + "/files", dependencies=[Depends(verify_secret)]) + @app.get(api_base + "/files", dependencies=[Depends(verify_secret)]) async def get_target_folder_files(folder_path: str): files: List[FileInfoDict] = [] try: @@ -450,7 +442,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): return {"files": filter_allowed_files(files)} - @app.get(pre + "/image-thumbnail", dependencies=[Depends(verify_secret)]) + @app.get(api_base + "/image-thumbnail", dependencies=[Depends(verify_secret)]) async def thumbnail(path: str, t: str, size: str = "256x256"): check_path_trust(path) if not temp_path: @@ -483,7 +475,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): headers={"Cache-Control": "max-age=31536000", "ETag": hash}, ) - @app.get(pre + "/file", dependencies=[Depends(verify_secret)]) + @app.get(api_base + "/file", dependencies=[Depends(verify_secret)]) async def get_file(path: str, t: str, disposition: Optional[str] = None): filename = path import mimetypes @@ -516,12 +508,12 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): headers=headers, ) - @app.post(pre + "/send_img_path", dependencies=[Depends(verify_secret)]) + @app.post(api_base + "/send_img_path", dependencies=[Depends(verify_secret)]) async def api_set_send_img_path(path: str): send_img_path["value"] = path # 等待图片信息生成完成 - @app.get(pre + "/gen_info_completed", dependencies=[Depends(verify_secret)]) + @app.get(api_base + "/gen_info_completed", dependencies=[Depends(verify_secret)]) async def api_set_send_img_path(): for _ in range(30): # timeout 3s if send_img_path["value"] == "": # 等待setup里面生成完成 @@ -531,7 +523,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): await asyncio.sleep(0.1) return send_img_path["value"] == "" - @app.get(pre + "/image_geninfo", dependencies=[Depends(verify_secret)]) + @app.get(api_base + "/image_geninfo", dependencies=[Depends(verify_secret)]) async def image_geninfo(path: str): with Image.open(path) as img: if is_img_created_by_comfyui(img): @@ -551,7 +543,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): class GeninfoBatchReq(BaseModel): paths: List[str] - @app.post(pre + "/image_geninfo_batch", dependencies=[Depends(verify_secret)]) + @app.post(api_base + "/image_geninfo_batch", dependencies=[Depends(verify_secret)]) async def image_geninfo_batch(req: GeninfoBatchReq): res = {} for path in req.paths: @@ -562,7 +554,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): class CheckPathExistsReq(BaseModel): paths: List[str] - @app.post(pre + "/check_path_exists", dependencies=[Depends(verify_secret)]) + @app.post(api_base + "/check_path_exists", dependencies=[Depends(verify_secret)]) async def check_path_exists(req: CheckPathExistsReq): update_all_scanned_paths() res = {} @@ -570,13 +562,24 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): res[path] = os.path.exists(path) and is_path_trusted(path) return res - @app.get(pre) + @app.get(api_base) def index_bd(): - if pre != DEFAULT_BASE: + if fe_public_path: with open(index_html_path, "r", encoding="utf-8") as file: - content = file.read().replace(DEFAULT_BASE, pre) + content = file.read().replace(DEFAULT_BASE, fe_public_path) return Response(content=content, media_type="text/html") return FileResponse(index_html_path) + + static_dir = f"{cwd}/vue/dist" + @app.get(api_base + "/fe-static/{file_path:path}") + async def serve_static_file(file_path: str): + file_full_path = f"{static_dir}/{file_path}" + if file_path.endswith(".js"): + with open(file_full_path, "r", encoding="utf-8") as file: + content = file.read().replace(DEFAULT_BASE, fe_public_path) + return Response(content=content, media_type="text/javascript") + else: + return FileResponse(file_full_path) class PathsReq(BaseModel): paths: List[str] @@ -585,7 +588,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): path: str @app.post( - pre + "/open_folder", + api_base + "/open_folder", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) def open_folder_using_explore(req: OpenFolderReq): @@ -593,7 +596,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): raise HTTPException(status_code=403) open_folder(*os.path.split(req.path)) - @app.post(pre + "/shutdown") + @app.post(api_base + "/shutdown") async def shutdown_app(): # This API endpoint is mainly used as a sidecar in Tauri applications to shut down the application if not kwargs.get("enable_shutdown"): @@ -602,7 +605,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): return {"message": "Application is shutting down."} @app.post( - pre + "/zip", + api_base + "/zip", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) def zip_files(req: PathsReq): @@ -614,9 +617,9 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): create_zip_file(req.paths, file_path) return FileResponse(file_path, media_type="application/zip") - db_pre = pre + "/db" + db_api_base = api_base + "/db" - @app.get(db_pre + "/basic_info", dependencies=[Depends(verify_secret)]) + @app.get(db_api_base + "/basic_info", dependencies=[Depends(verify_secret)]) async def get_db_basic_info(): conn = DataBase.get_conn() img_count = DbImg.count(conn) @@ -629,7 +632,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): "expired_dirs": expired_dirs, } - @app.get(db_pre + "/expired_dirs", dependencies=[Depends(verify_secret)]) + @app.get(db_api_base + "/expired_dirs", dependencies=[Depends(verify_secret)]) async def get_db_expired(): conn = DataBase.get_conn() expired_dirs = Folder.get_expired_dirs(conn) @@ -639,7 +642,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): } @app.post( - db_pre + "/update_image_data", + db_api_base + "/update_image_data", dependencies=[Depends(verify_secret)], ) async def update_image_db_data(): @@ -665,7 +668,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): folder_paths: List[str] = None size: Optional[int] = 200 - @app.post(db_pre + "/search_by_substr", dependencies=[Depends(verify_secret)]) + @app.post(db_api_base + "/search_by_substr", dependencies=[Depends(verify_secret)]) async def search_by_substr(req: SearchBySubstrReq): if IIB_DEBUG: logger.info(req) @@ -694,7 +697,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): folder_paths: List[str] = None size: Optional[int] = 200 - @app.post(db_pre + "/match_images_by_tags", dependencies=[Depends(verify_secret)]) + @app.post(db_api_base + "/match_images_by_tags", dependencies=[Depends(verify_secret)]) async def match_image_by_tags(req: MatchImagesByTagsReq): if IIB_DEBUG: logger.info(req) @@ -714,7 +717,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): "cursor": next_cursor } - @app.get(db_pre + "/img_selected_custom_tag", dependencies=[Depends(verify_secret)]) + @app.get(db_api_base + "/img_selected_custom_tag", dependencies=[Depends(verify_secret)]) async def get_img_selected_custom_tag(path: str): path = os.path.normpath(path) if not is_valid_image_path(path): @@ -733,7 +736,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): # tags = Tag.get_all_custom_tag() return ImageTag.get_tags_for_image(conn, img.id, type="custom") - @app.post(db_pre + "/get_image_tags", dependencies=[Depends(verify_secret)]) + @app.post(db_api_base + "/get_image_tags", dependencies=[Depends(verify_secret)]) async def get_img_tags(req: PathsReq): conn = DataBase.get_conn() return ImageTag.batch_get_tags_by_path(conn, req.paths) @@ -743,7 +746,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): tag_id: int @app.post( - db_pre + "/toggle_custom_tag_to_img", + db_api_base + "/toggle_custom_tag_to_img", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def toggle_custom_tag_to_img(req: ToggleCustomTagToImgReq): @@ -786,7 +789,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): tag_id: int @app.post( - db_pre + "/batch_update_image_tag", + db_api_base + "/batch_update_image_tag", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def batch_update_image_tag(req: BatchUpdateImageReq): @@ -828,7 +831,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): tag_name: str @app.post( - db_pre + "/add_custom_tag", + db_api_base + "/add_custom_tag", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def add_custom_tag(req: AddCustomTagReq): @@ -841,7 +844,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): tag_id: str @app.post( - db_pre + "/remove_custom_tag", + db_api_base + "/remove_custom_tag", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def remove_custom_tag(req: RemoveCustomTagReq): @@ -854,7 +857,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): tag_id: str @app.post( - db_pre + "/remove_custom_tag_from_img", + db_api_base + "/remove_custom_tag_from_img", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def remove_custom_tag_from_img(req: RemoveCustomTagFromReq): @@ -869,7 +872,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): type: Optional[ExtraPathType] @app.post( - f"{db_pre}/extra_paths", + f"{db_api_base}/extra_paths", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def create_extra_path(extra_path: ExtraPathModel): @@ -884,7 +887,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): conn.commit() @app.get( - f"{db_pre}/extra_paths", + f"{db_api_base}/extra_paths", dependencies=[Depends(verify_secret)], ) async def read_extra_paths(): @@ -892,7 +895,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): return ExtraPath.get_extra_paths(conn) @app.delete( - f"{db_pre}/extra_paths", + f"{db_api_base}/extra_paths", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def delete_extra_path(extra_path: ExtraPathModel): @@ -902,7 +905,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs): @app.post( - f"{db_pre}/rebuild_index", + f"{db_api_base}/rebuild_index", dependencies=[Depends(verify_secret), Depends(write_permission_required)], ) async def rebuild_index(): diff --git a/scripts/iib_setup.py b/scripts/iib_setup.py index 90733a8..930aaef 100644 --- a/scripts/iib_setup.py +++ b/scripts/iib_setup.py @@ -7,6 +7,7 @@ from scripts.iib.logger import logger from fastapi import FastAPI import gradio as gr +from modules.shared import cmd_opts """ api函数声明和启动分离方便另外一边被外部调用 @@ -71,7 +72,14 @@ def on_ui_tabs(): def on_app_started(_: gr.Blocks, app: FastAPI) -> None: # 第一个参数是SD-WebUI传进来的gr.Blocks,但是不需要使用 - infinite_image_browsing_api(app) + DEFAULT_BASE = "/infinite_image_browsing" + fe_public_path = None + subpath = vars(cmd_opts).get("subpath") + + if subpath: + base = "/" + subpath + "/" + DEFAULT_BASE + fe_public_path = base.replace('//', '/') + infinite_image_browsing_api(app, fe_public_path = fe_public_path) script_callbacks.on_ui_tabs(on_ui_tabs) diff --git a/vue/index.tpl.js b/vue/index.tpl.js index 873875c..f78d2a6 100644 --- a/vue/index.tpl.js +++ b/vue/index.tpl.js @@ -3,7 +3,7 @@ Promise.resolve().then(async () => { * This is a file generated using `yarn build`. * If you want to make changes, please modify `index.tpl.js` and run the command to generate it again. */ - const html = `__built_html__` + const html = `__built_html__`.replace(/\/infinite_image_browsing/g, (window.location.pathname + '/infinite_image_browsing').replace(/\/\//g, '/')) let containerSelector = '#infinite_image_browsing_container_wrapper' let shouldMaximize = true