# -*- coding: utf-8 -*- """ Crossfade-Plugin für Trixy. Ermöglicht nahtlose Übergänge zwischen Musik-Tracks durch Mischen des Track-Endes mit dem Anfang des nächsten Tracks. """ from trixy_core.plugins import TrixyPlugin from trixy_core.utils.debug import pinfo, pdebug, perror, pwarn from plugins.crossfade.processor import CrossfadeProcessor, CrossfadeConfig class CrossfadePlugin(TrixyPlugin): """ Crossfade-Plugin. Fügt der Audio-Pipeline einen Crossfade-Prozessor hinzu, der automatisch Übergänge zwischen Tracks mischt. """ NAME = "crossfade" VERSION = "1.0.0" DESCRIPTION = "Nahtlose Übergänge zwischen Musik-Tracks" AUTHOR = "Trixy" def __init__(self, application, plugin_path, config=None): super().__init__(application, plugin_path, config) self._processor: CrossfadeProcessor | None = None async def on_load(self) -> None: """Wird beim Laden des Plugins aufgerufen.""" pinfo(f"CrossfadePlugin: Lade Plugin v{self.VERSION}") # Konfiguration laden crossfade_config = CrossfadeConfig( duration_ms=self.get_config_value("crossfade_duration_ms", 5000), fade_curve=self.get_config_value("fade_curve", "linear"), min_track_duration_ms=self.get_config_value("min_track_duration_ms", 30000), prebuffer_duration_ms=self.get_config_value("prebuffer_duration_ms", 6000), enabled=self.get_config_value("enabled", True), ) # Prozessor erstellen self._processor = CrossfadeProcessor( processor_id=f"{self.NAME}_processor", name="Crossfade", config=crossfade_config, ) # Decoder-Registry setzen wenn verfügbar if self.application: try: # Versuche MusicPlayer zu finden from trixy_core.music.formats import get_default_registry registry = get_default_registry() self._processor.set_decoder_registry(registry) except Exception: pass pinfo(f"CrossfadePlugin: Geladen (Dauer: {crossfade_config.duration_ms}ms)") async def on_unload(self) -> None: """Wird beim Entladen des Plugins aufgerufen.""" if self._processor: self._processor.reset() self._processor = None pdebug("CrossfadePlugin: Entladen") async def on_enable(self) -> None: """Wird beim Aktivieren aufgerufen.""" pinfo("CrossfadePlugin: on_enable() aufgerufen") if self._processor: self._processor.enabled = True self._processor.config.enabled = True # Prozessor zur Pipeline hinzufügen self._register_processor() else: pwarn("CrossfadePlugin: Kein Processor vorhanden!") pinfo("CrossfadePlugin: Aktiviert") async def on_disable(self) -> None: """Wird beim Deaktivieren aufgerufen.""" if self._processor: self._processor.enabled = False self._processor.config.enabled = False # Prozessor aus Pipeline entfernen self._unregister_processor() pinfo("CrossfadePlugin: Deaktiviert") async def on_config_change(self, old_config: dict) -> None: """Wird bei Konfigurationsänderungen aufgerufen.""" if self._processor: self._processor.update_config( duration_ms=self.get_config_value("crossfade_duration_ms", 5000), fade_curve=self.get_config_value("fade_curve", "linear"), min_track_duration_ms=self.get_config_value("min_track_duration_ms", 30000), prebuffer_duration_ms=self.get_config_value("prebuffer_duration_ms", 6000), enabled=self.get_config_value("enabled", True), ) pdebug("CrossfadePlugin: Konfiguration aktualisiert") def _register_processor(self) -> None: """Registriert Prozessor bei der Audio-Pipeline.""" if not self._processor: return try: # MusicPlayer finden und Pipeline abrufen if self.application: # Server-Modus if hasattr(self.application, "services"): # Debug: Alle registrierten Services anzeigen all_services = list(self.application.services.services.keys()) pdebug(f"CrossfadePlugin: Verfügbare Services: {all_services}") music_service = self.application.services.get_service("music_player") pdebug(f"CrossfadePlugin: music_player Service = {music_service}") if music_service and hasattr(music_service, "player"): player = music_service.player pdebug(f"CrossfadePlugin: Player = {player}") if player: pipeline = player.processing_pipeline pipeline.add(self._processor) pinfo("CrossfadePlugin: Prozessor zur Pipeline hinzugefügt") return else: pdebug("CrossfadePlugin: Player ist None") else: pdebug(f"CrossfadePlugin: Service hat kein 'player' Attribut") pdebug("CrossfadePlugin: Keine Pipeline gefunden, Prozessor nicht registriert") except Exception as e: perror(f"CrossfadePlugin: Fehler beim Registrieren: {e}") import traceback pdebug(traceback.format_exc()) def _unregister_processor(self) -> None: """Entfernt Prozessor aus der Audio-Pipeline.""" if not self._processor: return try: if self.application: if hasattr(self.application, "services"): music_service = self.application.services.get_service("music_player") if music_service and hasattr(music_service, "player"): pipeline = music_service.player.processing_pipeline pipeline.remove(self._processor.id) pdebug("CrossfadePlugin: Prozessor aus Pipeline entfernt") except Exception as e: perror(f"CrossfadePlugin: Fehler beim Entfernen: {e}") @property def processor(self) -> CrossfadeProcessor | None: """Zugriff auf den Crossfade-Prozessor.""" return self._processor def get_stats(self) -> dict: """Liefert Statistiken.""" if self._processor: return self._processor.get_config() return {}