From 9253d4cef3a2ad21a0dc3c3ac714049992a937ac Mon Sep 17 00:00:00 2001 From: sbkelley Date: Sun, 30 Nov 2025 14:42:04 -0500 Subject: [PATCH] Continue restructuring --- src/obs_ctl/__init__.py | 52 +++++++------- src/obs_ctl/data/layout.tcss | 3 + src/obs_ctl/{widget => interface}/__init__.py | 5 +- src/obs_ctl/interface/element/__init__.py | 2 + .../element/bool_control.py | 0 .../element/text_readout.py | 0 src/obs_ctl/interface/message.py | 8 +++ src/obs_ctl/interface/molecule/__init__.py | 1 + .../interface/molecule/record_controls.py | 72 +++++++++++++++++++ 9 files changed, 117 insertions(+), 26 deletions(-) rename src/obs_ctl/{widget => interface}/__init__.py (96%) create mode 100644 src/obs_ctl/interface/element/__init__.py rename src/obs_ctl/{widget => interface}/element/bool_control.py (100%) rename src/obs_ctl/{widget => interface}/element/text_readout.py (100%) create mode 100644 src/obs_ctl/interface/message.py create mode 100644 src/obs_ctl/interface/molecule/__init__.py create mode 100644 src/obs_ctl/interface/molecule/record_controls.py diff --git a/src/obs_ctl/__init__.py b/src/obs_ctl/__init__.py index 440cccd..a4326e9 100644 --- a/src/obs_ctl/__init__.py +++ b/src/obs_ctl/__init__.py @@ -11,10 +11,11 @@ from textual.widgets import Footer, Header, Label, Placeholder, TabbedContent, T from textual.css.query import NoMatches from . import log, data -from .widget import OBSSettingsPane +from .interface import OBSSettingsPane -from .widget.element.bool_control import BoolControl -from .widget.element.text_readout import TextReadout +from .interface.element.bool_control import BoolControl +from .interface.element.text_readout import TextReadout +from .interface import RecordControls logger = log.getLogger(__name__) obs_req_client = obs.ReqClient() @@ -70,10 +71,11 @@ class OBSCtlApp(App): with ScrollableContainer(id="content_container"): with Vertical(id="blocks_v_container", classes="blocks_containers"): with Horizontal(id="blocks_h_container1", classes="blocks_h_containers"): - with Grid(id="controls_container", classes="blocks gridblocks"): - yield BoolControl(False, label="Recording", id="record_control", classes="controls") - yield BoolControl(False, label="Paused", true_label="Pause", false_label="Unpause", swap_red=True, id="pause_control", classes="controls") - yield BoolControl(False, label="Streaming", id="stream_control", classes="controls") + yield RecordControls(id="record_controls") + # with Grid(id="controls_container", classes="blocks gridblocks"): + # yield BoolControl(False, label="Recording", id="record_control", classes="controls") + # yield BoolControl(False, label="Paused", true_label="Pause", false_label="Unpause", swap_red=True, id="pause_control", classes="controls") + # yield BoolControl(False, label="Streaming", id="stream_control", classes="controls") with TabbedContent(id="settings_container", classes="blocks"): with TabPane("OBS Settings", id="settings-tab"): yield OBSSettingsPane(id="settings-content") @@ -93,9 +95,8 @@ class OBSCtlApp(App): yield TextReadout("Encdng frames dropped", "-/- (-.-%)", id="encoding_lag", classes="stats") - @on(BoolControl.SetFalse, "#stream_control") + @on(RecordControls.StreamStopped, "#record_controls",) def stream_off(self, message): - logger.debug(f"Stop message rcv'd from {message.control}") try: obs_req_client.stop_stream() except obs_error.OBSSDKRequestError as err: @@ -104,9 +105,9 @@ class OBSCtlApp(App): else: raise - @on(BoolControl.SetTrue, "#stream_control") + + @on(RecordControls.StreamStarted, "#record_controls") def stream_on(self, message): - logger.debug("Stream Started message rcv'd") try: obs_req_client.start_stream() except obs_error.OBSSDKRequestError as err: @@ -115,20 +116,9 @@ class OBSCtlApp(App): else: raise - @on(BoolControl.SetFalse, "#record_control") - def record_off(self, message): - logger.debug("Record Stopped message rcv'd") - try: - obs_req_client.stop_record() - except obs_error.OBSSDKRequestError as err: - if err.code == 501: - logger.debug("Record already stopped.") - else: - raise - @on(BoolControl.SetTrue, "#record_control") + @on(RecordControls.RecordStarted, "#record_controls") def record_on(self, message): - logger.debug("Record Started message rcv'd") try: obs_req_client.start_record() except obs_error.OBSSDKRequestError as err: @@ -138,7 +128,19 @@ class OBSCtlApp(App): raise - @on(BoolControl.SetTrue, "#pause_control") + @on(RecordControls.RecordStopped, "#record_controls") + def record_off(self, message): + logger.debug("Record Stopped message rcv'd") + try: + obs_req_client.stop_record() + except obs_error.OBSSDKRequestError as err: + if err.code == 501: + logger.debug("Record already stopped.") + else: + raise + + + @on(RecordControls.RecordPaused, "#record_controls") def pause_on(self, message): logger.debug("Record Paused message rcv'd") try: @@ -150,7 +152,7 @@ class OBSCtlApp(App): raise - @on(BoolControl.SetFalse, "#pause_control") + @on(RecordControls.RecordUnpaused, "#pause_control") def pause_off(self, message): logger.debug("Record Unpaused message rcv'd") try: diff --git a/src/obs_ctl/data/layout.tcss b/src/obs_ctl/data/layout.tcss index ef7d44e..e0dbb18 100644 --- a/src/obs_ctl/data/layout.tcss +++ b/src/obs_ctl/data/layout.tcss @@ -35,6 +35,9 @@ min-height:8; } +RecordControls { +} + BoolControl { background:$surface; border:blank; diff --git a/src/obs_ctl/widget/__init__.py b/src/obs_ctl/interface/__init__.py similarity index 96% rename from src/obs_ctl/widget/__init__.py rename to src/obs_ctl/interface/__init__.py index 6d1f0e0..2015550 100644 --- a/src/obs_ctl/widget/__init__.py +++ b/src/obs_ctl/interface/__init__.py @@ -6,9 +6,12 @@ from textual.widgets import Label, RadioButton, Button, Input from textual.widget import Widget from textual.css import query - +from .message import MSGBase +from .molecule import RecordControls from .. import log + + logger = log.getLogger(__name__) class OBSSettingsPane(VerticalScroll): diff --git a/src/obs_ctl/interface/element/__init__.py b/src/obs_ctl/interface/element/__init__.py new file mode 100644 index 0000000..99d7b11 --- /dev/null +++ b/src/obs_ctl/interface/element/__init__.py @@ -0,0 +1,2 @@ +from .bool_control import BoolControl +from .text_readout import TextReadout \ No newline at end of file diff --git a/src/obs_ctl/widget/element/bool_control.py b/src/obs_ctl/interface/element/bool_control.py similarity index 100% rename from src/obs_ctl/widget/element/bool_control.py rename to src/obs_ctl/interface/element/bool_control.py diff --git a/src/obs_ctl/widget/element/text_readout.py b/src/obs_ctl/interface/element/text_readout.py similarity index 100% rename from src/obs_ctl/widget/element/text_readout.py rename to src/obs_ctl/interface/element/text_readout.py diff --git a/src/obs_ctl/interface/message.py b/src/obs_ctl/interface/message.py new file mode 100644 index 0000000..650e06b --- /dev/null +++ b/src/obs_ctl/interface/message.py @@ -0,0 +1,8 @@ +from textual.message import Message +from textual.widget import Widget + +class MSGBase(Message): + control: Widget = None + def __init__(self, control): + self.control = control + super().__init__() \ No newline at end of file diff --git a/src/obs_ctl/interface/molecule/__init__.py b/src/obs_ctl/interface/molecule/__init__.py new file mode 100644 index 0000000..ace480d --- /dev/null +++ b/src/obs_ctl/interface/molecule/__init__.py @@ -0,0 +1 @@ +from .record_controls import RecordControls \ No newline at end of file diff --git a/src/obs_ctl/interface/molecule/record_controls.py b/src/obs_ctl/interface/molecule/record_controls.py new file mode 100644 index 0000000..18e1450 --- /dev/null +++ b/src/obs_ctl/interface/molecule/record_controls.py @@ -0,0 +1,72 @@ +from textual.containers import ScrollableContainer, Grid +from textual.message import Message +from textual.reactive import reactive + +from .. import MSGBase +from ..element import BoolControl +from ...log import getLogger + +logger=getLogger(__name__) +class RecordControls(ScrollableContainer): + DEFAULT_CSS = """ + Grid { + grid-size:2; + grid-rows:5; + height:10; + } + """ + + recording: reactive[bool] = False + paused: reactive[bool] = False + streaming: reactive[bool] = False + + class RecordStarted(MSGBase): ... + class RecordStopped(MSGBase): ... + class RecordPaused(MSGBase): ... + class RecordUnpaused(MSGBase): ... + class StreamStarted(MSGBase): ... + class StreamStopped(MSGBase): ... + + def compose(self): + with Grid(): + yield BoolControl(False, label="Record", id="record_control") + yield BoolControl( + False, + label="Pause", + true_label="Pause", + false_label="Unpause", + swap_red=True, + id="pause_control" + ) + yield BoolControl(False, label="Stream", id="stream_control") + + def on_bool_control_set_true(self, message: BoolControl.SetTrue): + match message.control.id: + case "record_control": + self.post_message(RecordControls.RecordStarted(self)) + case "pause_control": + self.post_message(RecordControls.RecordPaused(self)) + case "stream_control": + self.post_message(RecordControls.StreamStarted(self)) + + + def on_bool_control_set_false(self, message: BoolControl.SetFalse): + match message.control.id: + case "record_control": + self.post_message(RecordControls.RecordStopped(self)) + case "pause_control": + self.post_message(RecordControls.RecordUnpaused(self)) + case "stream_control": + self.post_message(RecordControls.StreamStopped(self)) + + def watch_recording(self, value): + control: BoolControl = self.query_exactly_one("#record_control") + control.value = value + + def watch_paused(self, value): + control: BoolControl = self.query_exactly_one("#pause_control") + control.value = value + + def watch_streaming(self, value): + control: BoolControl = self.query_exactly_one("#stream_control") + control.value = value \ No newline at end of file -- 2.47.3