diff options
author | Vilém Duha <vilda.novak@gmail.com> | 2020-01-31 16:45:46 +0300 |
---|---|---|
committer | Vilém Duha <vilda.novak@gmail.com> | 2020-02-06 11:56:35 +0300 |
commit | 168f14425616fea37529e79e278b9b46b436adfc (patch) | |
tree | b669ad50f229a5e9e6b382b3656968da622077f8 | |
parent | 55824952f59015f752a9796d2b73e0f75ca67fba (diff) |
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)
-rw-r--r-- | blenderkit/__init__.py | 17 | ||||
-rw-r--r-- | blenderkit/asset_inspector.py | 9 | ||||
-rw-r--r-- | blenderkit/oauth.py | 2 | ||||
-rw-r--r-- | blenderkit/ratings.py | 74 | ||||
-rw-r--r-- | blenderkit/search.py | 93 | ||||
-rw-r--r-- | blenderkit/tasks_queue.py | 25 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 31 |
7 files changed, 163 insertions, 88 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index bd9f7e3f..9db72bb0 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) @@ -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/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/search.py b/blenderkit/search.py index 56c22dbb..f5cadaad 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -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 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_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): |