Merge branch 'LimitedUI' of ssh://cckb9998.synology.me:30022/ckh08045/AutoPercenty3 into LimitedUI
This commit is contained in:
commit
2839f969c7
|
|
@ -2901,96 +2901,96 @@ class BrowserController(QThread):
|
||||||
self.logger.log(f"메모 입력 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
self.logger.log(f"메모 입력 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||||
|
|
||||||
# ESC 키 2번 전송하여 팝업 제거 시도
|
# ESC 키 2번 전송하여 팝업 제거 시도
|
||||||
self.logger.log("ESC 키를 2번 전송하여 팝업 제거 후 메모 입력 재시도합니다.", level=logging.WARNING)
|
# self.logger.log("ESC 키를 2번 전송하여 팝업 제거 후 메모 입력 재시도합니다.", level=logging.WARNING)
|
||||||
await self.page.keyboard.press("Escape")
|
await self.page.keyboard.press("Escape")
|
||||||
await self.page.keyboard.press("Escape")
|
await self.page.keyboard.press("Escape")
|
||||||
await asyncio.sleep(0.5) # 잠시 대기
|
await asyncio.sleep(0.5) # 잠시 대기
|
||||||
|
|
||||||
# 메모 입력 재시도
|
# # 메모 입력 재시도
|
||||||
try:
|
# try:
|
||||||
await memo_button.click()
|
# await memo_button.click()
|
||||||
self.logger.log("재시도: 메모 버튼을 클릭했습니다.", level=logging.DEBUG)
|
# self.logger.log("재시도: 메모 버튼을 클릭했습니다.", level=logging.DEBUG)
|
||||||
memo_input = self.page.locator(self.memo_input_locator)
|
# memo_input = self.page.locator(self.memo_input_locator)
|
||||||
await memo_input.wait_for(state="visible")
|
# await memo_input.wait_for(state="visible")
|
||||||
self.logger.log("재시도: 메모 입력란 대기 완료", level=logging.DEBUG)
|
# self.logger.log("재시도: 메모 입력란 대기 완료", level=logging.DEBUG)
|
||||||
|
|
||||||
# 5. 기존 메모 읽어오기
|
# # 5. 기존 메모 읽어오기
|
||||||
existing = await memo_input.input_value()
|
# existing = await memo_input.input_value()
|
||||||
combined = existing.strip()
|
# combined = existing.strip()
|
||||||
|
|
||||||
# 6. 새 메모 조합
|
# # 6. 새 메모 조합
|
||||||
suffix = "\n" + "-"*10 + "\n" + f"수집방법:[{collect_method}]\n"
|
# suffix = "\n" + "-"*10 + "\n" + f"수집방법:[{collect_method}]\n"
|
||||||
if combined:
|
# if combined:
|
||||||
if is_new_memo_first:
|
# if is_new_memo_first:
|
||||||
combined = new_memo + suffix + combined
|
# combined = new_memo + suffix + combined
|
||||||
else:
|
# else:
|
||||||
combined = combined + suffix + new_memo
|
# combined = combined + suffix + new_memo
|
||||||
else:
|
# else:
|
||||||
combined = f"수집방법:[{collect_method}]\n" + new_memo
|
# combined = f"수집방법:[{collect_method}]\n" + new_memo
|
||||||
|
|
||||||
# 7. 길이 제한 (2000자)
|
# # 7. 길이 제한 (2000자)
|
||||||
if len(combined) > 2000:
|
# if len(combined) > 2000:
|
||||||
combined = combined[:2000]
|
# combined = combined[:2000]
|
||||||
|
|
||||||
# 8. 채우기
|
# # 8. 채우기
|
||||||
await memo_input.fill(combined)
|
# await memo_input.fill(combined)
|
||||||
self.logger.log(f"재시도: 메모 입력: {combined!r}", level=logging.DEBUG)
|
# self.logger.log(f"재시도: 메모 입력: {combined!r}", level=logging.DEBUG)
|
||||||
|
|
||||||
# 9. 노출 체크박스 처리
|
# # 9. 노출 체크박스 처리
|
||||||
# chk = self.page.locator(
|
# # chk = self.page.locator(
|
||||||
# 'label:has-text("상품 목록에 메모 내용 노출하기") '
|
# # 'label:has-text("상품 목록에 메모 내용 노출하기") '
|
||||||
# 'input[type="checkbox"]'
|
# # 'input[type="checkbox"]'
|
||||||
# )
|
# # )
|
||||||
# await chk.wait_for(state="attached")
|
# # await chk.wait_for(state="attached")
|
||||||
# checked = await chk.is_checked()
|
# # checked = await chk.is_checked()
|
||||||
# if is_memo_exposure and not checked:
|
# # if is_memo_exposure and not checked:
|
||||||
# await chk.click()
|
# # await chk.click()
|
||||||
# self.logger.log("재시도: 메모 노출 체크 ON", level=logging.DEBUG)
|
# # self.logger.log("재시도: 메모 노출 체크 ON", level=logging.DEBUG)
|
||||||
# elif not is_memo_exposure and checked:
|
# # elif not is_memo_exposure and checked:
|
||||||
# await chk.click()
|
# # await chk.click()
|
||||||
# self.logger.log("재시도: 메모 노출 체크 OFF", level=logging.DEBUG)
|
# # self.logger.log("재시도: 메모 노출 체크 OFF", level=logging.DEBUG)
|
||||||
|
|
||||||
is_memo_exposure = self.toggle_states.get("memo_exposure", False)
|
# is_memo_exposure = self.toggle_states.get("memo_exposure", False)
|
||||||
exposer = self.page.locator(self.memo_exposer_locator)
|
# exposer = self.page.locator(self.memo_exposer_locator)
|
||||||
|
|
||||||
# 노출 체크박스 존재 대기
|
# # 노출 체크박스 존재 대기
|
||||||
await exposer.wait_for(state="attached")
|
# await exposer.wait_for(state="attached")
|
||||||
|
|
||||||
# 노출 체크박스가 보이고 활성화될 때까지 대기
|
# # 노출 체크박스가 보이고 활성화될 때까지 대기
|
||||||
await exposer.wait_for(state="visible", timeout=5000)
|
# await exposer.wait_for(state="visible", timeout=5000)
|
||||||
|
|
||||||
# 활성화 상태 확인 (disabled 속성이 없으면 활성화된 것)
|
# # 활성화 상태 확인 (disabled 속성이 없으면 활성화된 것)
|
||||||
max_attempts = 10
|
# max_attempts = 10
|
||||||
for attempt in range(max_attempts):
|
# for attempt in range(max_attempts):
|
||||||
is_disabled = await exposer.get_attribute('disabled')
|
# is_disabled = await exposer.get_attribute('disabled')
|
||||||
if is_disabled is None: # disabled 속성이 없으면 활성화됨
|
# if is_disabled is None: # disabled 속성이 없으면 활성화됨
|
||||||
break
|
# break
|
||||||
if attempt < max_attempts - 1:
|
# if attempt < max_attempts - 1:
|
||||||
await asyncio.sleep(0.5)
|
# await asyncio.sleep(0.5)
|
||||||
else:
|
# else:
|
||||||
self.logger.log("노출 체크박스가 활성화되지 않았습니다.", level=logging.WARNING)
|
# self.logger.log("노출 체크박스가 활성화되지 않았습니다.", level=logging.WARNING)
|
||||||
|
|
||||||
if is_memo_exposure:
|
# if is_memo_exposure:
|
||||||
await exposer.check()
|
# await exposer.check()
|
||||||
else:
|
# else:
|
||||||
await exposer.uncheck()
|
# await exposer.uncheck()
|
||||||
|
|
||||||
# 10. 저장
|
# # 10. 저장
|
||||||
save_btn = self.page.locator(self.memo_save_btn_locator)
|
# save_btn = self.page.locator(self.memo_save_btn_locator)
|
||||||
await save_btn.wait_for(state="visible")
|
# await save_btn.wait_for(state="visible")
|
||||||
await save_btn.click()
|
# await save_btn.click()
|
||||||
self.logger.log("재시도: 메모 저장 클릭", level=logging.DEBUG)
|
# self.logger.log("재시도: 메모 저장 클릭", level=logging.DEBUG)
|
||||||
await asyncio.sleep(0.3)
|
# await asyncio.sleep(0.3)
|
||||||
except Exception as e2:
|
# except Exception as e2:
|
||||||
screenshot_path = await self.save_error_screenshot()
|
# screenshot_path = await self.save_error_screenshot()
|
||||||
self.logger.log(f"재시도 후에도 메모 입력 중 오류 발생: {e2}", level=logging.ERROR, exc_info=True)
|
# self.logger.log(f"재시도 후에도 메모 입력 중 오류 발생: {e2}", level=logging.ERROR, exc_info=True)
|
||||||
# ESC 키 2번 전송하여 팝업 제거 후 재시도 로직을 제거하고, 이번 상품의 메모 입력을 건너뛰고 반환합니다.
|
# # ESC 키 2번 전송하여 팝업 제거 후 재시도 로직을 제거하고, 이번 상품의 메모 입력을 건너뛰고 반환합니다.
|
||||||
self.logger.log("ESC 키를 2번 전송하여 메모 창을 닫고 해당 상품의 메모 입력을 건너뜁니다.", level=logging.WARNING)
|
# self.logger.log("ESC 키를 2번 전송하여 메모 창을 닫고 해당 상품의 메모 입력을 건너뜁니다.", level=logging.WARNING)
|
||||||
await self.page.keyboard.press("Escape")
|
# await self.page.keyboard.press("Escape")
|
||||||
await self.page.keyboard.press("Escape")
|
# await self.page.keyboard.press("Escape")
|
||||||
await asyncio.sleep(0.5) # 잠시 대기
|
# await asyncio.sleep(0.5) # 잠시 대기
|
||||||
await asyncio.sleep(0.5)
|
# await asyncio.sleep(0.5)
|
||||||
return
|
# return
|
||||||
|
|
||||||
def generate_titles_with_prices(self, title_infos):
|
def generate_titles_with_prices(self, title_infos):
|
||||||
"""top_5_titles와 top_5_prices를 매칭하여 텍스트 생성"""
|
"""top_5_titles와 top_5_prices를 매칭하여 텍스트 생성"""
|
||||||
|
|
|
||||||
113
loggerModule.py
113
loggerModule.py
|
|
@ -121,79 +121,58 @@ class Logger(QObject):
|
||||||
self.gui_log_level = gui_log_level
|
self.gui_log_level = gui_log_level
|
||||||
|
|
||||||
|
|
||||||
class CustomRotatingFileHandler(BaseRotatingHandler):
|
class CustomRotatingFileHandler(RotatingFileHandler):
|
||||||
"""로그 파일을 모두 .log 확장자로 생성하는 커스텀 핸들러"""
|
"""`.log` 확장자를 강제하고 파일명을 `base_번호.log` 형식으로 롤오버하는 핸들러"""
|
||||||
|
|
||||||
def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None):
|
def __init__(self, filename, mode="a", maxBytes=0, backupCount=0, encoding=None):
|
||||||
super().__init__(filename, mode, encoding)
|
# 확장자를 무조건 .log 로 통일
|
||||||
self.maxBytes = maxBytes
|
if not filename.endswith(".log"):
|
||||||
self.backupCount = backupCount
|
base, _ = os.path.splitext(filename)
|
||||||
# 기존 로그 파일 확인 및 인덱스 설정
|
filename = base + ".log"
|
||||||
self._base_filename = filename
|
|
||||||
self._extension = '.log'
|
# 부모(RotatingFileHandler) 초기화
|
||||||
|
super().__init__(filename, mode, maxBytes, backupCount, encoding)
|
||||||
|
|
||||||
|
# 편의를 위해 확장자 없는 베이스 이름 저장
|
||||||
|
self._base_name, _ = os.path.splitext(self.baseFilename)
|
||||||
|
|
||||||
def doRollover(self):
|
def doRollover(self):
|
||||||
"""롤오버 수행 - 파일이 최대 크기에 도달하면 새 파일 생성"""
|
"""`RotatingFileHandler`의 롤오버 방식(emit → shouldRollover) 그대로 사용하되
|
||||||
|
파일명을 `base_번호.log` 패턴으로 변경한다."""
|
||||||
|
|
||||||
|
# 현재 스트림 닫기
|
||||||
if self.stream:
|
if self.stream:
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
self.stream = None
|
self.stream = None
|
||||||
|
|
||||||
# 기존 로그 파일 이름을 기반으로 백업 파일 생성
|
# existing backup logs
|
||||||
base_name, ext = os.path.splitext(self._base_filename)
|
existing_logs = glob.glob(f"{self._base_name}_*.log")
|
||||||
|
|
||||||
# 현재 디렉토리의 모든 로그 파일 확인
|
|
||||||
log_dir = os.path.dirname(self._base_filename) or '.'
|
|
||||||
existing_logs = glob.glob(f"{base_name}*.log")
|
|
||||||
existing_logs.sort()
|
existing_logs.sort()
|
||||||
|
|
||||||
# 최대 백업 수 초과하는 파일 제거
|
# backupCount 초과 시 오래된 파일 삭제
|
||||||
while len(existing_logs) >= self.backupCount:
|
if self.backupCount > 0 and len(existing_logs) >= self.backupCount:
|
||||||
try:
|
for old_log in existing_logs[: len(existing_logs) - self.backupCount + 1]:
|
||||||
oldest_file = existing_logs.pop(0)
|
|
||||||
os.remove(oldest_file)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 새 로그 파일 이름 생성 (주 로그 파일 이름은 그대로 유지)
|
|
||||||
# 예: log.log, log_1.log, log_2.log, ...
|
|
||||||
if os.path.exists(self._base_filename):
|
|
||||||
# 인덱스가 있는 로그 파일들 찾기
|
|
||||||
indexed_logs = [f for f in existing_logs if f != self._base_filename]
|
|
||||||
max_index = 0
|
|
||||||
|
|
||||||
for log_file in indexed_logs:
|
|
||||||
try:
|
try:
|
||||||
# 파일 이름에서 인덱스 부분 추출
|
os.remove(old_log)
|
||||||
name_part = os.path.basename(log_file)
|
|
||||||
name_without_ext = os.path.splitext(name_part)[0]
|
|
||||||
if '_' in name_without_ext:
|
|
||||||
idx_str = name_without_ext.split('_')[-1]
|
|
||||||
if idx_str.isdigit():
|
|
||||||
max_index = max(max_index, int(idx_str))
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 새 인덱스로 파일 이름 설정
|
# 새 인덱스 계산
|
||||||
new_index = max_index + 1
|
max_idx = 0
|
||||||
new_log_file = f"{base_name}_{new_index}.log"
|
for path in existing_logs:
|
||||||
|
idx_str = os.path.splitext(os.path.basename(path))[0].split("_")[-1]
|
||||||
# 기존 파일 이름 변경
|
if idx_str.isdigit():
|
||||||
try:
|
max_idx = max(max_idx, int(idx_str))
|
||||||
os.rename(self._base_filename, new_log_file)
|
|
||||||
except Exception:
|
new_idx = max_idx + 1
|
||||||
pass
|
rollover_target = f"{self._base_name}_{new_idx}.log"
|
||||||
|
|
||||||
# 스트림 다시 열기
|
# 현재 파일명을 rollover_target 으로 변경
|
||||||
self.mode = 'w'
|
try:
|
||||||
|
os.rename(self.baseFilename, rollover_target)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 새로운 스트림 오픈 (write 모드)
|
||||||
|
self.mode = "w"
|
||||||
self.stream = self._open()
|
self.stream = self._open()
|
||||||
|
|
||||||
def shouldRollover(self, record):
|
|
||||||
"""롤오버가 필요한지 확인"""
|
|
||||||
if self.stream is None: # 첫 번째 로그 쓰기 시도
|
|
||||||
self.stream = self._open()
|
|
||||||
|
|
||||||
if self.maxBytes > 0: # 최대 크기가 지정된 경우만 검사
|
|
||||||
self.stream.seek(0, 2) # 파일 끝으로 이동
|
|
||||||
if self.stream.tell() >= self.maxBytes:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
|
||||||
98
mainUI_SP.py
98
mainUI_SP.py
|
|
@ -539,6 +539,18 @@ class MAIN_GUI(QMainWindow):
|
||||||
"off": [],
|
"off": [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"font_type_combo": {
|
||||||
|
"key": "global/font_type_combo", # 폰트 타입
|
||||||
|
"state_key": "font_type",
|
||||||
|
"dependents": { # ON/OFF별 enable 위젯 리스트
|
||||||
|
"on": [],
|
||||||
|
"off": []
|
||||||
|
},
|
||||||
|
"visible": { # ON/OFF별 visible 위젯 리스트
|
||||||
|
"on": [],
|
||||||
|
"off": [],
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug_toggle": {
|
"debug_toggle": {
|
||||||
"key": "global/debug_enabled", # 디버그 사용 여부
|
"key": "global/debug_enabled", # 디버그 사용 여부
|
||||||
"state_key": "debug_mode",
|
"state_key": "debug_mode",
|
||||||
|
|
@ -943,7 +955,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"watermark_toggle": {
|
"watermark_toggle": {
|
||||||
"key": "detail/watermark_enabled", # 상세페이지 이미지 워터마크 사용 여부
|
"key": "detail/watermark", # 상세페이지 이미지 워터마크 사용 여부
|
||||||
"state_key": "watermark",
|
"state_key": "watermark",
|
||||||
"dependents": { # ON/OFF별 enable 위젯 리스트
|
"dependents": { # ON/OFF별 enable 위젯 리스트
|
||||||
"on": ["watermark_text_input", "opacity_percent_input"],
|
"on": ["watermark_text_input", "opacity_percent_input"],
|
||||||
|
|
@ -1084,7 +1096,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
'ed_mode': False,
|
'ed_mode': False,
|
||||||
'discord': False,
|
'discord': False,
|
||||||
'is_localServer': False,
|
'is_localServer': False,
|
||||||
'watermark': False,
|
'watermark_toggle': False,
|
||||||
'clientID': "",
|
'clientID': "",
|
||||||
'clientSecret': "",
|
'clientSecret': "",
|
||||||
'discord_webhook': "",
|
'discord_webhook': "",
|
||||||
|
|
@ -1234,52 +1246,55 @@ class MAIN_GUI(QMainWindow):
|
||||||
def get_allowed_options_for_tier(self, tier):
|
def get_allowed_options_for_tier(self, tier):
|
||||||
"""등급별로 허용되는 옵션들을 반환합니다."""
|
"""등급별로 허용되는 옵션들을 반환합니다."""
|
||||||
tier_options = {
|
tier_options = {
|
||||||
'basic': ['대체', 'CPU'],
|
'basic': ['CPU'],
|
||||||
'premium': ['대체', 'CPU'],
|
'premium': ['CPU', '자체서버'],
|
||||||
'vip': ['대체', 'CPU', '자체서버']
|
'vip': ['CPU', '자체서버']
|
||||||
}
|
}
|
||||||
return tier_options.get(tier, ['대체'])
|
return tier_options.get(tier, ['CPU'])
|
||||||
|
|
||||||
def get_best_option_for_tier(self, tier):
|
def get_best_option_for_tier(self, tier):
|
||||||
"""등급별로 가장 좋은 옵션을 반환합니다."""
|
"""등급별로 가장 좋은 옵션을 반환합니다."""
|
||||||
allowed_options = self.get_allowed_options_for_tier(tier)
|
allowed_options = self.get_allowed_options_for_tier(tier)
|
||||||
# 자체서버 > CPU > 대체 순서로 최고방식 선택
|
# 자체서버 > CPU 순서로 최고방식 선택
|
||||||
for option in ['자체서버', 'CPU', '대체']:
|
for option in ['CPU', '자체서버']:
|
||||||
if option in allowed_options:
|
if option in allowed_options:
|
||||||
return option
|
return option
|
||||||
return '대체'
|
return 'CPU'
|
||||||
|
|
||||||
def on_detailIMGTrans_type_combo_changed(self, selected_option):
|
def on_detailIMGTrans_type_combo_changed(self, selected_option):
|
||||||
"""상세페이지 이미지번역 엔진 드롭박스 변경 핸들러"""
|
"""상세페이지 이미지번역 엔진 드롭박스 변경 핸들러"""
|
||||||
if self.authenticated_by_admin:
|
if self.authenticated_by_admin:
|
||||||
return
|
validated_option = selected_option
|
||||||
validated_option = self.validate_and_adjust_option(
|
else:
|
||||||
self.detail_IMGTrans_type_combo,
|
validated_option = self.validate_and_adjust_option(
|
||||||
selected_option,
|
self.detail_IMGTrans_type_combo,
|
||||||
"상세페이지 이미지번역"
|
selected_option,
|
||||||
)
|
"상세페이지 이미지번역"
|
||||||
|
)
|
||||||
self.universal_input_handler(self.detail_IMGTrans_type_combo, validated_option)
|
self.universal_input_handler(self.detail_IMGTrans_type_combo, validated_option)
|
||||||
|
|
||||||
def on_thumb_trans_type_combo_changed(self, selected_option):
|
def on_thumb_trans_type_combo_changed(self, selected_option):
|
||||||
"""썸네일 번역 엔진 드롭박스 변경 핸들러"""
|
"""썸네일 번역 엔진 드롭박스 변경 핸들러"""
|
||||||
if self.authenticated_by_admin:
|
if self.authenticated_by_admin:
|
||||||
return
|
validated_option = selected_option
|
||||||
validated_option = self.validate_and_adjust_option(
|
else:
|
||||||
self.thumb_trans_type_combo,
|
validated_option = self.validate_and_adjust_option(
|
||||||
selected_option,
|
self.thumb_trans_type_combo,
|
||||||
"썸네일 번역"
|
selected_option,
|
||||||
)
|
"썸네일 번역"
|
||||||
|
)
|
||||||
self.universal_input_handler(self.thumb_trans_type_combo, validated_option)
|
self.universal_input_handler(self.thumb_trans_type_combo, validated_option)
|
||||||
|
|
||||||
def on_optionIMGTrans_type_combo_changed(self, selected_option):
|
def on_optionIMGTrans_type_combo_changed(self, selected_option):
|
||||||
"""옵션 이미지번역 엔진 드롭박스 변경 핸들러"""
|
"""옵션 이미지번역 엔진 드롭박스 변경 핸들러"""
|
||||||
if self.authenticated_by_admin:
|
if self.authenticated_by_admin:
|
||||||
return
|
validated_option = selected_option
|
||||||
validated_option = self.validate_and_adjust_option(
|
else:
|
||||||
self.optionIMGTrans_type_combo,
|
validated_option = self.validate_and_adjust_option(
|
||||||
selected_option,
|
self.optionIMGTrans_type_combo,
|
||||||
"옵션 이미지번역"
|
selected_option,
|
||||||
)
|
"옵션 이미지번역"
|
||||||
|
)
|
||||||
self.universal_input_handler(self.optionIMGTrans_type_combo, validated_option)
|
self.universal_input_handler(self.optionIMGTrans_type_combo, validated_option)
|
||||||
|
|
||||||
def universal_input_handler(self, widget, *args):
|
def universal_input_handler(self, widget, *args):
|
||||||
|
|
@ -2682,7 +2697,9 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.interval_widget = QWidget()
|
self.interval_widget = QWidget()
|
||||||
self.interval_spinbox_layout = QHBoxLayout(self.interval_widget)
|
self.interval_spinbox_layout = QHBoxLayout(self.interval_widget)
|
||||||
self.interval_spinbox_label = QLabel("번역간격", self)# 작업 완료 메서드 수정
|
self.interval_spinbox_label = QLabel("번역간격", self)# 작업 완료 메서드 수정
|
||||||
|
self.interval_spinbox_label.setEnabled(False)
|
||||||
self.interval_spinbox = QSpinBox(self)
|
self.interval_spinbox = QSpinBox(self)
|
||||||
|
self.interval_spinbox.setEnabled(False)
|
||||||
# self.interval_spinbox.setFixedHeight(30)
|
# self.interval_spinbox.setFixedHeight(30)
|
||||||
self.interval_spinbox.setObjectName("interval")
|
self.interval_spinbox.setObjectName("interval")
|
||||||
self.interval_spinbox.setRange(2, 10)
|
self.interval_spinbox.setRange(2, 10)
|
||||||
|
|
@ -2713,7 +2730,9 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.interval_widget.leaveEvent = lambda e: self.reset_manual(self.global_manual_group, self.global_manual_label)
|
self.interval_widget.leaveEvent = lambda e: self.reset_manual(self.global_manual_group, self.global_manual_label)
|
||||||
|
|
||||||
self.watingTime_spinbox_label = QLabel("번역대기", self)# 작업 완료 메서드 수정
|
self.watingTime_spinbox_label = QLabel("번역대기", self)# 작업 완료 메서드 수정
|
||||||
|
self.watingTime_spinbox_label.setEnabled(False)
|
||||||
self.watingTime_spinbox = QSpinBox(self)
|
self.watingTime_spinbox = QSpinBox(self)
|
||||||
|
self.watingTime_spinbox.setEnabled(False)
|
||||||
# self.watingTime_spinbox.setFixedHeight(30)
|
# self.watingTime_spinbox.setFixedHeight(30)
|
||||||
self.watingTime_spinbox.setObjectName("watingTime")
|
self.watingTime_spinbox.setObjectName("watingTime")
|
||||||
self.watingTime_spinbox.setRange(5, 60)
|
self.watingTime_spinbox.setRange(5, 60)
|
||||||
|
|
@ -3223,7 +3242,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
"""
|
"""
|
||||||
<p>옵션 이미지에 포함된 중국어 텍스트를 한국어로 번역하여 새 이미지를 생성합니다.</p>
|
<p>옵션 이미지에 포함된 중국어 텍스트를 한국어로 번역하여 새 이미지를 생성합니다.</p>
|
||||||
<p>옵션 이미지의 사이즈 표, 색상 설명 등을 번역하여 고객에게 제공합니다.</p>
|
<p>옵션 이미지의 사이즈 표, 색상 설명 등을 번역하여 고객에게 제공합니다.</p>
|
||||||
<p>옵션 이미지의 특성에 따라API 또는 대체 번역 방식을 선택할 수 있습니다.</p>
|
<p>옵션 이미지의 특성에 따라 CPU 또는 자체 번역 방식을 선택할 수 있습니다.</p>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
self.optionIMGTrans_widget.leaveEvent = lambda e: self.reset_manual(self.option_manual_group, self.option_manual_label)
|
self.optionIMGTrans_widget.leaveEvent = lambda e: self.reset_manual(self.option_manual_group, self.option_manual_label)
|
||||||
|
|
@ -3239,8 +3258,8 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.optionIMGTrans_type_label = QLabel("옵션 이미지번역 엔진", self)
|
self.optionIMGTrans_type_label = QLabel("옵션 이미지번역 엔진", self)
|
||||||
self.optionIMGTrans_type_combo = QComboBox(self)
|
self.optionIMGTrans_type_combo = QComboBox(self)
|
||||||
self.optionIMGTrans_type_combo.setObjectName("optionIMGTrans_type_combo")
|
self.optionIMGTrans_type_combo.setObjectName("optionIMGTrans_type_combo")
|
||||||
self.optionIMGTrans_type_combo.addItems(["대체", "CPU", "자체서버"])
|
self.optionIMGTrans_type_combo.addItems(["CPU", "자체서버"])
|
||||||
self.optionIMGTrans_type_combo.setCurrentText("대체")
|
self.optionIMGTrans_type_combo.setCurrentText("CPU")
|
||||||
self.optionIMGTrans_type_combo.currentTextChanged.connect(self.on_optionIMGTrans_type_combo_changed)
|
self.optionIMGTrans_type_combo.currentTextChanged.connect(self.on_optionIMGTrans_type_combo_changed)
|
||||||
self.optionIMGTrans_type_widget.enterEvent = lambda e: self.show_manual_html(
|
self.optionIMGTrans_type_widget.enterEvent = lambda e: self.show_manual_html(
|
||||||
self.option_manual_group,
|
self.option_manual_group,
|
||||||
|
|
@ -3250,7 +3269,6 @@ class MAIN_GUI(QMainWindow):
|
||||||
<p>옵션 이미지 번역에 사용할 번역 방식을 선택합니다:</p>
|
<p>옵션 이미지 번역에 사용할 번역 방식을 선택합니다:</p>
|
||||||
<p><b>자체서버</b>: 자체 API 서버를 활용한 고품질 이미지 번역 (VIP 등급)</p>
|
<p><b>자체서버</b>: 자체 API 서버를 활용한 고품질 이미지 번역 (VIP 등급)</p>
|
||||||
<p><b>CPU</b>: 로컬 CPU를 사용한 이미지 번역 (Basic, Premium 등급 이상)</p>
|
<p><b>CPU</b>: 로컬 CPU를 사용한 이미지 번역 (Basic, Premium 등급 이상)</p>
|
||||||
<p><b>대체</b>: 대체 이미지 번역 (모든 등급)</p>
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
self.optionIMGTrans_type_widget.leaveEvent = lambda e: self.reset_manual(self.option_manual_group, self.option_manual_label)
|
self.optionIMGTrans_type_widget.leaveEvent = lambda e: self.reset_manual(self.option_manual_group, self.option_manual_label)
|
||||||
|
|
@ -3633,7 +3651,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
"""
|
"""
|
||||||
<p>상품의 썸네일에 포함된 중국어 텍스트를 한국어로 번역합니다.</p>
|
<p>상품의 썸네일에 포함된 중국어 텍스트를 한국어로 번역합니다.</p>
|
||||||
<p>번역된 텍스트는 원본 이미지의 위치와 스타일을 최대한 유지합니다.</p>
|
<p>번역된 텍스트는 원본 이미지의 위치와 스타일을 최대한 유지합니다.</p>
|
||||||
<p>번역 엔진(파파고/대체)을 선택가능</p>
|
<p>번역 엔진(CPU/자체번역)을 선택가능</p>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
self.thumb_widget.leaveEvent = lambda e: self.reset_manual(self.thumbnail_manual_group, self.thumbnail_manual_label)
|
self.thumb_widget.leaveEvent = lambda e: self.reset_manual(self.thumbnail_manual_group, self.thumbnail_manual_label)
|
||||||
|
|
@ -3649,8 +3667,8 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.thumb_trans_type_label = QLabel("썸네일번역 엔진", self)
|
self.thumb_trans_type_label = QLabel("썸네일번역 엔진", self)
|
||||||
self.thumb_trans_type_combo = QComboBox(self)
|
self.thumb_trans_type_combo = QComboBox(self)
|
||||||
self.thumb_trans_type_combo.setObjectName("thumb_trans_type_combo")
|
self.thumb_trans_type_combo.setObjectName("thumb_trans_type_combo")
|
||||||
self.thumb_trans_type_combo.addItems(["대체", "CPU", "자체서버"])
|
self.thumb_trans_type_combo.addItems(["CPU", "자체서버"])
|
||||||
self.thumb_trans_type_combo.setCurrentText("대체")
|
self.thumb_trans_type_combo.setCurrentText("CPU")
|
||||||
self.thumb_trans_type_combo.currentTextChanged.connect(self.on_thumb_trans_type_combo_changed)
|
self.thumb_trans_type_combo.currentTextChanged.connect(self.on_thumb_trans_type_combo_changed)
|
||||||
self.thumb_trans_type_widget.enterEvent = lambda e: self.show_manual_html(
|
self.thumb_trans_type_widget.enterEvent = lambda e: self.show_manual_html(
|
||||||
self.thumbnail_manual_group,
|
self.thumbnail_manual_group,
|
||||||
|
|
@ -3660,7 +3678,6 @@ class MAIN_GUI(QMainWindow):
|
||||||
<p>썸네일 이미지 번역에 사용할 번역 엔진을 선택합니다:</p>
|
<p>썸네일 이미지 번역에 사용할 번역 엔진을 선택합니다:</p>
|
||||||
<p><b>자체서버</b>: 자체 API 서버를 활용한 고품질 이미지 번역 (VIP 등급)</p>
|
<p><b>자체서버</b>: 자체 API 서버를 활용한 고품질 이미지 번역 (VIP 등급)</p>
|
||||||
<p><b>CPU</b>: 로컬 CPU를 사용한 이미지 번역 (Basic, Premium 등급 이상)</p>
|
<p><b>CPU</b>: 로컬 CPU를 사용한 이미지 번역 (Basic, Premium 등급 이상)</p>
|
||||||
<p><b>대체</b>: 대체 이미지 번역 (모든 등급)</p>
|
|
||||||
<p>이미지의 특성과 텍스트 스타일에 따라 적합한 방식을 선택하세요.</p>
|
<p>이미지의 특성과 텍스트 스타일에 따라 적합한 방식을 선택하세요.</p>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
@ -3705,7 +3722,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.thumb_rmb_count_input.setObjectName("thumb_rmb_count_input")
|
self.thumb_rmb_count_input.setObjectName("thumb_rmb_count_input")
|
||||||
self.thumb_rmb_count_input.setMinimum(1)
|
self.thumb_rmb_count_input.setMinimum(1)
|
||||||
self.thumb_rmb_count_input.setMaximum(10)
|
self.thumb_rmb_count_input.setMaximum(10)
|
||||||
self.thumb_rmb_count_input.setValue(self.settings_manager.get_value("thumb_rmb_count", 3))
|
self.thumb_rmb_count_input.setValue(self.settings_manager.get_value("thumb_rmb_count", 1))
|
||||||
self.thumb_rmb_count_input.valueChanged.connect(lambda value: self.update_thumb_rmb_count(value))
|
self.thumb_rmb_count_input.valueChanged.connect(lambda value: self.update_thumb_rmb_count(value))
|
||||||
self.thumb_rmb_count_input.valueChanged.connect(lambda value: self.universal_input_handler(self.thumb_rmb_count_input, value))
|
self.thumb_rmb_count_input.valueChanged.connect(lambda value: self.universal_input_handler(self.thumb_rmb_count_input, value))
|
||||||
self.thumb_rmb_count_input.setFixedWidth(60)
|
self.thumb_rmb_count_input.setFixedWidth(60)
|
||||||
|
|
@ -3860,8 +3877,8 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.detail_IMGTrans_type_label = QLabel("상페번역엔진", self)
|
self.detail_IMGTrans_type_label = QLabel("상페번역엔진", self)
|
||||||
self.detail_IMGTrans_type_combo = QComboBox(self)
|
self.detail_IMGTrans_type_combo = QComboBox(self)
|
||||||
self.detail_IMGTrans_type_combo.setObjectName("detail_IMGTrans_type_combo")
|
self.detail_IMGTrans_type_combo.setObjectName("detail_IMGTrans_type_combo")
|
||||||
self.detail_IMGTrans_type_combo.addItems(["대체", "CPU", "자체서버"])
|
self.detail_IMGTrans_type_combo.addItems(["CPU", "자체서버"])
|
||||||
self.detail_IMGTrans_type_combo.setCurrentText("대체")
|
self.detail_IMGTrans_type_combo.setCurrentText("CPU")
|
||||||
self.detail_IMGTrans_type_combo.currentTextChanged.connect(self.on_detailIMGTrans_type_combo_changed)
|
self.detail_IMGTrans_type_combo.currentTextChanged.connect(self.on_detailIMGTrans_type_combo_changed)
|
||||||
self.detail_IMGTrans_type_widget.enterEvent = lambda e: self.show_manual_html(
|
self.detail_IMGTrans_type_widget.enterEvent = lambda e: self.show_manual_html(
|
||||||
self.detail_manual_group,
|
self.detail_manual_group,
|
||||||
|
|
@ -3871,7 +3888,6 @@ class MAIN_GUI(QMainWindow):
|
||||||
<p>상세페이지 이미지 번역에 사용할 번역 방식을 선택합니다:</p>
|
<p>상세페이지 이미지 번역에 사용할 번역 방식을 선택합니다:</p>
|
||||||
<p><b>자체서버</b>: 자체 API 서버를 활용한 고품질 이미지 번역 (VIP 등급)</p>
|
<p><b>자체서버</b>: 자체 API 서버를 활용한 고품질 이미지 번역 (VIP 등급)</p>
|
||||||
<p><b>CPU</b>: 로컬 CPU를 사용한 이미지 번역 (Basic, Premium 등급 이상)</p>
|
<p><b>CPU</b>: 로컬 CPU를 사용한 이미지 번역 (Basic, Premium 등급 이상)</p>
|
||||||
<p><b>대체</b>: 자체 이미지 번역 (모든 등급)</p>
|
|
||||||
<p>서버의 부담을 줄이기 위해 이미지번역속도가 느릴 수 있습니다.</p>
|
<p>서버의 부담을 줄이기 위해 이미지번역속도가 느릴 수 있습니다.</p>
|
||||||
<p>OCR을 설정할 경우 금지단어가 포함된 이미지는 자동삭제 됩니다.</p>
|
<p>OCR을 설정할 경우 금지단어가 포함된 이미지는 자동삭제 됩니다.</p>
|
||||||
<p>OCR을 설정할 경우 중국어가 없는 이미지는 번역되지 않아, 처리속도가 빨라집니다.</p>
|
<p>OCR을 설정할 경우 중국어가 없는 이미지는 번역되지 않아, 처리속도가 빨라집니다.</p>
|
||||||
|
|
@ -3889,7 +3905,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.watermark_toggle_layout = QHBoxLayout(self.watermark_widget)
|
self.watermark_toggle_layout = QHBoxLayout(self.watermark_widget)
|
||||||
self.watermark_toggle_label = QLabel("상페워터마크", self)
|
self.watermark_toggle_label = QLabel("상페워터마크", self)
|
||||||
self.watermark_toggle = ToggleSwitch(self)
|
self.watermark_toggle = ToggleSwitch(self)
|
||||||
self.watermark_toggle.setObjectName("watermark")
|
self.watermark_toggle.setObjectName("watermark_toggle")
|
||||||
self.watermark_toggle.clicked.connect(lambda checked: self.universal_input_handler(self.watermark_toggle, checked))
|
self.watermark_toggle.clicked.connect(lambda checked: self.universal_input_handler(self.watermark_toggle, checked))
|
||||||
self.watermark_widget.enterEvent = lambda e: self.show_manual_html(
|
self.watermark_widget.enterEvent = lambda e: self.show_manual_html(
|
||||||
self.detail_manual_group,
|
self.detail_manual_group,
|
||||||
|
|
@ -3916,7 +3932,7 @@ class MAIN_GUI(QMainWindow):
|
||||||
self.watermark_text_input = QLineEdit()
|
self.watermark_text_input = QLineEdit()
|
||||||
self.watermark_text_input.setObjectName("watermark_text_input")
|
self.watermark_text_input.setObjectName("watermark_text_input")
|
||||||
self.watermark_text_input.setText(self.toggle_states.get("watermark_text", ""))
|
self.watermark_text_input.setText(self.toggle_states.get("watermark_text", ""))
|
||||||
self.on_watermark_toggle_clicked
|
# self.watermark_text_input.connect(self.on_watermark_toggle_clicked())
|
||||||
self.watermark_text_input.textChanged.connect(lambda text: self.universal_input_handler(self.watermark_text_input, text))
|
self.watermark_text_input.textChanged.connect(lambda text: self.universal_input_handler(self.watermark_text_input, text))
|
||||||
# self.watermark_text_input.setEnabled(False)
|
# self.watermark_text_input.setEnabled(False)
|
||||||
self.watermark_text_widget.enterEvent = lambda e: self.show_manual_html(
|
self.watermark_text_widget.enterEvent = lambda e: self.show_manual_html(
|
||||||
|
|
|
||||||
|
|
@ -1002,6 +1002,19 @@ class OptionHandler:
|
||||||
original_name = item.get("original_name")
|
original_name = item.get("original_name")
|
||||||
name_input_elem = item.get("name_input_elem")
|
name_input_elem = item.get("name_input_elem")
|
||||||
|
|
||||||
|
# stale ElementHandle 방지: detach 여부 확인 후 필요 시 재조회
|
||||||
|
try:
|
||||||
|
if name_input_elem:
|
||||||
|
await name_input_elem.get_property("tagName")
|
||||||
|
except Exception:
|
||||||
|
name_input_elem = None
|
||||||
|
|
||||||
|
if not name_input_elem:
|
||||||
|
parent_elem = item.get("checkbox_elem")
|
||||||
|
if parent_elem:
|
||||||
|
name_input_elem = await parent_elem.query_selector("input.ant-input")
|
||||||
|
item["name_input_elem"] = name_input_elem
|
||||||
|
|
||||||
if not original_name or not name_input_elem:
|
if not original_name or not name_input_elem:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -1478,6 +1491,12 @@ class OptionHandler:
|
||||||
has_valid_image = True
|
has_valid_image = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# 체크박스 선택 여부 확인 – 노출되지 않은 옵션이면 건너뜀
|
||||||
|
checkbox_input = await item.locator("label.ant-checkbox-wrapper input[type='checkbox']")
|
||||||
|
is_checked = await checkbox_input.is_checked() if checkbox_input else False
|
||||||
|
if not is_checked:
|
||||||
|
continue
|
||||||
|
|
||||||
if has_valid_image:
|
if has_valid_image:
|
||||||
valid_items.append(item)
|
valid_items.append(item)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,8 @@ class ThumbnailHandler:
|
||||||
original_image_url=image_url,
|
original_image_url=image_url,
|
||||||
file_prefix="thumb_rmb"
|
file_prefix="thumb_rmb"
|
||||||
)
|
)
|
||||||
# result = await self.imageProcessor.process_single_image(page=self.page, original_image_url=image_url, index=idx, delay=1.0, file_prefix="thumb_rmb") # 누끼기능 충돌로 번역대체
|
if not result:
|
||||||
|
result = await self.imageProcessor.process_single_image(page=self.page, original_image_url=image_url, index=idx, delay=1.0, file_prefix="thumb_rmb") # 누끼기능 충돌로 번역대체
|
||||||
|
|
||||||
elif thumb:
|
elif thumb:
|
||||||
result = await self.imageProcessor.process_single_image(page=self.page, original_image_url=image_url, index=idx, delay=1.0, file_prefix="thumb")
|
result = await self.imageProcessor.process_single_image(page=self.page, original_image_url=image_url, index=idx, delay=1.0, file_prefix="thumb")
|
||||||
|
|
|
||||||
|
|
@ -204,19 +204,25 @@ class ClipboardImageManager:
|
||||||
bbox = draw.textbbox((0, 0), watermark_text, font=font)
|
bbox = draw.textbbox((0, 0), watermark_text, font=font)
|
||||||
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||||
|
|
||||||
|
# 텍스트 크기가 0인 경우 예외 처리 (빈 문자열 등)
|
||||||
|
if text_width == 0 or text_height == 0:
|
||||||
|
self.logger.log("워터마크 텍스트 크기를 계산할 수 없어 워터마크를 건너뜁니다.", level=logging.WARNING)
|
||||||
|
return image
|
||||||
|
|
||||||
# 이미지 크기
|
# 이미지 크기
|
||||||
width, height = image.size
|
width, height = image.size
|
||||||
|
|
||||||
# 워터마크 레이어 생성
|
# 워터마크 레이어 생성
|
||||||
watermark_layer = Image.new("RGBA", (width, height)) # RGBA 이미지 생성
|
watermark_layer = Image.new("RGBA", (width, height)) # RGBA 이미지 생성
|
||||||
|
|
||||||
# 지그재그 간격 설정
|
# 지그재그 간격 설정 (0 방지를 위해 최소 1 보장)
|
||||||
zigzag_step = int(text_height * 2) # Y축의 지그재그 간격
|
zigzag_step = max(1, int(text_height * 2)) # Y축의 지그재그 간격
|
||||||
|
|
||||||
|
step_x = max(1, int(text_width * 3)) # X축 반복 간격도 0 방지
|
||||||
|
|
||||||
# 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태)
|
# 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태)
|
||||||
for y in range(0, height, zigzag_step):
|
for y in range(0, height, zigzag_step):
|
||||||
for x in range(0, width, int(text_width * 3)): # 3배 너비 간격으로 반복
|
for x in range(0, width, step_x): # 3배 너비 간격으로 반복
|
||||||
# 텍스트가 한 줄씩 지그재그 형태로 X축을 교차하여 이동
|
# 텍스트가 한 줄씩 지그재그 형태로 X축을 교차하여 이동
|
||||||
x_offset = (y // zigzag_step) % 2 * int(text_width * 1.5) # 짝수 행에서는 X축을 약간 이동
|
x_offset = (y // zigzag_step) % 2 * int(text_width * 1.5) # 짝수 행에서는 X축을 약간 이동
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiofiles
|
import requests
|
||||||
|
import time
|
||||||
import logging
|
import logging
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -76,7 +77,7 @@ class ImageProcessor3:
|
||||||
self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url", None)
|
self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url", None)
|
||||||
# self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url_local", None)
|
# self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url_local", None)
|
||||||
|
|
||||||
if self.is_frozen():
|
if not self.is_frozen():
|
||||||
self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url_local", None)
|
self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url_local", None)
|
||||||
|
|
||||||
self.request_ai_server = Request_AI_Server(logger=self.logger, inpaint_server_url=self.request_inpainting_server_url, rembg_server_url=self.request_rembg_server_url)
|
self.request_ai_server = Request_AI_Server(logger=self.logger, inpaint_server_url=self.request_inpainting_server_url, rembg_server_url=self.request_rembg_server_url)
|
||||||
|
|
@ -181,7 +182,8 @@ class ImageProcessor3:
|
||||||
self.logger.log(f"이미지 {index+1} 처리 시작: {original_image_url} - {processing_mode}", level=logging.DEBUG)
|
self.logger.log(f"이미지 {index+1} 처리 시작: {original_image_url} - {processing_mode}", level=logging.DEBUG)
|
||||||
|
|
||||||
# 1. 이미지 다운로드
|
# 1. 이미지 다운로드
|
||||||
local_image_path = await self.download_image(page, original_image_url, index, file_prefix)
|
local_image_path = await self.download_image(image_url=original_image_url, index=index, file_prefix=file_prefix)
|
||||||
|
|
||||||
if not local_image_path:
|
if not local_image_path:
|
||||||
self.logger.log(f"이미지 {index+1} 다운로드 실패, 원본 URL 반환", level=logging.WARNING)
|
self.logger.log(f"이미지 {index+1} 다운로드 실패, 원본 URL 반환", level=logging.WARNING)
|
||||||
return {'status': 'failed', 'path': original_image_url, 'error': '다운로드 실패'}
|
return {'status': 'failed', 'path': original_image_url, 'error': '다운로드 실패'}
|
||||||
|
|
@ -365,7 +367,7 @@ class ImageProcessor3:
|
||||||
img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_img_{index+1}.png")
|
img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_img_{index+1}.png")
|
||||||
|
|
||||||
watermark_text=self.toggle_states.get("watermark_text", "이미지 저작권 보유")
|
watermark_text=self.toggle_states.get("watermark_text", "이미지 저작권 보유")
|
||||||
is_watermark_enabled = watermark_text != "" or self.toggle_states.get("watermark", False)
|
is_watermark_enabled = watermark_text != "" or self.toggle_states.get("watermark_toggle", False)
|
||||||
|
|
||||||
self.logger.log(f"watermark_text: {watermark_text}", level=logging.DEBUG)
|
self.logger.log(f"watermark_text: {watermark_text}", level=logging.DEBUG)
|
||||||
self.logger.log(f"is_watermark_enabled: {is_watermark_enabled}", level=logging.DEBUG)
|
self.logger.log(f"is_watermark_enabled: {is_watermark_enabled}", level=logging.DEBUG)
|
||||||
|
|
@ -394,9 +396,6 @@ class ImageProcessor3:
|
||||||
|
|
||||||
def download_image(self, image_url, index, file_prefix="", max_retries=3):
|
def download_image(self, image_url, index, file_prefix="", max_retries=3):
|
||||||
"""Requests를 사용해 이미지를 다운로드합니다"""
|
"""Requests를 사용해 이미지를 다운로드합니다"""
|
||||||
import requests
|
|
||||||
import time
|
|
||||||
import random
|
|
||||||
|
|
||||||
# 로컬 파일 경로면 바로 반환
|
# 로컬 파일 경로면 바로 반환
|
||||||
if os.path.isfile(image_url):
|
if os.path.isfile(image_url):
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,11 @@ class PostImageManager:
|
||||||
bbox = draw.textbbox((0, 0), watermark_text, font=font)
|
bbox = draw.textbbox((0, 0), watermark_text, font=font)
|
||||||
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||||
|
|
||||||
|
# 텍스트 크기가 0인 경우 예외 처리 (빈 문자열 등)
|
||||||
|
if text_width == 0 or text_height == 0:
|
||||||
|
self.logger.log("워터마크 텍스트 크기를 계산할 수 없어 워터마크를 건너뜁니다.", level=logging.WARNING)
|
||||||
|
return image_data
|
||||||
|
|
||||||
# 이미지 크기
|
# 이미지 크기
|
||||||
width, height = image_data.size
|
width, height = image_data.size
|
||||||
|
|
||||||
|
|
@ -116,6 +121,7 @@ class PostImageManager:
|
||||||
zigzag_step = max(1, int(text_height * 2)) # Y축 간격
|
zigzag_step = max(1, int(text_height * 2)) # Y축 간격
|
||||||
x_step = max(1, int(text_width * 3)) # X축 간격
|
x_step = max(1, int(text_width * 3)) # X축 간격
|
||||||
|
|
||||||
|
step_x = max(1, int(text_width * 3)) # X축 반복 간격도 0 방지
|
||||||
|
|
||||||
# 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태)
|
# 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태)
|
||||||
for y in range(0, height, zigzag_step):
|
for y in range(0, height, zigzag_step):
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -6,16 +6,21 @@
|
||||||
### 오류 수정
|
### 오류 수정
|
||||||
- 옵션명 번역 중 특수문자 처리 누락 추가
|
- 옵션명 번역 중 특수문자 처리 누락 추가
|
||||||
- 옵션 번역이 항상 CPU 나 자체서버로 처리되는 문제 수정
|
- 옵션 번역이 항상 CPU 나 자체서버로 처리되는 문제 수정
|
||||||
|
- 옵션선택시 간헐적으로 DOM을 못찾는 문제 수정
|
||||||
- 누끼만 선택했을 경우 상품수정이 멈추는 문제 수정
|
- 누끼만 선택했을 경우 상품수정이 멈추는 문제 수정
|
||||||
- 이미지프로세서 서버 변경
|
- 이미지프로세서 서버 변경
|
||||||
- 상세페이지 소개글 입력방법 개선
|
- 상세페이지 소개글 입력방법 개선
|
||||||
- 상품수집 오류 발생시 적정가격이 낮게 산정되는 문제 수정
|
- 상품수집 오류 발생시 적정가격이 낮게 산정되는 문제 수정
|
||||||
|
- 누끼 실패시 번역으로 백업
|
||||||
|
- 로거모듈 오류 수정
|
||||||
|
- 프리미엄등급에서 다른번역을 선택해도 대체번역으로 선택되는 문제 수정
|
||||||
|
- 워터마크 선택 상태가 저장되지 않는 문제 수정 및 기본값 OFF로 변경
|
||||||
|
|
||||||
|
|
||||||
### 기능 추가
|
### 기능 추가
|
||||||
- 이미지번역(옵션/섬네일/상세페이지)에서 대체번역 완전제외
|
- 이미지번역(옵션/섬네일/상세페이지)에서 대체번역 완전제외
|
||||||
- 가격설정(크무비포함)의 가격범위 최대 100만원 -> 1000만원 으로 변경
|
- 가격설정(크무비포함)의 가격범위 최대 100만원 -> 1000만원 으로 변경
|
||||||
- 기본 워터마크 글자제거
|
- 대체번역 삭제
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 3.9.8 업데이트 로그
|
# 3.9.8 업데이트 로그
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue