add config file comments, minor JS refactoring
authorAndrew Lorimer <andrew@lorimer.id.au>
Sat, 6 Nov 2021 01:07:36 +0000 (12:07 +1100)
committerAndrew Lorimer <andrew@lorimer.id.au>
Sat, 6 Nov 2021 01:07:36 +0000 (12:07 +1100)
README.md
ppt-control.ini
ppt_control/config.py
ppt_control/ppt_control.py
ppt_control/static/index.html
ppt_control/static/ppt-control.js
ppt_control/static/settings.js
ppt_control/static/style.css
setup.py
index 64a4d194a075172c0407a7c1107c2e0d69d71f96..5e3ba5f6d11e4e75b35bef39bdd92d7c29b1bec6 100755 (executable)
--- a/README.md
+++ b/README.md
@@ -68,3 +68,9 @@ Various settings can be changed in `%AppData%\ppt-control\ppt-control.ini`. This
 - ✔️ JS client show status on last slide\r
 - ✔️ Shortcut to edit config in systray menu\r
 - Export all slides on presentation init\r
 - ✔️ JS client show status on last slide\r
 - ✔️ Shortcut to edit config in systray menu\r
 - Export all slides on presentation init\r
+- Abstract pres name in class & underscore `presentation` instance\r
+- Check assertions\r
+- Use `__debug__` flag\r
+- Make constant for static dir\r
+- Comment sys.coinitflags\r
+- Die if error in run_ws or run_http
\ No newline at end of file
index a9b9bdda79f4132a972727728c4bdb0985853725..83b822e27a136da122b3c351d46aebae84417094 100644 (file)
@@ -1,14 +1,82 @@
 [Main]
 [Main]
-logging = debug
+
+# Logging level
+# Options: debug, info, warning, error, critical
+# (see https://docs.python.org/3/howto/logging.html#when-to-use-logging)
+logging = info
+
+# Cache location
+# Directory to store slide preview images for each presentation
 cache = C:\Windows\Temp\ppt-cache
 cache = C:\Windows\Temp\ppt-cache
+
+# Cache format
+# Can be set to any image filter set in the registry, but most commonly JPG or PNG
+# (see https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slide.export)
 cache_format = JPG
 cache_format = JPG
+
+# Cache timeout
+# Time after which slides should be exported again to reflect any changes made in the
+# PowerPoint editor. Set to 0 to never consider the cache stale (not recommended). Value 
+# is in seconds.
 cache_timeout = 300
 cache_timeout = 300
+
+# Cache initialisation
+# Whether to export all slides when a slideshow is started. Can be set to any falsy or
+# truthy value.
+cache_init = True
+
+# Black-white behaviour
+# Sets the behaviour when returning from black or white mode. Options: "both", "literal"
+# In PowerPoint's presenter view, if the slideshow is in black or white screen mode,
+# attempting to switch to the other mode (white/black) will result in the slideshow
+# returning to a normal (visible) state, rather than white/black. This behaviour is
+# also the default for ppt-control, and is invoked by setting "blackwhite" to "both"
+# (when the slideshow is in black or white mode, requesting either black or white mode 
+# will return the slideshow to normal). The more intuitive setting is "literal", whereby 
+# when the slideshow is in black or white mode, pressing the button corresponding to the 
+# mode that the slideshow is already in will return the slideshow to normal mode, and 
+# pressing the other button will put the slideshow into that mode explicitly.
 blackwhite = both
 
 blackwhite = both
 
+# Refresh interval
+# Describes the number of seconds between backend updates, which consist of:
+# - synchronisation of PowerPoint's events with the internal state. 
+# - updating the interface (systray icon) status
+# - check for PowerPoint files that have been opened in protected view
+# You could set a shorter value here, but these updates don't have to be instant so
+# the default value of 2 seconds should work fine.
+refresh = 2
+
+# Disable protecte view
+# If set to a truthy value, ppt-control will attempt to disable protected view on files
+# which have been opened in protected view. Obviously this introduces some security risk,
+# so if you are uncomfortable with this, leave it off and you will have to disable 
+# protected view for each file manually to be able to control it will ppt-control.
+disable_protected = True
+
 [HTTP]
 [HTTP]
+
+# HTTP interface
+# The interface to listen for HTTP requests on. Change this if you want to restrict the
+# HTTP frontend to a specific network. A blank value means all interfaces.
 interface = 
 interface = 
+
+# HTTP port
+# The port to listen for HTTP requests on. The default value of 80 is the standard 
+# port for HTTP, so when set to this value you don't need to include the port number
+# in the URL when navigating to the frontend. For all other values you will need to 
+# specify the port number in the web browser (except 443, but that's a bad idea).
 port = 80
 
 [WebSocket]
 port = 80
 
 [WebSocket]
+# WebSocket interface
+# The interface to listen for WebSocket requests on. Change this if you want to restrict
+# the WebSocket interface to a specific network. A value of 0.0.0.0 means all interfaces.
 interface = 0.0.0.0
 interface = 0.0.0.0
+
+
+# WebSocket port
+# The port to listen for WebSocket requests on. This needs to match the port specified in
+# whatever frontend you are using (e.g. in the JS code for the HTTP frontend, or in the
+# script settings for the OBS frontend).
 port = 5678
 port = 5678
index a1e90ab47ca07cbd324453ec627ab0eac1320371..61ff70fe936b616082b10dac09e47c7e33e7143e 100755 (executable)
@@ -4,83 +4,21 @@ prefs = None
 \r
 defaults = {\r
         'Main': {\r
 \r
 defaults = {\r
         'Main': {\r
-            # Logging level\r
-            # Options: debug, info, warning, error, critical\r
-            # (see https://docs.python.org/3/howto/logging.html#when-to-use-logging)\r
-            'logging': 'debug',\r
-\r
-            # Cache location\r
-            # Directory to store slide preview images for each presentation\r
+            'logging': 'info',\r
             'cache': r'''C:\Windows\Temp\ppt-cache''',\r
             'cache': r'''C:\Windows\Temp\ppt-cache''',\r
-\r
-            # Cache format\r
-            # Can be set to any image filter set in the registry, but most commonly JPG or PNG\r
-            # (see https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slide.export)\r
             'cache_format': 'JPG',\r
             'cache_format': 'JPG',\r
-\r
-            # Cache timeout\r
-            # Time after which slides should be exported again to reflect any changes made in the\r
-            # PowerPoint editor. Value is in seconds.\r
             'cache_timeout': 5*60,\r
             'cache_timeout': 5*60,\r
-\r
-            # Cache initialisation\r
-            # Whether to export all slides when a slideshow is started. Can be set to any falsy or\r
-            # truthy value.\r
             'cache_init': True,\r
             'cache_init': True,\r
-\r
-            # Black-white behaviour\r
-            # Sets the behaviour when returning from black or white mode. Options: "both", "literal"\r
-            # In PowerPoint's presenter view, if the slideshow is in black or white screen mode,\r
-            # attempting to switch to the other mode (white/black) will result in the slideshow\r
-            # returning to a normal (visible) state, rather than white/black. This behaviour is\r
-            # also the default for ppt-control, and is set by setting the value of "blackwhite"\r
-            # to "both" (since when the slideshow is in black or white mode, requesting either\r
-            # of black or white mode will return the slideshow to normal). The more intuitive\r
-            # settings is "literal", whereby when the slideshow is in black or white mode, \r
-            # pressing the button corresponding to the mode that the slideshow is already in will\r
-            # return the slideshow to normal mode, and pressing the other button will put the \r
-            # slideshow into that mode.\r
             'blackwhite': 'both',\r
             'blackwhite': 'both',\r
-\r
-            # Refresh interval\r
-            # Describes the number of seconds between backend updates, which consist of:\r
-            # - synchronisation of PowerPoint's events with the internal state. \r
-            # - updating the interface (systray icon) status\r
-            # - check for PowerPoint files that have been opened in protected view\r
-            # You could set a shorter value here, but these updates don't have to be instant so\r
-            # the default value of 2 seconds should work fine.\r
             'refresh': 2,\r
             'refresh': 2,\r
-\r
-            # Disable protecte view\r
-            # If set to a truthy value, ppt-control will attempt to disable protected view on files\r
-            # which have been opened in protected view. Obviously this introduces some security risk,\r
-            # so if you are uncomfortable with this, leave it off and you will have to disable \r
-            # protected view for each file manually to be able to control it will ppt-control.\r
             'disable_protected': True\r
         },\r
         'HTTP': {\r
             'disable_protected': True\r
         },\r
         'HTTP': {\r
-            # HTTP interface\r
-            # The interface to listen for HTTP requests on. Change this if you want to restrict the\r
-            # HTTP frontend to a specific network. A blank value means all interfaces.\r
             'interface': '',\r
             'interface': '',\r
-\r
-            # HTTP port\r
-            # The port to listen for HTTP requests on. The default value of 80 is the standard \r
-            # port for HTTP, so when set to this value you don't need to include the port number\r
-            # in the URL when navigating to the frontend. For all other values you will need to \r
-            # specify the port number in the web browser (except 443, but that's a bad idea).\r
             'port': 80\r
         },\r
         'WebSocket': {\r
             'port': 80\r
         },\r
         'WebSocket': {\r
-            # WebSocket interface\r
-            # The interface to listen for WebSocket requests on. Change this if you want to restrict\r
-            # the WebSocket interface to a specific network. A value of 0.0.0.0 means all interfaces.\r
             'interface': '0.0.0.0',\r
             'interface': '0.0.0.0',\r
-\r
-            # WebSocket port\r
-            # The port to listen for WebSocket requests on. This needs to match the port specified in\r
-            # whatever frontend you are using (e.g. in the JS code for the HTTP frontend, or in the\r
-            # script settings for the OBS frontend).\r
             'port': 5678\r
         }\r
 }\r
             'port': 5678\r
         }\r
 }\r
@@ -111,4 +49,4 @@ def export_defaults(file):
     after installation.\r
     """\r
     prefs = loadconf([])\r
     after installation.\r
     """\r
     prefs = loadconf([])\r
-    prefs.write(file, space_around_delimiters=True)
\ No newline at end of file
+    prefs.write(file, space_around_delimiters=True)\r
index e5a38c145c88338418bbc1d296ef77184f3d03c1..45e0611333fac488d1ba6ada4a2a3fffb01b32fa 100755 (executable)
@@ -514,12 +514,14 @@ class Presentation:
     def export(self, slide):\r
         """\r
         Export a relatively low-resolution image of a slide using PowerPoint's built-in export \r
     def export(self, slide):\r
         """\r
         Export a relatively low-resolution image of a slide using PowerPoint's built-in export \r
-        function. The cache destination is set in the config.\r
+        function. The cache destination is set in the config. The slide is not exported if it has \r
+        a non-stale cached file.\r
         """\r
         destination = config.prefs["Main"]["cache"] + "\\" + self.presentation.Name + "\\" + str(slide) + "." + config.prefs["Main"]["cache_format"].lower()\r
         logger.debug("Exporting slide {} of {}".format(slide, self.presentation.Name))\r
         os.makedirs(os.path.dirname(destination), exist_ok=True)\r
         """\r
         destination = config.prefs["Main"]["cache"] + "\\" + self.presentation.Name + "\\" + str(slide) + "." + config.prefs["Main"]["cache_format"].lower()\r
         logger.debug("Exporting slide {} of {}".format(slide, self.presentation.Name))\r
         os.makedirs(os.path.dirname(destination), exist_ok=True)\r
-        if not os.path.exists(destination) or time.time() - os.path.getmtime(destination) > config.prefs.getint("Main", "cache_timeout"):\r
+        if not os.path.exists(destination) or (config.prefs.getint("Main", "cache_timeout") > 0 and \r
+                time.time() - os.path.getmtime(destination) > config.prefs.getint("Main", "cache_timeout")):\r
             if slide <= self.slide_total():\r
                 attempts = 0\r
                 while attempts < 3:\r
             if slide <= self.slide_total():\r
                 attempts = 0\r
                 while attempts < 3:\r
@@ -731,10 +733,9 @@ def start_interface():
     if config.prefs["Main"]["logging"] == "debug":\r
         log_level = logging.DEBUG\r
     elif config.prefs["Main"]["logging"] == "info":\r
     if config.prefs["Main"]["logging"] == "debug":\r
         log_level = logging.DEBUG\r
     elif config.prefs["Main"]["logging"] == "info":\r
-        log_level = logging.CRITICAL\r
+        log_level = logging.INFO\r
     else:\r
         log_level = logging.WARNING\r
     else:\r
         log_level = logging.WARNING\r
-    log_level = logging.DEBUG\r
 \r
     log_formatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-7.7s]  %(message)s")\r
     logger = logging.getLogger("ppt-control")\r
 \r
     log_formatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-7.7s]  %(message)s")\r
     logger = logging.getLogger("ppt-control")\r
@@ -774,7 +775,6 @@ def start_interface():
     icon = MyIcon(PKG_NAME)\r
     icon.icon = Image.open(os.path.dirname(os.path.realpath(__file__)) + r'''\static\icons\ppt.ico''')\r
     icon.title = "{} {}".format(PKG_NAME, PKG_VERSION)\r
     icon = MyIcon(PKG_NAME)\r
     icon.icon = Image.open(os.path.dirname(os.path.realpath(__file__)) + r'''\static\icons\ppt.ico''')\r
     icon.title = "{} {}".format(PKG_NAME, PKG_VERSION)\r
-    #refresh_menu(icon)\r
     icon.visible = True\r
     icon.run(setup=start_server)\r
 \r
     icon.visible = True\r
     icon.run(setup=start_server)\r
 \r
index 0c04bc81df889319293099186325c7b54820a3c1..13c241b1ae62b62f4a2fe4119c5a96b598be4db7 100755 (executable)
                                <button id="white">White</button>\r
                        </p>\r
 \r
                                <button id="white">White</button>\r
                        </p>\r
 \r
-                       <input type="checkbox" checked="true" id="show_current">Show current slide</input>\r
-                       <input type="checkbox" checked="true" id="show_next">Show next slide</input>\r
-                       <input type="checkbox" checked="true" id="shortcuts">Keyboard shortcuts</input>\r
+                       \r
                                <p class="presentation_text">\r
                                        Presentation: <select id="presentation"></select>\r
                                <p class="presentation_text">\r
                                        Presentation: <select id="presentation"></select>\r
-                               <span class="status_text">Disconnected</span>\r
+                                       <span class="status_container">\r
+                                       <span class="status_text">Disconnected</span>\r
+                                               <button class="start">Start</button>\r
+                                               <button class="stop">Stop</button>\r
+                                       </span>\r
                                </p>\r
                                </p>\r
-                               <button class="start">Start</button>\r
-                               <button class="stop">Stop</button>\r
+                               <input type="checkbox" checked="true" id="show_current">Show current slide</input>\r
+                       <input type="checkbox" checked="true" id="show_next">Show next slide</input>\r
+                       <input type="checkbox" checked="true" id="shortcuts">Keyboard shortcuts</input>\r
                </div>\r
         </div>\r
 \r
                </div>\r
         </div>\r
 \r
index c00ca682e503558d6812f9d039ea32df5938436a..39714f6c910c88d7c2d923e2425924889b779a55 100644 (file)
@@ -1,4 +1,9 @@
-var DEFAULT_TITLE = "ppt-control"
+const DEFAULT_TITLE = "ppt-control";
+const LABEL_STOPPED = "Slideshow stopped";
+const LABEL_RUNNING = "Slideshow running";
+const LABEL_DISCONNECTED = "Disconnected";
+const LABEL_FINAL_PREFIX = "Final slide &#8208; ";
+const LABEL_END_PREFIX = "End of slideshow &#8208; ";
 var preloaded = false;
 var preload = [];
 
 var preloaded = false;
 var preload = [];
 
@@ -126,6 +131,8 @@ next_img.onclick = function (event) {
        next.click()
 }
 
        next.click()
 }
 
+window.addEventListener('resize', function(event) {set_control_width()}, true);
+
 
 function sync_current() {
     if (show_current.checked) {
 
 function sync_current() {
     if (show_current.checked) {
@@ -211,9 +218,8 @@ function refreshInterface() {
     var d = new Date;
     if (Object.keys(presentationData).length > 0) {
         currentPresentationData = presentationData[getPresentationName()];
     var d = new Date;
     if (Object.keys(presentationData).length > 0) {
         currentPresentationData = presentationData[getPresentationName()];
-        console.log("Current presentation is " + getPresentationName());
         presentation_text.style.display = "block";
         presentation_text.style.display = "block";
-        status_text.textContent = "Slideshow running";
+        status_text.innerHTML = LABEL_RUNNING;
         startBtn.style.display = "none";
         stopBtn.style.display = "block";
         if (show_current.checked) {
         startBtn.style.display = "none";
         stopBtn.style.display = "block";
         if (show_current.checked) {
@@ -230,10 +236,10 @@ function refreshInterface() {
             }
         }
         if (currentPresentationData.slide_current == currentPresentationData.slide_total) {
             }
         }
         if (currentPresentationData.slide_current == currentPresentationData.slide_total) {
-            status_text.textContent = "Slideshow running (last slide)";
+            status_text.innerHTML = LABEL_FINAL_PREFIX + LABEL_RUNNING;
         }
         if (currentPresentationData.slide_current == currentPresentationData.slide_total + 1) { 
         }
         if (currentPresentationData.slide_current == currentPresentationData.slide_total + 1) { 
-            status_text.textContent = "End of slideshow";
+            status_text.innerHTML = LABEL_END_PREFIX + LABEL_RUNNING;
             next_img.src = "/black.jpg";
         } else {
             next_img.src = "/cache/" + currentPresentationData.name + "/" + (currentPresentationData.slide_current + 1) + ".jpg?t=" + d.getTime();
             next_img.src = "/black.jpg";
         } else {
             next_img.src = "/cache/" + currentPresentationData.name + "/" + (currentPresentationData.slide_current + 1) + ".jpg?t=" + d.getTime();
@@ -241,7 +247,7 @@ function refreshInterface() {
         if (currentPresentationData.slide_current == 0) {
             current_img.src = "/black.jpg";
             next_img.src = "/black.jpg";
         if (currentPresentationData.slide_current == 0) {
             current_img.src = "/black.jpg";
             next_img.src = "/black.jpg";
-            status_text.textContent = "Slideshow not running";
+            status_text.innerHTML = LABEL_STOPPED;
             startBtn.style.display = "block";
             stopBtn.style.display = "none";
         }
             startBtn.style.display = "block";
             stopBtn.style.display = "none";
         }
@@ -261,7 +267,7 @@ function disconnect() {
     document.title = DEFAULT_TITLE;
     current_img.src = "/black.jpg";
     next_img.src = "/black.jpg";
     document.title = DEFAULT_TITLE;
     current_img.src = "/black.jpg";
     next_img.src = "/black.jpg";
-    status_text.textContent = "Disconnected";
+    status_text.innerHTML = LABEL_DISCONNECTED;
     total.textContent = "?";
     current.value = "";
     presentation_text.style.display = "none";
     total.textContent = "?";
     current.value = "";
     presentation_text.style.display = "none";
@@ -271,7 +277,6 @@ function disconnect() {
 
 function receive_message(event) {
     data = JSON.parse(event.data);
 
 function receive_message(event) {
     data = JSON.parse(event.data);
-    console.log("Received data: " + data);
     if (Object.keys(presentationData).includes(data.name)) {
         // Existing presentation
         presentationData[data.name] = {"name": data.name, "pres_open": data.pres_open, "slideshow": data.slideshow, "visible": data.visible, "slide_current": data.slide_current, "slide_total": data.slide_total, "option": presentationData[data.name].option};
     if (Object.keys(presentationData).includes(data.name)) {
         // Existing presentation
         presentationData[data.name] = {"name": data.name, "pres_open": data.pres_open, "slideshow": data.slideshow, "visible": data.visible, "slide_current": data.slide_current, "slide_total": data.slide_total, "option": presentationData[data.name].option};
index 901af0b5934e2536e5876fdfaf863126267b20b1..54dc1f0c6f04855b0ea2aa1889aa829b4ec936fe 100644 (file)
@@ -25,7 +25,6 @@ function getCookie(cname) {
 }
 
 function saveSettings() {
 }
 
 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);
 }
     settingsString = JSON.stringify({showcurrent: show_current.checked, shownext: show_next.checked, enable_shortcuts: shortcuts.checked});
     setCookie(COOKIENAME, settingsString, COOKIEEXP);
 }
index 56a3d57328169eb03b813cd629cb9146fc1e2fc9..fb1fec7c804838e550224baf43c1b4387ffccb65 100644 (file)
@@ -83,6 +83,15 @@ input[type='checkbox'] {
        display: none;
 }
 
        display: none;
 }
 
-.status_text {
+.start, .stop {
+       float: right;
+       margin: 0 0 0 10px;
+}
+
+.status_container {
        float: right;
        float: right;
+}
+
+.status_text {
+       vertical-align: middle;
 }
\ No newline at end of file
 }
\ No newline at end of file
index d7660daea9c4645b0f978a85a3468af4353a6c61..b8f20d9174c3215defabd99e9359566b5bb61ad8 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,8 @@ setuptools.setup(
     packages=setuptools.find_packages(),
     python_requires='>=3.6',    # as of v0.0.1, OBS only supports use of Python 3.6 for scripts. Otherwise the package works fine on > 3.6.
     install_requires=['pywin32', 'websockets', 'pystray'],   # https://packaging.python.org/en/latest/requirements.html
     packages=setuptools.find_packages(),
     python_requires='>=3.6',    # as of v0.0.1, OBS only supports use of Python 3.6 for scripts. Otherwise the package works fine on > 3.6.
     install_requires=['pywin32', 'websockets', 'pystray'],   # https://packaging.python.org/en/latest/requirements.html
-    data_files=[(ppt_control.__configdir__, ['ppt-control.ini'])],
+    data_files=[(ppt_control.CONFIG_DIR, [ppt_control.CONFIG_FILE])],
+    entry_points={'gui_scripts': ['ppt-control = ppt_control.ppt_control:start_interface']},
     package_data={"": ["static/*", "static/icons/*"]}
     #include_package_data=True
 )
     package_data={"": ["static/*", "static/icons/*"]}
     #include_package_data=True
 )