Skip to content

Commit

Permalink
Local Server
Browse files Browse the repository at this point in the history
The try-except block in ServerThread catches potential OSError exceptions, which can occur if there are issues with the socket or port.
  • Loading branch information
banyapon committed Aug 7, 2024
1 parent e224bbe commit 036f7c4
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 10 deletions.
8 changes: 4 additions & 4 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
bl_info = {
"name": "A-Frame Exporter",
"author": "Banyapon Poolsawas",
"version": (1, 0, 2),
"blender": (4, 0, 1),
"location": "File > Export",
"description": "Export Blender scene to A-Frame HTML and GLB for WebXR",
"version": (1, 0, 5),
"blender": (4, 0, 0),
"location": "File > Export, Scene Properties > A-Frame Export Settings",
"description": "Export Blender scene to A-Frame HTML and GLB with local server",
"warning": "",
"category": "Export",
}
Expand Down
100 changes: 94 additions & 6 deletions aframe_exporter.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,77 @@
import bpy
import os
import shutil
import http.server
import socketserver
import threading
import webbrowser
from bpy.props import BoolProperty, IntProperty
from bpy_extras.io_utils import ExportHelper

class ServerThread(threading.Thread):
def __init__(self, directory, port):
super().__init__()
self.directory = directory
self.port = port
self.httpd = None
self.error = None

def run(self):
os.chdir(self.directory)

# Error handling for the server
try:
handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", self.port), handler) as httpd:
self.httpd = httpd
print(f"Serving at port {self.port}")
httpd.serve_forever()
except OSError as e:
self.error = e

def stop(self):
if self.httpd:
self.httpd.shutdown()

class ExportAFrame(bpy.types.Operator, ExportHelper):
bl_idname = "export_scene.aframe"
bl_label = "Export A-Frame"
filename_ext = ".html"

start_server: BoolProperty(
name="Start Local Server",
description="Automatically start a local server after export",
default=False
)

port: IntProperty(
name="Port",
description="Port number for the local server",
default=8200,
min=1,
max=65535
)


def execute(self, context):
filepath = self.filepath
glb_filepath = os.path.splitext(filepath)[0] + ".glb"

# Export GLB
bpy.ops.export_scene.gltf(filepath=glb_filepath, export_format='GLB')

# Copy favicon.ico if it exists
favicon_path = os.path.join(os.path.dirname(__file__), "icons", "favicon.ico")
# Copy favicon
favicon_path = os.path.join(os.path.dirname(__file__), "icons", "favicon.ico")
if os.path.exists(favicon_path):
shutil.copy(favicon_path, os.path.dirname(filepath))
shutil.copy(favicon_path, os.path.dirname(filepath))

# Create HTML (A-Frame)
with open(filepath, "w") as f:
f.write(f"""
<!--This file generated from Aframe Exporter, Blender Add-on-->
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="favicon.ico">
<link rel="icon" href="favicon.ico">
<title>Aframe Exporter</title>
<script src="https://aframe.io/releases/1.6.0/aframe.min.js"></script>
</head>
Expand All @@ -47,8 +91,52 @@ def execute(self, context):
<a-entity gltf-model="#model" static-body></a-entity>
</a-scene>
</body>
</html>
""")

if self.start_server:
self.server_thread = ServerThread(os.path.dirname(filepath), self.port)
self.server_thread.start()

if self.server_thread.error: # Check for errors
self.report({'ERROR'}, f"Failed to start server: {self.server_thread.error}")
return {'CANCELLED'}

url = f"http://localhost:{self.port}/{os.path.basename(filepath)}"
else:
url = "file://" + filepath

webbrowser.open_new_tab(url)
return {'FINISHED'}

def start_local_server(self, directory, port):
os.chdir(directory)
handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", port), handler) as httpd:
print(f"Serving at port {port}")
threading.Thread(target=httpd.serve_forever).start()

def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}

class ExportAFramePanel(bpy.types.Panel):
bl_label = "A-Frame Export Settings" # Label for the panel
bl_idname = "OBJECT_PT_aframe_export" # Unique identifier for the panel
bl_space_type = 'PROPERTIES' # Panel location (Properties Editor)
bl_region_type = 'WINDOW' # Panel type (window)
bl_context = "scene" # Context for the panel (Scene)
bl_options = {'DEFAULT_CLOSED'} # Panel is closed by default

def draw(self, context):
layout = self.layout

# Export Button
layout.operator("export_scene.aframe", text="Export A-Frame") # Add the export operator button

# Local Server Options
row = layout.row() # Create a new row for the checkbox and button
row.prop(context.scene, "aframe_local_server", text="Start Local Server") # Checkbox to toggle local server
if context.scene.aframe_local_server: # Show port input if the checkbox is checked
row = layout.row() # Create a new row for the port input
row.prop(context.scene, "aframe_server_port", text="Port") # Input field for the port number

0 comments on commit 036f7c4

Please sign in to comment.