1import win32com.client2import pywintypes3import os4import shutil5import http_server_39 as server6#import http.server as server7import socketserver8import threading9import asyncio10import websockets11import logging, json1213logging.basicConfig()1415powerpoint = None16cache = r'''C:\Windows\Temp'''1718class Handler(server.CGIHTTPRequestHandler):19def __init__(self, *args, **kwargs):20super().__init__(*args, directory=os.path.dirname(os.path.realpath(__file__)))2122def run_http():23http_server = server.HTTPServer(("", 80), Handler)24http_server.serve_forever()2526async def first(websocket, path):27slideshow_view_first()28await websocket.send(True)2930STATE = {"value": "?", "visible": "1"}3132USERS = set()333435def state_event():36return json.dumps({"type": "state", **STATE})373839def users_event():40return json.dumps({"type": "users", "count": len(USERS)})414243async def notify_state():44STATE["value"] = str(current_slide()) + "/" + str(total_slides())45if USERS: # asyncio.wait doesn't accept an empty list46message = state_event()47await asyncio.wait([user.send(message) for user in USERS])484950async def notify_users():51if USERS: # asyncio.wait doesn't accept an empty list52message = users_event()53await asyncio.wait([user.send(message) for user in USERS])545556async def register(websocket):57USERS.add(websocket)58await notify_users()596061async def unregister(websocket):62USERS.remove(websocket)63await notify_users()646566async def ws_handle(websocket, path):67# register(websocket) sends user_event() to websocket68await register(websocket)69try:70await websocket.send(state_event())71async for message in websocket:72data = json.loads(message)73if data["action"] == "prev":74slideshow_view_previous()75await notify_state()76elif data["action"] == "next":77slideshow_view_next()78await notify_state()79elif data["action"] == "first":80slideshow_view_first()81await notify_state()82elif data["action"] == "last":83slideshow_view_last()84await notify_state()85else:86logging.error("unsupported event: {}", data)87finally:88await unregister(websocket)8990def run_ws():91start_server = websockets.serve(ws_handle, "0.0.0.0", 5678)92asyncio.get_event_loop().run_until_complete(start_server)93asyncio.get_event_loop().run_forever()9495def start_server():96STATE["value"] = current_slide()97http_daemon = threading.Thread(name="http_daemon", target=run_http)98http_daemon.setDaemon(True)99http_daemon.start()100101run_ws()102#ws_daemon = threading.Thread(name="ws_daemon", target=run_ws)103#ws_daemon.setDaemon(True)104#ws_daemon.start()105106#try:107# ws_daemon.start()108# http_daemon.start()109#except (KeyboardInterrupt, SystemExit):110# cleanup_stop_thread()111# sys.exit()112113114def get_slideshow_view():115global powerpoint116117if powerpoint is None:118powerpoint = win32com.client.Dispatch('Powerpoint.Application')119120if powerpoint is None:121return122123ssw = powerpoint.SlideShowWindows124if ssw.Count == 0:125return126127# https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slideshowwindow.view128ssv = ssw[0].View129130return ssv131132def get_activepresentation():133global powerpoint134135if powerpoint is None:136powerpoint = win32com.client.Dispatch('Powerpoint.Application')137138if powerpoint is None:139return140141activepres = powerpoint.ActivePresentation142return activepres143144def total_slides():145ssp = get_activepresentation()146if ssp:147return len(ssp.Slides)148149def current_slide():150ssv = get_slideshow_view()151if ssv:152return ssv.CurrentShowPosition153154def export(slide):155global cache156ssp = get_activepresentation()157if ssp:158for (slide, name) in [(slide, "current"), (slide+1, "next")]:159if slide < len(ssp.Slides):160ssp.Slides(slide).Export(os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + r'''0.jpg''', "JPG")161attempts = 0162while attempts < 3:163try:164os.replace(os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + r'''0.jpg''', os.path.dirname(os.path.realpath(__file__)) + r'''\\''' + name + '''.jpg''')165except:166pass167attempts += 1168else:169shutil.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'))170171def slideshow_view_first():172ssv = get_slideshow_view()173if ssv:174ssv.First()175export(ssv.CurrentShowPosition)176177def slideshow_view_previous():178ssv = get_slideshow_view()179if ssv:180ssv.Previous()181export(ssv.CurrentShowPosition)182183def slideshow_view_next():184ssv = get_slideshow_view()185if ssv:186ssv.Next()187export(ssv.CurrentShowPosition)188189190def slideshow_view_last():191ssv = get_slideshow_view()192if ssv:193ssv.Last()194export(ssv.CurrentShowPosition)195196def slideshow_view_black():197ssv = get_slideshow_view()198if ssv:199ssv.State = 3200201def slideshow_view_white():202ssv = get_slideshow_view()203if ssv:204ssv.State = 4205206def slideshow_view_normal():207ssv = get_slideshow_view()208if ssv:209ssv.State = 1210211if __name__ == "__main__":212start_server()