Downloadable engines (#34)

* Add blender_downloader.py

* Add engine_manager.py

* Add additional methods to engine_manager.py

* Refactor file layout to make engines on par with workers

* Add system platform info to status response

* Default to using system platform / cpu if none are provided

* Add API to download an engine and some general cleanup

* Add method to delete downloaded engine

* Add API calls to download engines and delete downloads

* Misc fixes
This commit is contained in:
2023-10-20 15:05:29 -05:00
committed by GitHub
parent 4563dcb255
commit 7d1ecf1fa5
21 changed files with 439 additions and 79 deletions

View File

@@ -0,0 +1,128 @@
import os
import logging
import platform
import shutil
try:
from .blender_engine import Blender
except ImportError:
from blender_engine import Blender
try:
from .ffmpeg_engine import FFMPEG
except ImportError:
from ffmpeg_engine import FFMPEG
logger = logging.getLogger()
class EngineManager:
engines_path = "~/zordon-uploads/engines"
@classmethod
def supported_engines(cls):
return [Blender, FFMPEG]
@classmethod
def all_engines(cls):
results = []
# Parse downloaded engine directory
try:
all_items = os.listdir(cls.engines_path)
all_directories = [item for item in all_items if os.path.isdir(os.path.join(cls.engines_path, item))]
for directory in all_directories:
# Split the input string by dashes to get segments
segments = directory.split('-')
# Define the keys for each word
keys = ["engine", "version", "system_os", "cpu"]
# Create a dictionary with named keys
executable_names = {'linux': 'blender', 'windows': 'blender.exe', 'macos': 'Blender.app'}
result_dict = {keys[i]: segments[i] for i in range(min(len(keys), len(segments)))}
result_dict['path'] = os.path.join(cls.engines_path, directory, executable_names[result_dict['system_os']])
result_dict['type'] = 'managed'
results.append(result_dict)
except FileNotFoundError:
logger.warning("Cannot find local engines download directory")
# add system installs to this list
for eng in cls.supported_engines():
if eng.default_renderer_path():
results.append({'engine': eng.name(), 'version': eng().version(),
'system_os': cls.system_os(),
'cpu': cls.system_cpu(),
'path': eng.default_renderer_path(), 'type': 'system'})
return results
@classmethod
def all_versions_for_engine(cls, engine):
return [x for x in cls.all_engines() if x['engine'] == engine]
@classmethod
def newest_engine_version(cls, engine, system_os=None, cpu=None):
system_os = system_os or cls.system_os()
cpu = cpu or cls.system_cpu()
try:
filtered = [x for x in cls.all_engines() if x['engine'] == engine and x['system_os'] == system_os and x['cpu'] == cpu]
versions = sorted(filtered, key=lambda x: x['version'], reverse=True)
return versions[0]
except IndexError:
logger.error(f"Cannot find newest engine version for {engine}-{system_os}-{cpu}")
return None
@classmethod
def has_engine_version(cls, engine, version, system_os=None, cpu=None):
system_os = system_os or cls.system_os()
cpu = cpu or cls.system_cpu()
filtered = [x for x in cls.all_engines() if
x['engine'] == engine and x['system_os'] == system_os and x['cpu'] == cpu and x['version'] == version]
return filtered[0] if filtered else False
@staticmethod
def system_os():
return platform.system().lower().replace('darwin', 'macos')
@staticmethod
def system_cpu():
return platform.machine().lower().replace('amd64', 'x64')
@classmethod
def download_engine(cls, engine, version, system_os=None, cpu=None):
existing_download = cls.has_engine_version(engine, version, system_os, cpu)
if existing_download:
logger.info(f"Requested download of {engine} {version}, but local copy already exists")
return existing_download
if engine == "blender":
from .scripts.blender.blender_downloader import BlenderDownloader
logger.info(f"Requesting download of {engine} {version}")
if BlenderDownloader.download_engine(version, download_location=cls.engines_path, system_os=system_os, cpu=cpu):
return cls.has_engine_version(engine, version, system_os, cpu)
else:
logger.error("Error downloading Engine")
return None # Return None to indicate an error
@classmethod
def delete_engine_download(cls, engine, version, system_os=None, cpu=None):
logger.info(f"Requested deletion of engine: {engine}-{version}")
found = cls.has_engine_version(engine, version, system_os, cpu)
if found:
dir_path = os.path.dirname(found['path'])
shutil.rmtree(dir_path, ignore_errors=True)
logger.info(f"Engine {engine}-{version}-{found['system_os']}-{found['cpu']} successfully deleted")
return True
else:
logger.error(f"Cannot find engine: {engine}-{version}")
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# print(EngineManager.newest_engine_version('blender', 'macos', 'arm64'))
EngineManager.delete_engine_download('blender', '3.2.1', 'windows', 'x64')