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:
authorNathan Letwory <nathan@blender.org>2020-02-06 19:56:34 +0300
committerNathan Letwory <nathan@blender.org>2020-02-06 19:56:34 +0300
commitcbcf507f3045e867c4b2d8e0976b76e1753dc49b (patch)
treefcdea8a2e60aeb02a7290061f156afa2b482c6d6
parent8e67bd2651cf04eee0aabdcac1836df0678a49e8 (diff)
parent8b659917baecbea5471a05369aa754ae544a814b (diff)
Merge branch 'blender-v2.82-release'
-rw-r--r--blenderkit/__init__.py19
-rw-r--r--blenderkit/asset_inspector.py9
-rw-r--r--blenderkit/bkit_oauth.py2
-rw-r--r--blenderkit/download.py2
-rw-r--r--blenderkit/oauth.py2
-rw-r--r--blenderkit/ratings.py74
-rw-r--r--blenderkit/rerequests.py2
-rw-r--r--blenderkit/search.py92
-rw-r--r--blenderkit/tasks_queue.py25
-rw-r--r--blenderkit/ui.py7
-rw-r--r--blenderkit/ui_panels.py31
11 files changed, 171 insertions, 94 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py
index bd9f7e3f..bf0ec328 100644
--- a/blenderkit/__init__.py
+++ b/blenderkit/__init__.py
@@ -385,11 +385,13 @@ 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)
-def search_procedural_update(self,context):
+
+def search_procedural_update(self, context):
if self.search_procedural in ('PROCEDURAL', 'BOTH'):
self.search_texture_resolution = False
search.search_update(self, context)
+
class BlenderKitCommonSearchProps(object):
# STATES
is_searching: BoolProperty(name="Searching", description="search is currently running (internal)", default=False)
@@ -472,13 +474,13 @@ class BlenderKitCommonSearchProps(object):
('ALL', 'All', 'All'),
('UPLOADING', 'Uploading', 'Uploading'),
('UPLOADED', 'Uploaded', 'Uploaded'),
+ ('READY', 'Ready for V.', 'Ready for validation (deprecated since 2.8)'),
('VALIDATED', 'Validated', 'Calidated'),
('ON_HOLD', 'On Hold', 'On Hold'),
('REJECTED', 'Rejected', 'Rejected'),
('DELETED', 'Deleted', 'Deleted'),
),
default='ALL',
- update=search.search_update,
)
@@ -645,9 +647,16 @@ class BlenderKitCommonUploadProps(object):
class BlenderKitRatingProps(PropertyGroup):
- rating_quality: IntProperty(name="Quality", description="quality of the material", default=0, min=-1, max=10)
- rating_work_hours: FloatProperty(name="Work Hours", description="How many hours did this work take?", default=0.01,
- min=0.0, max=1000
+ rating_quality: IntProperty(name="Quality",
+ description="quality of the material",
+ default=0,
+ min=-1, max=10,
+ update=ratings.update_ratings_quality)
+
+ rating_work_hours: FloatProperty(name="Work Hours",
+ description="How many hours did this work take?",
+ default=0.01,
+ min=0.0, max=1000, update=ratings.update_ratings_work_hours
)
rating_complexity: IntProperty(name="Complexity",
description="Complexity is a number estimating how much work was spent on the asset.aaa",
diff --git a/blenderkit/asset_inspector.py b/blenderkit/asset_inspector.py
index e6fdc659..0e26479f 100644
--- a/blenderkit/asset_inspector.py
+++ b/blenderkit/asset_inspector.py
@@ -118,9 +118,12 @@ def check_render_engine(props, obs):
if n.type not in shaders:
shaders.append(n.type)
if n.type == 'TEX_IMAGE':
- mattype = 'image based'
- props.is_procedural = False
- if n.image not in textures:
+
+
+ if n.image is not None and n.image not in textures:
+ props.is_procedural = False
+ mattype = 'image based'
+
textures.append(n.image)
props.texture_count += 1
props.total_megapixels += (n.image.size[0] * n.image.size[1])
diff --git a/blenderkit/bkit_oauth.py b/blenderkit/bkit_oauth.py
index dc272104..d56629e5 100644
--- a/blenderkit/bkit_oauth.py
+++ b/blenderkit/bkit_oauth.py
@@ -78,7 +78,7 @@ def refresh_token(api_key_refresh, url):
auth_token, refresh_token, oauth_response = authenticator.get_refreshed_token(api_key_refresh)
if auth_token is not None and refresh_token is not None:
tasks_queue.add_task((write_tokens, (auth_token, refresh_token, oauth_response)))
- return auth_token, refresh_token
+ return auth_token, refresh_token, oauth_response
def write_tokens(auth_token, refresh_token, oauth_response):
diff --git a/blenderkit/download.py b/blenderkit/download.py
index 3a99f66f..82c57235 100644
--- a/blenderkit/download.py
+++ b/blenderkit/download.py
@@ -441,7 +441,7 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
scene['assets rated'][id] = scene['assets rated'].get(id, False)
parent['asset_data'] = asset_data # TODO remove this??? should write to blenderkit Props?
- bpy.ops.wm.undo_push_context()
+ bpy.ops.wm.undo_push_context(message = 'add %s to scene'% asset_data['name'])
# moving reporting to on save.
# report_use_success(asset_data['id'])
diff --git a/blenderkit/oauth.py b/blenderkit/oauth.py
index afbd8f65..95c6bae6 100644
--- a/blenderkit/oauth.py
+++ b/blenderkit/oauth.py
@@ -52,7 +52,7 @@ class SimpleOAuthAuthenticator(object):
if response.status_code != 200:
print("error retrieving refresh tokens %s" % response.status_code)
print(response.content)
- return None, None
+ return None, None, None
response_json = json.loads(response.content)
refresh_token = response_json ['refresh_token']
diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py
index 96cbc01f..fdbdea79 100644
--- a/blenderkit/ratings.py
+++ b/blenderkit/ratings.py
@@ -22,8 +22,9 @@ if "bpy" in locals():
paths = reload(paths)
utils = reload(utils)
rerequests = reload(rerequests)
+ tasks_queue = reload(tasks_queue)
else:
- from blenderkit import paths, utils, rerequests
+ from blenderkit import paths, utils, rerequests, tasks_queue
import bpy
import requests, threading
@@ -44,12 +45,7 @@ from bpy.types import (
def pretty_print_POST(req):
"""
- At this point it is completely built and ready
- to be fired; it is "prepared".
-
- However pay attention at the formatting used in
- this function because it is programmed to be pretty
- printed and may differ from the actual request.
+ pretty print a request
"""
print('{}\n{}\n{}\n\n{}'.format(
'-----------START-----------',
@@ -60,6 +56,8 @@ def pretty_print_POST(req):
def uplaod_rating_thread(url, ratings, headers):
+ ''' Upload rating thread function / disconnected from blender data.'''
+ utils.p('upload rating', url, ratings)
for rating_name, score in ratings:
if (score != -1 and score != 0):
rating_url = url + rating_name + '/'
@@ -74,12 +72,26 @@ def uplaod_rating_thread(url, ratings, headers):
print('ratings upload failed: %s' % str(e))
+def send_rating_to_thread_quality(url, ratings, headers):
+ '''Sens rating into thread rating, main purpose is for tasks_queue.
+ One function per property to avoid lost data due to stashing.'''
+ thread = threading.Thread(target=uplaod_rating_thread, args=(url, ratings, headers))
+ thread.start()
+
+def send_rating_to_thread_work_hours(url, ratings, headers):
+ '''Sens rating into thread rating, main purpose is for tasks_queue.
+ One function per property to avoid lost data due to stashing.'''
+ thread = threading.Thread(target=uplaod_rating_thread, args=(url, ratings, headers))
+ thread.start()
+
+
def uplaod_review_thread(url, reviews, headers):
r = rerequests.put(url, data=reviews, verify=True, headers=headers)
# except requests.exceptions.RequestException as e:
# print('reviews upload failed: %s' % str(e))
+
def get_rating(asset_id):
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
api_key = user_preferences.api_key
@@ -88,11 +100,38 @@ def get_rating(asset_id):
rtypes = ['quality', 'working_hours']
for rt in rtypes:
params = {
- 'rating_type' : rt
+ 'rating_type': rt
}
r = rerequests.get(r1, params=data, verify=True, headers=headers)
print(r.text)
+
+def update_ratings_quality(self, context):
+ user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+ api_key = user_preferences.api_key
+ headers = utils.get_headers(api_key)
+ asset = self.id_data
+ bkit_ratings = asset.bkit_ratings
+ url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
+
+ if bkit_ratings.rating_quality > 0.1:
+ ratings = [('quality', bkit_ratings.rating_quality)]
+ tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=1, only_last=True)
+
+
+def update_ratings_work_hours(self, context):
+ user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+ api_key = user_preferences.api_key
+ headers = utils.get_headers(api_key)
+ asset = self.id_data
+ bkit_ratings = asset.bkit_ratings
+ url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
+
+ if bkit_ratings.rating_quality > 0.1:
+ ratings = [('working_hours', round(bkit_ratings.rating_work_hours, 1))]
+ tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=1, only_last=True)
+
+
def upload_rating(asset):
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
api_key = user_preferences.api_key
@@ -134,8 +173,8 @@ def upload_rating(asset):
class StarRatingOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.blenderkit_rating"
- bl_label = "Rate the Asset"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
+ bl_label = "Rate the Asset Quality"
+ bl_options = {'REGISTER', 'INTERNAL'}
property_name: StringProperty(
name="Rating Property",
@@ -148,7 +187,7 @@ class StarRatingOperator(bpy.types.Operator):
def execute(self, context):
asset = utils.get_active_asset()
props = asset.bkit_ratings
- props[self.property_name] = self.rating
+ props.rating_quality = self.rating
return {'FINISHED'}
@@ -162,6 +201,7 @@ asset_types = (
)
+# TODO drop this operator, not needed anymore.
class UploadRatingOperator(bpy.types.Operator):
"""Upload rating to the web db"""
bl_idname = "object.blenderkit_rating_upload"
@@ -169,12 +209,12 @@ class UploadRatingOperator(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
# type of upload - model, material, textures, e.t.c.
- asset_type: EnumProperty(
- name="Type",
- items=asset_types,
- description="Type of asset",
- default="MODEL",
- )
+ # asset_type: EnumProperty(
+ # name="Type",
+ # items=asset_types,
+ # description="Type of asset",
+ # default="MODEL",
+ # )
# @classmethod
# def poll(cls, context):
diff --git a/blenderkit/rerequests.py b/blenderkit/rerequests.py
index eab78fba..28c7e2ca 100644
--- a/blenderkit/rerequests.py
+++ b/blenderkit/rerequests.py
@@ -54,7 +54,7 @@ def rerequest(method, url, **kwargs):
if rdata.get('detail') == 'Invalid token.':
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.api_key != '':
- if user_preferences.enable_oauth:
+ if user_preferences.enable_oauth and user_preferences.api_key_refresh != '':
tasks_queue.add_task((ui.add_report, (
'refreshing token. If this fails, please login in BlenderKit Login panel.', 10)))
refresh_url = paths.get_bkit_url()
diff --git a/blenderkit/search.py b/blenderkit/search.py
index 56c22dbb..6eeafcd1 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -244,10 +244,9 @@ def timer_update(): # TODO might get moved to handle all blenderkit stuff.
'tags': r['tags'],
'can_download': r.get('canDownload', True),
'verification_status': r['verificationStatus'],
- 'author_id': str(r['author']['id'])
+ 'author_id': str(r['author']['id']),
# 'author': r['author']['firstName'] + ' ' + r['author']['lastName']
# 'description': r['description'],
- # 'author': r['description'],
}
asset_data['downloaded'] = 0
@@ -504,7 +503,7 @@ def generate_tooltip(mdata):
# t += 'uv: %s\n' % mdata['uv']
# t += '\n'
- t = writeblockm(t, mdata, key='license', width = col_w)
+ t = writeblockm(t, mdata, key='license', width=col_w)
# generator is for both upload preview and search, this is only after search
# if mdata.get('versionNumber'):
@@ -606,45 +605,45 @@ class ThumbDownloader(threading.Thread):
# f.write(chunk)
-def write_author(a_id, adata):
- # utils.p('writing author back')
+def write_gravatar(a_id, gravatar_path):
+ '''
+ Write down gravatar path, as a result of thread-based gravatar image download.
+ This should happen on timer in queue.
+ '''
+ # print('write author', a_id, type(a_id))
authors = bpy.context.window_manager['bkit authors']
- if authors.get(a_id) in (None, ''):
- adata['tooltip'] = generate_author_textblock(adata)
- authors[a_id] = adata
+ if authors.get(a_id) is not None:
+ adata = authors.get(a_id)
+ adata['gravatarImg'] = gravatar_path
-def fetch_author(a_id, api_key):
- utils.p('fetch author')
- try:
- a_url = paths.get_api_url() + 'accounts/' + a_id + '/'
- headers = utils.get_headers(api_key)
- r = rerequests.get(a_url, headers=headers)
+def fetch_gravatar(adata):
+ utils.p('fetch gravatar')
+ if adata.get('gravatarHash') is not None:
+ gravatar_path = paths.get_temp_dir(subdir='g/') + adata['gravatarHash'] + '.jpg'
+
+ if os.path.exists(gravatar_path):
+ tasks_queue.add_task((write_gravatar, (adata['id'], gravatar_path)))
+ return;
+
+ url = "https://www.gravatar.com/avatar/" + adata['gravatarHash'] + '?d=404'
+ r = rerequests.get(url, stream=False)
if r.status_code == 200:
- adata = r.json()
- if not hasattr(adata, 'id'):
- utils.p(adata)
- # utils.p(adata)
- tasks_queue.add_task((write_author, (a_id, adata)))
- if adata.get('gravatarHash') is not None:
- gravatar_path = paths.get_temp_dir(subdir='g/') + adata['gravatarHash'] + '.jpg'
- url = "https://www.gravatar.com/avatar/" + adata['gravatarHash'] + '?d=404'
- r = rerequests.get(url, stream=False)
- if r.status_code == 200:
- with open(gravatar_path, 'wb') as f:
- f.write(r.content)
- adata['gravatarImg'] = gravatar_path
- elif r.status_code == '404':
- adata['gravatarHash'] = None
- utils.p('gravatar for author not available.')
- except Exception as e:
- utils.p(e)
- utils.p('finish fetch')
+ with open(gravatar_path, 'wb') as f:
+ f.write(r.content)
+ tasks_queue.add_task((write_gravatar, (adata['id'], gravatar_path)))
+ elif r.status_code == '404':
+ adata['gravatarHash'] = None
+ utils.p('gravatar for author not available.')
-# profile_counter =0
+fetching_gravatars = {}
+
def get_author(r):
+ ''' Writes author info (now from search results) and fetches gravatar if needed.'''
+ global fetching_gravatars
+
a_id = str(r['author']['id'])
preferences = bpy.context.preferences.addons['blenderkit'].preferences
authors = bpy.context.window_manager.get('bkit authors', {})
@@ -652,12 +651,16 @@ def get_author(r):
bpy.context.window_manager['bkit authors'] = authors
a = authors.get(a_id)
if a is None: # or a is '' or (a.get('gravatarHash') is not None and a.get('gravatarImg') is None):
- authors[a_id] = ''
- thread = threading.Thread(target=fetch_author, args=(a_id, preferences.api_key), daemon=True)
+ a = r['author']
+ a['id'] = a_id
+ a['tooltip'] = generate_author_textblock(a)
+
+ authors[a_id] = a
+ if fetching_gravatars.get(a['id']) is None:
+ fetching_gravatars[a['id']] = True
+
+ thread = threading.Thread(target=fetch_gravatar, args=(a.copy(),), daemon=True)
thread.start()
- # global profile_counter
- # profile_counter+=1
- # print(profile_counter,'author:', a_id)
return a
@@ -846,11 +849,9 @@ class Searcher(threading.Thread):
thumb_full_urls = []
thumb_full_filepaths = []
# END OF PARSING
- getting_authors = {}
for d in rdata.get('results', []):
- if getting_authors.get(d['author']['id']) is None:
- get_author(d)
- getting_authors[d['author']['id']] = True
+
+ get_author(d)
for f in d['files']:
# TODO move validation of published assets to server, too manmy checks here.
@@ -964,14 +965,13 @@ def build_query_common(query, props):
# query["procedural"] = False
if props.search_procedural == "PROCEDURAL":
- #todo this procedural hack should be replaced with the parameter
+ # todo this procedural hack should be replaced with the parameter
query["files_size_lte"] = 1024 * 1024
# query["procedural"] = True
elif props.search_file_size:
query_common["files_size_gte"] = props.search_file_size_min * 1024 * 1024
query_common["files_size_lte"] = props.search_file_size_max * 1024 * 1024
-
query.update(query_common)
@@ -1114,7 +1114,7 @@ def search(category='', get_next=False, author_id=''):
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
search_start_time = time.time()
- #mt('start')
+ # mt('start')
scene = bpy.context.scene
uiprops = scene.blenderkitUI
diff --git a/blenderkit/tasks_queue.py b/blenderkit/tasks_queue.py
index 3bf20181..95ffb1a6 100644
--- a/blenderkit/tasks_queue.py
+++ b/blenderkit/tasks_queue.py
@@ -43,15 +43,15 @@ def get_queue():
return t.task_queue
class task_object:
- def __init__(self, command = '', arguments = (), wait = 0):
+ def __init__(self, command = '', arguments = (), wait = 0, only_last = False):
self.command = command
self.arguments = arguments
self.wait = wait
+ self.only_last = only_last
-
-def add_task(task, wait = 0):
+def add_task(task, wait = 0, only_last = False):
q = get_queue()
- taskob = task_object(task[0],task[1], wait = wait)
+ taskob = task_object(task[0],task[1], wait = wait, only_last = only_last)
q.put(taskob)
@@ -60,6 +60,23 @@ def queue_worker():
q = get_queue()
back_to_queue = [] #delayed events
+ stashed = {}
+ # first round we get all tasks that are supposed to be stashed and run only once (only_last option)
+ # stashing finds tasks with the property only_last and same command and executes only the last one.
+ while not q.empty():
+ task = q.get()
+ if task.only_last:
+ stashed[task.command] = task
+ else:
+ back_to_queue.append(task)
+ #return tasks to que except for stashed
+ for task in back_to_queue:
+ q.put(task)
+ #return stashed tasks to queue
+ for k in stashed.keys():
+ q.put(stashed[k])
+ #second round, execute or put back waiting tasks.
+ back_to_queue = []
while not q.empty():
# print('window manager', bpy.context.window_manager)
task = q.get()
diff --git a/blenderkit/ui.py b/blenderkit/ui.py
index 3f4d0381..2101e54b 100644
--- a/blenderkit/ui.py
+++ b/blenderkit/ui.py
@@ -1575,7 +1575,8 @@ class AssetBarOperator(bpy.types.Operator):
else:
# first, test if object can have material applied.
- if object is not None and not object.is_library_indirect:
+ #TODO add other types here if droppable.
+ if object is None or object.is_library_indirect and object.type =='MESH' :
target_object = object.name
# create final mesh to extract correct material slot
depsgraph = bpy.context.evaluated_depsgraph_get()
@@ -1778,6 +1779,8 @@ class UndoWithContext(bpy.types.Operator):
# def modal(self, context, event):
# return {'RUNNING_MODAL'}
+ message = StringProperty('Undo Message', default = 'BlenderKit operation')
+
def execute(self, context):
C_dict = bpy.context.copy()
C_dict.update(region='WINDOW')
@@ -1785,7 +1788,7 @@ class UndoWithContext(bpy.types.Operator):
w, a, r = get_largest_3dview()
override = {'window': w, 'screen': w.screen, 'area': a, 'region': r}
C_dict.update(override)
- bpy.ops.ed.undo_push(C_dict, 'INVOKE_REGION_WIN')
+ bpy.ops.ed.undo_push(C_dict, 'INVOKE_REGION_WIN', message = self.message)
return {'FINISHED'}
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
index 8bd9fd58..3dabbe68 100644
--- a/blenderkit/ui_panels.py
+++ b/blenderkit/ui_panels.py
@@ -81,9 +81,9 @@ def draw_ratings(layout, context):
# layout.label(text='compliments')
# layout.prop(bkit_ratings, 'rating_compliments', text='')
- row = layout.row()
- op = row.operator("object.blenderkit_rating_upload", text="Send rating", icon='URL')
- return op
+ # row = layout.row()
+ # op = row.operator("object.blenderkit_rating_upload", text="Send rating", icon='URL')
+ # return op
def draw_upload_common(layout, props, asset_type, context):
@@ -391,7 +391,7 @@ class VIEW3D_PT_blenderkit_model_properties(Panel):
bl_idname = "VIEW3D_PT_blenderkit_model_properties"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
- bl_label = "Selected Asset"
+ bl_label = "Selected Model"
bl_context = "objectmode"
@classmethod
@@ -403,7 +403,12 @@ class VIEW3D_PT_blenderkit_model_properties(Panel):
# draw asset properties here
layout = self.layout
- o = bpy.context.active_object
+ o = utils.get_active_model()
+ # o = bpy.context.active_object
+ if o.get('asset_data') is None:
+ label_multiline(layout, text='To upload this asset to BlenderKit, go to the Find and Upload Assets pael.')
+ layout.prop(o, 'name')
+
if o.get('asset_data') is not None:
ad = o['asset_data']
layout.label(text=str(ad['name']))
@@ -506,8 +511,8 @@ class VIEW3D_PT_blenderkit_login(Panel):
def draw_panel_model_rating(self, context):
o = bpy.context.active_object
- op = draw_ratings(self.layout, context) # , props)
- op.asset_type = 'MODEL'
+ draw_ratings(self.layout, context) # , props)
+ # op.asset_type = 'MODEL'
def draw_panel_material_upload(self, context):
@@ -584,7 +589,7 @@ def draw_panel_material_search(self, context):
if props.search_advanced:
layout.separator()
- layout.label(text = 'texture types')
+ layout.label(text='texture types')
col = layout.column()
col.prop(props, "search_procedural", expand=True)
@@ -609,8 +614,8 @@ def draw_panel_material_search(self, context):
def draw_panel_material_ratings(self, context):
- op = draw_ratings(self.layout, context) # , props)
- op.asset_type = 'MATERIAL'
+ draw_ratings(self.layout, context) # , props)
+ # op.asset_type = 'MATERIAL'
def draw_panel_brush_upload(self, context):
@@ -643,9 +648,9 @@ def draw_panel_brush_search(self, context):
def draw_panel_brush_ratings(self, context):
# props = utils.get_brush_props(context)
- op = draw_ratings(self.layout, context) # , props)
-
- op.asset_type = 'BRUSH'
+ draw_ratings(self.layout, context) # , props)
+ #
+ # op.asset_type = 'BRUSH'
def draw_login_buttons(layout):