mirror of
https://github.com/onyx-and-iris/duckypad-twitch.git
synced 2025-01-18 04:00:48 +00:00
re-run through ruff formatter
add hatch+ruff badges
This commit is contained in:
parent
3c979b8391
commit
5b4f3753db
@ -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)
|
||||
|
||||
---
|
||||
|
||||
|
76
__main__.py
76
__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()
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
@ -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.')
|
||||
|
@ -4,7 +4,7 @@ from dataclasses import dataclass
|
||||
@dataclass
|
||||
class StreamState:
|
||||
is_live: bool = False
|
||||
current_scene: str = ""
|
||||
current_scene: str = ''
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -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 = ''
|
||||
|
145
pyproject.toml
145
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
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user