Skip to content

Commit

Permalink
small updates
Browse files Browse the repository at this point in the history
  • Loading branch information
QuinnDamerell committed Jun 19, 2024
1 parent 0b5d58f commit d2a61c3
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 31 deletions.
15 changes: 12 additions & 3 deletions py_installer/Installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ def _RunInternal(self):
uninstall.DoUninstall(context)
return

# Since this runs an async thread, kick it off now so it can start working.
zstdInstallThreadOrNone = ZStandard.TryToInstallZStandardAsync(context)

# Next step is to discover and fill out the moonraker config file path and service file name.
# If we are doing an companion or bambu setup, we need the user to help us input the details to the external moonraker IP or bambu printer.
# This is the hardest part of the setup, because it's highly dependent on the system and different moonraker setups.
Expand Down Expand Up @@ -145,9 +148,6 @@ def _RunInternal(self):
# Installing ffmpeg is best effort and not required for the plugin to work.
Ffmpeg.TryToInstallFfmpeg(context)

# We also want to try to install the optional zstandard lib for compression.
ZStandard.TryToInstallZStandard(context)

# Before we start the service, check if the secrets config file already exists and if a printer id already exists.
# This will indicate if this is a fresh install or not.
context.ExistingPrinterId = Linker.GetPrinterIdFromServiceSecretsConfigFile(context)
Expand All @@ -174,6 +174,15 @@ def _RunInternal(self):
linker = Linker()
linker.Run(context)

# Wait for the install thread to complete or timeout.
# If we fail to wait for it, the plugin runtime will try to install zstandard as well, so nbd.
if zstdInstallThreadOrNone is not None:
Logger.Info("Finishing up... this might take a moment...")
try:
zstdInstallThreadOrNone.join(timeout=10.0)
except Exception as e:
Logger.Debug(f"Failed to join ztd installer thread. {str(e)}")

# Success!
Logger.Blank()
Logger.Blank()
Expand Down
15 changes: 12 additions & 3 deletions py_installer/Updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class Updater:
def DoUpdate(self, context:Context):
Logger.Header("Starting Update Logic")

# Since this takes a while, kick it off now. The pip install can take upwards of 30 seconds.
zstdInstallThreadOrNone = ZStandard.TryToInstallZStandardAsync(context)

# Enumerate all service file to find any local plugins, Sonic Pad plugins, companion service files, and bambu service files, since all service files contain this name.
# Note GetServiceFileFolderPath will return dynamically based on the OsType detected.
# Use sorted, so the results are in a nice user presentable order.
Expand All @@ -49,9 +52,6 @@ def DoUpdate(self, context:Context):
# On any system, try to install or update ffmpeg.
Ffmpeg.TryToInstallFfmpeg(context)

# We also want to try to install or update the optional zstandard lib for compression.
ZStandard.TryToInstallZStandard(context)

Logger.Info("We found the following plugins to update:")
for s in foundOeServices:
Logger.Info(f" {s}")
Expand Down Expand Up @@ -82,6 +82,15 @@ def DoUpdate(self, context:Context):
# Try to update the crontab job if needed
self.EnsureCronUpdateJob(context.RepoRootFolder)

# Wait for the install thread to complete or timeout.
# If we fail to wait for it, the plugin runtime will try to install zstandard as well, so nbd.
if zstdInstallThreadOrNone is not None:
Logger.Info("Finishing up... this might take a moment...")
try:
zstdInstallThreadOrNone.join(timeout=30.0)
except Exception as e:
Logger.Debug(f"Failed to join ztd installer thread. {str(e)}")

Logger.Blank()
Logger.Header("-------------------------------------------")
Logger.Info( " OctoEverywhere Update Successful")
Expand Down
64 changes: 39 additions & 25 deletions py_installer/ZStandard.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys
import time
import subprocess
import threading
import multiprocessing

from octoeverywhere.compression import Compression
Expand All @@ -13,36 +14,49 @@
class ZStandard:

# Tries to install zstandard, but this won't fail if the install fails.
# The PIP install can take quite a long time (20-30 seconds) so we run in async.
# Returns None if no work will be done. Otherwise it returns a thread.
@staticmethod
def TryToInstallZStandard(context:Context):
def TryToInstallZStandardAsync(context:Context) -> threading.Thread:

# We don't even try installing on K1 or SonicPad, we know it fail.
if context.OsType == OsTypes.K1 or context.OsType == OsTypes.SonicPad:
return
return None

# We also don't try install on systems with 2 cores or less, since it's too much work and the OS most of the time
# Can't support zstandard because there's no pre-made binary, it can't be built, and the install process will take too long.
if multiprocessing.cpu_count() < Compression.ZStandardMinCoreCountForInstall:
return

# Try to install the system package, if possible. This might bring in a binary.
# If this fails, the PY package might be able to still bring in a pre-built binary.
Logger.Info("Installing zstandard, this might take a moment...")
startSec = time.time()
(returnCode, stdOut, stdError) = Util.RunShellCommand("sudo apt-get install zstd -y", False)
Logger.Debug(f"Zstandard apt install result. Code: {returnCode}, StdOut: {stdOut}, StdErr: {stdError}")

# Now try to install the PY package.
# NOTE: Use the same logic as we do in the Compression class.
# Only allow blocking up to 20 seconds, so we don't hang the installer too long.
result = subprocess.run([sys.executable, '-m', 'pip', 'install', Compression.ZStandardPipPackageString], timeout=30.0, check=False, capture_output=True)
Logger.Debug(f"Zstandard PIP install result. Code: {result.returncode}, StdOut: {result.stdout}, StdErr: {result.stderr}")

# Report the status to the installer log.
if result.returncode == 0:
Logger.Info(f"zStandard successfully installed/updated. It took {str(round(time.time()-startSec, 2))} seconds.")
return

# Tell the user, but this is a best effort, so if it fails we don't care.
# Any user who wants to use RTSP and doesn't have ffmpeg installed can use our help docs to install it.
Logger.Info(f"We didn't install zstandard. It took {str(round(time.time()-startSec, 2))} seconds. Output: {result.stderr}")
return None

# Since the pip install can take a long time, do the install process async.
t = threading.Thread(target=ZStandard._InstallThread, daemon=True)
t.start()
return t


@staticmethod
def _InstallThread() -> None:
try:
# Try to install the system package, if possible. This might bring in a binary.
# If this fails, the PY package might be able to still bring in a pre-built binary.
Logger.Debug("Installing zstandard, this might take a moment...")
startSec = time.time()
(returnCode, stdOut, stdError) = Util.RunShellCommand("sudo apt-get install zstd -y", False)
Logger.Debug(f"Zstandard apt install result. Code: {returnCode}, StdOut: {stdOut}, StdErr: {stdError}")

# Now try to install the PY package.
# NOTE: Use the same logic as we do in the Compression class.
# Only allow blocking up to 20 seconds, so we don't hang the installer too long.
result = subprocess.run([sys.executable, '-m', 'pip', 'install', Compression.ZStandardPipPackageString], timeout=30.0, check=False, capture_output=True)
Logger.Debug(f"Zstandard PIP install result. Code: {result.returncode}, StdOut: {result.stdout}, StdErr: {result.stderr}")

# Report the status to the installer log.
if result.returncode == 0:
Logger.Debug(f"zStandard successfully installed/updated. It took {str(round(time.time()-startSec, 2))} seconds.")
return

# Tell the user, but this is a best effort, so if it fails we don't care.
# Any user who wants to use RTSP and doesn't have ffmpeg installed can use our help docs to install it.
Logger.Debug(f"We didn't install zstandard. It took {str(round(time.time()-startSec, 2))} seconds. Output: {result.stderr}")
except Exception as e:
Logger.Debug(f"Error installing zstandard. {str(e)}")

0 comments on commit d2a61c3

Please sign in to comment.