feat: add image folder scanning and safe output filename
- scan_image_folder(): recursively scans a directory for supported images (png/jpg/jpeg/webp, case-insensitive), returns List[ImageAsset], skips and logs unsupported files, raises OSError on bad path - make_safe_output_path(): builds <garment>_<print>.<ext> filename, appends _1/_2/... counter if destination already exists Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -8,7 +9,8 @@ SUPPORTED_IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp"}
|
|||||||
|
|
||||||
|
|
||||||
def is_supported_image(path):
|
def is_supported_image(path):
|
||||||
return path.suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
|
"""Return True if path has a supported image extension (case-insensitive)."""
|
||||||
|
return Path(path).suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -49,3 +51,65 @@ def get_output_dir():
|
|||||||
d = get_app_dir() / "output"
|
d = get_app_dir() / "output"
|
||||||
d.mkdir(exist_ok=True)
|
d.mkdir(exist_ok=True)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 文件夹扫描
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def scan_image_folder(folder) -> List:
|
||||||
|
"""Recursively scan *folder* for supported images.
|
||||||
|
|
||||||
|
Returns a list of ImageAsset objects (selected=True by default).
|
||||||
|
Skips unsupported files and logs them at DEBUG level.
|
||||||
|
Raises OSError if *folder* does not exist or is not a directory.
|
||||||
|
"""
|
||||||
|
from core.models import ImageAsset
|
||||||
|
|
||||||
|
folder = Path(folder)
|
||||||
|
if not folder.exists():
|
||||||
|
raise OSError("Folder not found: {}".format(folder))
|
||||||
|
if not folder.is_dir():
|
||||||
|
raise OSError("Path is not a directory: {}".format(folder))
|
||||||
|
|
||||||
|
assets = []
|
||||||
|
skipped_count = 0
|
||||||
|
|
||||||
|
for entry in sorted(folder.rglob("*")):
|
||||||
|
if not entry.is_file():
|
||||||
|
continue
|
||||||
|
if is_supported_image(entry):
|
||||||
|
assets.append(ImageAsset(path=entry))
|
||||||
|
else:
|
||||||
|
skipped_count += 1
|
||||||
|
logger.debug("Skipped unsupported file: %s", entry)
|
||||||
|
|
||||||
|
if skipped_count:
|
||||||
|
logger.info(
|
||||||
|
"Skipped %d unsupported file(s) in %s", skipped_count, folder
|
||||||
|
)
|
||||||
|
logger.info("Found %d image(s) in %s", len(assets), folder)
|
||||||
|
return assets
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 安全输出文件名
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def make_safe_output_path(output_dir, garment_path, print_path, output_format="PNG"):
|
||||||
|
"""Return an output Path that will not overwrite an existing file.
|
||||||
|
|
||||||
|
Filename pattern : <garment_stem>_<print_stem>.<ext>
|
||||||
|
If that path exists, appends _1, _2, ... until a free slot is found.
|
||||||
|
"""
|
||||||
|
output_dir = Path(output_dir)
|
||||||
|
ext = ".png" if output_format.upper() == "PNG" else ".jpg"
|
||||||
|
stem = "{}_{}".format(Path(garment_path).stem, Path(print_path).stem)
|
||||||
|
|
||||||
|
candidate = output_dir / (stem + ext)
|
||||||
|
counter = 1
|
||||||
|
while candidate.exists():
|
||||||
|
candidate = output_dir / ("{}_{}{}".format(stem, counter, ext))
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
return candidate
|
||||||
|
|||||||
@@ -160,20 +160,20 @@
|
|||||||
|
|
||||||
任务:
|
任务:
|
||||||
|
|
||||||
- [ ] 先读取 `src/services/file_service.py` 现有内容
|
- [x] 先读取 `src/services/file_service.py` 现有内容
|
||||||
- [ ] 完善 `src/services/file_service.py`
|
- [x] 完善 `src/services/file_service.py`
|
||||||
- [ ] 支持递归扫描衣服图片文件夹
|
- [x] 支持递归扫描衣服图片文件夹
|
||||||
- [ ] 支持递归扫描印花图片文件夹
|
- [x] 支持递归扫描印花图片文件夹
|
||||||
- [ ] 支持格式:PNG、JPG、JPEG、WEBP
|
- [x] 支持格式:PNG、JPG、JPEG、WEBP
|
||||||
- [ ] 忽略不支持格式并记录日志
|
- [x] 忽略不支持格式并记录日志
|
||||||
- [ ] 保留来源子文件夹信息
|
- [x] 保留来源子文件夹信息
|
||||||
- [ ] 生成安全输出文件名
|
- [x] 生成安全输出文件名
|
||||||
|
|
||||||
验收:
|
验收:
|
||||||
|
|
||||||
- [ ] 可以扫描包含子文件夹的中文路径
|
- [x] 可以扫描包含子文件夹的中文路径
|
||||||
- [ ] 不修改原始素材文件
|
- [x] 不修改原始素材文件
|
||||||
- [ ] 不支持文件格式不会导致扫描失败
|
- [x] 不支持文件格式不会导致扫描失败
|
||||||
|
|
||||||
## 4. 模板服务
|
## 4. 模板服务
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user