Last active 2 months ago

Use this script to sync streamlabs scene switches to Voicemeeter states.

onyx_online's Avatar onyx_online revised this gist 2 months ago. Go to revision

1 file changed, 130 insertions

vm_streamlabs_sync.py(file created)

@@ -0,0 +1,130 @@
1 + import asyncio
2 + import logging
3 + from collections import namedtuple
4 +
5 + import voicemeeterlib
6 + from pyslobs import ScenesService, config_from_ini_else_stdin, connection
7 +
8 +
9 + class Observer:
10 + """
11 + Observer class to handle scene switch events and perform corresponding actions.
12 + Attributes:
13 + _vm: An instance of the virtual mixer.
14 + conn: Connection object to communicate with the Streamlabs ScenesService.
15 + Methods:
16 + __init__(vm, conn):
17 + Initializes the Observer with a virtual mixer and streamlabs connection.
18 + subscribe():
19 + Subscribes to the scene switch event from the ScenesService.
20 + on_switch_scene(_, message):
21 + Handles the scene switch event and calls the appropriate method based on the scene name.
22 + on_start():
23 + Handles actions to be performed when the scene switches to 'START_TEST'.
24 + on_brb():
25 + Handles actions to be performed when the scene switches to 'BRB_TEST'.
26 + on_end():
27 + Handles actions to be performed when the scene switches to 'END_TEST'.
28 + on_live():
29 + Handles actions to be performed when the scene switches to 'LIVE_TEST'.
30 + """
31 +
32 + def __init__(self, vm, conn):
33 + self._vm = vm
34 + self.conn = conn
35 +
36 + async def subscribe(self):
37 + await ScenesService(self.conn).scene_switched.subscribe(self.on_switch_scene)
38 +
39 + async def on_switch_scene(self, _, message):
40 + scene = message.get('name')
41 + print(f'Switching to {scene}')
42 + match scene:
43 + case 'START_TEST':
44 + await self.on_start()
45 + case 'BRB_TEST':
46 + await self.on_brb()
47 + case 'END_TEST':
48 + await self.on_end()
49 + case 'LIVE_TEST':
50 + await self.on_live()
51 + case _:
52 + pass
53 +
54 + async def on_start(self):
55 + self._vm.strip[0].mute = True
56 + self._vm.strip[1].B1 = True
57 + self._vm.strip[2].B2 = True
58 +
59 + async def on_brb(self):
60 + self._vm.strip[7].fadeto(0, 500)
61 + self._vm.bus[0].mute = True
62 +
63 + async def on_end(self):
64 + self._vm.apply(
65 + {
66 + 'strip-0': {'mute': True},
67 + 'strip-1': {'mute': True, 'B1': False},
68 + 'strip-2': {'mute': True, 'B1': False},
69 + 'vban-in-0': {'on': False},
70 + }
71 + )
72 +
73 + async def on_live(self):
74 + self._vm.strip[0].mute = False
75 + self._vm.strip[7].fadeto(-6, 500)
76 + self._vm.strip[7].A3 = True
77 + self._vm.vban.instream[0].on = True
78 +
79 +
80 + async def rotate_scenes(conn):
81 + """
82 + Rotates through specific scenes and makes each one active for a short duration.
83 + Args:
84 + conn: The connection object to the ScenesService.
85 + This function retrieves all scenes using the ScenesService, then iterates through
86 + a predefined list of scene names ('START_TEST', 'BRB_TEST', 'END_TEST', 'LIVE_TEST').
87 + For each matching scene, it makes the scene active and waits for 1 second before
88 + moving to the next scene. Finally, it closes the connection.
89 + """
90 +
91 + Scene = namedtuple('Scene', 'name id')
92 +
93 + ss = ScenesService(conn)
94 + scenes = await ss.get_scenes()
95 + for scene in (Scene(s.name, s.id) for s in scenes):
96 + if scene.name in ('START_TEST', 'BRB_TEST', 'END_TEST', 'LIVE_TEST'):
97 + await ss.make_scene_active(scene.id)
98 + await asyncio.sleep(1)
99 + conn.close()
100 +
101 +
102 + async def main():
103 + """
104 + Main asynchronous function to initialize and run the application.
105 + This function performs the following tasks:
106 + 1. Establishes a connection to the Voicemeeter API using the 'potato' version.
107 + 2. Creates a connection to Streamlabs using configuration from an INI file or standard input.
108 + 3. Initializes an observer to monitor and react to events from Voicemeeter and Streamlabs.
109 + 4. Runs background processing tasks concurrently, including:
110 + - Streamlabs connection background processing
111 + - Observer subscription to events
112 + - Streamlabs scene rotation
113 + The function uses asyncio.gather to run these tasks concurrently.
114 + Note:
115 + This function should be run within an asyncio event loop.
116 + """
117 +
118 + with voicemeeterlib.api('potato') as vm:
119 + conn = connection.SlobsConnection(config_from_ini_else_stdin())
120 + await asyncio.gather(
121 + conn.background_processing(),
122 + Observer(vm, conn).subscribe(),
123 + rotate_scenes(conn),
124 + )
125 +
126 +
127 + if __name__ == '__main__':
128 + logging.basicConfig(level=logging.INFO)
129 +
130 + asyncio.run(main())
Newer Older