Update 2026-01-20 20:47:44
This commit is contained in:
@@ -24,51 +24,122 @@ def move_mac_files():
|
||||
dst.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
moved = 0
|
||||
skipped = 0
|
||||
missing = 0
|
||||
errors = []
|
||||
|
||||
# JSON 요청 파싱 (overwrite 플래그 확인)
|
||||
data = request.get_json(silent=True) or {}
|
||||
overwrite = data.get("overwrite", False)
|
||||
|
||||
# 1. 대상 파일 스냅샷 (이동 시도할, 또는 해야할 파일들)
|
||||
try:
|
||||
for file in src.iterdir():
|
||||
if not file.is_file():
|
||||
continue
|
||||
|
||||
try:
|
||||
# 파일이 존재하는지 확인
|
||||
if not file.exists():
|
||||
errors.append(f"{file.name}: 파일이 존재하지 않음")
|
||||
continue
|
||||
|
||||
# 대상 파일이 이미 존재하는 경우 건너뛰기
|
||||
target = dst / file.name
|
||||
if target.exists():
|
||||
logging.warning(f"⚠️ 파일이 이미 존재하여 건너뜀: {file.name}")
|
||||
continue
|
||||
|
||||
shutil.move(str(file), str(target))
|
||||
moved += 1
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"{file.name}: {str(e)}"
|
||||
errors.append(error_msg)
|
||||
logging.error(f"❌ 파일 이동 실패: {error_msg}")
|
||||
|
||||
# 결과 로깅
|
||||
if moved > 0:
|
||||
logging.info(f"✅ MAC 파일 이동 완료 ({moved}개)")
|
||||
|
||||
if errors:
|
||||
logging.warning(f"⚠️ 일부 파일 이동 실패: {errors}")
|
||||
|
||||
# 하나라도 성공하면 success: true 반환
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"moved": moved,
|
||||
"errors": errors if errors else None
|
||||
})
|
||||
|
||||
current_files = [f for f in src.iterdir() if f.is_file()]
|
||||
except Exception as e:
|
||||
logging.error(f"❌ MAC 이동 중 치명적 오류: {e}")
|
||||
logging.error(f"파일 목록 조회 실패: {e}")
|
||||
return jsonify({"success": False, "error": str(e)})
|
||||
|
||||
# [중복 체크 로직] 덮어쓰기 모드가 아닐 때, 미리 중복 검사
|
||||
if not overwrite:
|
||||
duplicates = []
|
||||
for file in current_files:
|
||||
target = dst / file.name
|
||||
if target.exists():
|
||||
duplicates.append(file.name)
|
||||
|
||||
if duplicates:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"requires_confirmation": True,
|
||||
"duplicates": duplicates,
|
||||
"duplicate_count": len(duplicates)
|
||||
})
|
||||
else:
|
||||
logging.warning(f"⚠️ [MAC] 덮어쓰기 모드 활성화됨 - 중복 파일을 덮어씁니다.")
|
||||
|
||||
total_target_count = len(current_files)
|
||||
|
||||
# 카운터
|
||||
moved_count = 0 # 내가 직접 옮김 (또는 덮어씀)
|
||||
verified_count = 0 # 최종적으로 목적지에 있음을 확인 (성공)
|
||||
lost_count = 0 # 소스에도 없고 목적지에도 없음 (진짜 유실?)
|
||||
errors = []
|
||||
|
||||
for file in current_files:
|
||||
target = dst / file.name
|
||||
|
||||
# [Step 1] 이미 목적지에 있는지 확인
|
||||
if target.exists():
|
||||
if overwrite:
|
||||
# 덮어쓰기 모드: 기존 파일 삭제 후 이동 진행 (또는 바로 move로 덮어쓰기)
|
||||
# shutil.move는 대상이 존재하면 에러가 날 수 있으므로(버전/OS따라 다름), 안전하게 삭제 시도
|
||||
try:
|
||||
# Windows에서는 사용 중인 파일 삭제 시 에러 발생 가능
|
||||
# shutil.move(src, dst)는 dst가 존재하면 덮어쓰기 시도함 (Python 3.x)
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# (중복 체크를 통과했거나 Race Condition으로 생성된 경우) -> 이미 완료된 것으로 간주
|
||||
verified_count += 1
|
||||
logging.info(f"⏭️ 파일 이미 존재 (Skipped): {file.name}")
|
||||
continue
|
||||
|
||||
# [Step 2] 소스에 있는지 확인 (Race Condition)
|
||||
if not file.exists():
|
||||
if target.exists():
|
||||
verified_count += 1
|
||||
continue
|
||||
else:
|
||||
lost_count += 1
|
||||
logging.warning(f"❓ 이동 중 사라짐: {file.name}")
|
||||
continue
|
||||
|
||||
# [Step 3] 이동 시도 (덮어쓰기 포함)
|
||||
try:
|
||||
shutil.move(str(file), str(target))
|
||||
moved_count += 1
|
||||
verified_count += 1
|
||||
except shutil.Error as e:
|
||||
# shutil.move might raise Error if destination exists depending on implementation,
|
||||
# but standard behavior overwrites if not same file.
|
||||
# If exact same file, verified.
|
||||
if target.exists():
|
||||
verified_count += 1
|
||||
else:
|
||||
errors.append(f"{file.name}: {str(e)}")
|
||||
except FileNotFoundError:
|
||||
# 옮기려는 찰나에 사라짐 -> 목적지 재확인
|
||||
if target.exists():
|
||||
verified_count += 1
|
||||
logging.info(f"⏭️ 동시 처리됨 (완료): {file.name}")
|
||||
else:
|
||||
lost_count += 1
|
||||
except Exception as e:
|
||||
# 권한 에러 등 진짜 실패
|
||||
error_msg = f"{file.name}: {str(e)}"
|
||||
errors.append(error_msg)
|
||||
logging.error(f"❌ 이동 에러: {error_msg}")
|
||||
|
||||
# 결과 요약
|
||||
msg = f"총 {total_target_count}건 중 {verified_count}건 처리 완료"
|
||||
if moved_count < verified_count:
|
||||
msg += f" (이동: {moved_count}, 이미 완료: {verified_count - moved_count})"
|
||||
|
||||
if lost_count > 0:
|
||||
msg += f", 확인 불가: {lost_count}"
|
||||
|
||||
logging.info(f"✅ MAC 처리 결과: {msg}")
|
||||
flash(msg, "success" if lost_count == 0 else "warning")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"total": total_target_count,
|
||||
"verified": verified_count,
|
||||
"message": msg,
|
||||
"errors": errors if errors else None
|
||||
})
|
||||
|
||||
|
||||
@utils_bp.route("/move_guid_files", methods=["POST"])
|
||||
@login_required
|
||||
@@ -78,47 +149,86 @@ def move_guid_files():
|
||||
dst.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
moved = 0
|
||||
skipped = 0
|
||||
missing = 0
|
||||
errors = []
|
||||
|
||||
# JSON 요청 파싱 (overwrite 플래그 확인)
|
||||
data = request.get_json(silent=True) or {}
|
||||
overwrite = data.get("overwrite", False)
|
||||
|
||||
try:
|
||||
for file in src.iterdir():
|
||||
if not file.is_file():
|
||||
continue
|
||||
files = [f for f in src.iterdir() if f.is_file()]
|
||||
except Exception:
|
||||
files = []
|
||||
|
||||
# [중복 체크]
|
||||
if not overwrite:
|
||||
duplicates = []
|
||||
for file in files:
|
||||
target = dst / file.name
|
||||
if target.exists():
|
||||
duplicates.append(file.name)
|
||||
if duplicates:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"requires_confirmation": True,
|
||||
"duplicates": duplicates,
|
||||
"duplicate_count": len(duplicates)
|
||||
})
|
||||
else:
|
||||
logging.warning(f"⚠️ [GUID] 덮어쓰기 모드 활성화됨 - 중복 파일을 덮어씁니다.")
|
||||
|
||||
total_target_count = len(files)
|
||||
verified_count = 0
|
||||
moved_count = 0
|
||||
errors = []
|
||||
lost_count = 0
|
||||
|
||||
try:
|
||||
for file in files:
|
||||
target = dst / file.name
|
||||
|
||||
# 1. 이미 완료되었는지 확인
|
||||
if target.exists():
|
||||
if not overwrite:
|
||||
verified_count += 1
|
||||
continue
|
||||
# overwrite=True면 계속 진행하여 덮어쓰기 시도
|
||||
|
||||
# 2. 소스 확인
|
||||
if not file.exists():
|
||||
if target.exists(): verified_count += 1
|
||||
else: lost_count += 1
|
||||
continue
|
||||
|
||||
# 3. 이동
|
||||
try:
|
||||
# 파일이 존재하는지 확인
|
||||
if not file.exists():
|
||||
errors.append(f"{file.name}: 파일이 존재하지 않음")
|
||||
continue
|
||||
|
||||
# 대상 파일이 이미 존재하는 경우 건너뛰기
|
||||
target = dst / file.name
|
||||
if target.exists():
|
||||
logging.warning(f"⚠️ 파일이 이미 존재하여 건너뜀: {file.name}")
|
||||
continue
|
||||
|
||||
shutil.move(str(file), str(target))
|
||||
moved += 1
|
||||
|
||||
moved_count += 1
|
||||
verified_count += 1
|
||||
except FileNotFoundError:
|
||||
if target.exists(): verified_count += 1
|
||||
else: lost_count += 1
|
||||
except Exception as e:
|
||||
error_msg = f"{file.name}: {str(e)}"
|
||||
errors.append(error_msg)
|
||||
logging.error(f"❌ 파일 이동 실패: {error_msg}")
|
||||
errors.append(f"{file.name}: {e}")
|
||||
|
||||
# 결과 메시지
|
||||
if moved > 0:
|
||||
flash(f"GUID 파일이 성공적으로 이동되었습니다. ({moved}개)", "success")
|
||||
logging.info(f"✅ GUID 파일 이동 완료 ({moved}개)")
|
||||
# 상세 메시지
|
||||
msg = f"총 {total_target_count}건 중 {verified_count}건 처리 완료"
|
||||
logging.info(f"✅ GUID 처리: {msg}")
|
||||
flash(msg, "success" if lost_count == 0 else "warning")
|
||||
|
||||
if errors:
|
||||
logging.warning(f"⚠️ 일부 파일 이동 실패: {errors}")
|
||||
flash(f"일부 파일 이동 실패: {len(errors)}개", "warning")
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"total": total_target_count,
|
||||
"verified": verified_count,
|
||||
"message": msg,
|
||||
"errors": errors if errors else None
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"❌ GUID 이동 오류: {e}")
|
||||
flash(f"오류 발생: {e}", "danger")
|
||||
|
||||
return redirect(url_for("main.index"))
|
||||
logging.error(f"❌ GUID 이동 중 오류: {e}")
|
||||
return jsonify({"success": False, "error": str(e)})
|
||||
|
||||
@utils_bp.route("/move_gpu_files", methods=["POST"])
|
||||
@login_required
|
||||
@@ -131,52 +241,85 @@ def move_gpu_files():
|
||||
dst.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
moved = 0
|
||||
skipped = 0
|
||||
missing = 0
|
||||
errors = []
|
||||
|
||||
# JSON 요청 파싱
|
||||
data = request.get_json(silent=True) or {}
|
||||
overwrite = data.get("overwrite", False)
|
||||
|
||||
try:
|
||||
for file in src.iterdir():
|
||||
if not file.is_file():
|
||||
continue
|
||||
files = [f for f in src.iterdir() if f.is_file()]
|
||||
except Exception:
|
||||
files = []
|
||||
|
||||
# GPU 관련 텍스트 파일만 이동 (GUID 등 제외)
|
||||
if not file.name.lower().endswith(".txt"):
|
||||
continue
|
||||
# [중복 체크]
|
||||
if not overwrite:
|
||||
duplicates = []
|
||||
for file in files:
|
||||
target = dst / file.name
|
||||
if target.exists():
|
||||
duplicates.append(file.name)
|
||||
if duplicates:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"requires_confirmation": True,
|
||||
"duplicates": duplicates,
|
||||
"duplicate_count": len(duplicates)
|
||||
})
|
||||
else:
|
||||
logging.warning(f"⚠️ [GPU] 덮어쓰기 모드 활성화됨 - 중복 파일을 덮어씁니다.")
|
||||
|
||||
total_target_count = len(files)
|
||||
verified_count = 0
|
||||
moved_count = 0
|
||||
errors = []
|
||||
lost_count = 0
|
||||
|
||||
try:
|
||||
for file in files:
|
||||
target = dst / file.name
|
||||
|
||||
# 1. 존재 확인 (덮어쓰기 아닐 경우)
|
||||
if target.exists():
|
||||
if not overwrite:
|
||||
verified_count += 1
|
||||
continue
|
||||
|
||||
# 2. 소스 확인
|
||||
if not file.exists():
|
||||
if target.exists(): verified_count += 1
|
||||
else: lost_count += 1
|
||||
continue
|
||||
|
||||
# 3. 이동
|
||||
try:
|
||||
# 파일 존재 확인
|
||||
if not file.exists():
|
||||
errors.append(f"{file.name}: 파일이 존재하지 않음")
|
||||
continue
|
||||
|
||||
# 대상 파일이 이미 존재하면 스킵
|
||||
target = dst / file.name
|
||||
if target.exists():
|
||||
logging.warning(f"⚠️ 이미 존재하여 건너뜀: {file.name}")
|
||||
continue
|
||||
|
||||
# 파일 이동
|
||||
shutil.move(str(file), str(target))
|
||||
moved += 1
|
||||
|
||||
moved_count += 1
|
||||
verified_count += 1
|
||||
except FileNotFoundError:
|
||||
if target.exists(): verified_count += 1
|
||||
else: lost_count += 1
|
||||
except Exception as e:
|
||||
error_msg = f"{file.name}: {str(e)}"
|
||||
errors.append(error_msg)
|
||||
logging.error(f"❌ GPU 파일 이동 실패: {error_msg}")
|
||||
errors.append(f"{file.name}: {e}")
|
||||
|
||||
# 결과 메시지
|
||||
if moved > 0:
|
||||
flash(f"GPU 시리얼 파일이 성공적으로 이동되었습니다. ({moved}개)", "success")
|
||||
logging.info(f"✅ GPU 파일 이동 완료 ({moved}개)")
|
||||
# 상세 메시지
|
||||
msg = f"총 {total_target_count}건 중 {verified_count}건 처리 완료"
|
||||
logging.info(f"✅ GPU 처리: {msg}")
|
||||
flash(msg, "success")
|
||||
|
||||
if errors:
|
||||
logging.warning(f"⚠️ 일부 파일 이동 실패: {errors}")
|
||||
flash(f"일부 GPU 파일 이동 실패: {len(errors)}개", "warning")
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"total": total_target_count,
|
||||
"verified": verified_count,
|
||||
"message": msg,
|
||||
"errors": errors if errors else None
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"❌ GPU 이동 오류: {e}")
|
||||
flash(f"오류 발생: {e}", "danger")
|
||||
|
||||
return redirect(url_for("main.index"))
|
||||
return jsonify({"success": False, "error": str(e)})
|
||||
|
||||
@utils_bp.route("/update_server_list", methods=["POST"])
|
||||
@login_required
|
||||
@@ -213,6 +356,8 @@ def update_server_list():
|
||||
@login_required
|
||||
def update_guid_list():
|
||||
content = request.form.get("server_list_content")
|
||||
slot_priority = request.form.get("slot_priority", "") # 슬롯 우선순위 받기
|
||||
|
||||
if not content:
|
||||
flash("내용을 입력하세요.", "warning")
|
||||
return redirect(url_for("main.index"))
|
||||
@@ -220,6 +365,13 @@ def update_guid_list():
|
||||
path = Path(Config.SERVER_LIST_FOLDER) / "guid_list.txt"
|
||||
try:
|
||||
path.write_text(content, encoding="utf-8")
|
||||
|
||||
# 슬롯 우선순위를 환경변수로 전달
|
||||
env = os.environ.copy()
|
||||
if slot_priority:
|
||||
env["GUID_SLOT_PRIORITY"] = slot_priority
|
||||
logging.info(f"GUID 슬롯 우선순위: {slot_priority}")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(Path(Config.SERVER_LIST_FOLDER) / "GUIDtxtT0Execl.py")],
|
||||
capture_output=True,
|
||||
@@ -227,6 +379,7 @@ def update_guid_list():
|
||||
check=True,
|
||||
cwd=str(Path(Config.SERVER_LIST_FOLDER)),
|
||||
timeout=300,
|
||||
env=env, # 환경변수 전달
|
||||
)
|
||||
logging.info(f"GUID 리스트 스크립트 실행 결과: {result.stdout}")
|
||||
flash("GUID 리스트가 업데이트되었습니다.", "success")
|
||||
|
||||
Reference in New Issue
Block a user