diff --git a/modules/downloader.py b/modules/downloader.py index d21e050..f489945 100755 --- a/modules/downloader.py +++ b/modules/downloader.py @@ -16,9 +16,9 @@ def try_download(url, save_path=None): response = requests.get(url, stream=True) response.raise_for_status() # Проверяем, что запрос успешен - # Определяем имя файла, если save_path не указан + # Определяем имя файла, если save_path не указан, или путь это директория if save_path is None: - save_path = os.path.basename(url) + save_path = tmp_path + "/" + os.path.basename(url) elif os.path.isdir(save_path): save_path = save_path + "/" + os.path.basename(url) @@ -39,9 +39,12 @@ def try_download(url, save_path=None): progress_bar.update(len(chunk)) # Обновляем прогресс-бар log.info(f"Файл успешно скачан и сохранён как {save_path}.") + return True except requests.exceptions.RequestException as e: log.error(f"Ошибка при скачивании файла: {e}") except Exception as e: log.error(f"Неизвестная ошибка: {e}") except KeyboardInterrupt as e: log.error(f"Прервано пользователем: {e}") + + return False diff --git a/modules/files_worker.py b/modules/files_worker.py index 0cb6b5d..276f847 100755 --- a/modules/files_worker.py +++ b/modules/files_worker.py @@ -3,9 +3,24 @@ import shutil import filecmp import tarfile import hashlib +import tempfile +from .env_var import * +from .config_parser import * from .log import * +# константы: +tmp_path = tempfile.gettempdir() + "/portproton" +work_path = get_env_var("USER_WORK_PATH") +data_path = work_path + "/data" +dist_path = data_path + "/dist" +img_path = data_path + "/img" +vulkan_path = data_path + "/vulkan" +plugins_path = data_path + "/plugins_v" + var("plugins_ver") +libs_path = data_path + "/libs_v" + var("libs_ver") + +log.info(f"рабочий каталог: {work_path}") + def try_copy_file(source, destination): # функция копирования если файлы различаются if not os.path.exists(source): raise FileNotFoundError (f"file not found for copy: {source}") @@ -62,6 +77,21 @@ def try_force_link_dir(path, link): except Exception as e: log.error(f"failed to create link for file: {e}") +def try_move_dir(src, dst): # Перемещает каталог src в dst, заменяя существующие файлы. + for src_dir, dirs, files in os.walk(src): + dst_dir = src_dir.replace(src, dst, 1) + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + for file_ in files: + src_file = os.path.join(src_dir, file_) + dst_file = os.path.join(dst_dir, file_) + if os.path.exists(dst_file): + if os.path.samefile(src_file, dst_file): + continue + os.remove(dst_file) + shutil.move(src_file, dst_dir) + try_remove_dir(src) + def replace_file(file_path, file_name): # функция замены файла (сначала запись во временный файл, потом замена) try: if os.path.exists(file_name) and os.path.getsize(file_name) > 0: @@ -98,8 +128,6 @@ def get_last_modified_time(file_path, fallback=None): # Добавьте fallba log.error(f"Ошибка при получении времени изменения файла {file_path}: {e}") return fallback # Возврат значения по умолчанию - - def unpack(archive_path, extract_to=None): # Проверяем, существует ли архив if not os.path.isfile(archive_path): @@ -107,15 +135,15 @@ def unpack(archive_path, extract_to=None): return False try: if extract_to is None: - # TODO: перенести распаковку по умолчанию в tmp - extract_to = os.path.dirname(archive_path) + extract_to = tmp_path + "/" + os.path.dirname(archive_path) elif not os.path.isdir(extract_to): create_new_dir(extract_to) - + log.info(f"unpacking file: {archive_path}") with tarfile.open(archive_path, mode="r:*") as tar: tar.extractall(path=extract_to) full_path = os.path.realpath(extract_to) log.info(f"Архив {archive_path} успешно распакован в {full_path}") + except tarfile.TarError as e: log.error(f"Ошибка при распаковке архива {archive_path}: {e}") return False @@ -129,9 +157,7 @@ def unpack(archive_path, extract_to=None): return True def check_hash_sum(check_file, check_sum): - if check_sum and isinstance(check_sum, str): - true_hash = check_sum - elif os.path.isfile(check_sum): + if os.path.isfile(check_sum): try: with open(check_sum, "r", encoding="utf-8") as file: first_line = file.readline().strip() @@ -144,6 +170,8 @@ def check_hash_sum(check_file, check_sum): log.error(f"Файл {check_sum} не найден.") except Exception as e: log.error(f"Ошибка при чтении файла: {e}") + elif check_sum and isinstance(check_sum, str): + true_hash = check_sum else: log.error(f"Verification sha256sum was failed: {check_file}") diff --git a/modules/init_wine.py b/modules/init_wine.py index d4e5671..79e7f05 100644 --- a/modules/init_wine.py +++ b/modules/init_wine.py @@ -6,24 +6,38 @@ from .log import * from .env_var import * from .files_worker import * from .config_parser import * +from .downloader import * -def init_wine(dist_path): - used_wine_upper = var("used_wine").upper() +def init_wine(wine_version=None): + if wine_version is None: + used_wine = var("used_wine") + else: + used_wine = wine_version # TODO: будем переименовывать все каталоги wine в верхний регистр? - if used_wine_upper != "SYSTEM": - if used_wine_upper == "WINE_LG": + if used_wine.upper() != "SYSTEM": + if used_wine.upper() == "WINE_LG": used_wine = var("default_wine") - elif used_wine_upper == "PROTON_LG": + elif used_wine.upper() == "PROTON_LG": used_wine = var("default_proton") wine_path = dist_path + "/" + used_wine if not os.path.exists(wine_path + "/bin/wine"): - # TODO: если нет wine то качаем и распаковываем log.warning(f"{used_wine} not found. Try download...") + # TODO: использовать зеркало при необходимости + # url_github_lg="https://github.com/Castro-Fidel/wine_builds/releases/download/" + used_wine + "/" + used_wine + + url_cloud_lg="https://cloud.linux-gaming.ru/portproton/" + used_wine + + if try_download(url_cloud_lg + ".tar.xz") and try_download(url_cloud_lg + ".sha256sum"): + if check_hash_sum(tmp_path + "/" + used_wine + ".tar.xz", tmp_path + "/" + used_wine + ".sha256sum"): + unpack(tmp_path + "/" + used_wine + ".tar.xz", dist_path) + for f in [".tar.xz", ".sha256sum"]: + try_remove_file(tmp_path + "/" + used_wine + f) + if not os.path.exists(wine_path + "/bin/wine"): log.critical(f"{used_wine} not found. Interrupt!") @@ -41,12 +55,15 @@ def init_wine(dist_path): set_env_var_force("MEDIACONV_BLANK_VIDEO_FILE", wine_share + "/media/blank.mkv") set_env_var_force("MEDIACONV_BLANK_AUDIO_FILE", wine_share + "/media/blank.ptna") - # TODO: mono, gecko + for wine_utils in ["mono", "gecko"]: + if not os.path.islink(wine_share + "/wine/" + wine_utils): + if os.path.isdir(wine_share + "/wine/" + wine_utils): + try_move_dir(wine_share + "/wine/" + wine_utils, data_path + "/" + wine_utils) + try_force_link_dir(data_path + "/" + wine_utils, wine_share + "/wine/" + wine_utils) else: try: - result = subprocess.run(['wine', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - text=True) + result = subprocess.run(['wine', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) if result.returncode == 0: log.info(f"Используется системная версия WINE: {result.stdout.strip()}") wine_path = "/usr" diff --git a/modules/source_fetcher.py b/modules/source_fetcher.py index 5cfd930..a203562 100644 --- a/modules/source_fetcher.py +++ b/modules/source_fetcher.py @@ -6,7 +6,6 @@ from modules.files_worker import * count_wines = 25 # Количество элементов для записи в .tmp файл - repos = { # Список репозиториев для обработки и их короткие имена "GloriousEggroll/proton-ge-custom": "proton-ge-custom", "GloriousEggroll/wine-ge-custom": "wine-ge-custom", @@ -19,13 +18,13 @@ mirrors = { "https://cloud.linux-gaming.ru": "LG_mirror" } -def source_list_checker(tmp_path): +def source_list_checker(): for repo, short_name in repos.items(): output_file = os.path.join(tmp_path, f"{short_name}.tmp") if not os.path.exists(output_file) or os.path.getsize(output_file) == 0 or \ (time.time() - get_last_modified_time(output_file, fallback=0)) >= 10800: - source_list_downloader(repo, tmp_path, short_name, output_file) + source_list_downloader(repo, short_name, output_file) else: log.info(f"Файл {output_file} существует и был обновлён менее 3 часов назад. Используем кэшированные данные.") @@ -34,11 +33,11 @@ def source_list_checker(tmp_path): if not os.path.exists(output_file) or os.path.getsize(output_file) == 0 or \ (time.time() - get_last_modified_time(output_file, fallback=0)) >= 10800: - source_list_downloader(mirror_url, tmp_path, short_name, output_file, use_github=False) + source_list_downloader(mirror_url, short_name, output_file, use_github=False) else: log.info(f"Файл {output_file} существует и был обновлён менее 3 часов назад. Используем кэшированные данные.") -def source_list_downloader(source, tmp_path, short_name, output_file, use_github=True): +def source_list_downloader(source, short_name, output_file, use_github=True): if use_github: url = f"https://api.github.com/repos/{source}/releases?per_page={count_wines}" else: @@ -87,9 +86,9 @@ def source_list_downloader(source, tmp_path, short_name, output_file, use_github except requests.exceptions.RequestException as e: log.error(f"Ошибка при получении данных из {source}: {str(e)}") -def get_sources(args, tmp_path, dist_path, mirror): +def get_sources(args, mirror): os.makedirs(tmp_path, exist_ok=True) - source_list_checker(tmp_path) + source_list_checker() if not args: log.critical("Аргументы не предоставлены. Завершение работы.") diff --git a/portproton.py b/portproton.py index 0781378..8290535 100755 --- a/portproton.py +++ b/portproton.py @@ -1,43 +1,25 @@ #!/usr/bin/env python3 import sys -import tempfile -from modules.config_parser import * from modules.log import * -from modules.env_var import * from modules.files_worker import * from modules.downloader import * from modules.init_wine import * from modules.source_fetcher import * -tmp_path = tempfile.gettempdir() + "/portproton" +mirror = True # перенести в функцию как (arg, mirror=True) -mirror = True - -work_path = get_env_var("USER_WORK_PATH") - -data_path = work_path + "/data" -dist_path = data_path + "/dist" -img_path = data_path + "/img" - -vulkan_path = data_path + "/vulkan" -plugins_path = data_path + "/plugins_v" + var("plugins_ver") -libs_path = data_path + "/libs_v" + var("libs_ver") - -create_new_dir(dist_path, tmp_path, img_path, vulkan_path) - -log.info(f"рабочий каталог: {work_path}") log.info(f"принятые аргументы: {sys.argv[1:]}") if __name__ == "__main__": - # TODO: реализовать все функции get_* в модуль downloader.py: + create_new_dir(dist_path, tmp_path, img_path, vulkan_path) if len(sys.argv) > 1: # Проверяем, что есть хотя бы один аргумент (кроме имени скрипта) match sys.argv[1]: # Игнорируем первый аргумент (имя скрипта) case "--get-wine": # без аргументов сохраняем список доступных в tmp_path/get_wine.tmp и выводим в терминал # если есть аргумент (например WINE_LG_10-1) то обновляем и парсим tmp_path/get_wine.tmp с последующим скачиванием - get_sources(sys.argv[2:], tmp_path, dist_path, mirror) + get_sources(sys.argv[2:], mirror) case "--get-dxvk": # без аргументов сохраняем список доступных в tmp_path/get_dxvk.tmp и выводим в терминал # если есть аргумент (например 2.5.3-31) то обновляем и парсим tmp_path/get_dxvk.tmp с последующим скачиванием @@ -53,4 +35,4 @@ if __name__ == "__main__": # версия контейнера будет захардкожена, парсить ничего не надо get_libs(libs_ver) - init_wine(dist_path) + init_wine("WINE_LG")