Add cleanup functionality for older files and update environment configuration

- Introduced `IMGWK_CLEAN_OLDER_THAN_SEC` in the .env file to specify the age of files to be cleaned.
- Enhanced `_safe_rmtree_contents` and `_cleanup_dir` functions to conditionally remove files older than the specified duration.
- Updated worker status to include device provider information.
- Modified tray application title to reflect the current provider status.
This commit is contained in:
Your Name 2025-10-29 09:13:54 +09:00
parent 938c998cf3
commit 87f4551420
9 changed files with 160 additions and 32 deletions

1
.env
View File

@ -20,3 +20,4 @@ IMGWK_REMBG_PROVIDER=auto # auto|dml|cpu
IMGWK_NO_TRAY = 0 # IMGWK_NO_TRAY=1 설정 시 트레이 비활성화
IMGWK_CLEAN_OLDER_THAN_SEC = 600 # 5분이 지난 파일만 정리하기

View File

@ -0,0 +1,23 @@
# 큐 대기 한도(가득 차면 429)
IMGWK_MAX_PENDING=400
IMGWK_WORKER_READY_TIMEOUT_SEC = 120 # 워커 작시 READY 타임아웃
# 워커 롤링 임계치
IMGWK_ROLL_MAX_RSS_MB=3600
IMGWK_ROLL_MAX_JOBS=500
IMGWK_ROLL_MAX_UPTIME_SEC=3600
# 잡 타임아웃(초)
IMGWK_JOB_TIMEOUT_SEC=600
# 테스트: 2건마다 워커 재시작 (1=활성화)
IMGWK_TEST_ROLL_EVERY_2=0
IMGWK_OCR_PROVIDER=auto # auto|dml|cpu
IMGWK_MIGAN_PROVIDER=auto # auto|dml|cpu
IMGWK_REMBG_PROVIDER=auto # auto|dml|cpu
IMGWK_NO_TRAY = 0 # IMGWK_NO_TRAY=1 설정 시 트레이 비활성화
IMGWK_CLEAN_OLDER_THAN_SEC = 600 # 5분이 지난 파일만 정리하기

84
main.py
View File

@ -238,7 +238,12 @@ def _cleanup_runtime_files():
pass
def _safe_rmtree_contents(target_dir: str):
def _safe_rmtree_contents(target_dir: str, older_than_sec: int = 0):
"""대상 디렉터리 하위의 항목을 삭제.
older_than_sec > 0 이면, 현재시각 기준 해당 초보다 오래된 항목만 삭제한다.
디렉터리는 내부 파일을 조건에 따라 정리한 비어 있으면 삭제한다.
"""
try:
if not target_dir:
return
@ -251,13 +256,51 @@ def _safe_rmtree_contents(target_dir: str):
except Exception:
return
os.makedirs(td, exist_ok=True)
now_ts = time.time()
threshold = max(0, int(older_than_sec or 0))
def _remove_file_if_old(fp: str):
try:
if threshold <= 0:
os.remove(fp)
return
mt = os.path.getmtime(fp)
if (now_ts - mt) >= threshold:
os.remove(fp)
except Exception:
pass
def _cleanup_dir(dp: str):
try:
# 파일은 조건부 삭제
for root, dirs, files in os.walk(dp, topdown=False):
for fn in files:
_remove_file_if_old(os.path.join(root, fn))
# 하위 폴더는 비어 있으면 삭제(항상 안전)
for dn in dirs:
full = os.path.join(root, dn)
try:
if not os.listdir(full):
os.rmdir(full)
except Exception:
pass
# 최상위 폴더도 비어 있으면 삭제
try:
if os.path.isdir(dp) and not os.listdir(dp):
os.rmdir(dp)
except Exception:
pass
except Exception:
pass
for name in os.listdir(td):
p = os.path.join(td, name)
try:
if os.path.isdir(p):
shutil.rmtree(p, ignore_errors=True)
_cleanup_dir(p)
else:
os.remove(p)
_remove_file_if_old(p)
except Exception:
pass
except Exception:
@ -271,8 +314,12 @@ def _purge_temp_dirs():
work_dir = os.path.join(APP_DATA_DIR, "work")
output_dir = os.path.join(APP_DATA_DIR, "output")
outputs_dir = os.path.join(APP_DATA_DIR, "outputs")
try:
ttl = int(os.environ.get("IMGWK_CLEAN_OLDER_THAN_SEC", "300"))
except Exception:
ttl = 300
for d in (incoming_dir, work_dir, output_dir, outputs_dir):
_safe_rmtree_contents(d)
_safe_rmtree_contents(d, older_than_sec=max(0, ttl))
except Exception:
pass
@ -442,6 +489,8 @@ class WorkerManager:
# 실행 상태/성능 지표
self._running_jobs = 0
self._recent_total_ms = [] # 최근 작업 total_ms 집계
# 최근 추론 장치(DML/GPU/CPU) 요약
self._last_device = None
# 기본 토글/설정(필요 시 요청에서 오버라이드)
self._toggle_states: Dict[str, Any] = {
@ -621,6 +670,17 @@ class WorkerManager:
self._recent_total_ms.append(total_ms)
if len(self._recent_total_ms) > 100:
self._recent_total_ms = self._recent_total_ms[-100:]
# 최근 장치 정보 업데이트
try:
rr = job.result if isinstance(job.result, dict) else {}
dev = (rr.get("inpaint_device") or rr.get("device") or rr.get("provider") or "").lower()
if dev:
if ("dml" in dev) or ("directml" in dev) or ("gpu" in dev):
self._last_device = "dml"
elif "cpu" in dev:
self._last_device = "cpu"
except Exception:
pass
except Exception:
pass
except Exception:
@ -1079,12 +1139,23 @@ def worker_status():
ready = _worker.is_ready if _worker else False
avg_sec = None
active = False
provider = None
running_jobs = 0
pending_jobs = 0
try:
if _worker is not None:
active = bool(getattr(_worker, "_running_jobs", 0) > 0)
running_jobs = int(getattr(_worker, "_running_jobs", 0) or 0)
try:
pending_jobs = int(_worker.pending_jobs_count())
except Exception:
pending_jobs = 0
# 대기중이거나 실행중이면 ACTIVE
active = bool((running_jobs + pending_jobs) > 0)
arr = getattr(_worker, "_recent_total_ms", [])
if arr:
avg_sec = (sum(arr) / len(arr)) / 1000.0
# 최근 장치 표시(dml|cpu)
provider = getattr(_worker, "_last_device", None)
except Exception:
pass
return {
@ -1092,6 +1163,9 @@ def worker_status():
"pid": _worker.pid if _worker else None,
"active": active,
"avg_sec_per_image": avg_sec,
"provider": provider,
"running_jobs": running_jobs,
"pending_jobs": pending_jobs,
}

View File

@ -114,7 +114,7 @@ def worker_main(
try:
base_program = os.environ.get("PROGRAMDATA", r"C:\\ProgramData")
app_data_dir = os.path.join(base_program, "ImgWorker")
def _safe_rmtree_contents(target_dir: str):
def _safe_rmtree_contents(target_dir: str, older_than_sec: int = 300):
try:
if not target_dir:
return
@ -123,20 +123,34 @@ def worker_main(
if os.path.commonpath([base, td]) != base:
return
os.makedirs(td, exist_ok=True)
for name in os.listdir(td):
p = os.path.join(td, name)
now_ts = time.time()
thr = max(0, int(older_than_sec or 0))
def _remove_if_old(fp: str):
try:
if os.path.isdir(p):
import shutil as _sh
_sh.rmtree(p, ignore_errors=True)
else:
os.remove(p)
if thr <= 0:
os.remove(fp)
return
mt = os.path.getmtime(fp)
if (now_ts - mt) >= thr:
os.remove(fp)
except Exception:
pass
for root, dirs, files in os.walk(td, topdown=False):
for fn in files:
_remove_if_old(os.path.join(root, fn))
for dn in dirs:
full = os.path.join(root, dn)
try:
if not os.listdir(full):
os.rmdir(full)
except Exception:
pass
except Exception:
pass
for d in (os.path.join(app_data_dir, "incoming"), os.path.join(app_data_dir, "work"), os.path.join(app_data_dir, "output"), os.path.join(app_data_dir, "outputs")):
_safe_rmtree_contents(d)
_safe_rmtree_contents(d, older_than_sec=int(os.environ.get("IMGWK_CLEAN_OLDER_THAN_SEC", "300")))
except Exception:
pass
@ -290,20 +304,37 @@ def worker_main(
if task is None:
logger.info("Shutdown signal 수신 → 종료")
try:
# 종료 시 임시 폴더 정리
# 종료 시 임시 폴더 정리(5분 경과 파일만)
base_program = os.environ.get("PROGRAMDATA", r"C:\\ProgramData")
app_data_dir = os.path.join(base_program, "ImgWorker")
for d in (os.path.join(app_data_dir, "incoming"), os.path.join(app_data_dir, "work"), os.path.join(app_data_dir, "output"), os.path.join(app_data_dir, "outputs")):
def _cleanup_dir(dp: str, older_than_sec: int = 300):
try:
for n in os.listdir(d):
p = os.path.join(d, n)
if os.path.isdir(p):
import shutil as _sh
_sh.rmtree(p, ignore_errors=True)
else:
os.remove(p)
now_ts = time.time()
thr = max(0, int(older_than_sec or 0))
for root, dirs, files in os.walk(dp, topdown=False):
for fn in files:
fp = os.path.join(root, fn)
try:
if thr <= 0:
os.remove(fp)
else:
mt = os.path.getmtime(fp)
if (now_ts - mt) >= thr:
os.remove(fp)
except Exception:
pass
for dn in dirs:
full = os.path.join(root, dn)
try:
if not os.listdir(full):
os.rmdir(full)
except Exception:
pass
except Exception:
pass
ttl = int(os.environ.get("IMGWK_CLEAN_OLDER_THAN_SEC", "300"))
for d in (os.path.join(app_data_dir, "incoming"), os.path.join(app_data_dir, "work"), os.path.join(app_data_dir, "output"), os.path.join(app_data_dir, "outputs")):
_cleanup_dir(d, older_than_sec=ttl)
except Exception:
pass
break

View File

@ -49,11 +49,13 @@ class TrayController:
ready = bool(st.get("ready", False))
active = bool(st.get("active", False))
avg_sec = st.get("avg_sec_per_image")
provider = (st.get("provider") or "").upper()
pid = st.get("pid")
self._last_ready = ready
status_label = 'ACTIVE' if active else ('READY' if ready else 'STOP')
perf = f" - avg. {avg_sec:.1f}(sec/장)" if isinstance(avg_sec, (int, float)) else ''
title = f"ImgWorker [{status_label}]{perf}"
prov = f" [{provider}]" if provider else ''
title = f"ImgWorker [{status_label}]{perf}{prov}"
if self.icon:
self.icon.title = title
# 메뉴를 상태에 맞게 재구성

View File

@ -148,14 +148,11 @@ class BuildWithBytecode(_build_exe):
except Exception:
pass
# 추가 파일들: README, .env(있다면), 스크립트
# EXTRA_FILES = [
# (os.path.join(ROOT_DIR, "README.md"), "README.md"),
# (os.path.join(ROOT_DIR, "requirements.txt"), "requirements.txt"),
# ]
# for src, dst in list(EXTRA_FILES):
# if os.path.isfile(src):
# DATA_FILES.append((src, dst))
# 추가 파일들: .env가 있으면 실행 파일과 같은 폴더에 포함
ENV_FILE = os.path.join(ROOT_DIR, ".env")
if os.path.isfile(ENV_FILE):
# exe가 위치한 루트에 배치 → main.py의 load_dotenv(ROOT_DIR/.env)와 일치
DATA_FILES.append((ENV_FILE, ".env"))
# PowerShell 스크립트 포함
# 배포 편의를 위해 modules/scripts 경로 사용