# need to import log to initalize logging
from . import data, const, log # type: ignore
-from .interface import RecordControls, StatsDisplay, SettingsBlock, obs, OBSWebSocketPane, OBSSettingsPane
+from .interface import RecordControls, StatsDisplay, SettingsBlock, obs, OBSWebSocketPane, OBSSettingsPane, OBSCTLPane
logger = getLogger(__name__)
def compose(self):
yield Header()
yield Footer()
- yield obs.API(1, ws_host=const.HOST, ws_port=const.PORT, ws_password=const.PASSWORD, ws_timeout=const.TIMEOUT, id="api")
+ yield obs.API(const.UPDATE_INTERVAL, ws_host=const.HOST, ws_port=const.PORT, ws_password=const.PASSWORD, ws_timeout=const.TIMEOUT, id="api")
logger.debug("Composing OBSCtlApp")
with ScrollableContainer(classes="scrollbox"):
with VerticalGroup(classes="content"):
def on_mount(self, message: events.Mount):
+ self.theme = const.THEME
obsws_pane = self.get_websocket_pane()
api = self.get_api()
obsws_pane.host = api.ws_host
obsws_pane.port = api.ws_port
obsws_pane.password = api.ws_password
obsws_pane.timeout = api.ws_timeout
- # self.get_api().on_record_state_changed(namedtuple("test", ("output_state"))(output_state="OBS_WEBSOCKET_OUTPUT_STARTED"))
+ # TODO: Update obsctl settings (like UPDATE_INTERVAL) also
@on(obs.API.Report, "#api")
self.get_api().set_record_path(message.value)
+ @on(OBSCTLPane.ThemeSubmitted, "#obs_ctl_pane")
+ def on_settings_block_obsctl_theme_changed(self, message: OBSCTLPane.ThemeSubmitted):
+ self.theme = message.value
+
+
+ @on(OBSCTLPane.UpdateIntervalSubmitted, "#obs_ctl_pane")
+ def on_settings_block_obsctl_update_interval_changed(self, message:OBSCTLPane.ThemeSubmitted):
+ self.get_api().interval = message.value
+
+
+ def watch_theme(self, value):
+ self.get_obs_ctl_pane().theme = value
+
+
def update_monitors(self, obs_stats: obs.OBSStats):
self.get_settings_pane().record_path = obs_stats.record_directory
self.get_stats_display().update_text_readouts(obs_stats)
return typing.cast(OBSSettingsPane, self.query_exactly_one("#obs_settings_pane"))
+ def get_obs_ctl_pane(self) -> OBSCTLPane:
+ return self.query_exactly_one("#obs_ctl_pane") # type: ignore
+
+
def get_stats_display(self) -> StatsDisplay:
return typing.cast(StatsDisplay, self.query_exactly_one("#stats_display"))
\ No newline at end of file
from platformdirs import PlatformDirs
from configparser import ConfigParser
+# App Info
APPNAME = "obs-ctl"
APPAUTHOR = "skyebee"
VERSION = "0.0.1"
-
+# Useful classes
USERDIRS = PlatformDirs(APPNAME, APPAUTHOR, ensure_exists=True)
-
CONFIG = ConfigParser()
-
+# Config-related
POSSIBLE_CONFIG: list[str] = [USERDIRS.user_config_dir]
CURRENT_CONFIG = CONFIG.read(POSSIBLE_CONFIG)
-
-
+# OBS WebSocket
HOST = "localhost"
PORT = 4455
PASSWORD = ""
-TIMEOUT = 1.0
\ No newline at end of file
+TIMEOUT = 1.0
+# obs-ctl
+UPDATE_INTERVAL = 1.0
+THEME = "monokai"
+
+# internal
+AVAILABLE_THEMES = [
+ 'textual-dark',
+ 'textual-light',
+ 'nord',
+ 'gruvbox',
+ 'catppuccin-mocha',
+ 'textual-ansi',
+ 'dracula',
+ 'tokyo-night',
+ 'monokai',
+ 'flexoki',
+ 'catppuccin-latte',
+ 'solarized-light'
+]
\ No newline at end of file
-from textual.widgets import TabbedContent
+from textual.widgets import TabbedContent, Select
from textual.containers import Container
from . import pane
+from .... import const
OBSSettingsPane = pane.make_pane(
"OBSSettingsPane",
OBSWebSocketPane = pane.make_pane(
"OBSWebSocketPane",
- pane.make_pane_widget("host", pane.SettingsWidget.STRING, "", compact=True),
- pane.make_pane_widget("port", pane.SettingsWidget.INT, 0, compact=True),
- pane.make_pane_widget("password", pane.SettingsWidget.STRING, "", compact=True),
- pane.make_pane_widget("timeout", pane.SettingsWidget.FLOAT, 0.0, compact=True),
+ pane.make_pane_widget("host", pane.SettingsWidget.STRING, const.HOST, compact=True),
+ pane.make_pane_widget("port", pane.SettingsWidget.INT, const.PORT, compact=True),
+ pane.make_pane_widget("password", pane.SettingsWidget.STRING, const.PASSWORD, compact=True),
+ pane.make_pane_widget("timeout", pane.SettingsWidget.FLOAT, const.TIMEOUT, compact=True),
)
OBSCTLPane = pane.make_pane(
"OBSCTLPane",
- names_list=["nothing"],
- settings_type_list=[pane.SettingsWidget.STRING],
- defaults_list=["zilch"]
+ pane.make_pane_widget(
+ "theme",
+ pane.SettingsWidget.LIST,
+ const.THEME,
+ zip(const.AVAILABLE_THEMES, const.AVAILABLE_THEMES),
+ allow_blank=False,
+ compact=True),
+ pane.make_pane_widget(
+ "update_interval",
+ pane.SettingsWidget.FLOAT,
+ const.UPDATE_INTERVAL,
+ compact=True,
+ )
)
-
class SettingsBlock(Container):
def compose(self):
with TabbedContent(classes="settings_block--tabbed-content"):
yield OBSSettingsPane("OBS Settings", id="obs_settings_pane")
yield OBSWebSocketPane("OBS WebSocket", id="obs_websocket_pane")
- # yield OBSCTLPane("obs-ctl Settings", id="obs_ctl_pane")
+ yield OBSCTLPane("obs-ctl Settings", id="obs_ctl_pane")
class RecordPathSubmitted(pane.SettingsMSGBase): ...
def watch_record_path(self: Self, value: str) -> None: ...
def on_record_path_submitted(self: Self, event: pane.SettingsMSGBase) -> None: ...
- def compose(self: Self) -> ComposeResult: ...
class OBSWebSocketPane(PaneBaseSubclass):
host: ClassVar[reactive[str]]
def on_port_submitted(self: Self, event: pane.SettingsMSGBase) -> None: ...
def on_password_submitted(self: Self, event: pane.SettingsMSGBase) -> None: ...
def on_timeout_submitted(self: Self, event: pane.SettingsMSGBase) -> None: ...
- def compose(self: Self) -> ComposeResult: ...
-class OBSCTLPane(PaneBaseSubclass): ...
+class OBSCTLPane(PaneBaseSubclass):
+ theme: ClassVar[reactive[str]]
+ class ThemeSubmitted(pane.SettingsMSGBase): ...
+ def watch_theme(self: Self, value: str) -> None: ...
+ def on_theme_submitted(self: Self, event: pane.SettingsMSGBase) -> None: ...
+ update_interval: ClassVar[reactive[float]]
+ class UpdateIntervalSubmitted(pane.SettingsMSGBase): ...
+ def watch_update_interval(self: Self, value: str) -> None: ...
+ def on_update_interval_submitted(self: Self, event: pane.SettingsMSGBase) -> None: ...
class SettingsBlock(Container):
def compose(self) -> ComposeResult: ...
_last_stream_state: reactive["OutputStates"] = reactive(OutputStates.STOPPED)
_last_record_state: reactive["OutputStates"] = reactive(OutputStates.STOPPED)
_trying_request: bool = False
- _timer: Timer | None = None
+ _timer: Timer
interval: reactive[float] = reactive(1.0)
ws_host: reactive[str] = reactive("localhost")
def watch_interval(self, value: float) -> None:
- self._reset_timer(value)
+ self._timer.stop()
+ self._reset_timer(float(value))
+ pass
def watch_ws_host(self, value: str) -> None: