Merge branch 'new_module' of ssh://cckb9998.synology.me:30022/ckh08045/AutoPercenty3 into new_module

This commit is contained in:
9700X_PC 2025-06-23 20:44:22 +09:00
commit acdf20e1f7
3 changed files with 152 additions and 85 deletions

View File

@ -136,7 +136,7 @@ class BrowserController(QThread):
# ImageProcessor를 포함하여 다른 핸들러들 초기화 # ImageProcessor를 포함하여 다른 핸들러들 초기화
self.optionHandler = OptionHandler(self.locator_manager, self, self.whale_translator, self.clipboardImageManager, self.TEMP_IMAGE_DIR, self.logger, self.gpt_client, self.update_detail_progress_signal, self.set_progress_visible_signal, toggle_states=self.toggle_states, imageProcessor=self.imageProcessor) self.optionHandler = OptionHandler(self.locator_manager, self, self.whale_translator, self.clipboardImageManager, self.TEMP_IMAGE_DIR, self.logger, self.gpt_client, self.update_detail_progress_signal, self.set_progress_visible_signal, toggle_states=self.toggle_states, imageProcessor=self.imageProcessor)
self.priceHandler = PriceHandler(self.locator_manager, self, self.logger, self.optionHandler, self.price_setting_diag, self.toggle_states, debug_flag=self.toggle_states['debug_mode']) self.priceHandler = PriceHandler(self.locator_manager, self, self.logger, self.optionHandler, self.price_setting_diag, self.toggle_states, debug_flag=self.toggle_states['debug_mode'])
self.thumbnailHandler = ThumbnailHandler(self.locator_manager, self, self.logger, self.whale_translator, self.clipboardImageManager, self.toggle_states, self.update_detail_progress_signal, self.set_progress_visible_signal) self.thumbnailHandler = ThumbnailHandler(self.locator_manager, self, self.logger, self.whale_translator, self.clipboardImageManager, self.toggle_states, self.update_detail_progress_signal, self.set_progress_visible_signal, self.base_path)
self.titleGenerator = TitleGenerator(self.locator_manager, self, self.logger, self.whale_translator, self.toggle_states, self.gpt_client, self.forbidden_word_manager, self.user_id, self.supabase_manager) self.titleGenerator = TitleGenerator(self.locator_manager, self, self.logger, self.whale_translator, self.toggle_states, self.gpt_client, self.forbidden_word_manager, self.user_id, self.supabase_manager)
self.tagsHandler = TagsHandler(self.locator_manager, self, self.logger, self.toggle_states) self.tagsHandler = TagsHandler(self.locator_manager, self, self.logger, self.toggle_states)
self.detailHandler = DetailHandler(self.locator_manager, self, self.whale_translator, self.clipboardImageManager, self.detail_text_widget, self.TEMP_IMAGE_DIR, self.imageProcessor, self.logger, self.gpt_client, self.update_detail_progress_signal, self.set_progress_visible_signal, self.toggle_states) self.detailHandler = DetailHandler(self.locator_manager, self, self.whale_translator, self.clipboardImageManager, self.detail_text_widget, self.TEMP_IMAGE_DIR, self.imageProcessor, self.logger, self.gpt_client, self.update_detail_progress_signal, self.set_progress_visible_signal, self.toggle_states)

View File

@ -1205,6 +1205,7 @@ class MAIN_GUI(QMainWindow):
def update_thumb_rmb_count(self, value): def update_thumb_rmb_count(self, value):
"""QSpinBox에 입력된 값을 toggle_states['thumb_rmb_count']에 저장""" """QSpinBox에 입력된 값을 toggle_states['thumb_rmb_count']에 저장"""
self.toggle_states['thumb_rmb_count'] = value # 변경된 정수 값을 바로 저장 self.toggle_states['thumb_rmb_count'] = value # 변경된 정수 값을 바로 저장
self.logger.log(f"썸네일 삭제 버튼 개수 업데이트: {self.toggle_states['thumb_rmb_count']}", level=logging.DEBUG)
def update_max_option_count(self, value): def update_max_option_count(self, value):
"""QSpinBox에 입력된 값을 toggle_states['max_option_count']에 저장""" """QSpinBox에 입력된 값을 toggle_states['max_option_count']에 저장"""
@ -3170,6 +3171,7 @@ class MAIN_GUI(QMainWindow):
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", 3))
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)
self.thumb_rmb_widget.enterEvent = lambda e: self.show_manual_html( self.thumb_rmb_widget.enterEvent = lambda e: self.show_manual_html(

View File

@ -5,7 +5,7 @@ import asyncio
import glob import glob
class ThumbnailHandler: class ThumbnailHandler:
def __init__(self, locator_manager, browser_controller, logger, whale_translator, clipboardImageManager, toggle_states, update_detail_progress_signal, set_progress_visible_signal): def __init__(self, locator_manager, browser_controller, logger, whale_translator, clipboardImageManager, toggle_states, update_detail_progress_signal, set_progress_visible_signal, base_path):
self.update_detail_progress_signal = update_detail_progress_signal self.update_detail_progress_signal = update_detail_progress_signal
self.set_progress_visible_signal = set_progress_visible_signal self.set_progress_visible_signal = set_progress_visible_signal
@ -16,13 +16,32 @@ class ThumbnailHandler:
self.clipboardImageManager = clipboardImageManager self.clipboardImageManager = clipboardImageManager
self.toggle_states = toggle_states self.toggle_states = toggle_states
self.logger = logger self.logger = logger
self.thumbnail_selector_template = "div#productMainContentContainerId div:nth-child({index}) > div > div:nth-child(2) > div > img" self.base_path = base_path
self.delete_button_selector_template = "div#productMainContentContainerId div:nth-child({index}) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span"
# self.delete_button_selector_for_Num1 = "div#productMainContentContainerId div:nth-child(1) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span"
self.upload_button_selector = "div#productMainContentContainerId div:nth-child({index}) > div > div:nth-child(2) > span"
self.file_upload_button_selector = 'span.ant-upload-btn input[type="file"]'
self.confirm_upload_button_selector = "div.ant-modal-footer > button[type=\"button\"].ant-btn.css-1li46mu.ant-btn-primary" # self.thumbnail_selector_template = self.locator_manager.get_locator('ThumbLocators', 'thumbnail_selector_template')
# self.delete_button_selector_template = self.locator_manager.get_locator('ThumbLocators', 'delete_button_selector_template')
# self.upload_button_selector = self.locator_manager.get_locator('ThumbLocators', 'upload_button_selector')
# self.file_upload_button_selector = self.locator_manager.get_locator('ThumbLocators', 'file_upload_button_selector')
# self.thumb_card_locator = self.locator_manager.get_locator('ThumbLocators', 'thumb_card_locator')
# self.edit_button_locator = self.locator_manager.get_locator('ThumbLocators', 'edit_button_locator')
# self.editor_title_locator = self.locator_manager.get_locator('ThumbLocators', 'editor_title_locator')
# self.confirm_upload_button_selector = self.locator_manager.get_locator('ThumbLocators', 'confirm_upload_button_selector')
# 편집 버튼 선택자 (개수 세기 및 편집 모드 진입에 사용)
self.edit_buttons_selector = 'div#productMainContentContainerId span[role="img"][aria-label="edit"]'
# 썸네일 이미지 선택자 (draggable=false인 img 요소들)
self.thumbnail_images_selector = 'div#productMainContentContainerId img[draggable="false"]'
# 삭제 버튼 선택자 (text가 "삭제"인 span 요소들)
self.delete_buttons_selector = 'div#productMainContentContainerId span:has-text("삭제")'
self.upload_button_selector = "span:has-text('Upload')"
self.file_upload_button_selector = 'span.ant-upload-btn input[type="file"]'
# self.editor_title_locator = "div.ant-drawer-title:has-text('퍼센티 이미지 에디터')"
self.editor_modal_locator = "div.Image_Editor_Modal_Drawer"
# self.confirm_upload_button_selector = "div.ant-modal-footer > button[type=\"button\"].ant-btn.css-1li46mu.ant-btn-primary"
self.confirm_upload_button_selector = ('role=dialog >> button[type="button"]:has(span:text("이미지 삽입"))')
# # main.py 실행 폴더의 tmp_images 폴더 경로 설정 # # main.py 실행 폴더의 tmp_images 폴더 경로 설정
# base_dir = os.path.dirname(os.path.abspath(__file__)) # main.py 위치 확인 # base_dir = os.path.dirname(os.path.abspath(__file__)) # main.py 위치 확인
@ -30,25 +49,25 @@ class ThumbnailHandler:
# os.makedirs(self.temp_dir, exist_ok=True) # 폴더가 없으면 생성 # os.makedirs(self.temp_dir, exist_ok=True) # 폴더가 없으면 생성
# main.py 실행 폴더의 tmp_images 폴더 경로 설정 # main.py 실행 폴더의 tmp_images 폴더 경로 설정
base_dir = self.get_base_dir() # get_base_dir() 메서드 호출 # base_dir = self.get_base_dir() # get_base_dir() 메서드 호출
self.temp_dir = os.path.join(base_dir, "tmp_images") # tmp_images 폴더 경로 설정 self.temp_dir = os.path.join(self.base_path, "tmp_images") # tmp_images 폴더 경로 설정
os.makedirs(self.temp_dir, exist_ok=True) # 폴더가 없으면 생성 os.makedirs(self.temp_dir, exist_ok=True) # 폴더가 없으면 생성
self.logger.log(f"임시 디렉토리 생성: {self.temp_dir}", level=logging.INFO) # 디렉토리 생성 로그 출력 self.logger.log(f"임시 디렉토리 생성: {self.temp_dir}", level=logging.INFO) # 디렉토리 생성 로그 출력
def get_base_dir(self): # def get_base_dir(self):
""" # """
실행 환경에 따라 base_dir을 설정하는 메서드. # 실행 환경에 따라 base_dir을 설정하는 메서드.
cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정. # cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정.
""" # """
if getattr(sys, 'frozen', False): # 패키징된 경우 # if getattr(sys, 'frozen', False): # 패키징된 경우
base_dir = os.path.dirname(sys.executable) # base_dir = os.path.dirname(sys.executable)
internal_dir = os.path.join(base_dir, '_internal') # _internal 디렉토리 포함 # internal_dir = os.path.join(base_dir, '_internal') # _internal 디렉토리 포함
if os.path.exists(internal_dir): # _internal 디렉토리가 존재하면 base_dir로 설정 # if os.path.exists(internal_dir): # _internal 디렉토리가 존재하면 base_dir로 설정
return internal_dir # return internal_dir
else: # 일반 Python 실행 환경 # else: # 일반 Python 실행 환경
base_dir = os.path.dirname(os.path.abspath(__file__)) # base_dir = os.path.dirname(os.path.abspath(__file__))
return base_dir # return base_dir
def update_page(self, page1): def update_page(self, page1):
self.page = page1 self.page = page1
@ -83,66 +102,65 @@ class ThumbnailHandler:
self.update_whale() self.update_whale()
# 썸네일 카드 개수 수집 # 편집 버튼 개수로 썸네일 개수 확인 (더 정확함)
thumbnails = await self.page.query_selector_all("div#productMainContentContainerId div.ant-row.ant-row-bottom.css-1li46mu > div") edit_buttons = await self.page.query_selector_all(self.edit_buttons_selector)
total_thumbnails = len(thumbnails) actual_thumbnails = len(edit_buttons)
# 마지막 카드는 '추가' 카드이므로 실제 썸네일 이미지 개수는 1개를 뺍니다 self.logger.log(f"편집 버튼 개수로 확인된 실제 썸네일 개수: {actual_thumbnails}", level=logging.DEBUG)
actual_thumbnails = total_thumbnails - 1
self.logger.log(f"총 썸네일 카드 수집 완료: {total_thumbnails}개 (실제 이미지: {actual_thumbnails}개)", level=logging.DEBUG)
self.set_progress_visible_signal.emit(True) self.set_progress_visible_signal.emit(True)
if thumb_trans_type: if thumb_trans_type:
for index in range(actual_thumbnails): for index in range(actual_thumbnails):
thumbnail_img_selector = self.thumbnail_selector_template.format(index=1) # 썸네일 이미지 선택자 (인덱스 기반)
delete_button_selector = self.delete_button_selector_template.format(index=1) # 첫번째카드만 삭제를 반복하면 되므로 thumbnail_images = await self.page.query_selector_all(self.thumbnail_images_selector)
delete_buttons = await self.page.query_selector_all(self.delete_buttons_selector)
# 이미지 URL 수집 # 이미지 URL 수집 (첫 번째 이미지 사용)
thumbnail_img_element = await self.page.query_selector(thumbnail_img_selector) if len(thumbnail_images) > 0:
if thumbnail_img_element: image_url = await thumbnail_images[0].get_attribute("src")
image_url = await thumbnail_img_element.get_attribute("src")
self.logger.log(f"{index+1}번째 썸네일 이미지 URL: {image_url}", level=logging.DEBUG) self.logger.log(f"{index+1}번째 썸네일 이미지 URL: {image_url}", level=logging.DEBUG)
# 이미지 번역 실행 # 이미지 번역 실행
translated_image_path = await self.translate_thumbnail_image(image_url, index) translated_image_path = await self.translate_thumbnail_image(image_url, index)
# 기존 썸네일 삭제 # 기존 썸네일 삭제 (첫 번째 삭제 버튼 사용)
await self.delete_thumbnail(delete_button_selector) if len(delete_buttons) > 0:
await delete_buttons[0].click()
self.logger.log("첫 번째 썸네일 삭제 버튼 클릭 완료", level=logging.DEBUG)
# 번역된 이미지 업로드 # 번역된 이미지 업로드
await self.upload_translated_image(translated_image_path, thumbnails) await self.upload_translated_image(translated_image_path)
self.update_detail_progress_signal.emit(index+1, actual_thumbnails) self.update_detail_progress_signal.emit(index+1, actual_thumbnails)
else: else:
# 첫번째 편집 버튼 클릭하여 에디터 열기 # 첫 번째 편집 버튼 클릭하여 에디터 열기
try: try:
# XPath를 사용하여 편집하기 버튼 직접 클릭 if len(edit_buttons) > 0:
edit_button = self.page.locator("xpath=//*[@id='productMainContentContainerId']/div/div[1]/div[4]/div[1]/div[1]/div/div[3]/div[2]/div/span[2]") await edit_buttons[0].click()
await edit_button.wait_for(state="attached", timeout=5000) self.logger.log("첫 번째 편집 버튼 클릭 성공", level=logging.DEBUG)
await edit_button.click() else:
self.logger.log("XPath를 사용하여 편집하기 버튼 클릭 성공", level=logging.DEBUG) raise Exception("편집 버튼을 찾을 수 없습니다")
# 약간의 대기 시간 추가 (페이지 반응 시간 고려) # 약간의 대기 시간 추가 (페이지 반응 시간 고려)
await asyncio.sleep(1) await asyncio.sleep(1)
except Exception as e: except Exception as e:
self.logger.log(f"XPath를 사용한 편집 버튼 클릭 실패: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"편집 버튼 클릭 실패: {e}", level=logging.ERROR, exc_info=True)
self.set_progress_visible_signal.emit(False) self.set_progress_visible_signal.emit(False)
return return
# '퍼센티 이미지 에디터' 확인 및 포커스 설정
try: try:
# 에디터 찾기 - 텍스트 내용으로 더 구체적으로 선택 # 모달 찾기 - 클래스명으로 간단하게 선택
editor_locator = self.page.locator("div.ant-drawer-title:has-text('퍼센티 이미지 에디터')") editor_locator = self.page.locator(self.editor_modal_locator)
await editor_locator.wait_for(timeout=5000) await editor_locator.wait_for(timeout=5000)
await editor_locator.focus() await editor_locator.focus()
self.logger.log("퍼센티 이미지 에디터 오픈 및 포커스 설정 완료", level=logging.DEBUG) self.logger.log("퍼센티 이미지 에디터 모달 오픈 및 포커스 설정 완료", level=logging.DEBUG)
except Exception as e: except Exception as e:
self.logger.log(f"에디터 포커스 설정 실패: {e}", level=logging.WARNING) self.logger.log(f"에디터 모달 포커스 설정 실패: {e}", level=logging.WARNING)
# 실패해도 계속 진행 # 실패해도 계속 진행
self.logger.log("퍼센티 이미지 에디터 오픈 및 포커스 설정 시도 완료", level=logging.DEBUG) self.logger.log("퍼센티 이미지 에디터 모달 오픈 및 포커스 설정 시도 완료", level=logging.DEBUG)
# 작업 대상 결정 (type==1: 첫번째 이미지만 배경지우기, type==2: 두번째 이미지까지 배경지우기) # 작업 대상 결정 (type==1: 첫번째 이미지만 배경지우기, type==2: 두번째 이미지까지 배경지우기)
bg_removal_count = thumb_rmb_count if actual_thumbnails >= 2 else 1 bg_removal_count = thumb_rmb_count if actual_thumbnails >= 2 else 1
@ -150,14 +168,9 @@ class ThumbnailHandler:
# 첫번째 이미지는 이미 열려 있으므로 작업 시작 # 첫번째 이미지는 이미 열려 있으므로 작업 시작
for i in range(actual_thumbnails): for i in range(actual_thumbnails):
self.logger.log(f"{i+1}번째 썸네일 작업 시작", level=logging.DEBUG) self.logger.log(f"{i+1}번째 썸네일 작업 시작", level=logging.DEBUG)
if i == 0:
# 첫번째 이미지는 현재 포커스된 상태 # 작업 시작 전 저장 다이얼로그 확인
pass await self.handle_save_dialog_if_exists()
else:
# 탭 키를 눌러 다음 이미지로 전환
await self.page.keyboard.press("Tab")
await asyncio.sleep(2) # 이미지 로딩 시간 대기
self.logger.log(f"{i+1}번째 썸네일로 탭 이동", level=logging.DEBUG)
# 각 이미지 작업 수행 # 각 이미지 작업 수행
if i < bg_removal_count: if i < bg_removal_count:
@ -189,6 +202,10 @@ class ThumbnailHandler:
except Exception as e: except Exception as e:
self.logger.log(f"배경지우기 작업 완료 확인 실패: {e}, 작업 계속 진행", level=logging.WARNING) self.logger.log(f"배경지우기 작업 완료 확인 실패: {e}, 작업 계속 진행", level=logging.WARNING)
await asyncio.sleep(1) await asyncio.sleep(1)
# 배경지우기 완료 후 저장 다이얼로그 확인
await self.handle_save_dialog_if_exists()
else: else:
self.logger.log(f"{i+1}번째 썸네일 이미지 번역 시작 (단축키 T)", level=logging.DEBUG) self.logger.log(f"{i+1}번째 썸네일 이미지 번역 시작 (단축키 T)", level=logging.DEBUG)
await self.page.keyboard.down("T") # T 키 누르기 await self.page.keyboard.down("T") # T 키 누르기
@ -217,30 +234,68 @@ class ThumbnailHandler:
except Exception as e: except Exception as e:
self.logger.log(f"이미지 번역 작업 완료 확인 실패: {e}, 작업 계속 진행", level=logging.WARNING) self.logger.log(f"이미지 번역 작업 완료 확인 실패: {e}, 작업 계속 진행", level=logging.WARNING)
await asyncio.sleep(1) await asyncio.sleep(1)
# 이미지 번역 완료 후 저장 다이얼로그 확인
await self.handle_save_dialog_if_exists()
# 각 이미지 작업 후 개별 저장 없이, 마지막 이미지까지 진행 후 전체 저장을 위해 진행률 업데이트 # 진행률 업데이트
self.update_detail_progress_signal.emit(i+1, actual_thumbnails) self.update_detail_progress_signal.emit(i+1, actual_thumbnails)
# 다음 이미지가 있는지 확인
if i < actual_thumbnails - 1:
# 다음 이미지가 있으면 Tab 키로 이동
await self.page.keyboard.press("Tab")
await asyncio.sleep(2) # 이미지 로딩 시간 대기
self.logger.log(f"{i+1}번째에서 {i+2}번째 썸네일로 탭 이동", level=logging.DEBUG)
# 탭 이동 후 저장 다이얼로그 확인
await self.handle_save_dialog_if_exists()
else:
# 마지막 이미지이면 저장
self.logger.log(f"{i+1}번째 썸네일이 마지막 이미지 - 저장 실행", level=logging.DEBUG)
await self.save_shortcut()
# 에디터 닫기 전 저장 다이얼로그 확인
await self.handle_save_dialog_if_exists()
# 에디터 닫기
try: try:
# 모든 이미지 작업 완료 후 전체 저장 (Ctrl+S) 및 에디터 닫기 (ESC)
await self.page.keyboard.press("Control+s")
self.logger.log("전체 수정사항 저장 (Ctrl+S) 전송", level=logging.DEBUG)
await asyncio.sleep(1)
await self.page.keyboard.press("Escape") await self.page.keyboard.press("Escape")
self.logger.log("이미지 에디터 종료 (ESC 전송)", level=logging.DEBUG) self.logger.log("이미지 에디터 종료 (ESC 전송)", level=logging.DEBUG)
except Exception as e:
self.logger.log(f"이미지 에디터 종료 실패: {e}", level=logging.ERROR, exc_info=True)
except TimeoutError as e:
self.logger.log(f"이미지 에디터 종료 중 타임아웃 발생: {e}", level=logging.ERROR, exc_info=True)
await self.handle_save_dialog()
except Exception as e2:
self.logger.log(f"이미지 에디터 종료 실패: {e2}", level=logging.ERROR, exc_info=True)
except Exception as e: except Exception as e:
self.logger.log(f"썸네일 작업 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"썸네일 작업 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
finally: finally:
self.set_progress_visible_signal.emit(False) self.set_progress_visible_signal.emit(False)
async def handle_save_dialog_if_exists(self) -> bool:
"""
저장 확인 다이얼로그가 있는지 확인하고 있으면 처리합니다.
다이얼로그가 없으면 아무 작업도 하지 않습니다.
Returns:
bool: 다이얼로그를 처리했으면 True, 없었으면 False
"""
try:
# 저장 확인 다이얼로그가 있는지 확인 (짧은 타임아웃으로)
save_dialog_selector = ".ant-modal-confirm"
dialog_exists = await self.page.locator(save_dialog_selector).count() > 0
if dialog_exists:
self.logger.log("저장 확인 다이얼로그 감지됨 - 처리 시작", level=logging.INFO)
return await self.handle_save_dialog()
else:
self.logger.log("저장 확인 다이얼로그 없음", level=logging.DEBUG)
return False
except Exception as e:
self.logger.log(f"저장 다이얼로그 확인 중 오류: {e}", level=logging.WARNING)
return False
async def handle_save_dialog(self) -> bool: async def handle_save_dialog(self) -> bool:
""" """
role="dialog" 안에 '저장' 버튼이 있으면 클릭하고 True를 반환. role="dialog" 안에 '저장' 버튼이 있으면 클릭하고 True를 반환.
@ -274,21 +329,10 @@ class ThumbnailHandler:
self.logger.log(f"{index+1}번째 썸네일 이미지 번역 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"{index+1}번째 썸네일 이미지 번역 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
return None return None
async def delete_thumbnail(self, delete_button_selector): async def upload_translated_image(self, image_path):
try: try:
delete_button = await self.page.query_selector(delete_button_selector) # Upload 버튼을 직접 찾아서 클릭
if delete_button: upload_button = await self.page.query_selector(self.upload_button_selector)
await delete_button.click()
self.logger.log("썸네일 삭제 버튼 클릭 완료", level=logging.DEBUG)
except Exception as e:
self.logger.log(f"썸네일 삭제 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
async def upload_translated_image(self, image_path, thumbnails):
try:
# 썸네일 카드 목록의 마지막 요소가 업로드 카드
last_thumbnail = thumbnails[-1]
upload_button = await last_thumbnail.query_selector("span:has-text('Upload')")
if upload_button: if upload_button:
await upload_button.click() await upload_button.click()
@ -306,3 +350,24 @@ class ThumbnailHandler:
self.logger.log("이미지 삽입 버튼 클릭 완료", level=logging.DEBUG) self.logger.log("이미지 삽입 버튼 클릭 완료", level=logging.DEBUG)
except Exception as e: except Exception as e:
self.logger.log(f"이미지 업로드 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"이미지 업로드 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
async def save_shortcut(self):
"""저장 단축키(Ctrl+S)를 실행하는 메서드"""
try:
# 저장 확인 다이얼로그가 있는지 먼저 확인
await self.handle_save_dialog_if_exists()
# 저장 단축키 (Ctrl+S) 실행
await self.page.keyboard.press("Control+s")
self.logger.log("저장 단축키 (Ctrl+S) 전송", level=logging.DEBUG)
await asyncio.sleep(1)
# 저장 후 다시 확인 다이얼로그 처리
await self.handle_save_dialog_if_exists()
except TimeoutError as e:
self.logger.log(f"저장 단축키 실행 중 타임아웃 발생: {e}", level=logging.ERROR, exc_info=True)
await self.handle_save_dialog()
except Exception as e:
self.logger.log(f"저장 단축키 실행 실패: {e}", level=logging.ERROR, exc_info=True)