diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index 6c690553b58eb3c1ec1a5f6f2007a54a928686af..f3746ce783f7808b7c13d808e58d3ff57c0ccec1 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -113,10 +113,7 @@ def check_timers_timer(): return 5.0 -licenses = ( - ('royalty_free', 'Royalty Free', 'royalty free commercial license'), - ('cc_zero', 'Creative Commons Zero', 'Creative Commons Zero'), -) + conditions = ( ('UNSPECIFIED', 'Unspecified', "Don't use this in search"), ('NEW', 'New', 'Shiny new item'), @@ -266,7 +263,7 @@ def asset_type_callback(self, context): ('HDR', 'Hdrs', 'Browse hdrs', 'WORLD', 4), ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 5) ) - + return items @@ -527,29 +524,6 @@ def name_update(self, context): utils.name_update() -def update_tags(self, context): - props = utils.get_upload_props() - - commasep = props.tags.split(',') - ntags = [] - for tag in commasep: - if len(tag) > 19: - short_tags = tag.split(' ') - for short_tag in short_tags: - if len(short_tag) > 19: - short_tag = short_tag[:18] - ntags.append(short_tag) - else: - ntags.append(tag) - if len(ntags) == 1: - ntags = ntags[0].split(' ') - ns = '' - for t in ntags: - if t != '': - ns += t + ',' - ns = ns[:-1] - if props.tags != ns: - props.tags = ns def update_free(self, context): @@ -597,7 +571,7 @@ class BlenderKitCommonUploadProps(object): name="Tags", description="List of tags, separated by commas (optional)", default="", - update=update_tags + update=utils.update_tags ) name_changed: BoolProperty(name="Name Changed", @@ -616,7 +590,7 @@ class BlenderKitCommonUploadProps(object): default="METALLIC", ) license: EnumProperty( - items=licenses, + items=upload.licenses, default='royalty_free', description='License. Please read our help for choosing the right licenses', ) diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py index 3705f8205fb2b2dae80e3074a23dbbb32d1e45e8..898867dd83377474bebb560039197949fe83f30d 100644 --- a/blenderkit/ratings.py +++ b/blenderkit/ratings.py @@ -29,6 +29,7 @@ else: import bpy import requests, threading import logging + bk_logger = logging.getLogger('blenderkit') from bpy.props import ( @@ -59,7 +60,7 @@ def pretty_print_POST(req): def upload_rating_thread(url, ratings, headers): ''' Upload rating thread function / disconnected from blender data.''' - bk_logger.debug('upload rating', url, ratings) + bk_logger.debug('upload rating ' + url + str(ratings)) for rating_name, score in ratings: if (score != -1 and score != 0): rating_url = url + rating_name + '/' @@ -388,7 +389,7 @@ class FastRateMenu(Operator): def poll(cls, context): # scene = bpy.context.scene # ui_props = scene.blenderkitUI - return True#ui_props.active_index > -1 + return True # ui_props.active_index > -1 def draw(self, context): layout = self.layout @@ -445,11 +446,12 @@ class FastRateMenu(Operator): wm = context.window_manager if utils.profile_is_validator() and self.asset_type == 'model': - #spawn a wider one for validators for the enum buttons - return wm.invoke_props_dialog(self, width = 500) + # spawn a wider one for validators for the enum buttons + return wm.invoke_props_dialog(self, width=500) else: return wm.invoke_props_dialog(self) + def rating_menu_draw(self, context): layout = self.layout diff --git a/blenderkit/ui.py b/blenderkit/ui.py index 60e99c4f19c1a9b7f2ca8cbd2445ce9df9ca50ee..411c3b18ef5f7a340d9d8ea850c5504251aa5840 100644 --- a/blenderkit/ui.py +++ b/blenderkit/ui.py @@ -1882,7 +1882,7 @@ def register_ui(): km = wm.keyconfigs.addon.keymaps['Window'] kmi = km.keymap_items.new(ratings.FastRateMenu.bl_idname, 'F', 'PRESS', ctrl=False, shift=False) addon_keymapitems.append(kmi) - kmi = km.keymap_items.new(upload.FastCategory.bl_idname, 'F', 'PRESS', ctrl=True, shift=False) + kmi = km.keymap_items.new(upload.FastMetadata.bl_idname, 'F', 'PRESS', ctrl=True, shift=False) addon_keymapitems.append(kmi) diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index f3e0491e2c12dc1a8f4132f4368921cf195353e4..985a846b374649a6616351e075fc852db9813350 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -1282,6 +1282,12 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False): if author_id == str(profile['user']['id']): layout.label(text='Management tools:') + + row = layout.row() + row.operator_context = 'INVOKE_DEFAULT' + op = layout.operator('wm.blenderkit_fast_metadata', text='Fast Edit Metadata') + op.asset_id = asset_data['id'] + row = layout.row() row.operator_context = 'INVOKE_DEFAULT' op = row.operator('object.blenderkit_change_status', text='Delete') diff --git a/blenderkit/upload.py b/blenderkit/upload.py index 9d9303a9f8b9dc4d3cf99fb088c29beb070ee0c6..9ca9f68db5eed0f57327b4dd7d3ae26e932a8b46 100644 --- a/blenderkit/upload.py +++ b/blenderkit/upload.py @@ -62,6 +62,10 @@ from bpy.types import ( UIList ) +licenses = ( + ('royalty_free', 'Royalty Free', 'royalty free commercial license'), + ('cc_zero', 'Creative Commons Zero', 'Creative Commons Zero'), +) def comma2array(text): commasep = text.split(',') @@ -161,7 +165,7 @@ def camel_to_sub(content): return replaced -def get_upload_data(caller = None, context = None, asset_type = None): +def get_upload_data(caller=None, context=None, asset_type=None): ''' works though metadata from addom props and prepares it for upload to dicts. Parameters @@ -431,7 +435,7 @@ def get_upload_data(caller = None, context = None, asset_type = None): ui_props = bpy.context.scene.blenderkitUI # imagename = ui_props.hdr_upload_image - image = ui_props.hdr_upload_image#bpy.data.images.get(imagename) + image = ui_props.hdr_upload_image # bpy.data.images.get(imagename) if not image: return None, None @@ -452,7 +456,7 @@ def get_upload_data(caller = None, context = None, asset_type = None): # mat analytics happen here, since they don't take up any time... upload_params = { - "textureResolutionMax" : props.texture_resolution_max + "textureResolutionMax": props.texture_resolution_max } @@ -491,7 +495,7 @@ def get_upload_data(caller = None, context = None, asset_type = None): upload_data["description"] = props.description upload_data["tags"] = comma2array(props.tags) - #category is always only one value by a slug, that's why we go down to the lowest level and overwrite. + # category is always only one value by a slug, that's why we go down to the lowest level and overwrite. if props.category == '': upload_data["category"] = asset_type.lower() else: @@ -517,6 +521,7 @@ def get_upload_data(caller = None, context = None, asset_type = None): return export_data, upload_data + def patch_individual_metadata(asset_id, metadata_dict, api_key): upload_data = metadata_dict url = paths.get_api_url() + 'assets/' + str(asset_id) + '/' @@ -528,23 +533,12 @@ def patch_individual_metadata(asset_id, metadata_dict, api_key): return {'CANCELLED'} return {'FINISHED'} -def category_change_thread(asset_id, category, api_key): - upload_data = { - "category": category - } - url = paths.get_api_url() + 'assets/' + str(asset_id) + '/' - headers = utils.get_headers(api_key) - try: - r = rerequests.patch(url, json=upload_data, headers=headers, verify=True) # files = files, - except requests.exceptions.RequestException as e: - print(e) - return {'CANCELLED'} - return {'FINISHED'} -# class OBJECT_MT_blenderkit_fast_category_menu(bpy.types.Menu): + +# class OBJECT_MT_blenderkit_fast_metadata_menu(bpy.types.Menu): # bl_label = "Fast category change" -# bl_idname = "OBJECT_MT_blenderkit_fast_category_menu" +# bl_idname = "OBJECT_MT_blenderkit_fast_metadata_menu" # # def draw(self, context): # layout = self.layout @@ -558,16 +552,35 @@ def category_change_thread(asset_id, category, api_key): # for c in categories: # if c['name'].lower() == asset_data['assetType']: # for ch in c['children']: -# op = layout.operator('wm.blenderkit_fast_category', text = ch['name']) -# op = layout.operator('wm.blenderkit_fast_category', text = ch['name']) +# op = layout.operator('wm.blenderkit_fast_metadata', text = ch['name']) +# op = layout.operator('wm.blenderkit_fast_metadata', text = ch['name']) -class FastCategory(bpy.types.Operator): +class FastMetadata(bpy.types.Operator): """Fast change of the category of object directly in asset bar.""" - bl_idname = "wm.blenderkit_fast_category" - bl_label = "Update categories" + bl_idname = "wm.blenderkit_fast_metadata" + bl_label = "Update metadata" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} + asset_id: StringProperty( + name="Asset Base Id", + description="Unique name of the asset (hidden)", + default="" + ) + name: StringProperty( + name="Name", + description="Main name of the asset", + default="", + ) + description: StringProperty( + name="Description", + description="Description of the asset", + default="") + tags: StringProperty( + name="Tags", + description="List of tags, separated by commas (optional)", + default="", + ) category: EnumProperty( name="Category", description="main category to put into", @@ -583,11 +596,21 @@ class FastCategory(bpy.types.Operator): description="main category to put into", items=categories.get_subcategory1_enums ) - - asset_id: StringProperty( - name="Asset Base Id", - description="Unique name of the asset (hidden)", - default="") + license: EnumProperty( + items=licenses, + default='royalty_free', + description='License. Please read our help for choosing the right licenses', + ) + is_private: EnumProperty( + name="Thumbnail Style", + items=( + ('PRIVATE', 'Private', "You asset will be hidden to public. The private assets are limited by a quota."), + ('PUBLIC', 'Public', '"Your asset will go into the validation process automatically') + ), + description="If not marked private, your asset will go into the validation process automatically\n" + "Private assets are limited by quota.", + default="PUBLIC", + ) @classmethod def poll(cls, context): @@ -600,34 +623,47 @@ class FastCategory(bpy.types.Operator): # col = layout.column() layout.label(text=self.message) row = layout.row() - # col = row.column() - # 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') if self.category != 'NONE' and self.subcategory != 'NONE': layout.prop(self, 'subcategory') if self.subcategory != 'NONE' and self.subcategory1 != 'NONE': layout.prop(self, 'subcategory1') + layout.prop(self, 'name') + layout.prop(self, 'description') + layout.prop(self, 'tags') + layout.prop(self, 'is_private', expand=True) + if self.is_private == 'PUBLIC': + layout.prop(self, 'license') + - # 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() - # layout.prop(self, 'subcategory') - # layout.prop(self, 'subcategory1') - # # layout.prop(self, 'subcategory', expand = True) def execute(self, context): user_preferences = bpy.context.preferences.addons['blenderkit'].preferences props = bpy.context.scene.blenderkitUI - if props.asset_type == 'MODEL': + if self.subcategory1 != 'NONE': + category = self.subcategory1 + elif self.subcategory != 'NONE': category = self.subcategory else: category = self.category - thread = threading.Thread(target=category_change_thread, - args=(self.asset_id, category, user_preferences.api_key)) + utils.update_tags(self, context) + + mdict = { + 'category': category, + 'displayName': self.name, + 'description': self.description, + 'tags': comma2array(self.tags), + 'isPrivate': self.is_private == 'PRIVATE', + 'license': self.license, + } + + thread = threading.Thread(target=patch_individual_metadata, + args=(self.asset_id, mdict, user_preferences.api_key)) thread.start() + tasks_queue.add_task((ui.add_report, (f'Uploading metadata for {self.name}. ' + f'Refreash search results to see that changes applied correctly.', 8,))) + return {'FINISHED'} def invoke(self, context, event): @@ -636,20 +672,34 @@ class FastCategory(bpy.types.Operator): if ui_props.active_index > -1: sr = bpy.context.scene['search results'] asset_data = dict(sr[ui_props.active_index]) - self.asset_id = asset_data['id'] - self.asset_type = asset_data['assetType'] - cat_path = categories.get_category_path(bpy.context.window_manager['bkit_categories'], - asset_data['category']) - try: - if len(cat_path) > 1: - self.category = cat_path[1] - if len(cat_path) > 2: - self.subcategory = cat_path[2] - except Exception as e: - print(e) - self.message = f"Recategorize asset {asset_data['name']}" + else: + for result in bpy.context.scene['search results']: + if result['id'] == self.asset_id: + asset_data = dict(result) + + self.asset_id = asset_data['id'] + self.asset_type = asset_data['assetType'] + cat_path = categories.get_category_path(bpy.context.window_manager['bkit_categories'], + asset_data['category']) + try: + if len(cat_path) > 1: + self.category = cat_path[1] + if len(cat_path) > 2: + self.subcategory = cat_path[2] + except Exception as e: + print(e) + self.message = f"Recategorize asset {asset_data['name']}" + self.name = asset_data['displayName'] + self.description = asset_data['description'] + self.tags = ','.join(asset_data['tags']) + if asset_data['isPrivate']: + self.is_private = 'PRIVATE' + else: + self.is_private = 'PUBLIC' + self.license = asset_data['license'] + wm = context.window_manager - return wm.invoke_props_dialog(self) + return wm.invoke_props_dialog(self, width = 600) def verification_status_change_thread(asset_id, state, api_key): @@ -773,7 +823,6 @@ class Uploader(threading.Thread): 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/' @@ -836,17 +885,16 @@ class Uploader(threading.Thread): 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']}'" + # 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']}'" + 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 + # 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: @@ -868,7 +916,7 @@ class Uploader(threading.Thread): with open(datafile, 'w') as s: json.dump(data, s) - #non waiting method - not useful here.. + # non waiting method - not useful here.. # proc = subprocess.Popen([ # binary_path, # "--background", @@ -893,7 +941,6 @@ class Uploader(threading.Thread): self.end_upload('Upload stopped by user') return - files = [] if 'THUMBNAIL' in self.upload_set: files.append({ @@ -902,7 +949,6 @@ class Uploader(threading.Thread): "file_path": self.export_data["thumbnail_path"] }) if 'MAINFILE' in self.upload_set: - files.append({ "type": "blend", "index": 0, @@ -959,7 +1005,6 @@ def check_missing_data(asset_type, props): check_missing_data_brush(props) - def start_upload(self, context, asset_type, reupload, upload_set): '''start upload process, by processing data, then start a thread that cares about the rest of the upload.''' @@ -980,7 +1025,6 @@ def start_upload(self, context, asset_type, reupload, upload_set): # do this for fixing long tags in some upload cases props.tags = props.tags[:] - # check for missing metadata check_missing_data(asset_type, props) # if previous check did find any problems then @@ -992,7 +1036,7 @@ def start_upload(self, context, asset_type, reupload, upload_set): props.asset_base_id = '' props.id = '' - export_data, upload_data = get_upload_data(caller = self, context = context, asset_type = asset_type) + export_data, upload_data = get_upload_data(caller=self, context=context, asset_type=asset_type) # print(export_data) # print(upload_data) # check if thumbnail exists, generate for HDR: @@ -1010,7 +1054,6 @@ def start_upload(self, context, asset_type, reupload, upload_set): props.upload_state = "Starting upload. Please don't close Blender until upload finishes" props.uploading = True - # save a copy of the file for processing. Only for blend files basename, ext = os.path.splitext(bpy.data.filepath) if not ext: @@ -1249,15 +1292,15 @@ class AssetVerificationStatusChange(Operator): def register_upload(): bpy.utils.register_class(UploadOperator) - # bpy.utils.register_class(FastCategoryMenu) - bpy.utils.register_class(FastCategory) + # bpy.utils.register_class(FastMetadataMenu) + bpy.utils.register_class(FastMetadata) bpy.utils.register_class(AssetDebugPrint) bpy.utils.register_class(AssetVerificationStatusChange) def unregister_upload(): bpy.utils.unregister_class(UploadOperator) - # bpy.utils.unregister_class(FastCategoryMenu) - bpy.utils.unregister_class(FastCategory) + # bpy.utils.unregister_class(FastMetadataMenu) + bpy.utils.unregister_class(FastMetadata) bpy.utils.unregister_class(AssetDebugPrint) bpy.utils.unregister_class(AssetVerificationStatusChange) diff --git a/blenderkit/utils.py b/blenderkit/utils.py index 09d6c77b5a706fa538902242e372a5e9ec757e61..7befc97e528f00176a05f896fab0fd7879612b34 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -677,6 +677,7 @@ def get_param(asset_data, parameter_name): return None + def params_to_dict(params): params_dict = {} for p in params: @@ -707,6 +708,29 @@ def dict_to_params(inputs, parameters=None): }) return parameters +def update_tags(self, context): + props = self + + commasep = props.tags.split(',') + ntags = [] + for tag in commasep: + if len(tag) > 19: + short_tags = tag.split(' ') + for short_tag in short_tags: + if len(short_tag) > 19: + short_tag = short_tag[:18] + ntags.append(short_tag) + else: + ntags.append(tag) + if len(ntags) == 1: + ntags = ntags[0].split(' ') + ns = '' + for t in ntags: + if t != '': + ns += t + ',' + ns = ns[:-1] + if props.tags != ns: + props.tags = ns def user_logged_in(): a = bpy.context.window_manager.get('bkit profile')