브라우저 컨트롤러에서 이미지 워커 재시작 로직을 활성화하고, 관련 주석을 정리하여 코드 가독성을 향상시켰습니다. 또한, UI에서 모든 태그 삭제 기능을 추가하고, 태그 처리 로직을 개선하여 사용자 경험을 향상시켰습니다. 버전 3.9.13으로 업데이트하며, 업데이트 로그에 변경 사항을 반영하였습니다.
This commit is contained in:
parent
8bee9ef6a1
commit
1df87f005d
|
|
@ -0,0 +1,328 @@
|
|||
; AutoPercenty3 Inno Setup Script
|
||||
; 이 스크립트는 cx_Freeze로 빌드된 결과물이 있는 "build\exe.win-amd64-3.11" 폴더를 기반으로 인스톨러를 제작합니다.
|
||||
; 20250802_205934에 생성됨
|
||||
|
||||
#define AppId "autopercenty"
|
||||
#define MyAppName "Edit_PartTimer"
|
||||
#define MyAppVersion "3.9.13"
|
||||
#define MyAppPublisher "WhenRideMyCar"
|
||||
#define MyAppProgramName "편집알바생"
|
||||
#define MyAppDescription "편집알바생"
|
||||
#define MyAppCopyright "Copyright 2024"
|
||||
#define MyAppExeName "Edit_PartTimer3"
|
||||
#define MySetupName "Edit_PartTimer Setup"
|
||||
#define MySetupIcon "src/Edit_PartTimer3.ico"
|
||||
#define MySetupOutputDir "dist/installer"
|
||||
|
||||
[Setup]
|
||||
; 기본 설정
|
||||
AppId={#AppId}
|
||||
AppName={#MyAppProgramName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppPublisher}
|
||||
OutputDir={#MySetupOutputDir}
|
||||
OutputBaseFilename={#MySetupName}
|
||||
SetupIconFile={#MySetupIcon}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
|
||||
; 업데이트 관련 설정 - 권한 최적화
|
||||
PrivilegesRequired=admin
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
UpdateUninstallLogAppName=yes
|
||||
AppMutex={#MyAppName}
|
||||
CloseApplications=yes
|
||||
RestartApplications=no
|
||||
|
||||
; 보안 및 호환성 설정
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
AllowNoIcons=yes
|
||||
|
||||
; 버전 정보
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
VersionInfoCompany={#MyAppPublisher}
|
||||
VersionInfoDescription={#MyAppDescription}
|
||||
VersionInfoCopyright={#MyAppCopyright}
|
||||
VersionInfoProductName={#MyAppProgramName}
|
||||
VersionInfoProductVersion={#MyAppVersion}
|
||||
|
||||
[Languages]
|
||||
Name: "korean"; MessagesFile: "compiler:Languages\Korean.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
|
||||
[Dirs]
|
||||
; 설치 시 {app}\logs 폴더를 생성하고,
|
||||
; Users 그룹에 'modify' 권한(=쓰기 가능)을 부여
|
||||
Name: "{app}\logs"; Permissions: users-modify
|
||||
; 설치 시 {app}\user_data 폴더를 생성하고,
|
||||
; Users 그룹에 'modify' 권한(=쓰기 가능)을 부여
|
||||
Name: "{app}\user_data"; Permissions: users-modify
|
||||
; Playwright 브라우저 폴더를 Program Files 내부에 생성
|
||||
Name: "{app}\lib\src\browsers\chromium-1155"; Permissions: users-modify
|
||||
; Playwright 브라우저 사용자폴더를 Program Files 내부에 생성
|
||||
Name: "{app}\lib\src\browsers\user_data"; Permissions: users-modify
|
||||
|
||||
[Files]
|
||||
; 프로그램 파일만 설치 (항상 덮어쓰기)
|
||||
Source: "build\exe.win-amd64-3.11\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; VC++ 재배포 패키지 파일을 임시 폴더({tmp})에 복사
|
||||
Source: "VC_redist.x64.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall
|
||||
|
||||
[Registry]
|
||||
; Playwright 브라우저 경로를 Program Files 내부로 설정
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "PLAYWRIGHT_BROWSERS_PATH"; ValueData: "{app}\lib\src\browsers"; Flags: preservestringtype
|
||||
|
||||
[Icons]
|
||||
; 시작 메뉴 바로가기
|
||||
Name: "{group}\{#MyAppProgramName}"; Filename: "{app}\{#MyAppExeName}.exe"
|
||||
; 바탕화면 바로가기
|
||||
Name: "{autodesktop}\{#MyAppProgramName}"; Filename: "{app}\{#MyAppExeName}.exe"; Tasks: desktopicon
|
||||
; 프로그램 제거 바로가기
|
||||
Name: "{group}\{#MyAppProgramName} 제거"; Filename: "{uninstallexe}"
|
||||
|
||||
[Run]
|
||||
; VC++ 재배포 패키지 설치 (필요할 경우)
|
||||
Filename: "{tmp}\VC_redist.x64.exe"; Parameters: "/install /passive /norestart"; StatusMsg: "VC++ 재배포 패키지 설치 중..."; Check: NeedsVCredist
|
||||
; 설치 후 프로그램 실행 (원할 경우)
|
||||
Filename: "{app}\{#MyAppExeName}.exe"; Description: "{cm:LaunchProgram,{#MyAppProgramName}}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Code]
|
||||
function CompareVersion(V1, V2: string): Integer;
|
||||
var
|
||||
P1, P2, N1, N2: Integer;
|
||||
begin
|
||||
P1 := 1;
|
||||
P2 := 1;
|
||||
Result := 0;
|
||||
while (Result = 0) and ((P1 <= Length(V1)) or (P2 <= Length(V2))) do begin
|
||||
while (P1 <= Length(V1)) and (V1[P1] = '.') do Inc(P1);
|
||||
while (P2 <= Length(V2)) and (V2[P2] = '.') do Inc(P2);
|
||||
if (P1 <= Length(V1)) and (P2 <= Length(V2)) then begin
|
||||
N1 := 0; while (P1 <= Length(V1)) and (V1[P1] >= '0') and (V1[P1] <= '9') do begin N1 := N1 * 10 + Ord(V1[P1]) - Ord('0'); Inc(P1); end;
|
||||
N2 := 0; while (P2 <= Length(V2)) and (V2[P2] >= '0') and (V2[P2] <= '9') do begin N2 := N2 * 10 + Ord(V2[P2]) - Ord('0'); Inc(P2); end;
|
||||
if N1 < N2 then Result := -1 else if N1 > N2 then Result := 1;
|
||||
end else begin
|
||||
if P1 <= Length(V1) then Result := 1 else if P2 <= Length(V2) then Result := -1;
|
||||
end;
|
||||
while (P1 <= Length(V1)) and (V1[P1] <> '.') do Inc(P1);
|
||||
while (P2 <= Length(V2)) and (V2[P2] <> '.') do Inc(P2);
|
||||
end;
|
||||
end;
|
||||
|
||||
// 파일 또는 폴더 복사 함수
|
||||
procedure CopyDir(const SourcePath, DestPath: string);
|
||||
var
|
||||
FindRec: TFindRec;
|
||||
SourceFilePath: string;
|
||||
DestFilePath: string;
|
||||
begin
|
||||
ForceDirectories(DestPath);
|
||||
|
||||
if FindFirst(SourcePath + '\*', FindRec) then
|
||||
begin
|
||||
try
|
||||
repeat
|
||||
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
|
||||
begin
|
||||
SourceFilePath := SourcePath + '\' + FindRec.Name;
|
||||
DestFilePath := DestPath + '\' + FindRec.Name;
|
||||
|
||||
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
|
||||
begin
|
||||
if FileCopy(SourceFilePath, DestFilePath, False) then
|
||||
Log('파일 복사 성공: ' + SourceFilePath + ' -> ' + DestFilePath)
|
||||
else
|
||||
Log('파일 복사 실패: ' + SourceFilePath);
|
||||
end
|
||||
else
|
||||
CopyDir(SourceFilePath, DestFilePath);
|
||||
end;
|
||||
until not FindNext(FindRec);
|
||||
finally
|
||||
FindClose(FindRec);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// 디렉토리 삭제 함수
|
||||
procedure DeleteDir(const DirPath: string);
|
||||
var
|
||||
FindRec: TFindRec;
|
||||
FilePath: string;
|
||||
begin
|
||||
if not DirExists(DirPath) then Exit;
|
||||
|
||||
if FindFirst(DirPath + '\*', FindRec) then
|
||||
begin
|
||||
try
|
||||
repeat
|
||||
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
|
||||
begin
|
||||
FilePath := DirPath + '\' + FindRec.Name;
|
||||
|
||||
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
|
||||
begin
|
||||
if DeleteFile(FilePath) then
|
||||
Log('파일 삭제 성공: ' + FilePath)
|
||||
else
|
||||
Log('파일 삭제 실패: ' + FilePath);
|
||||
end
|
||||
else
|
||||
DeleteDir(FilePath);
|
||||
end;
|
||||
until not FindNext(FindRec);
|
||||
finally
|
||||
FindClose(FindRec);
|
||||
end;
|
||||
end;
|
||||
|
||||
if RemoveDir(DirPath) then
|
||||
Log('디렉토리 삭제 성공: ' + DirPath)
|
||||
else
|
||||
Log('디렉토리 삭제 실패: ' + DirPath);
|
||||
end;
|
||||
|
||||
// 프로그램 실행 여부 확인
|
||||
function IsAppRunning(const FileName: string): Boolean;
|
||||
var
|
||||
Handle: THandle;
|
||||
begin
|
||||
Handle := FindWindowByWindowName('{#MyAppProgramName}'); // 프로그램의 윈도우 타이틀로 찾기
|
||||
Result := (Handle <> 0);
|
||||
end;
|
||||
|
||||
// 프로그램 종료
|
||||
procedure CloseApplication(const FileName: string);
|
||||
var
|
||||
Handle: THandle;
|
||||
begin
|
||||
Handle := FindWindowByWindowName('{#MyAppProgramName}');
|
||||
if Handle <> 0 then
|
||||
begin
|
||||
PostMessage(Handle, 18, 0, 0); // WM_QUIT
|
||||
Sleep(1000); // 종료 대기
|
||||
end;
|
||||
end;
|
||||
|
||||
// VC++ 재배포 패키지 필요 여부 확인
|
||||
function NeedsVCredist: Boolean;
|
||||
begin
|
||||
if RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64') then
|
||||
Result := False // 이미 설치됨
|
||||
else
|
||||
Result := True; // 미설치 -> 설치 필요
|
||||
end;
|
||||
|
||||
// 설치 완료 후 실행 여부 확인
|
||||
function InitializeFinish(): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
if MsgBox('설치가 완료되었습니다. 프로그램을 실행하시겠습니까?' + #13#10 +
|
||||
'(실행 시 서버와 동기화하여 설정이 업데이트됩니다)',
|
||||
mbConfirmation, MB_YESNO) = IDYES then
|
||||
begin
|
||||
Exec(ExpandConstant('{app}\{#MyAppExeName}.exe'), '', '', SW_SHOW, ewNoWait, ResultCode);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
var
|
||||
OldVersion: String;
|
||||
NewVersion: String;
|
||||
OldAppPath: String;
|
||||
UserDataSourcePath, UserDataBackupPath: String;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
NewVersion := '{#MyAppVersion}';
|
||||
UserDataBackupPath := ExpandConstant('{tmp}\user_data_backup');
|
||||
|
||||
// 현재 프로그램 버전 확인
|
||||
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppName}_is1',
|
||||
'DisplayVersion', OldVersion) then
|
||||
begin
|
||||
// 같은 버전이거나 더 높은 버전이 설치되어 있는 경우
|
||||
if CompareVersion(OldVersion, NewVersion) >= 0 then
|
||||
begin
|
||||
MsgBox('현재 설치된 버전(' + OldVersion + ')이 이 설치 프로그램의 버전(' +
|
||||
NewVersion + ')과 같거나 더 높습니다.' + #13#10 +
|
||||
'설치를 계속할 수 없습니다.', mbInformation, MB_OK);
|
||||
Result := False;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// 이전 버전이 설치되어 있는 경우 업데이트 진행
|
||||
if CompareVersion(OldVersion, NewVersion) < 0 then
|
||||
begin
|
||||
Log('업데이트 설치 진행: ' + OldVersion + ' -> ' + NewVersion);
|
||||
|
||||
// 프로그램이 실행 중인지 확인하고 종료 요청
|
||||
if IsAppRunning('{#MyAppExeName}.exe') then
|
||||
begin
|
||||
if MsgBox('프로그램을 업데이트하기 위해 실행 중인 프로그램을 종료해야 합니다.' + #13#10 +
|
||||
'계속하시겠습니까?', mbConfirmation, MB_YESNO) = IDNO then
|
||||
begin
|
||||
Result := False;
|
||||
exit;
|
||||
end;
|
||||
CloseApplication('{#MyAppExeName}.exe');
|
||||
Sleep(2000); // 프로세스 종료 대기
|
||||
end;
|
||||
|
||||
// 레지스트리에서 설치 경로 확인
|
||||
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppName}_is1',
|
||||
'InstallLocation', OldAppPath) then
|
||||
begin
|
||||
Log('기존 설치 경로: ' + OldAppPath);
|
||||
|
||||
// lib/src/user_data 폴더 백업
|
||||
UserDataSourcePath := OldAppPath + '\lib\src\user_data';
|
||||
if DirExists(UserDataSourcePath) then
|
||||
begin
|
||||
Log('사용자 데이터 백업 중: ' + UserDataSourcePath + ' -> ' + UserDataBackupPath);
|
||||
ForceDirectories(UserDataBackupPath);
|
||||
CopyDir(UserDataSourcePath, UserDataBackupPath);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Log('사용자 데이터 폴더가 존재하지 않음: ' + UserDataSourcePath);
|
||||
end;
|
||||
|
||||
// 기존 설치 폴더 완전 삭제
|
||||
if DirExists(OldAppPath) then
|
||||
begin
|
||||
Log('기존 설치 폴더 삭제 중: ' + OldAppPath);
|
||||
DeleteDir(OldAppPath);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
UserDataBackupPath, UserDataDestPath: String;
|
||||
begin
|
||||
// 설치 완료 후
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
UserDataBackupPath := ExpandConstant('{tmp}\user_data_backup');
|
||||
UserDataDestPath := ExpandConstant('{app}\lib\src\user_data');
|
||||
|
||||
// 백업한 사용자 데이터 폴더가 있으면 복원
|
||||
if DirExists(UserDataBackupPath) then
|
||||
begin
|
||||
Log('사용자 데이터 복원 중: ' + UserDataBackupPath + ' -> ' + UserDataDestPath);
|
||||
ForceDirectories(UserDataDestPath);
|
||||
CopyDir(UserDataBackupPath, UserDataDestPath);
|
||||
Log('사용자 데이터 복원 완료');
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -2924,14 +2924,14 @@ class BrowserController(QThread):
|
|||
self.logger.log(f'{completed_count}/[{total_products}]개 상품 수정 완료.', level=logging.INFO)
|
||||
|
||||
|
||||
# # --- (추가) N개마다 이미지 워커 재시작 ---
|
||||
# if (completed_count % self.image_worker_restart_every) == 0:
|
||||
# self.image_worker_mgr.restart(cause="periodic")
|
||||
# self.logger.log(f"이미지 워커 재시작", level=logging.DEBUG)
|
||||
# --- (추가) N개마다 이미지 워커 재시작 ---
|
||||
if (completed_count % self.image_worker_restart_every) == 0:
|
||||
self.image_worker_mgr.restart(cause="periodic")
|
||||
self.logger.log(f"이미지 워커 재시작", level=logging.DEBUG)
|
||||
|
||||
#test
|
||||
self.image_worker_mgr.restart(cause="periodic")
|
||||
self.logger.log(f"이미지 워커 재시작", level=logging.DEBUG)
|
||||
# #test
|
||||
# self.image_worker_mgr.restart(cause="periodic")
|
||||
# self.logger.log(f"이미지 워커 재시작", level=logging.DEBUG)
|
||||
|
||||
|
||||
# # --- (추가) N개마다 컨텍스트 재시작 ---
|
||||
|
|
@ -3846,9 +3846,9 @@ class ImageWorkerManager:
|
|||
)
|
||||
self._spawn_proc()
|
||||
|
||||
def _spawn_proc(self):
|
||||
self.proc = Process(target=worker_main, args=self.proc_args, daemon=True)
|
||||
self.proc.start()
|
||||
# def _spawn_proc(self):
|
||||
# self.proc = Process(target=worker_main, args=self.proc_args, daemon=True)
|
||||
# self.proc.start()
|
||||
|
||||
# ───────── 새 프로세스 스폰 ─────────
|
||||
def _spawn_proc(self):
|
||||
|
|
|
|||
40
mainUI_SP.py
40
mainUI_SP.py
|
|
@ -794,6 +794,18 @@ class MAIN_GUI(QMainWindow):
|
|||
"off": [],
|
||||
}
|
||||
},
|
||||
"delete_all_tags_toggle": {
|
||||
"key": "tag/delete_all_tags_toggle", # 태그 수정 사용 여부
|
||||
"state_key": "delete_all_tags",
|
||||
"dependents": { # ON/OFF별 enable 위젯 리스트
|
||||
"on": [],
|
||||
"off": []
|
||||
},
|
||||
"visible": { # ON/OFF별 visible 위젯 리스트
|
||||
"on": [],
|
||||
"off": [],
|
||||
}
|
||||
},
|
||||
|
||||
# 가격
|
||||
"price_toggle": {
|
||||
|
|
@ -1094,6 +1106,7 @@ class MAIN_GUI(QMainWindow):
|
|||
'price': False,
|
||||
'tag': False,
|
||||
'tag_ai': False,
|
||||
'delete_all_tags': False,
|
||||
'thumb': False,
|
||||
'thumb_trans_type': None,
|
||||
'thumb_nukki': False,
|
||||
|
|
@ -3452,6 +3465,33 @@ class MAIN_GUI(QMainWindow):
|
|||
# 레이아웃에 위젯 추가
|
||||
self.tags_toggle_layout.addWidget(self.tag_ai_widget)
|
||||
|
||||
# 태그 AI 생성 토글
|
||||
self.delete_all_tags_widget = QWidget()
|
||||
self.delete_all_tags_toggle_layout = QHBoxLayout(self.delete_all_tags_widget)
|
||||
self.delete_all_tags_toggle_label = QLabel("모든 태그 삭제", self)
|
||||
self.delete_all_tags_toggle = ToggleSwitch(self)
|
||||
self.delete_all_tags_toggle.setOnText("삭제")
|
||||
self.delete_all_tags_toggle.setOffText("유지")
|
||||
self.delete_all_tags_toggle.setObjectName("delete_all_tags_toggle")
|
||||
self.delete_all_tags_toggle.clicked.connect(lambda checked: self.universal_input_handler(self.delete_all_tags_toggle, checked))
|
||||
self.delete_all_tags_widget.enterEvent = lambda e: self.show_manual_html(
|
||||
self.tag_manual_group,
|
||||
"🏷️ 모든 태그 삭제",
|
||||
self.tag_manual_label,
|
||||
"""
|
||||
<p>새로운 태그 입력 전 모든 태그를 삭제합니다.</p>
|
||||
<p>기능 ON 시 모든 태그를 삭제합니다.</p>
|
||||
<p>기능 OFF 시 기존 태그를 유지합니다.</p>
|
||||
"""
|
||||
)
|
||||
self.delete_all_tags_widget.leaveEvent = lambda e: self.reset_manual(self.tag_manual_group, self.tag_manual_label)
|
||||
|
||||
self.delete_all_tags_toggle_layout.addWidget(self.delete_all_tags_toggle_label)
|
||||
self.delete_all_tags_toggle_layout.addWidget(self.delete_all_tags_toggle)
|
||||
|
||||
# 레이아웃에 위젯 추가
|
||||
self.tags_toggle_layout.addWidget(self.delete_all_tags_widget)
|
||||
|
||||
# 레이아웃에 그룹 추가
|
||||
self.tag_layout.addWidget(self.tags_toggle_group, 3)
|
||||
self.tag_layout.addWidget(self.tag_manual_group, 7)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ class TagsHandler:
|
|||
self.tags_input_locator = self.locator_manager.get_locator('TagsLocators', 'tags_input_locator')
|
||||
self.tags_input_button_locator = self.locator_manager.get_locator('TagsLocators', 'tags_input_Button_locator')
|
||||
self.delete_warning_tags_button_locator = self.locator_manager.get_locator('TagsLocators', 'delete_warning_tags_button_locator')
|
||||
self.delete_all_tags_button_locator = self.locator_manager.get_locator('TagsLocators', 'delete_all_tags_button_locator')
|
||||
self.warning_character_selector = self.locator_manager.get_locator('TagsLocators', 'warning_character_selector')
|
||||
# CharacterDanger 경고 요소 선택자 (서버 처리 후 지연 발생 가능)
|
||||
# self.warning_character_selector = 'div#productMainContentContainerId span.CharacterDanger'
|
||||
|
||||
def reset_state(self):
|
||||
self.logger.log("TagsHandler 상태 초기화", level=logging.DEBUG)
|
||||
|
|
@ -45,8 +49,8 @@ class TagsHandler:
|
|||
# 입력 버튼 클릭
|
||||
await self.click_input_button()
|
||||
|
||||
# 경고 키워드 삭제
|
||||
await self.delete_warning_tags()
|
||||
# 경고 키워드가 표시될 때까지 잠시 대기 후, 표시되면 삭제 버튼 클릭
|
||||
await self.wait_for_warning_and_delete(timeout_ms=2000)
|
||||
|
||||
except TimeoutError as te:
|
||||
self.logger.log(f"태그 처리 중 타임아웃 오류 발생: {te}", level=logging.ERROR, exc_info=True)
|
||||
|
|
@ -96,14 +100,18 @@ class TagsHandler:
|
|||
]
|
||||
self.logger.log(f"Filtered keyword_tags List : {filtered_tags}", level=logging.DEBUG)
|
||||
|
||||
if self.toggle_states.get("delete_all_tags", False):
|
||||
# 모든 태그 삭제
|
||||
await self.delete_all_tags()
|
||||
|
||||
# 태그 입력
|
||||
await self.enter_tags(filtered_tags)
|
||||
|
||||
# 입력 버튼 클릭
|
||||
await self.click_input_button()
|
||||
|
||||
# 경고 키워드 삭제
|
||||
await self.delete_warning_tags()
|
||||
# 경고 키워드가 표시될 때까지 잠시 대기 후, 표시되면 삭제 버튼 클릭
|
||||
await self.wait_for_warning_and_delete(timeout_ms=2000)
|
||||
|
||||
except TimeoutError as te:
|
||||
self.logger.log(f"태그 처리 중 타임아웃 오류 발생: {te}", level=logging.ERROR, exc_info=True)
|
||||
|
|
@ -167,6 +175,20 @@ class TagsHandler:
|
|||
except Exception as e:
|
||||
self.logger.log(f"태그 입력 버튼 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
|
||||
async def delete_all_tags(self):
|
||||
"""모든 태그 삭제 버튼 클릭."""
|
||||
try:
|
||||
self.logger.log("모든 태그 삭제 버튼 클릭 시도", level=logging.DEBUG)
|
||||
delete_all_button = await self.page.query_selector(self.delete_all_tags_button_locator)
|
||||
if delete_all_button is None:
|
||||
raise Exception("모든 태그 삭제 버튼을 찾을 수 없습니다.")
|
||||
|
||||
await delete_all_button.click()
|
||||
self.logger.log("모든 태그 삭제 버튼 클릭 완료", level=logging.INFO)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"모든 태그 삭제 버튼 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
|
||||
async def delete_warning_tags(self):
|
||||
"""경고 키워드 삭제 버튼 클릭."""
|
||||
try:
|
||||
|
|
@ -182,6 +204,24 @@ class TagsHandler:
|
|||
except Exception as e:
|
||||
self.logger.log(f"경고 키워드 삭제 버튼 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
|
||||
async def wait_for_warning_and_delete(self, timeout_ms: int = 2000):
|
||||
"""
|
||||
'CharacterDanger' 경고 요소가 나타나는지 최대 timeout_ms(ms) 동안 대기한 후,
|
||||
표시되면 경고 키워드 삭제를 수행하고, 아니면 조용히 건너뜁니다.
|
||||
"""
|
||||
try:
|
||||
self.logger.log(
|
||||
f"경고 키워드 표시 대기 시작 (최대 {timeout_ms}ms): selector={self.warning_character_selector}",
|
||||
level=logging.DEBUG,
|
||||
)
|
||||
await self.page.wait_for_selector(self.warning_character_selector, state='visible', timeout=timeout_ms)
|
||||
self.logger.log("경고 키워드 표시 감지됨. 삭제 버튼 클릭 시도", level=logging.INFO)
|
||||
await self.delete_warning_tags()
|
||||
except TimeoutError:
|
||||
self.logger.log("경고 키워드 표시 없음(타임아웃). 삭제를 건너뜀", level=logging.DEBUG)
|
||||
except Exception as e:
|
||||
self.logger.log(f"경고 키워드 대기/삭제 중 예외 발생: {e}", level=logging.ERROR, exc_info=True)
|
||||
|
||||
def _filter_chinese_characters(self, tags_list: List[str]) -> List[str]:
|
||||
"""주어진 태그 리스트에서 중국어(한자) 문자가 포함된 태그를 제거합니다."""
|
||||
chinese_pattern = re.compile(r"[\u3400-\u4DBF\u4E00-\u9FFF]")
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import logging
|
|||
""" 프로그램 기본 정보 """
|
||||
__title__ = "Edit_PartTimer"
|
||||
__description__ = "편집알바생"
|
||||
__version__ = "3.9.12"
|
||||
__version__ = "3.9.13"
|
||||
__build__ = "1" # 빌드 번호
|
||||
__author__ = "WhenRideMyCar"
|
||||
__author_email__ = "abc@gmail.com"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
|
||||
- 비밀번호 변경 추가
|
||||
|
||||
# 3.9.13 업데이트 로그
|
||||
|
||||
### 오류 수정
|
||||
- 이미지 워커 재시작주기가 일치하지 않는 문제 수정
|
||||
|
||||
### 기능 추가
|
||||
- 키워드 탭에 기존태그 삭제 옵션 추가 (기존 태그를 모두 삭제 후 생성된 태그 입력)
|
||||
|
||||
|
||||
|
||||
# 3.9.12 업데이트 로그
|
||||
|
||||
### 오류 수정
|
||||
|
|
|
|||
Loading…
Reference in New Issue