diff --git a/MediaAction.py b/MediaAction.py index 999adce..03c8290 100644 --- a/MediaAction.py +++ b/MediaAction.py @@ -6,7 +6,8 @@ import gi gi.require_version("Gtk", "4.0") gi.require_version("Adw", "1") -from gi.repository import Gtk, Adw +from gi.repository import Gtk, Adw, Gio, GLib +import globals as gl from PIL import Image, ImageEnhance import os @@ -39,13 +40,26 @@ def get_config_rows(self) -> "list[Adw.PreferencesRow]": self.label_toggle = Adw.SwitchRow(title=self.plugin_base.lm.get("actions.media-action.show-name-switch.label"), subtitle=self.plugin_base.lm.get("actions.media-action.show-name-switch.subtitle")) self.thumbnail_toggle = Adw.SwitchRow(title=self.plugin_base.lm.get("actions.media-action.show-thumbnail-switch.label"), subtitle=self.plugin_base.lm.get("actions.media-action.show-thumbnail-switch.subtitle")) + self.idle_icon_row = Adw.ActionRow( + title=self.plugin_base.lm.get("actions.media-action.idle-icon.label"), + ) + self.choose_idle_icon_button = Gtk.Button.new_from_icon_name("document-open-symbolic") + self.choose_idle_icon_button.set_valign(Gtk.Align.CENTER) + self.idle_icon_row.add_suffix(self.choose_idle_icon_button) + + self.clear_idle_icon_button = Gtk.Button.new_from_icon_name("edit-clear-symbolic") + self.clear_idle_icon_button.set_valign(Gtk.Align.CENTER) + self.idle_icon_row.add_suffix(self.clear_idle_icon_button) + self.load_config_defaults() self.player_selector.connect("notify::selected-item", self.on_change_player) self.label_toggle.connect("notify::active", self.on_toggle_label) self.thumbnail_toggle.connect("notify::active", self.on_toggle_thumbnail) + self.choose_idle_icon_button.connect("clicked", self.on_choose_idle_icon_clicked) + self.clear_idle_icon_button.connect("clicked", self.on_clear_idle_icon) - return [self.player_selector, self.label_toggle, self.thumbnail_toggle] + return [self.player_selector, self.label_toggle, self.thumbnail_toggle, self.idle_icon_row] ## Custom methods def load_config_defaults(self): @@ -55,10 +69,12 @@ def load_config_defaults(self): show_label = settings.setdefault("show_label", True) show_thumbnail = settings.setdefault("show_thumbnail", True) + idle_icon = settings.setdefault("idle_icon", "") # Update ui self.label_toggle.set_active(show_label) self.thumbnail_toggle.set_active(show_thumbnail) + self.update_idle_icon_ui(idle_icon) self.update_player_selector() def update_player_selector(self): @@ -143,6 +159,52 @@ def on_toggle_thumbnail(self, switch, *args): # Update image self.on_tick() + def update_idle_icon_ui(self, path): + if path and os.path.isfile(path): + filename = os.path.basename(path) + self.idle_icon_row.set_subtitle(filename) + self.clear_idle_icon_button.set_sensitive(True) + else: + default_text = self.plugin_base.lm.get("actions.media-action.idle-icon.default-subtitle") + self.idle_icon_row.set_subtitle("None" if default_text is None else default_text) + self.clear_idle_icon_button.set_sensitive(False) + + def on_choose_idle_icon_clicked(self, button): + settings = self.get_settings() + current_val = settings.get("idle_icon", "") if settings else "" + + def on_select_callback(path): + if not path: + return + settings = self.get_settings() + if settings is not None: + settings["idle_icon"] = path + self.set_settings(settings) + self.update_idle_icon_ui(path) + self.on_tick() + + GLib.idle_add(gl.app.let_user_select_asset, current_val, on_select_callback) + + def on_clear_idle_icon(self, button): + settings = self.get_settings() + if settings is not None: + settings["idle_icon"] = "" + self.set_settings(settings) + self.update_idle_icon_ui("") + self.on_tick() + + def get_idle_icon(self) -> Image.Image | None: + settings = self.get_settings() + if settings is None: + return None + idle_icon_path = settings.get("idle_icon", "") + if idle_icon_path and os.path.isfile(idle_icon_path): + try: + return Image.open(idle_icon_path) + except Exception as e: + pass + return None + def generate_image(self, icon:Image.Image = None, background:Image.Image=None, valign: float = 0, halign: float = 0, size: float = 1): if background is None: background = Image.new("RGBA", (self.deck_controller.deck.key_image_format()["size"]), (0, 0, 0, 0)) diff --git a/locales/de_DE.json b/locales/de_DE.json index 86cd4c2..b7980eb 100644 --- a/locales/de_DE.json +++ b/locales/de_DE.json @@ -22,5 +22,9 @@ "settings.composite-timeout.label": "Composite-Timeout", "settings.composite-timeout.subtitle": "Verzögerung vor dem Zusammensetzen von Miniaturbildern (ms)", "settings.log-level.label": "Log-Ebene", - "settings.log-level.subtitle": "Kontrollen Sie die Ausführlichkeit der Plugin-Protokollierung" + "settings.log-level.subtitle": "Kontrollen Sie die Ausführlichkeit der Plugin-Protokollierung", + "actions.media-action.idle-icon.label": "Inaktives Icon", + "actions.media-action.idle-icon.subtitle": "Icon, das angezeigt wird, wenn keine Medien abgespielt werden", + "actions.media-action.idle-icon.dialog-title": "Inaktives Icon auswählen", + "actions.media-action.idle-icon.default-subtitle": "Keines" } \ No newline at end of file diff --git a/locales/en_US.json b/locales/en_US.json index 4acc734..ca8e974 100644 --- a/locales/en_US.json +++ b/locales/en_US.json @@ -23,5 +23,9 @@ "settings.composite-timeout.label": "Composite Timeout", "settings.composite-timeout.subtitle": "Delay before compositing thumbnails (ms)", "settings.log-level.label": "Log Level", - "settings.log-level.subtitle": "Control the verbosity of plugin logging" + "settings.log-level.subtitle": "Control the verbosity of plugin logging", + "actions.media-action.idle-icon.label": "Idle Icon", + "actions.media-action.idle-icon.subtitle": "Icon to show when no media is playing", + "actions.media-action.idle-icon.dialog-title": "Select Idle Icon", + "actions.media-action.idle-icon.default-subtitle": "None" } \ No newline at end of file diff --git a/main.py b/main.py index b72dd43..cd1317d 100644 --- a/main.py +++ b/main.py @@ -70,6 +70,10 @@ def update_image(self): if status == None: if self.current_status == None: self.current_status = "Playing" + idle_image = self.get_idle_icon() + if idle_image is not None: + self.set_media(image=idle_image, size=size, valign=valign) + return image = Image.open(icon_path) enhancer = ImageEnhance.Brightness(image) image = enhancer.enhance(0.6) @@ -145,6 +149,10 @@ def update_image(self): if status == None: if self.current_status == None: self.current_status = "Playing" + idle_image = self.get_idle_icon() + if idle_image is not None: + self.set_media(image=idle_image, size=size, valign=valign) + return image = Image.open(icon_path) enhancer = ImageEnhance.Brightness(image) image = enhancer.enhance(0.6) @@ -229,6 +237,10 @@ def update_image(self): if status == None: if self.current_status == None: self.current_status = "Playing" + idle_image = self.get_idle_icon() + if idle_image is not None: + self.set_media(image=idle_image, size=size, valign=valign) + return file_path = file[self.current_status] image = Image.open(file_path) enhancer = ImageEnhance.Brightness(image) @@ -290,6 +302,10 @@ def update_image(self): image = Image.open(os.path.join(self.plugin_base.PATH, "assets", "next.png")) if status == None: + idle_image = self.get_idle_icon() + if idle_image is not None: + self.set_media(image=idle_image, size=size, valign=valign) + return enhancer = ImageEnhance.Brightness(image) image = enhancer.enhance(0.6) @@ -338,6 +354,10 @@ def update_image(self): image = Image.open(os.path.join(self.plugin_base.PATH, "assets", "previous.png")) if status == None: + idle_image = self.get_idle_icon() + if idle_image is not None: + self.set_media(image=idle_image, size=size, valign=valign) + return enhancer = ImageEnhance.Brightness(image) image = enhancer.enhance(0.6) @@ -366,27 +386,47 @@ def on_tick(self): self.update_image() def update_image(self): + status = self.plugin_base.mc.status(self.get_player_name()) + if isinstance(status, list): + status = status[0] + + if status == None: + self.set_top_label("", font_size=12) + self.set_center_label("", font_size=12) + self.set_bottom_label("", font_size=12) + + idle_image = self.get_idle_icon() + if idle_image is not None: + self.set_media(image=idle_image) + else: + self.set_media(image=Image.new("RGBA", (256, 256), (255, 255, 255, 0))) + return + title = self.plugin_base.mc.title(self.get_player_name()) artist = self.plugin_base.mc.artist(self.get_player_name()) if title is not None: title = self.shorten_label(title[0], 10) - if title is not None: + if artist is not None: artist = self.shorten_label(artist[0], 10) if self.get_settings() is None: return - self.set_top_label(str(title), font_size=12) - self.set_center_label(self.get_settings().get("seperator_text", "--"), font_size=12) - self.set_bottom_label(str(artist), font_size=12) + self.set_top_label(str(title) if title is not None else "", font_size=12) + self.set_center_label(self.get_settings().get("seperator_text", "--") if (title is not None or artist is not None) else "", font_size=12) + self.set_bottom_label(str(artist) if artist is not None else "", font_size=12) ## Thumbnail thumbnail = None if self.get_settings().setdefault("show_thumbnail", True): thumbnail = self.plugin_base.mc.thumbnail(self.get_player_name()) if thumbnail == None: - thumbnail = Image.new("RGBA", (256, 256), (255, 255, 255, 0)) + idle_image = self.get_idle_icon() + if idle_image is not None: + thumbnail = idle_image + else: + thumbnail = Image.new("RGBA", (256, 256), (255, 255, 255, 0)) elif isinstance(thumbnail, list): if thumbnail[0] == None: return @@ -832,6 +872,10 @@ def get_config_rows(self) -> "list[Adw.PreferencesRow]": self.size_mode_selector.connect("notify::selected", self.on_change_size_mode) rows.append(self.size_mode_selector) # type: ignore[arg-type] + + if hasattr(self, "idle_icon_row") and self.idle_icon_row is not None: + rows.append(self.idle_icon_row) + return rows def load_size_mode_default(self): @@ -905,17 +949,21 @@ def update_image(self): if thumbnail_path is None: self.last_thumbnail_path = None - self.restore_original_background() - return - - # Load thumbnail image - try: - thumbnail = Image.open(thumbnail_path) - except (OSError, ValueError) as e: - log.error(f"Failed to load thumbnail image from {thumbnail_path}: {e}") - self.last_thumbnail_path = None - self.restore_original_background() - return + idle_image = self.get_idle_icon() + if idle_image is not None: + thumbnail = idle_image + else: + self.restore_original_background() + return + else: + # Load thumbnail image + try: + thumbnail = Image.open(thumbnail_path) + except (OSError, ValueError) as e: + log.error(f"Failed to load thumbnail image from {thumbnail_path}: {e}") + self.last_thumbnail_path = None + self.restore_original_background() + return # Track thumbnail path, background path, and position self.last_thumbnail_path = thumbnail_path