옵션이미지 수정중
This commit is contained in:
parent
f37edc088f
commit
d758a11812
3581
appTranslator.log
3581
appTranslator.log
File diff suppressed because one or more lines are too long
|
|
@ -680,12 +680,12 @@ class BrowserController:
|
|||
self.logger.error(f"이미지 URL 추출 & 옵션데이터 입력 처리 중 오류: {e}", exc_info=True)
|
||||
return image_urls if image_urls else []
|
||||
|
||||
def paste_image_in_chrome(self, clipboardImageManager, url, is_success_translated, toggle_states, is_watermark=False, watermark_text= "", opacity_percent=25):
|
||||
def paste_image_in_chrome(self, clipboardImageManager, url, is_success_translated, toggle_states, is_watermark=False, watermark_text= ""):
|
||||
"""크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력"""
|
||||
self.logger.debug("크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력")
|
||||
try:
|
||||
self.switch_to_chrome() # 크롬으로 포커스 이동
|
||||
clipboardImageManager.process_clipboard(original_url=url, is_success_translated=is_success_translated, toggle_states=toggle_states, opacity_percent=opacity_percent) # 클립보드 내용을 처리
|
||||
clipboardImageManager.process_clipboard(original_url=url, is_success_translated=is_success_translated, toggle_states=toggle_states) # 클립보드 내용을 처리
|
||||
# clipboard_content = pyperclip.paste()
|
||||
if clipboardImageManager.is_clipboard_image():
|
||||
pyautogui.hotkey('ctrl', 'v') # 클립보드 이미지 붙여넣기
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class ClipboardImageManager:
|
|||
if image:
|
||||
# 이미지를 저장 경로에 저장
|
||||
self.logger.info(f"이미지 저장 완료 : {path}")
|
||||
image.save(path)
|
||||
image.save(path, format='PNG')
|
||||
return path
|
||||
|
||||
except Exception as e:
|
||||
|
|
@ -204,7 +204,7 @@ class ClipboardImageManager:
|
|||
self.logger.debug("이미지 다운로드 최대 재시도 횟수를 초과했습니다.")
|
||||
return None
|
||||
|
||||
def process_clipboard(self, original_url, is_success_translated, toggle_states, opacity_percent, path=None):
|
||||
def process_clipboard(self, original_url, is_success_translated, toggle_states, path=None):
|
||||
"""클립보드의 내용을 처리하고, 필요한 경우 이미지 변환, 크롭 또는 클립보드 비우기"""
|
||||
|
||||
try:
|
||||
|
|
@ -214,6 +214,9 @@ class ClipboardImageManager:
|
|||
watermark_text = toggle_states['watermark_text']
|
||||
self.logger.debug(f"watermark_text : {watermark_text}")
|
||||
|
||||
opacity_percent = toggle_states['opacity_percent']
|
||||
self.logger.debug(f"opacity_percent : {opacity_percent}")
|
||||
|
||||
clipboard_data = self.get_clipboard_data()
|
||||
|
||||
self.logger.debug("clipboard_data")
|
||||
|
|
@ -242,7 +245,7 @@ class ClipboardImageManager:
|
|||
self.set_image_to_clipboard(cropped_image) # 클립보드에 저장
|
||||
if path:
|
||||
self.logger.debug("이미지 저장 시도...")
|
||||
self.save_image_to_path(path)
|
||||
self.save_image_to_path(cropped_image, path)
|
||||
else:
|
||||
self.logger.debug("이미지 가로 크기 200픽셀 이하: 클립보드 비움.")
|
||||
self.clear_clipboard()
|
||||
|
|
@ -270,7 +273,7 @@ class ClipboardImageManager:
|
|||
self.set_image_to_clipboard(cropped_image) # 클립보드에 저장
|
||||
if path:
|
||||
self.logger.debug("이미지 저장 시도...")
|
||||
self.save_image_to_path(path)
|
||||
self.save_image_to_path(cropped_image, path)
|
||||
|
||||
else:
|
||||
self.logger.debug("이미지 가로 크기 200픽셀 이하: 클립보드 비움.")
|
||||
|
|
@ -293,7 +296,7 @@ class ClipboardImageManager:
|
|||
self.set_image_to_clipboard(image) # 크롭 없이 저장
|
||||
if path:
|
||||
self.logger.debug("이미지 저장 시도...")
|
||||
self.save_image_to_path(path)
|
||||
self.save_image_to_path(image, path)
|
||||
else:
|
||||
self.logger.debug("원본 이미지 다운로드 실패.")
|
||||
else:
|
||||
|
|
|
|||
19
config.ini
19
config.ini
|
|
@ -30,10 +30,21 @@ checkbox_selector_template = '#productMainContentContainerId li:nth-child({index
|
|||
image_selector_template = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > img'
|
||||
; price_selector_template = '#productMainContentContainerId li:nth-child({index}) sup'
|
||||
price_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[3]/div[1]/div[2]/button/span/sup'
|
||||
delete_button_selector_template = '#productMainContentContainerId > div.sc-TOgAA.fZvEqY > div:nth-child(2) > div > div > div:nth-child(2) > div > div.sc-cFShuL.dbIeho > div > div > div.ant-collapse-content.ant-collapse-content-active > div > div > div.sc-fGdiLE.iyXMeU > div.ant-list.ant-list-split.css-1li46mu > div > div > ul > li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div'
|
||||
confirm_delete_button_locator = 'body > div:nth-child(18) > div > div.ant-modal-wrap.ant-modal-confirm-centered.ant-modal-centered > div > div.sc-ddjGPC.jbwEYW > div > div > div > div.ant-modal-confirm-btns > button.ant-btn.css-1li46mu.ant-btn-primary.ant-btn-dangerous'
|
||||
add_button_selector_template = '#productMainContentContainerId > div.sc-TOgAA.fZvEqY > div:nth-child(2) > div > div > div:nth-child(2) > div > div.sc-cFShuL.dbIeho > div > div > div.ant-collapse-content.ant-collapse-content-active > div > div > div.sc-fGdiLE.iyXMeU > div.ant-list.ant-list-split.css-1li46mu > div > div > ul > li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div > img'
|
||||
file_input_locator = 'input[type="file"]'
|
||||
|
||||
# 옵션 상자
|
||||
option_box_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc'
|
||||
excluded_option_marker = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dfauwV.bXsMpn'
|
||||
delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) span:has-text("삭제")'
|
||||
confirm_delete_button_selector = '.ant-modal.css-1li46mu.ant-modal-confirm.ant-modal-confirm-confirm button:has-text("삭제")'
|
||||
; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dRGYJT.hmQUGb'
|
||||
add_button_selector = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div'
|
||||
; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-krITIZ.ckztYT'
|
||||
; file_upload_button_selector = '.ant-modal-content button:has-text("클릭 or 드레그로 파일 업로드")'
|
||||
file_upload_button_selector = 'div.ant-modal-body > span > div > span > div'
|
||||
|
||||
; confirm_upload_button_selector = '.ant-modal-content button:has-text("이미지 삽입")'
|
||||
confirm_upload_button_selector = 'div.ant-modal-footer > button[type=\"button\"].ant-btn.css-1li46mu.ant-btn-primary'
|
||||
|
||||
low_order_button_locator = 'button:has-text("가격 낮은 순")'
|
||||
AtoZ_button_locator = 'button:has-text("A-Z")'
|
||||
one_to_nine_button_locator = 'button:has-text("1-99")'
|
||||
|
|
|
|||
14
gui.py
14
gui.py
|
|
@ -55,7 +55,7 @@ class TranslationApp(QWidget):
|
|||
|
||||
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, db_manager=self.db_manager, initial_db_path=self.initial_db_path, user_db_path=self.user_db_path, debug=self.debug)
|
||||
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, watermark_font_size=36, debug=self.debug)
|
||||
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.logger, self.vertexAI, self.debug)
|
||||
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.clipboardImageManager, self.logger, self.vertexAI, self.debug)
|
||||
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.optionHandler, self.vertexAI, self.cmb_diag, self.debug)
|
||||
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
|
||||
self.running = False
|
||||
|
|
@ -93,7 +93,7 @@ class TranslationApp(QWidget):
|
|||
'ed_mode': False, # 등록된 상품을 수정할때
|
||||
'watermark': False, # 워터마크 토글 추가
|
||||
'watermark_text': "WaterMark", # 워터마크 텍스트 저장
|
||||
'opacity_percent': 35, # 워터마크 투명도
|
||||
'opacity_percent': 25, # 워터마크 투명도
|
||||
}
|
||||
|
||||
# 이전에 저장된 설정 불러오기
|
||||
|
|
@ -689,8 +689,9 @@ class TranslationApp(QWidget):
|
|||
self.logger.info('신규 상품 등록 페이지로 이동 중...')
|
||||
await self.browser_controller.go_to_new_product_page()
|
||||
|
||||
# 옵션핸들러에 초기화된 page 객체 전달.
|
||||
# 각 핸들러에 초기화된 page 객체 전달.
|
||||
self.optionHandler.update_page(self.browser_controller.page)
|
||||
self.optionHandler.update_whale(self.whale_translator)
|
||||
self.titleHandler.update_page(self.browser_controller.page)
|
||||
self.priceHandler.update_page(self.browser_controller.page)
|
||||
|
||||
|
|
@ -803,8 +804,8 @@ class TranslationApp(QWidget):
|
|||
if not product_buttons:
|
||||
self.logger.debug('수정할 상품이 없습니다. 작업을 종료합니다.')
|
||||
break
|
||||
|
||||
deleted_imgs = self.browser_controller.deleted_img_urls_from_logs()
|
||||
if self.toggle_states['recovery_mode']:
|
||||
deleted_imgs = self.browser_controller.deleted_img_urls_from_logs()
|
||||
|
||||
# 5. 각 상품에 대해 번역 작업 수행
|
||||
for index, button in enumerate(product_buttons, start=1):
|
||||
|
|
@ -931,8 +932,7 @@ class TranslationApp(QWidget):
|
|||
self.logger.debug(f"웨일 브라우저를 활용한 이미지 번역 프로세스")
|
||||
is_success_translated = self.whale_translator.translate_image(url)
|
||||
|
||||
opacity_percent=35
|
||||
is_paste_success = self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url, is_success_translated, self.toggle_states, opacity_percent=opacity_percent)
|
||||
is_paste_success = self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url, is_success_translated, self.toggle_states)
|
||||
if is_paste_success:
|
||||
self.logger.debug(f"{url} gui 이미지 붙여넣기 성공")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -88,10 +88,13 @@ class LocatorManager:
|
|||
'checkbox_selector_template': self.config.get('OptionLocators', 'checkbox_selector_template').strip("'"),
|
||||
'image_selector_template': self.config.get('OptionLocators', 'image_selector_template').strip("'"),
|
||||
'price_selector_template': self.config.get('OptionLocators', 'price_selector_template').strip("'"),
|
||||
'delete_button_selector_template': self.config.get('OptionLocators', 'delete_button_selector_template').strip("'"),
|
||||
'confirm_delete_button_locator': self.config.get('OptionLocators', 'confirm_delete_button_locator').strip("'"),
|
||||
'add_button_selector_template': self.config.get('OptionLocators', 'add_button_selector_template').strip("'"),
|
||||
'file_input_locator': self.config.get('OptionLocators', 'file_input_locator').strip("'"),
|
||||
'option_box_selector': self.config.get('OptionLocators', 'option_box_selector').strip("'"),
|
||||
'excluded_option_marker': self.config.get('OptionLocators', 'excluded_option_marker').strip("'"),
|
||||
'delete_button_selector': self.config.get('OptionLocators', 'delete_button_selector').strip("'"),
|
||||
'confirm_delete_button_selector': self.config.get('OptionLocators', 'confirm_delete_button_selector').strip("'"),
|
||||
'add_button_selector': self.config.get('OptionLocators', 'add_button_selector').strip("'"),
|
||||
'file_upload_button_selector': self.config.get('OptionLocators', 'file_upload_button_selector').strip("'"),
|
||||
'confirm_upload_button_selector': self.config.get('OptionLocators', 'confirm_upload_button_selector').strip("'"),
|
||||
'low_order_button_locator': self.config.get('OptionLocators', 'low_order_button_locator').strip("'"),
|
||||
'AtoZ_button_locator': self.config.get('OptionLocators', 'AtoZ_button_locator').strip("'"),
|
||||
'one_to_nine_button_locator': self.config.get('OptionLocators', 'one_to_nine_button_locator').strip("'"),
|
||||
|
|
|
|||
255
option.py
255
option.py
|
|
@ -3,12 +3,14 @@ import pyautogui
|
|||
from datetime import datetime
|
||||
import numpy as np
|
||||
import asyncio, time, math
|
||||
import tempfile, os
|
||||
|
||||
class OptionHandler:
|
||||
def __init__(self, locator_manager, browser_controller, whale_translator, logger, vertexAI, debug_flag=False):
|
||||
def __init__(self, locator_manager, browser_controller, whale_translator, clipboardImageManager, logger, vertexAI, debug_flag=False):
|
||||
self.locator_manager = locator_manager
|
||||
self.browser_controller = browser_controller
|
||||
self.page = self.browser_controller.page
|
||||
self.clipboardImageManager = clipboardImageManager
|
||||
self.logger = logger
|
||||
self.debug_flag = debug_flag
|
||||
self.vertexAItranslator = vertexAI
|
||||
|
|
@ -30,10 +32,15 @@ class OptionHandler:
|
|||
self.checkbox_selector_template = self.locator_manager.get_locator('OptionLocators', 'checkbox_selector_template')
|
||||
self.image_selector_template = self.locator_manager.get_locator('OptionLocators', 'image_selector_template')
|
||||
self.price_selector_template = self.locator_manager.get_locator('OptionLocators', 'price_selector_template')
|
||||
self.delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'delete_button_selector_template')
|
||||
self.confirm_delete_button_locator = self.locator_manager.get_locator('OptionLocators', 'confirm_delete_button_locator')
|
||||
self.add_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'add_button_selector_template')
|
||||
self.file_input_locator = self.locator_manager.get_locator('OptionLocators', 'file_input_locator')
|
||||
|
||||
self.option_box_selector = self.locator_manager.get_locator('OptionLocators', 'option_box_selector')
|
||||
self.excluded_option_marker = self.locator_manager.get_locator('OptionLocators', 'excluded_option_marker')
|
||||
self.delete_button_selector = self.locator_manager.get_locator('OptionLocators', 'delete_button_selector')
|
||||
self.confirm_delete_button_selector = self.locator_manager.get_locator('OptionLocators', 'confirm_delete_button_selector')
|
||||
self.add_button_selector = self.locator_manager.get_locator('OptionLocators', 'add_button_selector')
|
||||
self.file_upload_button_selector = self.locator_manager.get_locator('OptionLocators', 'file_upload_button_selector')
|
||||
self.confirm_upload_button_selector = self.locator_manager.get_locator('OptionLocators', 'confirm_upload_button_selector')
|
||||
|
||||
self.low_order_button_locator = self.locator_manager.get_locator('OptionLocators', 'low_order_button_locator')
|
||||
self.AtoZ_button_locator = self.locator_manager.get_locator('OptionLocators', 'AtoZ_button_locator')
|
||||
self.one_to_nine_button_locator = self.locator_manager.get_locator('OptionLocators', 'one_to_nine_button_locator')
|
||||
|
|
@ -41,6 +48,9 @@ class OptionHandler:
|
|||
def update_page(self, page1):
|
||||
self.page = page1
|
||||
self.logger.debug(f"page객체 업데이트 : {page1}")
|
||||
def update_whale(self, whale1):
|
||||
self.whale_translator = whale1
|
||||
self.logger.debug(f"whale_translator 객체 업데이트 : {whale1}")
|
||||
|
||||
def init_option_info(self):
|
||||
self.option_info = {
|
||||
|
|
@ -198,57 +208,61 @@ class OptionHandler:
|
|||
|
||||
# 3. 가격 낮은 순 정렬 클릭
|
||||
await self.low_order_click()
|
||||
|
||||
# 4. 옵션 정보 수집 및 번역
|
||||
|
||||
try:
|
||||
if toggle_states['optionTrnas']:
|
||||
self.logger.debug(f"옵션 AI번역 : {toggle_states['optionTrnas']}")
|
||||
self.option_info = await self.collect_options_info()
|
||||
self.logger.info(f"옵션 정보 수집")
|
||||
self.option_info = await self.collect_options_info()
|
||||
self.logger.debug(f"수집된 옵션 정보 : {self.option_info}")
|
||||
except Exception as e:
|
||||
# 옵션 처리 중 오류 발생 시 전체 로그 출력
|
||||
self.logger.error(f"옵션 정보 수집 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
try:
|
||||
# Vertex AI를 통한 번역 시도
|
||||
translated_options = await self.vertexAItranslator.translate_options(self.option_info['original_names'], product_name)
|
||||
self.logger.debug(f"번역된 옵션 입력")
|
||||
await self.apply_translated_options(translated_options, self.option_info['edit_fields'])
|
||||
# 4. 옵션 정보 수집 및 번역
|
||||
|
||||
self.is_vertext_success = True # 번역 성공
|
||||
if toggle_states['optionTrnas']:
|
||||
self.logger.debug(f"옵션 AI번역 : {toggle_states['optionTrnas']}")
|
||||
|
||||
except ValueError as ve:
|
||||
# 안전 필터 예외 처리
|
||||
if "SAFETY" in str(ve):
|
||||
self.logger.error(f"안전 필터에 의해 번역 요청이 차단되었습니다. {ve}")
|
||||
self.logger.debug("퍼센티 자체 AI번역 사용 시도")
|
||||
await self.page.click(self.ai_option_btn_selector)
|
||||
self.logger.debug("번역을 위한 5초간 대기")
|
||||
await asyncio.sleep(5)
|
||||
self.is_vertext_success = False
|
||||
self.is_percenty_success = True
|
||||
try:
|
||||
# Vertex AI를 통한 번역 시도
|
||||
translated_options = await self.vertexAItranslator.translate_options(self.option_info['original_names'], product_name)
|
||||
self.logger.debug(f"번역된 옵션 입력")
|
||||
await self.apply_translated_options(translated_options, self.option_info['edit_fields'])
|
||||
|
||||
# except google.api_core.exceptions.ResourceExhausted as re:
|
||||
# # 할당량 초과 예외 처리
|
||||
# self.logger.error(f"Vertex AI 할당량 초과: {re}")
|
||||
# self.logger.debug("퍼센티 자체 AI번역 사용 시도")
|
||||
# pyautogui.hotkey('alt', 'q')
|
||||
# self.logger.debug("번역을 위한 5초간 대기")
|
||||
# time.sleep(5)
|
||||
# translation_success = False # 번역 실패
|
||||
self.is_vertext_success = True # 번역 성공
|
||||
|
||||
except Exception as e:
|
||||
# 기타 예외 처리
|
||||
self.logger.error(f"번역 처리 중 알 수 없는 오류 발생: {e}", exc_info=True)
|
||||
except ValueError as ve:
|
||||
# 안전 필터 예외 처리
|
||||
if "SAFETY" in str(ve):
|
||||
self.logger.error(f"안전 필터에 의해 번역 요청이 차단되었습니다. {ve}")
|
||||
self.logger.debug("퍼센티 자체 AI번역 사용 시도")
|
||||
await self.page.click(self.ai_option_btn_selector)
|
||||
self.logger.debug("번역을 위한 5초간 대기")
|
||||
await asyncio.sleep(5)
|
||||
self.is_vertext_success = False # 번역 실패
|
||||
self.is_vertext_success = False
|
||||
self.is_percenty_success = True
|
||||
|
||||
# 번역 성공 여부에 따른 로그
|
||||
self.logger.debug(f"[{'VertexAI' if self.is_vertext_success else '퍼센티AI'}] 를 이용한 옵션번역 성공")
|
||||
# except google.api_core.exceptions.ResourceExhausted as re:
|
||||
# # 할당량 초과 예외 처리
|
||||
# self.logger.error(f"Vertex AI 할당량 초과: {re}")
|
||||
# self.logger.debug("퍼센티 자체 AI번역 사용 시도")
|
||||
# pyautogui.hotkey('alt', 'q')
|
||||
# self.logger.debug("번역을 위한 5초간 대기")
|
||||
# time.sleep(5)
|
||||
# translation_success = False # 번역 실패
|
||||
|
||||
except Exception as e:
|
||||
# 기타 예외 처리
|
||||
self.logger.error(f"번역 처리 중 알 수 없는 오류 발생: {e}", exc_info=True)
|
||||
self.logger.debug("퍼센티 자체 AI번역 사용 시도")
|
||||
await self.page.click(self.ai_option_btn_selector)
|
||||
self.logger.debug("번역을 위한 5초간 대기")
|
||||
await asyncio.sleep(5)
|
||||
self.is_vertext_success = False # 번역 실패
|
||||
self.is_percenty_success = True
|
||||
|
||||
# 번역 성공 여부에 따른 로그
|
||||
self.logger.debug(f"[{'VertexAI' if self.is_vertext_success else '퍼센티AI'}] 를 이용한 옵션번역 성공")
|
||||
|
||||
except Exception as e:
|
||||
# 옵션 처리 중 오류 발생 시 전체 로그 출력
|
||||
self.logger.error(f"옵션 처리 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
# 5. 옵션 필터링 및 조정
|
||||
if toggle_states['optionAutoSelect']:
|
||||
|
|
@ -261,11 +275,9 @@ class OptionHandler:
|
|||
await self.store_selected_options() # 페이지에서 실제 선택된 옵션을 수집하여 저장
|
||||
|
||||
# 7. 옵션 이미지 업데이트 (옵션 이미지가 있는 경우)
|
||||
if toggle_states['optionIMGTrans']:
|
||||
self.logger.debug(f"옵션 이미지번역(옵션 이미지가 있는 경우만) : {toggle_states['optionIMGTrans']}")
|
||||
for index, option_image_url in enumerate(self.option_info.get('option_images', []), start=1):
|
||||
option_name = translated_options.get(f'trans_option_{index}', f'옵션_{index}')
|
||||
await self.update_option_image(index, option_image_url, product_name, option_name, self.debug_flag)
|
||||
if toggle_states.get('optionIMGTrans'):
|
||||
self.logger.debug(f"옵션 이미지 번역을 시작합니다.")
|
||||
await self.update_option_image(toggle_states, debug_flag=self.debug_flag)
|
||||
|
||||
# 8. A-Z or 1-99 button 클릭
|
||||
|
||||
|
|
@ -654,92 +666,93 @@ class OptionHandler:
|
|||
async def low_order_click(self):
|
||||
self.logger.debug("가격 낮은 순 정렬을 클릭합니다.")
|
||||
await self.page.click(self.low_order_button_locator)
|
||||
|
||||
async def update_option_image(self, index, option_image_url, product_name, option_name, debug_flag=False):
|
||||
|
||||
async def update_option_image(self, toggle_states, debug_flag=False):
|
||||
"""
|
||||
옵션 이미지가 존재할 경우, 기존 이미지를 삭제하고 번역된 이미지를 추가하는 메서드.
|
||||
옵션 이미지가 존재할 경우, 제외된 옵션이 아닌 경우 번역하여 업데이트하는 메서드.
|
||||
|
||||
:param index: 옵션 인덱스.
|
||||
:param option_image_url: 옵션 이미지 URL.
|
||||
:param product_name: 상품명.
|
||||
:param option_name: 옵션명.
|
||||
:param debug: 디버그 모드일 경우 이미지를 삭제하지 않음 (기본값 False).
|
||||
:param debug_flag: 디버그 모드일 경우 임시 이미지를 삭제하지 않음 (기본값 False).
|
||||
"""
|
||||
# main.py 실행 폴더의 tmp_images 폴더 경로 설정
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__)) # main.py 위치 확인
|
||||
temp_dir = os.path.join(base_dir, "tmp_images") # tmp_images 폴더 경로 설정
|
||||
os.makedirs(temp_dir, exist_ok=True) # 폴더가 없으면 생성
|
||||
|
||||
try:
|
||||
# 이미지가 없을 경우 메서드 종료
|
||||
if not option_image_url:
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지가 존재하지 않아 작업을 종료합니다.")
|
||||
return
|
||||
# 모든 옵션 상자 요소 가져오기
|
||||
option_boxes = await self.page.query_selector_all(self.option_box_selector)
|
||||
total_options = len(option_boxes)
|
||||
self.logger.debug(f"총 {total_options}개의 옵션 이미지 번역을 시작합니다.")
|
||||
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지를 업데이트합니다.")
|
||||
# 각 옵션 상자를 인덱스로 순회
|
||||
for index in range(1, total_options + 1):
|
||||
# 선택자에서 인덱스를 반영해 동적 선택자를 생성
|
||||
excluded_marker_selector = self.excluded_option_marker.format(index=index)
|
||||
delete_button_selector = self.delete_button_selector.format(index=index)
|
||||
add_button_selector = self.add_button_selector.format(index=index)
|
||||
|
||||
delete_button_selector = self.delete_button_selector_template.format(index=index)
|
||||
confirm_delete_button_locator = self.confirm_delete_button_locator
|
||||
add_button_selector = self.add_button_selector_template.format(index=index)
|
||||
file_input_locator = self.file_input_locator
|
||||
|
||||
# 기존 이미지 삭제 (삭제 버튼이 존재할 경우)
|
||||
delete_button = await self.page.query_selector(delete_button_selector)
|
||||
# 제외된 옵션 확인
|
||||
excluded_marker = await self.page.query_selector(excluded_marker_selector)
|
||||
if excluded_marker:
|
||||
self.logger.debug(f"{index}번째 옵션은 제외된 옵션입니다. 번역을 생략합니다.")
|
||||
continue
|
||||
|
||||
if delete_button:
|
||||
self.logger.debug(f"{index}번째 옵션의 기존 이미지를 삭제합니다.")
|
||||
await delete_button.click()
|
||||
# 옵션 이미지 URL 가져오기
|
||||
option_image = await option_boxes[index - 1].query_selector("img")
|
||||
if not option_image:
|
||||
self.logger.debug(f"{index}번째 옵션에 이미지가 없습니다. 다음 옵션으로 이동합니다.")
|
||||
continue
|
||||
|
||||
# 삭제 확인 버튼 클릭
|
||||
confirm_delete_button = await self.page.query_selector(confirm_delete_button_locator)
|
||||
if confirm_delete_button:
|
||||
self.logger.debug(f"삭제 확인버튼 클릭")
|
||||
await confirm_delete_button.click()
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지가 삭제되었습니다.")
|
||||
else:
|
||||
self.logger.debug(f"삭제 확인 버튼을 찾을 수 없습니다.")
|
||||
option_image_url = await option_image.get_attribute("src")
|
||||
self.logger.debug(f"{index}번째 옵션 이미지 URL: {option_image_url}")
|
||||
|
||||
# 이미지 번역 후 추가
|
||||
# 이미지 번역 및 업로드
|
||||
translated_image_path = os.path.join(temp_dir, f"translated_option_{index}.png") # 이미지 저장 경로 설정
|
||||
|
||||
# 디렉토리가 존재하지 않으면 생성
|
||||
self.logger.debug("이미지 저장 경로 설정")
|
||||
translated_image_path = f"tmp_image/{product_name}-{option_name}-{index}.png"
|
||||
if not os.path.exists('tmp_image'):
|
||||
os.makedirs('tmp_image')
|
||||
self.logger.debug("이미지 임시저장폴더가 존재하지 않아 생성.")
|
||||
|
||||
self.logger.debug(f"옵션 이미지 번역")
|
||||
await self.whale_translator.translate_image(option_image_url)
|
||||
self.clipboardImageManager.process_clipboard(option_image_url, translated_image_path)
|
||||
try:
|
||||
# 이미지 번역
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지 번역 시도")
|
||||
is_success_translated = self.whale_translator.translate_image(option_image_url)
|
||||
self.clipboardImageManager.process_clipboard(option_image_url, is_success_translated, toggle_states, translated_image_path)
|
||||
self.logger.debug(f"{index}번째 옵션의 번역 이미지 저장 완료: {translated_image_path}")
|
||||
|
||||
# 이미지 업로드 버튼 클릭 (옵션 이미지가 없는 경우)
|
||||
add_button = await self.page.query_selector(add_button_selector)
|
||||
if is_success_translated and os.path.exists(translated_image_path):
|
||||
# 삭제 버튼 클릭
|
||||
delete_button = await self.page.query_selector(delete_button_selector)
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지 삭제 버튼 가져오기")
|
||||
|
||||
if add_button:
|
||||
await add_button.click()
|
||||
if delete_button:
|
||||
await delete_button.click()
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지 삭제 버튼 클릭")
|
||||
confirm_delete_button = await self.page.wait_for_selector(self.confirm_delete_button_selector)
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지 삭제확인 버튼 가져오기")
|
||||
if confirm_delete_button:
|
||||
await confirm_delete_button.click()
|
||||
self.logger.debug(f"{index}번째 옵션의 기존 이미지가 삭제되었습니다.")
|
||||
|
||||
# '+ 버튼' 클릭 후 파일 업로드
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 가져오기")
|
||||
add_button = await self.page.query_selector(add_button_selector)
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 : {add_button}")
|
||||
if add_button:
|
||||
await add_button.click()
|
||||
self.logger.debug(f"{index}번째 옵션의 이미지 추가 버튼 클릭")
|
||||
file_upload_button = await self.page.wait_for_selector(self.file_upload_button_selector)
|
||||
self.logger.debug(f"{index}번째 옵션의 파일업로드 버튼 가져오기")
|
||||
await file_upload_button.set_input_files(translated_image_path)
|
||||
self.logger.debug(f"{index}번째 옵션의 파일 업로드")
|
||||
|
||||
# 파일 선택 다이얼로그에서 번역된 이미지 파일 입력
|
||||
file_input = await self.page.wait_for_selector(file_input_locator, timeout=5000)
|
||||
await file_input.set_input_files(translated_image_path)
|
||||
self.logger.debug(f"{index}번째 옵션에 번역된 이미지가 추가되었습니다.")
|
||||
|
||||
# 디버그 모드가 아닐 경우, 성공적으로 업로드 후 임시 파일 삭제
|
||||
if not debug_flag:
|
||||
import os
|
||||
if os.path.exists(translated_image_path):
|
||||
# '이미지 삽입' 버튼 클릭
|
||||
confirm_upload_button = await self.page.wait_for_selector(self.confirm_upload_button_selector)
|
||||
await confirm_upload_button.click()
|
||||
self.logger.debug(f"{index}번째 옵션에 이미지가 업로드되었습니다.")
|
||||
finally:
|
||||
# 파일 사용 후 0.5초 대기하여 접근 완료 보장
|
||||
time.sleep(0.5)
|
||||
# 디버그 모드가 아닐 경우 임시 파일 삭제
|
||||
if not debug_flag and os.path.exists(translated_image_path):
|
||||
os.remove(translated_image_path)
|
||||
self.logger.debug(f"번역된 이미지 파일이 삭제되었습니다: {translated_image_path}")
|
||||
self.logger.debug(f"{index}번째 옵션의 임시 번역 이미지 파일 삭제 완료: {translated_image_path}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"{index}번째 옵션 이미지 업데이트 중 오류 발생: {e}", exc_info=True)
|
||||
self.logger.debug(f"원본이미지를 다시 넣습니다")
|
||||
|
||||
# 이미지 번역이 실패하면 원본 이미지를 다시 다운로드하여 PIL 이미지로 변환
|
||||
original_image = await self.download_image_from_url(option_image_url)
|
||||
|
||||
if original_image:
|
||||
# original_image를 로컬에 저장
|
||||
original_image_path = f"tmp_image/{product_name}-original-{index}.png"
|
||||
original_image.save(original_image_path)
|
||||
|
||||
# 저장된 원본 이미지를 다시 업로드
|
||||
file_input = await self.page.wait_for_selector(file_input_locator, timeout=5000)
|
||||
await file_input.set_input_files(original_image_path)
|
||||
self.logger.debug(f"{index}번째 옵션에 원본 이미지가 업로드되었습니다.")
|
||||
else:
|
||||
self.logger.error(f"원본 이미지를 다운로드하는 데 실패했습니다: {option_image_url}")
|
||||
self.logger.error(f"옵션 이미지 업데이트 중 오류 발생: {e}", exc_info=True)
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 277 KiB |
|
|
@ -354,8 +354,18 @@ class WhaleTranslator:
|
|||
|
||||
# 경로를 인자로 받을경우 해당경로에 파일 저장
|
||||
if path:
|
||||
pass # 클립보드의 이미지를 path의 파일로 저장하고 저장경로를 리턴하는 메서드
|
||||
# path에는 현재 폴더의 tmp_img폴더에 상품명-옵션명 형태로 제공됨
|
||||
try:
|
||||
# 클립보드에서 이미지 가져오기
|
||||
clipboard_image = ImageGrab.grabclipboard()
|
||||
if clipboard_image:
|
||||
clipboard_image.save(path, format='PNG')
|
||||
self.logger.info(f"번역된 이미지가 {path}에 저장되었습니다.")
|
||||
else:
|
||||
self.logger.error("클립보드에 이미지가 존재하지 않아 파일로 저장할 수 없습니다.")
|
||||
return False
|
||||
except Exception as e:
|
||||
self.logger.error(f"이미지 저장 중 오류 발생: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
return True if self.translation_success_flag else False
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue