1import win32com.client
2import pywintypes
3import os
4import shutil
5import http_server_39 as server
6#import http.server as server
7import socketserver
8import threading
9import asyncio
10import websockets
11import logging, json
12
13logging.basicConfig()
14
15powerpoint = None
16cache = r'''C:\Windows\Temp'''
17
18class Handler(server.CGIHTTPRequestHandler):
19 def __init__(self, *args, **kwargs):
20 super().__init__(*args, directory=os.path.dirname(os.path.realpath(__file__)))
21
22def run_http():
23 http_server = server.HTTPServer(("", 80), Handler)
24 http_server.serve_forever()
25
26async def first(websocket, path):
27 slideshow_view_first()
28 await websocket.send(True)
29
30STATE = {"value": "?", "visible": "1"}
31
32USERS = set()
33
34
35def state_event():
36 return json.dumps({"type": "state", **STATE})
37
38
39def users_event():
40 return json.dumps({"type": "users", "count": len(USERS)})
41
42
43async def notify_state():
44 STATE["value"] = str(current_slide()) + "/" + str(total_slides())
45 if USERS: # asyncio.wait doesn't accept an empty list
46 message = state_event()
47 await asyncio.wait([user.send(message) for user in USERS])
48
49
50async def notify_users():
51 if USERS: # asyncio.wait doesn't accept an empty list
52 message = users_event()
53 await asyncio.wait([user.send(message) for user in USERS])
54
55
56async def register(websocket):
57 USERS.add(websocket)
58 await notify_users()
59
60
61async def unregister(websocket):
62 USERS.remove(websocket)
63 await notify_users()
64
65
66async def ws_handle(websocket, path):
67 # register(websocket) sends user_event() to websocket
68 await register(websocket)
69 try:
70 await websocket.send(state_event())
71 async for message in websocket:
72 data = json.loads(message)
73 if data["action"] == "prev":
74 slideshow_view_previous()
75 await notify_state()
76 elif data["action"] == "next":
77 slideshow_view_next()
78 await notify_state()
79 elif data["action"] == "first":
80 slideshow_view_first()
81 await notify_state()
82 elif data["action"] == "last":
83 slideshow_view_last()
84 await notify_state()
85 else:
86 logging.error("unsupported event: {}", data)
87 finally:
88 await unregister(websocket)
89
90def run_ws():
91 start_server = websockets.serve(ws_handle, "0.0.0.0", 5678)
92 asyncio.get_event_loop().run_until_complete(start_server)
93 asyncio.get_event_loop().run_forever()
94
95def start_server():
96 STATE["value"] = current_slide()
97 http_daemon = threading.Thread(name="http_daemon", target=run_http)
98 http_daemon.setDaemon(True)
99 http_daemon.start()
100
101 run_ws()
102 #ws_daemon = threading.Thread(name="ws_daemon", target=run_ws)
103 #ws_daemon.setDaemon(True)
104 #ws_daemon.start()
105
106 #try:
107 # ws_daemon.start()
108 # http_daemon.start()
109 #except (KeyboardInterrupt, SystemExit):
110 # cleanup_stop_thread()
111 # sys.exit()
112
113
114def get_slideshow_view():
115 global powerpoint
116
117 if powerpoint is None:
118 powerpoint = win32com.client.Dispatch('Powerpoint.Application')
119
120 if powerpoint is None:
121 return
122
123 ssw = powerpoint.SlideShowWindows
124 if ssw.Count == 0:
125 return
126
127 # https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slideshowwindow.view
128 ssv = ssw[0].View
129
130 return ssv
131
132def get_activepresentation():
133 global powerpoint
134
135 if powerpoint is None:
136 powerpoint = win32com.client.Dispatch('Powerpoint.Application')
137
138 if powerpoint is None:
139 return
140
141 activepres = powerpoint.ActivePresentation
142 return activepres
143
144def total_slides():
145 ssp = get_activepresentation()
146 if ssp:
147 return len(ssp.Slides)
148
149def current_slide():
150 ssv = get_slideshow_view()
151 if ssv:
152 return ssv.CurrentShowPosition
153
154def export(slide):
155 global cache
156 ssp = get_activepresentation()
157 if ssp:
158 for (slide, name) in [(slide, "current"), (slide+1, "next")]:
159 if slide < len(ssp.Slides):
160 ssp.Slides(slide).Export(os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + r'''0.jpg''', "JPG")
161 attempts = 0
162 while attempts < 3:
163 try:
164 os.replace(os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + r'''0.jpg''', os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + '''.jpg''')
165 except:
166 pass
167 attempts += 1
168 else:
169 shutil.copyfileobj(open(os.path.dirname(os.path.realpath(__file__)) + r'''\blank.jpg''', 'rb'), open(os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + r'''next.jpg''', 'wb'))
170
171def slideshow_view_first():
172 ssv = get_slideshow_view()
173 if ssv:
174 ssv.First()
175 export(ssv.CurrentShowPosition)
176
177def slideshow_view_previous():
178 ssv = get_slideshow_view()
179 if ssv:
180 ssv.Previous()
181 export(ssv.CurrentShowPosition)
182
183def slideshow_view_next():
184 ssv = get_slideshow_view()
185 if ssv:
186 ssv.Next()
187 export(ssv.CurrentShowPosition)
188
189
190def slideshow_view_last():
191 ssv = get_slideshow_view()
192 if ssv:
193 ssv.Last()
194 export(ssv.CurrentShowPosition)
195
196def slideshow_view_black():
197 ssv = get_slideshow_view()
198 if ssv:
199 ssv.State = 3
200
201def slideshow_view_white():
202 ssv = get_slideshow_view()
203 if ssv:
204 ssv.State = 4
205
206def slideshow_view_normal():
207 ssv = get_slideshow_view()
208 if ssv:
209 ssv.State = 1
210
211if __name__ == "__main__":
212 start_server()