mirror of
https://github.com/relativemodder/aegnux.git
synced 2025-12-11 22:19:38 +05:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4285914512 | ||
|
|
aed41431f2 | ||
|
|
e05e9e576b | ||
|
|
d0fb24a741 | ||
|
|
700a669bc5 | ||
|
|
e6307377ef | ||
|
|
1fd0252870 | ||
|
|
6d6dd386f5 | ||
|
|
49830680b0 | ||
|
|
83c077a54d | ||
|
|
969ecd7884 | ||
|
|
77a3bc3727 | ||
|
|
f9aa1b3878 | ||
|
|
3a835bdb63 | ||
|
|
cf8d516b8a | ||
|
|
0430495564 | ||
|
|
3ad6138077 | ||
|
|
b93d02faf3 | ||
|
|
b30982f9e8 | ||
|
|
a4447fa369 | ||
|
|
8cdd09da51 | ||
|
|
36b095210d |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,4 +13,5 @@ bin/*
|
||||
assets/wine
|
||||
assets/vcr.zip
|
||||
assets/msxml3.zip
|
||||
assets/gdiplus.dll
|
||||
assets/gdiplus.dll
|
||||
assets/dxvk.tar.gz
|
||||
11
README.md
11
README.md
@@ -6,6 +6,17 @@ A convenient way to install Adobe After Effects on Linux using Wine. Heavily ins
|
||||
|
||||
**⚠️ SOFTWARE IS NOT IN THE RELEASE STATE**
|
||||
|
||||
<div align="center">
|
||||
|
||||
### Aegnux community
|
||||
|
||||
[](https://www.reddit.com/r/Aegnux/)
|
||||
[](https://aur.archlinux.org/packages/aegnux)
|
||||
[](https://t.me/Aegnux)
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
*If you're interested in the project's roadmap, check out [**ROADMAP.md**](https://github.com/relativemodder/aegnux/blob/main/ROADMAP.md)*.
|
||||
|
||||
[Download Flatpak package](https://github.com/relativemodder/com.relative.Aegnux/releases/latest) if you want to install it on any distro.
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
- [ ] Get hardware acceleration working on AMD GPUs
|
||||
|
||||
- [ ] Plugins installation
|
||||
- [x] Plugins installation
|
||||
|
||||
- [x] Ged rid of binary blobs
|
||||
|
||||
- [ ] Replace slow winetricks script
|
||||
- [ ] Replace slow winetricks script (almost done)
|
||||
|
||||
- [ ] Desktop integration (.aep file association)
|
||||
- [x] Desktop integration (.aep file association)
|
||||
|
||||
- [ ] Link opening support (e.g. Mister Horse or CC Login)
|
||||
|
||||
- [ ] Make .deb package
|
||||
|
||||
|
||||
8
assets/dxvk.reg
Normal file
8
assets/dxvk.reg
Normal file
@@ -0,0 +1,8 @@
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Wine\DllOverrides]
|
||||
"d3d10core"="native,builtin"
|
||||
"d3d11"="native,builtin"
|
||||
"d3d8"="native,builtin"
|
||||
"d3d9"="native,builtin"
|
||||
"dxgi"="native,builtin"
|
||||
7
assets/fontsmooth.reg
Normal file
7
assets/fontsmooth.reg
Normal file
@@ -0,0 +1,7 @@
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_CURRENT_USER\Control Panel\Desktop]
|
||||
"FontSmoothing"="2"
|
||||
"FontSmoothingGamma"=dword:00000578
|
||||
"FontSmoothingOrientation"=dword:00000001
|
||||
"FontSmoothingType"=dword:00000002
|
||||
27
prepare.sh
27
prepare.sh
@@ -1,12 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
# Download Kitty Binary
|
||||
echo Downloading Kitty Binary...
|
||||
curl -LO https://github.com/kovidgoyal/kitty/releases/download/v0.43.1/kitty-0.43.1-x86_64.txz
|
||||
mkdir -p ./bin/kitty && tar Jxf kitty-0.43.1-x86_64.txz --strip-components=0 -C ./bin/kitty
|
||||
rm kitty-0.43.1-x86_64.txz
|
||||
|
||||
# Yeah, bundling the entire terminal emulator for flatpak builds lol
|
||||
if [ -d /app ]; then
|
||||
# Download Kitty Binary
|
||||
echo Downloading Kitty Binary...
|
||||
curl -LO https://github.com/kovidgoyal/kitty/releases/download/v0.43.1/kitty-0.43.1-x86_64.txz
|
||||
mkdir -p ./bin/kitty && tar Jxf kitty-0.43.1-x86_64.txz --strip-components=0 -C ./bin/kitty
|
||||
rm kitty-0.43.1-x86_64.txz
|
||||
fi
|
||||
|
||||
# Download winetricks
|
||||
echo Downloading Winetricks...
|
||||
@@ -27,9 +28,9 @@ rm cabextract-1.11-1.x86_64.rpm
|
||||
|
||||
# Download Wine
|
||||
echo Downloading Wine...
|
||||
curl -LO https://github.com/Kron4ek/Wine-Builds/releases/download/10.17/wine-10.17-amd64-wow64.tar.xz
|
||||
mkdir -p ./assets/wine && tar Jxf wine-10.17-amd64-wow64.tar.xz --strip-components=1 -C ./assets/wine
|
||||
rm wine-10.17-amd64-wow64.tar.xz
|
||||
curl -LO https://github.com/Kron4ek/Wine-Builds/releases/download/10.18/wine-10.18-staging-tkg-amd64-wow64.tar.xz
|
||||
mkdir -p ./assets/wine && tar Jxf wine-10.18-staging-tkg-amd64-wow64.tar.xz --strip-components=1 -C ./assets/wine
|
||||
rm wine-10.18-staging-tkg-amd64-wow64.tar.xz
|
||||
|
||||
|
||||
# Download Visual C++ Redistributable Runtimes
|
||||
@@ -52,5 +53,11 @@ echo Downloading gdiplus.dll...
|
||||
curl -LO https://github.com/relativemodder/aegnux/releases/download/vcrbin/gdiplus.dll
|
||||
mv gdiplus.dll ./assets/
|
||||
|
||||
|
||||
# Download dxvk
|
||||
echo Downloading dxvk...
|
||||
curl -LO https://github.com/doitsujin/dxvk/releases/download/v2.7.1/dxvk-2.7.1.tar.gz
|
||||
mv dxvk-2.7.1.tar.gz ./assets/dxvk.tar.gz
|
||||
|
||||
echo --------------------------------------------
|
||||
echo Done!
|
||||
7
run.sh
7
run.sh
@@ -1,8 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPT_PATH=$(readlink -f "$0")
|
||||
SCRIPT_DIR=$(dirname "SCRIPT_PATH")
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
python main.py
|
||||
python main.py "$@"
|
||||
15
src/app.py
15
src/app.py
@@ -11,7 +11,18 @@ def main():
|
||||
app.setDesktopFileName(DESKTOP_FILE_NAME)
|
||||
app.setWindowIcon(QIcon(AE_ICON_PATH))
|
||||
|
||||
mainWindow = MainWindow()
|
||||
mainWindow.show()
|
||||
show_window = True
|
||||
quit_after_handling_args = False
|
||||
|
||||
for arg in sys.argv:
|
||||
if 'misterhorsepm://' in arg or '.aep' in arg:
|
||||
show_window = False
|
||||
quit_after_handling_args = True
|
||||
break
|
||||
|
||||
mainWindow = MainWindow(quit_after_handling_args)
|
||||
|
||||
if show_window:
|
||||
mainWindow.show()
|
||||
|
||||
return app.exec()
|
||||
@@ -12,6 +12,9 @@ WINE_RUNNER_DIR = BASE_DIR + '/assets/wine'
|
||||
WINETRICKS_BIN = BASE_DIR + '/bin/winetricks'
|
||||
CABEXTRACT_BIN = BASE_DIR + '/bin/cabextract'
|
||||
GDIPLUS_DLL = BASE_DIR + '/assets/gdiplus.dll'
|
||||
FONTSMOOTH_REG = BASE_DIR + '/assets/fontsmooth.reg'
|
||||
DXVK_TAR = BASE_DIR + '/assets/dxvk.tar.gz'
|
||||
DXVK_REG = BASE_DIR + '/assets/dxvk.reg'
|
||||
|
||||
VCR_ZIP = BASE_DIR + '/assets/vcr.zip'
|
||||
MSXML_ZIP = BASE_DIR + '/assets/msxml3.zip'
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import tarfile
|
||||
import traceback
|
||||
from src.config import (
|
||||
AE_DOWNLOAD_URL, AE_FILENAME,
|
||||
AE_DOWNLOAD_URL, AE_FILENAME, DXVK_REG, FONTSMOOTH_REG,
|
||||
WINE_RUNNER_DIR, WINETRICKS_BIN,
|
||||
CABEXTRACT_BIN, WINE_STYLE_REG,
|
||||
VCR_ZIP, MSXML_ZIP, GDIPLUS_DLL
|
||||
VCR_ZIP, MSXML_ZIP, GDIPLUS_DLL, DXVK_TAR
|
||||
)
|
||||
from src.processthread import ProcessThread
|
||||
from src.utils import (
|
||||
DownloadMethod, get_aegnux_installation_dir,
|
||||
get_ae_install_dir, get_wine_runner_dir, is_nvidia_present,
|
||||
get_winetricks_bin, get_wineprefix_dir, get_cabextract_bin,
|
||||
get_vcr_dir_path, get_msxml_dir_path, mark_aegnux_as_installed
|
||||
get_vcr_dir_path, get_msxml_dir_path, mark_aegnux_as_installed,
|
||||
get_cep_dir
|
||||
)
|
||||
|
||||
class InstallationThread(ProcessThread):
|
||||
@@ -32,11 +35,8 @@ class InstallationThread(ProcessThread):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
try:
|
||||
shutil.rmtree(get_aegnux_installation_dir(), True)
|
||||
except:
|
||||
self.log_signal.emit(f'[WARNING] Can\'t remove existing installation.')
|
||||
|
||||
self.try_cleanup_installation()
|
||||
|
||||
self.progress_signal.emit(10)
|
||||
|
||||
if self.download_method == DownloadMethod.ONLINE:
|
||||
@@ -45,35 +45,7 @@ class InstallationThread(ProcessThread):
|
||||
|
||||
self.progress_signal.emit(15)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Unpacking AE from {self.ae_filename}...')
|
||||
aegnux_install_dir = get_aegnux_installation_dir()
|
||||
self.unpack_zip(self.ae_filename, aegnux_install_dir.as_posix())
|
||||
|
||||
root_path = Path(aegnux_install_dir)
|
||||
target_dir = Path(get_ae_install_dir())
|
||||
source_folder_to_delete = None
|
||||
|
||||
self.log_signal.emit('[DEBUG] Searching for AfterFX.exe...')
|
||||
|
||||
for exe_path in root_path.rglob('AfterFX.exe'):
|
||||
source_dir_to_move = exe_path.parent
|
||||
self.log_signal.emit(f'[DEBUG] Found installation folder: {source_dir_to_move}')
|
||||
for item in source_dir_to_move.iterdir():
|
||||
shutil.move(item.as_posix(), target_dir.joinpath(item.name).as_posix())
|
||||
self.log_signal.emit(f'[DEBUG] All contents moved to {target_dir}')
|
||||
source_folder_to_delete = source_dir_to_move.parent
|
||||
break
|
||||
|
||||
if source_folder_to_delete and source_folder_to_delete != root_path:
|
||||
self.log_signal.emit(f'[DEBUG] Removing temporary folder: {source_folder_to_delete}')
|
||||
try:
|
||||
shutil.rmtree(source_folder_to_delete.as_posix())
|
||||
self.log_signal.emit('[DEBUG] Temporary folder removed successfully.')
|
||||
except OSError as e:
|
||||
self.log_signal.emit(f'[ERROR] Failed to remove temporary folder {source_folder_to_delete}: {e}')
|
||||
else:
|
||||
self.log_signal.emit('[WARNING] Installation folder not found or matched root path. No folder was deleted.')
|
||||
|
||||
self.unpack_ae()
|
||||
|
||||
self.progress_signal.emit(20)
|
||||
|
||||
@@ -107,48 +79,27 @@ class InstallationThread(ProcessThread):
|
||||
|
||||
self.progress_signal.emit(60)
|
||||
|
||||
tweaks = ['dxvk', 'corefonts', 'fontsmooth=rgb']
|
||||
tweaks = ['corefonts']
|
||||
for tweak in tweaks:
|
||||
self.log_signal.emit(f'[DEBUG] Installing {tweak} with winetricks')
|
||||
self.run_command(['winetricks', '-q', tweak], in_prefix=True)
|
||||
self.progress_signal.emit(60 + tweaks.index(tweak) * 2)
|
||||
|
||||
self.install_dxvk()
|
||||
|
||||
self.progress_signal.emit(70)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Unpacking VCR to {get_vcr_dir_path()}...')
|
||||
self.unpack_zip(VCR_ZIP, get_vcr_dir_path().as_posix())
|
||||
|
||||
self.progress_signal.emit(80)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Installing VCR')
|
||||
self.run_command(['wine', get_vcr_dir_path().joinpath('install_all.bat').as_posix()], in_prefix=True)
|
||||
self.install_vcr()
|
||||
|
||||
self.progress_signal.emit(85)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Unpacking MSXML3 to {get_msxml_dir_path()}...')
|
||||
self.unpack_zip(MSXML_ZIP, get_msxml_dir_path().as_posix())
|
||||
|
||||
self.progress_signal.emit(90)
|
||||
self.install_msxml3()
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Overriding MSXML3 DLL...')
|
||||
system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32')
|
||||
shutil.copy(get_msxml_dir_path().joinpath('msxml3.dll'), system32_dir.joinpath('msxml3.dll'))
|
||||
shutil.copy(get_msxml_dir_path().joinpath('msxml3r.dll'), system32_dir.joinpath('msxml3r.dll'))
|
||||
self.install_gdiplus()
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Applying fontsmooth settings')
|
||||
self.run_command(
|
||||
['wine', 'reg', 'add',
|
||||
'HKCU\\Software\\Wine\\DllOverrides', '/v',
|
||||
'msxml3', '/d', 'native,builtin', '/f'],
|
||||
in_prefix=True
|
||||
)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Overriding gdiplus DLL...')
|
||||
shutil.copy(GDIPLUS_DLL, system32_dir.joinpath('gdiplus.dll'))
|
||||
|
||||
self.run_command(
|
||||
['wine', 'reg', 'add',
|
||||
'HKCU\\Software\\Wine\\DllOverrides', '/v',
|
||||
'gdiplus', '/d', 'native,builtin', '/f'],
|
||||
['wine', 'regedit', FONTSMOOTH_REG],
|
||||
in_prefix=True
|
||||
)
|
||||
|
||||
@@ -156,6 +107,13 @@ class InstallationThread(ProcessThread):
|
||||
self.log_signal.emit("[INFO] Starting NVIDIA libs installation...")
|
||||
self.install_nvidia_libs()
|
||||
|
||||
try:
|
||||
self.log_signal.emit(f"[INFO] Created CEP directory in {get_cep_dir()}")
|
||||
except:
|
||||
pass
|
||||
|
||||
self.symlink_support_files()
|
||||
|
||||
self.progress_signal.emit(99)
|
||||
|
||||
self.cleanup()
|
||||
@@ -166,8 +124,139 @@ class InstallationThread(ProcessThread):
|
||||
|
||||
self.finished_signal.emit(True)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self.log_signal.emit(f'[ERROR] {e}')
|
||||
self.finished_signal.emit(False)
|
||||
|
||||
def symlink_support_files(self):
|
||||
ae_dir = get_ae_install_dir()
|
||||
ae_pf_dir = get_wineprefix_dir().joinpath('drive_c/Program Files/Adobe/Adobe After Effects 2024')
|
||||
support_files_dir = ae_pf_dir.joinpath('Support Files')
|
||||
|
||||
if not ae_pf_dir.exists():
|
||||
os.makedirs(ae_pf_dir)
|
||||
|
||||
if not support_files_dir.exists():
|
||||
os.symlink(ae_dir, support_files_dir)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Created symlink from {ae_dir} to {support_files_dir}')
|
||||
|
||||
def try_cleanup_installation(self):
|
||||
try:
|
||||
shutil.rmtree(get_aegnux_installation_dir(), True)
|
||||
except:
|
||||
self.log_signal.emit(f'[WARNING] Can\'t remove existing installation.')
|
||||
|
||||
def install_vcr(self):
|
||||
self.log_signal.emit(f'[DEBUG] Unpacking VCR to {get_vcr_dir_path()}...')
|
||||
self.unpack_zip(VCR_ZIP, get_vcr_dir_path().as_posix())
|
||||
|
||||
self.progress_signal.emit(80)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Installing VCR')
|
||||
self.run_command(['wine', get_vcr_dir_path().joinpath('install_all.bat').as_posix()], in_prefix=True)
|
||||
|
||||
def install_msxml3(self):
|
||||
self.log_signal.emit(f'[DEBUG] Unpacking MSXML3 to {get_msxml_dir_path()}...')
|
||||
self.unpack_zip(MSXML_ZIP, get_msxml_dir_path().as_posix())
|
||||
|
||||
self.progress_signal.emit(90)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Overriding MSXML3 DLL...')
|
||||
system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32')
|
||||
shutil.copy(get_msxml_dir_path().joinpath('msxml3.dll'), system32_dir.joinpath('msxml3.dll'))
|
||||
shutil.copy(get_msxml_dir_path().joinpath('msxml3r.dll'), system32_dir.joinpath('msxml3r.dll'))
|
||||
|
||||
self.run_command(
|
||||
['wine', 'reg', 'add',
|
||||
'HKCU\\Software\\Wine\\DllOverrides', '/v',
|
||||
'msxml3', '/d', 'native,builtin', '/f'],
|
||||
in_prefix=True
|
||||
)
|
||||
|
||||
def install_gdiplus(self):
|
||||
system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32')
|
||||
self.log_signal.emit(f'[DEBUG] Overriding gdiplus DLL...')
|
||||
shutil.copy(GDIPLUS_DLL, system32_dir.joinpath('gdiplus.dll'))
|
||||
|
||||
self.run_command(
|
||||
['wine', 'reg', 'add',
|
||||
'HKCU\\Software\\Wine\\DllOverrides', '/v',
|
||||
'gdiplus', '/d', 'native,builtin', '/f'],
|
||||
in_prefix=True
|
||||
)
|
||||
|
||||
def get_tar_root_dir_name(self, tar_path) -> str | None:
|
||||
with tarfile.open(tar_path, 'r') as tar:
|
||||
member_names = tar.getnames()
|
||||
if not member_names:
|
||||
return None
|
||||
|
||||
first_member = member_names[0]
|
||||
root_name = first_member.split(os.path.sep)[0]
|
||||
|
||||
return root_name
|
||||
|
||||
def install_dxvk(self):
|
||||
system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32')
|
||||
syswow64_dir = get_wineprefix_dir().joinpath('drive_c/windows/syswow64')
|
||||
self.unpack_tar(DXVK_TAR, get_wineprefix_dir())
|
||||
|
||||
dxvk_name = self.get_tar_root_dir_name(DXVK_TAR)
|
||||
dxvk_root_dir = get_wineprefix_dir().joinpath(dxvk_name)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] DXVK root dir is {dxvk_root_dir}')
|
||||
|
||||
source_x64 = dxvk_root_dir.joinpath('x64')
|
||||
source_x32 = dxvk_root_dir.joinpath('x32')
|
||||
|
||||
for source in [source_x64, source_x32]:
|
||||
for item in source.iterdir():
|
||||
system_dir = system32_dir if 'x64' in source.as_posix() else syswow64_dir
|
||||
|
||||
if item.is_file():
|
||||
shutil.copy2(item, system_dir.joinpath(item.name))
|
||||
elif item.is_dir():
|
||||
dest = system_dir.joinpath(item.name)
|
||||
if dest.exists():
|
||||
shutil.rmtree(dest)
|
||||
shutil.copytree(item, dest)
|
||||
|
||||
self.log_signal.emit(f'[DEBUG] Overriding DXVK dlls')
|
||||
self.run_command(
|
||||
['wine', 'regedit', DXVK_REG],
|
||||
in_prefix=True
|
||||
)
|
||||
|
||||
def unpack_ae(self):
|
||||
self.log_signal.emit(f'[DEBUG] Unpacking AE from {self.ae_filename}...')
|
||||
aegnux_install_dir = get_aegnux_installation_dir()
|
||||
self.unpack_zip(self.ae_filename, aegnux_install_dir.as_posix())
|
||||
|
||||
root_path = Path(aegnux_install_dir)
|
||||
target_dir = Path(get_ae_install_dir())
|
||||
source_folder_to_delete = None
|
||||
|
||||
self.log_signal.emit('[DEBUG] Searching for AfterFX.exe...')
|
||||
|
||||
for exe_path in root_path.rglob('AfterFX.exe'):
|
||||
source_dir_to_move = exe_path.parent
|
||||
self.log_signal.emit(f'[DEBUG] Found installation folder: {source_dir_to_move}')
|
||||
for item in source_dir_to_move.iterdir():
|
||||
shutil.move(item.as_posix(), target_dir.joinpath(item.name).as_posix())
|
||||
self.log_signal.emit(f'[DEBUG] All contents moved to {target_dir}')
|
||||
source_folder_to_delete = source_dir_to_move.parent
|
||||
break
|
||||
|
||||
if source_folder_to_delete and source_folder_to_delete != root_path:
|
||||
self.log_signal.emit(f'[DEBUG] Removing temporary folder: {source_folder_to_delete}')
|
||||
try:
|
||||
shutil.rmtree(source_folder_to_delete.as_posix())
|
||||
self.log_signal.emit('[DEBUG] Temporary folder removed successfully.')
|
||||
except OSError as e:
|
||||
self.log_signal.emit(f'[ERROR] Failed to remove temporary folder {source_folder_to_delete}: {e}')
|
||||
else:
|
||||
self.log_signal.emit('[WARNING] Installation folder not found or matched root path. No folder was deleted.')
|
||||
|
||||
def install_nvidia_libs(self):
|
||||
download_url = "https://github.com/SveSop/nvidia-libs/releases/download/v0.8.5/nvidia-libs-v0.8.5.tar.xz"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from ui.mainwindow import MainWindowUI
|
||||
from translations import gls
|
||||
from PySide6.QtCore import Slot
|
||||
@@ -9,19 +10,24 @@ from src.installationthread import InstallationThread
|
||||
from src.runaethread import RunAEThread
|
||||
from src.runexethread import RunExeThread
|
||||
from src.killaethread import KillAEThread
|
||||
from src.pluginthread import PluginThread
|
||||
from src.removeaethread import RemoveAEThread
|
||||
from src.utils import (
|
||||
check_aegnux_tip_marked, get_wine_bin_path_env,
|
||||
show_download_method_dialog, get_ae_plugins_dir, get_wineprefix_dir,
|
||||
check_aegnux_tip_marked, get_default_terminal, get_mhtb_install_dir, get_wine_bin_path_env,
|
||||
get_cep_dir, get_ae_plugins_dir, get_wineprefix_dir,
|
||||
check_aegnux_installed, mark_aegnux_tip_as_shown, get_ae_install_dir, get_aegnux_installation_dir
|
||||
)
|
||||
from src.types import DownloadMethod
|
||||
|
||||
|
||||
class MainWindow(MainWindowUI):
|
||||
def __init__(self):
|
||||
def __init__(self, quit_after_handling_args: bool = False):
|
||||
super().__init__()
|
||||
|
||||
self.ran_from_aep_file = False
|
||||
self.ran_from_mhtb_link = False
|
||||
self.quit_after_handling_args = quit_after_handling_args
|
||||
|
||||
self.setWindowTitle(gls('welcome_win_title'))
|
||||
self.install_button.clicked.connect(self.install_button_clicked)
|
||||
self.run_button.clicked.connect(self.run_ae_button_clicked)
|
||||
@@ -40,6 +46,11 @@ class MainWindow(MainWindowUI):
|
||||
self.remove_ae_thread = RemoveAEThread()
|
||||
self.remove_ae_thread.finished_signal.connect(self._finished)
|
||||
|
||||
self.plugin_thread = PluginThread()
|
||||
self.plugin_thread.log_signal.connect(self._log)
|
||||
self.plugin_thread.progress_signal.connect(self.progress_bar.setValue)
|
||||
self.plugin_thread.finished_signal.connect(self._finished)
|
||||
|
||||
self.alt_t_action = QAction(self)
|
||||
self.alt_t_action.setShortcut(QKeySequence("Alt+T"))
|
||||
self.alt_t_action.triggered.connect(self.run_command_alt_t)
|
||||
@@ -56,6 +67,7 @@ class MainWindow(MainWindowUI):
|
||||
self.ae_action.triggered.connect(self.run_ae_button_clicked)
|
||||
self.exe_action.triggered.connect(self.run_exe_button_clicked)
|
||||
self.reg_action.triggered.connect(self.reg_button_clicked)
|
||||
self.plugininst_action.triggered.connect(self.install_plugins_button_clicked)
|
||||
self.kill_action.triggered.connect(self.kill_ae_button_clicked)
|
||||
self.log_action.triggered.connect(self.toggle_logs)
|
||||
self.term_action.triggered.connect(self.run_command_alt_t)
|
||||
@@ -63,6 +75,58 @@ class MainWindow(MainWindowUI):
|
||||
self.plugind_action.triggered.connect(self.plugins_folder_clicked)
|
||||
self.aed_action.triggered.connect(self.ae_folder_clicked)
|
||||
self.aeg_action.triggered.connect(self.aegnux_folder_clicked)
|
||||
self.cep_action.triggered.connect(self.cep_folder_clicked)
|
||||
|
||||
def try_autoopen_mhtb(self):
|
||||
if self.ran_from_mhtb_link:
|
||||
return
|
||||
|
||||
self.ran_from_mhtb_link = True
|
||||
|
||||
mhtb_link = ''
|
||||
|
||||
for arg in sys.argv:
|
||||
if 'misterhorsepm://' in arg:
|
||||
mhtb_link = arg
|
||||
break
|
||||
|
||||
if mhtb_link == '':
|
||||
return
|
||||
|
||||
mhtb_dir = get_mhtb_install_dir()
|
||||
if mhtb_dir is None:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
gls('mhtb_not_found_title'),
|
||||
gls('mhtb_not_found_text')
|
||||
)
|
||||
exit(0)
|
||||
|
||||
self.run_mhtb_thread = RunExeThread([f'{mhtb_dir.as_posix()}/ProductManager.exe', mhtb_link])
|
||||
self.run_mhtb_thread.log_signal.connect(self._log)
|
||||
self.run_mhtb_thread.finished_signal.connect(self._finished)
|
||||
|
||||
self.run_mhtb_thread.start()
|
||||
|
||||
def try_autoopen_aep(self):
|
||||
self.run_ae_thread.clear_aep_file_arg()
|
||||
if self.ran_from_aep_file:
|
||||
return
|
||||
|
||||
self.ran_from_aep_file = True
|
||||
|
||||
aep_file = ''
|
||||
|
||||
for arg in sys.argv:
|
||||
if '.aep' in arg:
|
||||
aep_file = arg
|
||||
break
|
||||
|
||||
if aep_file == '':
|
||||
return
|
||||
|
||||
self.run_ae_thread.add_aep_file_arg(aep_file)
|
||||
self.run_ae_button_clicked()
|
||||
|
||||
def init_installation(self):
|
||||
if check_aegnux_installed():
|
||||
@@ -73,7 +137,10 @@ class MainWindow(MainWindowUI):
|
||||
self.runMenu.setEnabled(True)
|
||||
self.browseMenu.setEnabled(True)
|
||||
self.kill_action.setEnabled(True)
|
||||
self.plugininst_action.setEnabled(True)
|
||||
self.term_action.setEnabled(True)
|
||||
self.try_autoopen_aep()
|
||||
self.try_autoopen_mhtb()
|
||||
|
||||
else:
|
||||
self.install_button.show()
|
||||
@@ -84,11 +151,13 @@ class MainWindow(MainWindowUI):
|
||||
self.browseMenu.setEnabled(False)
|
||||
self.kill_action.setEnabled(False)
|
||||
self.term_action.setEnabled(False)
|
||||
self.plugininst_action.setEnabled(False)
|
||||
|
||||
def _construct_menubar(self):
|
||||
self.runMenu = self.menuBar().addMenu(gls('run_menu'))
|
||||
self.ae_action = self.runMenu.addAction(gls('ae_action'))
|
||||
self.exe_action = self.runMenu.addAction(gls('exe_action'))
|
||||
self.plugininst_action = self.runMenu.addAction(gls('plugininst_action'))
|
||||
self.reg_action = self.runMenu.addAction(gls('reg_action'))
|
||||
|
||||
self.browseMenu = self.menuBar().addMenu(gls('browse_menu'))
|
||||
@@ -96,6 +165,7 @@ class MainWindow(MainWindowUI):
|
||||
self.plugind_action = self.browseMenu.addAction(gls('plugind_action'))
|
||||
self.aed_action = self.browseMenu.addAction(gls('aed_action'))
|
||||
self.aeg_action = self.browseMenu.addAction(gls('aeg_action'))
|
||||
self.cep_action = self.browseMenu.addAction(gls('cep_action'))
|
||||
|
||||
self.debugMenu = self.menuBar().addMenu(gls('debug_menu'))
|
||||
self.kill_action = self.debugMenu.addAction(gls('kill_action'))
|
||||
@@ -106,6 +176,8 @@ class MainWindow(MainWindowUI):
|
||||
self.install_button.setEnabled(not lock)
|
||||
self.run_button.setEnabled(not lock)
|
||||
self.remove_aegnux_button.setEnabled(not lock)
|
||||
|
||||
self.runMenu.setEnabled(not lock)
|
||||
|
||||
@Slot()
|
||||
def toggle_logs(self):
|
||||
@@ -116,10 +188,21 @@ class MainWindow(MainWindowUI):
|
||||
|
||||
@Slot(bool)
|
||||
def _finished(self, success: bool):
|
||||
if self.quit_after_handling_args:
|
||||
exit(0)
|
||||
|
||||
self.lock_ui(False)
|
||||
self.progress_bar.hide()
|
||||
self.init_installation()
|
||||
|
||||
if not success:
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
gls('error'),
|
||||
self.logs_edit.toPlainText().split('\n')[-2]
|
||||
)
|
||||
return
|
||||
|
||||
if check_aegnux_installed() and not check_aegnux_tip_marked():
|
||||
QMessageBox.information(self, '', gls('tip_alt_t'))
|
||||
mark_aegnux_tip_as_shown()
|
||||
@@ -141,6 +224,12 @@ class MainWindow(MainWindowUI):
|
||||
self.install_thread.set_download_method(method)
|
||||
|
||||
if method == DownloadMethod.OFFLINE:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
gls('offline_note'),
|
||||
gls('offline_note_text')
|
||||
)
|
||||
|
||||
filename, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
gls('offline_ae_zip_title'),
|
||||
@@ -156,6 +245,29 @@ class MainWindow(MainWindowUI):
|
||||
self.progress_bar.show()
|
||||
self.install_thread.start()
|
||||
|
||||
@Slot()
|
||||
def install_plugins_button_clicked(self):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
gls('plugin_note'),
|
||||
gls('plugin_note_text')
|
||||
)
|
||||
|
||||
filename, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
gls('offline_ae_zip_title'),
|
||||
"",
|
||||
"Zip Files (*.zip);;All Files (*)"
|
||||
)
|
||||
if filename == '':
|
||||
return
|
||||
|
||||
self.plugin_thread.set_plugin_zip_filename(filename)
|
||||
|
||||
self.lock_ui()
|
||||
self.progress_bar.show()
|
||||
self.plugin_thread.start()
|
||||
|
||||
@Slot()
|
||||
def run_ae_button_clicked(self):
|
||||
self.lock_ui()
|
||||
@@ -222,16 +334,21 @@ class MainWindow(MainWindowUI):
|
||||
def aegnux_folder_clicked(self):
|
||||
os.system(f'xdg-open "{get_aegnux_installation_dir()}"')
|
||||
|
||||
@Slot()
|
||||
def cep_folder_clicked(self):
|
||||
os.system(f'xdg-open "{get_cep_dir()}"')
|
||||
|
||||
@Slot()
|
||||
def run_command_alt_t(self):
|
||||
env = os.environ.copy()
|
||||
env['WINEPREFIX'] = get_wineprefix_dir()
|
||||
env['PATH'] = get_wine_bin_path_env('/usr/bin')
|
||||
|
||||
process = subprocess.Popen(
|
||||
['./bin/kitty/bin/kitty', 'bash'],
|
||||
env=env
|
||||
)
|
||||
try:
|
||||
terminal = get_default_terminal()
|
||||
subprocess.Popen([terminal, "bash"], env=env)
|
||||
except RuntimeError as e:
|
||||
print("[CRITICAL ERROR]:", e)
|
||||
|
||||
@Slot()
|
||||
def run_command_ctrl_q(self):
|
||||
|
||||
162
src/pluginthread.py
Normal file
162
src/pluginthread.py
Normal file
@@ -0,0 +1,162 @@
|
||||
import os
|
||||
import shutil
|
||||
from src.processthread import ProcessThread
|
||||
from src.utils import get_private_plugins_unpack_path, get_ae_plugins_dir, get_wineprefix_dir
|
||||
|
||||
|
||||
class PluginThread(ProcessThread):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def set_plugin_zip_filename(self, filename: str):
|
||||
self.plugin_zip_filename = filename
|
||||
|
||||
def run(self):
|
||||
self.log_signal.emit('[DEBUG] Unpacking plugins from the archive...')
|
||||
self.progress_signal.emit(5)
|
||||
self.remove_ppu_dir()
|
||||
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
self.unpack_zip(self.plugin_zip_filename, ppu_dir.as_posix())
|
||||
self.progress_signal.emit(15)
|
||||
|
||||
self.install_aex_plugins()
|
||||
self.install_cep_extensions()
|
||||
self.install_presets()
|
||||
self.run_installers()
|
||||
|
||||
self.remove_ppu_dir()
|
||||
self.progress_signal.emit(95)
|
||||
self.log_signal.emit('[INFO] The plugins have been installed')
|
||||
|
||||
self.finished_signal.emit(True)
|
||||
self.progress_signal.emit(100)
|
||||
|
||||
def install_aex_plugins(self):
|
||||
self.log_signal.emit('[DEBUG] Installing aex plugins...')
|
||||
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
aex_src = ppu_dir.joinpath('aex')
|
||||
|
||||
for item in os.listdir(aex_src.as_posix()):
|
||||
if self._is_cancelled:
|
||||
self.cancelled.emit()
|
||||
return
|
||||
src_path = aex_src.joinpath(item)
|
||||
dst_path = get_ae_plugins_dir().joinpath(item)
|
||||
|
||||
if os.path.isdir(src_path):
|
||||
shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
|
||||
else:
|
||||
shutil.copy2(src_path, dst_path)
|
||||
|
||||
self.log_signal.emit('[INFO] AEX plugins installed')
|
||||
self.progress_signal.emit(30)
|
||||
|
||||
def install_presets(self):
|
||||
self.log_signal.emit('[DEBUG] Installing presets...')
|
||||
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
preset_src = ppu_dir.joinpath('preset-backup')
|
||||
preset_dest = get_wineprefix_dir().joinpath('drive_c/users/relative/Documents/Adobe/After Effects 2024/User Presets')
|
||||
|
||||
os.makedirs(preset_dest, exist_ok=True)
|
||||
for item in os.listdir(preset_src):
|
||||
if self._is_cancelled:
|
||||
self.cancelled.emit()
|
||||
return
|
||||
src_path = preset_src.joinpath(item)
|
||||
dst_path = preset_dest.joinpath(item)
|
||||
if os.path.isdir(src_path):
|
||||
shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
|
||||
else:
|
||||
shutil.copy2(src_path, dst_path)
|
||||
|
||||
self.log_signal.emit("[INFO] Presets installed")
|
||||
self.progress_signal.emit(55)
|
||||
|
||||
def run_installers(self):
|
||||
self.log_signal.emit('[DEBUG] Running installers...')
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
install_src = ppu_dir.joinpath('installer')
|
||||
|
||||
installers_counter = 0
|
||||
|
||||
for exe in os.listdir(install_src.as_posix()):
|
||||
if exe.endswith('.exe') and exe not in ['E3D.exe', 'saber.exe']:
|
||||
self.progress_signal.emit(55 + installers_counter * 3)
|
||||
|
||||
self.log_signal.emit(f"[INFO] Installing: {exe}")
|
||||
self.run_command(
|
||||
['wine', exe, '/verysilent', '/suppressmsgboxes'],
|
||||
install_src.as_posix(), True
|
||||
)
|
||||
|
||||
self.progress_signal.emit(70)
|
||||
|
||||
# Special handling for E3D and saber
|
||||
for exe in ['E3D.exe', 'saber.exe']:
|
||||
self.log_signal.emit(f"[INFO] Please manually install: {exe}")
|
||||
self.run_command(
|
||||
['wine', exe],
|
||||
install_src.as_posix(), True
|
||||
)
|
||||
|
||||
self.progress_signal.emit(85)
|
||||
self.copy_element_files()
|
||||
|
||||
def copy_element_files(self):
|
||||
self.log_signal.emit('[DEBUG] Copying Element files...')
|
||||
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
install_src = ppu_dir.joinpath('installer')
|
||||
|
||||
video_copilot_dir = get_ae_plugins_dir().joinpath("VideoCopilot")
|
||||
element_files = [
|
||||
("Element.aex", "Element.aex"),
|
||||
("Element.license", "Element.license")
|
||||
]
|
||||
|
||||
for src_name, dst_name in element_files:
|
||||
src_path = install_src.joinpath(src_name)
|
||||
if os.path.exists(src_path):
|
||||
shutil.copy2(src_path, os.path.join(video_copilot_dir, dst_name))
|
||||
self.log_signal.emit(f"[INFO] {src_name} copied successfully")
|
||||
|
||||
self.log_signal.emit("[INFO] Element installed")
|
||||
self.progress_signal.emit(90)
|
||||
|
||||
def install_cep_extensions(self):
|
||||
self.log_signal.emit('[DEBUG] Installing CEP extensions...')
|
||||
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
cep_dir = ppu_dir.joinpath('CEP')
|
||||
|
||||
cep_reg_file = cep_dir.joinpath("AddKeys.reg")
|
||||
self.run_command(['wine', "regedit", cep_reg_file.as_posix()], in_prefix=True)
|
||||
|
||||
self.install_flow()
|
||||
|
||||
self.log_signal.emit("[INFO] CEP extensions installed")
|
||||
self.progress_signal.emit(45)
|
||||
|
||||
def install_flow(self):
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
cep_dir = ppu_dir.joinpath('CEP')
|
||||
|
||||
self.log_signal.emit('[DEBUG] Installing Flow...')
|
||||
|
||||
flow_src = cep_dir.joinpath("flowv1.4.2")
|
||||
cep_dst = get_wineprefix_dir().joinpath("drive_c/Program Files (x86)/Common Files/Adobe/CEP/extensions")
|
||||
|
||||
os.makedirs(cep_dst, exist_ok=True)
|
||||
shutil.copytree(flow_src, os.path.join(cep_dst, "flowv1.4.2"), dirs_exist_ok=True)
|
||||
self.log_signal.emit("[INFO] Flow installed")
|
||||
|
||||
def remove_ppu_dir(self):
|
||||
ppu_dir = get_private_plugins_unpack_path()
|
||||
try:
|
||||
shutil.rmtree(ppu_dir.as_posix())
|
||||
self.log_signal.emit('[DEBUG] Temporary folder removed successfully.')
|
||||
except OSError as e:
|
||||
self.log_signal.emit(f'[WARNING] Failed to remove temporary folder {ppu_dir}: {e}')
|
||||
@@ -6,5 +6,13 @@ class RunAEThread(RunExeThread):
|
||||
def __init__(self):
|
||||
super().__init__(['AfterFX.exe'])
|
||||
|
||||
def add_aep_file_arg(self, aep_file: str):
|
||||
self.exe_args.append('Z:' + aep_file)
|
||||
|
||||
def clear_aep_file_arg(self):
|
||||
for arg in self.exe_args:
|
||||
if '.aep' in arg:
|
||||
self.exe_args.remove(arg)
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
75
src/utils.py
75
src/utils.py
@@ -91,6 +91,15 @@ def get_wineprefix_dir():
|
||||
|
||||
return wineprefix_dir
|
||||
|
||||
def get_cep_dir():
|
||||
wineprefix_dir = get_wineprefix_dir()
|
||||
cep_dir = wineprefix_dir.joinpath('drive_c/Program Files (x86)/Common Files/Adobe/CEP')
|
||||
|
||||
if not os.path.exists(cep_dir):
|
||||
os.makedirs(cep_dir)
|
||||
|
||||
return cep_dir
|
||||
|
||||
def get_wine_runner_dir():
|
||||
aegnux_dir = get_aegnux_installation_dir()
|
||||
runner_dir = aegnux_dir.joinpath('runner')
|
||||
@@ -139,7 +148,61 @@ def get_wine_bin_path_env(old_path: str | None):
|
||||
old_path = old_path if old_path is not None else os.getenv('PATH')
|
||||
return f'{get_wine_runner_dir().as_posix()}/bin:{old_path}'
|
||||
|
||||
def get_mhtb_install_dir():
|
||||
wineprefix_dir = get_wineprefix_dir()
|
||||
mhtb_dir = wineprefix_dir.joinpath('drive_c/Program Files/Mister Horse Product Manager')
|
||||
|
||||
if not os.path.exists(mhtb_dir):
|
||||
return None
|
||||
|
||||
return mhtb_dir
|
||||
|
||||
def get_default_terminal() -> str:
|
||||
DEFAULT_ENVS = ["TERMINAL", "TERM_PROGRAM"]
|
||||
TERMINALS = [
|
||||
"konsole",
|
||||
"kitty",
|
||||
"alacritty",
|
||||
"gnome-terminal",
|
||||
"xfce4-terminal",
|
||||
"terminator",
|
||||
"lxterminal",
|
||||
"tilix",
|
||||
"st",
|
||||
"mate-terminal",
|
||||
"xterm",
|
||||
"urxvt",
|
||||
"deepin-terminal",
|
||||
"sakura",
|
||||
"tilda",
|
||||
"guake",
|
||||
"hyper",
|
||||
"eterm",
|
||||
"rxvt",
|
||||
"lxterm",
|
||||
"cosmic-terminal",
|
||||
]
|
||||
|
||||
candidates = (
|
||||
shutil.which(os.environ.get(var)) for var in DEFAULT_ENVS if os.environ.get(var)
|
||||
)
|
||||
|
||||
# Debian/Ubuntu
|
||||
alt = "/etc/alternatives/x-terminal-emulator"
|
||||
if os.path.exists(alt):
|
||||
candidates = (shutil.which(os.readlink(alt)), *candidates)
|
||||
|
||||
# Popular terminals
|
||||
candidates = (*candidates, *(shutil.which(term) for term in TERMINALS))
|
||||
|
||||
terminal = next((t for t in candidates if t), None)
|
||||
if terminal:
|
||||
return terminal
|
||||
|
||||
raise RuntimeError(
|
||||
"Your terminal was not found or is not supported.\n"
|
||||
"Install one of the following: " + ", ".join(TERMINALS)
|
||||
)
|
||||
|
||||
def get_aegnux_tip_marked_flag_path():
|
||||
hades = get_aegnux_installation_dir()
|
||||
@@ -150,4 +213,14 @@ def check_aegnux_tip_marked():
|
||||
|
||||
def mark_aegnux_tip_as_shown():
|
||||
with open(get_aegnux_tip_marked_flag_path(), 'w') as f:
|
||||
f.write('Press ALT+T to open up a terminal')
|
||||
f.write('Press ALT+T to open up a terminal')
|
||||
|
||||
|
||||
def get_private_plugins_unpack_path():
|
||||
aegnux_dir = get_aegnux_installation_dir()
|
||||
ppu_dir = aegnux_dir.joinpath('private-plugins')
|
||||
|
||||
if not os.path.exists(ppu_dir):
|
||||
os.makedirs(ppu_dir)
|
||||
|
||||
return ppu_dir
|
||||
@@ -28,5 +28,17 @@ STRINGS = {
|
||||
'debug_menu': 'Debug',
|
||||
'kill_action': 'Kill AE',
|
||||
'log_action': 'Toggle log',
|
||||
'term_action': 'Open Terminal'
|
||||
'term_action': 'Open Terminal',
|
||||
'cep_action': 'CEP directory',
|
||||
'offline_note': 'Offline note',
|
||||
'offline_note_text': 'Note that the .zip archive should contain After Effects from Program Files (including AfterFX.exe).',
|
||||
'error': 'Error',
|
||||
'plugininst_action': 'Install private plugins from ZIP',
|
||||
'plugin_note': 'Private plugins note',
|
||||
'plugin_note_text': 'Those plugins are available at https://t.me/Aegnux',
|
||||
'done_title': 'Done!',
|
||||
'done_ae': 'AE has been installed.',
|
||||
'done_plugins': 'The plugins have been installed.',
|
||||
'mhtb_not_found_title': 'Mister Horse Product Manager Not Found',
|
||||
'mhtb_not_found_text': 'Mister Horse Product Manager is not installed in the Wine prefix. Please install it first.'
|
||||
}
|
||||
@@ -28,5 +28,16 @@ STRINGS = {
|
||||
'debug_menu': 'Отладка',
|
||||
'kill_action': 'Остановить AE',
|
||||
'log_action': 'Вкл/выкл логи',
|
||||
'term_action': 'Открыть терминал'
|
||||
'term_action': 'Открыть терминал',
|
||||
'cep_action': 'Папка с CEP',
|
||||
'offline_note': 'Внимание, оффлайн',
|
||||
'offline_note_text': 'Учтите, что .zip-архив должен содержать After Effects из Program Files (включая AfterFX.exe).',
|
||||
'error': 'Ошибка',
|
||||
'plugininst_action': 'Установить приватные плагины из ZIP',
|
||||
'plugin_note': 'Замечание к приватным плагинам',
|
||||
'plugin_note_text': 'Эти плагины доступны здесь: https://t.me/Aegnux',
|
||||
'done_ae': 'AE был установлен.',
|
||||
'done_plugins': 'Плагины были установлены.',
|
||||
'mhtb_not_found_title': 'Mister Horse Product Manager не найден',
|
||||
'mhtb_not_found_text': 'Mister Horse Product Manager не установлен в префиксе Wine. Пожалуйста, сначала установите его.'
|
||||
}
|
||||
43
translations/uk_UA.py
Normal file
43
translations/uk_UA.py
Normal file
@@ -0,0 +1,43 @@
|
||||
STRINGS = {
|
||||
"welcome_win_title": "Aegnux — Ласкаво просимо",
|
||||
"welcome_to_aegnux": "Ласкаво просимо до Aegnux!",
|
||||
"subtitle_text": "Простіший спосіб змусити працювати After Effects на GNU/Linux",
|
||||
"install": "Встановити",
|
||||
"footer_text": "Зроблено з любовʼю від Relative",
|
||||
"installation_method_title": "Метод встановлення",
|
||||
"installation_method_text": "Як ви хочете встановити Aegnux?",
|
||||
"offline_ae_zip_title": "Виберіть ZIP-архів AE",
|
||||
"run_ae": "Запустити AE",
|
||||
"kill_ae": "Зупинити AE",
|
||||
"toggle_logs": "Увімк/вимк логування",
|
||||
"remove_aegnux": "Видалити Aegnux",
|
||||
"plugins": "Плагіни",
|
||||
"wineprefix": "Префікс Wine",
|
||||
"tip_alt_t": "Порада: натисніть ALT+T, щоб відкрити термінал із середовищем Wine та вже заданим префіксом.",
|
||||
"confirm_exit": "Підтвердження виходу",
|
||||
"confirm_exit_text": "Ви справді хочете вийти з Aegnux?",
|
||||
"run_menu": "Запустити",
|
||||
"ae_action": "After Effects",
|
||||
"exe_action": "Інший файл .EXE",
|
||||
"reg_action": "Імпортувати файл реєстру",
|
||||
"browse_menu": "Огляд",
|
||||
"wpd_action": "Префікс Wine",
|
||||
"plugind_action": "Папка з плагінами",
|
||||
"aed_action": "Папка з AE",
|
||||
"aeg_action": "Папка з інсталяцією Aegnux",
|
||||
"debug_menu": "Відлагодження",
|
||||
"kill_action": "Зупинити AE",
|
||||
"log_action": "Увімк/вимк логування",
|
||||
"term_action": "Відкрити термінал",
|
||||
"cep_action": "Папка з CEP",
|
||||
'offline_note': 'Увага, офлайн',
|
||||
'offline_note_text': 'Зауважте, що .zip-архів повинен містити After Effects з Program Files (включаючи AfterFX.exe).',
|
||||
'error': 'Помилка',
|
||||
'plugininst_action': 'Встановити приватні плагіни з ZIP',
|
||||
'plugin_note': 'Примітка до приватних плагінів',
|
||||
'plugin_note_text': 'Ці плагіни доступні тут: https://t.me/Aegnux',
|
||||
'done_ae': 'AE було встановлено.',
|
||||
'done_plugins': 'Плагіни було встановлено.',
|
||||
'mhtb_not_found_title': 'Mister Horse Product Manager не знайдено',
|
||||
'mhtb_not_found_text': 'Mister Horse Product Manager не встановлено в префіксі Wine. Будь ласка, спочатку встановіть його.'
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class MainWindowUI(QMainWindow):
|
||||
super().__init__()
|
||||
self._construct_ui()
|
||||
self._set_styles()
|
||||
self.setMinimumSize(480, 600)
|
||||
self.setMinimumSize(500, 600)
|
||||
|
||||
def _set_styles(self):
|
||||
with open(f'{STYLES_PATH}/mainwindow.css') as fp:
|
||||
@@ -33,8 +33,21 @@ class MainWindowUI(QMainWindow):
|
||||
def _construct_ui(self):
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
layout_container = QWidget()
|
||||
layout_container.setMinimumWidth(500)
|
||||
|
||||
self.root_layout = QVBoxLayout(central_widget)
|
||||
self.root_layout = QVBoxLayout(layout_container)
|
||||
|
||||
main_layout = QHBoxLayout(central_widget)
|
||||
|
||||
main_layout.addItem(
|
||||
QSpacerItem(1, 1, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
)
|
||||
main_layout.addWidget(layout_container)
|
||||
main_layout.addItem(
|
||||
QSpacerItem(1, 1, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
)
|
||||
|
||||
self.add_expanding_vertical_sizer()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user