From f3942325ffed97968792afbda958fe57e99fc499 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 23 Nov 2025 20:48:38 +0800 Subject: [PATCH] update --- index.html | 137 +++++++++++++++++++++++++++++++ main.py | 231 +++++++++++++++++++++++------------------------------ 2 files changed, 235 insertions(+), 133 deletions(-) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..dab37c9 --- /dev/null +++ b/index.html @@ -0,0 +1,137 @@ + + + + + + 画廊下载管理器 + + + +
+
+

🎨 画廊下载管理器

+

管理您的画廊下载任务

+
+ +
+ + + +
+ + +
+ + + + \ No newline at end of file diff --git a/main.py b/main.py index eda065b..2df8b2c 100644 --- a/main.py +++ b/main.py @@ -6,11 +6,12 @@ from pathlib import Path from typing import Dict, Any, List import asyncio import httpx +import shutil import aiofiles from fastapi import FastAPI, HTTPException, BackgroundTasks from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import HTMLResponse +from fastapi.responses import HTMLResponse, FileResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel import uvicorn @@ -96,17 +97,25 @@ def get_all_galleries() -> List[GalleryInfo]: downloaded_count = 0 if 'all_images' in data: - for filename, url in data['all_images'].items(): - image_path = gallery_dir / filename - if image_path.exists(): + # 获取目录下所有图片文件 + image_files = list(gallery_dir.glob("*.*")) + image_filenames = {file.stem for file in image_files if file.is_file() and file.name != "data.json"} + + # 检查JSON中每个图片是否有对应的实际文件(忽略后缀名) + for filename in data['all_images'].keys(): + # 移除可能的扩展名(如果有的话),只比较文件名主体 + filename_stem = Path(filename).stem + if filename_stem in image_filenames: downloaded_count += 1 - galleries.append(GalleryInfo( - title=data.get('title', gallery_dir.name), - path=str(gallery_dir), - total_images=data.get('total_images', 0), - downloaded_images=downloaded_count - )) + # 只显示未完成的任务(下载进度不是100%的) + if downloaded_count < data.get('total_images', 0): + galleries.append(GalleryInfo( + title=data.get('title', gallery_dir.name), + path=str(gallery_dir), + total_images=data.get('total_images', 0), + downloaded_images=downloaded_count + )) except Exception as e: logger.error(f"读取画廊数据失败 {gallery_dir}: {e}") @@ -130,7 +139,8 @@ async def download_single_image(client: httpx.AsyncClient, url: str, file_path: # 创建带后缀的文件路径 file_path_with_suffix = file_path.with_suffix('.' + suffix) - if file_path_with_suffix.exists(): + # 检查是否已存在(考虑所有可能的扩展名) + if check_image_exists(file_path): return True img_response = await client.get(real_img_url, timeout=DOWNLOAD_TIMEOUT) @@ -145,6 +155,25 @@ async def download_single_image(client: httpx.AsyncClient, url: str, file_path: logger.error(f"下载失败 {url}: {e}") return False +def check_image_exists(file_path: Path) -> bool: + """检查图片文件是否存在(忽略扩展名)""" + if file_path.exists(): + return True + + # 检查是否有相同文件名但不同扩展名的文件 + parent_dir = file_path.parent + stem = file_path.stem + + # 常见的图片扩展名 + image_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'} + + for ext in image_extensions: + potential_file = parent_dir / f"{stem}{ext}" + if potential_file.exists(): + return True + + return False + async def download_gallery_images(title: str) -> DownloadStatusResponse: safe_title = sanitize_filename(title) gallery_path = downloads_path / safe_title @@ -197,7 +226,8 @@ async def download_gallery_images(title: str) -> DownloadStatusResponse: for filename, url in all_images.items(): image_path = gallery_path / filename - if image_path.exists(): + # 使用新的检查方法,忽略扩展名 + if check_image_exists(image_path): download_status[title]["downloaded"] += 1 continue @@ -264,6 +294,46 @@ async def download_all_pending_galleries(): logger.info("批量下载任务完成") +def delete_completed_json_files(): + """删除已完成任务的JSON文件""" + downloads_path = Path(DOWNLOADS_DIR) + deleted_count = 0 + + if not downloads_path.exists(): + return deleted_count + + for gallery_dir in downloads_path.iterdir(): + if gallery_dir.is_dir(): + data_file = gallery_dir / "data.json" + if data_file.exists(): + try: + with open(data_file, 'r', encoding='utf-8') as f: + data = json.load(f) + + # 检查是否所有图片都已下载 + downloaded_count = 0 + if 'all_images' in data: + image_files = list(gallery_dir.glob("*.*")) + image_filenames = {file.stem for file in image_files if file.is_file() and file.name != "data.json"} + + for filename in data['all_images'].keys(): + filename_stem = Path(filename).stem + if filename_stem in image_filenames: + downloaded_count += 1 + + total_images = len(data.get('all_images', {})) + + # 如果所有图片都已下载,删除JSON文件 + if downloaded_count == total_images and total_images > 0: + data_file.unlink() + deleted_count += 1 + logger.info(f"已删除已完成任务的JSON文件: {gallery_dir.name}") + + except Exception as e: + logger.error(f"处理画廊目录失败 {gallery_dir}: {e}") + + return deleted_count + # 初始化 downloads_path = setup_downloads_directory() @@ -295,128 +365,9 @@ async def save_url_data(request: SaveDataRequest = None): logger.error(f"保存数据失败: {e}") raise HTTPException(status_code=500, detail=f"保存失败: {str(e)}") -@app.get("/", response_class=HTMLResponse) +@app.get("/") async def read_gallery_manager(): - return """ - - - - - - 画廊下载管理器 - - - -
-
-

🎨 画廊下载管理器

-

管理您的画廊下载任务

-
- -
- - -
- - -
- - - - - """ + return FileResponse("index.html") @app.get("/api/galleries") async def get_galleries(): @@ -440,6 +391,20 @@ async def download_gallery(title: str, background_tasks: BackgroundTasks): "title": title } +@app.post("/api/cleanup") +async def cleanup_completed_galleries(): + """清理已完成任务的JSON文件""" + try: + deleted_count = delete_completed_json_files() + return { + "status": "success", + "message": f"成功删除 {deleted_count} 个已完成任务的JSON文件", + "deleted_count": deleted_count + } + except Exception as e: + logger.error(f"清理JSON文件失败: {e}") + raise HTTPException(status_code=500, detail=f"清理失败: {str(e)}") + @app.get("/health") async def health_check(): return {"status": "healthy"}