BlenderKit: convert all imports to importlib

+ new Oauth script version (finished in next commit)
+split login/signup buttons
This commit is contained in:
Vilem Duha 2019-05-29 15:01:01 +02:00
parent 88d0e75f1f
commit 85fd48c32c
22 changed files with 281 additions and 137 deletions

View File

@ -30,19 +30,23 @@ bl_info = {
}
if "bpy" in locals():
import importlib
from importlib import reload
importlib.reload(asset_inspector)
importlib.reload(search)
importlib.reload(download)
importlib.reload(ratings)
importlib.reload(autothumb)
importlib.reload(ui)
importlib.reload(bg_blender)
importlib.reload(paths)
importlib.reload(utils)
importlib.reload(bkit_oauth)
importlib.reload(tasks_queue)
asset_inspector = reload(asset_inspector)
search = reload(search)
download = reload(download)
upload = reload(upload)
ratings = reload(ratings)
autothumb = reload(autothumb)
ui = reload(ui)
bg_blender = reload(bg_blender)
paths = reload(paths)
utils = reload(utils)
overrides = reload(overrides)
ui_panels = reload(ui_panels)
categories = reload(categories)
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, \
overrides, ui_panels, categories, bkit_oauth, tasks_queue
@ -73,6 +77,7 @@ from bpy.types import (
PropertyGroup,
)
# logging.basicConfig(filename = 'blenderkit.log', level = logging.INFO,
# format = ' %(asctime)s:%(filename)s:%(funcName)s:%(lineno)d:%(message)s')
@ -86,6 +91,7 @@ def scene_load(context):
preferences = bpy.context.preferences.addons['blenderkit'].preferences
preferences.login_attempt = False
licenses = (
('royalty_free', 'Royalty Free', 'royalty free commercial license'),
('cc_zero', 'Creative Commons Zero', 'Creative Commons Zero'),
@ -457,7 +463,7 @@ class BlenderKitCommonUploadProps(object):
('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.",
"Private assets are limited by quota.",
default="PUBLIC",
)
@ -1076,7 +1082,7 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps):
)
free_only: BoolProperty(name="Free only", description="Show only free models",
default=False)
default=False)
search_advanced: BoolProperty(name="Advanced Search Options", description="use advanced search properties",
default=False)
@ -1237,7 +1243,7 @@ class BlenderKitAddonPreferences(AddonPreferences):
default_global_dict = paths.default_global_dict()
enable_oauth = False
enable_oauth = True
api_key: StringProperty(
name="BlenderKit API Key",
@ -1320,12 +1326,11 @@ class BlenderKitAddonPreferences(AddonPreferences):
min=0,
max=20)
asset_counter: IntProperty(name="Usage Counter",
description="Counts usages so it asks for registration only after reaching a limit",
default=0,
min=0,
max=20000)
asset_counter: IntProperty(name="Usage Counter",
description="Counts usages so it asks for registration only after reaching a limit",
default=0,
min=0,
max=20000)
# allow_proximity : BoolProperty(
# name="allow proximity data reports",
@ -1340,8 +1345,7 @@ class BlenderKitAddonPreferences(AddonPreferences):
if self.api_key.strip() == '':
if self.enable_oauth:
layout.operator("wm.blenderkit_login", text="Login/ Sign up",
icon='URL')
ui_panels.draw_login_buttons(layout)
else:
op = layout.operator("wm.url_open", text="Register online and get your API Key",
icon='QUESTION')
@ -1438,8 +1442,8 @@ def register():
bpy.app.handlers.load_post.append(scene_load)
def unregister():
def unregister():
ui.unregister_ui()
search.unregister_search()
asset_inspector.unregister_asset_inspector()

View File

@ -16,9 +16,16 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
else:
from blenderkit import utils
import bpy
import os, uuid
from blenderkit import utils
import uuid
def append_brush(file_name, brushname=None, link=False, fake_user=True):

View File

@ -16,18 +16,16 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
imp.reload(utils)
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
else:
from blenderkit import utils
import bpy, bmesh
import mathutils
import object_print3d_utils
import bpy
from object_print3d_utils import operators as ops
from mathutils import Vector
RENDER_OBTYPES = ['MESH', 'CURVE', 'SURFACE', 'METABALL', 'TEXT']
@ -94,7 +92,6 @@ def check_render_engine(props, obs):
props.engine = 'CYCLES'
for mname in materials:
m = bpy.data.materials[mname]
if m is not None and m.node_tree is not None:
@ -315,7 +312,7 @@ def check_modifiers(props, obs):
def get_autotags():
""" call all analysis functions """
ui = bpy.context.scene.blenderkitUI
if ui.asset_type =='MODEL':
if ui.asset_type == 'MODEL':
ob = utils.get_active_model()
obs = utils.get_hierarchy(ob)
props = ob.blenderkit
@ -339,7 +336,7 @@ def get_autotags():
check_meshprops(props, obs)
check_modifiers(props, obs)
countObs(props, obs)
elif ui.asset_type =='MATERIAL':
elif ui.asset_type == 'MATERIAL':
# reset some properties here, because they might not get re-filled at all when they aren't needed anymore.
mat = utils.get_active_asset()

View File

@ -16,17 +16,13 @@
#
# ##### END GPL LICENSE BLOCK #####
import importlib
if "bpy" in locals():
from importlib import reload
if "paths" in locals():
reload(paths)
if "utils" in locals():
reload(utils)
if "bg_blender" in locals():
reload(bg_blender)
paths = reload(paths)
utils = reload(utils)
bg_blender = reload(bg_blender)
else:
from blenderkit import paths, utils, bg_blender

View File

@ -16,10 +16,19 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
append_link = reload(append_link)
bg_blender = reload(bg_blender)
else:
from blenderkit import utils, append_link, bg_blender
import sys, json, math
import bpy
from pathlib import Path
from blenderkit import utils, append_link, bg_blender
BLENDERKIT_EXPORT_TEMP_DIR = sys.argv[-1]
BLENDERKIT_THUMBNAIL_PATH = sys.argv[-2]

View File

@ -17,10 +17,18 @@
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
append_link = reload(append_link)
bg_blender = reload(bg_blender)
else:
from blenderkit import utils, append_link, bg_blender
import sys, json, math
from pathlib import Path
import bpy
from blenderkit import utils, append_link, bg_blender
import mathutils
BLENDERKIT_EXPORT_TEMP_DIR = sys.argv[-1]

View File

@ -16,6 +16,12 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
else:
from blenderkit import utils
import bpy
import sys, threading, os
@ -25,8 +31,6 @@ from bpy.props import (
EnumProperty,
)
from blenderkit import utils
bg_processes = []
@ -112,19 +116,19 @@ def bg_update():
# readthread.
if tcom.error:
estring = tcom.eval_path_computing + ' = False'
exec (estring)
exec(estring)
tcom.lasttext = tcom.outtext
if tcom.outtext != '':
tcom.outtext = ''
estring = tcom.eval_path_state + ' = tcom.lasttext'
exec (estring)
exec(estring)
# print(tcom.lasttext)
if 'finished successfully' in tcom.lasttext:
bg_processes.remove(p)
estring = tcom.eval_path_computing + ' = False'
exec (estring)
exec(estring)
else:
readthread = threading.Thread(target=threadread, args=([tcom]), daemon=True)
readthread.start()
@ -205,7 +209,7 @@ class KillBgProcess(bpy.types.Operator):
kill = True
if kill:
estring = tcom.eval_path_computing + ' = False'
exec (estring)
exec(estring)
processes.remove(p)
tcom.proc.kill()

View File

@ -16,25 +16,46 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
tasks_queue = reload(tasks_queue)
utils = reload(utils)
paths = reload(paths)
search = reload(search)
categories = reload(categories)
oauth = reload(oauth)
else:
from blenderkit import tasks_queue, utils, paths, search, categories, oauth
import bpy
import threading
import blenderkit
from blenderkit import tasks_queue, utils, paths, search, categories, oauth
from bpy.props import (
BoolProperty,
)
CLIENT_ID = "IdFRwa3SGA8eMpzhRVFMg5Ts8sPK93xBjif93x0F"
PORTS = [62485, 1234]
def login_thread():
thread = threading.Thread(target=login, args=([]), daemon=True)
def login_thread(signup=False):
thread = threading.Thread(target=login, args=([signup]), daemon=True)
thread.start()
def login():
authenticator = oauth.SimpleOAuthAuthenticator(server_url=paths.get_bkit_url(), client_id=CLIENT_ID, ports=PORTS)
def login(signup):
if signup:
r_url = paths.BLENDERKIT_SIGNUP_URL
else:
r_url = paths.BLENDERKIT_LOGIN_URL
authenticator = oauth.SimpleOAuthAuthenticator(server_url=paths.get_bkit_url(), client_id=CLIENT_ID, ports=PORTS,
redirect_url=r_url)
auth_token, refresh_token = authenticator.get_new_token()
utils.p('tokens retrieved')
tasks_queue.add_task((write_tokens , (auth_token, refresh_token)))
tasks_queue.add_task((write_tokens, (auth_token, refresh_token)))
def refresh_token_thread():
@ -45,10 +66,11 @@ def refresh_token_thread():
def refresh_token(api_key_refresh):
authenticator = oauth.SimpleOAuthAuthenticator(server_url=paths.get_bkit_url(), client_id=CLIENT_ID, ports=PORTS)
authenticator = oauth.SimpleOAuthAuthenticator(server_url=paths.get_bkit_url(), client_id=CLIENT_ID, ports=PORTS,
redirect_url='')
auth_token, refresh_token = authenticator.get_refreshed_token(api_key_refresh)
if auth_token is not None and refresh_token is not None:
tasks_queue.add_task((blenderkit.bkit_oauth.write_tokens , (auth_token, refresh_token)))
tasks_queue.add_task((blenderkit.bkit_oauth.write_tokens, (auth_token, refresh_token)))
def write_tokens(auth_token, refresh_token):
@ -70,6 +92,13 @@ class RegisterLoginOnline(bpy.types.Operator):
bl_label = "BlenderKit login or signup"
bl_options = {'REGISTER', 'UNDO'}
signup: BoolProperty(
name="create a new account",
description="True for register, otherwise login",
default=False,
options={'SKIP_SAVE'}
)
@classmethod
def poll(cls, context):
return True
@ -77,7 +106,7 @@ class RegisterLoginOnline(bpy.types.Operator):
def execute(self, context):
preferences = bpy.context.preferences.addons['blenderkit'].preferences
preferences.login_attempt = True
login_thread()
login_thread(self.signup)
return {'FINISHED'}
@ -116,6 +145,7 @@ class CancelLoginOnline(bpy.types.Operator):
preferences.login_attempt = False
return {'FINISHED'}
classess = (
RegisterLoginOnline,
CancelLoginOnline,

View File

@ -1,8 +1,35 @@
# ##### 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 #####
if "bpy" in locals():
from importlib import reload
paths = reload(paths)
utils = reload(utils)
tasks_queue = reload(tasks_queue)
else:
from blenderkit import paths, utils, tasks_queue
import requests
import json
import os
import bpy
from blenderkit import paths, utils, tasks_queue
import shutil
import threading

View File

@ -16,23 +16,22 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
paths = reload(paths)
append_link = reload(append_link)
utils = reload(utils)
else:
from blenderkit import paths, append_link, utils
import threading
import time
import requests
import shutil, sys, os, math
import random
import shutil, sys, os
import uuid
import copy
if "bpy" in locals():
import imp
imp.reload(paths)
imp.reload(append_link)
imp.reload(utils)
else:
from blenderkit import paths, append_link, utils
import bpy
from bpy.props import (
IntProperty,
@ -492,7 +491,7 @@ def timer_update(): # TODO might get moved to handle all blenderkit stuff, not
done = try_finished_append(asset_data, **tcom.passargs)
if not done:
at = asset_data['asset_type']
tcom.passargs['retry_counter'] = tcom.passargs.get('retry_counter',0) +1
tcom.passargs['retry_counter'] = tcom.passargs.get('retry_counter', 0) + 1
if at in ('model', 'material'):
download(asset_data, **tcom.passargs)
elif asset_data['asset_type'] == 'material':
@ -595,13 +594,12 @@ def download(asset_data, **kwargs):
tcom = ThreadCom()
tcom.passargs = kwargs
if kwargs.get('retry_counter',0) > 3:
if kwargs.get('retry_counter', 0) > 3:
sprops = utils.get_search_props()
sprops.report = f"Maximum retries exceeded for {asset_data['name']}"
utils.p(sprops.report)
return
# incoming data can be either directly dict from python, or blender id property
# (recovering failed downloads on reload)
if type(asset_data) == dict:

View File

@ -26,10 +26,11 @@ import requests
class SimpleOAuthAuthenticator(object):
def __init__(self, server_url, client_id, ports):
def __init__(self, server_url, client_id, ports, redirect_url):
self.server_url = server_url
self.client_id = client_id
self.ports = ports
self.redirect_url = redirect_url
def _get_tokens(self, authorization_code=None, refresh_token=None, grant_type="authorization_code"):
data = {
@ -62,7 +63,14 @@ class SimpleOAuthAuthenticator(object):
if 'code' in self.path:
self.auth_code = self.path.split('=')[1]
# Display to the user that they no longer need the browser window
self.wfile.write(bytes('<html><h1>You may now close this window.</h1></html>', 'utf-8'))
self.wfile.write(bytes(
'<html>'
'<head><meta http-equiv="refresh" content="0;url=%(redirect_url)s"></head>'
'<script> window.location.href="%(redirect_url)s"; </script>'
'<h1>You may now close this window.</h1>'
'</html>' % {'redirect_url': self.redirect_url},
'utf-8',
))
qs = parse_qs(urlparse(self.path).query)
self.server.authorization_code = qs['code'][0]
@ -82,4 +90,4 @@ class SimpleOAuthAuthenticator(object):
return self._get_tokens(authorization_code=authorization_code)
def get_refreshed_token(self, refresh_token):
return self._get_tokens(refresh_token=refresh_token, grant_type="refresh_token")
return self._get_tokens(refresh_token=refresh_token, grant_type="refresh_token")

View File

@ -16,12 +16,18 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
else:
from blenderkit import utils
import bpy, mathutils
from bpy.types import (
Operator)
from blenderkit import utils
def getNodes(nt, node_type='OUTPUT_MATERIAL'):
chnodes = nt.nodes[:]

View File

@ -28,7 +28,8 @@ BLENDERKIT_PLANS = "https://www.blenderkit.com/plans/pricing/"
BLENDERKIT_MANUAL = "https://youtu.be/1hVgcQhIAo8"
BLENDERKIT_MODEL_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/upload/"
BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-material/"
BLENDERKIT_SIGNUP_URL = "https://www.blenderkit.com/accounts/register/"
BLENDERKIT_LOGIN_URL = "https://www.blenderkit.com/accounts/login"
BLENDERKIT_SIGNUP_URL = "https://www.blenderkit.com/accounts/register"
BLENDERKIT_ADDON_URL = "https://www.blenderkit.com/api/v1/assets/6923b215-7df0-46f3-95ae-a2b5ff44ddd5/"
BLENDERKIT_ADDON_FILE_URL = "https://www.blenderkit.com/get-blenderkit/"
_presets = os.path.join(bpy.utils.user_resource('SCRIPTS'), "presets")

View File

@ -17,10 +17,10 @@
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
imp.reload(paths)
from importlib import reload
paths = reload(paths)
utils = reload(utils)
else:
from blenderkit import paths, utils
@ -85,7 +85,6 @@ def upload_rating(asset):
api_key = user_preferences.api_key
headers = utils.get_headers(api_key)
asset_data = asset['asset_data']
bkit_ratings = asset.bkit_ratings

View File

@ -16,16 +16,17 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
imp.reload(paths)
imp.reload(utils)
imp.reload(categories)
imp.reload(ui)
imp.reload(version_checker)
imp.reload(bkit_oauth)
imp.reload(tasks_queue)
if "bpy" in locals():
from importlib import reload
paths = reload(paths)
utils = reload(utils)
categories = reload(categories)
ui = reload(ui)
bkit_oauth = reload(bkit_oauth)
version_checker = reload(version_checker)
tasks_queue = reload(tasks_queue)
else:
from blenderkit import paths, utils, categories, ui, bkit_oauth, version_checker, tasks_queue

View File

@ -1,9 +1,33 @@
# ##### 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 #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
else:
from blenderkit import utils
import bpy
from bpy.app.handlers import persistent
import queue
from blenderkit import utils
@persistent

View File

@ -16,14 +16,18 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
imp.reload(paths)
imp.reload(ratings)
imp.reload(utils)
imp.reload(search)
imp.reload(upload)
if "bpy" in locals():
import importlib
paths = importlib.reload(paths)
ratings = importlib.reload(ratings)
utils = importlib.reload(utils)
search = importlib.reload(search)
upload = importlib.reload(upload)
ui_bgl = importlib.reload(ui_bgl)
download = importlib.reload(download)
bg_blender = importlib.reload(bg_blender)
else:
from blenderkit import paths, ratings, utils, search, upload, ui_bgl, download, bg_blender
@ -1425,7 +1429,7 @@ class AssetBarOperator(bpy.types.Operator):
a = asset_data['author_id']
if a is not None:
utils.p('author:', a)
search.search(author_id = a)
search.search(author_id=a)
return {'RUNNING_MODAL'}
if event.type == 'X' and ui_props.active_index != -3:
sr = bpy.context.scene['search results']

View File

@ -17,16 +17,15 @@
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
import importlib
imp.reload(paths)
imp.reload(ratings)
imp.reload(utils)
imp.reload(download)
imp.reload(categories)
imp.reload(ui)
paths = importlib.reload(paths)
ratings = importlib.reload(ratings)
utils = importlib.reload(utils)
download = importlib.reload(download)
categories = importlib.reload(categories)
else:
from blenderkit import paths, ratings, utils, download, categories, ui
from blenderkit import paths, ratings, utils, download, categories
from bpy.types import (
Panel
@ -411,8 +410,7 @@ class VIEW3D_PT_blenderkit_profile(Panel):
if len(user_preferences.api_key) < 20:
if user_preferences.enable_oauth:
layout.operator("wm.blenderkit_login", text="Login/ Sign up",
icon='URL')
draw_login_buttons(layout)
else:
me = bpy.context.window_manager.get('bkit profile')
if me is not None:
@ -545,6 +543,13 @@ def draw_panel_brush_ratings(self, context):
op.asset_type = 'BRUSH'
def draw_login_buttons(layout):
layout.operator("wm.blenderkit_login", text="Login",
icon='URL').signup = False
layout.operator("wm.blenderkit_login", text="Sign up",
icon='URL').signup = True
class VIEW3D_PT_blenderkit_unified(Panel):
bl_category = "BlenderKit"
bl_idname = "VIEW3D_PT_blenderkit_unified"
@ -581,8 +586,7 @@ class VIEW3D_PT_blenderkit_unified(Panel):
if len(user_preferences.api_key) < 20 and user_preferences.asset_counter > 20:
if user_preferences.enable_oauth:
layout.operator("wm.blenderkit_login", text="Login/ Sign up",
icon='URL')
draw_login_buttons(layout)
else:
op = layout.operator("wm.url_open", text="Get your API Key",
icon='QUESTION')

View File

@ -16,19 +16,20 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
imp.reload(asset_inspector)
imp.reload(paths)
imp.reload(utils)
imp.reload(search)
imp.reload(bg_blender)
imp.reload(autothumb)
imp.reload(version_checker)
imp.reload(ui_panels)
if "bpy" in locals():
from importlib import reload
asset_inspector = reload(asset_inspector)
paths = reload(paths)
utils = reload(utils)
bg_blender = reload(bg_blender)
autothumb = reload(autothumb)
version_checker = reload(version_checker)
search = reload(searchr)
ui_panels = reload(ui_panels)
else:
from blenderkit import asset_inspector, paths, utils, bg_blender, autothumb, version_checker, search,ui_panels
from blenderkit import asset_inspector, paths, utils, bg_blender, autothumb, version_checker, search, ui_panels
import tempfile, os, subprocess, json, re
@ -504,7 +505,7 @@ def get_upload_location(props):
def check_storage_quota(props):
if props.is_private =='PUBLIC':
if props.is_private == 'PUBLIC':
return True
profile = bpy.context.window_manager.get('bkit profile')
@ -739,8 +740,6 @@ class ModelUploadOperator(Operator):
' after upload. '
'Click Ok to proceed.')
def invoke(self, context, event):
props = utils.get_upload_props()

View File

@ -16,13 +16,22 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
from importlib import reload
paths = reload(paths)
append_link = reload(append_link)
bg_blender = reload(bg_blender)
utils = reload(utils)
else:
from blenderkit import paths, append_link, bg_blender, utils
import sys, json, os, time
import requests
import logging
import bpy
import addon_utils
from blenderkit import paths, append_link, bg_blender, utils
BLENDERKIT_EXPORT_DATA = sys.argv[-1]

View File

@ -16,12 +16,14 @@
#
# ##### END GPL LICENSE BLOCK #####
if "bpy" in locals():
import imp
imp.reload(paths)
if "bpy" in locals():
from importlib import reload
paths = reload(paths)
else:
from blenderkit import paths, categories
from blenderkit import paths
import bpy
from mathutils import Vector
import json

View File

@ -16,8 +16,15 @@
#
# ##### END GPL LICENSE BLOCK #####
if "requests" in locals():
from importlib import reload
paths = reload(paths)
else:
from blenderkit import paths
import requests, os, json, threading
from blenderkit import paths
def get_addon_version():