From: Andrew Lorimer Date: Sat, 1 May 2021 07:11:33 +0000 (+1000) Subject: bugfixing, obs interface X-Git-Tag: v0.0.1~3 X-Git-Url: https://git.lorimer.id.au/ppt-control.git/diff_plain/0dafb3fe809570fed80f0e0fb06511a231201748?hp=d403fb62e2d0d54d217f6c9ae5d76ca5051855ad bugfixing, obs interface --- diff --git a/icons/ppt.ico b/icons/ppt.ico new file mode 100755 index 0000000..e3fb47b Binary files /dev/null and b/icons/ppt.ico differ diff --git a/icons/ppt.png b/icons/ppt.png new file mode 100755 index 0000000..841b67b Binary files /dev/null and b/icons/ppt.png differ diff --git a/index.html b/index.html index 5a0cec6..b19415f 100755 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ + ppt-control @@ -28,7 +29,7 @@ - Current: /? + Current: /?

@@ -41,6 +42,7 @@ + diff --git a/obs_ppt.py b/obs_ppt.py deleted file mode 100755 index a4e0c39..0000000 --- a/obs_ppt.py +++ /dev/null @@ -1,209 +0,0 @@ -# -*- coding: utf-8 -*- - -import obspython as obs -# pip install pywin32 -import win32com.client -import pywintypes -import os -import shutil -import http_server_39 as server -#import http.server as server -import socketserver -import threading -import functools - -powerpoint = None -hotkey_id_frst = None -hotkey_id_prev = None -hotkey_id_next = None -hotkey_id_last = None -hotkey_id_black = None - -HOTKEY_NAME_FRST = 'powerpoint_slides.first' -HOTKEY_NAME_PREV = 'powerpoint_slides.previous' -HOTKEY_NAME_NEXT = 'powerpoint_slides.next' -HOTKEY_NAME_LAST = 'powerpoint_slides.last' -HOTKEY_NAME_BLACK = 'powerpoint_slides.black' -HOTKEY_NAME_WHITE = 'powerpoint_slides.white' - -HOTKEY_DESC_FRST = 'First PowerPoint slide' -HOTKEY_DESC_PREV = 'Previous PowerPoint slide' -HOTKEY_DESC_NEXT = 'Next PowerPoint slide' -HOTKEY_DESC_LAST = 'Last PowerPoint slide' -HOTKEY_DESC_BLACK = 'Black PowerPoint slide' -HOTKEY_DESC_WHITE = 'White PowerPoint slide' - -class Handler(server.CGIHTTPRequestHandler): - def __init__(self, *args, **kwargs): - super().__init__(*args, directory=os.path.dirname(os.path.realpath(__file__))) - -def run_http(): - httpd = server.HTTPServer(("", 8000), Handler) - httpd.serve_forever() - - - - -# ------------------------------------------------------------ -# global functions for script plugins - -def script_load(settings): - global hotkey_id_frst - global hotkey_id_prev - global hotkey_id_next - global hotkey_id_last - global hotkey_id_black - global hotkey_id_white - - hotkey_id_frst = register_and_load_hotkey(settings, HOTKEY_NAME_FRST, HOTKEY_DESC_FRST, slideshow_view_first) - hotkey_id_prev = register_and_load_hotkey(settings, HOTKEY_NAME_PREV, HOTKEY_DESC_PREV, slideshow_view_previous) - hotkey_id_next = register_and_load_hotkey(settings, HOTKEY_NAME_NEXT, HOTKEY_DESC_NEXT, slideshow_view_next) - hotkey_id_last = register_and_load_hotkey(settings, HOTKEY_NAME_LAST, HOTKEY_DESC_LAST, slideshow_view_last) - hotkey_id_black = register_and_load_hotkey(settings, HOTKEY_NAME_BLACK, HOTKEY_DESC_BLACK, slideshow_view_black) - hotkey_id_white = register_and_load_hotkey(settings, HOTKEY_NAME_WHITE, HOTKEY_DESC_WHITE, slideshow_view_white) - - daemon = threading.Thread(name="daemon_server", target=run_http) - daemon.setDaemon(True) - daemon.start() - -def script_unload(): - obs.obs_hotkey_unregister(slideshow_view_first) - obs.obs_hotkey_unregister(slideshow_view_previous) - obs.obs_hotkey_unregister(slideshow_view_next) - obs.obs_hotkey_unregister(slideshow_view_last) - obs.obs_hotkey_unregister(slideshow_view_black) - obs.obs_hotkey_unregister(slideshow_view_white) - -def script_save(settings): - save_hotkey(settings, HOTKEY_NAME_FRST, hotkey_id_frst) - save_hotkey(settings, HOTKEY_NAME_PREV, hotkey_id_prev) - save_hotkey(settings, HOTKEY_NAME_NEXT, hotkey_id_next) - save_hotkey(settings, HOTKEY_NAME_LAST, hotkey_id_last) - save_hotkey(settings, HOTKEY_NAME_BLACK, hotkey_id_black) - save_hotkey(settings, HOTKEY_NAME_WHITE, hotkey_id_white) - -def script_description(): - return 'Navigate Powerpoint Slides.' - -def script_defaults(settings): - obs.obs_data_set_default_string(settings, 'cache', r'''C:\Windows\Temp''') - -def script_properties(): - props = obs.obs_properties_create() - - obs.obs_properties_add_path(props, "cache", "Slide cache: ", obs.OBS_PATH_DIRECTORY, "*.jpg", r'''C:\Windows\Temp''') - return props - -def script_update(settings): - global cache - cache = obs.obs_data_get_string(settings, "cache").replace("/", "\\") - -def register_and_load_hotkey(settings, name, description, callback): - hotkey_id = obs.obs_hotkey_register_frontend(name, description, callback) - hotkey_save_array = obs.obs_data_get_array(settings, name) - obs.obs_hotkey_load(hotkey_id, hotkey_save_array) - obs.obs_data_array_release(hotkey_save_array) - - return hotkey_id - -def save_hotkey(settings, name, hotkey_id): - hotkey_save_array = obs.obs_hotkey_save(hotkey_id) - obs.obs_data_set_array(settings, name, hotkey_save_array) - obs.obs_data_array_release(hotkey_save_array) - -#------------------------------------- - -def get_slideshow_view(): - global powerpoint - - if powerpoint is None: - powerpoint = win32com.client.Dispatch('Powerpoint.Application') - - if powerpoint is None: - return - - ssw = powerpoint.SlideShowWindows - if ssw.Count == 0: - return - - # https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slideshowwindow.view - ssv = ssw[0].View - - return ssv - -def get_activepresentation(): - global powerpoint - - if powerpoint is None: - powerpoint = win32com.client.Dispatch('Powerpoint.Application') - - if powerpoint is None: - return - - activepres = powerpoint.ActivePresentation - return activepres - -def export_next(slide): - global cache - ssp = get_activepresentation() - if ssp: - if slide < len(ssp.Slides): - ssp.Slides(slide + 1).Export(cache + r'''\slide0.jpg''', "JPG") - attempts = 0 - while attempts < 3: - try: - os.replace(cache + r'''\slide0.jpg''', cache + r'''\slide.jpg''') - except: - pass - attempts += 1 - else: - shutil.copyfileobj(open(os.path.dirname(os.path.realpath(__file__)) + r'''\blank.jpg''', 'rb'), open(cache + r'''\slide.jpg''', 'wb')) - -def slideshow_view_first(pressed): - if pressed: - ssv = get_slideshow_view() - if ssv: - ssv.First() - ssv.State = 1 - -def slideshow_view_previous(pressed): - if pressed: - ssv = get_slideshow_view() - if ssv: - ssv.Previous() - ssv.State = 1 - export_next(ssv.CurrentShowPosition) - -def slideshow_view_next(pressed): - if pressed: - ssv = get_slideshow_view() - if ssv: - ssv.Next() - ssv.State = 1 - export_next(ssv.CurrentShowPosition) - - -def slideshow_view_last(pressed): - if pressed: - ssv = get_slideshow_view() - if ssv: - ssv.Last() - ssv.State = 1 - -def slideshow_view_black(pressed): - if pressed: - ssv = get_slideshow_view() - if ssv: - if ssv.State == 3 or ssv.State == 4: - ssv.State = 1 - else: - ssv.State = 3 - -def slideshow_view_white(pressed): - if pressed: - ssv = get_slideshow_view() - if ssv: - if ssv.State == 4 or ssv.State == 3: - ssv.State = 1 - else: - ssv.State = 4 diff --git a/ppt-control.js b/ppt-control.js index 7e4c312..e1b8841 100644 --- a/ppt-control.js +++ b/ppt-control.js @@ -1,4 +1,6 @@ +var DEFAULT_TITLE = "ppt-control" var preloaded = false; +var preload = []; function imageRefresh(id) { img = document.getElementById(id); @@ -108,6 +110,11 @@ function sync_next() { } show_next.onclick = sync_next; +function sync_shortcuts() { + saveSettings(); +} +shortcuts.onclick = sync_shortcuts; + function set_control_width() { var width = window.innerWidth || document.documentElement.clientWidth @@ -154,10 +161,27 @@ document.addEventListener('keydown', function (e) { } }); +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: @@ -194,14 +218,13 @@ websocket.onmessage = function (event) { 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; } @@ -211,6 +234,7 @@ websocket.onmessage = function (event) { var interval = setInterval(refresh, 5000); function refresh() { + console.log("Refreshing") websocket.send(JSON.stringify({action: 'refresh'})); } diff --git a/ppt_control.py b/ppt_control.py index 75cf1b0..2e0cc3d 100755 --- a/ppt_control.py +++ b/ppt_control.py @@ -15,9 +15,15 @@ import urllib import posixpath import time import pythoncom +import pystray +import tkinter as tk +from tkinter import ttk +from PIL import Image, ImageDraw logging.basicConfig() +global STATE +global STATE_DEFAULT global current_slideshow current_slideshow = None CACHEDIR = r'''C:\Windows\Temp\ppt-cache''' @@ -50,6 +56,9 @@ class Handler(server.SimpleHTTPRequestHandler): if len(words) > 0 and words[0] == "cache": if current_slideshow: path = CACHEDIR + "\\" + current_slideshow.name() + else: + path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "black.jpg") + '/' + return path words.pop(0) else: path = self.directory @@ -67,11 +76,13 @@ def run_http(): http_server = server.HTTPServer(("", 80), Handler) http_server.serve_forever() -STATE = {"connected": 0, "current": 0, "total": 0, "visible": 0, "name": ""} +STATE_DEFAULT = {"connected": 0, "current": 0, "total": 0, "visible": 0, "name": ""} +STATE = STATE_DEFAULT USERS = set() def state_event(): + print("Running state event") return json.dumps({"type": "state", **STATE}) @@ -80,12 +91,15 @@ def users_event(): async def notify_state(): - global current_slideshow - if current_slideshow: + print("Notifying state to " + str(len(USERS)) + " users") + global STATE + if current_slideshow and STATE["connected"] == 1: STATE["current"] = current_slideshow.current_slide() STATE["total"] = current_slideshow.total_slides() STATE["visible"] = current_slideshow.visible() STATE["name"] = current_slideshow.name() + else: + STATE = STATE_DEFAULT if USERS: # asyncio.wait doesn't accept an empty list message = state_event() await asyncio.wait([user.send(message) for user in USERS]) @@ -108,6 +122,7 @@ async def unregister(websocket): async def ws_handle(websocket, path): + print("Received command") global current_slideshow # register(websocket) sends user_event() to websocket await register(websocket) @@ -150,19 +165,22 @@ async def ws_handle(websocket, path): current_slideshow.goto(int(data["value"])) await notify_state() elif data["action"] == "refresh": + print("Received refresh command") + await notify_state() if current_slideshow: current_slideshow.export_current_next() current_slideshow.refresh() - await notify_state() else: logging.error("unsupported event: {}", data) finally: await unregister(websocket) def run_ws(): + # https://stackoverflow.com/questions/21141217/how-to-launch-win32-applications-in-separate-threads-in-python/22619084#22619084 + # https://www.reddit.com/r/learnpython/comments/mwt4qi/pywintypescom_error_2147417842_the_application/ pythoncom.CoInitializeEx(pythoncom.COINIT_MULTITHREADED) asyncio.set_event_loop(asyncio.new_event_loop()) - start_server = websockets.serve(ws_handle, "0.0.0.0", 5678) + start_server = websockets.serve(ws_handle, "0.0.0.0", 5678, ping_interval=None) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() @@ -200,71 +218,120 @@ class Slideshow: if self.instance.ActivePresentation is None: raise ValueError("PPT instance has no active presentation") self.presentation = self.instance.ActivePresentation - - self.export_all() + + def unload(self): + connect_ppt() def refresh(self): - if self.instance is None: - raise ValueError("PPT instance cannot be None") + try: + if self.instance is None: + raise ValueError("PPT instance cannot be None") - #if self.instance.SlideShowWindows.Count == 0: - # raise ValueError("PPT instance has no slideshow windows") - self.view = self.instance.SlideShowWindows[0].View + if self.instance.SlideShowWindows.Count == 0: + raise ValueError("PPT instance has no slideshow windows") + self.view = self.instance.SlideShowWindows[0].View - if self.instance.ActivePresentation is None: - raise ValueError("PPT instance has no active presentation") + if self.instance.ActivePresentation is None: + raise ValueError("PPT instance has no active presentation") + except: + self.unload() def total_slides(self): - return len(self.presentation.Slides) + try: + self.refresh() + return len(self.presentation.Slides) + except ValueError or pywintypes.com_error: + self.unload() def current_slide(self): - return self.view.CurrentShowPosition + try: + self.refresh() + return self.view.CurrentShowPosition + except ValueError or pywintypes.com_error: + self.unload() def visible(self): - return self.view.State + try: + self.refresh() + return self.view.State + except ValueError or pywintypes.com_error: + self.unload() def prev(self): - self.refresh() - self.view.Previous() + try: + self.refresh() + self.view.Previous() + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def next(self): - self.refresh() - self.view.Next() - self.export_current_next() + try: + self.refresh() + self.view.Next() + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def first(self): - self.refresh() - self.view.First() - self.export_current_next() + try: + self.refresh() + self.view.First() + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def last(self): - self.refresh() - self.view.Last() - self.export_current_next() + try: + self.refresh() + self.view.Last() + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def goto(self, slide): - self.refresh() - if slide <= self.total_slides(): - self.view.GotoSlide(slide) - else: - self.last() - self.next() - self.export_current_next() + try: + self.refresh() + if slide <= self.total_slides(): + self.view.GotoSlide(slide) + else: + self.last() + self.next() + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def black(self): - self.refresh() - self.view.State = 3 + try: + self.refresh() + self.view.State = 3 + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def white(self): - self.refresh() - self.view.State = 4 + try: + self.refresh() + self.view.State = 4 + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def normal(self): - self.refresh() - self.view.State = 1 + try: + self.refresh() + self.view.State = 1 + self.export_current_next() + except ValueError or pywintypes.com_error: + self.unload() def name(self): - return self.presentation.Name + try: + self.refresh() + return self.presentation.Name + except ValueError or pywintypes.com_error: + self.unload() + def export_current_next(self): self.export(self.current_slide()) @@ -279,7 +346,6 @@ class Slideshow: while attempts < 3: try: self.presentation.Slides(slide).Export(destination, "JPG") - time.sleep(0.5) break except: pass @@ -302,22 +368,74 @@ def get_ppt_instance(): def get_current_slideshow(): return current_slideshow - -if __name__ == "__main__": - - start_server() - +def connect_ppt(): + global STATE + if STATE["connected"] == 1: + print("Disconnected from PowerPoint instance") + STATE = STATE_DEFAULT while True: - # Check if PowerPoint is running - instance = get_ppt_instance() try: + instance = get_ppt_instance() + global current_slideshow current_slideshow = Slideshow(instance) STATE["connected"] = 1 STATE["current"] = current_slideshow.current_slide() STATE["total"] = current_slideshow.total_slides() print("Connected to PowerPoint instance") + current_slideshow.export_all() break except ValueError as e: current_slideshow = None pass time.sleep(1) + +def start(_=None): + #root = tk.Tk() + #root.iconphoto(False, tk.PhotoImage(file="icons/ppt.png")) + #root.geometry("250x150+300+300") + #app = Interface(root) + #interface_thread = threading.Thread(target=root.mainloop()) + #interface_thread.setDaemon(True) + #interface_thread.start() + start_server() + connect_ppt() + + +def null_action(): + pass + +class Interface(ttk.Frame): + + def __init__(self, parent): + ttk.Frame.__init__(self, parent) + + self.parent = parent + + self.initUI() + + def initUI(self): + + self.parent.title("ppt-control") + self.style = ttk.Style() + #self.style.theme_use("default") + + self.pack(fill=tk.BOTH, expand=1) + + quitButton = ttk.Button(self, text="Close", + command=self.quit) + quitButton.place(x=50, y=50) + status_label = ttk.Label(self, text="PowerPoint status: not detected") + status_label.place(x=10,y=10) + + + +def show_icon(): + menu = (pystray.MenuItem("Status", lambda: null_action(), enabled=False), + pystray.MenuItem("Restart", lambda: start()), + pystray.MenuItem("Settings", lambda: open_settings())) + icon = pystray.Icon("ppt-control", Image.open("icons/ppt.ico"), "ppt-control", menu) + icon.visible = True + icon.run(setup=start) + +if __name__ == "__main__": + show_icon() diff --git a/ppt_control_obs.py b/ppt_control_obs.py new file mode 100755 index 0000000..d9ab70d --- /dev/null +++ b/ppt_control_obs.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- + +import obspython as obs +import asyncio +import websockets +import threading + +hotkey_id_first = None +hotkey_id_prev = None +hotkey_id_next = None +hotkey_id_last = None +hotkey_id_black = None +hotkey_id_white = None + +HOTKEY_NAME_FIRST = 'powerpoint_slides.first' +HOTKEY_NAME_PREV = 'powerpoint_slides.previous' +HOTKEY_NAME_NEXT = 'powerpoint_slides.next' +HOTKEY_NAME_LAST = 'powerpoint_slides.last' +HOTKEY_NAME_BLACK = 'powerpoint_slides.black' +HOTKEY_NAME_WHITE = 'powerpoint_slides.white' + +HOTKEY_DESC_FIRST = 'First PowerPoint slide' +HOTKEY_DESC_PREV = 'Previous PowerPoint slide' +HOTKEY_DESC_NEXT = 'Next PowerPoint slide' +HOTKEY_DESC_LAST = 'Last PowerPoint slide' +HOTKEY_DESC_BLACK = 'Black PowerPoint slide' +HOTKEY_DESC_WHITE = 'White PowerPoint slide' + +global cmd +cmd = "" + +async def communicate(): + global cmd + async with websockets.connect("ws://10.0.0.93:5678", ping_interval=None) as websocket: + while True: + if cmd: + await websocket.send("{\"action\": \"" + cmd + "\"}") + cmd = "" + await asyncio.sleep(0.05) + +def run_ws(): + asyncio.set_event_loop(asyncio.new_event_loop()) + asyncio.get_event_loop().run_until_complete(communicate()) + + + +#------------------------------------------------------------ +# global functions for script plugins + +def script_load(settings): + global hotkey_id_first + global hotkey_id_prev + global hotkey_id_next + global hotkey_id_last + global hotkey_id_black + global hotkey_id_white + + hotkey_id_first = register_and_load_hotkey(settings, HOTKEY_NAME_FIRST, HOTKEY_DESC_FIRST, first_slide) + hotkey_id_prev = register_and_load_hotkey(settings, HOTKEY_NAME_PREV, HOTKEY_DESC_PREV, prev_slide) + hotkey_id_next = register_and_load_hotkey(settings, HOTKEY_NAME_NEXT, HOTKEY_DESC_NEXT, next_slide) + hotkey_id_last = register_and_load_hotkey(settings, HOTKEY_NAME_LAST, HOTKEY_DESC_LAST, last_slide) + hotkey_id_black = register_and_load_hotkey(settings, HOTKEY_NAME_BLACK, HOTKEY_DESC_BLACK, black) + hotkey_id_white = register_and_load_hotkey(settings, HOTKEY_NAME_WHITE, HOTKEY_DESC_WHITE, white) + ws_daemon = threading.Thread(name="ws_daemon", target=run_ws) + ws_daemon.setDaemon(True) + ws_daemon.start() + print("Started websocket client") + +def script_unload(): + obs.obs_hotkey_unregister(first_slide) + obs.obs_hotkey_unregister(prev_slide) + obs.obs_hotkey_unregister(next_slide) + obs.obs_hotkey_unregister(last_slide) + obs.obs_hotkey_unregister(black) + obs.obs_hotkey_unregister(white) + +def script_save(settings): + save_hotkey(settings, HOTKEY_NAME_FIRST, hotkey_id_first) + save_hotkey(settings, HOTKEY_NAME_PREV, hotkey_id_prev) + save_hotkey(settings, HOTKEY_NAME_NEXT, hotkey_id_next) + save_hotkey(settings, HOTKEY_NAME_LAST, hotkey_id_last) + save_hotkey(settings, HOTKEY_NAME_BLACK, hotkey_id_black) + save_hotkey(settings, HOTKEY_NAME_WHITE, hotkey_id_white) + +def script_description(): + return "ppt-control client\nHotkeys for controlling PowerPoint slides using websockets" + +def script_defaults(settings): + obs.obs_data_set_default_int(settings, 'port', 5678) + +def script_properties(): + props = obs.obs_properties_create() + + obs.obs_properties_add_int(props, "port", "Websocket port: ", 0, 9999, 1) + return props + +def script_update(settings): + global port + port = obs.obs_data_get_int(settings, "port") + +def register_and_load_hotkey(settings, name, description, callback): + hotkey_id = obs.obs_hotkey_register_frontend(name, description, callback) + hotkey_save_array = obs.obs_data_get_array(settings, name) + obs.obs_hotkey_load(hotkey_id, hotkey_save_array) + obs.obs_data_array_release(hotkey_save_array) + + return hotkey_id + +def save_hotkey(settings, name, hotkey_id): + hotkey_save_array = obs.obs_hotkey_save(hotkey_id) + obs.obs_data_set_array(settings, name, hotkey_save_array) + obs.obs_data_array_release(hotkey_save_array) + +#------------------------------------- + +def first_slide(pressed): + if pressed: + global cmd + cmd = "first" + +def prev_slide(pressed): + if pressed: + global cmd + cmd = "prev" + +def next_slide(pressed): + if pressed: + global cmd + cmd = "next" + +def last_slide(pressed): + if pressed: + global cmd + cmd = "last" + +def black(pressed): + if pressed: + global cmd + cmd = "black" + +def white(pressed): + if pressed: + global cmd + cmd = "white" diff --git a/settings.js b/settings.js index eb94f8e..901af0b 100644 --- a/settings.js +++ b/settings.js @@ -25,16 +25,17 @@ function getCookie(cname) { } 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));