aa2e44b7bb72e42a8f7634127404b0dae7102d0f
1# -*- coding: utf-8 -*-
2
3import obspython as obs
4import asyncio
5import websockets
6import threading
7from time import sleep
8
9PORT_DEFAULT = 5678
10HOSTNAME_DEFAULT = "localhost"
11
12hotkey_id_first = None
13hotkey_id_prev = None
14hotkey_id_next = None
15hotkey_id_last = None
16hotkey_id_black = None
17hotkey_id_white = None
18
19HOTKEY_NAME_FIRST = 'powerpoint_slides.first'
20HOTKEY_NAME_PREV = 'powerpoint_slides.previous'
21HOTKEY_NAME_NEXT = 'powerpoint_slides.next'
22HOTKEY_NAME_LAST = 'powerpoint_slides.last'
23HOTKEY_NAME_BLACK = 'powerpoint_slides.black'
24HOTKEY_NAME_WHITE = 'powerpoint_slides.white'
25
26HOTKEY_DESC_FIRST = 'First PowerPoint slide'
27HOTKEY_DESC_PREV = 'Previous PowerPoint slide'
28HOTKEY_DESC_NEXT = 'Next PowerPoint slide'
29HOTKEY_DESC_LAST = 'Last PowerPoint slide'
30HOTKEY_DESC_BLACK = 'Black PowerPoint slide'
31HOTKEY_DESC_WHITE = 'White PowerPoint slide'
32
33global cmd
34global attempts
35cmd = ""
36hostname = HOSTNAME_DEFAULT
37port = PORT_DEFAULT
38attempts = 0
39
40async def communicate():
41 async with websockets.connect("ws://%s:%s" % (hostname, port), ping_interval=None) as websocket:
42 global cmd
43 global attempts
44 while True:
45 if cmd:
46 try:
47 cmd_temp = cmd
48 cmd = ""
49 await websocket.send('{"action": "%s"}' % cmd_temp)
50 except websockets.ConnectionClosed as exc:
51 attempts += 1
52 if attempts == 4:
53 print("Failed to send command after {} attempts - aborting connection".format(attempts))
54 attempts = 0
55 cmd = ""
56 raise websockets.exceptions.ConnectionClosedError(1006, "Sending command failed after {} attempts".format(attempts))
57 await asyncio.sleep(0.05 + 0.5*attempts**2)
58
59def run_ws():
60 while True:
61 try:
62 asyncio.set_event_loop(asyncio.new_event_loop())
63 asyncio.get_event_loop().run_until_complete(communicate())
64 except (OSError, websockets.exceptions.ConnectionClosedError):
65 # No server available - just keep trying
66 pass
67 except Exception as e:
68 print("Failed to connect to websocket: {} - {}".format(type(e), e))
69 finally:
70 sleep(1)
71
72#------------------------------------------------------------
73# global functions for script plugins
74
75def script_load(settings):
76 global hotkey_id_first
77 global hotkey_id_prev
78 global hotkey_id_next
79 global hotkey_id_last
80 global hotkey_id_black
81 global hotkey_id_white
82
83 hotkey_id_first = register_and_load_hotkey(settings, HOTKEY_NAME_FIRST, HOTKEY_DESC_FIRST, first_slide)
84 hotkey_id_prev = register_and_load_hotkey(settings, HOTKEY_NAME_PREV, HOTKEY_DESC_PREV, prev_slide)
85 hotkey_id_next = register_and_load_hotkey(settings, HOTKEY_NAME_NEXT, HOTKEY_DESC_NEXT, next_slide)
86 hotkey_id_last = register_and_load_hotkey(settings, HOTKEY_NAME_LAST, HOTKEY_DESC_LAST, last_slide)
87 hotkey_id_black = register_and_load_hotkey(settings, HOTKEY_NAME_BLACK, HOTKEY_DESC_BLACK, black)
88 hotkey_id_white = register_and_load_hotkey(settings, HOTKEY_NAME_WHITE, HOTKEY_DESC_WHITE, white)
89
90 ws_daemon = threading.Thread(name="ws_daemon", target=run_ws)
91 ws_daemon.setDaemon(True)
92 ws_daemon.start()
93 print("Started websocket client")
94
95def script_unload():
96 obs.obs_hotkey_unregister(first_slide)
97 obs.obs_hotkey_unregister(prev_slide)
98 obs.obs_hotkey_unregister(next_slide)
99 obs.obs_hotkey_unregister(last_slide)
100 obs.obs_hotkey_unregister(black)
101 obs.obs_hotkey_unregister(white)
102
103def script_save(settings):
104 save_hotkey(settings, HOTKEY_NAME_FIRST, hotkey_id_first)
105 save_hotkey(settings, HOTKEY_NAME_PREV, hotkey_id_prev)
106 save_hotkey(settings, HOTKEY_NAME_NEXT, hotkey_id_next)
107 save_hotkey(settings, HOTKEY_NAME_LAST, hotkey_id_last)
108 save_hotkey(settings, HOTKEY_NAME_BLACK, hotkey_id_black)
109 save_hotkey(settings, HOTKEY_NAME_WHITE, hotkey_id_white)
110
111def script_description():
112 return """ppt-control client
113
114 Provides hotkeys for controlling PowerPoint slides using websockets.
115 Go to OBS settings -> Hotkeys to change hotkeys (none set by default)."""
116
117def script_defaults(settings):
118 obs.obs_data_set_default_string(settings, 'hostname', HOSTNAME_DEFAULT)
119 obs.obs_data_set_default_int(settings, 'port', PORT_DEFAULT)
120
121def script_properties():
122 props = obs.obs_properties_create()
123
124 obs.obs_properties_add_text(props, "hostname", "Hostname: ", obs.OBS_TEXT_DEFAULT)
125 obs.obs_properties_add_int(props, "port", "Port: ", 0, 9999, 1)
126 return props
127
128def script_update(settings):
129 global port
130 port = obs.obs_data_get_int(settings, "port")
131 hostname = obs.obs_data_get_string(settings, "hostname")
132
133def register_and_load_hotkey(settings, name, description, callback):
134 hotkey_id = obs.obs_hotkey_register_frontend(name, description, callback)
135 hotkey_save_array = obs.obs_data_get_array(settings, name)
136 obs.obs_hotkey_load(hotkey_id, hotkey_save_array)
137 obs.obs_data_array_release(hotkey_save_array)
138
139 return hotkey_id
140
141def save_hotkey(settings, name, hotkey_id):
142 hotkey_save_array = obs.obs_hotkey_save(hotkey_id)
143 obs.obs_data_set_array(settings, name, hotkey_save_array)
144 obs.obs_data_array_release(hotkey_save_array)
145
146#-------------------------------------
147
148def first_slide(pressed):
149 if pressed:
150 global cmd
151 cmd = "first"
152
153def prev_slide(pressed):
154 if pressed:
155 global cmd
156 cmd = "prev"
157
158def next_slide(pressed):
159 if pressed:
160 global cmd
161 cmd = "next"
162
163def last_slide(pressed):
164 if pressed:
165 global cmd
166 cmd = "last"
167
168def black(pressed):
169 if pressed:
170 global cmd
171 cmd = "black"
172
173def white(pressed):
174 if pressed:
175 global cmd
176 cmd = "white"