BlenderKit: Fix rating updates for panel.
Fix a bug in upload when checking image /procedural assets Fix oauth return values in case of error Fix fetching of authors through search api instead of profiles. Fix task_queue with multiple tasks of the same by enabling stashing Fix selected asset panel, rename it to selected model by now (not supporting other assets now)
This commit is contained in:
parent
55824952f5
commit
168f144256
|
@ -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)
|
||||
|
@ -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",
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -244,10 +244,10 @@ 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'],
|
||||
'author': r['author'],
|
||||
}
|
||||
asset_data['downloaded'] = 0
|
||||
|
||||
|
@ -504,7 +504,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 +606,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 +652,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 +850,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 +966,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 +1115,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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue