From 5b4f3753dbed06d9bbac3020430e784a2a97c9fc Mon Sep 17 00:00:00 2001 From: Onyx and Iris Date: Fri, 17 Jan 2025 20:43:34 +0000 Subject: [PATCH] re-run through ruff formatter add hatch+ruff badges --- README.md | 4 +- __main__.py | 76 ++++++++-------- duckypad_twitch/__about__.py | 2 +- duckypad_twitch/audio.py | 60 ++++++------- duckypad_twitch/configuration.py | 6 +- duckypad_twitch/duckypad.py | 19 ++-- duckypad_twitch/obsws.py | 39 ++++---- duckypad_twitch/scene.py | 44 ++++----- duckypad_twitch/states.py | 2 +- duckypad_twitch/streamlabs.py | 48 ++++------ pyproject.toml | 149 ++++++++++++++++++------------- 11 files changed, 230 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index 4deb418..18294f2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # duckypad twitch -[![PyPI - Version](https://img.shields.io/pypi/v/duckypad-twitch.svg)](https://pypi.org/project/duckypad-twitch) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/duckypad-twitch.svg)](https://pypi.org/project/duckypad-twitch) +[![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) --- diff --git a/__main__.py b/__main__.py index edddedf..630cf38 100644 --- a/__main__.py +++ b/__main__.py @@ -7,71 +7,69 @@ import xair_api import duckypad_twitch from duckypad_twitch import configuration -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) def register_hotkeys(duckypad): def audio_hotkeys(): - keyboard.add_hotkey("F13", duckypad.audio.mute_mics) - keyboard.add_hotkey("F14", duckypad.audio.only_discord) - keyboard.add_hotkey("F15", duckypad.audio.only_stream) - keyboard.add_hotkey("F16", duckypad.audio.sound_test) - keyboard.add_hotkey("F17", duckypad.audio.solo_onyx) - keyboard.add_hotkey("F18", duckypad.audio.solo_iris) - keyboard.add_hotkey("F19", duckypad.audio.toggle_workstation_to_onyx) + keyboard.add_hotkey('F13', duckypad.audio.mute_mics) + keyboard.add_hotkey('F14', duckypad.audio.only_discord) + keyboard.add_hotkey('F15', duckypad.audio.only_stream) + keyboard.add_hotkey('F16', duckypad.audio.sound_test) + keyboard.add_hotkey('F17', duckypad.audio.solo_onyx) + keyboard.add_hotkey('F18', duckypad.audio.solo_iris) + keyboard.add_hotkey('F19', duckypad.audio.toggle_workstation_to_onyx) def scene_hotkeys(): - keyboard.add_hotkey("ctrl+F13", duckypad.scene.onyx_only) - keyboard.add_hotkey("ctrl+F14", duckypad.scene.iris_only) - keyboard.add_hotkey("ctrl+F15", duckypad.scene.dual_scene) - keyboard.add_hotkey("ctrl+F16", duckypad.scene.onyx_big) - keyboard.add_hotkey("ctrl+F17", duckypad.scene.iris_big) - keyboard.add_hotkey("ctrl+F18", duckypad.scene.start) - keyboard.add_hotkey("ctrl+F19", duckypad.scene.brb) - keyboard.add_hotkey("ctrl+F20", duckypad.scene.end) + keyboard.add_hotkey('ctrl+F13', duckypad.scene.onyx_only) + keyboard.add_hotkey('ctrl+F14', duckypad.scene.iris_only) + keyboard.add_hotkey('ctrl+F15', duckypad.scene.dual_scene) + keyboard.add_hotkey('ctrl+F16', duckypad.scene.onyx_big) + keyboard.add_hotkey('ctrl+F17', duckypad.scene.iris_big) + keyboard.add_hotkey('ctrl+F18', duckypad.scene.start) + keyboard.add_hotkey('ctrl+F19', duckypad.scene.brb) + keyboard.add_hotkey('ctrl+F20', duckypad.scene.end) def obsws_hotkeys(): - keyboard.add_hotkey("ctrl+alt+F13", duckypad.obsws.start) - keyboard.add_hotkey("ctrl+alt+F14", duckypad.obsws.brb) - keyboard.add_hotkey("ctrl+alt+F15", duckypad.obsws.end) - keyboard.add_hotkey("ctrl+alt+F16", duckypad.obsws.live) - keyboard.add_hotkey("ctrl+alt+F17", duckypad.obsws.toggle_mute_mic) - keyboard.add_hotkey("ctrl+alt+F18", duckypad.obsws.toggle_stream) + keyboard.add_hotkey('ctrl+alt+F13', duckypad.obsws.start) + keyboard.add_hotkey('ctrl+alt+F14', duckypad.obsws.brb) + keyboard.add_hotkey('ctrl+alt+F15', duckypad.obsws.end) + keyboard.add_hotkey('ctrl+alt+F16', duckypad.obsws.live) + keyboard.add_hotkey('ctrl+alt+F17', duckypad.obsws.toggle_mute_mic) + keyboard.add_hotkey('ctrl+alt+F18', duckypad.obsws.toggle_stream) def streamlabs_controller_hotkeys(): - keyboard.add_hotkey("ctrl+F22", duckypad.streamlabs_controller.begin_stream) - keyboard.add_hotkey("ctrl+F23", duckypad.streamlabs_controller.end_stream) - keyboard.add_hotkey( - "ctrl+alt+F23", duckypad.streamlabs_controller.launch, args=(10,) - ) - keyboard.add_hotkey("ctrl+alt+F24", duckypad.streamlabs_controller.shutdown) + keyboard.add_hotkey('ctrl+F22', duckypad.streamlabs_controller.begin_stream) + keyboard.add_hotkey('ctrl+F23', duckypad.streamlabs_controller.end_stream) + keyboard.add_hotkey('ctrl+alt+F23', duckypad.streamlabs_controller.launch, args=(10,)) + keyboard.add_hotkey('ctrl+alt+F24', duckypad.streamlabs_controller.shutdown) def duckypad_hotkeys(): - keyboard.add_hotkey("ctrl+F21", duckypad.reset) + keyboard.add_hotkey('ctrl+F21', duckypad.reset) - steps = ( + for step in ( audio_hotkeys, scene_hotkeys, obsws_hotkeys, streamlabs_controller_hotkeys, duckypad_hotkeys, - ) - [step() for step in steps] + ): + step() def main(): - xair_config = configuration.get("xair") + xair_config = configuration.get('xair') - with voicemeeterlib.api("potato") as vm: - with xair_api.connect("MR18", **xair_config) as mixer: + with voicemeeterlib.api('potato') as vm: + with xair_api.connect('MR18', **xair_config) as mixer: with duckypad_twitch.connect(vm=vm, mixer=mixer) as duckypad: - vm.apply_config("streaming_extender") # extends the streaming config + vm.apply_config('streaming_extender') # extends the streaming config register_hotkeys(duckypad) - print("press ctrl+m to quit") - keyboard.wait("ctrl+m") + print('press ctrl+m to quit') + keyboard.wait('ctrl+m') -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/duckypad_twitch/__about__.py b/duckypad_twitch/__about__.py index 2a62a4c..79e35de 100644 --- a/duckypad_twitch/__about__.py +++ b/duckypad_twitch/__about__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2023-present onyx-and-iris <75868496+onyx-and-iris@users.noreply.github.com> # # SPDX-License-Identifier: MIT -__version__ = "1.0.2" +__version__ = '1.0.3' diff --git a/duckypad_twitch/audio.py b/duckypad_twitch/audio.py index b8f09e7..7cc98b6 100644 --- a/duckypad_twitch/audio.py +++ b/duckypad_twitch/audio.py @@ -10,7 +10,7 @@ from .states import AudioState logger = logging.getLogger(__name__) -Buttons = IntEnum("Buttons", "mute_mics only_discord only_stream", start=0) +Buttons = IntEnum('Buttons', 'mute_mics only_discord only_stream', start=0) class Audio(ILayer): @@ -46,12 +46,12 @@ class Audio(ILayer): self.vm.strip[0].mute = True self.vm.strip[1].mute = True self.vm.strip[4].mute = True - self.logger.info("Mics Muted") + self.logger.info('Mics Muted') else: self.vm.strip[0].mute = False self.vm.strip[1].mute = False self.vm.strip[4].mute = False - self.logger.info("Mics Unmuted") + self.logger.info('Mics Unmuted') self.vm.button[Buttons.mute_mics].stateonly = self.state.mute_mics def only_discord(self): @@ -59,11 +59,11 @@ class Audio(ILayer): if self.state.only_discord: self.mixer.dca[0].on = False self.vm.strip[4].mute = True - self.logger.info("Only Discord Enabled") + self.logger.info('Only Discord Enabled') else: self.vm.strip[4].mute = False self.mixer.dca[0].on = True - self.logger.info("Only Discord Disabled") + self.logger.info('Only Discord Disabled') self.vm.button[Buttons.only_discord].stateonly = self.state.only_discord def only_stream(self): @@ -74,59 +74,57 @@ class Audio(ILayer): self.vm.strip[2].gain = -3 self.vm.strip[3].gain = -3 self.vm.strip[6].gain = -3 - self.logger.info("Only Stream Enabled") + self.logger.info('Only Stream Enabled') else: self.vm.strip[2].gain = 0 self.vm.strip[3].gain = 0 self.vm.strip[6].gain = 0 self.vm.bus[5].mute = False self.vm.bus[6].mute = False - self.logger.info("Only Stream Disabled") + self.logger.info('Only Stream Disabled') self.vm.button[Buttons.only_stream].stateonly = self.state.only_stream def sound_test(self): def toggle_soundtest(params): - onyx_conn = configuration.get("vban_onyx") - iris_conn = configuration.get("vban_iris") - assert all( - [onyx_conn, iris_conn] - ), "expected configurations for onyx_conn, iris_conn" + onyx_conn = configuration.get('vban_onyx') + iris_conn = configuration.get('vban_iris') + assert all([onyx_conn, iris_conn]), 'expected configurations for onyx_conn, iris_conn' - with vban_cmd.api("potato", outbound=True, **onyx_conn) as vban: + with vban_cmd.api('potato', outbound=True, **onyx_conn) as vban: vban.strip[0].apply(params) - with vban_cmd.api("potato", outbound=True, **iris_conn) as vban: + with vban_cmd.api('potato', outbound=True, **iris_conn) as vban: vban.strip[0].apply(params) ENABLE_SOUNDTEST = { - "A1": True, - "A2": True, - "B1": False, - "B2": False, - "mono": True, + 'A1': True, + 'A2': True, + 'B1': False, + 'B2': False, + 'mono': True, } DISABLE_SOUNDTEST = { - "A1": False, - "A2": False, - "B1": True, - "B2": True, - "mono": False, + 'A1': False, + 'A2': False, + 'B1': True, + 'B2': True, + 'mono': False, } self.state.sound_test = not self.state.sound_test if self.state.sound_test: - self.vm.strip[4].apply({"B3": False, "A1": True, "mute": False}) + self.vm.strip[4].apply({'B3': False, 'A1': True, 'mute': False}) self.vm.vban.outstream[0].on = True self.vm.vban.outstream[1].on = True self.vm.vban.outstream[0].route = 0 self.vm.vban.outstream[1].route = 0 toggle_soundtest(ENABLE_SOUNDTEST) - self.logger.info("Sound Test Enabled") + self.logger.info('Sound Test Enabled') else: toggle_soundtest(DISABLE_SOUNDTEST) self.vm.vban.outstream[0].route = 5 self.vm.vban.outstream[1].route = 6 - self.vm.strip[4].apply({"B3": True, "A1": False, "mute": True}) - self.logger.info("Sound Test Disabled") + self.vm.strip[4].apply({'B3': True, 'A1': False, 'mute': True}) + self.logger.info('Sound Test Disabled') def solo_onyx(self): """placeholder method.""" @@ -136,14 +134,14 @@ class Audio(ILayer): def toggle_workstation_to_onyx(self): self.state.ws_to_onyx = not self.state.ws_to_onyx - onyx_conn = configuration.get("vban_onyx") + onyx_conn = configuration.get('vban_onyx') if self.state.ws_to_onyx: - with vban_cmd.api("potato", **onyx_conn) as vban: + with vban_cmd.api('potato', **onyx_conn) as vban: vban.vban.instream[0].on = True self.vm.strip[5].gain = -6 self.vm.vban.outstream[2].on = True else: - with vban_cmd.api("potato", **onyx_conn) as vban: + with vban_cmd.api('potato', **onyx_conn) as vban: vban.vban.instream[0].on = False self.vm.strip[5].gain = 0 self.vm.vban.outstream[2].on = False diff --git a/duckypad_twitch/configuration.py b/duckypad_twitch/configuration.py index 85f4a8d..ba23e6e 100644 --- a/duckypad_twitch/configuration.py +++ b/duckypad_twitch/configuration.py @@ -7,11 +7,11 @@ except ModuleNotFoundError: configuration = {} -configpath = Path.cwd() / "configs" / "duckypad.toml" +configpath = Path.cwd() / 'configs' / 'duckypad.toml' if not configpath.exists(): - raise OSError(f"unable to locate {configpath}") + raise OSError(f'unable to locate {configpath}') -with open(configpath, "rb") as f: +with open(configpath, 'rb') as f: configuration = tomllib.load(f) diff --git a/duckypad_twitch/duckypad.py b/duckypad_twitch/duckypad.py index 5f78fc6..94395f8 100644 --- a/duckypad_twitch/duckypad.py +++ b/duckypad_twitch/duckypad.py @@ -28,32 +28,29 @@ class DuckyPad: def __exit__(self, exc_value, exc_type, traceback): self.streamlabs_controller.conn.disconnect() + self.obsws.disconnect() def reset(self): - ''' + """ apply streaming config, then apply current scene settings if stream is live enable both mics over vban - ''' - self.vm.apply_config("streaming") + """ + self.vm.apply_config('streaming') self.audio.reset_states() if self.stream.current_scene: - self.logger.debug( - f"Running function for current scene {self.stream.current_scene}" - ) + self.logger.debug(f'Running function for current scene {self.stream.current_scene}') fn = getattr( self.scene, - "_".join([word.lower() for word in self.stream.current_scene.split()]), + '_'.join([word.lower() for word in self.stream.current_scene.split()]), ) fn() if self.stream.is_live: - self.logger.debug("stream is live, enabling both mics over vban") + self.logger.debug('stream is live, enabling both mics over vban') self.vm.vban.outstream[0].on = True self.vm.vban.outstream[1].on = True else: - self.logger.debug( - "stream is not live. Leaving both vban outstreams disabled" - ) + self.logger.debug('stream is not live. Leaving both vban outstreams disabled') def connect(*args, **kwargs): diff --git a/duckypad_twitch/obsws.py b/duckypad_twitch/obsws.py index 0adc085..ed37117 100644 --- a/duckypad_twitch/obsws.py +++ b/duckypad_twitch/obsws.py @@ -29,15 +29,15 @@ class OBSWS(ILayer): self._state = val def reset_states(self): - resp = self.request.get_input_mute("Mic/Aux") + resp = self.request.get_input_mute('Mic/Aux') self.state.mute_mic = resp.input_muted resp = self.request.get_stream_status() self._duckypad.stream.is_live = resp.output_active def obs_connect(self): try: - conn = configuration.get("obsws") - assert conn is not None, "expected configuration for obs" + conn = configuration.get('obsws') + assert conn is not None, 'expected configuration for obs' self.request = obsws.ReqClient(**conn) self.reset_states() self.event = obsws.EventClient(**conn) @@ -50,25 +50,28 @@ class OBSWS(ILayer): ] ) except (ConnectionRefusedError, TimeoutError) as e: - self.logger.error(f"{type(e).__name__}: {e}") + self.logger.error(f'{type(e).__name__}: {e}') raise + def obs_disconnect(self): + for client in (self.request, self.event): + if client: + client.disconnect() + def on_current_program_scene_changed(self, data): self._duckypad.stream.current_scene = data.scene_name - self.logger.info(f"scene switched to {self._duckypad.stream.current_scene}") - if self._duckypad.stream.current_scene in ("START", "BRB", "END"): + self.logger.info(f'scene switched to {self._duckypad.stream.current_scene}') + if self._duckypad.stream.current_scene in ('START', 'BRB', 'END'): self.mute_mic_state(True) def on_input_mute_state_changed(self, data): - if data.input_name == "Mic/Aux": + if data.input_name == 'Mic/Aux': self.state.mute_mic = data.input_muted - self.logger.info(f"mic was {'muted' if self.state.mute_mic else 'unmuted'}") + self.logger.info(f'mic was {"muted" if self.state.mute_mic else "unmuted"}') def on_stream_state_changed(self, data): self._duckypad.stream.is_live = data.output_active - self.logger.info( - f"stream is {'live' if self._duckypad.stream.is_live else 'offline'}" - ) + self.logger.info(f'stream is {"live" if self._duckypad.stream.is_live else "offline"}') def on_exit_started(self, _): self.event.unsubscribe() @@ -80,22 +83,22 @@ class OBSWS(ILayer): return resp def start(self): - self.call("set_current_program_scene", "START") + self.call('set_current_program_scene', 'START') def brb(self): - self.call("set_current_program_scene", "BRB") + self.call('set_current_program_scene', 'BRB') def end(self): - self.call("set_current_program_scene", "END") + self.call('set_current_program_scene', 'END') def live(self): - self.call("set_current_program_scene", "LIVE") + self.call('set_current_program_scene', 'LIVE') def mute_mic_state(self, val): - self.call("set_input_mute", "Mic/Aux", val) + self.call('set_input_mute', 'Mic/Aux', val) def toggle_mute_mic(self): - self.call("toggle_input_mute", "Mic/Aux") + self.call('toggle_input_mute', 'Mic/Aux') def toggle_stream(self): - self.call("toggle_stream") + self.call('toggle_stream') diff --git a/duckypad_twitch/scene.py b/duckypad_twitch/scene.py index 635c1e8..d99ed98 100644 --- a/duckypad_twitch/scene.py +++ b/duckypad_twitch/scene.py @@ -32,49 +32,49 @@ class Scene(ILayer): self._state = SceneState() def onyx_only(self): - if self._duckypad.streamlabs_controller.switch_scene("onyx_only"): + if self._duckypad.streamlabs_controller.switch_scene('onyx_only'): self.vm.strip[2].mute = False self.vm.strip[3].mute = True - self.logger.info("Only Onyx Scene enabled, Iris game pc muted") + self.logger.info('Only Onyx Scene enabled, Iris game pc muted') def iris_only(self): - if self._duckypad.streamlabs_controller.switch_scene("iris_only"): + if self._duckypad.streamlabs_controller.switch_scene('iris_only'): self.vm.strip[2].mute = True self.vm.strip[3].mute = False - self.logger.info("Only Iris Scene enabled, Onyx game pc muted") + self.logger.info('Only Iris Scene enabled, Onyx game pc muted') def dual_scene(self): - if self._duckypad.streamlabs_controller.switch_scene("dual_scene"): - self.vm.strip[2].apply({"mute": False, "gain": 0}) - self.vm.strip[3].apply({"A5": True, "mute": False, "gain": 0}) - self.logger.info("Dual Scene enabled") + if self._duckypad.streamlabs_controller.switch_scene('dual_scene'): + self.vm.strip[2].apply({'mute': False, 'gain': 0}) + self.vm.strip[3].apply({'A5': True, 'mute': False, 'gain': 0}) + self.logger.info('Dual Scene enabled') def onyx_big(self): - if self._duckypad.streamlabs_controller.switch_scene("onyx_big"): - self.vm.strip[2].apply({"mute": False, "gain": 0}) - self.vm.strip[3].apply({"mute": False, "gain": -3}) - self.logger.info("Onyx Big scene enabled") + if self._duckypad.streamlabs_controller.switch_scene('onyx_big'): + self.vm.strip[2].apply({'mute': False, 'gain': 0}) + self.vm.strip[3].apply({'mute': False, 'gain': -3}) + self.logger.info('Onyx Big scene enabled') def iris_big(self): - if self._duckypad.streamlabs_controller.switch_scene("iris_big"): - self.vm.strip[2].apply({"mute": False, "gain": -3}) - self.vm.strip[3].apply({"mute": False, "gain": 0}) - self.logger.info("Iris Big enabled") + if self._duckypad.streamlabs_controller.switch_scene('iris_big'): + self.vm.strip[2].apply({'mute': False, 'gain': -3}) + self.vm.strip[3].apply({'mute': False, 'gain': 0}) + self.logger.info('Iris Big enabled') def start(self): - if self._duckypad.streamlabs_controller.switch_scene("start"): + if self._duckypad.streamlabs_controller.switch_scene('start'): self.vm.strip[2].mute = True self.vm.strip[3].mute = True - self.logger.info("Start scene enabled.. ready to go live!") + self.logger.info('Start scene enabled.. ready to go live!') def brb(self): - if self._duckypad.streamlabs_controller.switch_scene("brb"): + if self._duckypad.streamlabs_controller.switch_scene('brb'): self.vm.strip[2].mute = True self.vm.strip[3].mute = True - self.logger.info("BRB: game pcs muted") + self.logger.info('BRB: game pcs muted') def end(self): - if self._duckypad.streamlabs_controller.switch_scene("end"): + if self._duckypad.streamlabs_controller.switch_scene('end'): self.vm.strip[2].mute = True self.vm.strip[3].mute = True - self.logger.info("End scene enabled.") + self.logger.info('End scene enabled.') diff --git a/duckypad_twitch/states.py b/duckypad_twitch/states.py index 892946f..a5be0f0 100644 --- a/duckypad_twitch/states.py +++ b/duckypad_twitch/states.py @@ -4,7 +4,7 @@ from dataclasses import dataclass @dataclass class StreamState: is_live: bool = False - current_scene: str = "" + current_scene: str = '' @dataclass diff --git a/duckypad_twitch/streamlabs.py b/duckypad_twitch/streamlabs.py index 7732f86..84f842f 100644 --- a/duckypad_twitch/streamlabs.py +++ b/duckypad_twitch/streamlabs.py @@ -31,21 +31,17 @@ class StreamlabsController: def connect(self): try: - conn = configuration.get("streamlabs") - assert conn is not None, "expected configuration for streamlabs" + conn = configuration.get('streamlabs') + assert conn is not None, 'expected configuration for streamlabs' self.conn.connect(**conn) except slobs_websocket.exceptions.ConnectionFailure as e: - self.logger.error(f"{type(e).__name__}: {e}") + self.logger.error(f'{type(e).__name__}: {e}') raise - self._duckypad.scene.scenes = { - scene.name: scene.id for scene in self.conn.ScenesService.getScenes() - } - self.logger.debug(f"registered scenes: {self._duckypad.scene.scenes}") + self._duckypad.scene.scenes = {scene.name: scene.id for scene in self.conn.ScenesService.getScenes()} + self.logger.debug(f'registered scenes: {self._duckypad.scene.scenes}') self.conn.ScenesService.sceneSwitched += self.on_scene_switched - self.conn.StreamingService.streamingStatusChange += ( - self.on_streaming_status_change - ) + self.conn.StreamingService.streamingStatusChange += self.on_streaming_status_change def disconnect(self): self.conn.disconnect() @@ -55,17 +51,15 @@ class StreamlabsController: #################################################################################### def on_streaming_status_change(self, data): - self.logger.debug(f"streaming status changed, now: {data}") - if data in ("live", "starting"): + self.logger.debug(f'streaming status changed, now: {data}') + if data in ('live', 'starting'): self._duckypad.stream.is_live = True else: self._duckypad.stream.is_live = False def on_scene_switched(self, data): self._duckypad.stream.current_scene = data.name - self.logger.debug( - f"stream.current_scene updated to {self._duckypad.stream.current_scene}" - ) + self.logger.debug(f'stream.current_scene updated to {self._duckypad.stream.current_scene}') #################################################################################### # START/STOP the stream @@ -74,14 +68,14 @@ class StreamlabsController: @ensure_sl def begin_stream(self): if self._duckypad.stream.is_live: - self.logger.info("Stream is already online") + self.logger.info('Stream is already online') return self.conn.StreamingService.toggleStreaming() @ensure_sl def end_stream(self): if not self._duckypad.stream.is_live: - self.logger.info("Stream is already offline") + self.logger.info('Stream is already offline') return self.conn.StreamingService.toggleStreaming() @@ -91,9 +85,7 @@ class StreamlabsController: @ensure_sl def switch_scene(self, name): - return self.conn.ScenesService.makeSceneActive( - self._duckypad.scene.scenes[name.upper()] - ) + return self.conn.ScenesService.makeSceneActive(self._duckypad.scene.scenes[name.upper()]) #################################################################################### # LAUNCH/SHUTDOWN the streamlabs process @@ -102,16 +94,14 @@ class StreamlabsController: @cached_property def sl_fullpath(self) -> Path: try: - self.logger.debug("fetching sl_fullpath from the registry") - SL_KEY = "029c4619-0385-5543-9426-46f9987161d9" + self.logger.debug('fetching sl_fullpath from the registry') + SL_KEY = '029c4619-0385-5543-9426-46f9987161d9' - with winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, r"{}".format("SOFTWARE" + "\\" + SL_KEY) - ) as regpath: - slpath = winreg.QueryValueEx(regpath, r"InstallLocation")[0] - return Path(slpath) / "Streamlabs OBS.exe" + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'{}'.format('SOFTWARE' + '\\' + SL_KEY)) as regpath: + slpath = winreg.QueryValueEx(regpath, r'InstallLocation')[0] + return Path(slpath) / 'Streamlabs OBS.exe' except FileNotFoundError as e: - self.logger.exception(f"{type(e).__name__}: {e}") + self.logger.exception(f'{type(e).__name__}: {e}') raise def launch(self, delay=5): @@ -126,4 +116,4 @@ class StreamlabsController: if self.proc is not None: self.proc.terminate() self.proc = None - self._duckypad.stream.current_scene = "" + self._duckypad.stream.current_scene = '' diff --git a/pyproject.toml b/pyproject.toml index 4e7556a..fb1e5b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.7" license = "MIT" keywords = [] authors = [ - { name = "onyx-and-iris", email = "75868496+onyx-and-iris@users.noreply.github.com" }, + { name = "onyx-and-iris", email = "code@onyxandiris.online" }, ] classifiers = [ "Development Status :: 4 - Beta", @@ -28,11 +28,11 @@ dependencies = [ "tomli >= 2.0.1;python_version < '3.11'", "websocket-client", "keyboard", - "voicemeeter-api", - "xair-api", + "voicemeeter-api>=2.6.1", + "xair-api>=2.4.1", "slobs_websocket@git+https://git@github.com/onyx-and-iris/slobs_websocket@v0.1.4#egg=slobs_websocket", - "obsws-python", - "vban-cmd", + "obsws-python>=1.7.0", + "vban-cmd>=2.5.0", ] [project.urls] @@ -66,61 +66,90 @@ style = ["ruff {args:.}", "black --check --diff {args:.}"] fmt = ["black {args:.}", "ruff --fix {args:.}", "style"] all = ["style", "typing"] -[tool.black] -target-version = ["py37"] -line-length = 120 -skip-string-normalization = true - [tool.ruff] -target-version = "py37" +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] + +# Same as Black. line-length = 120 -select = [ - "A", - "ARG", - "B", - "C", - "DTZ", - "E", - "EM", - "F", - "FBT", - "I", - "ICN", - "ISC", - "N", - "PLC", - "PLE", - "PLR", - "PLW", - "Q", - "RUF", - "S", - "T", - "TID", - "UP", - "W", - "YTT", -] -ignore = [ - # Allow non-abstract empty methods in abstract base classes - "B027", - # Allow boolean positional values in function calls, like `dict.get(... True)` - "FBT003", - # Ignore checks for possible passwords - "S105", - "S106", - "S107", - # Ignore complexity - "C901", - "PLR0911", - "PLR0912", - "PLR0913", - "PLR0915", -] -unfixable = [ - # Don't touch unused imports - "F401", +indent-width = 4 + +# Assume Python 3.10 +target-version = "py310" + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E4", "E7", "E9", "F"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + + +[tool.ruff.format] +# Unlike Black, use single quotes for strings. +quote-style = "single" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" + +[tool.ruff.lint.mccabe] +max-complexity = 10 + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = [ + "E402", + "F401", ] +# Tests can use magic values, assertions, and relative imports +"tests/**/*" = ["PLR2004", "S101", "TID252"] [tool.ruff.isort] known-first-party = ["duckypad_twitch"] @@ -128,10 +157,6 @@ known-first-party = ["duckypad_twitch"] [tool.ruff.flake8-tidy-imports] ban-relative-imports = "all" -[tool.ruff.per-file-ignores] -# Tests can use magic values, assertions, and relative imports -"tests/**/*" = ["PLR2004", "S101", "TID252"] - [tool.coverage.run] source_pkgs = ["duckypad_twitch", "tests"] branch = true @@ -143,4 +168,4 @@ duckypad_twitch = ["duckypad_twitch", "*/duckypad-twitch/duckypad_twitch"] tests = ["tests", "*/duckypad-twitch/tests"] [tool.coverage.report] -exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"] +exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"] \ No newline at end of file