Added downloading of texture thumbnails
The downloading is multi-threaded, and streams directly to disk in 10kB chunks.
This commit is contained in:
parent
80c9f15192
commit
6a5c8c7e9a
@ -71,7 +71,8 @@ class BlenderCloudPreferences(AddonPreferences):
|
|||||||
else:
|
else:
|
||||||
blender_id_icon = 'WORLD_DATA'
|
blender_id_icon = 'WORLD_DATA'
|
||||||
blender_id_text = "You are logged in as %s." % blender_id_profile['username']
|
blender_id_text = "You are logged in as %s." % blender_id_profile['username']
|
||||||
blender_id_help = "To logout or change profile, go to the Blender ID add-on preferences."
|
blender_id_help = "To logout or change profile, " \
|
||||||
|
"go to the Blender ID add-on preferences."
|
||||||
|
|
||||||
sub = layout.column()
|
sub = layout.column()
|
||||||
sub.label(text=blender_id_text, icon=blender_id_icon)
|
sub.label(text=blender_id_text, icon=blender_id_icon)
|
||||||
@ -89,10 +90,20 @@ class PillarCredentialsUpdate(Operator):
|
|||||||
bl_idname = "pillar.credentials_update"
|
bl_idname = "pillar.credentials_update"
|
||||||
bl_label = "Update credentials"
|
bl_label = "Update credentials"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
# Only allow activation when the user is actually logged in.
|
||||||
|
return cls.is_logged_in(context)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_logged_in(cls, context):
|
||||||
|
active_user_id = getattr(context.window_manager, 'blender_id_active_profile', None)
|
||||||
|
return bool(active_user_id)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
active_user_id = getattr(bpy.context.window_manager, 'blender_id_active_profile', None)
|
# Only allow activation when the user is actually logged in.
|
||||||
if not active_user_id:
|
if not self.is_logged_in(context):
|
||||||
self.report({'ERROR'}, "No profile active found")
|
self.report({'ERROR'}, "No active profile found")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
# Test the new URL
|
# Test the new URL
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import concurrent.futures
|
||||||
|
|
||||||
# Add our shipped Pillar SDK wheel to the Python path
|
# Add our shipped Pillar SDK wheel to the Python path
|
||||||
if not any('pillar_sdk' in path for path in sys.path):
|
if not any('pillar_sdk' in path for path in sys.path):
|
||||||
@ -12,7 +13,6 @@ if not any('pillar_sdk' in path for path in sys.path):
|
|||||||
|
|
||||||
import pillarsdk
|
import pillarsdk
|
||||||
import pillarsdk.exceptions
|
import pillarsdk.exceptions
|
||||||
import bpy
|
|
||||||
|
|
||||||
_pillar_api = None # will become a pillarsdk.Api object.
|
_pillar_api = None # will become a pillarsdk.Api object.
|
||||||
|
|
||||||
@ -27,6 +27,8 @@ class UserNotLoggedInError(RuntimeError):
|
|||||||
def blender_id_profile() -> dict:
|
def blender_id_profile() -> dict:
|
||||||
"""Returns the Blender ID profile of the currently logged in user."""
|
"""Returns the Blender ID profile of the currently logged in user."""
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
active_user_id = getattr(bpy.context.window_manager, 'blender_id_active_profile', None)
|
active_user_id = getattr(bpy.context.window_manager, 'blender_id_active_profile', None)
|
||||||
if not active_user_id:
|
if not active_user_id:
|
||||||
return None
|
return None
|
||||||
@ -42,6 +44,7 @@ def pillar_api() -> pillarsdk.Api:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
global _pillar_api
|
global _pillar_api
|
||||||
|
import bpy
|
||||||
|
|
||||||
# Only return the Pillar API object if the user is still logged in.
|
# Only return the Pillar API object if the user is still logged in.
|
||||||
profile = blender_id_profile()
|
profile = blender_id_profile()
|
||||||
@ -74,22 +77,72 @@ def get_project_uuid(project_url: str) -> str:
|
|||||||
return project['_id']
|
return project['_id']
|
||||||
|
|
||||||
|
|
||||||
def get_nodes(project_uuid: str, parent_node_uuid: str = '') -> list:
|
def get_nodes(project_uuid: str = None, parent_node_uuid: str = None) -> list:
|
||||||
if not parent_node_uuid:
|
"""Gets nodes for either a project or given a parent node.
|
||||||
parent_spec = {'$exists': False}
|
|
||||||
else:
|
@param project_uuid: the UUID of the project, or None if only querying by parent_node_uuid.
|
||||||
parent_spec = parent_node_uuid
|
@param parent_node_uuid: the UUID of the parent node. Can be the empty string if the
|
||||||
|
node should be a top-level node in the project. Can also be None to query all nodes in a
|
||||||
|
project. In both these cases the project UUID should be given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not project_uuid and not parent_node_uuid:
|
||||||
|
raise ValueError('get_nodes(): either project_uuid or parent_node_uuid must be given.')
|
||||||
|
|
||||||
|
where = {'properties.status': 'published'}
|
||||||
|
|
||||||
|
# Build the parent node where-clause
|
||||||
|
if parent_node_uuid == '':
|
||||||
|
where['parent'] = {'$exists': False}
|
||||||
|
elif parent_node_uuid is not None:
|
||||||
|
where['parent'] = parent_node_uuid
|
||||||
|
|
||||||
|
# Build the project where-clause
|
||||||
|
if project_uuid:
|
||||||
|
where['project'] = project_uuid
|
||||||
|
|
||||||
children = pillarsdk.Node.all({
|
children = pillarsdk.Node.all({
|
||||||
'projection': {'name': 1, 'parent': 1, 'node_type': 1,
|
'projection': {'name': 1, 'parent': 1, 'node_type': 1,
|
||||||
'properties.order': 1, 'properties.status': 1,
|
'properties.order': 1, 'properties.status': 1,
|
||||||
'properties.content_type': 1,
|
'properties.content_type': 1, 'picture': 1,
|
||||||
'permissions': 1, 'project': 1, # for permission checking
|
'permissions': 1, 'project': 1, # for permission checking
|
||||||
},
|
},
|
||||||
'where': {'project': project_uuid,
|
'where': where,
|
||||||
'parent': parent_spec,
|
|
||||||
'properties.status': 'published'},
|
|
||||||
'sort': 'properties.order'},
|
'sort': 'properties.order'},
|
||||||
api=pillar_api())
|
api=pillar_api())
|
||||||
|
|
||||||
return children['_items']
|
return children['_items']
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_texture_thumbs(parent_node_uuid: str, desired_size: str):
|
||||||
|
"""Fetches all texture thumbnails in a certain parent node.
|
||||||
|
|
||||||
|
@param parent_node_uuid: the UUID of the parent node. All sub-nodes will be downloaded.
|
||||||
|
@param desired_size: size indicator, from 'sbtmlh'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def fetch_thumbnail_from_node(texture_node: pillarsdk.Node, api: pillarsdk.Api):
|
||||||
|
# Fetch the File description JSON
|
||||||
|
pic_uuid = texture_node['picture']
|
||||||
|
file_desc = pillarsdk.File.find(pic_uuid, {
|
||||||
|
'projection': {'filename': 1, 'variations': 1, 'width': 1, 'height': 1},
|
||||||
|
}, api=api)
|
||||||
|
|
||||||
|
# Save the thumbnail
|
||||||
|
thumb_path = file_desc.stream_thumb_to_file('/tmp', desired_size, api=api)
|
||||||
|
|
||||||
|
return texture_node['name'], thumb_path
|
||||||
|
|
||||||
|
api = pillar_api()
|
||||||
|
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
|
||||||
|
# Queue up fetching of thumbnails
|
||||||
|
futures = [executor.submit(fetch_thumbnail_from_node, node, api)
|
||||||
|
for node in get_nodes(parent_node_uuid=parent_node_uuid)
|
||||||
|
if node['node_type'] == 'texture']
|
||||||
|
|
||||||
|
for future in futures:
|
||||||
|
node, file = future.result()
|
||||||
|
print('Node {} has picture {}'.format(node, file))
|
||||||
|
|
||||||
|
print('Done downloading texture thumbnails')
|
||||||
|
Reference in New Issue
Block a user