<head>\r
<link href="style.css" rel="stylesheet" type="text/css" media="all" />\r
<script src="settings.js"></script>\r
+ <meta name="viewport" content="width=device-width, initial-scale=1" />\r
<title>ppt-control</title>\r
</head>\r
<body onload="initSettings();">\r
<img class="icon" id="prev" src="icons/left.svg" />\r
<img class="icon" id="next" src="icons/right.svg" />\r
<img class="icon" id="last" src="icons/last.svg" />\r
- <span id="count"><span id="slide_label">Current: </span><input type="text" id="current"></input>/<span id="total">?</span></span>\r
+ <span id="count"><span id="slide_label">Current: </span><input type="text" id="current"></input>/<span id="total">?</span></span>\r
<button id="black">Black</button>\r
<button id="white">White</button>\r
</p>\r
</div>\r
</div>\r
\r
+ <img id="preload_img" style="display: none;" />\r
<script src="ppt-control.js"></script>\r
\r
</body>\r
+++ /dev/null
-# -*- coding: utf-8 -*-\r
-\r
-import obspython as obs\r
-# pip install pywin32\r
-import win32com.client\r
-import pywintypes\r
-import os\r
-import shutil\r
-import http_server_39 as server\r
-#import http.server as server\r
-import socketserver\r
-import threading\r
-import functools\r
-\r
-powerpoint = None\r
-hotkey_id_frst = None\r
-hotkey_id_prev = None\r
-hotkey_id_next = None\r
-hotkey_id_last = None\r
-hotkey_id_black = None\r
-\r
-HOTKEY_NAME_FRST = 'powerpoint_slides.first'\r
-HOTKEY_NAME_PREV = 'powerpoint_slides.previous'\r
-HOTKEY_NAME_NEXT = 'powerpoint_slides.next'\r
-HOTKEY_NAME_LAST = 'powerpoint_slides.last'\r
-HOTKEY_NAME_BLACK = 'powerpoint_slides.black'\r
-HOTKEY_NAME_WHITE = 'powerpoint_slides.white'\r
-\r
-HOTKEY_DESC_FRST = 'First PowerPoint slide'\r
-HOTKEY_DESC_PREV = 'Previous PowerPoint slide'\r
-HOTKEY_DESC_NEXT = 'Next PowerPoint slide'\r
-HOTKEY_DESC_LAST = 'Last PowerPoint slide'\r
-HOTKEY_DESC_BLACK = 'Black PowerPoint slide'\r
-HOTKEY_DESC_WHITE = 'White PowerPoint slide'\r
-\r
-class Handler(server.CGIHTTPRequestHandler):\r
- def __init__(self, *args, **kwargs):\r
- super().__init__(*args, directory=os.path.dirname(os.path.realpath(__file__)))\r
-\r
-def run_http():\r
- httpd = server.HTTPServer(("", 8000), Handler)\r
- httpd.serve_forever()\r
- \r
-\r
-\r
-\r
-# ------------------------------------------------------------\r
-# global functions for script plugins\r
-\r
-def script_load(settings):\r
- global hotkey_id_frst\r
- global hotkey_id_prev\r
- global hotkey_id_next\r
- global hotkey_id_last\r
- global hotkey_id_black\r
- global hotkey_id_white\r
-\r
- hotkey_id_frst = register_and_load_hotkey(settings, HOTKEY_NAME_FRST, HOTKEY_DESC_FRST, slideshow_view_first)\r
- hotkey_id_prev = register_and_load_hotkey(settings, HOTKEY_NAME_PREV, HOTKEY_DESC_PREV, slideshow_view_previous)\r
- hotkey_id_next = register_and_load_hotkey(settings, HOTKEY_NAME_NEXT, HOTKEY_DESC_NEXT, slideshow_view_next)\r
- hotkey_id_last = register_and_load_hotkey(settings, HOTKEY_NAME_LAST, HOTKEY_DESC_LAST, slideshow_view_last)\r
- hotkey_id_black = register_and_load_hotkey(settings, HOTKEY_NAME_BLACK, HOTKEY_DESC_BLACK, slideshow_view_black)\r
- hotkey_id_white = register_and_load_hotkey(settings, HOTKEY_NAME_WHITE, HOTKEY_DESC_WHITE, slideshow_view_white)\r
-\r
- daemon = threading.Thread(name="daemon_server", target=run_http)\r
- daemon.setDaemon(True)\r
- daemon.start()\r
-\r
-def script_unload():\r
- obs.obs_hotkey_unregister(slideshow_view_first)\r
- obs.obs_hotkey_unregister(slideshow_view_previous)\r
- obs.obs_hotkey_unregister(slideshow_view_next)\r
- obs.obs_hotkey_unregister(slideshow_view_last)\r
- obs.obs_hotkey_unregister(slideshow_view_black)\r
- obs.obs_hotkey_unregister(slideshow_view_white)\r
-\r
-def script_save(settings):\r
- save_hotkey(settings, HOTKEY_NAME_FRST, hotkey_id_frst)\r
- save_hotkey(settings, HOTKEY_NAME_PREV, hotkey_id_prev)\r
- save_hotkey(settings, HOTKEY_NAME_NEXT, hotkey_id_next)\r
- save_hotkey(settings, HOTKEY_NAME_LAST, hotkey_id_last)\r
- save_hotkey(settings, HOTKEY_NAME_BLACK, hotkey_id_black)\r
- save_hotkey(settings, HOTKEY_NAME_WHITE, hotkey_id_white)\r
-\r
-def script_description():\r
- return 'Navigate Powerpoint Slides.'\r
-\r
-def script_defaults(settings):\r
- obs.obs_data_set_default_string(settings, 'cache', r'''C:\Windows\Temp''')\r
-\r
-def script_properties():\r
- props = obs.obs_properties_create()\r
-\r
- obs.obs_properties_add_path(props, "cache", "Slide cache: ", obs.OBS_PATH_DIRECTORY, "*.jpg", r'''C:\Windows\Temp''')\r
- return props\r
-\r
-def script_update(settings):\r
- global cache\r
- cache = obs.obs_data_get_string(settings, "cache").replace("/", "\\")\r
-\r
-def register_and_load_hotkey(settings, name, description, callback):\r
- hotkey_id = obs.obs_hotkey_register_frontend(name, description, callback)\r
- hotkey_save_array = obs.obs_data_get_array(settings, name)\r
- obs.obs_hotkey_load(hotkey_id, hotkey_save_array)\r
- obs.obs_data_array_release(hotkey_save_array)\r
-\r
- return hotkey_id\r
-\r
-def save_hotkey(settings, name, hotkey_id):\r
- hotkey_save_array = obs.obs_hotkey_save(hotkey_id)\r
- obs.obs_data_set_array(settings, name, hotkey_save_array)\r
- obs.obs_data_array_release(hotkey_save_array)\r
-\r
-#-------------------------------------\r
-\r
-def get_slideshow_view():\r
- global powerpoint\r
-\r
- if powerpoint is None:\r
- powerpoint = win32com.client.Dispatch('Powerpoint.Application')\r
-\r
- if powerpoint is None:\r
- return\r
-\r
- ssw = powerpoint.SlideShowWindows\r
- if ssw.Count == 0:\r
- return\r
-\r
- # https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slideshowwindow.view\r
- ssv = ssw[0].View\r
-\r
- return ssv\r
-\r
-def get_activepresentation():\r
- global powerpoint\r
-\r
- if powerpoint is None:\r
- powerpoint = win32com.client.Dispatch('Powerpoint.Application')\r
-\r
- if powerpoint is None:\r
- return\r
-\r
- activepres = powerpoint.ActivePresentation\r
- return activepres\r
-\r
-def export_next(slide):\r
- global cache\r
- ssp = get_activepresentation()\r
- if ssp:\r
- if slide < len(ssp.Slides):\r
- ssp.Slides(slide + 1).Export(cache + r'''\slide0.jpg''', "JPG")\r
- attempts = 0\r
- while attempts < 3:\r
- try:\r
- os.replace(cache + r'''\slide0.jpg''', cache + r'''\slide.jpg''')\r
- except:\r
- pass\r
- attempts += 1\r
- else:\r
- shutil.copyfileobj(open(os.path.dirname(os.path.realpath(__file__)) + r'''\blank.jpg''', 'rb'), open(cache + r'''\slide.jpg''', 'wb'))\r
-\r
-def slideshow_view_first(pressed):\r
- if pressed:\r
- ssv = get_slideshow_view()\r
- if ssv:\r
- ssv.First()\r
- ssv.State = 1\r
-\r
-def slideshow_view_previous(pressed):\r
- if pressed:\r
- ssv = get_slideshow_view()\r
- if ssv:\r
- ssv.Previous()\r
- ssv.State = 1\r
- export_next(ssv.CurrentShowPosition)\r
-\r
-def slideshow_view_next(pressed):\r
- if pressed:\r
- ssv = get_slideshow_view()\r
- if ssv:\r
- ssv.Next()\r
- ssv.State = 1\r
- export_next(ssv.CurrentShowPosition)\r
- \r
-\r
-def slideshow_view_last(pressed):\r
- if pressed:\r
- ssv = get_slideshow_view()\r
- if ssv:\r
- ssv.Last()\r
- ssv.State = 1\r
-\r
-def slideshow_view_black(pressed):\r
- if pressed:\r
- ssv = get_slideshow_view()\r
- if ssv:\r
- if ssv.State == 3 or ssv.State == 4:\r
- ssv.State = 1\r
- else:\r
- ssv.State = 3\r
-\r
-def slideshow_view_white(pressed):\r
- if pressed:\r
- ssv = get_slideshow_view()\r
- if ssv:\r
- if ssv.State == 4 or ssv.State == 3:\r
- ssv.State = 1\r
- else:\r
- ssv.State = 4\r
+var DEFAULT_TITLE = "ppt-control"
var preloaded = false;
+var preload = [];
function imageRefresh(id) {
img = document.getElementById(id);
}
show_next.onclick = sync_next;
+function sync_shortcuts() {
+ saveSettings();
+}
+shortcuts.onclick = sync_shortcuts;
+
function set_control_width() {
var width = window.innerWidth
|| document.documentElement.clientWidth
}
});
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+function disconnect() {
+ document.title = DEFAULT_TITLE;
+ current_img.src = "/black.jpg";
+ next_img.src = "/black.jpg";
+ users.textContent = "Connection to PowerPoint failed";
+}
+
websocket.onmessage = function (event) {
+ console.log("Received data");
data = JSON.parse(event.data);
switch (data.type) {
case 'state':
+ if (data.connected == "0" || data.connected == 0) {
+ console.log("Disconnected");
+ disconnect();
+ break;
+ }
var d = new Date;
switch (data.visible) {
case 3:
console.error(
"unsupported event", data);
}
- if (!preloaded) {
- var i = 0
- var preload = [];
+ if (preloaded == false && ! isNaN(total.textContent)) {
+ image = document.getElementById("preload_img");
for (let i=1; i<=Number(total.textContent); i++) {
- image = new Image();
image.src = "/cache/" + i + ".jpg";
preload.push(image);
- console.log("Preloaded image " + i);
+ console.log("Preloaded " + total.textContent);
+ //sleep(0.5)
}
preloaded = true;
}
var interval = setInterval(refresh, 5000);
function refresh() {
+ console.log("Refreshing")
websocket.send(JSON.stringify({action: 'refresh'}));
}
import posixpath\r
import time\r
import pythoncom\r
+import pystray\r
+import tkinter as tk\r
+from tkinter import ttk\r
+from PIL import Image, ImageDraw\r
\r
logging.basicConfig()\r
\r
+global STATE\r
+global STATE_DEFAULT\r
global current_slideshow\r
current_slideshow = None\r
CACHEDIR = r'''C:\Windows\Temp\ppt-cache'''\r
if len(words) > 0 and words[0] == "cache":\r
if current_slideshow:\r
path = CACHEDIR + "\\" + current_slideshow.name()\r
+ else:\r
+ path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "black.jpg") + '/'\r
+ return path\r
words.pop(0)\r
else:\r
path = self.directory\r
http_server = server.HTTPServer(("", 80), Handler)\r
http_server.serve_forever()\r
\r
-STATE = {"connected": 0, "current": 0, "total": 0, "visible": 0, "name": ""}\r
+STATE_DEFAULT = {"connected": 0, "current": 0, "total": 0, "visible": 0, "name": ""}\r
+STATE = STATE_DEFAULT\r
USERS = set()\r
\r
\r
def state_event():\r
+ print("Running state event")\r
return json.dumps({"type": "state", **STATE})\r
\r
\r
\r
\r
async def notify_state():\r
- global current_slideshow\r
- if current_slideshow:\r
+ print("Notifying state to " + str(len(USERS)) + " users")\r
+ global STATE\r
+ if current_slideshow and STATE["connected"] == 1:\r
STATE["current"] = current_slideshow.current_slide()\r
STATE["total"] = current_slideshow.total_slides()\r
STATE["visible"] = current_slideshow.visible()\r
STATE["name"] = current_slideshow.name()\r
+ else:\r
+ STATE = STATE_DEFAULT\r
if USERS: # asyncio.wait doesn't accept an empty list\r
message = state_event()\r
await asyncio.wait([user.send(message) for user in USERS])\r
\r
\r
async def ws_handle(websocket, path):\r
+ print("Received command")\r
global current_slideshow\r
# register(websocket) sends user_event() to websocket\r
await register(websocket)\r
current_slideshow.goto(int(data["value"]))\r
await notify_state()\r
elif data["action"] == "refresh":\r
+ print("Received refresh command")\r
+ await notify_state()\r
if current_slideshow:\r
current_slideshow.export_current_next()\r
current_slideshow.refresh()\r
- await notify_state()\r
else:\r
logging.error("unsupported event: {}", data)\r
finally:\r
await unregister(websocket)\r
\r
def run_ws():\r
+ # https://stackoverflow.com/questions/21141217/how-to-launch-win32-applications-in-separate-threads-in-python/22619084#22619084\r
+ # https://www.reddit.com/r/learnpython/comments/mwt4qi/pywintypescom_error_2147417842_the_application/\r
pythoncom.CoInitializeEx(pythoncom.COINIT_MULTITHREADED)\r
asyncio.set_event_loop(asyncio.new_event_loop())\r
- start_server = websockets.serve(ws_handle, "0.0.0.0", 5678)\r
+ start_server = websockets.serve(ws_handle, "0.0.0.0", 5678, ping_interval=None)\r
asyncio.get_event_loop().run_until_complete(start_server)\r
asyncio.get_event_loop().run_forever()\r
\r
if self.instance.ActivePresentation is None:\r
raise ValueError("PPT instance has no active presentation")\r
self.presentation = self.instance.ActivePresentation\r
- \r
- self.export_all()\r
+\r
+ def unload(self):\r
+ connect_ppt()\r
\r
def refresh(self):\r
- if self.instance is None:\r
- raise ValueError("PPT instance cannot be None")\r
+ try:\r
+ if self.instance is None:\r
+ raise ValueError("PPT instance cannot be None")\r
\r
- #if self.instance.SlideShowWindows.Count == 0:\r
- # raise ValueError("PPT instance has no slideshow windows")\r
- self.view = self.instance.SlideShowWindows[0].View\r
+ if self.instance.SlideShowWindows.Count == 0:\r
+ raise ValueError("PPT instance has no slideshow windows")\r
+ self.view = self.instance.SlideShowWindows[0].View\r
\r
- if self.instance.ActivePresentation is None:\r
- raise ValueError("PPT instance has no active presentation")\r
+ if self.instance.ActivePresentation is None:\r
+ raise ValueError("PPT instance has no active presentation")\r
+ except:\r
+ self.unload()\r
\r
def total_slides(self):\r
- return len(self.presentation.Slides)\r
+ try:\r
+ self.refresh()\r
+ return len(self.presentation.Slides)\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def current_slide(self):\r
- return self.view.CurrentShowPosition\r
+ try:\r
+ self.refresh()\r
+ return self.view.CurrentShowPosition\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def visible(self):\r
- return self.view.State\r
+ try:\r
+ self.refresh()\r
+ return self.view.State\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def prev(self):\r
- self.refresh()\r
- self.view.Previous()\r
+ try:\r
+ self.refresh()\r
+ self.view.Previous()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def next(self):\r
- self.refresh()\r
- self.view.Next()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ self.view.Next()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def first(self):\r
- self.refresh()\r
- self.view.First()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ self.view.First()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def last(self):\r
- self.refresh()\r
- self.view.Last()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ self.view.Last()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def goto(self, slide):\r
- self.refresh()\r
- if slide <= self.total_slides():\r
- self.view.GotoSlide(slide)\r
- else:\r
- self.last()\r
- self.next()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ if slide <= self.total_slides():\r
+ self.view.GotoSlide(slide)\r
+ else:\r
+ self.last()\r
+ self.next()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def black(self):\r
- self.refresh()\r
- self.view.State = 3\r
+ try:\r
+ self.refresh()\r
+ self.view.State = 3\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def white(self):\r
- self.refresh()\r
- self.view.State = 4\r
+ try:\r
+ self.refresh()\r
+ self.view.State = 4\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def normal(self):\r
- self.refresh()\r
- self.view.State = 1\r
+ try:\r
+ self.refresh()\r
+ self.view.State = 1\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def name(self):\r
- return self.presentation.Name\r
+ try:\r
+ self.refresh()\r
+ return self.presentation.Name\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
+\r
\r
def export_current_next(self):\r
self.export(self.current_slide())\r
while attempts < 3:\r
try:\r
self.presentation.Slides(slide).Export(destination, "JPG")\r
- time.sleep(0.5)\r
break\r
except:\r
pass\r
def get_current_slideshow():\r
return current_slideshow\r
\r
-\r
-if __name__ == "__main__":\r
-\r
- start_server()\r
- \r
+def connect_ppt():\r
+ global STATE\r
+ if STATE["connected"] == 1:\r
+ print("Disconnected from PowerPoint instance")\r
+ STATE = STATE_DEFAULT\r
while True:\r
- # Check if PowerPoint is running\r
- instance = get_ppt_instance()\r
try:\r
+ instance = get_ppt_instance()\r
+ global current_slideshow\r
current_slideshow = Slideshow(instance)\r
STATE["connected"] = 1\r
STATE["current"] = current_slideshow.current_slide()\r
STATE["total"] = current_slideshow.total_slides()\r
print("Connected to PowerPoint instance")\r
+ current_slideshow.export_all()\r
break\r
except ValueError as e:\r
current_slideshow = None\r
pass\r
time.sleep(1)\r
+\r
+def start(_=None):\r
+ #root = tk.Tk()\r
+ #root.iconphoto(False, tk.PhotoImage(file="icons/ppt.png"))\r
+ #root.geometry("250x150+300+300")\r
+ #app = Interface(root)\r
+ #interface_thread = threading.Thread(target=root.mainloop())\r
+ #interface_thread.setDaemon(True)\r
+ #interface_thread.start()\r
+ start_server()\r
+ connect_ppt()\r
+ \r
+\r
+def null_action():\r
+ pass\r
+\r
+class Interface(ttk.Frame):\r
+\r
+ def __init__(self, parent):\r
+ ttk.Frame.__init__(self, parent)\r
+\r
+ self.parent = parent\r
+\r
+ self.initUI()\r
+\r
+ def initUI(self):\r
+\r
+ self.parent.title("ppt-control")\r
+ self.style = ttk.Style()\r
+ #self.style.theme_use("default")\r
+\r
+ self.pack(fill=tk.BOTH, expand=1)\r
+\r
+ quitButton = ttk.Button(self, text="Close",\r
+ command=self.quit)\r
+ quitButton.place(x=50, y=50)\r
+ status_label = ttk.Label(self, text="PowerPoint status: not detected")\r
+ status_label.place(x=10,y=10)\r
+ \r
+ \r
+\r
+def show_icon():\r
+ menu = (pystray.MenuItem("Status", lambda: null_action(), enabled=False),\r
+ pystray.MenuItem("Restart", lambda: start()),\r
+ pystray.MenuItem("Settings", lambda: open_settings()))\r
+ icon = pystray.Icon("ppt-control", Image.open("icons/ppt.ico"), "ppt-control", menu)\r
+ icon.visible = True\r
+ icon.run(setup=start)\r
+\r
+if __name__ == "__main__":\r
+ show_icon()\r
--- /dev/null
+# -*- coding: utf-8 -*-\r
+\r
+import obspython as obs\r
+import asyncio\r
+import websockets\r
+import threading\r
+\r
+hotkey_id_first = None\r
+hotkey_id_prev = None\r
+hotkey_id_next = None\r
+hotkey_id_last = None\r
+hotkey_id_black = None\r
+hotkey_id_white = None\r
+\r
+HOTKEY_NAME_FIRST = 'powerpoint_slides.first'\r
+HOTKEY_NAME_PREV = 'powerpoint_slides.previous'\r
+HOTKEY_NAME_NEXT = 'powerpoint_slides.next'\r
+HOTKEY_NAME_LAST = 'powerpoint_slides.last'\r
+HOTKEY_NAME_BLACK = 'powerpoint_slides.black'\r
+HOTKEY_NAME_WHITE = 'powerpoint_slides.white'\r
+\r
+HOTKEY_DESC_FIRST = 'First PowerPoint slide'\r
+HOTKEY_DESC_PREV = 'Previous PowerPoint slide'\r
+HOTKEY_DESC_NEXT = 'Next PowerPoint slide'\r
+HOTKEY_DESC_LAST = 'Last PowerPoint slide'\r
+HOTKEY_DESC_BLACK = 'Black PowerPoint slide'\r
+HOTKEY_DESC_WHITE = 'White PowerPoint slide'\r
+\r
+global cmd \r
+cmd = ""\r
+\r
+async def communicate():\r
+ global cmd\r
+ async with websockets.connect("ws://10.0.0.93:5678", ping_interval=None) as websocket:\r
+ while True:\r
+ if cmd:\r
+ await websocket.send("{\"action\": \"" + cmd + "\"}")\r
+ cmd = ""\r
+ await asyncio.sleep(0.05)\r
+\r
+def run_ws():\r
+ asyncio.set_event_loop(asyncio.new_event_loop())\r
+ asyncio.get_event_loop().run_until_complete(communicate())\r
+\r
+\r
+\r
+#------------------------------------------------------------\r
+# global functions for script plugins\r
+\r
+def script_load(settings):\r
+ global hotkey_id_first\r
+ global hotkey_id_prev\r
+ global hotkey_id_next\r
+ global hotkey_id_last\r
+ global hotkey_id_black\r
+ global hotkey_id_white\r
+\r
+ hotkey_id_first = register_and_load_hotkey(settings, HOTKEY_NAME_FIRST, HOTKEY_DESC_FIRST, first_slide)\r
+ hotkey_id_prev = register_and_load_hotkey(settings, HOTKEY_NAME_PREV, HOTKEY_DESC_PREV, prev_slide)\r
+ hotkey_id_next = register_and_load_hotkey(settings, HOTKEY_NAME_NEXT, HOTKEY_DESC_NEXT, next_slide)\r
+ hotkey_id_last = register_and_load_hotkey(settings, HOTKEY_NAME_LAST, HOTKEY_DESC_LAST, last_slide)\r
+ hotkey_id_black = register_and_load_hotkey(settings, HOTKEY_NAME_BLACK, HOTKEY_DESC_BLACK, black)\r
+ hotkey_id_white = register_and_load_hotkey(settings, HOTKEY_NAME_WHITE, HOTKEY_DESC_WHITE, white)\r
+ ws_daemon = threading.Thread(name="ws_daemon", target=run_ws)\r
+ ws_daemon.setDaemon(True)\r
+ ws_daemon.start()\r
+ print("Started websocket client")\r
+\r
+def script_unload():\r
+ obs.obs_hotkey_unregister(first_slide)\r
+ obs.obs_hotkey_unregister(prev_slide)\r
+ obs.obs_hotkey_unregister(next_slide)\r
+ obs.obs_hotkey_unregister(last_slide)\r
+ obs.obs_hotkey_unregister(black)\r
+ obs.obs_hotkey_unregister(white)\r
+\r
+def script_save(settings):\r
+ save_hotkey(settings, HOTKEY_NAME_FIRST, hotkey_id_first)\r
+ save_hotkey(settings, HOTKEY_NAME_PREV, hotkey_id_prev)\r
+ save_hotkey(settings, HOTKEY_NAME_NEXT, hotkey_id_next)\r
+ save_hotkey(settings, HOTKEY_NAME_LAST, hotkey_id_last)\r
+ save_hotkey(settings, HOTKEY_NAME_BLACK, hotkey_id_black)\r
+ save_hotkey(settings, HOTKEY_NAME_WHITE, hotkey_id_white)\r
+\r
+def script_description():\r
+ return "ppt-control client\nHotkeys for controlling PowerPoint slides using websockets"\r
+\r
+def script_defaults(settings):\r
+ obs.obs_data_set_default_int(settings, 'port', 5678)\r
+\r
+def script_properties():\r
+ props = obs.obs_properties_create()\r
+\r
+ obs.obs_properties_add_int(props, "port", "Websocket port: ", 0, 9999, 1)\r
+ return props\r
+\r
+def script_update(settings):\r
+ global port\r
+ port = obs.obs_data_get_int(settings, "port")\r
+\r
+def register_and_load_hotkey(settings, name, description, callback):\r
+ hotkey_id = obs.obs_hotkey_register_frontend(name, description, callback)\r
+ hotkey_save_array = obs.obs_data_get_array(settings, name)\r
+ obs.obs_hotkey_load(hotkey_id, hotkey_save_array)\r
+ obs.obs_data_array_release(hotkey_save_array)\r
+\r
+ return hotkey_id\r
+\r
+def save_hotkey(settings, name, hotkey_id):\r
+ hotkey_save_array = obs.obs_hotkey_save(hotkey_id)\r
+ obs.obs_data_set_array(settings, name, hotkey_save_array)\r
+ obs.obs_data_array_release(hotkey_save_array)\r
+\r
+#-------------------------------------\r
+\r
+def first_slide(pressed):\r
+ if pressed:\r
+ global cmd\r
+ cmd = "first"\r
+\r
+def prev_slide(pressed):\r
+ if pressed:\r
+ global cmd\r
+ cmd = "prev"\r
+\r
+def next_slide(pressed):\r
+ if pressed:\r
+ global cmd\r
+ cmd = "next"\r
+\r
+def last_slide(pressed):\r
+ if pressed:\r
+ global cmd\r
+ cmd = "last"\r
+\r
+def black(pressed):\r
+ if pressed:\r
+ global cmd\r
+ cmd = "black"\r
+\r
+def white(pressed):\r
+ if pressed:\r
+ global cmd\r
+ cmd = "white"\r
}
function saveSettings() {
+ console.log("Saving settings")
settingsString = JSON.stringify({showcurrent: show_current.checked, shownext: show_next.checked, enable_shortcuts: shortcuts.checked});
setCookie(COOKIENAME, settingsString, COOKIEEXP);
}
function initSettings() {
if (getCookie(COOKIENAME) == 0) {
- if (window.obssstudio) {
- shortcuts.checked = False;
- show_current.checked = False;
- }
+ if (window.obssstudio) {
+ shortcuts.checked = False;
+ show_current.checked = False;
+ }
saveSettings()
} else {
cookie = JSON.parse(getCookie(COOKIENAME));