diff --git a/AutoPercenty_20250710_160532.iss b/AutoPercenty_20250710_160532.iss new file mode 100644 index 00000000..2b522dfe --- /dev/null +++ b/AutoPercenty_20250710_160532.iss @@ -0,0 +1,359 @@ +; AutoPercenty3 Inno Setup Script +; 이 스크립트는 cx_Freeze로 빌드된 결과물이 있는 "build\exe.win-amd64-3.11" 폴더를 기반으로 인스톨러를 제작합니다. +; 20250710_160532에 생성됨 + +#define AppId "autopercenty" +#define MyAppName "Edit_PartTimer" +#define MyAppVersion "3.9.1" +#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" +#define OldAppName "AutoPercenty3" + +[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-1140"; 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 + +[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; + +// 프로그램 실행 여부 확인 +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; + OldUninstallString: String; + ResultCode: Integer; +begin + Result := True; + NewVersion := '{#MyAppVersion}'; + UserDataBackupPath := ExpandConstant('{tmp}\user_data_backup'); + + // 이전 버전(AutoPercenty3) 확인 + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#OldAppName}_is1', + 'DisplayVersion', OldVersion) then + begin + // 이전 설치 경로 가져오기 + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#OldAppName}_is1', + 'InstallLocation', OldAppPath) then + begin + Log('이전 버전({#OldAppName})이 발견되었습니다. 경로: ' + OldAppPath); + + // user_data 폴더 경로 설정 - 우선순위: Program Files → AppData\Local + UserDataSourcePath := OldAppPath + '\user_data'; + + // Program Files에 user_data가 없으면 AppData\Local에서 찾기 + if not DirExists(UserDataSourcePath) then + begin + UserDataSourcePath := ExpandConstant('{localappdata}\' + '{#OldAppName}' + '\lib\user_data'); + Log('Program Files에 user_data 없음. AppData\Local 경로 확인: ' + UserDataSourcePath); + end + else + begin + Log('Program Files에서 user_data 발견: ' + UserDataSourcePath); + end; + + // 프로그램이 실행 중인지 확인하고 종료 요청 + if IsAppRunning('{#OldAppName}.exe') then + begin + if MsgBox('프로그램을 업데이트하기 위해 실행 중인 프로그램을 종료해야 합니다.' + #13#10 + + '계속하시겠습니까?', mbConfirmation, MB_YESNO) = IDNO then + begin + Result := False; + exit; + end; + CloseApplication('{#OldAppName}.exe'); + // 프로세스가 완전히 종료될 때까지 대기 + Sleep(2000); + end; + + // user_data 폴더 백업 + if DirExists(UserDataSourcePath) then + begin + Log('user_data 폴더 백업 중: ' + UserDataSourcePath + ' -> ' + UserDataBackupPath); + ForceDirectories(UserDataBackupPath); + CopyDir(UserDataSourcePath, UserDataBackupPath); + end; + + // 이전 버전 제거 (제거 프로그램 실행) + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#OldAppName}_is1', + 'UninstallString', OldUninstallString) then + begin + // 사용자에게 확인 + if MsgBox('이전 버전을 제거하고 새 버전을 설치하시겠습니까?' + #13#10 + + '사용자 데이터는 유지됩니다.', mbConfirmation, MB_YESNO) = IDYES then + begin + // 자동 제거를 위해 /SILENT 매개변수 추가 + OldUninstallString := OldUninstallString + ' /SILENT'; + Log('이전 버전 제거 실행: ' + OldUninstallString); + + // 제거 프로그램 실행 및 완료 대기 + if Exec(RemoveQuotes(OldUninstallString), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then + begin + Log('이전 버전 제거 완료: 결과 코드 ' + IntToStr(ResultCode)); + end + else + begin + Log('이전 버전 제거 실패'); + // 제거에 실패해도 계속 진행 + end; + end; + end; + end; + end + // 현재 프로그램(Edit_PartTimer3) 버전 확인 + else 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'); + end; + + // Edit_PartTimer3의 user_data 폴더 백업 - 우선순위: Program Files → AppData\Local + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppName}_is1', + 'InstallLocation', OldAppPath) then + begin + UserDataSourcePath := OldAppPath + '\user_data'; + + // Program Files에 user_data가 없으면 AppData\Local에서 찾기 + if not DirExists(UserDataSourcePath) then + begin + UserDataSourcePath := ExpandConstant('{localappdata}\' + '{#MyAppName}' + '\lib\user_data'); + Log('Program Files에 user_data 없음. AppData\Local 경로 확인: ' + UserDataSourcePath); + end + else + begin + Log('Program Files에서 user_data 발견: ' + UserDataSourcePath); + end; + + if DirExists(UserDataSourcePath) then + begin + Log('user_data 폴더 백업 중: ' + UserDataSourcePath + ' -> ' + UserDataBackupPath); + ForceDirectories(UserDataBackupPath); + CopyDir(UserDataSourcePath, UserDataBackupPath); + 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}\user_data'); + + // 백업한 user_data 폴더가 있으면 복원 + if DirExists(UserDataBackupPath) then + begin + Log('백업된 user_data 폴더 복원 중: ' + UserDataBackupPath + ' -> ' + UserDataDestPath); + ForceDirectories(UserDataDestPath); + CopyDir(UserDataBackupPath, UserDataDestPath); + Log('user_data 폴더 복원 완료'); + end; + end; +end; diff --git a/AutoPercenty_20250710_160643.iss b/AutoPercenty_20250710_160643.iss new file mode 100644 index 00000000..7f23d7c9 --- /dev/null +++ b/AutoPercenty_20250710_160643.iss @@ -0,0 +1,359 @@ +; AutoPercenty3 Inno Setup Script +; 이 스크립트는 cx_Freeze로 빌드된 결과물이 있는 "build\exe.win-amd64-3.11" 폴더를 기반으로 인스톨러를 제작합니다. +; 20250710_160643에 생성됨 + +#define AppId "autopercenty" +#define MyAppName "Edit_PartTimer" +#define MyAppVersion "3.9.2" +#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" +#define OldAppName "AutoPercenty3" + +[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-1140"; 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 + +[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; + +// 프로그램 실행 여부 확인 +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; + OldUninstallString: String; + ResultCode: Integer; +begin + Result := True; + NewVersion := '{#MyAppVersion}'; + UserDataBackupPath := ExpandConstant('{tmp}\user_data_backup'); + + // 이전 버전(AutoPercenty3) 확인 + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#OldAppName}_is1', + 'DisplayVersion', OldVersion) then + begin + // 이전 설치 경로 가져오기 + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#OldAppName}_is1', + 'InstallLocation', OldAppPath) then + begin + Log('이전 버전({#OldAppName})이 발견되었습니다. 경로: ' + OldAppPath); + + // user_data 폴더 경로 설정 - 우선순위: Program Files → AppData\Local + UserDataSourcePath := OldAppPath + '\user_data'; + + // Program Files에 user_data가 없으면 AppData\Local에서 찾기 + if not DirExists(UserDataSourcePath) then + begin + UserDataSourcePath := ExpandConstant('{localappdata}\' + '{#OldAppName}' + '\lib\user_data'); + Log('Program Files에 user_data 없음. AppData\Local 경로 확인: ' + UserDataSourcePath); + end + else + begin + Log('Program Files에서 user_data 발견: ' + UserDataSourcePath); + end; + + // 프로그램이 실행 중인지 확인하고 종료 요청 + if IsAppRunning('{#OldAppName}.exe') then + begin + if MsgBox('프로그램을 업데이트하기 위해 실행 중인 프로그램을 종료해야 합니다.' + #13#10 + + '계속하시겠습니까?', mbConfirmation, MB_YESNO) = IDNO then + begin + Result := False; + exit; + end; + CloseApplication('{#OldAppName}.exe'); + // 프로세스가 완전히 종료될 때까지 대기 + Sleep(2000); + end; + + // user_data 폴더 백업 + if DirExists(UserDataSourcePath) then + begin + Log('user_data 폴더 백업 중: ' + UserDataSourcePath + ' -> ' + UserDataBackupPath); + ForceDirectories(UserDataBackupPath); + CopyDir(UserDataSourcePath, UserDataBackupPath); + end; + + // 이전 버전 제거 (제거 프로그램 실행) + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#OldAppName}_is1', + 'UninstallString', OldUninstallString) then + begin + // 사용자에게 확인 + if MsgBox('이전 버전을 제거하고 새 버전을 설치하시겠습니까?' + #13#10 + + '사용자 데이터는 유지됩니다.', mbConfirmation, MB_YESNO) = IDYES then + begin + // 자동 제거를 위해 /SILENT 매개변수 추가 + OldUninstallString := OldUninstallString + ' /SILENT'; + Log('이전 버전 제거 실행: ' + OldUninstallString); + + // 제거 프로그램 실행 및 완료 대기 + if Exec(RemoveQuotes(OldUninstallString), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then + begin + Log('이전 버전 제거 완료: 결과 코드 ' + IntToStr(ResultCode)); + end + else + begin + Log('이전 버전 제거 실패'); + // 제거에 실패해도 계속 진행 + end; + end; + end; + end; + end + // 현재 프로그램(Edit_PartTimer3) 버전 확인 + else 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'); + end; + + // Edit_PartTimer3의 user_data 폴더 백업 - 우선순위: Program Files → AppData\Local + if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppName}_is1', + 'InstallLocation', OldAppPath) then + begin + UserDataSourcePath := OldAppPath + '\user_data'; + + // Program Files에 user_data가 없으면 AppData\Local에서 찾기 + if not DirExists(UserDataSourcePath) then + begin + UserDataSourcePath := ExpandConstant('{localappdata}\' + '{#MyAppName}' + '\lib\user_data'); + Log('Program Files에 user_data 없음. AppData\Local 경로 확인: ' + UserDataSourcePath); + end + else + begin + Log('Program Files에서 user_data 발견: ' + UserDataSourcePath); + end; + + if DirExists(UserDataSourcePath) then + begin + Log('user_data 폴더 백업 중: ' + UserDataSourcePath + ' -> ' + UserDataBackupPath); + ForceDirectories(UserDataBackupPath); + CopyDir(UserDataSourcePath, UserDataBackupPath); + 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}\user_data'); + + // 백업한 user_data 폴더가 있으면 복원 + if DirExists(UserDataBackupPath) then + begin + Log('백업된 user_data 폴더 복원 중: ' + UserDataBackupPath + ' -> ' + UserDataDestPath); + ForceDirectories(UserDataDestPath); + CopyDir(UserDataBackupPath, UserDataDestPath); + Log('user_data 폴더 복원 완료'); + end; + end; +end; diff --git a/mainUI_SP.py b/mainUI_SP.py index 6ddc0ede..fbaf5a0d 100644 --- a/mainUI_SP.py +++ b/mainUI_SP.py @@ -5107,25 +5107,30 @@ class MAIN_GUI(QMainWindow): # 로그인 후 설정 버튼 비활성화 self.toggle_settings_button.setEnabled(False) - is_level_premium = self.user_membership_level == 'premium' - is_level_vip = self.user_membership_level == 'vip' + # is_valid_level = self.user_membership_level == 'premium' or self.user_membership_level == 'vip' + # is_level_vip = self.user_membership_level == 'vip' is_trans_enabled = self.toggle_states.get('optionIMGTrans', False) or self.toggle_states.get('detail_IMGTrans', False) or self.toggle_states.get('thumb', False) or self.toggle_states.get('thumb_nukki', False) - self.logger.log(f"user_membership_level: {self.user_membership_level}", level=logging.DEBUG) - self.logger.log(f"is_level_premium: {is_level_premium}, is_trans_enabled: {is_trans_enabled}", level=logging.DEBUG) - try: - if is_level_premium and is_trans_enabled: - from src.modules.image_processor3 import ImageProcessor3 - self.image_processor = ImageProcessor3(self.logger, self.browser_controller.page, self.toggle_states, self.toggle_states['unwanted_words'], self.base_dir) - self.browser_controller.image_processor = self.image_processor + # self.logger.log(f"user_membership_level: {self.user_membership_level}", level=logging.DEBUG) + # self.logger.log(f"is_valid_level: {is_valid_level}", level=logging.DEBUG) + self.logger.log(f"is_trans_enabled: {is_trans_enabled}", level=logging.DEBUG) + # try: + # if is_valid_level and is_trans_enabled: + # from src.modules.image_processor3 import ImageProcessor3 + # self.image_processor = ImageProcessor3(self.logger, self.browser_controller.page, self.toggle_states, self.toggle_states['unwanted_words'], self.base_dir) + # self.browser_controller.image_processor = self.image_processor + # except Exception as e: + # self.logger.log(f"ImageProcessor3 초기화 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) - if is_level_vip and is_trans_enabled: + try: + if is_trans_enabled: from src.modules.image_processor3 import ImageProcessor3 self.image_processor = ImageProcessor3(self.logger, self.browser_controller.page, self.toggle_states, self.toggle_states['unwanted_words'], self.base_dir) self.browser_controller.image_processor = self.image_processor except Exception as e: self.logger.log(f"ImageProcessor3 초기화 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + self.logger.log(f"ImageProcessor3 초기화 완료", level=logging.DEBUG) self.browser_controller.update_toggle_states(self.toggle_states) diff --git a/src/modules/background_removal_module.py b/src/modules/background_removal_module.py index a72eee08..b904541e 100644 --- a/src/modules/background_removal_module.py +++ b/src/modules/background_removal_module.py @@ -24,10 +24,20 @@ class BackgroundRemovalModule: "u2net": "범용 모델 (사람/사물 모두, 빠르고 정확함, 대부분 상황에서 권장)", "u2netp": "u2net 경량화 버전 (속도 빠름, 품질은 약간 낮음, 실시간/저사양용)", "u2net_human_seg": "사람 인식 특화 (프로필, 인물 사진)", - "u2net_cloth_seg": "의류/패션 이미지 특화", + "u2net_cloth_seg": "의류/패션 이미지 특화 (상/하/전체 의류 분리)", + "silueta": "u2net과 유사하나 모델 크기가 43MB로 작음 (경량화)", "isnet-general-use": "최신 고성능 범용 모델 (디테일 강조, 고성능PC 추천)", + "isnet-anime": "애니메이션 캐릭터 특화 고정밀 세그멘테이션", "sam": "Segment Anything Model (최고 품질, 고사양/메모리 충분할 때)", - "sam-mobile": "SAM의 경량화 (모바일, 저사양PC, 빠른 속도)" + "sam-mobile": "SAM의 경량화 (모바일, 저사양PC, 빠른 속도)", + "birefnet-general": "범용 사전학습 모델 (빠르고 다양한 상황에 적합)", + "birefnet-general-lite": "경량화된 범용 사전학습 모델 (속도↑, 성능↓)", + "birefnet-portrait": "인물 사진 특화 사전학습 모델", + "birefnet-dis": "이분법적 이미지 세그멘테이션(Dichotomous Image Segmentation) 특화", + "birefnet-hrsod": "고해상도 주목 객체 검출(High-Resolution Salient Object Detection) 특화", + "birefnet-cod": "은닉 객체 검출(Concealed Object Detection) 특화", + "birefnet-massive": "대규모 데이터셋 기반 범용 모델", + "ben2-base": "Confidence Guided Matting(CGM) 기반의 새로운 전경 분리 방식 도입, 최신 연구 기반" } def __init__(self, logger=None, default_model="u2net"): diff --git a/src/modules/image_processor3.py b/src/modules/image_processor3.py index 14eb51ec..f26e150e 100644 --- a/src/modules/image_processor3.py +++ b/src/modules/image_processor3.py @@ -55,7 +55,7 @@ class ImageProcessor3: self.mask_module = MaskModule(logger=self.logger, base_dir=self.base_dir) self.text_rendering_module = TextRenderingModule(logger=self.logger, font_path=self.font_path) self.postImageManager = PostImageManager(logger=self.logger, toggle_states=self.toggle_states) - self.background_removal_module = BackgroundRemovalModule(logger=self.logger) + self.background_removal_module = BackgroundRemovalModule(logger=self.logger, default_model="birefnet-general") self.request_inpainting = RequestInpainting(logger=self.logger, server_url=self.toggle_states.get("request_inpainting_server_url", None)) diff --git a/src/modules/img/nobg_img_9.png b/src/modules/img/nobg_img_9.png new file mode 100644 index 00000000..02e9b147 Binary files /dev/null and b/src/modules/img/nobg_img_9.png differ diff --git a/src/modules/modules/PP_Models/cls/inference.pdiparams b/src/modules/modules/PP_Models/cls/inference.pdiparams new file mode 100644 index 00000000..3449efb5 Binary files /dev/null and b/src/modules/modules/PP_Models/cls/inference.pdiparams differ diff --git a/src/modules/modules/PP_Models/cls/inference.pdiparams.info b/src/modules/modules/PP_Models/cls/inference.pdiparams.info new file mode 100644 index 00000000..f31a1575 Binary files /dev/null and b/src/modules/modules/PP_Models/cls/inference.pdiparams.info differ diff --git a/src/modules/modules/PP_Models/cls/inference.pdmodel b/src/modules/modules/PP_Models/cls/inference.pdmodel new file mode 100644 index 00000000..b90c1550 Binary files /dev/null and b/src/modules/modules/PP_Models/cls/inference.pdmodel differ diff --git a/src/modules/modules/PP_Models/det/ch_PP-OCRv4_det_infer.tar.tmp b/src/modules/modules/PP_Models/det/ch_PP-OCRv4_det_infer.tar.tmp deleted file mode 100644 index cdfdcd66..00000000 Binary files a/src/modules/modules/PP_Models/det/ch_PP-OCRv4_det_infer.tar.tmp and /dev/null differ diff --git a/src/modules/modules/PP_Models/det/inference.pdiparams b/src/modules/modules/PP_Models/det/inference.pdiparams new file mode 100644 index 00000000..089594ae Binary files /dev/null and b/src/modules/modules/PP_Models/det/inference.pdiparams differ diff --git a/src/modules/modules/PP_Models/det/inference.pdiparams.info b/src/modules/modules/PP_Models/det/inference.pdiparams.info new file mode 100644 index 00000000..082c148e Binary files /dev/null and b/src/modules/modules/PP_Models/det/inference.pdiparams.info differ diff --git a/src/modules/modules/PP_Models/det/inference.pdmodel b/src/modules/modules/PP_Models/det/inference.pdmodel new file mode 100644 index 00000000..223b8614 Binary files /dev/null and b/src/modules/modules/PP_Models/det/inference.pdmodel differ diff --git a/src/modules/modules/PP_Models/rec/inference.pdiparams b/src/modules/modules/PP_Models/rec/inference.pdiparams new file mode 100644 index 00000000..4c3d9e9c Binary files /dev/null and b/src/modules/modules/PP_Models/rec/inference.pdiparams differ diff --git a/src/modules/modules/PP_Models/rec/inference.pdiparams.info b/src/modules/modules/PP_Models/rec/inference.pdiparams.info new file mode 100644 index 00000000..923329f5 Binary files /dev/null and b/src/modules/modules/PP_Models/rec/inference.pdiparams.info differ diff --git a/src/modules/modules/PP_Models/rec/inference.pdmodel b/src/modules/modules/PP_Models/rec/inference.pdmodel new file mode 100644 index 00000000..dccddcc7 Binary files /dev/null and b/src/modules/modules/PP_Models/rec/inference.pdmodel differ diff --git a/src/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db b/src/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db index 9e5f5acb..dc8dbda6 100644 Binary files a/src/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db and b/src/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db differ diff --git a/updateManager/__version__.py b/updateManager/__version__.py index 68d536d1..05ada69f 100644 --- a/updateManager/__version__.py +++ b/updateManager/__version__.py @@ -7,7 +7,7 @@ import logging """ 프로그램 기본 정보 """ __title__ = "Edit_PartTimer" __description__ = "편집알바생" -__version__ = "3.9.1" +__version__ = "3.9.2" __build__ = "1" # 빌드 번호 __author__ = "WhenRideMyCar" __author_email__ = "abc@gmail.com" diff --git a/updateManager/updateLog.md b/updateManager/updateLog.md index 00635bbc..587b4784 100644 --- a/updateManager/updateLog.md +++ b/updateManager/updateLog.md @@ -1,7 +1,12 @@ # 3.9.2 업데이트 로그 +## 초기 실행시 모델로딩으로 인해 반응이 느릴수 있습니다. + ### 오류수정 -- 썸네일 번역방식변경 추가 +- 썸네일 번역방식 변경 추가 +- vip일 경우 이미지프로세서가 초기화되지 않는 문제 수정 + + # 3.9.1 업데이트 로그