service.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. # -*- coding: utf-8 -*-
  2. """
  3. AssetService für die ServiceContainer-Integration.
  4. Wrapper um den AssetManager, der als IService im ServiceContainer
  5. registriert werden kann.
  6. """
  7. from pathlib import Path
  8. from typing import TYPE_CHECKING, ClassVar, Iterator
  9. from trixy_core.service import IService, ServicePriority, ServiceGroup, ServiceState
  10. from trixy_core.assets.manager import AssetManager, AssetType, AssetInfo, AssetNotFoundError
  11. from trixy_core.utils.debug import pinfo, pdebug
  12. if TYPE_CHECKING:
  13. from trixy_core.application import IApplication
  14. class AssetService(IService):
  15. """
  16. Service-Wrapper für den AssetManager.
  17. Integriert den AssetManager in den ServiceContainer und
  18. übernimmt die Konfiguration aus der Anwendungskonfiguration.
  19. Der Service lädt die Assets-Konfiguration (base_dir, profile) aus
  20. der Anwendungskonfiguration und stellt den AssetManager für
  21. andere Services und Plugins bereit.
  22. Beispiel:
  23. # Im ServiceContainer
  24. asset_service = container.get(AssetService)
  25. # Asset-Zugriff
  26. path = asset_service.get("audio/success.wav")
  27. # Manager direkt
  28. manager = asset_service.manager
  29. for info in manager.list_audio():
  30. print(info.name)
  31. """
  32. NAME: ClassVar[str] = "AssetService"
  33. PRIORITY: ClassVar[ServicePriority] = ServicePriority.CRITICAL
  34. GROUP: ClassVar[ServiceGroup] = ServiceGroup.UTILITY
  35. DEPENDENCIES: ClassVar[list[str]] = []
  36. def __init__(self, application: "IApplication") -> None:
  37. """
  38. Initialisiert den AssetService.
  39. Args:
  40. application: Referenz zur Hauptanwendung
  41. """
  42. super().__init__(application)
  43. self._manager: AssetManager | None = None
  44. @property
  45. def manager(self) -> AssetManager:
  46. """
  47. Gibt den AssetManager zurück.
  48. Returns:
  49. AssetManager-Instanz
  50. Raises:
  51. RuntimeError: Wenn der Service nicht gestartet ist
  52. """
  53. if self._manager is None:
  54. raise RuntimeError("AssetService ist nicht gestartet")
  55. return self._manager
  56. async def start(self) -> None:
  57. """Startet den AssetService."""
  58. self._set_state(ServiceState.STARTING)
  59. # Konfiguration aus Application lesen
  60. config = self._application.config
  61. base_dir = getattr(config, "assets_directory", "assets")
  62. profile = getattr(config, "profile", "default")
  63. # Absoluten Pfad auflösen (relativ zum Arbeitsverzeichnis)
  64. base_path = Path(base_dir)
  65. if not base_path.is_absolute():
  66. base_path = Path.cwd() / base_dir
  67. # Manager initialisieren
  68. self._manager = AssetManager(
  69. base_dir=base_path,
  70. profile=profile,
  71. cache_enabled=True,
  72. cache_ttl=60.0,
  73. )
  74. self._set_state(ServiceState.RUNNING)
  75. pinfo(f"AssetService gestartet: profile={profile}, base={base_path}")
  76. async def stop(self) -> None:
  77. """Stoppt den AssetService."""
  78. self._set_state(ServiceState.STOPPING)
  79. if self._manager is not None:
  80. self._manager.clear_cache()
  81. self._manager = None
  82. self._set_state(ServiceState.STOPPED)
  83. pdebug("AssetService gestoppt")
  84. async def health_check(self) -> bool:
  85. """Prüft die Gesundheit des Services."""
  86. if self._manager is None:
  87. return False
  88. # Prüfe ob Basis-Verzeichnis existiert
  89. return self._manager.base_dir.exists()
  90. # =========================================================================
  91. # Delegierte Methoden für einfachen Zugriff
  92. # =========================================================================
  93. def get(self, asset_path: str, raise_on_missing: bool = False) -> Path | None:
  94. """
  95. Gibt den Pfad zu einem Asset zurück.
  96. Args:
  97. asset_path: Relativer Pfad zum Asset
  98. raise_on_missing: Bei True wird AssetNotFoundError geworfen
  99. Returns:
  100. Pfad oder None
  101. """
  102. return self.manager.get(asset_path, raise_on_missing)
  103. def get_info(self, asset_path: str) -> AssetInfo | None:
  104. """
  105. Gibt detaillierte Informationen über ein Asset zurück.
  106. Args:
  107. asset_path: Relativer Pfad zum Asset
  108. Returns:
  109. AssetInfo oder None
  110. """
  111. return self.manager.get_info(asset_path)
  112. def exists(self, asset_path: str) -> bool:
  113. """
  114. Prüft, ob ein Asset existiert.
  115. Args:
  116. asset_path: Relativer Pfad zum Asset
  117. Returns:
  118. True wenn das Asset existiert
  119. """
  120. return self.manager.exists(asset_path)
  121. def get_audio(self, filename: str) -> Path | None:
  122. """Gibt den Pfad zu einer Audio-Datei zurück."""
  123. return self.manager.get_audio(filename)
  124. def get_music(self, filename: str) -> Path | None:
  125. """Gibt den Pfad zu einer Musik-Datei zurück."""
  126. return self.manager.get_music(filename)
  127. def get_image(self, filename: str) -> Path | None:
  128. """Gibt den Pfad zu einer Bild-Datei zurück."""
  129. return self.manager.get_image(filename)
  130. def list_assets(
  131. self,
  132. asset_type: AssetType | None = None,
  133. directory: str | None = None,
  134. include_default: bool = True,
  135. ) -> Iterator[AssetInfo]:
  136. """Listet alle Assets auf."""
  137. return self.manager.list_assets(asset_type, directory, include_default)
  138. def list_audio(self) -> Iterator[AssetInfo]:
  139. """Listet alle Audio-Assets."""
  140. return self.manager.list_audio()
  141. def list_music(self) -> Iterator[AssetInfo]:
  142. """Listet alle Musik-Assets."""
  143. return self.manager.list_music()
  144. def list_profiles(self) -> list[str]:
  145. """Listet alle verfügbaren Profile."""
  146. return self.manager.list_profiles()
  147. @property
  148. def profile(self) -> str:
  149. """Gibt das aktive Profil zurück."""
  150. return self.manager.profile
  151. @profile.setter
  152. def profile(self, value: str) -> None:
  153. """Setzt das aktive Profil."""
  154. self.manager.profile = value
  155. @property
  156. def base_dir(self) -> Path:
  157. """Gibt das Basis-Verzeichnis zurück."""
  158. return self.manager.base_dir
  159. def get_stats(self) -> dict:
  160. """Gibt Statistiken zurück."""
  161. return self.manager.get_stats()
  162. def clear_cache(self) -> None:
  163. """Leert den Asset-Cache."""
  164. self.manager.clear_cache()