Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVilém Duha <vilda.novak@gmail.com>2020-12-22 16:45:40 +0300
committerVilém Duha <vilda.novak@gmail.com>2020-12-22 16:45:51 +0300
commit10ae0f7a5a84b0d9ac8ccbb4abe97e42ab6ca9d1 (patch)
treeb742a78c088cd9baa7d1252054f5595f700dbc4b /blenderkit
parent105d485a0327976e12b34f23373079b3b2c4d834 (diff)
BlenderKit: multiple improvements:
HDR and scene asset types - These are now available when user enables experimental features in addon preferences. Refactor Upload - no Blender instance isn't launched when not needed and everything happens in a thread, so no waiting for the user when e.g. updating metadata. improve pasted asset_id string Change asset type picking interface to fit more asset types Notify user about brush mode needs Update categories file
Diffstat (limited to 'blenderkit')
-rw-r--r--blenderkit/__init__.py110
-rw-r--r--blenderkit/append_link.py75
-rw-r--r--blenderkit/bg_blender.py14
-rw-r--r--blenderkit/categories.py2
-rw-r--r--blenderkit/data/categories.json1578
-rw-r--r--blenderkit/download.py30
-rw-r--r--blenderkit/image_utils.py84
-rw-r--r--blenderkit/paths.py4
-rw-r--r--blenderkit/ratings.py7
-rw-r--r--blenderkit/resolutions.py33
-rw-r--r--blenderkit/search.py36
-rw-r--r--blenderkit/ui.py25
-rw-r--r--blenderkit/ui_panels.py73
-rw-r--r--blenderkit/upload.py510
-rw-r--r--blenderkit/upload_bg.py142
-rw-r--r--blenderkit/utils.py28
16 files changed, 2204 insertions, 547 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py
index b02985e0..d69f3e0d 100644
--- a/blenderkit/__init__.py
+++ b/blenderkit/__init__.py
@@ -199,9 +199,6 @@ thumbnail_resolutions = (
)
-
-
-
def switch_search_results(self, context):
s = bpy.context.scene
props = s.blenderkitUI
@@ -211,6 +208,9 @@ def switch_search_results(self, context):
elif props.asset_type == 'SCENE':
s['search results'] = s.get('bkit scene search')
s['search results orig'] = s.get('bkit scene search orig')
+ elif props.asset_type == 'HDR':
+ s['search results'] = s.get('bkit hdr search')
+ s['search results orig'] = s.get('bkit hdr search orig')
elif props.asset_type == 'MATERIAL':
s['search results'] = s.get('bkit material search')
s['search results orig'] = s.get('bkit material search orig')
@@ -220,7 +220,14 @@ def switch_search_results(self, context):
elif props.asset_type == 'BRUSH':
s['search results'] = s.get('bkit brush search')
s['search results orig'] = s.get('bkit brush search orig')
+ if not(context.sculpt_object or context.image_paint_object):
+ ui.add_report(
+ 'Switch to paint or sculpt mode to search in BlenderKit brushes.')
+
search.load_previews()
+ if s['search results'] == None:
+ search.search_update(self, context)
+
def asset_type_callback(self, context):
@@ -229,27 +236,47 @@ def asset_type_callback(self, context):
items for Enum property, depending on the down_up property - BlenderKit is either in search or in upload mode.
'''
- if self.down_up == 'SEARCH':
- items = (
- ('MODEL', 'Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0),
- # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1),
- ('MATERIAL', 'Materials', 'Find materials in the BlenderKit online database', 'MATERIAL', 2),
- # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
- ('BRUSH', 'Brushes', 'Find brushes in the BlenderKit online database', 'BRUSH_DATA', 3)
- )
+ user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+
+ if user_preferences.experimental_features:
+ if self.down_up == 'SEARCH':
+ items = (
+ ('MODEL', 'Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0),
+ ('MATERIAL', 'Materials', 'Find materials in the BlenderKit online database', 'MATERIAL', 2),
+ # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
+ ('SCENE', 'Scenes', 'Browse scenes', 'SCENE_DATA', 3),
+ ('HDR', 'Hdrs', 'Browse hdrs', 'WORLD', 4),
+ ('BRUSH', 'Brushes', 'Find brushes in the BlenderKit online database', 'BRUSH_DATA', 5)
+ )
+ else:
+ items = (
+ ('MODEL', 'Model', 'Upload a model to BlenderKit', 'OBJECT_DATAMODE', 0),
+ # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1),
+ ('MATERIAL', 'Material', 'Upload a material to BlenderKit', 'MATERIAL', 2),
+ # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
+ ('SCENE', 'Scenes', 'Browse scenes', 'SCENE_DATA', 3),
+ ('HDR', 'Hdrs', 'Browse hdrs', 'WORLD', 4),
+ ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 5)
+ )
else:
- items = (
- ('MODEL', 'Model', 'Upload a model to BlenderKit', 'OBJECT_DATAMODE', 0),
- # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1),
- ('MATERIAL', 'Material', 'Upload a material to BlenderKit', 'MATERIAL', 2),
- # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
- ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 3)
- )
+ if self.down_up == 'SEARCH':
+ items = (
+ ('MODEL', 'Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0),
+ ('MATERIAL', 'Materials', 'Find materials in the BlenderKit online database', 'MATERIAL', 2),
+ # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
+ ('BRUSH', 'Brushes', 'Find brushes in the BlenderKit online database', 'BRUSH_DATA', 5)
+ )
+ else:
+ items = (
+ ('MODEL', 'Model', 'Upload a model to BlenderKit', 'OBJECT_DATAMODE', 0),
+ # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1),
+ ('MATERIAL', 'Material', 'Upload a material to BlenderKit', 'MATERIAL', 2),
+ # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
+ ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 5)
+ )
return items
-
-
class BlenderKitUIProps(PropertyGroup):
down_up: EnumProperty(
name="Download vs Upload",
@@ -363,6 +390,15 @@ class BlenderKitUIProps(PropertyGroup):
dragging_rating_work_hours: BoolProperty(name="Dragging Rating Work Hours", default=False)
last_rating_time: FloatProperty(name="Last Rating Time", default=0.0)
+ hdr_upload_image: PointerProperty(name='Upload HDR',
+ type=bpy.types.Image,
+ description='Pick an image to upload')
+
+ # StringProperty(
+ # name="Upload HDR",
+ # description="Active HDR image to upload",
+ # default="")
+
def search_procedural_update(self, context):
if self.search_procedural in ('PROCEDURAL', 'BOTH'):
@@ -867,6 +903,10 @@ class BlenderKitBrushSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
)
+class BlenderKitHDRUploadProps(PropertyGroup, BlenderKitCommonUploadProps):
+ pass;
+
+
class BlenderKitBrushUploadProps(PropertyGroup, BlenderKitCommonUploadProps):
mode: EnumProperty(
name="Mode",
@@ -1274,7 +1314,6 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
free_only: BoolProperty(name="Free only", description="Show only free models",
default=False, update=search.search_update)
-
# CONDITION
search_condition: EnumProperty(
items=conditions,
@@ -1395,6 +1434,15 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
)
+class BlenderKitHDRSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
+ search_keywords: StringProperty(
+ name="Search",
+ description="Search for these keywords",
+ default="",
+ update=search.search_update
+ )
+
+
class BlenderKitSceneSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
search_keywords: StringProperty(
name="Search",
@@ -1602,6 +1650,12 @@ class BlenderKitAddonPreferences(AddonPreferences):
update=utils.save_prefs
)
+ experimental_features: BoolProperty(
+ name="Enable experimental features",
+ description="Enable all experimental features of BlenderKit. Use at your own risk.",
+ default=False,
+ update=utils.save_prefs
+ )
# allow_proximity : BoolProperty(
# name="allow proximity data reports",
# description="This sends anonymized proximity data \n \
@@ -1642,6 +1696,7 @@ class BlenderKitAddonPreferences(AddonPreferences):
layout.prop(self, "search_in_header")
if bpy.context.preferences.view.show_developer_ui:
layout.prop(self, "use_timers")
+ layout.prop(self, "experimental_features")
# registration
@@ -1656,6 +1711,9 @@ classes = (
BlenderKitSceneSearchProps,
BlenderKitSceneUploadProps,
+ BlenderKitHDRSearchProps,
+ BlenderKitHDRUploadProps,
+
BlenderKitMaterialUploadProps,
BlenderKitMaterialSearchProps,
@@ -1691,6 +1749,14 @@ def register():
bpy.types.Scene.bkit_ratings = PointerProperty( # for uploads, not now...
type=BlenderKitRatingProps)
+ # HDRs
+ bpy.types.Scene.blenderkit_HDR = PointerProperty(
+ type=BlenderKitHDRSearchProps)
+ bpy.types.Image.blenderkit = PointerProperty( # for uploads, not now...
+ type=BlenderKitHDRUploadProps)
+ bpy.types.Image.bkit_ratings = PointerProperty( # for uploads, not now...
+ type=BlenderKitRatingProps)
+
# MATERIALS
bpy.types.Scene.blenderkit_mat = PointerProperty(
type=BlenderKitMaterialSearchProps)
@@ -1755,11 +1821,13 @@ def unregister():
del bpy.types.Scene.blenderkit_models
del bpy.types.Scene.blenderkit_scene
+ del bpy.types.Scene.blenderkit_HDR
del bpy.types.Scene.blenderkit_brush
del bpy.types.Scene.blenderkit_mat
del bpy.types.Scene.blenderkit
del bpy.types.Object.blenderkit
+ del bpy.types.Image.blenderkit
del bpy.types.Material.blenderkit
del bpy.types.Brush.blenderkit
diff --git a/blenderkit/append_link.py b/blenderkit/append_link.py
index 66fa711f..c80027c5 100644
--- a/blenderkit/append_link.py
+++ b/blenderkit/append_link.py
@@ -96,6 +96,81 @@ def append_scene(file_name, scenename=None, link=False, fake_user=False):
return scene
+def get_node_sure(node_tree, ntype=''):
+ '''
+ Gets a node of certain type, but creates a new one if not pre
+ '''
+ node = None
+ for n in node_tree.nodes:
+ if ntype == n.bl_rna.identifier:
+ node = n
+ return node
+ if not node:
+ node = node_tree.nodes.new(type=ntype)
+
+ return node
+
+def hdr_swap(name, hdr):
+ '''
+ Try to replace the hdr in current world setup. If this fails, create a new world.
+ :param name: Name of the resulting world (renamse the current one if swap is successfull)
+ :param hdr: Image type
+ :return: None
+ '''
+ w = bpy.context.scene.world
+ w.use_nodes = True
+ w.name = name
+ nt = w.node_tree
+ for n in nt.nodes:
+ if 'ShaderNodeTexEnvironment' == n.bl_rna.identifier:
+ env_node = n
+ env_node.image = hdr
+ return
+ new_hdr_world(name,hdr)
+
+
+def new_hdr_world(name, hdr):
+ '''
+ creates a new world, links in the hdr with mapping node, and links the world to scene
+ :param name: Name of the world datablock
+ :param hdr: Image type
+ :return: None
+ '''
+ w = bpy.data.worlds.new(name=name)
+ w.use_nodes = True
+ bpy.context.scene.world = w
+
+ nt = w.node_tree
+ env_node = nt.nodes.new(type='ShaderNodeTexEnvironment')
+ env_node.image = hdr
+ background = get_node_sure(nt, 'ShaderNodeBackground')
+ tex_coord = get_node_sure(nt, 'ShaderNodeTexCoord')
+ mapping = get_node_sure(nt, 'ShaderNodeMapping')
+
+ nt.links.new(env_node.outputs['Color'], background.inputs['Color'])
+ nt.links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector'])
+ nt.links.new(mapping.outputs['Vector'], env_node.inputs['Vector'])
+ env_node.location.x = -400
+ mapping.location.x = -600
+ tex_coord.location.x = -800
+
+
+def load_HDR(file_name, name):
+ '''Load a HDR into file and link it to scene world. '''
+ already_linked = False
+ for i in bpy.data.images:
+ if i.filepath == file_name:
+ hdr = i
+ already_linked = True
+ break;
+
+ if not already_linked:
+ hdr = bpy.data.images.load(file_name)
+
+ hdr_swap(name, hdr)
+ return hdr
+
+
def link_collection(file_name, obnames=[], location=(0, 0, 0), link=False, parent = None, **kwargs):
'''link an instanced group - model type asset'''
sel = utils.selection_get()
diff --git a/blenderkit/bg_blender.py b/blenderkit/bg_blender.py
index a8597675..128cf457 100644
--- a/blenderkit/bg_blender.py
+++ b/blenderkit/bg_blender.py
@@ -55,7 +55,9 @@ class threadCom: # object passed to threads to read background process stdout i
def threadread(tcom):
- '''reads stdout of background process, done this way to have it non-blocking. this threads basically waits for a stdout line to come in, fills the data, dies.'''
+ '''reads stdout of background process.
+ this threads basically waits for a stdout line to come in,
+ fills the data, dies.'''
found = False
while not found:
inline = tcom.proc.stdout.readline()
@@ -155,6 +157,7 @@ process_types = (
process_sources = (
('MODEL', 'Model', 'set of objects'),
('SCENE', 'Scene', 'set of scenes'),
+ ('HDR', 'HDR', 'HDR image'),
('MATERIAL', 'Material', 'any .blend Material'),
('TEXTURE', 'Texture', 'a texture, or texture set'),
('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
@@ -203,9 +206,18 @@ class KillBgProcess(bpy.types.Operator):
if tcom.process_type == self.process_type:
source = eval(tcom.eval_path)
kill = False
+ #TODO HDR - add killing of process
if source.bl_rna.name == 'Object' and self.process_source == 'MODEL':
if source.name == bpy.context.active_object.name:
kill = True
+ if source.bl_rna.name == 'Scene' and self.process_source == 'SCENE':
+ if source.name == bpy.context.scene.name:
+ kill = True
+ if source.bl_rna.name == 'Image' and self.process_source == 'HDR':
+ ui_props = bpy.context.scene.blenderkitUI
+ if source.name == ui_props.hdr_upload_image.name:
+ kill = False
+
if source.bl_rna.name == 'Material' and self.process_source == 'MATERIAL':
if source.name == bpy.context.active_object.active_material.name:
kill = True
diff --git a/blenderkit/categories.py b/blenderkit/categories.py
index 07c811d8..43d6f2ba 100644
--- a/blenderkit/categories.py
+++ b/blenderkit/categories.py
@@ -104,6 +104,7 @@ def get_upload_asset_type(self):
typemapper = {
bpy.types.Object.blenderkit: 'model',
bpy.types.Scene.blenderkit: 'scene',
+ bpy.types.Image.blenderkit: 'hdr',
bpy.types.Material.blenderkit: 'material',
bpy.types.Brush.blenderkit: 'brush'
}
@@ -174,6 +175,7 @@ def load_categories():
wm['active_category'] = {
'MODEL': ['model'],
'SCENE': ['scene'],
+ 'HDR': ['hdr'],
'MATERIAL': ['material'],
'BRUSH': ['brush'],
}
diff --git a/blenderkit/data/categories.json b/blenderkit/data/categories.json
index d6286050..8bf6c85c 100644
--- a/blenderkit/data/categories.json
+++ b/blenderkit/data/categories.json
@@ -82,6 +82,23 @@
"assetCountCumulative": 8
},
{
+ "name": "crack",
+ "slug": "crack",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "crack",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "cut",
"slug": "cut",
"active": true,
@@ -129,8 +146,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 1,
- "assetCountCumulative": 1
+ "assetCount": 2,
+ "assetCountCumulative": 2
},
{
"name": "fabric",
@@ -197,8 +214,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 9,
- "assetCountCumulative": 9
+ "assetCount": 10,
+ "assetCountCumulative": 10
},
{
"name": "landscape",
@@ -231,8 +248,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 1,
- "assetCountCumulative": 1
+ "assetCount": 7,
+ "assetCountCumulative": 7
+ },
+ {
+ "name": "nature",
+ "slug": "nature-brush",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "nature",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "pattern",
@@ -248,8 +282,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 7,
- "assetCountCumulative": 7
+ "assetCount": 9,
+ "assetCountCumulative": 9
},
{
"name": "rock",
@@ -269,6 +303,40 @@
"assetCountCumulative": 8
},
{
+ "name": "rust",
+ "slug": "rust-brush",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "rust",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "sculpture",
+ "slug": "sculpture-brush",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "sculpture",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "stitches",
"slug": "stitches",
"active": true,
@@ -286,6 +354,23 @@
"assetCountCumulative": 12
},
{
+ "name": "stone",
+ "slug": "stone-brush",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "stone",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "tree",
"slug": "tree-brush",
"active": true,
@@ -301,10 +386,79 @@
"children": [],
"assetCount": 4,
"assetCountCumulative": 4
+ },
+ {
+ "name": "wood",
+ "slug": "wood-brush",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "wood",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ }
+ ],
+ "assetCount": 104,
+ "assetCountCumulative": 104
+ },
+ {
+ "name": "HDR",
+ "slug": "hdr",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "HDR",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [
+ {
+ "name": "Indoor",
+ "slug": "indoor",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "Indoor",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "Outdoor",
+ "slug": "hdr-outdoor",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "Outdoor",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
],
- "assetCount": 94,
- "assetCountCumulative": 94
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "material",
@@ -334,8 +488,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 36,
- "assetCountCumulative": 36
+ "assetCount": 24,
+ "assetCountCumulative": 24
},
{
"name": "asphalt",
@@ -351,8 +505,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 36,
- "assetCountCumulative": 36
+ "assetCount": 45,
+ "assetCountCumulative": 45
},
{
"name": "bricks",
@@ -368,8 +522,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 64,
- "assetCountCumulative": 64
+ "assetCount": 106,
+ "assetCountCumulative": 106
},
{
"name": "ceramic",
@@ -385,8 +539,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 16,
- "assetCountCumulative": 16
+ "assetCount": 33,
+ "assetCountCumulative": 33
},
{
"name": "concrete",
@@ -402,8 +556,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 64,
- "assetCountCumulative": 64
+ "assetCount": 89,
+ "assetCountCumulative": 89
},
{
"name": "dirt",
@@ -436,8 +590,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 169,
- "assetCountCumulative": 169
+ "assetCount": 192,
+ "assetCountCumulative": 192
},
{
"name": "floor",
@@ -453,8 +607,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 41,
- "assetCountCumulative": 41
+ "assetCount": 68,
+ "assetCountCumulative": 68
},
{
"name": "food",
@@ -470,8 +624,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 30,
- "assetCountCumulative": 30
+ "assetCount": 35,
+ "assetCountCumulative": 35
},
{
"name": "fx",
@@ -487,8 +641,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 19,
- "assetCountCumulative": 19
+ "assetCount": 28,
+ "assetCountCumulative": 28
},
{
"name": "glass",
@@ -504,8 +658,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 28,
- "assetCountCumulative": 28
+ "assetCount": 50,
+ "assetCountCumulative": 50
+ },
+ {
+ "name": "grass",
+ "slug": "grass",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "grass",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 4,
+ "assetCountCumulative": 4
},
{
"name": "ground",
@@ -521,8 +692,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 70,
- "assetCountCumulative": 70
+ "assetCount": 96,
+ "assetCountCumulative": 96
+ },
+ {
+ "name": "human",
+ "slug": "human",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "human",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "ice",
@@ -538,8 +726,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 20,
- "assetCountCumulative": 20
+ "assetCount": 25,
+ "assetCountCumulative": 25
},
{
"name": "leather",
@@ -555,8 +743,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 37,
- "assetCountCumulative": 37
+ "assetCount": 53,
+ "assetCountCumulative": 53
},
{
"name": "liquid",
@@ -572,8 +760,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 7,
- "assetCountCumulative": 7
+ "assetCount": 10,
+ "assetCountCumulative": 10
+ },
+ {
+ "name": "marble",
+ "slug": "marble",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "marble",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "metal",
@@ -589,8 +794,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 119,
- "assetCountCumulative": 119
+ "assetCount": 214,
+ "assetCountCumulative": 214
},
{
"name": "organic",
@@ -606,8 +811,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 29,
- "assetCountCumulative": 29
+ "assetCount": 40,
+ "assetCountCumulative": 40
},
{
"name": "ornaments",
@@ -623,8 +828,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 3,
- "assetCountCumulative": 3
+ "assetCount": 14,
+ "assetCountCumulative": 14
},
{
"name": "paper",
@@ -640,8 +845,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 30,
- "assetCountCumulative": 30
+ "assetCount": 41,
+ "assetCountCumulative": 41
},
{
"name": "paving",
@@ -657,8 +862,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 32,
- "assetCountCumulative": 32
+ "assetCount": 39,
+ "assetCountCumulative": 39
},
{
"name": "plaster",
@@ -674,8 +879,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 34,
- "assetCountCumulative": 34
+ "assetCount": 53,
+ "assetCountCumulative": 53
},
{
"name": "plastic",
@@ -691,8 +896,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 25,
- "assetCountCumulative": 25
+ "assetCount": 41,
+ "assetCountCumulative": 41
},
{
"name": "rock",
@@ -708,8 +913,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 21,
- "assetCountCumulative": 21
+ "assetCount": 39,
+ "assetCountCumulative": 39
},
{
"name": "roofing",
@@ -725,8 +930,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 12,
- "assetCountCumulative": 12
+ "assetCount": 16,
+ "assetCountCumulative": 16
},
{
"name": "rubber",
@@ -759,8 +964,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 17,
- "assetCountCumulative": 17
+ "assetCount": 20,
+ "assetCountCumulative": 20
},
{
"name": "sand",
@@ -776,8 +981,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 16,
- "assetCountCumulative": 16
+ "assetCount": 20,
+ "assetCountCumulative": 20
+ },
+ {
+ "name": "soil",
+ "slug": "soil",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "soil",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "stone",
@@ -793,8 +1015,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 117,
- "assetCountCumulative": 117
+ "assetCount": 136,
+ "assetCountCumulative": 136
},
{
"name": "tech",
@@ -810,8 +1032,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 34,
- "assetCountCumulative": 34
+ "assetCount": 63,
+ "assetCountCumulative": 63
},
{
"name": "tiles",
@@ -827,8 +1049,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 84,
- "assetCountCumulative": 84
+ "assetCount": 118,
+ "assetCountCumulative": 118
},
{
"name": "wood",
@@ -844,12 +1066,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 121,
- "assetCountCumulative": 121
+ "assetCount": 193,
+ "assetCountCumulative": 193
}
],
- "assetCount": 1365,
- "assetCountCumulative": 1365
+ "assetCount": 1939,
+ "assetCountCumulative": 1939
},
{
"name": "model",
@@ -893,12 +1115,97 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 20,
- "assetCountCumulative": 20
+ "assetCount": 10,
+ "assetCountCumulative": 10
+ },
+ {
+ "name": "helicopter",
+ "slug": "helicopter",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "helicopter",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "historic",
+ "slug": "historic-aircraft",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "historic",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "jet",
+ "slug": "jet",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "jet",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "part",
+ "slug": "part-aircraft",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "part",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "private",
+ "slug": "private",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "private",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
],
- "assetCount": 20,
- "assetCountCumulative": 20
+ "assetCount": 10,
+ "assetCountCumulative": 10
},
{
"name": "architecture",
@@ -928,12 +1235,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 133,
- "assetCountCumulative": 133
+ "assetCount": 225,
+ "assetCountCumulative": 225
}
],
- "assetCount": 137,
- "assetCountCumulative": 137
+ "assetCount": 229,
+ "assetCountCumulative": 229
},
{
"name": "art",
@@ -950,6 +1257,40 @@
"metaExtra": "",
"children": [
{
+ "name": "design",
+ "slug": "design",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "design",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 6,
+ "assetCountCumulative": 6
+ },
+ {
+ "name": "drawing",
+ "slug": "drawing",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "drawing",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "literature",
"slug": "literature",
"active": true,
@@ -963,8 +1304,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 9,
- "assetCountCumulative": 9
+ "assetCount": 17,
+ "assetCountCumulative": 17
},
{
"name": "painting",
@@ -980,8 +1321,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 15,
- "assetCountCumulative": 15
+ "assetCount": 39,
+ "assetCountCumulative": 39
+ },
+ {
+ "name": "photo",
+ "slug": "photo",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "photo",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "sculpture",
@@ -997,8 +1355,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 15,
- "assetCountCumulative": 15
+ "assetCount": 25,
+ "assetCountCumulative": 25
},
{
"name": "supplies",
@@ -1014,12 +1372,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 8,
- "assetCountCumulative": 8
+ "assetCount": 7,
+ "assetCountCumulative": 7
}
],
- "assetCount": 49,
- "assetCountCumulative": 49
+ "assetCount": 95,
+ "assetCountCumulative": 95
},
{
"name": "character",
@@ -1049,8 +1407,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 14,
- "assetCountCumulative": 14
+ "assetCount": 16,
+ "assetCountCumulative": 16
+ },
+ {
+ "name": "child",
+ "slug": "child",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "child",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "clothing",
@@ -1066,8 +1441,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 9,
- "assetCountCumulative": 9
+ "assetCount": 18,
+ "assetCountCumulative": 18
},
{
"name": "fantasy",
@@ -1083,8 +1458,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 2,
- "assetCountCumulative": 2
+ "assetCount": 10,
+ "assetCountCumulative": 10
},
{
"name": "man",
@@ -1100,8 +1475,42 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 13,
- "assetCountCumulative": 13
+ "assetCount": 18,
+ "assetCountCumulative": 18
+ },
+ {
+ "name": "people",
+ "slug": "people",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "people",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "sci-fi",
+ "slug": "sci-fi-character",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "sci-fi",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "woman",
@@ -1117,12 +1526,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 7,
- "assetCountCumulative": 7
+ "assetCount": 8,
+ "assetCountCumulative": 8
}
],
- "assetCount": 46,
- "assetCountCumulative": 46
+ "assetCount": 71,
+ "assetCountCumulative": 71
},
{
"name": "exterior",
@@ -1152,8 +1561,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 24,
- "assetCountCumulative": 24
+ "assetCount": 31,
+ "assetCountCumulative": 31
},
{
"name": "cityspace",
@@ -1169,8 +1578,76 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 24,
- "assetCountCumulative": 24
+ "assetCount": 30,
+ "assetCountCumulative": 30
+ },
+ {
+ "name": "historic",
+ "slug": "historic",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "historic",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "house",
+ "slug": "house",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "house",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "industrial",
+ "slug": "industrial-exterior",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "industrial",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "landmark",
+ "slug": "landmark",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "landmark",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "landscape",
@@ -1186,8 +1663,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 35,
- "assetCountCumulative": 35
+ "assetCount": 43,
+ "assetCountCumulative": 43
},
{
"name": "public",
@@ -1203,8 +1680,42 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 12,
- "assetCountCumulative": 12
+ "assetCount": 22,
+ "assetCountCumulative": 22
+ },
+ {
+ "name": "sci-fi",
+ "slug": "sci-fi",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "sci-fi",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "stadium",
+ "slug": "stadium",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "stadium",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "street",
@@ -1220,12 +1731,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 35,
- "assetCountCumulative": 35
+ "assetCount": 45,
+ "assetCountCumulative": 45
}
],
- "assetCount": 132,
- "assetCountCumulative": 132
+ "assetCount": 174,
+ "assetCountCumulative": 174
},
{
"name": "food & drink",
@@ -1255,8 +1766,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 33,
- "assetCountCumulative": 33
+ "assetCount": 99,
+ "assetCountCumulative": 99
},
{
"name": "drink",
@@ -1272,8 +1783,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 27,
- "assetCountCumulative": 27
+ "assetCount": 83,
+ "assetCountCumulative": 83
},
{
"name": "drugs",
@@ -1306,12 +1817,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 47,
- "assetCountCumulative": 47
+ "assetCount": 73,
+ "assetCountCumulative": 73
}
],
- "assetCount": 117,
- "assetCountCumulative": 117
+ "assetCount": 269,
+ "assetCountCumulative": 269
},
{
"name": "furniture",
@@ -1341,8 +1852,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 21,
- "assetCountCumulative": 21
+ "assetCount": 36,
+ "assetCountCumulative": 36
},
{
"name": "carpet",
@@ -1358,8 +1869,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 10,
- "assetCountCumulative": 10
+ "assetCount": 13,
+ "assetCountCumulative": 13
},
{
"name": "desk",
@@ -1375,8 +1886,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 21,
- "assetCountCumulative": 21
+ "assetCount": 27,
+ "assetCountCumulative": 27
},
{
"name": "fireplace",
@@ -1409,8 +1920,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 106,
- "assetCountCumulative": 106
+ "assetCount": 143,
+ "assetCountCumulative": 143
},
{
"name": "seating",
@@ -1426,8 +1937,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 179,
- "assetCountCumulative": 179
+ "assetCount": 276,
+ "assetCountCumulative": 276
},
{
"name": "shelving",
@@ -1443,8 +1954,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 38,
- "assetCountCumulative": 38
+ "assetCount": 65,
+ "assetCountCumulative": 65
},
{
"name": "sofa",
@@ -1460,8 +1971,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 57,
- "assetCountCumulative": 57
+ "assetCount": 101,
+ "assetCountCumulative": 101
},
{
"name": "storage",
@@ -1477,8 +1988,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 65,
- "assetCountCumulative": 65
+ "assetCount": 102,
+ "assetCountCumulative": 102
},
{
"name": "table",
@@ -1494,12 +2005,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 106,
- "assetCountCumulative": 106
+ "assetCount": 184,
+ "assetCountCumulative": 184
}
],
- "assetCount": 611,
- "assetCountCumulative": 611
+ "assetCount": 955,
+ "assetCountCumulative": 955
},
{
"name": "industrial",
@@ -1516,6 +2027,57 @@
"metaExtra": "",
"children": [
{
+ "name": "agriculture",
+ "slug": "agriculture",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "agriculture",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "communication",
+ "slug": "communication",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "communication",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "construction",
+ "slug": "construction",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "construction",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "container",
"slug": "container-industrial",
"active": true,
@@ -1529,8 +2091,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 48,
- "assetCountCumulative": 48
+ "assetCount": 66,
+ "assetCountCumulative": 66
+ },
+ {
+ "name": "machine",
+ "slug": "machine",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "machine",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 18,
+ "assetCountCumulative": 18
},
{
"name": "tool",
@@ -1546,12 +2125,29 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 29,
- "assetCountCumulative": 29
+ "assetCount": 62,
+ "assetCountCumulative": 62
+ },
+ {
+ "name": "utility",
+ "slug": "utility-industrial",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "part",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
],
- "assetCount": 85,
- "assetCountCumulative": 85
+ "assetCount": 157,
+ "assetCountCumulative": 157
},
{
"name": "interior",
@@ -1581,8 +2177,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 96,
- "assetCountCumulative": 96
+ "assetCount": 133,
+ "assetCountCumulative": 133
},
{
"name": "bedroom",
@@ -1598,8 +2194,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 64,
- "assetCountCumulative": 64
+ "assetCount": 83,
+ "assetCountCumulative": 83
},
{
"name": "decoration",
@@ -1615,8 +2211,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 291,
- "assetCountCumulative": 291
+ "assetCount": 385,
+ "assetCountCumulative": 385
},
{
"name": "hall",
@@ -1632,8 +2228,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 8,
- "assetCountCumulative": 8
+ "assetCount": 12,
+ "assetCountCumulative": 12
},
{
"name": "kids room",
@@ -1649,8 +2245,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 42,
- "assetCountCumulative": 42
+ "assetCount": 47,
+ "assetCountCumulative": 47
},
{
"name": "kitchen",
@@ -1666,8 +2262,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 181,
- "assetCountCumulative": 181
+ "assetCount": 258,
+ "assetCountCumulative": 258
},
{
"name": "living room",
@@ -1683,8 +2279,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 127,
- "assetCountCumulative": 127
+ "assetCount": 170,
+ "assetCountCumulative": 170
},
{
"name": "office",
@@ -1700,8 +2296,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 48,
- "assetCountCumulative": 48
+ "assetCount": 60,
+ "assetCountCumulative": 60
},
{
"name": "utility",
@@ -1717,12 +2313,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 74,
- "assetCountCumulative": 74
+ "assetCount": 150,
+ "assetCountCumulative": 150
}
],
- "assetCount": 932,
- "assetCountCumulative": 932
+ "assetCount": 1299,
+ "assetCountCumulative": 1299
},
{
"name": "military",
@@ -1739,6 +2335,23 @@
"metaExtra": "",
"children": [
{
+ "name": "air",
+ "slug": "air",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "air",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 2,
+ "assetCountCumulative": 2
+ },
+ {
"name": "equipment",
"slug": "equipment",
"active": true,
@@ -1752,8 +2365,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 3,
- "assetCountCumulative": 3
+ "assetCount": 5,
+ "assetCountCumulative": 5
+ },
+ {
+ "name": "ground",
+ "slug": "ground",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "ground",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "historic",
@@ -1769,8 +2399,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 6,
- "assetCountCumulative": 6
+ "assetCount": 9,
+ "assetCountCumulative": 9
+ },
+ {
+ "name": "naval",
+ "slug": "naval",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "naval",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "weapon",
@@ -1786,12 +2433,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 29,
- "assetCountCumulative": 29
+ "assetCount": 44,
+ "assetCountCumulative": 44
}
],
- "assetCount": 40,
- "assetCountCumulative": 40
+ "assetCount": 64,
+ "assetCountCumulative": 64
},
{
"name": "music",
@@ -1821,12 +2468,63 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 13,
- "assetCountCumulative": 13
+ "assetCount": 22,
+ "assetCountCumulative": 22
+ },
+ {
+ "name": "instruments",
+ "slug": "instruments",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "instruments",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 5,
+ "assetCountCumulative": 5
+ },
+ {
+ "name": "stage",
+ "slug": "stage",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "stage",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "studio",
+ "slug": "studio",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "studio",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
],
- "assetCount": 13,
- "assetCountCumulative": 13
+ "assetCount": 27,
+ "assetCountCumulative": 27
},
{
"name": "nature",
@@ -1856,8 +2554,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 7,
- "assetCountCumulative": 7
+ "assetCount": 11,
+ "assetCountCumulative": 11
},
{
"name": "atmosphere",
@@ -1890,8 +2588,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 35,
- "assetCountCumulative": 35
+ "assetCount": 47,
+ "assetCountCumulative": 47
},
{
"name": "plant",
@@ -1907,8 +2605,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 48,
- "assetCountCumulative": 48
+ "assetCount": 80,
+ "assetCountCumulative": 80
},
{
"name": "tree",
@@ -1924,12 +2622,29 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 47,
- "assetCountCumulative": 47
+ "assetCount": 109,
+ "assetCountCumulative": 109
+ },
+ {
+ "name": "weather",
+ "slug": "weather",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "weather",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
],
- "assetCount": 141,
- "assetCountCumulative": 141
+ "assetCount": 251,
+ "assetCountCumulative": 251
},
{
"name": "space",
@@ -1946,6 +2661,57 @@
"metaExtra": "",
"children": [
{
+ "name": "astronomy",
+ "slug": "astronomy",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "astronomy",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "planets",
+ "slug": "planets",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "planets",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "sci-fi",
+ "slug": "sci-fi-space",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "sci-fi",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "spacecraft",
"slug": "spacecraft",
"active": true,
@@ -1959,12 +2725,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 9,
- "assetCountCumulative": 9
+ "assetCount": 21,
+ "assetCountCumulative": 21
}
],
- "assetCount": 10,
- "assetCountCumulative": 10
+ "assetCount": 22,
+ "assetCountCumulative": 22
},
{
"name": "sports",
@@ -1981,6 +2747,23 @@
"metaExtra": "",
"children": [
{
+ "name": "animal",
+ "slug": "animal",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "animal",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
"name": "exercise",
"slug": "exercise",
"active": true,
@@ -1994,8 +2777,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 12,
- "assetCountCumulative": 12
+ "assetCount": 14,
+ "assetCountCumulative": 14
},
{
"name": "extreme",
@@ -2011,8 +2794,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 4,
- "assetCountCumulative": 4
+ "assetCount": 7,
+ "assetCountCumulative": 7
},
{
"name": "individual",
@@ -2028,8 +2811,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 5,
- "assetCountCumulative": 5
+ "assetCount": 6,
+ "assetCountCumulative": 6
},
{
"name": "outdoor",
@@ -2045,8 +2828,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 4,
- "assetCountCumulative": 4
+ "assetCount": 8,
+ "assetCountCumulative": 8
},
{
"name": "team",
@@ -2062,12 +2845,12 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 6,
- "assetCountCumulative": 6
+ "assetCount": 15,
+ "assetCountCumulative": 15
}
],
- "assetCount": 31,
- "assetCountCumulative": 31
+ "assetCount": 50,
+ "assetCountCumulative": 50
},
{
"name": "technology",
@@ -2084,6 +2867,23 @@
"metaExtra": "",
"children": [
{
+ "name": "ai",
+ "slug": "ai",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "ai",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 6,
+ "assetCountCumulative": 6
+ },
+ {
"name": "audio",
"slug": "audio",
"active": true,
@@ -2097,8 +2897,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 16,
- "assetCountCumulative": 16
+ "assetCount": 33,
+ "assetCountCumulative": 33
},
{
"name": "computer",
@@ -2114,8 +2914,42 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 102,
- "assetCountCumulative": 102
+ "assetCount": 140,
+ "assetCountCumulative": 140
+ },
+ {
+ "name": "medical",
+ "slug": "medical",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "medical",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 2,
+ "assetCountCumulative": 2
+ },
+ {
+ "name": "medicine",
+ "slug": "medicine",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "medicine",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "phone",
@@ -2131,8 +2965,8 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 8,
- "assetCountCumulative": 8
+ "assetCount": 15,
+ "assetCountCumulative": 15
},
{
"name": "photography",
@@ -2148,12 +2982,46 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
+ "assetCount": 11,
+ "assetCountCumulative": 11
+ },
+ {
+ "name": "science",
+ "slug": "science",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "science",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 1,
+ "assetCountCumulative": 1
+ },
+ {
+ "name": "video",
+ "slug": "video",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "video",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
"assetCount": 6,
"assetCountCumulative": 6
}
],
- "assetCount": 138,
- "assetCountCumulative": 138
+ "assetCount": 222,
+ "assetCountCumulative": 222
},
{
"name": "vehicle",
@@ -2170,6 +3038,40 @@
"metaExtra": "",
"children": [
{
+ "name": "bicycle",
+ "slug": "bicycle",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "bicycle",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "bus",
+ "slug": "bus",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "bus",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 2,
+ "assetCountCumulative": 2
+ },
+ {
"name": "car",
"slug": "car",
"active": true,
@@ -2183,8 +3085,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 43,
- "assetCountCumulative": 43
+ "assetCount": 63,
+ "assetCountCumulative": 63
+ },
+ {
+ "name": "historic",
+ "slug": "historic-vehicle",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "historic",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "industrial",
@@ -2200,8 +3119,25 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 5,
- "assetCountCumulative": 5
+ "assetCount": 7,
+ "assetCountCumulative": 7
+ },
+ {
+ "name": "motorcycle",
+ "slug": "motorcycle",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "motorcycle",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
},
{
"name": "part",
@@ -2217,32 +3153,222 @@
"metaKeywords": "",
"metaExtra": "",
"children": [],
- "assetCount": 20,
- "assetCountCumulative": 20
+ "assetCount": 54,
+ "assetCountCumulative": 54
},
{
- "name": "Animals",
- "slug": "animals",
+ "name": "train",
+ "slug": "train",
"active": true,
"thumbnail": null,
"thumbnailWidth": null,
"thumbnailHeight": null,
"order": 0,
- "alternateTitle": "Animals",
+ "alternateTitle": "train",
"alternateUrl": "",
"description": "",
"metaKeywords": "",
"metaExtra": "",
"children": [],
"assetCount": 0,
- "assetCountCumulative": 11
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "truck",
+ "slug": "truck",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "truck",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
],
- "assetCount": 70,
- "assetCountCumulative": 70
+ "assetCount": 129,
+ "assetCountCumulative": 129
+ },
+ {
+ "name": "watercraft",
+ "slug": "watercraft",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "watercraft",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [
+ {
+ "name": "historic",
+ "slug": "historic-watercraft",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "historic",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "industrial",
+ "slug": "industrial-watercraft",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "industrial",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "part",
+ "slug": "part-watercraft",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "part",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "personal",
+ "slug": "personal",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "personal",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "recreational",
+ "slug": "recreational",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "recreational",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ }
+ ],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ }
+ ],
+ "assetCount": 4025,
+ "assetCountCumulative": 4025
+ },
+ {
+ "name": "texture",
+ "slug": "texture",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "texture",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [
+ {
+ "name": "Animals",
+ "slug": "animals",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "Animals",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [
+ {
+ "name": "Mammals",
+ "slug": "mammals",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "Mammals",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ },
+ {
+ "name": "Plants",
+ "slug": "plants",
+ "active": true,
+ "thumbnail": null,
+ "thumbnailWidth": null,
+ "thumbnailHeight": null,
+ "order": 0,
+ "alternateTitle": "Plants",
+ "alternateUrl": "",
+ "description": "",
+ "metaKeywords": "",
+ "metaExtra": "",
+ "children": [],
+ "assetCount": 0,
+ "assetCountCumulative": 0
+ }
+ ],
+ "assetCount": 0,
+ "assetCountCumulative": 12
}
],
- "assetCount": 2572,
- "assetCountCumulative": 2572
+ "assetCount": 0,
+ "assetCountCumulative": 0
}
] \ No newline at end of file
diff --git a/blenderkit/download.py b/blenderkit/download.py
index 8749b696..dae95a11 100644
--- a/blenderkit/download.py
+++ b/blenderkit/download.py
@@ -290,6 +290,8 @@ def udpate_asset_data_in_dicts(asset_data):
id = asset_data['assetBaseId']
scene['assets rated'][id] = scene['assets rated'].get(id, False)
sr = bpy.context.scene['search results']
+ if not sr:
+ return;
for i, r in enumerate(sr):
if r['assetBaseId'] == asset_data['assetBaseId']:
for f in asset_data['files']:
@@ -323,6 +325,11 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
props = scene.blenderkit
asset_main = scene
+ if asset_data['assetType'] == 'hdr':
+ hdr = append_link.load_HDR(file_name = file_names[0], name = asset_data['name'])
+ props = hdr.blenderkit
+ asset_main = hdr
+
s = bpy.context.scene
if asset_data['assetType'] == 'model':
@@ -604,7 +611,7 @@ def timer_update():
if ((bpy.context.mode == 'OBJECT' and \
(at == 'model' or at == 'material'))) \
or ((at == 'brush') \
- and wm.get('appendable') == True) or at == 'scene':
+ and wm.get('appendable') == True) or at == 'scene' or at == 'hdr':
# don't do this stuff in editmode and other modes, just wait...
download_threads.remove(threaddata)
@@ -646,14 +653,8 @@ def timer_update():
if not done:
at = asset_data['assetType']
tcom.passargs['retry_counter'] = tcom.passargs.get('retry_counter', 0) + 1
- if at in ('model', 'material'):
- download(asset_data, **tcom.passargs)
- elif asset_data['assetType'] == 'material':
- download(asset_data, **tcom.passargs)
- elif asset_data['assetType'] == 'scene':
- download(asset_data, **tcom.passargs)
- elif asset_data['assetType'] == 'brush' or asset_data['assetType'] == 'texture':
- download(asset_data, **tcom.passargs)
+ download(asset_data, **tcom.passargs)
+
if bpy.context.scene['search results'] is not None and done:
for sres in bpy.context.scene['search results']:
if asset_data['id'] == sres['id']:
@@ -776,14 +777,14 @@ class Downloader(threading.Thread):
# this sends the thread for processing, where another check should occur, since the file might be corrupted.
tcom.downloaded = 100
utils.p('not downloading, trying to append again')
- return;
+ return
file_name = paths.get_download_filepaths(asset_data, self.resolution)[0] # prefer global dir if possible.
# for k in asset_data:
# print(asset_data[k])
if self.stopped():
utils.p('stopping download: ' + asset_data['name'])
- return;
+ return
download_canceled = False
with open(file_name, "wb") as f:
@@ -827,7 +828,7 @@ class Downloader(threading.Thread):
if download_canceled:
delete_unfinished_file(file_name)
- return;
+ return
# unpack the file immediately after download
tcom.report = f'Unpacking files'
@@ -1170,15 +1171,14 @@ def start_download(asset_data, **kwargs):
'rotation': kwargs['model_rotation']}
download(asset_data, downloaders=[downloader], **kwargs)
- elif asset_data['assetType'] == 'scene':
- download(asset_data, **kwargs)
- elif asset_data['assetType'] == 'brush' or asset_data['assetType'] == 'texture':
+ else:
download(asset_data, **kwargs)
asset_types = (
('MODEL', 'Model', 'set of objects'),
('SCENE', 'Scene', 'scene'),
+ ('HDR', 'Hdr', 'hdr'),
('MATERIAL', 'Material', 'any .blend Material'),
('TEXTURE', 'Texture', 'a texture, or texture set'),
('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
diff --git a/blenderkit/image_utils.py b/blenderkit/image_utils.py
new file mode 100644
index 00000000..b2ade804
--- /dev/null
+++ b/blenderkit/image_utils.py
@@ -0,0 +1,84 @@
+import bpy
+import numpy
+import os
+
+def get_orig_render_settings():
+ rs = bpy.context.scene.render
+ ims = rs.image_settings
+
+ vs = bpy.context.scene.view_settings
+
+ orig_settings = {
+ 'file_format': ims.file_format,
+ 'quality': ims.quality,
+ 'color_mode': ims.color_mode,
+ 'compression': ims.compression,
+ 'view_transform': vs.view_transform
+ }
+ return orig_settings
+
+
+def set_orig_render_settings(orig_settings):
+ rs = bpy.context.scene.render
+ ims = rs.image_settings
+ vs = bpy.context.scene.view_settings
+
+ ims.file_format = orig_settings['file_format']
+ ims.quality = orig_settings['quality']
+ ims.color_mode = orig_settings['color_mode']
+ ims.compression = orig_settings['compression']
+
+ vs.view_transform = orig_settings['view_transform']
+
+
+def img_save_as(img, filepath='//', file_format='JPEG', quality=90, color_mode='RGB', compression=15):
+ '''Uses Blender 'save render' to save images - BLender isn't really able so save images with other methods correctly.'''
+
+ ors = get_orig_render_settings()
+
+ rs = bpy.context.scene.render
+ ims = rs.image_settings
+ ims.file_format = file_format
+ ims.quality = quality
+ ims.color_mode = color_mode
+ ims.compression = compression
+
+ img.save_render(filepath=bpy.path.abspath(filepath), scene=bpy.context.scene)
+
+ set_orig_render_settings(ors)
+
+
+def generate_hdr_thumbnail():
+ scene = bpy.context.scene
+ ui_props = scene.blenderkitUI
+ hdr_image = ui_props.hdr_upload_image#bpy.data.images.get(ui_props.hdr_upload_image)
+
+ base, ext = os.path.splitext(hdr_image.filepath)
+ thumb_path = base + '.jpg'
+ thumb_name = os.path.basename(thumb_path)
+
+ max_thumbnail_size = 2048
+ size = hdr_image.size
+ ratio = size[0] / size[1]
+
+ imageWidth = size[0]
+ imageHeight = size[1]
+ thumbnailWidth = min(size[0], max_thumbnail_size)
+ thumbnailHeight = min(size[1], int(max_thumbnail_size / ratio))
+
+ tempBuffer = numpy.empty(imageWidth * imageHeight * 4, dtype=numpy.float32)
+ inew = bpy.data.images.new(thumb_name, imageWidth, imageHeight, alpha=False, float_buffer=False)
+
+ hdr_image.pixels.foreach_get(tempBuffer)
+
+ inew.filepath = thumb_path
+ inew.colorspace_settings.name = 'Linear'
+ inew.pixels.foreach_set(tempBuffer)
+
+ bpy.context.view_layer.update()
+ if thumbnailWidth < imageWidth:
+ inew.scale(thumbnailWidth, thumbnailHeight)
+
+ img_save_as(inew, filepath=inew.filepath)
+
+
diff --git a/blenderkit/paths.py b/blenderkit/paths.py
index ef0dd19d..95c5fd93 100644
--- a/blenderkit/paths.py
+++ b/blenderkit/paths.py
@@ -31,6 +31,8 @@ BLENDERKIT_MANUAL = "https://youtu.be/1hVgcQhIAo8"
BLENDERKIT_MODEL_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/upload/"
BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-material/"
BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-brush/"
+BLENDERKIT_HDR_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-brush/"
+BLENDERKIT_SCENE_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-brush/"
BLENDERKIT_LOGIN_URL = "https://www.blenderkit.com/accounts/login"
BLENDERKIT_OAUTH_LANDING_URL = "/oauth-landing/"
BLENDERKIT_SIGNUP_URL = "https://www.blenderkit.com/accounts/register"
@@ -295,6 +297,8 @@ def get_download_filepaths(asset_data, resolution='blend', can_return_others = F
# utils.pprint('get download filenames ', dict(res_file))
file_names = []
+ if not res_file:
+ return file_names
# fn = asset_data['file_name'].replace('blend_', '')
if res_file.get('url') is not None:
#Tweak the names a bit:
diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py
index b6ad0185..a2ade101 100644
--- a/blenderkit/ratings.py
+++ b/blenderkit/ratings.py
@@ -208,6 +208,7 @@ def get_assets_for_rating():
asset_types = (
('MODEL', 'Model', 'set of objects'),
('SCENE', 'Scene', 'scene'),
+ ('HDR', 'HDR', 'hdr'),
('MATERIAL', 'Material', 'any .blend Material'),
('TEXTURE', 'Texture', 'a texture, or texture set'),
('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
@@ -269,7 +270,7 @@ def update_quality_ui(self, context):
# return
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
- self.rating_quality_ui = '0'
+ # self.rating_quality_ui = '0'
self.rating_quality = int(self.rating_quality_ui)
@@ -281,7 +282,7 @@ def update_ratings_work_hours_ui(self, context):
# return
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
- self.rating_work_hours_ui = '0'
+ # self.rating_work_hours_ui = '0'
self.rating_work_hours = float(self.rating_work_hours_ui)
def update_ratings_work_hours_ui_1_5(self, context):
@@ -292,7 +293,7 @@ def update_ratings_work_hours_ui_1_5(self, context):
# return
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
- self.rating_work_hours_ui_1_5 = '0'
+ # self.rating_work_hours_ui_1_5 = '0'
# print('updating 1-5')
# print(float(self.rating_work_hours_ui_1_5))
self.rating_work_hours = float(self.rating_work_hours_ui_1_5)
diff --git a/blenderkit/resolutions.py b/blenderkit/resolutions.py
index 89ddb6d6..a3de18ee 100644
--- a/blenderkit/resolutions.py
+++ b/blenderkit/resolutions.py
@@ -179,13 +179,6 @@ def save_image_safely(teximage, filepath):
orig_color_mode = ims.color_mode
orig_compression = ims.compression
- ims = rs.image_settings
-
- orig_file_format = ims.file_format
- orig_quality = ims.quality
- orig_color_mode = ims.color_mode
- orig_compression = ims.compression
-
ims.file_format = teximage.file_format
if teximage.file_format == 'PNG':
ims.color_mode = 'RGBA'
@@ -317,6 +310,7 @@ def upload_resolutions(files, data):
preferences = bpy.context.preferences.addons['blenderkit'].preferences
upload_data = {
+ "name": data['asset_data']['name'],
"token": preferences.api_key,
"id": data['asset_data']['id']
}
@@ -743,18 +737,19 @@ def iterate_for_resolutions(filepath, process_count=12, api_key=''):
if check_needs_resolutions(asset_data):
print('downloading and generating resolution for %s' % asset_data['name'])
# this is just a quick hack for not using original dirs in blendrkit...
- thread = threading.Thread(target=generate_resolution_thread, args=(asset_data, api_key))
- thread.start()
-
- threads.append(thread)
- print('processes ', len(threads))
- while len(threads) > process_count - 1:
- for proc in threads:
- if not proc.is_alive():
- threads.remove(proc)
- break;
- else:
- print(f'Failed to retrieve asset from server:{asset_data["name"]}')
+ generate_resolution_thread(asset_data,api_key)
+ # thread = threading.Thread(target=generate_resolution_thread, args=(asset_data, api_key))
+ # thread.start()
+ #
+ # threads.append(thread)
+ # print('processes ', len(threads))
+ # while len(threads) > process_count - 1:
+ # for t in threads:
+ # if not t.is_alive():
+ # threads.remove(t)
+ # break;
+ # else:
+ # print(f'Failed to generate resolution:{asset_data["name"]}')
else:
print('not generated resolutions:', asset_data['name'])
diff --git a/blenderkit/search.py b/blenderkit/search.py
index c04d5705..f54d97d3 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -372,6 +372,9 @@ def timer_update():
if asset_type == 'scene':
props = scene.blenderkit_scene
# json_filepath = os.path.join(icons_dir, 'scene_searchresult.json')
+ if asset_type == 'hdr':
+ props = scene.blenderkit_HDR
+ # json_filepath = os.path.join(icons_dir, 'scene_searchresult.json')
if asset_type == 'material':
props = scene.blenderkit_mat
# json_filepath = os.path.join(icons_dir, 'material_searchresult.json')
@@ -1131,6 +1134,19 @@ def build_query_scene():
return query
+def build_query_HDR():
+ '''use all search input to request results from server'''
+
+ props = bpy.context.scene.blenderkit_scene
+ query = {
+ "asset_type": 'hdr',
+ # "engine": props.search_engine,
+ # "adult": props.search_adult,
+ }
+ build_query_common(query, props)
+ return query
+
+
def build_query_material():
props = bpy.context.scene.blenderkit_mat
query = {
@@ -1221,7 +1237,8 @@ def add_search_process(query, params, orig_result):
while (len(search_threads) > 0):
old_thread = search_threads.pop(0)
old_thread[0].stop()
- # TODO CARE HERE FOR ALSO KILLING THE THREADS...AT LEAST NOW SEARCH DONE FIRST WON'T REWRITE AN OLDER ONE
+ # TODO CARE HERE FOR ALSO KILLING THE Thumbnail THREADS.?
+ # AT LEAST NOW SEARCH DONE FIRST WON'T REWRITE AN OLDER ONE
tempdir = paths.get_temp_dir('%s_search' % query['asset_type'])
thread = Searcher(query, params, orig_result)
@@ -1304,6 +1321,12 @@ def search(category='', get_next=False, author_id=''):
props = scene.blenderkit_scene
query = build_query_scene()
+ if ui_props.asset_type == 'HDR':
+ if not hasattr(scene, 'blenderkit_HDR'):
+ return;
+ props = scene.blenderkit_HDR
+ query = build_query_HDR()
+
if ui_props.asset_type == 'MATERIAL':
if not hasattr(scene, 'blenderkit_mat'):
return;
@@ -1367,7 +1390,7 @@ def search_update(self, context):
if ui_props.down_up != 'SEARCH':
ui_props.down_up = 'SEARCH'
- # here we tweak the input if it comes form the clipboard. we need to get rid of asset type and set it to
+ # here we tweak the input if it comes form the clipboard. we need to get rid of asset type and set it in UI
sprops = utils.get_search_props()
instr = 'asset_base_id:'
atstr = 'asset_type:'
@@ -1376,8 +1399,6 @@ def search_update(self, context):
ati = kwds.find(atstr)
# if the asset type already isn't there it means this update function
# was triggered by it's last iteration and needs to cancel
- if idi > -1 and ati == -1:
- return;
if ati > -1:
at = kwds[ati:].lower()
# uncertain length of the remaining string - find as better method to check the presence of asset type
@@ -1387,12 +1408,19 @@ def search_update(self, context):
ui_props.asset_type = 'MATERIAL'
elif at.find('brush') > -1:
ui_props.asset_type = 'BRUSH'
+ elif at.find('scene') > -1:
+ ui_props.asset_type = 'SCENE'
+ elif at.find('hdr') > -1:
+ ui_props.asset_type = 'HDR'
# now we trim the input copypaste by anything extra that is there,
# this is also a way for this function to recognize that it already has parsed the clipboard
# the search props can have changed and this needs to transfer the data to the other field
# this complex behaviour is here for the case where the user needs to paste manually into blender?
sprops = utils.get_search_props()
sprops.search_keywords = kwds[:ati].rstrip()
+ #return here since writing into search keywords triggers this update function once more.
+ return
+
search()
diff --git a/blenderkit/ui.py b/blenderkit/ui.py
index fdba3971..91b8397a 100644
--- a/blenderkit/ui.py
+++ b/blenderkit/ui.py
@@ -61,6 +61,7 @@ reports = []
mappingdict = {
'MODEL': 'model',
'SCENE': 'scene',
+ 'HDR': 'hdr',
'MATERIAL': 'material',
'TEXTURE': 'texture',
'BRUSH': 'brush'
@@ -699,12 +700,11 @@ def draw_callback_2d_progress(self, context):
draw_downloader(loc[0], loc[1], percent=tcom.progress, img=img, text=tcom.report)
else:
draw_downloader(loc[0], loc[1], percent=tcom.progress, img=img, text=tcom.report)
-
-
else:
draw_progress(x, y - index * 30, text='downloading %s' % asset_data['name'],
percent=tcom.progress)
index += 1
+
for process in bg_blender.bg_processes:
tcom = process[1]
draw_progress(x, y - index * 30, '%s' % tcom.lasttext,
@@ -721,6 +721,11 @@ def draw_callback_2d_upload_preview(self, context):
ui_props = context.scene.blenderkitUI
props = utils.get_upload_props()
+
+ #assets which don't need asset preview
+ if ui_props.asset_type =='HDR':
+ return
+
if props != None and ui_props.draw_tooltip:
if ui_props.asset_type != 'BRUSH':
@@ -1378,12 +1383,11 @@ class AssetBarOperator(bpy.types.Operator):
ao = bpy.context.active_object
if ui_props.asset_type == 'MODEL' and ao != None \
or ui_props.asset_type == 'MATERIAL' and ao != None and ao.active_material != None \
- or ui_props.asset_type == 'BRUSH' and utils.get_active_brush() is not None:
- export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props = upload.get_upload_data(
- self,
- context,
- ui_props.asset_type)
- ui_props.tooltip = search.generate_tooltip(upload_data)
+ or ui_props.asset_type == 'BRUSH' and utils.get_active_brush() is not None\
+ or ui_props.asset_type =='SCENE' or ui_props.asset_type == 'HDR':
+ export_data, upload_data = upload.get_upload_data(self, context, ui_props.asset_type)
+ if upload_data:
+ ui_props.tooltip = search.generate_tooltip(upload_data)
return {'PASS_THROUGH'}
@@ -1548,12 +1552,13 @@ class AssetBarOperator(bpy.types.Operator):
ui_props = context.scene.blenderkitUI
if event.value == 'PRESS' and ui_props.active_index > -1:
+ #start dragging models and materials
if ui_props.asset_type == 'MODEL' or ui_props.asset_type == 'MATERIAL':
# check if asset is locked and let the user know in that case
asset_search_index = ui_props.active_index
asset_data = sr[asset_search_index]
if not asset_data.get('canDownload'):
- message = "Let's support asset creators and Blender development."
+ message = "Let's support asset creators and Open source."
link_text = 'Unlock the asset.'
url = paths.get_bkit_url() + '/get-blenderkit/' + asset_data['id'] + '/?from_addon'
bpy.ops.wm.blenderkit_url_dialog('INVOKE_REGION_WIN', url=url, message=message,
@@ -1670,7 +1675,9 @@ class AssetBarOperator(bpy.types.Operator):
if asset_search_index == -3:
return {'RUNNING_MODAL'}
+
if asset_search_index > -3:
+ #picking of assets and using them
if ui_props.asset_type == 'MATERIAL':
if target_object != '':
# position is for downloader:
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
index 4cf9e6dc..9d5777a1 100644
--- a/blenderkit/ui_panels.py
+++ b/blenderkit/ui_panels.py
@@ -101,11 +101,16 @@ def draw_upload_common(layout, props, asset_type, context):
op.url = paths.BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL
if asset_type == 'BRUSH':
op.url = paths.BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL
+ if asset_type == 'SCENE':
+ op.url = paths.BLENDERKIT_SCENE_UPLOAD_INSTRUCTIONS_URL
+ if asset_type == 'HDR':
+ op.url = paths.BLENDERKIT_HDR_UPLOAD_INSTRUCTIONS_URL
row = layout.row(align=True)
if props.upload_state != '':
utils.label_multiline(layout, text=props.upload_state, width=context.region.width)
if props.uploading:
+ print(bpy.context.scene.name)
op = layout.operator('object.kill_bg_process', text="", icon='CANCEL')
op.process_source = asset_type
op.process_type = 'UPLOAD'
@@ -165,6 +170,38 @@ def prop_needed(layout, props, name, value, is_not_filled=''):
icon = None
row.prop(props, name)
+def draw_panel_hdr_upload(self, context):
+ layout = self.layout
+ ui_props = bpy.context.scene.blenderkitUI
+
+ # layout.prop_search(ui_props, "hdr_upload_image", bpy.data, "images")
+ layout.prop(ui_props, "hdr_upload_image")
+
+ hdr = utils.get_active_HDR()
+
+
+ if hdr is not None:
+ props = hdr.blenderkit
+
+ layout = self.layout
+
+ draw_upload_common(layout, props, 'HDR', context)
+
+ layout.prop(props, 'name')
+ layout.prop(props, 'description')
+ layout.prop(props, 'tags')
+
+def draw_panel_hdr_search(self, context):
+ s = context.scene
+ props = s.blenderkit_HDR
+
+ layout = self.layout
+ row = layout.row()
+ row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
+ draw_assetbar_show_hide(row, props)
+ layout.prop(props, "own_only")
+
+ utils.label_multiline(layout, text=props.report)
def draw_panel_model_upload(self, context):
ob = bpy.context.active_object
@@ -225,9 +262,9 @@ def draw_panel_scene_upload(self, context):
props = s.blenderkit
layout = self.layout
- if bpy.app.debug_value != -1:
- layout.label(text='Scene upload not Implemented')
- return
+ # if bpy.app.debug_value != -1:
+ # layout.label(text='Scene upload not Implemented')
+ # return
draw_upload_common(layout, props, 'SCENE', context)
# layout = layout.column()
@@ -259,6 +296,7 @@ def draw_panel_scene_upload(self, context):
# elif props.thumbnail_generating_state != '':
# utils.label_multiline(layout, text = props.thumbnail_generating_state)
+ layout.prop(props, 'is_free')
layout.prop(props, 'description')
layout.prop(props, 'tags')
layout.prop(props, 'style')
@@ -664,8 +702,8 @@ def draw_panel_brush_upload(self, context):
def draw_panel_brush_search(self, context):
- wm = context.scene
- props = wm.blenderkit_brush
+ s = context.scene
+ props = s.blenderkit_brush
layout = self.layout
row = layout.row()
@@ -916,8 +954,9 @@ class VIEW3D_PT_blenderkit_unified(Panel):
row = layout.row(align=True)
row.scale_x = 1.6
row.scale_y = 1.6
- # split = row.split(factor=.5)
- row.prop(ui_props, 'asset_type', expand=True, icon_only=False)
+ # split = row.split(factor=.
+ col = layout.column()
+ col.prop(ui_props, 'asset_type', expand=True, icon_only=False)
# row = layout.column(align = False)
# layout.prop(ui_props, 'asset_type', expand=False, text='')
@@ -954,7 +993,9 @@ class VIEW3D_PT_blenderkit_unified(Panel):
if ui_props.asset_type == 'SCENE':
# noinspection PyCallByClass
draw_panel_scene_search(self, context)
-
+ if ui_props.asset_type == 'HDR':
+ # noinspection PyCallByClass
+ draw_panel_hdr_search(self, context)
elif ui_props.asset_type == 'MATERIAL':
draw_panel_material_search(self, context)
elif ui_props.asset_type == 'BRUSH':
@@ -962,7 +1003,7 @@ class VIEW3D_PT_blenderkit_unified(Panel):
# noinspection PyCallByClass
draw_panel_brush_search(self, context)
else:
- utils.label_multiline(layout, text='switch to paint or sculpt mode.', width=context.region.width)
+ utils.label_multiline(layout, text='Switch to paint or sculpt mode.', width=context.region.width)
return
@@ -991,6 +1032,8 @@ class VIEW3D_PT_blenderkit_unified(Panel):
layout.label(text='selet object to upload')
elif ui_props.asset_type == 'SCENE':
draw_panel_scene_upload(self, context)
+ elif ui_props.asset_type == 'HDR':
+ draw_panel_hdr_upload(self, context)
elif ui_props.asset_type == 'MATERIAL':
# utils.label_multiline(layout, "Uploaded materials won't be available in b2.79", icon='ERROR')
@@ -1004,7 +1047,7 @@ class VIEW3D_PT_blenderkit_unified(Panel):
if context.sculpt_object or context.image_paint_object:
draw_panel_brush_upload(self, context)
else:
- layout.label(text='switch to paint or sculpt mode.')
+ layout.label(text='Switch to paint or sculpt mode.')
elif ui_props.down_up == 'RATING': # the poll functions didn't work here, don't know why.
@@ -1159,6 +1202,7 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False):
op.model_rotation = o.rotation_euler
op.target_object = o.name
op.material_target_slot = o.active_material_index
+
elif asset_data['assetType'] == 'MATERIAL':
aob = bpy.context.active_object
op.model_location = aob.location
@@ -1166,6 +1210,8 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False):
op.target_object = aob.name
op.material_target_slot = aob.active_material_index
op.replace_resolution = True
+ op.replace = False
+
op.invoke_resolution = True
op.max_resolution = asset_data.get('max_resolution',
0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
@@ -1177,6 +1223,7 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False):
op.asset_index = ui_props.active_index
# op.asset_type = ui_props.asset_type
op.replace_resolution = True
+ op.replace = False
op.invoke_resolution = True
o = utils.get_active_model()
if o and o.get('asset_data'):
@@ -1514,11 +1561,15 @@ def header_search_draw(self, context):
props = s.blenderkit_mat
if ui_props.asset_type == 'BRUSH':
props = s.blenderkit_brush
+ if ui_props.asset_type == 'HDR':
+ props = s.blenderkit_HDR
+ if ui_props.asset_type == 'SCENE':
+ props = s.blenderkit_scene
# the center snap menu is in edit and object mode if tool settings are off.
if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'):
layout.separator_spacer()
- layout.prop(ui_props, "asset_type", text='', icon='URL')
+ layout.prop(ui_props, "asset_type", expand = True, icon_only = True, text='', icon='URL')
layout.prop(props, "search_keywords", text="", icon='VIEWZOOM')
draw_assetbar_show_hide(layout, props)
diff --git a/blenderkit/upload.py b/blenderkit/upload.py
index f99b4d56..89fa50d6 100644
--- a/blenderkit/upload.py
+++ b/blenderkit/upload.py
@@ -33,15 +33,19 @@ if "bpy" in locals():
colors = reload(colors)
rerequests = reload(rerequests)
categories = reload(categories)
+ upload_bg = reload(upload_bg)
+ tasks_queue = reload(tasks_queue)
+ image_utils = reload(image_utils)
else:
from blenderkit import asset_inspector, paths, utils, bg_blender, autothumb, version_checker, search, ui_panels, ui, \
- overrides, colors, rerequests, categories
+ overrides, colors, rerequests, categories, upload_bg, tasks_queue, image_utils
import tempfile, os, subprocess, json, re
import bpy
import requests
import threading
+import sys
BLENDERKIT_EXPORT_DATA_FILE = "data.json"
@@ -82,13 +86,11 @@ def add_version(data):
data["addonVersion"] = addon_version
-
-
def write_to_report(props, text):
props.report = props.report + text + '\n'
-def get_missing_data_model(props):
+def check_missing_data_model(props):
props.report = ''
autothumb.update_upload_model_preview(None, None)
@@ -106,7 +108,7 @@ def get_missing_data_model(props):
write_to_report(props, 'Run autotags operator or fill in dimensions manually')
-def get_missing_data_scene(props):
+def check_missing_data_scene(props):
props.report = ''
autothumb.update_upload_model_preview(None, None)
@@ -122,7 +124,7 @@ def get_missing_data_scene(props):
write_to_report(props, 'Set at least one rendering/output engine')
-def get_missing_data_material(props):
+def check_missing_data_material(props):
props.report = ''
autothumb.update_upload_material_preview(None, None)
if props.name == '':
@@ -136,7 +138,7 @@ def get_missing_data_material(props):
write_to_report(props, 'Set rendering/output engine')
-def get_missing_data_brush(props):
+def check_missing_data_brush(props):
autothumb.update_upload_brush_preview(None, None)
props.report = ''
if props.name == '':
@@ -160,11 +162,31 @@ def camel_to_sub(content):
def get_upload_data(self, context, asset_type):
+ '''
+ works though metadata from addom props and prepares it for upload to dicts.
+ Parameters
+ ----------
+ self
+ context
+ asset_type
+
+ Returns
+ -------
+ export_ddta- all extra data that the process needs to upload and communicate with UI from a thread.
+ - eval_path_computing - string path to UI prop that denots if upload is still running
+ - eval_path_state - string path to UI prop that delivers messages about upload to ui
+ - eval_path - path to object holding upload data to be able to access it with various further commands
+ - models - in case of model upload, list of objects
+ - thumbnail_path - path to thumbnail file
+
+ upload_data - asset_data generated from the ui properties
+
+ '''
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
api_key = user_preferences.api_key
export_data = {
- "type": asset_type,
+ # "type": asset_type,
}
upload_params = {}
if asset_type == 'MODEL':
@@ -236,7 +258,7 @@ def get_upload_data(self, context, asset_type):
"procedural": props.is_procedural,
"nodeCount": props.node_count,
"textureCount": props.texture_count,
- "megapixels": round(props.total_megapixels/ 1000000),
+ "megapixels": round(props.total_megapixels / 1000000),
# "scene": props.is_scene,
}
if props.use_design_year:
@@ -364,7 +386,7 @@ def get_upload_data(self, context, asset_type):
"procedural": props.is_procedural,
"nodeCount": props.node_count,
"textureCount": props.texture_count,
- "megapixels": round(props.total_megapixels/ 1000000),
+ "megapixels": round(props.total_megapixels / 1000000),
}
@@ -405,6 +427,38 @@ def get_upload_data(self, context, asset_type):
"assetType": 'brush',
}
+ elif asset_type == 'HDR':
+ ui_props = bpy.context.scene.blenderkitUI
+
+ # imagename = ui_props.hdr_upload_image
+ image = ui_props.hdr_upload_image#bpy.data.images.get(imagename)
+ if not image:
+ return None, None
+
+ props = image.blenderkit
+ # props.name = brush.name
+ base, ext = os.path.splitext(image.filepath)
+ thumb_path = base + '.jpg'
+ export_data["thumbnail_path"] = bpy.path.abspath(thumb_path)
+
+ export_data["hdr"] = str(image.name)
+ export_data["hdr_filepath"] = str(bpy.path.abspath(image.filepath))
+ # export_data["thumbnail_path"] = bpy.path.abspath(brush.icon_filepath)
+
+ eval_path_computing = "bpy.data.images['%s'].blenderkit.uploading" % image.name
+ eval_path_state = "bpy.data.images['%s'].blenderkit.upload_state" % image.name
+ eval_path = "bpy.data.images['%s']" % image.name
+
+ # mat analytics happen here, since they don't take up any time...
+
+ upload_params = {
+
+ }
+
+ upload_data = {
+ "assetType": 'hdr',
+ }
+
elif asset_type == 'TEXTURE':
style = props.style
# if style == 'OTHER':
@@ -441,13 +495,16 @@ def get_upload_data(self, context, asset_type):
upload_data["isPrivate"] = props.is_private == 'PRIVATE'
upload_data["token"] = user_preferences.api_key
- if props.asset_base_id != '':
- upload_data['assetBaseId'] = props.asset_base_id
- upload_data['id'] = props.id
-
upload_data['parameters'] = upload_params
- return export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props
+ # if props.asset_base_id != '':
+ export_data['assetBaseId'] = props.asset_base_id
+ export_data['id'] = props.id
+ export_data['eval_path_computing'] = eval_path_computing
+ export_data['eval_path_state'] = eval_path_state
+ export_data['eval_path'] = eval_path
+
+ return export_data, upload_data
def category_change_thread(asset_id, category, api_key):
@@ -464,7 +521,6 @@ def category_change_thread(asset_id, category, api_key):
return {'FINISHED'}
-
# class OBJECT_MT_blenderkit_fast_category_menu(bpy.types.Menu):
# bl_label = "Fast category change"
# bl_idname = "OBJECT_MT_blenderkit_fast_category_menu"
@@ -527,7 +583,7 @@ class FastCategory(bpy.types.Operator):
# layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
# col.prop(self, 'category')
- layout.prop(self, 'category')#, expand = True)
+ layout.prop(self, 'category') # , expand = True)
props = bpy.context.scene.blenderkitUI
if props.asset_type == 'MODEL': # by now block this for other asset types.
# col = row.column()
@@ -568,6 +624,7 @@ class FastCategory(bpy.types.Operator):
wm = context.window_manager
return wm.invoke_props_dialog(self)
+
def verification_status_change_thread(asset_id, state, api_key):
upload_data = {
"verificationStatus": state
@@ -583,6 +640,16 @@ def verification_status_change_thread(asset_id, state, api_key):
def get_upload_location(props):
+ '''
+ not used by now, gets location of uploaded asset - potentially usefull if we draw a nice upload gizmo in viewport.
+ Parameters
+ ----------
+ props
+
+ Returns
+ -------
+
+ '''
scene = bpy.context.scene
ui_props = scene.blenderkitUI
if ui_props.asset_type == 'MODEL':
@@ -630,8 +697,244 @@ def auto_fix(asset_type=''):
asset.name = props.name
+upload_threads = []
+
+
+class Uploader(threading.Thread):
+ '''
+ Upload thread -
+ - first uploads metadata
+ - blender gets started to process the file if .blend is uploaded
+ - if files need to be uploaded, uploads them
+ - thumbnail goes first
+ - files get uploaded
+
+ Returns
+ -------
+
+ '''
+
+ def __init__(self, upload_data=None, export_data=None, upload_set=None):
+ super(Uploader, self).__init__()
+ self.upload_data = upload_data
+ self.export_data = export_data
+ self.upload_set = upload_set
+ self._stop_event = threading.Event()
+
+ def stop(self):
+ self._stop_event.set()
+
+ def stopped(self):
+ return self._stop_event.is_set()
+
+ def send_message(self, message):
+ message = str(message)
+ # this adds a UI report but also writes above the upload panel fields.
+ tasks_queue.add_task((ui.add_report, (message,)))
+ estring = f"{self.export_data['eval_path_state']} = '{message}'"
+ tasks_queue.add_task((exec, (estring,)))
+
+ def end_upload(self, message):
+ estring = self.export_data['eval_path_computing'] + ' = False'
+ tasks_queue.add_task((exec, (estring,)))
+ self.send_message(message)
+
+ def run(self):
+ # utils.pprint(upload_data)
+ self.upload_data['parameters'] = utils.dict_to_params(
+ self.upload_data['parameters']) # weird array conversion only for upload, not for tooltips.
+
+ script_path = os.path.dirname(os.path.realpath(__file__))
+
+
+ # first upload metadata to server, so it can be saved inside the current file
+ url = paths.get_api_url() + 'assets/'
+
+ headers = utils.get_headers(self.upload_data['token'])
+
+ # self.upload_data['license'] = 'ovejajojo'
+ json_metadata = self.upload_data # json.dumps(self.upload_data, ensure_ascii=False).encode('utf8')
+
+ # tasks_queue.add_task((ui.add_report, ('Posting metadata',)))
+ self.send_message('Posting metadata')
+ if self.export_data['assetBaseId'] == '':
+ try:
+ r = rerequests.post(url, json=json_metadata, headers=headers, verify=True,
+ immediate=True) # files = files,
+
+ # tasks_queue.add_task((ui.add_report, ('uploaded metadata',)))
+ utils.p(r.text)
+ self.send_message('uploaded metadata')
+
+ except requests.exceptions.RequestException as e:
+ print(e)
+ self.end_upload(e)
+ return {'CANCELLED'}
+
+ else:
+ url += self.export_data['id'] + '/'
+ try:
+ if 'MAINFILE' in self.upload_set:
+ json_metadata["verificationStatus"] = "uploading"
+ r = rerequests.patch(url, json=json_metadata, headers=headers, verify=True,
+ immediate=True) # files = files,
+ self.send_message('uploaded metadata')
+
+ # tasks_queue.add_task((ui.add_report, ('uploaded metadata',)))
+ # parse the request
+ # print('uploaded metadata')
+ print(r.text)
+ except requests.exceptions.RequestException as e:
+ print(e)
+ self.end_upload(e)
+ return {'CANCELLED'}
+
+ if self.stopped():
+ self.end_upload('Upload cancelled by user')
+ return
+ # props.upload_state = 'step 1'
+ if self.upload_set == ['METADATA']:
+ self.end_upload('Metadata posted successfully')
+ return {'FINISHED'}
+ try:
+ rj = r.json()
+ utils.pprint(rj)
+ # if r.status_code not in (200, 201):
+ # if r.status_code == 401:
+ # ###ui.add_report(r.detail, 5, colors.RED)
+ # return {'CANCELLED'}
+ # if props.asset_base_id == '':
+ # props.asset_base_id = rj['assetBaseId']
+ # props.id = rj['id']
+ if self.export_data['assetBaseId'] == '':
+ self.export_data['assetBaseId'] = rj['assetBaseId']
+ self.export_data['id'] = rj['id']
+ #here we need to send asset ID's back into UI to be written in asset data.
+ estring = f"{self.export_data['eval_path']}.blenderkit.asset_base_id = '{rj['assetBaseId']}'"
+ tasks_queue.add_task((exec, (estring,)))
+ estring = f"{self.export_data['eval_path']}.blenderkit.id = '{rj['id']}'"
+ tasks_queue.add_task((exec, (estring,)))
+ #after that, the user's file needs to be saved to save the
+
+ self.upload_data['assetBaseId'] = self.export_data['assetBaseId']
+ self.upload_data['id'] = self.export_data['id']
+
+
+ # props.uploading = True
+
+ if 'MAINFILE' in self.upload_set:
+ if self.upload_data['assetType'] == 'hdr':
+ fpath = self.export_data['hdr_filepath']
+ else:
+ fpath = os.path.join(self.export_data['temp_dir'], self.upload_data['assetBaseId'] + '.blend')
+
+ clean_file_path = paths.get_clean_filepath()
+
+ data = {
+ 'export_data': self.export_data,
+ 'upload_data': self.upload_data,
+ 'debug_value': self.export_data['debug_value'],
+ 'upload_set': self.upload_set,
+ }
+ datafile = os.path.join(self.export_data['temp_dir'], BLENDERKIT_EXPORT_DATA_FILE)
+
+ with open(datafile, 'w') as s:
+ json.dump(data, s)
+
+ #non waiting method - not useful here..
+ # proc = subprocess.Popen([
+ # binary_path,
+ # "--background",
+ # "-noaudio",
+ # clean_file_path,
+ # "--python", os.path.join(script_path, "upload_bg.py"),
+ # "--", datafile # ,filepath, tempdir
+ # ], bufsize=5000, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+ # tasks_queue.add_task((ui.add_report, ('preparing scene - running blender instance',)))
+ self.send_message('preparing scene - running blender instance')
+
+ proc = subprocess.run([
+ self.export_data['binary_path'],
+ "--background",
+ "-noaudio",
+ clean_file_path,
+ "--python", os.path.join(script_path, "upload_bg.py"),
+ "--", datafile
+ ], bufsize=1, stdout=sys.stdout, stdin=subprocess.PIPE, creationflags=utils.get_process_flags())
+
+ if self.stopped():
+ self.end_upload('Upload stopped by user')
+ return
+
+
+ files = []
+ if 'THUMBNAIL' in self.upload_set:
+ files.append({
+ "type": "thumbnail",
+ "index": 0,
+ "file_path": self.export_data["thumbnail_path"]
+ })
+ if 'MAINFILE' in self.upload_set:
+
+ files.append({
+ "type": "blend",
+ "index": 0,
+ "file_path": fpath
+ })
+
+ self.send_message('Uploading files')
+
+ uploaded = upload_bg.upload_files(self.upload_data, files)
+
+ if uploaded:
+ # mark on server as uploaded
+ if 'MAINFILE' in self.upload_set:
+ confirm_data = {
+ "verificationStatus": "uploaded"
+ }
+
+ url = paths.get_api_url() + 'assets/'
+
+ headers = utils.get_headers(self.upload_data['token'])
+
+ url += self.upload_data["id"] + '/'
+
+ r = rerequests.patch(url, json=confirm_data, headers=headers, verify=True) # files = files,
+
+ self.end_upload('Upload finished successfully')
+ else:
+ self.end_upload('Upload failed')
+ except Exception as e:
+ self.end_upload(e)
+ print(e)
+ return {'CANCELLED'}
+
+
+def check_missing_data(asset_type, props):
+ '''
+ checks if user did everything allright for particular assets and notifies him back if not.
+ Parameters
+ ----------
+ asset_type
+ props
+
+ Returns
+ -------
+
+ '''
+ if asset_type == 'MODEL':
+ check_missing_data_model(props)
+ if asset_type == 'SCENE':
+ check_missing_data_scene(props)
+ elif asset_type == 'MATERIAL':
+ check_missing_data_material(props)
+ elif asset_type == 'BRUSH':
+ check_missing_data_brush(props)
+
+
+
def start_upload(self, context, asset_type, reupload, upload_set):
- '''start upload process, by processing data'''
+ '''start upload process, by processing data, then start a thread that cares about the rest of the upload.'''
# fix the name first
utils.name_update()
@@ -651,17 +954,10 @@ def start_upload(self, context, asset_type, reupload, upload_set):
props.tags = props.tags[:]
props.name = props.name.strip()
- # TODO move this to separate function
- # check for missing metadata
- if asset_type == 'MODEL':
- get_missing_data_model(props)
- if asset_type == 'SCENE':
- get_missing_data_scene(props)
- elif asset_type == 'MATERIAL':
- get_missing_data_material(props)
- elif asset_type == 'BRUSH':
- get_missing_data_brush(props)
+ # check for missing metadata
+ check_missing_data(asset_type, props)
+ # if previous check did find any problems then
if props.report != '':
self.report({'ERROR_INVALID_INPUT'}, props.report)
return {'CANCELLED'}
@@ -669,131 +965,50 @@ def start_upload(self, context, asset_type, reupload, upload_set):
if not reupload:
props.asset_base_id = ''
props.id = ''
- export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props = get_upload_data(self, context,
- asset_type)
- # utils.pprint(upload_data)
- upload_data['parameters'] = utils.dict_to_params(
- upload_data['parameters']) # weird array conversion only for upload, not for tooltips.
-
- binary_path = bpy.app.binary_path
- script_path = os.path.dirname(os.path.realpath(__file__))
- basename, ext = os.path.splitext(bpy.data.filepath)
- # if not basename:
- # basename = os.path.join(basename, "temp")
- if not ext:
- ext = ".blend"
- tempdir = tempfile.mkdtemp()
- source_filepath = os.path.join(tempdir, "export_blenderkit" + ext)
- clean_file_path = paths.get_clean_filepath()
- data = {
- 'clean_file_path': clean_file_path,
- 'source_filepath': source_filepath,
- 'temp_dir': tempdir,
- 'export_data': export_data,
- 'upload_data': upload_data,
- 'debug_value': bpy.app.debug_value,
- 'upload_set': upload_set,
- }
- datafile = os.path.join(tempdir, BLENDERKIT_EXPORT_DATA_FILE)
- # check if thumbnail exists:
+ export_data, upload_data = get_upload_data(self, context, asset_type)
+
+ # check if thumbnail exists, generate for HDR:
if 'THUMBNAIL' in upload_set:
- if not os.path.exists(export_data["thumbnail_path"]):
+ if asset_type == 'HDR':
+ image_utils.generate_hdr_thumbnail()
+ elif not os.path.exists(export_data["thumbnail_path"]):
props.upload_state = 'Thumbnail not found'
props.uploading = False
return {'CANCELLED'}
- # first upload metadata to server, so it can be saved inside the current file
- url = paths.get_api_url() + 'assets/'
+ props.upload_state = "Starting upload. Please don't close Blender until upload finishes"
+ props.uploading = True
- headers = utils.get_headers(upload_data['token'])
- # upload_data['license'] = 'ovejajojo'
- json_metadata = upload_data # json.dumps(upload_data, ensure_ascii=False).encode('utf8')
- global reports
- if props.asset_base_id == '':
- try:
- r = rerequests.post(url, json=json_metadata, headers=headers, verify=True, immediate=True) # files = files,
- ui.add_report('uploaded metadata')
- utils.p(r.text)
- except requests.exceptions.RequestException as e:
- print(e)
- props.upload_state = str(e)
- props.uploading = False
- return {'CANCELLED'}
+ # save a copy of the file for processing. Only for blend files
+ basename, ext = os.path.splitext(bpy.data.filepath)
+ if not ext:
+ ext = ".blend"
+ export_data['temp_dir'] = tempfile.mkdtemp()
+ export_data['source_filepath'] = os.path.join(export_data['temp_dir'], "export_blenderkit" + ext)
+ if asset_type != 'HDR':
+ bpy.ops.wm.save_as_mainfile(filepath=export_data['source_filepath'], compress=False, copy=True)
- else:
- url += props.id + '/'
- try:
- if 'MAINFILE' in upload_set:
- json_metadata["verificationStatus"] = "uploading"
- r = rerequests.patch(url, json=json_metadata, headers=headers, verify=True, immediate=True) # files = files,
- ui.add_report('uploaded metadata')
- # parse the request
- # print('uploaded metadata')
- print(r.text)
- except requests.exceptions.RequestException as e:
- print(e)
- props.upload_state = str(e)
- props.uploading = False
- return {'CANCELLED'}
+ export_data['binary_path'] = bpy.app.binary_path
+ export_data['debug_value'] = bpy.app.debug_value
- # props.upload_state = 'step 1'
- if upload_set == ['METADATA']:
- props.uploading = False
- props.upload_state = 'upload finished successfully'
- return {'FINISHED'}
- try:
- rj = r.json()
- utils.pprint(rj)
- # if r.status_code not in (200, 201):
- # if r.status_code == 401:
- # ui.add_report(r.detail, 5, colors.RED)
- # return {'CANCELLED'}
- if props.asset_base_id == '':
- props.asset_base_id = rj['assetBaseId']
- props.id = rj['id']
- upload_data['assetBaseId'] = props.asset_base_id
- upload_data['id'] = props.id
-
- # bpy.ops.wm.save_mainfile()
- # bpy.ops.wm.save_as_mainfile(filepath=filepath, compress=False, copy=True)
-
- props.uploading = True
- # save a copy of actual scene but don't interfere with the users models
- bpy.ops.wm.save_as_mainfile(filepath=source_filepath, compress=False, copy=True)
-
- with open(datafile, 'w') as s:
- json.dump(data, s)
-
- proc = subprocess.Popen([
- binary_path,
- "--background",
- "-noaudio",
- clean_file_path,
- "--python", os.path.join(script_path, "upload_bg.py"),
- "--", datafile # ,filepath, tempdir
- ], bufsize=5000, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
-
- bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state,
- eval_path=eval_path, process_type='UPLOAD', process=proc, location=location)
-
- except Exception as e:
- props.upload_state = str(e)
- props.uploading = False
- print(e)
- return {'CANCELLED'}
+ upload_thread = Uploader(upload_data=upload_data, export_data=export_data, upload_set=upload_set)
+
+ upload_thread.start()
+ upload_threads.append(upload_thread)
return {'FINISHED'}
asset_types = (
- ('MODEL', 'Model', 'set of objects'),
- ('SCENE', 'Scene', 'scene'),
- ('MATERIAL', 'Material', 'any .blend Material'),
- ('TEXTURE', 'Texture', 'a texture, or texture set'),
- ('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
- ('ADDON', 'Addon', 'addnon'),
+ ('MODEL', 'Model', 'Set of objects'),
+ ('SCENE', 'Scene', 'Scene'),
+ ('HDR', 'HDR', 'HDR image'),
+ ('MATERIAL', 'Material', 'Any .blend Material'),
+ ('TEXTURE', 'Texture', 'A texture, or texture set'),
+ ('BRUSH', 'Brush', 'Brush, can be any type of blender brush'),
+ ('ADDON', 'Addon', 'Addnon'),
)
@@ -865,11 +1080,9 @@ class UploadOperator(Operator):
if self.main_file:
upload_set.append('MAINFILE')
- result = start_upload(self, context, self.asset_type, self.reupload, upload_set)
-
- return result
-
+ result = start_upload(self, context, self.asset_type, self.reupload, upload_set=upload_set, )
+ return {'FINISHED'}
def draw(self, context):
props = utils.get_upload_props()
@@ -887,16 +1100,16 @@ class UploadOperator(Operator):
if props.is_private == 'PUBLIC':
utils.label_multiline(layout, text='public assets are validated several hours'
- ' or days after upload. Remember always to '
- 'test download your asset to a clean file'
- ' to see if it uploaded correctly.'
- , width=300)
+ ' or days after upload. Remember always to '
+ 'test download your asset to a clean file'
+ ' to see if it uploaded correctly.'
+ , width=300)
def invoke(self, context, event):
props = utils.get_upload_props()
if not utils.user_logged_in():
- ui_panels.draw_not_logged_in(self, message = 'To upload assets you need to login/signup.')
+ ui_panels.draw_not_logged_in(self, message='To upload assets you need to login/signup.')
return {'CANCELLED'}
if props.is_private == 'PUBLIC':
@@ -905,7 +1118,6 @@ class UploadOperator(Operator):
return self.execute(context)
-
class AssetDebugPrint(Operator):
"""Change verification status"""
bl_idname = "object.blenderkit_print_asset_debug"
@@ -922,7 +1134,6 @@ class AssetDebugPrint(Operator):
def poll(cls, context):
return True
-
def execute(self, context):
preferences = bpy.context.preferences.addons['blenderkit'].preferences
@@ -993,7 +1204,6 @@ class AssetVerificationStatusChange(Operator):
if r['id'] == self.asset_id:
r['verificationStatus'] = self.state
-
thread = threading.Thread(target=verification_status_change_thread,
args=(self.asset_id, self.state, preferences.api_key))
thread.start()
diff --git a/blenderkit/upload_bg.py b/blenderkit/upload_bg.py
index a0e95535..74ae8906 100644
--- a/blenderkit/upload_bg.py
+++ b/blenderkit/upload_bg.py
@@ -25,8 +25,10 @@ if "bpy" in locals():
bg_blender = reload(bg_blender)
utils = reload(utils)
rerequests = reload(rerequests)
+ tasks_queue = reload(tasks_queue)
+ ui = reload(ui)
else:
- from blenderkit import paths, append_link, bg_blender, utils, rerequests
+ from blenderkit import paths, append_link, bg_blender, utils, rerequests, tasks_queue, ui
import sys, json, os, time
import requests
@@ -66,7 +68,9 @@ class upload_in_chunks(object):
break
self.readsofar += len(data)
percent = self.readsofar * 1e2 / self.totalsize
- bg_blender.progress('uploading %s' % self.report_name, percent)
+ tasks_queue.add_task((ui.add_report, (f"Uploading {self.report_name} {percent}%",)))
+
+ # bg_blender.progress('uploading %s' % self.report_name, percent)
# sys.stderr.write("\r{percent:3.0f}%".format(percent=percent))
yield data
@@ -77,7 +81,10 @@ class upload_in_chunks(object):
def upload_file(upload_data, f):
headers = utils.get_headers(upload_data['token'])
version_id = upload_data['id']
- bg_blender.progress(f"uploading {f['type']} {os.path.basename(f['file_path'])}")
+
+ message = f"uploading {f['type']} {os.path.basename(f['file_path'])}"
+ tasks_queue.add_task((ui.add_report, (message,)))
+
upload_info = {
'assetId': version_id,
'fileType': f['type'],
@@ -100,14 +107,17 @@ def upload_file(upload_data, f):
data=upload_in_chunks(f['file_path'], chunk_size, f['type']),
stream=True, verify=True)
- if 250>upload_response.status_code >199:
+ if 250 > upload_response.status_code > 199:
uploaded = True
else:
print(upload_response.text)
- bg_blender.progress(f'Upload failed, retry. {a}')
+ message = f"Upload failed, retry. File : {f['type']} {os.path.basename(f['file_path'])}"
+ tasks_queue.add_task((ui.add_report, (message,)))
+
except Exception as e:
print(e)
- bg_blender.progress('Upload %s failed, retrying' % f['type'])
+ message = f"Upload failed, retry. File : {f['type']} {os.path.basename(f['file_path'])}"
+ tasks_queue.add_task((ui.add_report, (message,)))
time.sleep(1)
# confirm single file upload to bkit server
@@ -115,7 +125,7 @@ def upload_file(upload_data, f):
upload_done_url = paths.get_api_url() + 'uploads_s3/' + upload['id'] + '/upload-file/'
upload_response = rerequests.post(upload_done_url, headers=headers, verify=True)
- bg_blender.progress('finished uploading')
+ tasks_queue.add_task((ui.add_report, (f"Finished file upload{os.path.basename(f['file_path'])}",)))
return uploaded
@@ -127,13 +137,13 @@ def upload_files(upload_data, files):
uploaded = upload_file(upload_data, f)
if not uploaded:
uploaded_all = False
- bg_blender.progress('finished uploading')
+ tasks_queue.add_task((ui.add_report, (f"Uploaded all files for asset {upload_data['name']}",)))
return uploaded_all
if __name__ == "__main__":
try:
- bg_blender.progress('preparing scene - append data')
+ # bg_blender.progress('preparing scene - append data')
with open(BLENDERKIT_EXPORT_DATA, 'r') as s:
data = json.load(s)
@@ -141,84 +151,44 @@ if __name__ == "__main__":
export_data = data['export_data']
upload_data = data['upload_data']
- upload_set = data['upload_set']
- if 'MAINFILE' in upload_set:
- bpy.data.scenes.new('upload')
- for s in bpy.data.scenes:
- if s.name != 'upload':
- bpy.data.scenes.remove(s)
-
- if export_data['type'] == 'MODEL':
- obnames = export_data['models']
- main_source, allobs = append_link.append_objects(file_name=data['source_filepath'],
- obnames=obnames,
- rotation=(0, 0, 0))
- g = bpy.data.collections.new(upload_data['name'])
- for o in allobs:
- g.objects.link(o)
- bpy.context.scene.collection.children.link(g)
- if export_data['type'] == 'SCENE':
- sname = export_data['scene']
- main_source = append_link.append_scene(file_name=data['source_filepath'],
- scenename=sname)
- bpy.data.scenes.remove(bpy.data.scenes['upload'])
- main_source.name = sname
- elif export_data['type'] == 'MATERIAL':
- matname = export_data['material']
- main_source = append_link.append_material(file_name=data['source_filepath'], matname=matname)
-
- elif export_data['type'] == 'BRUSH':
- brushname = export_data['brush']
- main_source = append_link.append_brush(file_name=data['source_filepath'], brushname=brushname)
-
- bpy.ops.file.pack_all()
-
- main_source.blenderkit.uploading = False
- fpath = os.path.join(data['temp_dir'], upload_data['assetBaseId'] + '.blend')
-
- bpy.ops.wm.save_as_mainfile(filepath=fpath, compress=True, copy=False)
- os.remove(data['source_filepath'])
-
- bg_blender.progress('preparing scene - open files')
-
- files = []
- if 'THUMBNAIL' in upload_set:
- files.append({
- "type": "thumbnail",
- "index": 0,
- "file_path": export_data["thumbnail_path"]
- })
- if 'MAINFILE' in upload_set:
- files.append({
- "type": "blend",
- "index": 0,
- "file_path": fpath
- })
-
- bg_blender.progress('uploading')
-
- uploaded = upload_files(upload_data, files)
-
- if uploaded:
- # mark on server as uploaded
- if 'MAINFILE' in upload_set:
- confirm_data = {
- "verificationStatus": "uploaded"
- }
-
- url = paths.get_api_url() + 'assets/'
-
- headers = utils.get_headers(upload_data['token'])
-
- url += upload_data["id"] + '/'
-
- r = rerequests.patch(url, json=confirm_data, headers=headers, verify=True) # files = files,
-
- bg_blender.progress('upload finished successfully')
- else:
- bg_blender.progress('upload failed.')
+ bpy.data.scenes.new('upload')
+ for s in bpy.data.scenes:
+ if s.name != 'upload':
+ bpy.data.scenes.remove(s)
+
+ if upload_data['assetType'] == 'model':
+ obnames = export_data['models']
+ main_source, allobs = append_link.append_objects(file_name=export_data['source_filepath'],
+ obnames=obnames,
+ rotation=(0, 0, 0))
+ g = bpy.data.collections.new(upload_data['name'])
+ for o in allobs:
+ g.objects.link(o)
+ bpy.context.scene.collection.children.link(g)
+ elif upload_data['assetType'] == 'scene':
+ sname = export_data['scene']
+ main_source = append_link.append_scene(file_name=export_data['source_filepath'],
+ scenename=sname)
+ bpy.data.scenes.remove(bpy.data.scenes['upload'])
+ main_source.name = sname
+ elif upload_data['assetType'] == 'material':
+ matname = export_data['material']
+ main_source = append_link.append_material(file_name=export_data['source_filepath'], matname=matname)
+
+ elif upload_data['assetType'] == 'brush':
+ brushname = export_data['brush']
+ main_source = append_link.append_brush(file_name=export_data['source_filepath'], brushname=brushname)
+
+ bpy.ops.file.pack_all()
+
+ main_source.blenderkit.uploading = False
+ fpath = os.path.join(export_data['temp_dir'], upload_data['assetBaseId'] + '.blend')
+
+ bpy.ops.wm.save_as_mainfile(filepath=fpath, compress=True, copy=False)
+ os.remove(export_data['source_filepath'])
+
except Exception as e:
print(e)
- bg_blender.progress(e)
+ # bg_blender.progress(e)
sys.exit(1)
diff --git a/blenderkit/utils.py b/blenderkit/utils.py
index 36bf1fc9..b3f65042 100644
--- a/blenderkit/utils.py
+++ b/blenderkit/utils.py
@@ -76,6 +76,13 @@ def get_active_model():
return None
+def get_active_HDR():
+ scene = bpy.context.scene
+ ui_props = scene.blenderkitUI
+ image = ui_props.hdr_upload_image
+ return image
+
+
def get_selected_models():
'''
Detect all hierarchies that contain asset data from selection. Only parents that have actual ['asset data'] get returned
@@ -150,6 +157,10 @@ def get_search_props():
if not hasattr(scene, 'blenderkit_scene'):
return;
props = scene.blenderkit_scene
+ if uiprops.asset_type == 'HDR':
+ if not hasattr(scene, 'blenderkit_HDR'):
+ return;
+ props = scene.blenderkit_HDR
if uiprops.asset_type == 'MATERIAL':
if not hasattr(scene, 'blenderkit_mat'):
return;
@@ -176,7 +187,8 @@ def get_active_asset():
return ob
if ui_props.asset_type == 'SCENE':
return bpy.context.scene
-
+ if ui_props.asset_type == 'HDR':
+ return get_active_HDR()
elif ui_props.asset_type == 'MATERIAL':
if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
return bpy.context.active_object.active_material
@@ -199,6 +211,12 @@ def get_upload_props():
if ui_props.asset_type == 'SCENE':
s = bpy.context.scene
return s.blenderkit
+ if ui_props.asset_type == 'HDR':
+
+ hdr = ui_props.hdr_upload_image#bpy.data.images.get(ui_props.hdr_upload_image)
+ if not hdr:
+ return None
+ return hdr.blenderkit
elif ui_props.asset_type == 'MATERIAL':
if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
return bpy.context.active_object.active_material.blenderkit
@@ -412,6 +430,7 @@ def delete_hierarchy(ob):
obs = get_hierarchy(ob)
bpy.ops.object.delete({"selected_objects": obs})
+
def get_bounds_snappable(obs, use_modifiers=False):
# progress('getting bounds of object(s)')
parent = obs[0]
@@ -601,6 +620,9 @@ def automap(target_object=None, target_slot=None, tex_size=1, bg_exception=False
def name_update():
+ scene = bpy.context.scene
+ ui_props = scene.blenderkitUI
+
props = get_upload_props()
if props.name_old != props.name:
props.name_changed = True
@@ -617,7 +639,9 @@ def name_update():
fname = fname.replace('\'', '')
fname = fname.replace('\"', '')
asset = get_active_asset()
- asset.name = fname
+ if ui_props.asset_type != 'HDR':
+ # Here we actually rename assets datablocks, but don't do that with HDR's and possibly with others
+ asset.name = fname
def get_param(asset_data, parameter_name):