diff --git a/addon/i3dio/.gitignore b/addon/i3dio/.gitignore
index 49d4825..907ecbc 100644
--- a/addon/i3dio/.gitignore
+++ b/addon/i3dio/.gitignore
@@ -1,2 +1,2 @@
exporter_old.py
-
+**/i3dConverter.exe
\ No newline at end of file
diff --git a/addon/i3dio/ui/addon_preferences.py b/addon/i3dio/ui/addon_preferences.py
index 6e69c20..c3880af 100644
--- a/addon/i3dio/ui/addon_preferences.py
+++ b/addon/i3dio/ui/addon_preferences.py
@@ -3,7 +3,7 @@
import bpy
from bpy.types import AddonPreferences
-from bpy.props import (StringProperty, EnumProperty)
+from bpy.props import (StringProperty, EnumProperty, BoolProperty)
from .. import xml_i3d
@@ -78,6 +78,7 @@ def draw(self, context):
row.prop(self, 'i3d_converter_path')
if(next((True for addon in addon_utils.modules() if addon.bl_info.get("name") == "GIANTS I3D Exporter Tools"), False)):
row.operator('i3dio.i3d_converter_path_from_giants_addon', text="", icon="EVENT_G")
+ row.operator("i3dio.download_i3d_converter", text="", icon='WORLD_DATA')
case 'AUTOMATIC':
#row = c_box.row()
#row.operator("i3dio.download_i3d_converter", text="Manage Automatic Download")
@@ -101,11 +102,79 @@ def execute(self, context):
class I3D_IO_OT_download_i3d_converter(bpy.types.Operator):
bl_idname = "i3dio.download_i3d_converter"
bl_label = "Download I3D Converter"
- bl_description = "Download I3D Converter"
+ bl_description = "Download from Giants Developer Network (Requires valid login)"
bl_options = {'INTERNAL'}
+ email: StringProperty(name="Email", default="")
+ password: StringProperty(name="Password", default="", subtype="PASSWORD")
+
def execute(self, context):
- return {"FINISHED"}
+ import re
+ from io import BytesIO
+ from requests import Session
+ from zipfile import (ZipFile, BadZipfile)
+ from shutil import copyfileobj
+
+ # Attempt to login using provided credentials
+ session = Session()
+ request = session.post('https://gdn.giants-software.com/index.php', data={'greenstoneX':'1', 'redstoneX':self.email, 'bluestoneX':self.password})
+
+ ## Clear email and password after usage
+ self.email = ""
+ self.password = ""
+
+ # Check if login was successful
+ if '
' not in request.text:
+ self.report({'WARNING'}, f"Could not login to https://gdn.giants-software.com/index.php with provided credentials!")
+ return {'CANCELLED'}
+
+ # Get download page
+ request = session.get('https://gdn.giants-software.com/downloads.php')
+
+ # Find the download IDs for the all Giants Blender Exporters (As long as Giants names them the same way)
+ result = re.findall(r'href="download.php\?downloadId=([0-9]+)">Blender Exporter Plugins v([0-9]+.[0-9]+.[0-9]+)', request.text)
+
+ # Assume the first found is the newest
+ download_id, exporter_version = result[0]
+
+ # Request download of Giants I3D Exporter
+ download_url = f'https://gdn.giants-software.com/download.php?downloadId={download_id}'
+ request = session.get(download_url)
+
+ try:
+ # Create in-memory zipfile from downloaded content
+ zipfile = ZipFile(BytesIO(request.content), 'r')
+ # Find path to this exporter addon
+ binary_path = 'i3dConverter.exe'
+ for addon in addon_utils.modules():
+ if addon.bl_info.get("name") == "Unofficial GIANTS I3D Exporter Tools":
+ binary_path = pathlib.PurePath(addon.__file__).parent.joinpath(binary_path)
+ # Extract I3D Converter Binary from zipfile and save to disk
+ with zipfile.open('io_export_i3d/util/i3dConverter.exe') as zipped_binary, open(binary_path, 'wb') as saved_binary:
+ copyfileobj(zipped_binary, saved_binary)
+ # Set I3D Converter Binary path to newly downloaded converter
+ bpy.context.preferences.addons['i3dio'].preferences.i3d_converter_path = str(binary_path)
+ except (BadZipfile, KeyError, OSError) as e:
+ self.report({'WARNING'}, f"The Community I3D Exporter did not succesfully fetch and install the Giants I3D Converter binary! ({e})")
+ return {'CANCELLED'}
+
+ self.report({'INFO'}, f"Fetched i3dConverter.exe from version {exporter_version} of the Giants Exporter downloaded from {download_url}")
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ wm = bpy.context.window_manager
+ # Width increased to fit the warning about the download freezing the UI
+ return wm.invoke_props_dialog(self, width=350)
+
+ def draw(self, context):
+ layout = self.layout
+ row = layout.row()
+ row.prop(self, "email")
+ row = layout.row()
+ row.prop(self, "password")
+ row = layout.row()
+ row.alignment = "CENTER"
+ row.label(text="Blender UI will appear frozen during file download (~15MB) ", icon="ERROR")
def register():