211 lines
9.8 KiB
Python
211 lines
9.8 KiB
Python
import time
|
||
import os
|
||
import logging
|
||
import re
|
||
from pywinauto import Application, findwindows, clipboard, timings
|
||
from pywinauto.controls.hwndwrapper import HwndWrapper
|
||
|
||
# 로깅 설정
|
||
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(message)s")
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class WhaleTranslator:
|
||
def __init__(self):
|
||
self.app = None
|
||
self.whale_window = None
|
||
self.hwnd_wrapper = None
|
||
|
||
def start_whale_in_secret_mode(self):
|
||
whale_exe_path = os.path.join(os.getcwd(), "browsers", "whale", "whale.exe")
|
||
user_data_dir = os.path.join(os.getcwd(), "browsers", "whale", "user_data")
|
||
cache_dir = os.path.join(os.getcwd(), "browsers", "whale", "cache")
|
||
|
||
self.app = Application(backend="uia").start(
|
||
f'"{whale_exe_path}" --incognito --user-data-dir="{user_data_dir}" --disk-cache-dir="{cache_dir}"'
|
||
)
|
||
|
||
# 창이 완전히 생성될 때까지 대기
|
||
self.whale_window = self.find_whale_window()
|
||
|
||
if self.whale_window:
|
||
self.logger.info("웨일 시크릿 모드로 시작 완료.")
|
||
else:
|
||
self.logger.warning("웨일 창을 찾을 수 없습니다.")
|
||
|
||
def find_whale_window(self):
|
||
try:
|
||
# 최대 10초 동안 '새 시크릿 탭 - Whale' 창이 나타나기를 기다림
|
||
timings.wait_until(10, 0.5, lambda: any(window.name == '새 시크릿 탭 - Whale' for window in findwindows.find_elements()))
|
||
|
||
windows = findwindows.find_elements()
|
||
for window in windows:
|
||
if window.name == '새 시크릿 탭 - Whale':
|
||
whale_pid = window.process_id
|
||
self.whale_app = Application(backend="uia").connect(process=whale_pid)
|
||
self.whale_window = self.whale_app.top_window()
|
||
|
||
# 위치 및 크기 조절
|
||
self.hwnd_wrapper = HwndWrapper(self.whale_window.handle)
|
||
self.hwnd_wrapper.move_window(x=1, y=1, width=1280, height=720)
|
||
self.whale_window.set_focus()
|
||
|
||
self.logger.info("웨일 창을 성공적으로 찾았습니다.")
|
||
return self.whale_window
|
||
self.logger.error("'새 시크릿 탭 - Whale' 창을 찾을 수 없습니다.")
|
||
except Exception as e:
|
||
self.logger.error(f"웨일 창 탐색 중 오류 발생: {e}", exc_info=True)
|
||
return None
|
||
|
||
def navigate_to_url(self, url):
|
||
"""주소창에 URL을 입력하고 페이지 로딩을 대기"""
|
||
try:
|
||
address_bar = self.whale_window.child_window(title="주소창 및 검색창", control_type="Edit")
|
||
address_bar.click_input()
|
||
address_bar.type_keys(f"{url}{{ENTER}}", with_spaces=True)
|
||
logger.debug(f"{url}로 이동 중...")
|
||
|
||
# 5초 동안 0.1초 간격으로 이미지 요소가 나타나는지 검사
|
||
start_time = time.time()
|
||
while time.time() - start_time < 5:
|
||
try:
|
||
# 특정 이미지 요소를 찾으면 즉시 반환
|
||
image_element = self.whale_window.child_window(title="누락된 이미지 설명을 확인하려면 컨텍스트 메뉴를 여세요.", control_type="Image")
|
||
if image_element.exists(timeout=0.5):
|
||
logger.debug("페이지 로딩 완료: 이미지 요소가 나타났습니다.")
|
||
return
|
||
except Exception:
|
||
pass # 요소가 아직 나타나지 않은 경우 대기
|
||
|
||
logger.debug("지정된 시간 내에 이미지 요소를 찾지 못했습니다.")
|
||
except Exception as e:
|
||
logger.error(f"주소창에 접근할 수 없습니다: {e}", exc_info=True)
|
||
|
||
def check_translation_status(self, max_wait_time=10, check_interval=1):
|
||
start_time = time.time()
|
||
|
||
while time.time() - start_time < max_wait_time:
|
||
try:
|
||
fail_indicator = self.whale_window.child_window(title="번역할 영역을 선택하세요.", control_type="Text")
|
||
if fail_indicator.exists():
|
||
logger.debug("번역 실패: '번역할 영역을 선택하세요.' 문구가 나타났습니다.")
|
||
return "fail"
|
||
|
||
image_element = self.whale_window.child_window(title="누락된 이미지 설명을 확인하려면 컨텍스트 메뉴를 여세요.", control_type="Image")
|
||
if image_element.exists():
|
||
image_element.right_click_input()
|
||
success_indicator = self.whale_window.child_window(title="이미지 복사(C)", control_type="MenuItem")
|
||
|
||
if success_indicator.wait('visible', timeout=5):
|
||
success_indicator.click_input()
|
||
time.sleep(0.5)
|
||
|
||
formats = clipboard.GetClipboardFormats()
|
||
# logger.debug(f"클립보드에 있는 형식 목록: {formats}")
|
||
|
||
for format_id in formats:
|
||
format_name = clipboard.GetFormatName(format_id)
|
||
# logger.debug(f"형식 ID {format_id}: {format_name}")
|
||
|
||
if format_name in ("CF_BITMAP", "CF_DIB"):
|
||
image_data = clipboard.GetData(format_id=format_id)
|
||
if isinstance(image_data, bytes):
|
||
logger.info("번역 성공: 클립보드에 이미지 데이터가 있습니다.")
|
||
return "success"
|
||
logger.info("번역이 아직 완료되지 않았습니다. 다시 시도 중...")
|
||
|
||
except Exception as e:
|
||
logger.error("클립보드 접근 중 오류 발생", exc_info=True)
|
||
return "error"
|
||
|
||
time.sleep(check_interval)
|
||
|
||
logger.debug("번역 확인 시간 초과.")
|
||
return "timeout"
|
||
|
||
def right_click_on_image_and_inspect(self):
|
||
try:
|
||
image = self.whale_window.child_window(title="누락된 이미지 설명을 확인하려면 컨텍스트 메뉴를 여세요.", control_type="Image")
|
||
|
||
if image.exists():
|
||
image.right_click_input()
|
||
logger.debug("이미지 요소에서 우클릭을 수행했습니다.")
|
||
else:
|
||
logger.debug("이미지 요소를 찾을 수 없습니다.")
|
||
return
|
||
|
||
translate_menu_item = self.whale_window.child_window(title="이미지 번역 (R)", control_type="MenuItem")
|
||
translate_menu_item.click_input()
|
||
logger.debug("이미지 번역 명령이 실행되었습니다.")
|
||
time.sleep(0.5)
|
||
|
||
status = self.check_translation_status()
|
||
if status == "success":
|
||
logger.debug("번역이 성공적으로 완료되었습니다.")
|
||
elif status == "fail":
|
||
logger.debug("번역에 실패했습니다.")
|
||
else:
|
||
logger.debug("번역 상태를 확인할 수 없습니다.")
|
||
|
||
except Exception as e:
|
||
logger.debug(f"이미지 요소에서 우클릭 중 오류 발생: {e}", exc_info=True)
|
||
|
||
def get_image_dimensions(self):
|
||
"""창 제목에서 이미지의 너비와 높이를 추출하여 반환합니다."""
|
||
if self.whale_window:
|
||
try:
|
||
title = self.whale_window.window_text()
|
||
match = re.search(r"\((\d+)×(\d+)\)", title)
|
||
if match:
|
||
width, height = int(match.group(1)), int(match.group(2))
|
||
logger.debug(f"이미지 크기: 너비={width}, 높이={height}")
|
||
return width, height
|
||
else:
|
||
logger.debug("창 제목에서 이미지 크기를 찾을 수 없습니다.")
|
||
return None, None
|
||
except Exception as e:
|
||
logger.error("이미지 크기 추출 중 오류 발생", exc_info=True)
|
||
return None, None
|
||
else:
|
||
logger.debug("웨일 창이 활성화되지 않았습니다.")
|
||
return None, None
|
||
|
||
def click_back_button(self):
|
||
"""'뒤로' 버튼을 클릭합니다."""
|
||
try:
|
||
# '뒤로' 버튼 찾기
|
||
back_button = self.whale_window.child_window(title="뒤로", control_type="Button")
|
||
if back_button.exists():
|
||
back_button.click_input()
|
||
self.logger.debug("'뒤로' 버튼을 클릭했습니다.")
|
||
else:
|
||
self.logger.warning("'뒤로' 버튼을 찾을 수 없습니다.")
|
||
except Exception as e:
|
||
self.logger.error(f"'뒤로' 버튼 클릭 중 오류 발생: {e}", exc_info=True)
|
||
|
||
def close_whale_window(self):
|
||
"""웨일 창을 종료하는 메서드."""
|
||
try:
|
||
if self.app:
|
||
self.app.kill()
|
||
logger.debug("웨일 창을 성공적으로 종료했습니다.")
|
||
else:
|
||
logger.debug("웨일 애플리케이션이 시작되지 않았습니다.")
|
||
except Exception as e:
|
||
logger.error("웨일 창을 종료하는 중 오류 발생", exc_info=True)
|
||
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
translator = WhaleTranslator()
|
||
translator.start_whale_in_secret_mode()
|
||
# translator.find_whale_window()
|
||
|
||
if translator.whale_window:
|
||
translator.navigate_to_url("https://file.percenty.co.kr/public/652bed8e865b1f32ea62bf1f/products/6728686e9acd5506735107f0/dd4e8b61-da3a-4213-a953-d878d91651c7.jpg")
|
||
translator.right_click_on_image_and_inspect()
|
||
|
||
width, height = translator.get_image_dimensions()
|
||
print(f"width : {width}, height : {height}")
|
||
|
||
translator.close_whale_window() |