Skip to content
Open
30 changes: 9 additions & 21 deletions actions/Dial.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from src.backend.DeckManagement.InputIdentifier import Input
from src.backend.PluginManager.ActionBase import ActionBase
from src.backend.PluginManager.ActionBase import ActionBase
from src.backend.DeckManagement.DeckController import DeckController
from src.backend.PageManagement.Page import Page
from src.backend.PluginManager.PluginBase import PluginBase
Expand All @@ -9,13 +8,12 @@
from loguru import logger as log
from fuzzywuzzy import fuzz
import math

import os


class Dial(ActionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.plugin_base.volume_actions.append(self)

def on_ready(self):
Expand All @@ -25,7 +23,6 @@ def on_ready(self):

def on_tick(self):
index = self.get_index()

inputs = self.plugin_base.pulse.sink_input_list()
if index < len(inputs):
self.update_labels()
Expand All @@ -38,50 +35,41 @@ def clear(self):
self.set_center_label(None)

def event_callback(self, event, data):
# Toggle mute
inputs = self.plugin_base.pulse.sink_input_list()

index = self.get_index()
if index >= len(inputs):
return

if event == Input.Dial.Events.SHORT_UP:
mute = inputs[index].mute == 0
self.plugin_base.pulse.mute(obj=inputs[index], mute=mute)

elif event == Input.Dial.Events.TURN_CW:
volume = inputs[index].volume.value_flat
volume += self.plugin_base.volume_increment

volume = inputs[index].volume.value_flat + self.plugin_base.volume_increment
self.plugin_base.pulse.volume_set_all_chans(obj=inputs[index], vol=min(1, volume))

elif event == Input.Dial.Events.TURN_CCW:
volume = inputs[index].volume.value_flat
volume -= self.plugin_base.volume_increment

volume = inputs[index].volume.value_flat - self.plugin_base.volume_increment
self.plugin_base.pulse.volume_set_all_chans(obj=inputs[index], vol=max(0, volume))

self.update_labels()

def get_index(self) -> int:
start_index = self.plugin_base.start_index
own_index = int(self.input_ident.json_identifier)
index = start_index + own_index
return index
return start_index + own_index

def update_labels(self):
inputs = self.plugin_base.pulse.sink_input_list()
index = self.get_index()

if inputs[index].mute == 0:
# Display volume % if input is not muted
volumeLabel = str(math.ceil(inputs[index].volume.value_flat*100)) + "%"
volumeLabel = str(math.ceil(inputs[index].volume.value_flat * 100)) + "%"
labelColor = [255, 255, 255]
else:
# Display "muted" text if input is muted
volumeLabel = "- " + self.plugin_base.lm.get("input.muted").upper() + " -"
labelColor = [255, 0, 0]

self.set_top_label(text=volumeLabel, color=labelColor, font_size=16)
self.set_center_label(text=inputs[index].name, font_size=18)
self.set_center_label(text=self.plugin_base.get_display_name(inputs[index]), font_size=18)

17 changes: 11 additions & 6 deletions actions/MuteKey.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from src.backend.PluginManager.ActionBase import ActionBase
from src.backend.PluginManager.ActionBase import ActionBase
from src.backend.DeckManagement.DeckController import DeckController
from src.backend.PageManagement.Page import Page
from src.backend.PluginManager.PluginBase import PluginBase
Expand All @@ -10,10 +9,11 @@

import os


class MuteKey(ActionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.plugin_base.volume_actions.append(self)

def on_ready(self):
Expand All @@ -26,7 +26,12 @@ def on_tick(self):

inputs = self.plugin_base.pulse.sink_input_list()
if index < len(inputs):
self.set_label(text=inputs[index].name, position="center", font_size=10)
# Better stream/app name (avoids generic "AudioStream", shows e.g. "ESO", "YouTube")
self.set_label(
text=self.plugin_base.get_display_name(inputs[index]),
position="center",
font_size=10
)
else:
self.clear()

Expand All @@ -41,12 +46,12 @@ def on_key_down(self):
index = self.get_index()
if index >= len(inputs):
return

mute = inputs[index].mute == 0
self.plugin_base.pulse.mute(obj=inputs[index], mute=mute)

def get_index(self) -> int:
start_index = self.plugin_base.start_index
own_index = self.input_ident.coords[0]
index = start_index + own_index - 1 # -1 because we want to ignore the first column containing the navigation keys
return index
index = start_index + own_index - 1 # -1 because we want to ignore the first column containing the navigation keys
return index
71 changes: 65 additions & 6 deletions actions/OpenVolumeMixer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from src.backend.PluginManager.ActionBase import ActionBase
from src.backend.PluginManager.ActionBase import ActionBase
from src.backend.DeckManagement.DeckController import DeckController
from src.backend.PageManagement.Page import Page
from src.backend.PluginManager.PluginBase import PluginBase
Expand All @@ -16,6 +15,7 @@
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw


class OpenVolumeMixer(ActionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand All @@ -39,26 +39,85 @@ def on_key_down(self):
page = gl.page_manager.get_page(path=page_path, deck_controller=self.deck_controller)
if page is None:
log.error("Could not create volume mixer page object. Consider reinstalling the plugin.")
return

self.plugin_base.original_page_path = self.deck_controller.active_page.json_path
self.deck_controller.load_page(page)

def get_config_rows(self) -> list:
# Increments (%)
self.increments_row = Adw.SpinRow.new_with_range(min=0, max=100, step=5)
self.increments_row.set_title("Increments (%):")

# Load default
settings = self.get_settings()
settings = self.get_settings() or {}
self.increments_row.set_value(settings.get("increments", 10))
self.plugin_base.volume_increment = self.increments_row.get_value() / 100

# Connect signal
self.increments_row.connect("changed", self.on_increments_change)

return [self.increments_row]

# --- Label mode (plugin-wide) ---
self.label_mode_row = Adw.ComboRow()
self.label_mode_row.set_title(self.plugin_base.lm.get("settings.label_mode.title"))
self.label_mode_row.set_subtitle(self.plugin_base.lm.get("settings.label_mode.subtitle"))

# Values used for storage
self._label_mode_values = ["auto", "app", "media", "app+media"]

model = Gtk.StringList.new([
self.plugin_base.lm.get("settings.label_mode.auto"),
self.plugin_base.lm.get("settings.label_mode.app"),
self.plugin_base.lm.get("settings.label_mode.media"),
self.plugin_base.lm.get("settings.label_mode.app_media"),
])
self.label_mode_row.set_model(model)

# Load current plugin setting (global)
try:
ps = self.plugin_base.get_settings() or {}
current = (ps.get("label_mode", "auto") or "auto").lower()
except Exception:
current = "auto"

selected = 0
if current in self._label_mode_values:
selected = self._label_mode_values.index(current)
self.label_mode_row.set_selected(selected)

self.label_mode_row.connect("notify::selected", self.on_label_mode_changed)

return [self.increments_row, self.label_mode_row]

def on_increments_change(self, row):
settings = self.get_settings()
settings = self.get_settings() or {}
settings["increments"] = row.get_value()
self.plugin_base.volume_increment = row.get_value() / 100
self.set_settings(settings)
self.set_settings(settings)

def on_label_mode_changed(self, row, _pspec=None):
try:
idx = row.get_selected()
except Exception:
idx = 0

mode = "auto"
try:
if hasattr(self, "_label_mode_values") and 0 <= idx < len(self._label_mode_values):
mode = self._label_mode_values[idx]
except Exception:
mode = "auto"

# Store in plugin settings (global for all mixer keys)
try:
ps = self.plugin_base.get_settings() or {}
ps["label_mode"] = mode
self.plugin_base.set_settings(ps)
except Exception as ex:
log.error(f"Failed to store label_mode: {ex}")

# Refresh all labels immediately
try:
self.plugin_base.refresh_volume_actions()
except Exception:
pass
2 changes: 1 addition & 1 deletion actions/VolumeDownKey.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ def show_state(self, state: int) -> None:
with Image.open(self.icon_path) as image:
enhancer = ImageEnhance.Brightness(image)
image = enhancer.enhance(brightness)
self.set_media(image=image.copy, size=0.8, valign=-1)
self.set_media(image=image.copy(), size=0.8, valign=-1)
8 changes: 7 additions & 1 deletion locales/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@
"plugin.name": "Lautstärkemixer",
"actions.hotkey.recorder.confirm-text": "Bestätigen",
"actions.hotkey.recorder.clear-text": "Zurücksetzen",
"input.muted": "Stumm"
"input.muted": "Stumm",
"settings.label_mode.title": "Label-Quelle",
"settings.label_mode.subtitle": "Was soll als Stream-/Programmname auf dem Deck stehen",
"settings.label_mode.auto": "Auto (Media bevorzugen, sonst App)",
"settings.label_mode.app": "Anwendung",
"settings.label_mode.media": "Media / Stream-Titel",
"settings.label_mode.app_media": "App + Media"
}
8 changes: 7 additions & 1 deletion locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@
"plugin.name": "Volume Mixer",
"actions.hotkey.recorder.confirm-text": "Confirm",
"actions.hotkey.recorder.clear-text": "Clear",
"input.muted": "Muted"
"input.muted": "Muted",
"settings.label_mode.title": "Label source",
"settings.label_mode.subtitle": "What should be shown as the stream name on the deck",
"settings.label_mode.auto": "Auto (prefer Media, fallback App)",
"settings.label_mode.app": "Application",
"settings.label_mode.media": "Media / Stream title",
"settings.label_mode.app_media": "App + Media"
}
10 changes: 8 additions & 2 deletions locales/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@
"open-browser.url.title": "URL :",
"open-browser.new-window": "Ouvrir dans une nouvelle fenêtre",
"delay.entry.title": "Délai (s) :",
"delay.entry.subtitle": "Le délai avant que la prochaine action"
}
"delay.entry.subtitle": "Le délai avant que la prochaine action",
"settings.label_mode.title": "Source du libellé",
"settings.label_mode.subtitle": "Texte affiché comme nom du flux sur le deck",
"settings.label_mode.auto": "Auto (Media, sinon App)",
"settings.label_mode.app": "Application",
"settings.label_mode.media": "Media / Titre du flux",
"settings.label_mode.app_media": "App + Media"
}
Loading