diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py
index 5cecc7b9144a2739646fc8c62e10a2ad5c80b4c4..87358d9b7e43f62bdcfe3183a73a7336ada5967c 100644
--- a/blenderkit/__init__.py
+++ b/blenderkit/__init__.py
@@ -19,14 +19,14 @@
 bl_info = {
     "name": "BlenderKit Asset Library",
     "author": "Vilem Duha, Petr Dlouhy",
-    "version": (1, 0, 29),
+    "version": (1, 0, 30),
     "blender": (2, 82, 0),
     "location": "View3D > Properties > BlenderKit",
     "description": "Online BlenderKit library (materials, models, brushes and more)",
     "warning": "",
     "wiki_url": "https://youtu.be/1hVgcQhIAo8"
                 "Scripts/Add_Mesh/BlenderKit",
-    "category": "Add Mesh",
+    "category": "3D View",
 }
 
 if "bpy" in locals():
@@ -39,6 +39,7 @@ if "bpy" in locals():
     ratings = reload(ratings)
     autothumb = reload(autothumb)
     ui = reload(ui)
+    icons = reload(icons)
     bg_blender = reload(bg_blender)
     paths = reload(paths)
     utils = reload(utils)
@@ -48,7 +49,8 @@ if "bpy" in locals():
     bkit_oauth = reload(bkit_oauth)
     tasks_queue = reload(tasks_queue)
 else:
-    from blenderkit import asset_inspector, search, download, upload, ratings, autothumb, ui, bg_blender, paths, utils, \
+    from blenderkit import asset_inspector, search, download, upload, ratings, autothumb, ui, icons, bg_blender, paths, \
+        utils, \
         overrides, ui_panels, categories, bkit_oauth, tasks_queue
 
 import os
@@ -103,6 +105,7 @@ def check_timers_timer():
         bpy.app.timers.register(bg_blender.bg_update)
     return 5.0
 
+
 licenses = (
     ('royalty_free', 'Royalty Free', 'royalty free commercial license'),
     ('cc_zero', 'Creative Commons Zero', 'Creative Commons Zero'),
@@ -191,6 +194,7 @@ thumbnail_resolutions = (
     ('2048', '2048', ''),
 )
 
+
 def get_upload_asset_type(self):
     typemapper = {
         BlenderKitModelUploadProps: 'model',
@@ -244,16 +248,17 @@ def switch_search_results(self, context):
         s['search results orig'] = s.get('bkit brush search orig')
     search.load_previews()
 
+
 def asset_type_callback(self, context):
-    #s = bpy.context.scene
-    #ui_props = s.blenderkitUI
+    # s = bpy.context.scene
+    # ui_props = s.blenderkitUI
     if self.down_up == 'SEARCH':
         items = (
-            ('MODEL', 'Search Models', 'Browse models', 'OBJECT_DATAMODE', 0),
+            ('MODEL', 'Find Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0),
             # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1),
-            ('MATERIAL', 'Search Materials', 'Browse materials', 'MATERIAL', 2),
+            ('MATERIAL', 'Find Materials', 'Find models in the BlenderKit online database', 'MATERIAL', 2),
             # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3),
-            ('BRUSH', 'Search Brushes', 'Browse brushes', 'BRUSH_DATA', 3)
+            ('BRUSH', 'Find Brushes', 'Find models in the BlenderKit online database', 'BRUSH_DATA', 3)
         )
     else:
         items = (
@@ -265,6 +270,7 @@ def asset_type_callback(self, context):
         )
     return items
 
+
 class BlenderKitUIProps(PropertyGroup):
     down_up: EnumProperty(
         name="Download vs Upload",
@@ -378,6 +384,10 @@ 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):
+    if self.search_procedural in ('PROCEDURAL', 'BOTH'):
+        self.search_texture_resolution = False
+    search.search_update(self, context)
 
 class BlenderKitCommonSearchProps(object):
     # STATES
@@ -386,12 +396,90 @@ class BlenderKitCommonSearchProps(object):
                                  default=False)
     search_done: BoolProperty(name="Search Completed", description="at least one search did run (internal)",
                               default=False)
+    own_only: BoolProperty(name="My Assets", description="Search only for your assets",
+                           default=False)
+    search_advanced: BoolProperty(name="Advanced Search Options", description="use advanced search properties",
+                                  default=False, update=search.search_update)
+
     search_error: BoolProperty(name="Search Error", description="last search had an error", default=False)
     report: StringProperty(
         name="Report",
         description="errors and messages",
         default="")
 
+    # TEXTURE RESOLUTION
+    search_texture_resolution: BoolProperty(name="Texture Resolution",
+                                            description="Span of the texture resolutions",
+                                            default=False,
+                                            update=search.search_update,
+                                            )
+    search_texture_resolution_min: IntProperty(name="Min Texture Resolution",
+                                               description="Minimum texture resolution",
+                                               default=256,
+                                               min=0,
+                                               max=32768,
+                                               update=search.search_update,
+                                               )
+
+    search_texture_resolution_max: IntProperty(name="Max Texture Resolution",
+                                               description="Maximum texture resolution",
+                                               default=4096,
+                                               min=0,
+                                               max=32768,
+                                               update=search.search_update,
+                                               )
+
+    # file_size
+    search_file_size: BoolProperty(name="File Size",
+                                   description="Span of the file sizes",
+                                   default=False,
+                                   update=search.search_update,
+                                   )
+    search_file_size_min: IntProperty(name="Min File Size",
+                                      description="Minimum file size",
+                                      default=0,
+                                      min=0,
+                                      max=2000,
+                                      update=search.search_update,
+                                      )
+
+    search_file_size_max: IntProperty(name="Max File Size",
+                                      description="Maximum file size",
+                                      default=500,
+                                      min=0,
+                                      max=2000,
+                                      update=search.search_update,
+                                      )
+
+    search_procedural: EnumProperty(
+        items=(
+            ('BOTH', 'Both', ''),
+            ('PROCEDURAL', 'Procedural', ''),
+            ('TEXTURE_BASED', 'Texture based', ''),
+
+        ),
+        default='BOTH',
+        description='Search only procedural/texture based assets',
+        update=search_procedural_update
+    )
+
+    search_verification_status: EnumProperty(
+        name="Verification status",
+        description="Search by verification status",
+        items=
+        (
+            ('ALL', 'All', 'All'),
+            ('UPLOADING', 'Uploading', 'Uploading'),
+            ('UPLOADED', 'Uploaded', 'Uploaded'),
+            ('VALIDATED', 'Validated', 'Calidated'),
+            ('ON_HOLD', 'On Hold', 'On Hold'),
+            ('REJECTED', 'Rejected', 'Rejected'),
+            ('DELETED', 'Deleted', 'Deleted'),
+        ),
+        default='ALL',
+        update=search.search_update,
+    )
+
 
 def name_update(self, context):
     ''' checks for name change, because it decides if whole asset has to be re-uploaded. Name is stored in the blend file
@@ -399,7 +487,6 @@ def name_update(self, context):
     utils.name_update()
 
 
-
 def update_tags(self, context):
     props = utils.get_upload_props()
 
@@ -424,6 +511,7 @@ def update_tags(self, context):
     if props.tags != ns:
         props.tags = ns
 
+
 def update_free(self, context):
     if self.is_free == False:
         self.is_free = True
@@ -438,6 +526,7 @@ def update_free(self, context):
 
         bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
 
+
 class BlenderKitCommonUploadProps(object):
     id: StringProperty(
         name="Asset Version Id",
@@ -504,12 +593,12 @@ class BlenderKitCommonUploadProps(object):
     )
 
     is_procedural: BoolProperty(name="Procedural",
-                          description="Asset is procedural - has no texture.",
-                          default=True
-                          )
+                                description="Asset is procedural - has no texture.",
+                                default=True
+                                )
     node_count: IntProperty(name="Node count", description="Total nodes in the asset", default=0)
-    texture_count: IntProperty(name="Node count", description="Total nodes in the asset", default=0)
-    total_megapixels: IntProperty(name="Node count", description="Total nodes in the asset", default=0)
+    texture_count: IntProperty(name="Texture count", description="Total texture count in asset", default=0)
+    total_megapixels: IntProperty(name="Megapixels", description="Total megapixels of texture", default=0)
 
     # is_private: BoolProperty(name="Asset is Private",
     #                       description="If not marked private, your asset will go into the validation process automatically\n"
@@ -591,22 +680,26 @@ class BlenderKitMaterialSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
         items=search_material_styles,
         description="Style of material",
         default="ANY",
+        update=search.search_update,
     )
     search_style_other: StringProperty(
         name="Style Other",
         description="Style not in the list",
         default="",
+        update=search.search_update,
     )
     search_engine: EnumProperty(
         name='Engine',
         items=engines,
         default='NONE',
         description='Output engine',
+        update=search.search_update,
     )
     search_engine_other: StringProperty(
         name="Engine",
         description="engine not specified by addon",
         default="",
+        update=search.search_update,
     )
     automap: BoolProperty(name="Auto-Map",
                           description="reset object texture space and also add automatically a cube mapped UV "
@@ -1145,10 +1238,7 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
     )
 
     free_only: BoolProperty(name="Free only", description="Show only free models",
-                            default=False)
-
-    search_advanced: BoolProperty(name="Advanced Search Options", description="use advanced search properties",
-                                  default=False)
+                            default=False, update=search.search_update)
 
     # CONDITION
     search_condition: EnumProperty(
@@ -1167,52 +1257,45 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
 
     # DESIGN YEAR
     search_design_year: BoolProperty(name="Sesigned in Year",
-                                     description="when the object was approximately designed",
-                                     default=False)
+                                     description="When the object was approximately designed",
+                                     default=False,
+                                     update=search.search_update,
+                                     )
 
-    search_design_year_min: IntProperty(name="Min Age",
-                                        description="when the object was approximately designed",
-                                        default=1950, min=-100000000, max=1000000000)
+    search_design_year_min: IntProperty(name="Minimum Design Year",
+                                        description="Minimum design year",
+                                        default=1950, min=-100000000, max=1000000000,
+                                        update=search.search_update,
+                                        )
 
-    search_design_year_max: IntProperty(name="Max Age",
-                                        description="when the object was approximately designed",
+    search_design_year_max: IntProperty(name="Maximum Design Year",
+                                        description="Maximum design year",
                                         default=2017,
                                         min=0,
-                                        max=10000000)
-
-    # TEXTURE RESOLUTION
-    search_texture_resolution: BoolProperty(name="Texture Resolution",
-                                            description="Span of the texture resolutions",
-                                            default=False)
-
-    search_texture_resolution_min: IntProperty(name="Min Texture Resolution",
-                                               description="when the object was approximately designed",
-                                               default=256,
-                                               min=0,
-                                               max=32768)
-
-    search_texture_resolution_max: IntProperty(name="Max Texture Resolution",
-                                               description="when the object was approximately designed",
-                                               default=4096,
-                                               min=0,
-                                               max=32768)
+                                        max=10000000,
+                                        update=search.search_update,
+                                        )
 
     # POLYCOUNT
     search_polycount: BoolProperty(name="Use Polycount",
-                                   description="use polycount of object search tag",
-                                   default=False)
+                                   description="Use polycount of object search tag",
+                                   default=False,
+                                   update=search.search_update, )
 
     search_polycount_min: IntProperty(name="Min Polycount",
-                                      description="polycount of the asset minimum",
+                                      description="Minimum poly count of the asset",
                                       default=0,
                                       min=0,
-                                      max=100000000)
+                                      max=100000000,
+                                      update=search.search_update, )
 
     search_polycount_max: IntProperty(name="Max Polycount",
-                                      description="polycount of the asset maximum",
+                                      description="Maximum poly count of the asset",
                                       default=100000000,
                                       min=0,
-                                      max=100000000)
+                                      max=100000000,
+                                      update=search.search_update,
+                                      )
 
     append_method: EnumProperty(
         name="Import Method",
@@ -1324,9 +1407,9 @@ class BlenderKitAddonPreferences(AddonPreferences):
     )
 
     api_key_timeout: IntProperty(
-        name = 'api key timeout',
-        description = 'time where the api key will need to be refreshed',
-        default = 0,
+        name='api key timeout',
+        description='time where the api key will need to be refreshed',
+        default=0,
     )
 
     api_key_life: IntProperty(
@@ -1353,6 +1436,18 @@ class BlenderKitAddonPreferences(AddonPreferences):
         default=False
     )
 
+    tips_on_start: BoolProperty(
+        name="Show tips when starting blender",
+        description="Show tips when starting blender",
+        default=False
+    )
+
+    search_in_header: BoolProperty(
+        name="Show BlenderKit search in 3d view header",
+        description="Show BlenderKit search in 3d view header",
+        default=True
+    )
+
     global_dir: StringProperty(
         name="Global Files Directory",
         description="Global storage for your assets, will use subdirectories for the contents",
@@ -1389,7 +1484,7 @@ class BlenderKitAddonPreferences(AddonPreferences):
     thumbnail_use_gpu: BoolProperty(
         name="Use GPU for Thumbnails Rendering",
         description="By default this is off so you can continue your work without any lag",
-        default=True
+        default=False
     )
 
     panel_behaviour: EnumProperty(
@@ -1413,10 +1508,8 @@ class BlenderKitAddonPreferences(AddonPreferences):
                                    min=0,
                                    max=20)
 
-
     thumb_size: IntProperty(name="Assetbar thumbnail Size", default=96, min=-1, max=256)
 
-
     asset_counter: IntProperty(name="Usage Counter",
                                description="Counts usages so it asks for registration only after reaching a limit",
                                default=0,
@@ -1447,7 +1540,7 @@ class BlenderKitAddonPreferences(AddonPreferences):
                 layout.operator("wm.blenderkit_logout", text="Logout",
                                 icon='URL')
 
-        #if not self.enable_oauth:
+        # if not self.enable_oauth:
         layout.prop(self, "api_key", text='Your API Key')
         # layout.label(text='After you paste API Key, categories are downloaded, so blender will freeze for a few seconds.')
         layout.prop(self, "global_dir")
@@ -1459,6 +1552,8 @@ class BlenderKitAddonPreferences(AddonPreferences):
         # layout.prop(self, "panel_behaviour")
         layout.prop(self, "thumb_size")
         layout.prop(self, "max_assetbar_rows")
+        layout.prop(self, "tips_on_start")
+        layout.prop(self, "search_in_header")
 
 
 # registration
@@ -1531,6 +1626,7 @@ def register():
     ratings.register_ratings()
     autothumb.register_thumbnailer()
     ui.register_ui()
+    icons.register_icons()
     ui_panels.register_ui_panels()
     bg_blender.register()
     utils.load_prefs()
@@ -1544,10 +1640,10 @@ def register():
 
 
 def unregister():
-
     bpy.app.timers.unregister(check_timers_timer)
 
     ui.unregister_ui()
+    icons.unregister_icons()
     search.unregister_search()
     asset_inspector.unregister_asset_inspector()
     download.unregister_download()
diff --git a/blenderkit/asset_inspector.py b/blenderkit/asset_inspector.py
index 74f814ca3e3fefdec393297b7a7ee45e62b5b7a4..e6fdc65938ec1ad3167a70e5fbf318bae78deec2 100644
--- a/blenderkit/asset_inspector.py
+++ b/blenderkit/asset_inspector.py
@@ -85,8 +85,11 @@ def check_render_engine(props, obs):
     mattype = None
     materials = []
     shaders = []
+    textures = []
     props.uv = False
-
+    props.texture_count = 0
+    props.total_megapixels = 0
+    props.node_count = 0
     for ob in obs:  # TODO , this is duplicated here for other engines, otherwise this should be more clever.
         for ms in ob.material_slots:
             if ms.material is not None:
@@ -108,6 +111,7 @@ def check_render_engine(props, obs):
                 checknodes = m.node_tree.nodes[:]
                 while len(checknodes) > 0:
                     n = checknodes.pop()
+                    props.node_count +=1
                     if n.type == 'GROUP':  # dive deeper here.
                         checknodes.extend(n.node_tree.nodes)
                     if len(n.outputs) == 1 and n.outputs[0].type == 'SHADER' and n.type != 'GROUP':
@@ -115,19 +119,21 @@ def check_render_engine(props, obs):
                             shaders.append(n.type)
                     if n.type == 'TEX_IMAGE':
                         mattype = 'image based'
-                        if n.image is not None:
+                        props.is_procedural = False
+                        if n.image not in textures:
+                            textures.append(n.image)
+                            props.texture_count += 1
+                            props.total_megapixels += (n.image.size[0] * n.image.size[1])
 
                             maxres = max(n.image.size[0], n.image.size[1])
-
                             props.texture_resolution_max = max(props.texture_resolution_max, maxres)
-
                             minres = min(n.image.size[0], n.image.size[1])
-
                             if props.texture_resolution_min == 0:
                                 props.texture_resolution_min = minres
                             else:
                                 props.texture_resolution_min = min(props.texture_resolution_min, minres)
 
+
         # if mattype == None:
         #    mattype = 'procedural'
         # tags['material type'] = mattype
diff --git a/blenderkit/autothumb.py b/blenderkit/autothumb.py
index 06efd8a031b341de0fbe21adad4c96562bc4c2af..f26f99ddae467be508abfa17a05387e56ee4f1fe 100644
--- a/blenderkit/autothumb.py
+++ b/blenderkit/autothumb.py
@@ -262,6 +262,8 @@ class GenerateThumbnailOperator(bpy.types.Operator):
         layout.prop(props, 'thumbnail_samples')
         layout.prop(props, 'thumbnail_resolution')
         layout.prop(props, 'thumbnail_denoising')
+        preferences = bpy.context.preferences.addons['blenderkit'].preferences
+        layout.prop(preferences, "thumbnail_use_gpu")
 
     def execute(self, context):
         start_thumbnailer(self, context)
@@ -307,6 +309,8 @@ class GenerateMaterialThumbnailOperator(bpy.types.Operator):
         layout.prop(props, 'thumbnail_samples')
         layout.prop(props, 'thumbnail_denoising')
         layout.prop(props, 'adaptive_subdivision')
+        preferences = bpy.context.preferences.addons['blenderkit'].preferences
+        layout.prop(preferences, "thumbnail_use_gpu")
 
     def execute(self, context):
         start_material_thumbnailer(self, context)
diff --git a/blenderkit/download.py b/blenderkit/download.py
index c4a14ecdfc40b74f45c8daaa6a7c271926e186ab..3a99f66f42cc65ffab616bc544e50610bfa88eeb 100644
--- a/blenderkit/download.py
+++ b/blenderkit/download.py
@@ -441,6 +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()
     # moving reporting to on save.
     # report_use_success(asset_data['id'])
 
@@ -523,6 +524,34 @@ def timer_update():  # TODO might get moved to handle all blenderkit stuff, not
     return .5
 
 
+def download_file(asset_data):
+    #this is a simple non-threaded way to download files for background resolution genenration tool
+    file_name = paths.get_download_filenames(asset_data)[0]  # prefer global dir if possible.
+
+    if check_existing(asset_data):
+        # this sends the thread for processing, where another check should occur, since the file might be corrupted.
+        utils.p('not downloading, already in db')
+        return file_name
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    api_key = preferences.api_key
+
+    with open(file_name, "wb") as f:
+        print("Downloading %s" % file_name)
+        headers = utils.get_headers(api_key)
+
+        response = requests.get(asset_data['url'], stream=True)
+        total_length = response.headers.get('Content-Length')
+
+        if total_length is None:  # no content length header
+            f.write(response.content)
+        else:
+            dl = 0
+            for data in response.iter_content(chunk_size=4096):
+                dl += len(data)
+                print(dl)
+                f.write(data)
+    return file_name
+
 class Downloader(threading.Thread):
     def __init__(self, asset_data, tcom, scene_id, api_key):
         super(Downloader, self).__init__()
diff --git a/blenderkit/icons.py b/blenderkit/icons.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c6cea4b039f2ea24c2211b32dd35aa27364cc45
--- /dev/null
+++ b/blenderkit/icons.py
@@ -0,0 +1,53 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+
+import os
+import bpy
+
+# We can store multiple preview collections here,
+# however in this example we only store "main"
+icon_collections = {}
+
+icons_read = {
+    'fp.png': 'free',
+    'flp.png': 'full',
+}
+
+
+def register_icons():
+    # Note that preview collections returned by bpy.utils.previews
+    # are regular py objects - you can use them to store custom data.
+    import bpy.utils.previews
+    pcoll = bpy.utils.previews.new()
+
+    # path to the folder where the icon is
+    # the path is calculated relative to this py file inside the addon folder
+    icons_dir = os.path.join(os.path.dirname(__file__), "thumbnails")
+
+    # load a preview thumbnail of a file and store in the previews collection
+    for ir in icons_read.keys():
+        pcoll.load(icons_read[ir], os.path.join(icons_dir, ir), 'IMAGE')
+
+    icon_collections["main"] = pcoll
+
+
+def unregister_icons():
+    for pcoll in icon_collections.values():
+        bpy.utils.previews.remove(pcoll)
+    icon_collections.clear()
diff --git a/blenderkit/paths.py b/blenderkit/paths.py
index 3b0f22f9f630971213cb171bbdfd4545a91120aa..112e2465b907bca8d9be20a2df86b6838f7852fa 100644
--- a/blenderkit/paths.py
+++ b/blenderkit/paths.py
@@ -33,7 +33,6 @@ BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uplo
 BLENDERKIT_LOGIN_URL = "https://www.blenderkit.com/accounts/login"
 BLENDERKIT_OAUTH_LANDING_URL = "/oauth-landing/"
 BLENDERKIT_SIGNUP_URL = "https://www.blenderkit.com/accounts/register"
-BLENDERKIT_ADDON_FILE_URL = "https://www.blenderkit.com/get-blenderkit/"
 BLENDERKIT_SETTINGS_FILENAME = os.path.join(_presets, "bkit.json")
 
 
diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py
index 7684d0177d781bed07a14672f35e6b04b51299a5..96cbc01f91868d9fdfc054be979255b4961d1e68 100644
--- a/blenderkit/ratings.py
+++ b/blenderkit/ratings.py
@@ -80,19 +80,30 @@ def uplaod_review_thread(url, reviews, 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
+    headers = utils.get_headers(api_key)
+    rl = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
+    rtypes = ['quality', 'working_hours']
+    for rt in rtypes:
+        params = {
+            'rating_type' : rt
+        }
+        r = rerequests.get(r1, params=data, verify=True, headers=headers)
+        print(r.text)
 
 def upload_rating(asset):
     user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
     api_key = user_preferences.api_key
     headers = utils.get_headers(api_key)
 
-    asset_data = asset['asset_data']
-
     bkit_ratings = asset.bkit_ratings
     # print('rating asset', asset_data['name'], asset_data['asset_base_id'])
     url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
 
     ratings = [
+
     ]
 
     if bkit_ratings.rating_quality > 0.1:
@@ -154,7 +165,7 @@ asset_types = (
 class UploadRatingOperator(bpy.types.Operator):
     """Upload rating to the web db"""
     bl_idname = "object.blenderkit_rating_upload"
-    bl_label = "Upload the Rating"
+    bl_label = "Send Rating"
     bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
 
     # type of upload - model, material, textures, e.t.c.
diff --git a/blenderkit/rerequests.py b/blenderkit/rerequests.py
index 0524c156929300e82dd9ddbbf12fb6fd7d922b95..eab78fba473957d2cb34415e2be5a9245dd1ebd2 100644
--- a/blenderkit/rerequests.py
+++ b/blenderkit/rerequests.py
@@ -55,9 +55,11 @@ def rerequest(method, url, **kwargs):
             user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
             if user_preferences.api_key != '':
                 if user_preferences.enable_oauth:
-                    tasks_queue.add_task((ui.add_report, ('refreshing token.',)))
+                    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()
-                    auth_token, refresh_token, oauth_response = bkit_oauth.refresh_token(user_preferences.api_key_refresh, refresh_url)
+                    auth_token, refresh_token, oauth_response = bkit_oauth.refresh_token(
+                        user_preferences.api_key_refresh, refresh_url)
 
                     # utils.p(auth_token, refresh_token)
                     if auth_token is not None:
diff --git a/blenderkit/search.py b/blenderkit/search.py
index 87bf45da1f223e5b6a79076aca2e3452b84e5e56..56c22dbba5106dffb6f29d94f7ab1eb4abc9668c 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -24,12 +24,13 @@ if "bpy" in locals():
     utils = reload(utils)
     categories = reload(categories)
     ui = reload(ui)
+    colors = reload(colors)
     bkit_oauth = reload(bkit_oauth)
     version_checker = reload(version_checker)
     tasks_queue = reload(tasks_queue)
     rerequests = reload(rerequests)
 else:
-    from blenderkit import paths, utils, categories, ui, bkit_oauth, version_checker, tasks_queue, rerequests
+    from blenderkit import paths, utils, categories, ui, colors, bkit_oauth, version_checker, tasks_queue, rerequests
 
 import blenderkit
 from bpy.app.handlers import persistent
@@ -80,6 +81,16 @@ thumb_sml_download_threads = {}
 thumb_full_download_threads = {}
 reports = ''
 
+rtips = ['Click or drag model or material in scene to link/append ',
+         "Please rate responsively and plentifully. This helps us distribute rewards to the authors.",
+         "Click on brushes to link them into scene.",
+         "All materials and brushes are free.",
+         "Storage for public assets is unlimited.",
+         "Locked models are available if you subscribe to Full plan.",
+         "Login to upload your own models, materials or brushes.",
+         "Use 'A' key over asset bar to search assets by same author.",
+         "Use 'W' key over asset bar to open Authors webpage.", ]
+
 
 def refresh_token_timer():
     ''' this timer gets run every time the token needs refresh. It refreshes tokens and also categories.'''
@@ -117,16 +128,37 @@ def fetch_server_data():
 
 
 first_time = True
+last_clipboard = ''
+
 
 @bpy.app.handlers.persistent
 def timer_update():  # TODO might get moved to handle all blenderkit stuff.
-    #this makes a first search after opening blender. showing latest assets.
+    # this makes a first search after opening blender. showing latest assets.
     global first_time
     preferences = bpy.context.preferences.addons['blenderkit'].preferences
     if first_time:
         first_time = False
         if preferences.show_on_start:
             search()
+        if preferences.tips_on_start:
+            ui.get_largest_3dview()
+            ui.update_ui_size(ui.active_area, ui.active_region)
+            ui.add_report(text='BlenderKit Tip: ' + random.choice(rtips), timeout=12, color=colors.GREEN)
+
+    # clipboard monitoring to search assets from web
+    global last_clipboard
+    if bpy.context.window_manager.clipboard != last_clipboard:
+        last_clipboard = bpy.context.window_manager.clipboard
+        instr = 'asset_base_id:'
+        if last_clipboard[:len(instr)] == instr:
+            atstr = 'asset_type:'
+            ati = last_clipboard.find(atstr)
+            if ati > -1:
+                at = last_clipboard[ati:]
+
+            search_props = utils.get_search_props()
+            search_props.search_keywords = last_clipboard
+            search()
 
     global search_threads
     # don't do anything while dragging - this could switch asset type during drag, and make results list length different,
@@ -134,7 +166,7 @@ def timer_update():  # TODO might get moved to handle all blenderkit stuff.
     if len(search_threads) == 0 or bpy.context.scene.blenderkitUI.dragging:
         return 1
     for thread in search_threads:  # TODO this doesn't check all processes when one gets removed,
-                                   # but most of the time only one is running anyway
+        # but most of the time only one is running anyway
         if not thread[0].is_alive():
             search_threads.remove(thread)  #
             icons_dir = thread[1]
@@ -220,7 +252,7 @@ def timer_update():  # TODO might get moved to handle all blenderkit stuff.
                                 asset_data['downloaded'] = 0
 
                                 # parse extra params needed for blender here
-                                params = params_to_dict(r['parameters'])
+                                params = utils.params_to_dict(r['parameters'])
 
                                 if asset_type == 'model':
                                     if params.get('boundBoxMinX') != None:
@@ -333,11 +365,11 @@ def split_subs(text, threshold=40):
     lines = []
 
     while len(text) > threshold:
-        #first handle if there's an \n line ending
+        # first handle if there's an \n line ending
         i_rn = text.find('\n')
         if 1 < i_rn < threshold:
             i = i_rn
-            text = text.replace('\n','',1)
+            text = text.replace('\n', '', 1)
         else:
             i = text.rfind(' ', 0, threshold)
             i1 = text.rfind(',', 0, threshold)
@@ -403,17 +435,10 @@ def has(mdata, prop):
         return False
 
 
-def params_to_dict(params):
-    params_dict = {}
-    for p in params:
-        params_dict[p['parameterType']] = p['value']
-    return params_dict
-
-
 def generate_tooltip(mdata):
     col_w = 40
     if type(mdata['parameters']) == list:
-        mparams = params_to_dict(mdata['parameters'])
+        mparams = utils.params_to_dict(mdata['parameters'])
     else:
         mparams = mdata['parameters']
     t = ''
@@ -479,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'):
@@ -500,14 +525,7 @@ def generate_tooltip(mdata):
 
 def get_random_tip(mdata):
     t = ''
-    rtips = ['Click or drag model or material in scene to link/append ',
-             "Click on brushes to link them into scene.",
-             "All materials are free.",
-             "All brushes are free.",
-             "Locked models are available if you subscribe to Full plan.",
-             "Login to upload your own models, materials or brushes.",
-             "Use 'A' key to search assets by same author.",
-             "Use 'W' key to open Authors webpage.", ]
+
     tip = 'Tip: ' + random.choice(rtips)
     t = writeblock(t, tip)
     return t
@@ -623,6 +641,7 @@ def fetch_author(a_id, api_key):
         utils.p(e)
     utils.p('finish fetch')
 
+
 # profile_counter =0
 
 def get_author(r):
@@ -632,7 +651,7 @@ def get_author(r):
     if authors == {}:
         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):
+    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)
         thread.start()
@@ -683,7 +702,6 @@ def fetch_profile(api_key):
         utils.p(e)
 
 
-
 def get_profile():
     preferences = bpy.context.preferences.addons['blenderkit'].preferences
     a = bpy.context.window_manager.get('bkit profile')
@@ -691,11 +709,6 @@ def get_profile():
     thread.start()
     return a
 
-def profile_is_validator():
-    a = bpy.context.window_manager.get('bkit profile')
-    if a is not None and a['user'].get('exmenu'):
-        return True
-    return False
 
 class Searcher(threading.Thread):
     query = None
@@ -712,6 +725,45 @@ class Searcher(threading.Thread):
     def stopped(self):
         return self._stop_event.is_set()
 
+    def query_to_url(self):
+        query = self.query
+        params = self.params
+        # build a new request
+        url = paths.get_api_url() + 'search/'
+
+        # build request manually
+        # TODO use real queries
+        requeststring = '?query='
+        #
+        if query.get('query') not in ('', None):
+            requeststring += query['query'].lower()
+        for i, q in enumerate(query):
+            if q != 'query':
+                requeststring += '+'
+                requeststring += q + ':' + str(query[q]).lower()
+
+        # result ordering: _score - relevance, score - BlenderKit score
+
+        if query.get('query') is None and query.get('category_subtree') == None:
+            # assumes no keywords and no category, thus an empty search that is triggered on start.
+            # orders by last core file upload
+            requeststring += '+order:-last_upload'
+        elif query.get('author_id') is not None and utils.profile_is_validator():
+
+            requeststring += '+order:-created'
+        else:
+            if query.get('category_subtree') is not None:
+                requeststring += '+order:-score,_score'
+            else:
+                requeststring += '+order:_score'
+
+        requeststring += '&addon_version=%s' % params['addon_version']
+        if params.get('scene_uuid') is not None:
+            requeststring += '&scene_uuid=%s' % params['scene_uuid']
+        # print('params', params)
+        urlquery = url + requeststring
+        return urlquery
+
     def run(self):
         maxthreads = 50
         query = self.query
@@ -733,44 +785,23 @@ class Searcher(threading.Thread):
                 try:
                     origdata = json.load(infile)
                     urlquery = origdata['next']
+                    # rparameters = {}
                     if urlquery == None:
                         return;
                 except:
                     # in case no search results found on drive we don't do next page loading.
                     params['get_next'] = False
         if not params['get_next']:
-            # build a new request
             url = paths.get_api_url() + 'search/'
 
-            # build request manually
-            # TODO use real queries
-            requeststring = '?query=' + query['keywords'].lower() + '+'
-            #
-            for i, q in enumerate(query):
-                requeststring += q + ':' + str(query[q]).lower()
-                if i < len(query) - 1:
-                    requeststring += '+'
-
-            # result ordering: _score - relevance, score - BlenderKit score
-            #first condition assumes no keywords and no category, thus an empty search that is triggered on start.
-            if query['keywords'] == '' and query.get('category_subtree') == None:
-                requeststring += '+order:-created'
-            elif query.get('author_id') is not None and profile_is_validator():
-                requeststring += '+order:-created'
-            else:
-                if query.get('category_subtree') is not None:
-                    requeststring += '+order:-score,_score'
-                else:
-                    requeststring += '+order:_score'
-
-            requeststring += '&addon_version=%s' % params['addon_version']
-            if params.get('scene_uuid') is not None:
-                requeststring += '&scene_uuid=%s' % params['scene_uuid']
+            urlquery = url
 
-            urlquery = url + requeststring
+            # rparameters = query
+            urlquery = self.query_to_url()
         try:
             utils.p(urlquery)
-            r = rerequests.get(urlquery, headers=headers)
+            r = rerequests.get(urlquery, headers=headers)  # , params = rparameters)
+            # print(r.url)
             reports = ''
             # utils.p(r.text)
         except requests.exceptions.RequestException as e:
@@ -798,13 +829,13 @@ class Searcher(threading.Thread):
                     if p['parameterType'] == 'mode':
                         mode = p['value']
             if query['asset_type'] != 'brush' or (
-                    query.get('brushType') != None and query['brushType']) == mode:
+                    query.get('mode') != None and query['mode']) == mode:
                 nresults.append(d)
         rdata['results'] = nresults
 
         # print('number of results: ', len(rdata.get('results', [])))
         if self.stopped():
-            utils.p('stopping search : ' + query['keywords'])
+            utils.p('stopping search : ' + str(query))
             return
 
         mt('search finished')
@@ -863,7 +894,7 @@ class Searcher(threading.Thread):
         # TODO do the killing/ stopping here! remember threads might have finished inbetween!
 
         if self.stopped():
-            utils.p('stopping search : ' + query['keywords'])
+            utils.p('stopping search : ' + str(query))
             return
 
         # this loop handles downloading of small thumbnails
@@ -887,7 +918,7 @@ class Searcher(threading.Thread):
                                 # utils.p('fetched thumbnail ', i)
                                 i += 1
         if self.stopped():
-            utils.p('stopping search : ' + query['keywords'])
+            utils.p('stopping search : ' + str(query))
             return
         idx = 0
         while len(thumb_sml_download_threads) > 0:
@@ -899,7 +930,7 @@ class Searcher(threading.Thread):
                     i += 1
 
         if self.stopped():
-            utils.p('stopping search : ' + query['keywords'])
+            utils.p('stopping search : ' + str(query))
             return
 
         # start downloading full thumbs in the end
@@ -914,13 +945,35 @@ class Searcher(threading.Thread):
 
 
 def build_query_common(query, props):
-    query_common = {
-        "keywords": props.search_keywords
-    }
-    query.update(query_common)
+    '''add shared parameters to query'''
+    query_common = {}
+    if props.search_keywords != '':
+        query_common["query"] = props.search_keywords
+
+    if props.search_verification_status != 'ALL':
+        query_common['verification_status'] = props.search_verification_status.lower()
+
+    if props.search_advanced:
+        if props.search_texture_resolution:
+            query["textureResolutionMax_gte"] = props.search_texture_resolution_min
+            query["textureResolutionMax_lte"] = props.search_texture_resolution_max
+
+        elif props.search_procedural == 'TEXTURE_BASED':
+            # todo this procedural hack should be replaced with the parameter
+            query["textureResolutionMax_gte"] = 0
+            # query["procedural"] = False
 
+        if props.search_procedural == "PROCEDURAL":
+            #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)
 
-# def query_add_range(query, name, rmin, rmax):
 
 def build_query_model():
     '''use all search input to request results from server'''
@@ -944,14 +997,11 @@ def build_query_model():
         if props.search_condition != 'UNSPECIFIED':
             query["condition"] = props.search_condition
         if props.search_design_year:
-            query["designYearMin"] = props.search_design_year_min
-            query["designYearMax"] = props.search_design_year_max
+            query["designYear_gte"] = props.search_design_year_min
+            query["designYear_lte"] = props.search_design_year_max
         if props.search_polycount:
-            query["polyCountMin"] = props.search_polycount_min
-            query["polyCountMax"] = props.search_polycount_max
-        if props.search_texture_resolution:
-            query["textureResolutionMin"] = props.search_texture_resolution_min
-            query["textureResolutionMax"] = props.search_texture_resolution_max
+            query["faceCount_gte"] = props.search_polycount_min
+            query["faceCount_lte"] = props.search_polycount_max
 
     build_query_common(query, props)
 
@@ -988,6 +1038,7 @@ def build_query_material():
             query["style"] = props.search_style
         else:
             query["style"] = props.search_style_other
+
     build_query_common(query, props)
 
     return query
@@ -1024,7 +1075,7 @@ def build_query_brush():
     query = {
         "asset_type": 'brush',
 
-        "brushType": brush_type
+        "mode": brush_type
     }
 
     build_query_common(query, props)
@@ -1063,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
 
@@ -1106,6 +1157,12 @@ def search(category='', get_next=False, author_id=''):
     if author_id != '':
         query['author_id'] = author_id
 
+    elif props.own_only:
+        # if user searches for [another] author, 'only my assets' is invalid. that's why in elif.
+        profile = bpy.context.window_manager.get('bkit profile')
+        if profile is not None:
+            query['author_id'] = str(profile['user']['id'])
+
     # utils.p('searching')
     props.is_searching = True
 
@@ -1127,7 +1184,7 @@ def search(category='', get_next=False, author_id=''):
 
 def search_update(self, context):
     utils.p('search updater')
-    #if self.search_keywords != '':
+    # if self.search_keywords != '':
     ui_props = bpy.context.scene.blenderkitUI
     if ui_props.down_up != 'SEARCH':
         ui_props.down_up = 'SEARCH'
diff --git a/blenderkit/thumbnails/flp.png b/blenderkit/thumbnails/flp.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ac3c3d75ad7181f0105f40a4cc9001ae12b8ce8
Binary files /dev/null and b/blenderkit/thumbnails/flp.png differ
diff --git a/blenderkit/thumbnails/fp.png b/blenderkit/thumbnails/fp.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e356ab19d22eac1b05edc2a7f97e3a50c3cf407
Binary files /dev/null and b/blenderkit/thumbnails/fp.png differ
diff --git a/blenderkit/thumbnails/vs_rejected.png b/blenderkit/thumbnails/vs_rejected.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ff663cf546c5c520bb21a659e4cc52251ac57dc
Binary files /dev/null and b/blenderkit/thumbnails/vs_rejected.png differ
diff --git a/blenderkit/ui.py b/blenderkit/ui.py
index fc9563a9d4d997cda6770102e50c7d86743c2b83..3f4d0381a4c142397fbce984457ee6953f902d9d 100644
--- a/blenderkit/ui.py
+++ b/blenderkit/ui.py
@@ -50,6 +50,11 @@ import os
 
 handler_2d = None
 handler_3d = None
+active_area = None
+active_area = None
+active_window = None
+active_region = None
+
 reports = []
 
 mappingdict = {
@@ -67,7 +72,7 @@ verification_icons = {
     'uploading': 'vs_uploading.png',
     'on_hold': 'vs_on_hold.png',
     'validated': None,
-    'rejected': None
+    'rejected': 'vs_rejected.png'
 
 }
 
@@ -133,7 +138,8 @@ class Report():
                     pass;
 
     def draw(self, x, y):
-        ui_bgl.draw_text(self.text, x, y + 8, 16, self.draw_color)
+        if bpy.context.area == active_area:
+            ui_bgl.draw_text(self.text, x, y + 8, 16, self.draw_color)
 
 
 def get_asset_under_mouse(mousex, mousey):
@@ -698,6 +704,7 @@ def draw_callback_2d_upload_preview(self, context):
 
     props = utils.get_upload_props()
     if props != None and ui_props.draw_tooltip:
+
         if ui_props.asset_type != 'BRUSH':
             ui_props.thumbnail_image = props.thumbnail
         else:
@@ -889,8 +896,12 @@ def draw_callback_2d_search(self, context):
                            ui_props.mouse_y - linelength, 2, white)
 
 
+
 def draw_callback_3d(self, context):
     ''' Draw snapped bbox while dragging and in the future other blenderkit related stuff. '''
+    if not utils.guard_from_crash():
+        return;
+
     ui = context.scene.blenderkitUI
 
     if ui.dragging and ui.asset_type == 'MODEL':
@@ -1173,6 +1184,10 @@ def get_largest_3dview():
                     for r in a.regions:
                         if r.type == 'WINDOW':
                             region = r
+    global active_area, active_window, active_region
+    active_window = maxw
+    active_area = maxa
+    active_region = region
     return maxw, maxa, region
 
 
@@ -1417,6 +1432,8 @@ class AssetBarOperator(bpy.types.Operator):
                     ui_props.draw_tooltip = True
 
                     ui_props.tooltip = asset_data['tooltip']
+                    # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
+
                 else:
                     ui_props.draw_tooltip = False
 
@@ -1476,7 +1493,7 @@ class AssetBarOperator(bpy.types.Operator):
                     if not asset_data['can_download']:
                         message = 'Asset locked. Find out how to unlock Everything and ...'
                         link_text = 'support all BlenderKit artists.'
-                        url = paths.get_bkit_url() + '/get-blenderkit/' + asset_data['id']
+                        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,
                                                          link_text=link_text)
                         return {'RUNNING_MODAL'}
@@ -1696,30 +1713,38 @@ class AssetBarOperator(bpy.types.Operator):
         if sr is None:
             bpy.context.scene['search results'] = []
 
-        if context.area.type == 'VIEW_3D':
-            # the arguments we pass the the callback
-            args = (self, context)
-            self.window = context.window
-            self.area = context.area
-            self.scene = bpy.context.scene
-            self.has_quad_views = len(bpy.context.area.spaces[0].region_quadviews) > 0
+        if context.area.type != 'VIEW_3D':
+            self.report({'WARNING'}, "View3D not found, cannot run operator")
+            return {'CANCELLED'}
 
-            for r in self.area.regions:
-                if r.type == 'WINDOW':
-                    self.region = r
+        # the arguments we pass the the callback
+        args = (self, context)
 
-            update_ui_size(self.area, self.region)
+        self.window = context.window
+        self.area = context.area
+        self.scene = bpy.context.scene
 
-            self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')
-            self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW')
 
-            context.window_manager.modal_handler_add(self)
-            ui_props.assetbar_on = True
-            return {'RUNNING_MODAL'}
-        else:
 
-            self.report({'WARNING'}, "View3D not found, cannot run operator")
-            return {'CANCELLED'}
+        self.has_quad_views = len(bpy.context.area.spaces[0].region_quadviews) > 0
+
+        for r in self.area.regions:
+            if r.type == 'WINDOW':
+                self.region = r
+
+        global active_window, active_area, active_region
+        active_window = self.window
+        active_area = self.area
+        active_region = self.region
+
+        update_ui_size(self.area, self.region)
+
+        self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')
+        self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW')
+
+        context.window_manager.modal_handler_add(self)
+        ui_props.assetbar_on = True
+        return {'RUNNING_MODAL'}
 
     def execute(self, context):
         return {'RUNNING_MODAL'}
@@ -1743,6 +1768,27 @@ class TransferBlenderkitData(bpy.types.Operator):
         return {'FINISHED'}
 
 
+class UndoWithContext(bpy.types.Operator):
+    """Regenerate cobweb"""
+    bl_idname = "wm.undo_push_context"
+    bl_label = "BlnenderKit undo push"
+    bl_description = "BlenderKit undo push with fixed context"
+    bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
+
+    # def modal(self, context, event):
+    #     return {'RUNNING_MODAL'}
+
+    def execute(self, context):
+        C_dict = bpy.context.copy()
+        C_dict.update(region='WINDOW')
+        if context.area is None or context.area.type != 'VIEW_3D':
+            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')
+        return {'FINISHED'}
+
+
 class RunAssetBarWithContext(bpy.types.Operator):
     """Regenerate cobweb"""
     bl_idname = "object.run_assetbar_fix_context"
@@ -1761,18 +1807,28 @@ class RunAssetBarWithContext(bpy.types.Operator):
             override = {'window': w, 'screen': w.screen, 'area': a, 'region': r}
             C_dict.update(override)
         bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=True, do_search=False)
-        return {'RUNNING_MODAL'}
+        return {'FINISHED'}
 
 
 classess = (
     AssetBarOperator,
     RunAssetBarWithContext,
-    TransferBlenderkitData
+    TransferBlenderkitData,
+    UndoWithContext
 )
 
 # store keymap items here to access after registration
 addon_keymapitems = []
 
+#@persistent
+def pre_load(context):
+    ui_props = bpy.context.scene.blenderkitUI
+    ui_props.assetbar_on = False
+    ui_props.turn_off = True
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    preferences.login_attempt = False
+
+
 
 def register_ui():
     global handler_2d, handler_3d
@@ -1803,6 +1859,7 @@ def register_ui():
 
 def unregister_ui():
     global handler_2d, handler_3d
+    pre_load(bpy.context)
 
     bpy.types.SpaceView3D.draw_handler_remove(handler_2d, 'WINDOW')
     bpy.types.SpaceView3D.draw_handler_remove(handler_3d, 'WINDOW')
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
index b981fbccd905c56c0094b041e18419c3569480f2..8bd9fd58cefb3d635df151007c0d1c90d9b2ec83 100644
--- a/blenderkit/ui_panels.py
+++ b/blenderkit/ui_panels.py
@@ -24,8 +24,9 @@ if "bpy" in locals():
     utils = importlib.reload(utils)
     download = importlib.reload(download)
     categories = importlib.reload(categories)
+    icons = importlib.reload(icons)
 else:
-    from blenderkit import paths, ratings, utils, download, categories
+    from blenderkit import paths, ratings, utils, download, categories, icons
 
 from bpy.types import (
     Panel
@@ -43,7 +44,7 @@ def label_multiline(layout, text='', icon='NONE', width=-1):
         threshold = int(width / 5.5)
     else:
         threshold = 35
-    maxlines = 6
+    maxlines = 8
     li = 0
     for l in lines:
         while len(l) > threshold:
@@ -53,7 +54,7 @@ def label_multiline(layout, text='', icon='NONE', width=-1):
             l1 = l[:i]
             layout.label(text=l1, icon=icon)
             icon = 'NONE'
-            l = l[i:]
+            l = l[i:].lstrip()
             li += 1
             if li > maxlines:
                 break;
@@ -75,10 +76,10 @@ def draw_ratings(layout, context):
     layout.prop(bkit_ratings, 'rating_work_hours')
     w = context.region.width
 
-    layout.label(text='problems')
-    layout.prop(bkit_ratings, 'rating_problems', text='')
-    layout.label(text='compliments')
-    layout.prop(bkit_ratings, 'rating_compliments', text='')
+    # layout.label(text='problems')
+    # layout.prop(bkit_ratings, 'rating_problems', text='')
+    # 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')
@@ -151,7 +152,7 @@ def prop_needed(layout, props, name, value, is_not_filled=''):
         # row.label(text='', icon = 'ERROR')
         icon = 'ERROR'
         row.alert = True
-        row.prop(props, name)#, icon=icon)
+        row.prop(props, name)  # , icon=icon)
         row.alert = False
     else:
         # row.label(text='', icon = 'FILE_TICK')
@@ -264,6 +265,7 @@ def draw_panel_scene_upload(self, context):
     row.prop(props, 'work_hours')
     layout.prop(props, 'adult')
 
+
 def draw_assetbar_show_hide(layout, props):
     s = bpy.context.scene
     ui_props = s.blenderkitUI
@@ -299,7 +301,9 @@ def draw_panel_model_search(self, context):
         layout.operator("wm.url_open", text="Get Full plan", icon='URL').url = paths.BLENDERKIT_PLANS
 
     layout.prop(props, "search_style")
+    layout.prop(props, "own_only")
     layout.prop(props, "free_only")
+
     # if props.search_style == 'OTHER':
     #     layout.prop(props, "search_style_other")
     # layout.prop(props, "search_engine")
@@ -307,7 +311,7 @@ def draw_panel_model_search(self, context):
     # layout.prop(props, 'append_link', expand=True, icon_only=False)
     # layout.prop(props, 'import_as', expand=True, icon_only=False)
 
-    # layout.prop(props, "search_advanced")
+    layout.prop(props, "search_advanced")
     if props.search_advanced:
         layout.separator()
 
@@ -318,34 +322,39 @@ def draw_panel_model_search(self, context):
         #     layout.prop(props, "search_engine_keyword")
 
         # AGE
-        layout.prop(props, "search_condition")  # , text ='condition of object new/old e.t.c.')
+        layout.prop(props, "search_condition", text='Condition')  # , text ='condition of object new/old e.t.c.')
 
         # DESIGN YEAR
         layout.prop(props, "search_design_year", text='designed in ( min - max )')
-        row = layout.row(align=True)
-        if not props.search_design_year_min:
-            row.active = False
-        row.prop(props, "search_design_year_min", text='min')
-        row.prop(props, "search_design_year_max", text='max')
+        if props.search_design_year:
+            row = layout.row(align=True)
+            row.prop(props, "search_design_year_min", text='min')
+            row.prop(props, "search_design_year_max", text='max')
 
         # POLYCOUNT
-        layout.prop(props, "search_polycount", text='polycount in ( min - max )')
-        row = layout.row(align=True)
-        if not props.search_polycount:
-            row.active = False
-        row.prop(props, "search_polycount_min", text='min')
-        row.prop(props, "search_polycount_max", text='max')
+        layout.prop(props, "search_polycount", text='Poly count in ( min - max )')
+        if props.search_polycount:
+            row = layout.row(align=True)
+            row.prop(props, "search_polycount_min", text='min')
+            row.prop(props, "search_polycount_max", text='max')
 
         # TEXTURE RESOLUTION
         layout.prop(props, "search_texture_resolution", text='texture resolution ( min - max )')
-        row = layout.row(align=True)
-        if not props.search_texture_resolution:
-            row.active = False
-        row.prop(props, "search_texture_resolution_min", text='min')
-        row.prop(props, "search_texture_resolution_max", text='max')
-
+        if props.search_texture_resolution:
+            row = layout.row(align=True)
+            row.prop(props, "search_texture_resolution_min", text='min')
+            row.prop(props, "search_texture_resolution_max", text='max')
+
+        # FILE SIZE
+        layout.prop(props, "search_file_size", text='File size ( min - max )')
+        if props.search_file_size:
+            row = layout.row(align=True)
+            row.prop(props, "search_file_size_min", text='min')
+            row.prop(props, "search_file_size_max", text='max')
+
+        # layout.prop(props, "search_procedural", expand=True)
         # ADULT
-        layout.prop(props, "search_adult")  # , text ='condition of object new/old e.t.c.')
+        # layout.prop(props, "search_adult")  # , text ='condition of object new/old e.t.c.')
 
     draw_panel_categories(self, context)
 
@@ -366,7 +375,7 @@ def draw_panel_scene_search(self, context):
     row = layout.row()
     row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
     draw_assetbar_show_hide(row, props)
-
+    layout.prop(props, "own_only")
     label_multiline(layout, text=props.report)
 
     # layout.prop(props, "search_style")
@@ -401,6 +410,8 @@ class VIEW3D_PT_blenderkit_model_properties(Panel):
             if o.instance_type == 'COLLECTION' and o.instance_collection is not None:
                 layout.operator('object.blenderkit_bring_to_scene', text='Bring to scene')
 
+            draw_panel_model_rating(self, context)
+
             # if 'rig' in ad['tags']:
             #     # layout.label(text = 'can make proxy')
             #     layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
@@ -424,7 +435,7 @@ class VIEW3D_PT_blenderkit_profile(Panel):
     bl_idname = "VIEW3D_PT_blenderkit_profile"
     bl_space_type = 'VIEW_3D'
     bl_region_type = 'UI'
-    bl_label = "Profile"
+    bl_label = "BlenderKit Profile"
 
     @classmethod
     def poll(cls, context):
@@ -440,26 +451,59 @@ class VIEW3D_PT_blenderkit_profile(Panel):
             draw_login_progress(layout)
             return
 
-        if user_preferences.enable_oauth:
-            draw_login_buttons(layout)
-
         if user_preferences.api_key != '':
             me = bpy.context.window_manager.get('bkit profile')
             if me is not None:
                 me = me['user']
-                layout.label(text='User: %s %s' % (me['firstName'], me['lastName']))
-                layout.label(text='Email: %s' % (me['email']))
-                if me.get('sumAssetFilesSize') is not None:  # TODO remove this when production server has these too.
-                    layout.label(text='Public assets: %i MiB' % (me['sumAssetFilesSize']))
-                if me.get('sumPrivateAssetFilesSize') is not None:
-                    layout.label(text='Private assets: %i MiB' % (me['sumPrivateAssetFilesSize']))
+                # user name
+                layout.label(text='Me: %s %s' % (me['firstName'], me['lastName']))
+                # layout.label(text='Email: %s' % (me['email']))
+
+                # plan information
+
+                # pcoll = icons.icon_collections["main"]
+                # my_icon = pcoll['free']
+                # row = layout.row()
+                # row.label(text='My plan:')
+                # row.label(text='Free plan', icon_value=my_icon.icon_id)
+                # layout.operator("wm.url_open", text="Change plan",
+                #                 icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_PLANS
+
+                # storage statistics
+                # if me.get('sumAssetFilesSize') is not None:  # TODO remove this when production server has these too.
+                #     layout.label(text='My public assets: %i MiB' % (me['sumAssetFilesSize']))
+                # if me.get('sumPrivateAssetFilesSize') is not None:
+                #     layout.label(text='My private assets: %i MiB' % (me['sumPrivateAssetFilesSize']))
                 if me.get('remainingPrivateQuota') is not None:
-                    layout.label(text='Remaining private storage: %i MiB' % (me['remainingPrivateQuota']))
+                    layout.label(text='My free storage: %i MiB' % (me['remainingPrivateQuota']))
 
             layout.operator("wm.url_open", text="See my uploads",
                             icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_USER_ASSETS
 
 
+class VIEW3D_PT_blenderkit_login(Panel):
+    bl_category = "BlenderKit"
+    bl_idname = "VIEW3D_PT_blenderkit_login"
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'UI'
+    bl_label = "BlenderKit Login"
+
+    @classmethod
+    def poll(cls, context):
+        return True
+
+    def draw(self, context):
+        layout = self.layout
+        user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+
+        if user_preferences.login_attempt:
+            draw_login_progress(layout)
+            return
+
+        if user_preferences.enable_oauth:
+            draw_login_buttons(layout)
+
+
 def draw_panel_model_rating(self, context):
     o = bpy.context.active_object
     op = draw_ratings(self.layout, context)  # , props)
@@ -526,7 +570,7 @@ def draw_panel_material_search(self, context):
     row = layout.row()
     row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
     draw_assetbar_show_hide(row, props)
-
+    layout.prop(props, "own_only")
     label_multiline(layout, text=props.report)
 
     # layout.prop(props, 'search_style')
@@ -536,10 +580,33 @@ def draw_panel_material_search(self, context):
     # if props.search_engine == 'OTHER':
     #     layout.prop(props, 'search_engine_other')
 
-    layout.prop(props, 'automap')
+    layout.prop(props, "search_advanced")
+    if props.search_advanced:
+        layout.separator()
+
+        layout.label(text = 'texture types')
+        col = layout.column()
+        col.prop(props, "search_procedural", expand=True)
+
+        if props.search_procedural == 'TEXTURE_BASED':
+            # TEXTURE RESOLUTION
+            layout.prop(props, "search_texture_resolution", text='texture resolution ( min - max )')
+            if props.search_texture_resolution:
+                row = layout.row(align=True)
+                row.prop(props, "search_texture_resolution_min", text='min')
+                row.prop(props, "search_texture_resolution_max", text='max')
+
+        # FILE SIZE
+        layout.prop(props, "search_file_size", text='File size ( min - max in mb)')
+        if props.search_file_size:
+            row = layout.row(align=True)
+            row.prop(props, "search_file_size_min", text='min')
+            row.prop(props, "search_file_size_max", text='max')
 
     draw_panel_categories(self, context)
 
+    layout.prop(props, 'automap')
+
 
 def draw_panel_material_ratings(self, context):
     op = draw_ratings(self.layout, context)  # , props)
@@ -568,7 +635,7 @@ def draw_panel_brush_search(self, context):
     row = layout.row()
     row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
     draw_assetbar_show_hide(row, props)
-
+    layout.prop(props, "own_only")
 
     label_multiline(layout, text=props.report)
     draw_panel_categories(self, context)
@@ -605,7 +672,7 @@ class VIEW3D_PT_blenderkit_unified(Panel):
     bl_idname = "VIEW3D_PT_blenderkit_unified"
     bl_space_type = 'VIEW_3D'
     bl_region_type = 'UI'
-    bl_label = "BlenderKit"
+    bl_label = "Find and Upload Assets"
 
     @classmethod
     def poll(cls, context):
@@ -622,19 +689,19 @@ class VIEW3D_PT_blenderkit_unified(Panel):
         # layout.prop_tabs_enum(ui_props, "asset_type", icon_only = True)
 
         row = layout.row()
-        #row.scale_x = 1.6
-        #row.scale_y = 1.6
+        # row.scale_x = 1.6
+        # row.scale_y = 1.6
         #
         row.prop(ui_props, 'down_up', expand=True, icon_only=False)
         # row.label(text='')
-        #row = row.split().row()
-        #layout.alert = True
-        #layout.alignment = 'CENTER'
-        #row = layout.row(align = True)
-        #split = row.split(factor=.5)
-        #row.prop(ui_props, 'asset_type', expand=True, icon_only=True)
-        #row = layout.column(align = False)
-        layout.prop(ui_props, 'asset_type', expand=False, text = '')
+        # row = row.split().row()
+        # layout.alert = True
+        # layout.alignment = 'CENTER'
+        # row = layout.row(align = True)
+        # split = row.split(factor=.5)
+        # row.prop(ui_props, 'asset_type', expand=True, icon_only=True)
+        # row = layout.column(align = False)
+        layout.prop(ui_props, 'asset_type', expand=False, text='')
 
         w = context.region.width
         if user_preferences.login_attempt:
@@ -651,14 +718,16 @@ class VIEW3D_PT_blenderkit_unified(Panel):
                 layout.label(text='Paste your API Key:')
                 layout.prop(user_preferences, 'api_key', text='')
             layout.separator()
-        if bpy.data.filepath == '':
-            layout.alert = True
-            label_multiline(layout, text="It's better to save your file first.", width=w)
-            layout.alert = False
-            layout.separator()
+        # if bpy.data.filepath == '':
+        #     layout.alert = True
+        #     label_multiline(layout, text="It's better to save your file first.", width=w)
+        #     layout.alert = False
+        #     layout.separator()
 
         if ui_props.down_up == 'SEARCH':
-
+            if utils.profile_is_validator():
+                search_props = utils.get_search_props()
+                layout.prop(search_props, 'search_verification_status')
             if ui_props.asset_type == 'MODEL':
                 # noinspection PyCallByClass
                 draw_panel_model_search(self, context)
@@ -744,7 +813,6 @@ class VIEW3D_PT_blenderkit_unified(Panel):
                 layout.label(text='not yet implemented')
 
 
-
 class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
     bl_label = "Asset options:"
     bl_idname = "OBJECT_MT_blenderkit_asset_menu"
@@ -788,9 +856,13 @@ class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
         wm = bpy.context.window_manager
         profile = wm.get('bkit profile')
         if profile is not None:
-            # validation by admin
-            if profile['user']['exmenu']:
+            # validation
+            if utils.profile_is_validator():
                 layout.label(text='Validation tools:')
+                if asset_data['verificationStatus'] != 'uploaded':
+                    op = layout.operator('object.blenderkit_change_status', text='set Uploaded')
+                    op.asset_id = asset_data['id']
+                    op.state = 'uploaded'
                 if asset_data['verificationStatus'] != 'validated':
                     op = layout.operator('object.blenderkit_change_status', text='Validate')
                     op.asset_id = asset_data['id']
@@ -881,7 +953,7 @@ class UrlPopupDialog(bpy.types.Operator):
         op.url = self.url
 
     def execute(self, context):
-        #start_thumbnailer(self, context)
+        # start_thumbnailer(self, context)
         return {'FINISHED'}
 
     def invoke(self, context, event):
@@ -976,20 +1048,28 @@ class VIEW3D_PT_blenderkit_downloads(Panel):
 
 def header_search_draw(self, context):
     '''Top bar menu in 3d view'''
-    layout = self.layout
-    s = bpy.context.scene
-    ui_props = s.blenderkitUI
-    if ui_props.asset_type == 'MODEL':
-        props = s.blenderkit_models
-    if ui_props.asset_type == 'MATERIAL':
-        props = s.blenderkit_mat
-    if ui_props.asset_type == 'BRUSH':
-        props = s.blenderkit_brush
 
-    layout.separator_spacer()
-    layout.prop(ui_props, "asset_type", text='', icon='URL')
-    layout.prop(props, "search_keywords", text="", icon='VIEWZOOM')
-    draw_assetbar_show_hide(layout, props)
+    if not utils.guard_from_crash():
+        return;
+
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    if preferences.search_in_header:
+        layout = self.layout
+        s = bpy.context.scene
+        ui_props = s.blenderkitUI
+        if ui_props.asset_type == 'MODEL':
+            props = s.blenderkit_models
+        if ui_props.asset_type == 'MATERIAL':
+            props = s.blenderkit_mat
+        if ui_props.asset_type == 'BRUSH':
+            props = s.blenderkit_brush
+
+        # 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(props, "search_keywords", text="", icon='VIEWZOOM')
+        draw_assetbar_show_hide(layout, props)
 
 
 # We can store multiple preview collections here,
@@ -997,11 +1077,11 @@ def header_search_draw(self, context):
 preview_collections = {}
 classess = (
     SetCategoryOperator,
-
+    VIEW3D_PT_blenderkit_profile,
+    VIEW3D_PT_blenderkit_login,
     VIEW3D_PT_blenderkit_unified,
     VIEW3D_PT_blenderkit_model_properties,
     VIEW3D_PT_blenderkit_downloads,
-    VIEW3D_PT_blenderkit_profile,
     OBJECT_MT_blenderkit_asset_menu,
     UrlPopupDialog
 )
@@ -1015,4 +1095,6 @@ def register_ui_panels():
 
 def unregister_ui_panels():
     for c in classess:
+        print('unregister', c)
         bpy.utils.unregister_class(c)
+    bpy.types.VIEW3D_MT_editor_menus.remove(header_search_draw)
diff --git a/blenderkit/upload.py b/blenderkit/upload.py
index b1c4b25c812c9f59843f8bc029dfd87873d48343..f2f295e5998b810167acc3e41d583ec1c832eaef 100644
--- a/blenderkit/upload.py
+++ b/blenderkit/upload.py
@@ -81,28 +81,6 @@ def add_version(data):
     data["addonVersion"] = addon_version
 
 
-def params_to_dict(inputs, parameters=None):
-    if parameters == None:
-        parameters = []
-    for k in inputs.keys():
-        if type(inputs[k]) == list:
-            strlist = ""
-            for idx, s in enumerate(inputs[k]):
-                strlist += s
-                if idx < len(inputs[k]) - 1:
-                    strlist += ','
-
-            value = "%s" % strlist
-        elif type(inputs[k]) != bool:
-            value = inputs[k]
-        else:
-            value = str(inputs[k])
-        parameters.append(
-            {
-                "parameterType": k,
-                "value": value
-            })
-    return parameters
 
 
 def write_to_report(props, text):
@@ -254,6 +232,10 @@ def get_upload_data(self, context, asset_type):
             "manifold": props.manifold,
             "objectCount": props.object_count,
 
+            "procedural": props.is_procedural,
+            "nodeCount": props.node_count,
+            "textureCount": props.texture_count,
+            "megapixels": round(props.total_megapixels/ 1000000),
             # "scene": props.is_scene,
         }
         if props.use_design_year:
@@ -381,6 +363,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),
 
         }
 
@@ -570,7 +553,7 @@ def start_upload(self, context, asset_type, reupload, upload_set):
     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'] = params_to_dict(
+    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
@@ -783,7 +766,10 @@ class UploadOperator(Operator):
 
         if props.is_private == 'PUBLIC':
             ui_panels.label_multiline(layout, text='public assets are validated several hours'
-                                                   ' or days after upload. ', 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()
diff --git a/blenderkit/upload_bg.py b/blenderkit/upload_bg.py
index b2db44dad4f94071a537e30e942f3cba84a8b756..236793c08a03f984b67bf6ed7d2897bf6e368118 100644
--- a/blenderkit/upload_bg.py
+++ b/blenderkit/upload_bg.py
@@ -87,8 +87,8 @@ def upload_file(upload_data, f):
     upload_create_url = paths.get_api_url() + 'uploads/'
     upload = rerequests.post(upload_create_url, json=upload_info, headers=headers, verify=True)
     upload = upload.json()
-
-    chunk_size = 1024 * 256
+    #
+    chunk_size = 1024 * 1024 * 2
     utils.pprint(upload)
     # file gets uploaded here:
     uploaded = False
@@ -103,8 +103,10 @@ def upload_file(upload_data, f):
                 if upload_response.status_code == 200:
                     uploaded = True
                 else:
+                    print(upload_response.text)
                     bg_blender.progress(f'Upload failed, retry. {a}')
             except Exception as e:
+                print(e)
                 bg_blender.progress('Upload %s failed, retrying' % f['type'])
                 time.sleep(1)
 
diff --git a/blenderkit/utils.py b/blenderkit/utils.py
index c1a59cd8b9488a61c3eb3d06b0acf9a960e785fd..706600ee9b72e2659dfe27cbb074e37683b0871d 100644
--- a/blenderkit/utils.py
+++ b/blenderkit/utils.py
@@ -514,4 +514,48 @@ def name_update():
     fname = fname.replace('\'', '')
     fname = fname.replace('\"', '')
     asset = get_active_asset()
-    asset.name = fname
\ No newline at end of file
+    asset.name = fname
+
+def params_to_dict(params):
+    params_dict = {}
+    for p in params:
+        params_dict[p['parameterType']] = p['value']
+    return params_dict
+
+def dict_to_params(inputs, parameters=None):
+    if parameters == None:
+        parameters = []
+    for k in inputs.keys():
+        if type(inputs[k]) == list:
+            strlist = ""
+            for idx, s in enumerate(inputs[k]):
+                strlist += s
+                if idx < len(inputs[k]) - 1:
+                    strlist += ','
+
+            value = "%s" % strlist
+        elif type(inputs[k]) != bool:
+            value = inputs[k]
+        else:
+            value = str(inputs[k])
+        parameters.append(
+            {
+                "parameterType": k,
+                "value": value
+            })
+    return parameters
+
+
+def profile_is_validator():
+    a = bpy.context.window_manager.get('bkit profile')
+    if a is not None and a['user'].get('exmenu'):
+        return True
+    return False
+
+def guard_from_crash():
+    '''Blender tends to crash when trying to run some functions with the addon going through unregistration process.'''
+    if bpy.context.preferences.addons['blenderkit'] is None:
+        return False;
+    if bpy.context.preferences.addons['blenderkit'].preferences is None:
+        return False;
+    return True
\ No newline at end of file