diff --git a/scripts/gdm.py b/scripts/gdm.py
index 5a573a4..a5c23cf 100644
--- a/scripts/gdm.py
+++ b/scripts/gdm.py
@@ -6,9 +6,10 @@ from .theme import Theme
from .utils import remove_properties, remove_keywords
from . import config
from .utils.alternatives_updater import AlternativesUpdater
-from .utils.console import Console, Color, Format
+from scripts.utils.logger.console import Console, Color, Format
from .utils.files_labeler import FilesLabeler
-from .utils.gresource import Gresource, GresourceBackupNotFoundError
+from .utils.gresource import GresourceBackupNotFoundError
+from .utils.gresource.gresource import Gresource
class GlobalTheme:
@@ -40,7 +41,7 @@ class GlobalTheme:
self.__gresource_file = os.path.join(self.destination_folder, self.destination_file)
self.__gresource_temp_folder = os.path.join(self.temp_folder, config.extracted_gdm_folder)
- self.__gresource = Gresource(self.destination_file, self.__gresource_temp_folder, self.destination_folder)
+ self.__gresource = Gresource(self.destination_file, self.__gresource_temp_folder, self.destination_folder, logger_factory=Console())
def prepare(self):
if self.__is_installed():
diff --git a/scripts/install/global_theme_installer.py b/scripts/install/global_theme_installer.py
index a45a341..5370a73 100644
--- a/scripts/install/global_theme_installer.py
+++ b/scripts/install/global_theme_installer.py
@@ -3,7 +3,7 @@ import os
from scripts import config
from scripts.gdm import GlobalTheme
from scripts.install.theme_installer import ThemeInstaller
-from scripts.utils.console import Console, Color, Format
+from scripts.utils.logger.console import Console, Color, Format
class GlobalThemeInstaller(ThemeInstaller):
diff --git a/scripts/install/local_theme_installer.py b/scripts/install/local_theme_installer.py
index 7648003..266f356 100644
--- a/scripts/install/local_theme_installer.py
+++ b/scripts/install/local_theme_installer.py
@@ -4,7 +4,7 @@ from scripts import config
from scripts.install.theme_installer import ThemeInstaller
from scripts.theme import Theme
from scripts.utils import remove_files
-from scripts.utils.console import Console, Color, Format
+from scripts.utils.logger.console import Console, Color, Format
class LocalThemeInstaller(ThemeInstaller):
diff --git a/scripts/theme.py b/scripts/theme.py
index 05573de..7c3241f 100644
--- a/scripts/theme.py
+++ b/scripts/theme.py
@@ -8,7 +8,7 @@ from .utils import (
copy_files, # copy files from source to destination
destination_return, # copied/modified theme location
generate_file) # combine files from folder to one file
-from .utils.console import Console, Color, Format
+from scripts.utils.logger.console import Console, Color, Format
class Theme:
diff --git a/scripts/utils/alternatives_updater.py b/scripts/utils/alternatives_updater.py
index f5e4ff8..1f9928a 100644
--- a/scripts/utils/alternatives_updater.py
+++ b/scripts/utils/alternatives_updater.py
@@ -2,7 +2,7 @@ import functools
import subprocess
from typing import TypeAlias
-from scripts.utils.console import Console
+from scripts.utils.logger.console import Console
PathString: TypeAlias = str | bytes
diff --git a/scripts/utils/gnome.py b/scripts/utils/gnome.py
index 9448766..83e65cc 100644
--- a/scripts/utils/gnome.py
+++ b/scripts/utils/gnome.py
@@ -2,7 +2,7 @@ import subprocess
import time
from scripts import config
-from scripts.utils.console import Console, Format, Color
+from scripts.utils.logger.console import Console, Format, Color
from scripts.utils.parse_folder import parse_folder
diff --git a/scripts/utils/gresource.py b/scripts/utils/gresource.py
deleted file mode 100644
index 9091c19..0000000
--- a/scripts/utils/gresource.py
+++ /dev/null
@@ -1,171 +0,0 @@
-import os
-import subprocess
-import textwrap
-from pathlib import Path
-
-from scripts.utils.console import Console
-
-
-class GresourceBackupNotFoundError(FileNotFoundError):
- def __init__(self, location: str = None):
- if location:
- super().__init__(f"Gresource backup file not found: {location}")
- else:
- super().__init__("Gresource backup file not found.")
-
-class MissingDependencyError(Exception):
- def __init__(self, dependency: str):
- super().__init__(f"Missing required dependency: {dependency}")
- self.dependency = dependency
-
-
-class Gresource:
- """Handles the extraction and compilation of gresource files for GNOME Shell themes."""
-
- def __init__(self, gresource_file: str, temp_folder: str, destination: str):
- """
- :param gresource_file: The name of the gresource file to be processed.
- :param temp_folder: The temporary folder where resources will be extracted.
- :param destination: The destination folder where the compiled gresource file will be saved.
- """
- self.gresource_file = gresource_file
- self.temp_folder = temp_folder
- self.destination = destination
-
- self._temp_gresource = os.path.join(temp_folder, gresource_file)
- self._destination_gresource = os.path.join(destination, gresource_file)
- self._active_source_gresource = self._destination_gresource
- self._backup_gresource = os.path.join(destination, f"{gresource_file}.backup")
- self._gresource_xml = os.path.join(temp_folder, f"{gresource_file}.xml")
-
- def use_backup_gresource(self):
- if not os.path.exists(self._backup_gresource):
- raise GresourceBackupNotFoundError(self._backup_gresource)
- self._active_source_gresource = self._backup_gresource
-
- def extract(self):
- extract_line = Console.Line()
- extract_line.update("Extracting gresource files...")
-
- resources = self._get_resources_list()
- self._extract_resources(resources)
-
- extract_line.success("Extracted gresource files.")
-
- def _get_resources_list(self):
- resources_list_response = subprocess.run(
- ["gresource", "list", self._active_source_gresource],
- capture_output=True, text=True, check=False
- )
-
- if resources_list_response.stderr:
- raise Exception(f"gresource could not process the theme file: {self._active_source_gresource}")
-
- return resources_list_response.stdout.strip().split("\n")
-
- def _extract_resources(self, resources: list[str]):
- prefix = "/org/gnome/shell/theme/"
- try:
- for resource in resources:
- resource_path = resource.replace(prefix, "")
- output_path = os.path.join(self.temp_folder, resource_path)
- os.makedirs(os.path.dirname(output_path), exist_ok=True)
-
- with open(output_path, 'wb') as f:
- subprocess.run(
- ["gresource", "extract", self._active_source_gresource, resource],
- stdout=f, check=True
- )
- except FileNotFoundError as e:
- if "gresource" in str(e):
- self._raise_gresource_error(e)
- raise
-
- @staticmethod
- def _raise_gresource_error(e: Exception):
- print("Error: 'gresource' command not found.")
- print("Please install the glib2-devel package:")
- print(" - For Fedora/RHEL: sudo dnf install glib2-devel")
- print(" - For Ubuntu/Debian: sudo apt install libglib2.0-dev")
- print(" - For Arch: sudo pacman -S glib2-devel")
- raise MissingDependencyError("glib2-devel") from e
-
- def compile(self):
- compile_line = Console.Line()
- compile_line.update("Compiling gnome-shell theme...")
-
- self._create_gresource_xml()
- self._compile_resources()
-
- compile_line.success("Theme compiled.")
-
- def _create_gresource_xml(self):
- with open(self._gresource_xml, 'w') as gresource_xml:
- gresource_xml.write(self._generate_gresource_xml())
-
- def _generate_gresource_xml(self):
- files_to_include = self._get_files_to_include()
- nl = "\n" # fstring doesn't support newline character
- return textwrap.dedent(f"""
-
-
-
- {nl.join(files_to_include)}
-
-
- """)
-
- def _get_files_to_include(self):
- temp_path = Path(self.temp_folder)
- return [
- f"{file.relative_to(temp_path)}"
- for file in temp_path.glob('**/*')
- if file.is_file()
- ]
-
- def _compile_resources(self):
- try:
- subprocess.run(["glib-compile-resources",
- "--sourcedir", self.temp_folder,
- "--target", self._temp_gresource,
- self._gresource_xml
- ],
- cwd=self.temp_folder, check=True)
- except FileNotFoundError as e:
- if "glib-compile-resources" in str(e):
- self._raise_gresource_error(e)
- raise
-
- def backup(self):
- backup_line = Console.Line()
- backup_line.update("Backing up gresource files...")
-
- subprocess.run(["cp", "-aT",
- self._destination_gresource,
- self._backup_gresource],
- check=True)
-
- backup_line.success("Backed up gresource files.")
-
- def restore(self):
- if not os.path.exists(self._backup_gresource):
- raise GresourceBackupNotFoundError(self._backup_gresource)
-
- subprocess.run(["mv", "-f",
- self._backup_gresource,
- self._destination_gresource],
- check=True)
-
-
- def move(self):
- move_line = Console.Line()
- move_line.update("Moving gresource files...")
-
- subprocess.run(["cp", "-f",
- self._temp_gresource,
- self._destination_gresource],
- check=True)
-
- subprocess.run(["chmod", "644", self._destination_gresource], check=True)
-
- move_line.success("Moved gresource files.")
\ No newline at end of file
diff --git a/scripts/utils/gresource/__init__.py b/scripts/utils/gresource/__init__.py
new file mode 100644
index 0000000..b69e6ab
--- /dev/null
+++ b/scripts/utils/gresource/__init__.py
@@ -0,0 +1,21 @@
+class GresourceBackupNotFoundError(FileNotFoundError):
+ def __init__(self, location: str = None):
+ if location:
+ super().__init__(f"Gresource backup file not found: {location}")
+ else:
+ super().__init__("Gresource backup file not found.")
+
+
+class MissingDependencyError(Exception):
+ def __init__(self, dependency: str):
+ super().__init__(f"Missing required dependency: {dependency}")
+ self.dependency = dependency
+
+
+def raise_gresource_error(tool: str, e: Exception):
+ print(f"Error: '{tool}' command not found.")
+ print("Please install the glib2-devel package:")
+ print(" - For Fedora/RHEL: sudo dnf install glib2-devel")
+ print(" - For Ubuntu/Debian: sudo apt install libglib2.0-dev")
+ print(" - For Arch: sudo pacman -S glib2-devel")
+ raise MissingDependencyError("glib2-devel") from e
diff --git a/scripts/utils/gresource/gresource.py b/scripts/utils/gresource/gresource.py
new file mode 100644
index 0000000..31295bb
--- /dev/null
+++ b/scripts/utils/gresource/gresource.py
@@ -0,0 +1,46 @@
+import os
+
+from scripts.utils.gresource.gresource_backuper import GresourceBackuperManager
+from scripts.utils.gresource.gresource_complier import GresourceCompiler
+from scripts.utils.gresource.gresource_extractor import GresourceExtractor
+from scripts.utils.gresource.gresource_mover import GresourceMover
+from scripts.utils.logger.logger import LoggerFactory
+
+
+class Gresource:
+ """Orchestrator for gresource files. Manages the extraction, compilation, and backup of gresource files."""
+
+ def __init__(self, gresource_file: str, temp_folder: str, destination: str, logger_factory: LoggerFactory):
+ """
+ :param gresource_file: The name of the gresource file to be processed.
+ :param temp_folder: The temporary folder where resources will be extracted.
+ :param destination: The destination folder where the compiled gresource file will be saved.
+ """
+ self.gresource_file = gresource_file
+ self.temp_folder = temp_folder
+ self.destination = destination
+ self.logger_factory = logger_factory
+
+ self._temp_gresource = os.path.join(temp_folder, gresource_file)
+ self._destination_gresource = os.path.join(destination, gresource_file)
+ self._active_source_gresource = self._destination_gresource
+
+ self._backuper = GresourceBackuperManager(self._destination_gresource, logger_factory=self.logger_factory)
+
+ def use_backup_gresource(self):
+ self._active_source_gresource = self._backuper.get_backup()
+
+ def extract(self):
+ GresourceExtractor(self._active_source_gresource, self.temp_folder, logger_factory=self.logger_factory).extract()
+
+ def compile(self):
+ GresourceCompiler(self.temp_folder, self._temp_gresource, logger_factory=self.logger_factory).compile()
+
+ def backup(self):
+ self._backuper.backup()
+
+ def restore(self):
+ self._backuper.restore()
+
+ def move(self):
+ GresourceMover(self._temp_gresource, self._destination_gresource, logger_factory=self.logger_factory).move()
diff --git a/scripts/utils/gresource/gresource_backuper.py b/scripts/utils/gresource/gresource_backuper.py
new file mode 100644
index 0000000..45beb0b
--- /dev/null
+++ b/scripts/utils/gresource/gresource_backuper.py
@@ -0,0 +1,52 @@
+import os
+import shutil
+import subprocess
+
+from scripts.utils.gresource import GresourceBackupNotFoundError
+from scripts.utils.logger.logger import LoggerFactory
+
+
+class GresourceBackuperManager:
+ def __init__(self, destination_file: str, logger_factory: LoggerFactory):
+ self.destination_file = destination_file
+ self._backup_file = f"{destination_file}.backup"
+ self._backuper = GresourceBackuper(destination_file, self._backup_file, logger_factory)
+
+ def backup(self):
+ self._backuper.backup()
+
+ def restore(self):
+ self._backuper.restore()
+
+ def get_backup(self) -> str:
+ return self._backuper.get_backup()
+
+
+class GresourceBackuper:
+ def __init__(self, destination_file: str, backup_file, logger_factory: LoggerFactory):
+ self.destination_file = destination_file
+ self.backup_file = backup_file
+ self.logger_factory = logger_factory
+
+ def get_backup(self) -> str:
+ if not os.path.exists(self.backup_file):
+ raise GresourceBackupNotFoundError(self.backup_file)
+ return self.backup_file
+
+ def backup(self):
+ backup_line = self.logger_factory.create_logger()
+ backup_line.update("Backing up gresource files...")
+
+ if os.path.exists(self.backup_file):
+ os.remove(self.backup_file)
+
+ shutil.copy2(self.destination_file, self.backup_file)
+ # subprocess.run(["cp", "-aT", self.destination_file, self.backup_file], check=True)
+
+ backup_line.success("Backed up gresource files.")
+
+ def restore(self):
+ if not os.path.exists(self.backup_file):
+ raise GresourceBackupNotFoundError(self.backup_file)
+
+ subprocess.run(["mv", "-f", self.backup_file, self.destination_file], check=True)
diff --git a/scripts/utils/gresource/gresource_complier.py b/scripts/utils/gresource/gresource_complier.py
new file mode 100644
index 0000000..2d7698b
--- /dev/null
+++ b/scripts/utils/gresource/gresource_complier.py
@@ -0,0 +1,63 @@
+import subprocess
+import textwrap
+from pathlib import Path
+
+from scripts.utils.gresource import raise_gresource_error
+from scripts.utils.logger.logger import LoggerFactory
+
+
+class GresourceCompiler:
+ def __init__(self, source_folder: str, target_file: str, logger_factory: LoggerFactory):
+ self.source_folder = source_folder
+ self.target_file = target_file
+ self.gresource_xml = target_file + ".xml"
+ self.logger_factory = logger_factory
+
+ def compile(self):
+ compile_line = self.logger_factory.create_logger()
+ compile_line.update("Compiling gnome-shell theme...")
+
+ self._create_gresource_xml()
+ self._compile_resources()
+
+ compile_line.success("Compiled gnome-shell theme.")
+
+ def _create_gresource_xml(self):
+ with open(self.gresource_xml, 'w') as gresource_xml:
+ gresource_xml.write(self._generate_gresource_xml())
+
+ def _generate_gresource_xml(self):
+ files_to_include = self._get_files_to_include()
+ nl = "\n" # fstring doesn't support newline character
+ return textwrap.dedent(f"""
+
+
+
+ {nl.join(files_to_include)}
+
+
+ """)
+
+ def _get_files_to_include(self):
+ source_path = Path(self.source_folder)
+ return [
+ f"{file.relative_to(source_path)}"
+ for file in source_path.glob('**/*')
+ if file.is_file()
+ ]
+
+ def _compile_resources(self):
+ try:
+ self._try_compile_resources()
+ except FileNotFoundError as e:
+ if "glib-compile-resources" in str(e):
+ raise_gresource_error("glib-compile-resources", e)
+ raise
+
+ def _try_compile_resources(self):
+ subprocess.run(["glib-compile-resources",
+ "--sourcedir", self.source_folder,
+ "--target", self.target_file,
+ self.gresource_xml
+ ],
+ cwd=self.source_folder, check=True)
diff --git a/scripts/utils/gresource/gresource_extractor.py b/scripts/utils/gresource/gresource_extractor.py
new file mode 100644
index 0000000..a84e3f8
--- /dev/null
+++ b/scripts/utils/gresource/gresource_extractor.py
@@ -0,0 +1,53 @@
+import os
+import subprocess
+
+from scripts.utils.gresource import raise_gresource_error
+from scripts.utils.logger.logger import LoggerFactory
+
+
+class GresourceExtractor:
+ def __init__(self, gresource_path: str, extract_folder: str, logger_factory: LoggerFactory):
+ self.gresource_path = gresource_path
+ self.extract_folder = extract_folder
+ self.logger_factory = logger_factory
+
+ def extract(self):
+ extract_line = self.logger_factory.create_logger()
+ extract_line.update("Extracting gresource files...")
+
+ resources = self._get_resources_list()
+ self._extract_resources(resources)
+
+ extract_line.success("Extracted gresource files.")
+
+ def _get_resources_list(self):
+ resources_list_response = subprocess.run(
+ ["gresource", "list", self.gresource_path],
+ capture_output=True, text=True, check=False
+ )
+
+ if resources_list_response.stderr:
+ raise Exception(f"gresource could not process the theme file: {self.gresource_path}")
+
+ return resources_list_response.stdout.strip().split("\n")
+
+ def _extract_resources(self, resources: list[str]):
+ try:
+ self._try_extract_resources(resources)
+ except FileNotFoundError as e:
+ if "gresource" in str(e):
+ raise_gresource_error("gresource", e)
+ raise
+
+ def _try_extract_resources(self, resources: list[str]):
+ prefix = "/org/gnome/shell/theme/"
+ for resource in resources:
+ resource_path = resource.replace(prefix, "")
+ output_path = os.path.join(self.extract_folder, resource_path)
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
+
+ with open(output_path, 'wb') as f:
+ subprocess.run(
+ ["gresource", "extract", self.gresource_path, resource],
+ stdout=f, check=True
+ )
diff --git a/scripts/utils/gresource/gresource_mover.py b/scripts/utils/gresource/gresource_mover.py
new file mode 100644
index 0000000..4c8a34a
--- /dev/null
+++ b/scripts/utils/gresource/gresource_mover.py
@@ -0,0 +1,23 @@
+import subprocess
+
+from scripts.utils.logger.logger import LoggerFactory
+
+
+class GresourceMover:
+ def __init__(self, source_file: str, destination_file: str, logger_factory: LoggerFactory):
+ self.source_file = source_file
+ self.destination_file = destination_file
+ self.logger_factory = logger_factory
+
+ def move(self):
+ move_line = self.logger_factory.create_logger()
+ move_line.update("Moving gresource files...")
+
+ subprocess.run(["cp", "-f",
+ self.source_file,
+ self.destination_file],
+ check=True)
+
+ subprocess.run(["chmod", "644", self.destination_file], check=True)
+
+ move_line.success("Moved gresource files.")
diff --git a/scripts/utils/logger/__init__.py b/scripts/utils/logger/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/utils/console.py b/scripts/utils/logger/console.py
similarity index 88%
rename from scripts/utils/console.py
rename to scripts/utils/logger/console.py
index 53d2738..b04552f 100644
--- a/scripts/utils/console.py
+++ b/scripts/utils/logger/console.py
@@ -3,14 +3,24 @@ import threading
from enum import Enum
from typing import Optional
+from scripts.utils.logger.logger import LoggerFactory, Logger
-class Console:
+
+class Console(LoggerFactory):
"""Manages console output for concurrent processes with line tracking"""
_print_lock = threading.Lock()
_line_mapping = {}
_next_line = 0
- class Line:
+ def create_logger(self, name: Optional[str]=None) -> 'Console.Line':
+ """
+ Create a logger instance with the given name.
+ :param name: Name of the logger.
+ :return: Logger instance.
+ """
+ return Console.Line(name)
+
+ class Line(Logger):
def __init__(self, name: Optional[str]=None):
"""Initialize a new managed line"""
self.name = name or f"line_{Console._next_line}"
diff --git a/scripts/utils/logger/logger.py b/scripts/utils/logger/logger.py
new file mode 100644
index 0000000..e3a00e3
--- /dev/null
+++ b/scripts/utils/logger/logger.py
@@ -0,0 +1,36 @@
+from abc import ABC, abstractmethod
+from typing import Optional
+
+
+class LoggerFactory(ABC):
+ @staticmethod
+ @abstractmethod
+ def create_logger(name: Optional[str] = None) -> 'Logger':
+ """
+ Create a logger instance with the given name.
+ :param name: Name of the logger.
+ :return: Logger instance.
+ """
+ pass
+
+
+class Logger(ABC):
+ @abstractmethod
+ def update(self, message: str):
+ pass
+
+ @abstractmethod
+ def success(self, message):
+ pass
+
+ @abstractmethod
+ def error(self, message):
+ pass
+
+ @abstractmethod
+ def warn(self, message):
+ pass
+
+ @abstractmethod
+ def info(self, message):
+ pass
\ No newline at end of file
diff --git a/scripts/utils/remove_files.py b/scripts/utils/remove_files.py
index ffb9173..7a1d9af 100644
--- a/scripts/utils/remove_files.py
+++ b/scripts/utils/remove_files.py
@@ -7,7 +7,7 @@ import shutil
from collections import defaultdict
from typing import Any
-from .console import Console, Color, Format
+from scripts.utils.logger.console import Console, Color, Format
from .parse_folder import parse_folder
from .. import config
import os