Skip to content
Snippets Groups Projects
upload.py 34.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vilem Duha's avatar
    Vilem Duha committed
    # ##### 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 #####
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    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(search)
    
        ui_panels = reload(ui_panels)
    
        overrides = reload(overrides)
    
        colors = reload(colors)
    
        rerequests = reload(rerequests)
    
        categories = reload(categories)
    
    Vilem Duha's avatar
    Vilem Duha committed
    else:
    
        from blenderkit import asset_inspector, paths, utils, bg_blender, autothumb, version_checker, search, ui_panels, ui, \
    
            overrides, colors, rerequests, categories
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    import tempfile, os, subprocess, json, re
    
    import bpy
    import requests
    
    import threading
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    BLENDERKIT_EXPORT_DATA_FILE = "data.json"
    
    from bpy.props import (  # TODO only keep the ones actually used when cleaning
        EnumProperty,
        BoolProperty,
    
        StringProperty,
    
    Vilem Duha's avatar
    Vilem Duha committed
    )
    from bpy.types import (
        Operator,
        Panel,
        AddonPreferences,
        PropertyGroup,
        UIList
    )
    
    
    def comma2array(text):
        commasep = text.split(',')
        ar = []
        for i, s in enumerate(commasep):
            s = s.strip()
            if s != '':
                ar.append(s)
        return ar
    
    
    def get_app_version():
        ver = bpy.app.version
        return '%i.%i.%i' % (ver[0], ver[1], ver[2])
    
    
    def add_version(data):
        app_version = get_app_version()
        addon_version = version_checker.get_addon_version()
        data["sourceAppName"] = "blender"
        data["sourceAppVersion"] = app_version
        data["addonVersion"] = addon_version
    
    
    
    
    def write_to_report(props, text):
        props.report = props.report + text + '\n'
    
    
    def get_missing_data_model(props):
        props.report = ''
        autothumb.update_upload_model_preview(None, None)
    
        if props.name == '':
            write_to_report(props, 'Set model name')
    
        # if props.tags == '':
        #     write_to_report(props, 'Write at least 3 tags')
    
    Vilem Duha's avatar
    Vilem Duha committed
        if not props.has_thumbnail:
            write_to_report(props, 'Add thumbnail:')
    
            props.report += props.thumbnail_generating_state + '\n'
        if props.engine == 'NONE':
            write_to_report(props, 'Set at least one rendering/output engine')
        if not any(props.dimensions):
            write_to_report(props, 'Run autotags operator or fill in dimensions manually')
    
    
    def get_missing_data_scene(props):
        props.report = ''
        autothumb.update_upload_model_preview(None, None)
    
        if props.name == '':
            write_to_report(props, 'Set scene name')
    
        # if props.tags == '':
        #     write_to_report(props, 'Write at least 3 tags')
    
    Vilem Duha's avatar
    Vilem Duha committed
        if not props.has_thumbnail:
            write_to_report(props, 'Add thumbnail:')
    
            props.report += props.thumbnail_generating_state + '\n'
        if props.engine == 'NONE':
            write_to_report(props, 'Set at least one rendering/output engine')
    
    
    def get_missing_data_material(props):
        props.report = ''
        autothumb.update_upload_material_preview(None, None)
        if props.name == '':
            write_to_report(props, 'Set material name')
    
        # if props.tags == '':
        #     write_to_report(props, 'Write at least 3 tags')
    
    Vilem Duha's avatar
    Vilem Duha committed
        if not props.has_thumbnail:
            write_to_report(props, 'Add thumbnail:')
            props.report += props.thumbnail_generating_state
        if props.engine == 'NONE':
            write_to_report(props, 'Set rendering/output engine')
    
    
    def get_missing_data_brush(props):
        autothumb.update_upload_brush_preview(None, None)
        props.report = ''
        if props.name == '':
            write_to_report(props, 'Set brush name')
    
        # if props.tags == '':
        #     write_to_report(props, 'Write at least 3 tags')
    
    Vilem Duha's avatar
    Vilem Duha committed
        if not props.has_thumbnail:
            write_to_report(props, 'Add thumbnail:')
            props.report += props.thumbnail_generating_state
    
    
    def sub_to_camel(content):
        replaced = re.sub(r"_.",
                          lambda m: m.group(0)[1].upper(), content)
        return (replaced)
    
    
    def camel_to_sub(content):
        replaced = re.sub(r"[A-Z]", lambda m: '_' + m.group(0).lower(), content)
        return replaced
    
    
    def get_upload_data(self, context, asset_type):
        user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
        api_key = user_preferences.api_key
    
        export_data = {
            "type": asset_type,
        }
        upload_params = {}
        if asset_type == 'MODEL':
            # Prepare to save the file
            mainmodel = utils.get_active_model()
    
            props = mainmodel.blenderkit
    
            obs = utils.get_hierarchy(mainmodel)
            obnames = []
            for ob in obs:
                obnames.append(ob.name)
            export_data["models"] = obnames
            export_data["thumbnail_path"] = bpy.path.abspath(props.thumbnail)
    
            eval_path_computing = "bpy.data.objects['%s'].blenderkit.uploading" % mainmodel.name
            eval_path_state = "bpy.data.objects['%s'].blenderkit.upload_state" % mainmodel.name
            eval_path = "bpy.data.objects['%s']" % mainmodel.name
    
            engines = [props.engine.lower()]
            if props.engine1 != 'NONE':
                engines.append(props.engine1.lower())
            if props.engine2 != 'NONE':
                engines.append(props.engine2.lower())
            if props.engine3 != 'NONE':
                engines.append(props.engine3.lower())
            if props.engine == 'OTHER':
                engines.append(props.engine_other.lower())
    
            style = props.style.lower()
    
            # if style == 'OTHER':
            #     style = props.style_other.lower()
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            pl_dict = {'FINISHED': 'finished', 'TEMPLATE': 'template'}
    
            upload_data = {
                "assetType": 'model',
    
            }
            upload_params = {
                "productionLevel": props.production_level.lower(),
                "model_style": style,
                "engines": engines,
                "modifiers": comma2array(props.modifiers),
                "materials": comma2array(props.materials),
                "shaders": comma2array(props.shaders),
                "uv": props.uv,
                "dimensionX": round(props.dimensions[0], 4),
                "dimensionY": round(props.dimensions[1], 4),
                "dimensionZ": round(props.dimensions[2], 4),
    
                "boundBoxMinX": round(props.bbox_min[0], 4),
                "boundBoxMinY": round(props.bbox_min[1], 4),
                "boundBoxMinZ": round(props.bbox_min[2], 4),
    
                "boundBoxMaxX": round(props.bbox_max[0], 4),
                "boundBoxMaxY": round(props.bbox_max[1], 4),
                "boundBoxMaxZ": round(props.bbox_max[2], 4),
    
                "animated": props.animated,
                "rig": props.rig,
                "simulation": props.simulation,
                "purePbr": props.pbr,
                "faceCount": props.face_count,
                "faceCountRender": props.face_count_render,
                "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),
    
    Vilem Duha's avatar
    Vilem Duha committed
                # "scene": props.is_scene,
            }
            if props.use_design_year:
                upload_params["designYear"] = props.design_year
            if props.condition != 'UNSPECIFIED':
                upload_params["condition"] = props.condition.lower()
            if props.pbr:
                pt = props.pbr_type
                pt = pt.lower()
                upload_params["pbrType"] = pt
    
            if props.texture_resolution_max > 0:
                upload_params["textureResolutionMax"] = props.texture_resolution_max
                upload_params["textureResolutionMin"] = props.texture_resolution_min
            if props.mesh_poly_type != 'OTHER':
                upload_params["meshPolyType"] = props.mesh_poly_type.lower()  # .replace('_',' ')
    
            optional_params = ['manufacturer', 'designer', 'design_collection', 'design_variant']
            for p in optional_params:
                if eval('props.%s' % p) != '':
                    upload_params[sub_to_camel(p)] = eval('props.%s' % p)
    
        if asset_type == 'SCENE':
            # Prepare to save the file
            s = bpy.context.scene
    
            props = s.blenderkit
    
            export_data["scene"] = s.name
            export_data["thumbnail_path"] = bpy.path.abspath(props.thumbnail)
    
            eval_path_computing = "bpy.data.scenes['%s'].blenderkit.uploading" % s.name
            eval_path_state = "bpy.data.scenes['%s'].blenderkit.upload_state" % s.name
            eval_path = "bpy.data.scenes['%s']" % s.name
    
            engines = [props.engine.lower()]
            if props.engine1 != 'NONE':
                engines.append(props.engine1.lower())
            if props.engine2 != 'NONE':
                engines.append(props.engine2.lower())
            if props.engine3 != 'NONE':
                engines.append(props.engine3.lower())
            if props.engine == 'OTHER':
                engines.append(props.engine_other.lower())
    
            style = props.style.lower()
    
            # if style == 'OTHER':
            #     style = props.style_other.lower()
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            pl_dict = {'FINISHED': 'finished', 'TEMPLATE': 'template'}
    
            upload_data = {
                "assetType": 'scene',
    
            }
            upload_params = {
                "productionLevel": props.production_level.lower(),
                "model_style": style,
                "engines": engines,
                "modifiers": comma2array(props.modifiers),
                "materials": comma2array(props.materials),
                "shaders": comma2array(props.shaders),
                "uv": props.uv,
    
                "animated": props.animated,
                # "simulation": props.simulation,
                "purePbr": props.pbr,
                "faceCount": 1,  # props.face_count,
                "faceCountRender": 1,  # props.face_count_render,
                "objectCount": 1,  # props.object_count,
    
                # "scene": props.is_scene,
            }
            if props.use_design_year:
                upload_params["designYear"] = props.design_year
            if props.condition != 'UNSPECIFIED':
                upload_params["condition"] = props.condition.lower()
            if props.pbr:
                pt = props.pbr_type
                pt = pt.lower()
                upload_params["pbrType"] = pt
    
            if props.texture_resolution_max > 0:
                upload_params["textureResolutionMax"] = props.texture_resolution_max
                upload_params["textureResolutionMin"] = props.texture_resolution_min
            if props.mesh_poly_type != 'OTHER':
                upload_params["meshPolyType"] = props.mesh_poly_type.lower()  # .replace('_',' ')
    
        elif asset_type == 'MATERIAL':
            mat = bpy.context.active_object.active_material
            props = mat.blenderkit
    
            # props.name = mat.name
    
            export_data["material"] = str(mat.name)
            export_data["thumbnail_path"] = bpy.path.abspath(props.thumbnail)
            # mat analytics happen here, since they don't take up any time...
            asset_inspector.check_material(props, mat)
    
            eval_path_computing = "bpy.data.materials['%s'].blenderkit.uploading" % mat.name
            eval_path_state = "bpy.data.materials['%s'].blenderkit.upload_state" % mat.name
            eval_path = "bpy.data.materials['%s']" % mat.name
    
            engine = props.engine
            if engine == 'OTHER':
                engine = props.engine_other
            engine = engine.lower()
            style = props.style.lower()
    
            # if style == 'OTHER':
            #     style = props.style_other.lower()
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            upload_data = {
                "assetType": 'material',
    
            }
    
            upload_params = {
                "material_style": style,
                "engine": engine,
                "shaders": comma2array(props.shaders),
                "uv": props.uv,
                "animated": props.animated,
                "purePbr": props.pbr,
                "textureSizeMeters": props.texture_size_meters,
    
                "procedural": props.is_procedural,
                "nodeCount": props.node_count,
                "textureCount": props.texture_count,
    
                "megapixels": round(props.total_megapixels/ 1000000),
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            }
    
            if props.pbr:
                upload_params["pbrType"] = props.pbr_type.lower()
    
            if props.texture_resolution_max > 0:
                upload_params["textureResolutionMax"] = props.texture_resolution_max
                upload_params["textureResolutionMin"] = props.texture_resolution_min
    
        elif asset_type == 'BRUSH':
            brush = utils.get_active_brush()
    
            props = brush.blenderkit
            # props.name = brush.name
    
            export_data["brush"] = str(brush.name)
            export_data["thumbnail_path"] = bpy.path.abspath(brush.icon_filepath)
    
            eval_path_computing = "bpy.data.brushes['%s'].blenderkit.uploading" % brush.name
            eval_path_state = "bpy.data.brushes['%s'].blenderkit.upload_state" % brush.name
            eval_path = "bpy.data.brushes['%s']" % brush.name
    
            # mat analytics happen here, since they don't take up any time...
    
            brush_type = ''
            if bpy.context.sculpt_object is not None:
                brush_type = 'sculpt'
    
            elif bpy.context.image_paint_object:  # could be just else, but for future p
                brush_type = 'texture_paint'
    
            upload_params = {
                "mode": brush_type,
            }
    
            upload_data = {
                "assetType": 'brush',
            }
    
        elif asset_type == 'TEXTURE':
            style = props.style
    
            # if style == 'OTHER':
            #     style = props.style_other
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            upload_data = {
                "assetType": 'texture',
    
            }
            upload_params = {
                "style": style,
                "animated": props.animated,
                "purePbr": props.pbr,
                "resolution": props.resolution,
            }
            if props.pbr:
                pt = props.pbr_type
                pt = pt.lower()
                upload_data["pbrType"] = pt
    
        add_version(upload_data)
    
        upload_data["name"] = props.name
        upload_data["description"] = props.description
        upload_data["tags"] = comma2array(props.tags)
        if props.category == '':
            upload_data["category"] = asset_type.lower()
        else:
            upload_data["category"] = props.category
        if props.subcategory != '':
            upload_data["category"] = props.subcategory
        upload_data["license"] = props.license
        upload_data["isFree"] = props.is_free
    
        upload_data["isPrivate"] = props.is_private == 'PRIVATE'
    
    Vilem Duha's avatar
    Vilem Duha committed
        upload_data["token"] = user_preferences.api_key
    
        if props.asset_base_id != '':
            upload_data['assetBaseId'] = props.asset_base_id
            upload_data['id'] = props.id
    
        upload_data['parameters'] = upload_params
    
        return export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props
    
    
    
    def category_change_thread(asset_id, category, api_key):
        upload_data = {
            "category": category
        }
        url = paths.get_api_url() + 'assets/' + str(asset_id) + '/'
        headers = utils.get_headers(api_key)
        try:
            r = rerequests.patch(url, json=upload_data, headers=headers, verify=True)  # files = files,
        except requests.exceptions.RequestException as e:
            print(e)
            return {'CANCELLED'}
        return {'FINISHED'}
    
    
    
    # class OBJECT_MT_blenderkit_fast_category_menu(bpy.types.Menu):
    #     bl_label = "Fast category change"
    #     bl_idname = "OBJECT_MT_blenderkit_fast_category_menu"
    #
    #     def draw(self, context):
    #         layout = self.layout
    #         ui_props = context.scene.blenderkitUI
    #
    #         # sr = bpy.context.scene['search results']
    #         sr = bpy.context.scene['search results']
    #         asset_data = sr[ui_props.active_index]
    #         categories = bpy.context.window_manager['bkit_categories']
    #         wm = bpy.context.win
    #         for c in categories:
    #             if c['name'].lower() == asset_data['assetType']:
    #                 for ch in c['children']:
    #                     op = layout.operator('wm.blenderkit_fast_category', text = ch['name'])
    #                     op = layout.operator('wm.blenderkit_fast_category', text = ch['name'])
    
    
    class FastCategory(bpy.types.Operator):
        """Fast change of the category of object directly in asset bar."""
        bl_idname = "wm.blenderkit_fast_category"
        bl_label = "Update categories"
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
        category: EnumProperty(
            name="Category",
            description="main category to put into",
            items=categories.get_category_enums
        )
        subcategory: EnumProperty(
            name="Subcategory",
            description="main category to put into",
            items=categories.get_subcategory_enums
        )
    
    Vilém Duha's avatar
    Vilém Duha committed
        subcategory1: EnumProperty(
            name="Subcategory",
            description="main category to put into",
            items=categories.get_subcategory1_enums
        )
    
    
        asset_id: StringProperty(
            name="Asset Base Id",
            description="Unique name of the asset (hidden)",
            default="")
    
        @classmethod
        def poll(cls, context):
            scene = bpy.context.scene
            ui_props = scene.blenderkitUI
            return ui_props.active_index > -1
    
        def draw(self, context):
            layout = self.layout
            # col = layout.column()
            layout.label(text=self.message)
            row = layout.row()
            # col = row.column()
            # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
            # col.prop(self, 'category')
    
            layout.prop(self, 'category')#, expand = True)
            props = bpy.context.scene.blenderkitUI
            if props.asset_type == 'MODEL':  # by now block this for other asset types.
                # col = row.column()
                layout.prop(self, 'subcategory')
    
    Vilém Duha's avatar
    Vilém Duha committed
                layout.prop(self, 'subcategory1')
    
                # layout.prop(self, 'subcategory', expand = True)
    
        def execute(self, context):
            user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
            props = bpy.context.scene.blenderkitUI
            if props.asset_type == 'MODEL':
                category = self.subcategory
            else:
                category = self.category
            thread = threading.Thread(target=category_change_thread,
                                      args=(self.asset_id, category, user_preferences.api_key))
            thread.start()
            return {'FINISHED'}
    
        def invoke(self, context, event):
            scene = bpy.context.scene
            ui_props = scene.blenderkitUI
            if ui_props.active_index > -1:
                sr = bpy.context.scene['search results']
                asset_data = dict(sr[ui_props.active_index])
                self.asset_id = asset_data['id']
                self.asset_type = asset_data['assetType']
                cat_path = categories.get_category_path(bpy.context.window_manager['bkit_categories'],
                                                        asset_data['category'])
                try:
                    if len(cat_path) > 1:
                        self.category = cat_path[1]
                    if len(cat_path) > 2:
                        self.subcategory = cat_path[2]
                except Exception as e:
                    print(e)
                self.message = f"Recategorize asset {asset_data['name']}"
            wm = context.window_manager
            return wm.invoke_props_dialog(self)
    
    
    def verification_status_change_thread(asset_id, state, api_key):
    
    Vilem Duha's avatar
    Vilem Duha committed
        upload_data = {
    
            "verificationStatus": state
    
        url = paths.get_api_url() + 'assets/' + str(asset_id) + '/'
    
        headers = utils.get_headers(api_key)
    
    Vilem Duha's avatar
    Vilem Duha committed
        try:
    
            r = rerequests.patch(url, json=upload_data, headers=headers, verify=True)  # files = files,
    
    Vilem Duha's avatar
    Vilem Duha committed
        except requests.exceptions.RequestException as e:
    
            print(e)
    
    Vilem Duha's avatar
    Vilem Duha committed
            return {'CANCELLED'}
        return {'FINISHED'}
    
    
    def get_upload_location(props):
        scene = bpy.context.scene
        ui_props = scene.blenderkitUI
        if ui_props.asset_type == 'MODEL':
    
            if bpy.context.view_layer.objects.active is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                ob = utils.get_active_model()
                return ob.location
        if ui_props.asset_type == 'SCENE':
            return None
        elif ui_props.asset_type == 'MATERIAL':
    
            if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                return bpy.context.active_object.location
        elif ui_props.asset_type == 'TEXTURE':
            return None
        elif ui_props.asset_type == 'BRUSH':
            return None
        return None
    
    
    def check_storage_quota(props):
    
        if props.is_private == 'PUBLIC':
    
            return True
    
        profile = bpy.context.window_manager.get('bkit profile')
        if profile is None or profile.get('remainingPrivateQuota') is None:
            preferences = bpy.context.preferences.addons['blenderkit'].preferences
            adata = search.request_profile(preferences.api_key)
            if adata is None:
    
                props.report = 'Please log-in first.'
    
                return False
            search.write_profile(adata)
            profile = adata
    
        quota = profile['user'].get('remainingPrivateQuota')
        if quota is None or quota > 0:
    
            return True
        props.report = 'Private storage quota exceeded.'
        return False
    
    def auto_fix(asset_type=''):
    
        # this applies various procedures to ensure coherency in the database.
    
        asset = utils.get_active_asset()
        props = utils.get_upload_props()
        if asset_type == 'MATERIAL':
            overrides.ensure_eevee_transparency(asset)
            asset.name = props.name
    
    
    
    def start_upload(self, context, asset_type, reupload, upload_set):
    
        '''start upload process, by processing data'''
    
    
        # fix the name first
        utils.name_update()
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        props = utils.get_upload_props()
    
        storage_quota_ok = check_storage_quota(props)
        if not storage_quota_ok:
            self.report({'ERROR_INVALID_INPUT'}, props.report)
            return {'CANCELLED'}
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        location = get_upload_location(props)
        props.upload_state = 'preparing upload'
    
        auto_fix(asset_type=asset_type)
    
    Vilem Duha's avatar
    Vilem Duha committed
        # do this for fixing long tags in some upload cases
        props.tags = props.tags[:]
    
        props.name = props.name.strip()
        # TODO  move this to separate function
        # check for missing metadata
        if asset_type == 'MODEL':
            get_missing_data_model(props)
        if asset_type == 'SCENE':
            get_missing_data_scene(props)
        elif asset_type == 'MATERIAL':
            get_missing_data_material(props)
        elif asset_type == 'BRUSH':
            get_missing_data_brush(props)
    
        if props.report != '':
            self.report({'ERROR_INVALID_INPUT'}, props.report)
            return {'CANCELLED'}
    
        if not reupload:
    
    Vilem Duha's avatar
    Vilem Duha committed
            props.asset_base_id = ''
            props.id = ''
        export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props = get_upload_data(self, context,
                                                                                                           asset_type)
        # utils.pprint(upload_data)
    
    Vilém Duha's avatar
    Vilém Duha committed
        upload_data['parameters'] = utils.dict_to_params(
    
    Vilem Duha's avatar
    Vilem Duha committed
            upload_data['parameters'])  # weird array conversion only for upload, not for tooltips.
    
        binary_path = bpy.app.binary_path
        script_path = os.path.dirname(os.path.realpath(__file__))
        basename, ext = os.path.splitext(bpy.data.filepath)
        # if not basename:
        #     basename = os.path.join(basename, "temp")
        if not ext:
            ext = ".blend"
        tempdir = tempfile.mkdtemp()
        source_filepath = os.path.join(tempdir, "export_blenderkit" + ext)
        clean_file_path = paths.get_clean_filepath()
        data = {
            'clean_file_path': clean_file_path,
            'source_filepath': source_filepath,
            'temp_dir': tempdir,
            'export_data': export_data,
            'upload_data': upload_data,
            'debug_value': bpy.app.debug_value,
    
            'upload_set': upload_set,
    
    Vilem Duha's avatar
    Vilem Duha committed
        }
        datafile = os.path.join(tempdir, BLENDERKIT_EXPORT_DATA_FILE)
    
        # check if thumbnail exists:
    
        if 'THUMBNAIL' in upload_set:
    
            if not os.path.exists(export_data["thumbnail_path"]):
                props.upload_state = 'Thumbnail not found'
                props.uploading = False
                return {'CANCELLED'}
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        # first upload metadata to server, so it can be saved inside the current file
    
    
        headers = utils.get_headers(upload_data['token'])
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        # upload_data['license'] = 'ovejajojo'
        json_metadata = upload_data  # json.dumps(upload_data, ensure_ascii=False).encode('utf8')
        global reports
        if props.asset_base_id == '':
            try:
    
                r = rerequests.post(url, json=json_metadata, headers=headers, verify=True, immediate=True)  # files = files,
    
                ui.add_report('uploaded metadata')
    
                utils.p(r.text)
    
    Vilem Duha's avatar
    Vilem Duha committed
            except requests.exceptions.RequestException as e:
                print(e)
                props.upload_state = str(e)
                props.uploading = False
                return {'CANCELLED'}
    
        else:
            url += props.id + '/'
            try:
    
                if 'MAINFILE' in upload_set:
    
    Vilem Duha's avatar
    Vilem Duha committed
                    json_metadata["verificationStatus"] = "uploading"
    
    Vilém Duha's avatar
    Vilém Duha committed
                r = rerequests.patch(url, json=json_metadata, headers=headers, verify=True, immediate=True)  # files = files,
    
                ui.add_report('uploaded metadata')
    
    Vilem Duha's avatar
    Vilem Duha committed
                # print('uploaded metadata')
    
    Vilém Duha's avatar
    Vilém Duha committed
                print(r.text)
    
    Vilem Duha's avatar
    Vilem Duha committed
            except requests.exceptions.RequestException as e:
                print(e)
                props.upload_state = str(e)
                props.uploading = False
                return {'CANCELLED'}
    
        # props.upload_state = 'step 1'
    
        if upload_set == ['METADATA']:
    
    Vilem Duha's avatar
    Vilem Duha committed
            props.uploading = False
    
            props.upload_state = 'upload finished successfully'
    
    Vilem Duha's avatar
    Vilem Duha committed
            return {'FINISHED'}
        try:
            rj = r.json()
    
            # if r.status_code not in (200, 201):
            #     if r.status_code == 401:
            #         ui.add_report(r.detail, 5, colors.RED)
            #     return {'CANCELLED'}
    
    Vilem Duha's avatar
    Vilem Duha committed
            if props.asset_base_id == '':
                props.asset_base_id = rj['assetBaseId']
                props.id = rj['id']
            upload_data['assetBaseId'] = props.asset_base_id
            upload_data['id'] = props.id
    
    
            # bpy.ops.wm.save_mainfile()
            # bpy.ops.wm.save_as_mainfile(filepath=filepath, compress=False, copy=True)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
            props.uploading = True
            # save a copy of actual scene but don't interfere with the users models
            bpy.ops.wm.save_as_mainfile(filepath=source_filepath, compress=False, copy=True)
    
            with open(datafile, 'w') as s:
                json.dump(data, s)
    
            proc = subprocess.Popen([
                binary_path,
                "--background",
                "-noaudio",
                clean_file_path,
                "--python", os.path.join(script_path, "upload_bg.py"),
                "--", datafile  # ,filepath, tempdir
            ], bufsize=5000, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
    
            bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state,
                                      eval_path=eval_path, process_type='UPLOAD', process=proc, location=location)
    
        except Exception as e:
            props.upload_state = str(e)
            props.uploading = False
    
    Vilem Duha's avatar
    Vilem Duha committed
            return {'CANCELLED'}
    
        return {'FINISHED'}
    
    
    asset_types = (
        ('MODEL', 'Model', 'set of objects'),
        ('SCENE', 'Scene', 'scene'),
        ('MATERIAL', 'Material', 'any .blend Material'),
        ('TEXTURE', 'Texture', 'a texture, or texture set'),
        ('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
        ('ADDON', 'Addon', 'addnon'),
    )
    
    
    
    class UploadOperator(Operator):
    
    Vilem Duha's avatar
    Vilem Duha committed
        """Tooltip"""
        bl_idname = "object.blenderkit_upload"
        bl_description = "Upload or re-upload asset + thumbnail + metadata"
    
        bl_label = "BlenderKit asset upload"
    
    Vilém Duha's avatar
    Vilém Duha committed
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        # type of upload - model, material, textures, e.t.c.
        asset_type: EnumProperty(
            name="Type",
            items=asset_types,
            description="Type of upload",
            default="MODEL",
        )
    
    
        reupload: BoolProperty(
            name="reupload",
            description="reupload but also draw so that it asks what to reupload",
            default=False,
            options={'SKIP_SAVE'}
        )
    
        metadata: BoolProperty(
            name="metadata",
            default=True,
            options={'SKIP_SAVE'}
        )
    
        thumbnail: BoolProperty(
            name="thumbnail",
    
    Vilem Duha's avatar
    Vilem Duha committed
            default=False,
            options={'SKIP_SAVE'}
        )
    
    
        main_file: BoolProperty(
            name="main file",
    
    Vilem Duha's avatar
    Vilem Duha committed
            default=False,
            options={'SKIP_SAVE'}
        )
    
        @classmethod
        def poll(cls, context):
    
            return bpy.context.view_layer.objects.active is not None
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        def execute(self, context):
            bpy.ops.object.blenderkit_auto_tags()
            props = utils.get_upload_props()
    
            # in case of name change, we have to reupload everything, since the name is stored in blender file,
            # and is used for linking to scene
            if props.name_changed:
    
                # TODO: this needs to be replaced with new double naming scheme (metadata vs blend data)
    
    Vilem Duha's avatar
    Vilem Duha committed
                # print('has to reupload whole data, name has changed.')
    
                self.main_file = True
    
    Vilem Duha's avatar
    Vilem Duha committed
                props.name_changed = False
    
    
            upload_set = []
            if not self.reupload:
                upload_set = ['METADATA', 'THUMBNAIL', 'MAINFILE']
            else:
                if self.metadata:
                    upload_set.append('METADATA')
                if self.thumbnail:
                    upload_set.append('THUMBNAIL')
                if self.main_file:
                    upload_set.append('MAINFILE')
    
            result = start_upload(self, context, self.asset_type, self.reupload, upload_set)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            return result
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        def draw(self, context):
    
    Vilem Duha's avatar
    Vilem Duha committed
            layout = self.layout
    
    
            if self.reupload:
                # layout.prop(self, 'metadata')
                layout.prop(self, 'main_file')
                layout.prop(self, 'thumbnail')
    
            if props.asset_base_id != '' and not self.reupload:
    
                layout.label(text="Really upload as new? ")
                layout.label(text="Do this only when you create a new asset from an old one.")
                layout.label(text="For updates of thumbnail or model use reupload.")
    
            if props.is_private == 'PUBLIC':
    
                utils.label_multiline(layout, text='public assets are validated several hours'
    
    Vilém Duha's avatar
    Vilém Duha committed
                                                       ' 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()
    
    
    Vilém Duha's avatar
    Vilém Duha committed
            if not utils.user_logged_in():
    
                ui_panels.draw_not_logged_in(self, message = 'To upload assets you need to login/signup.')
    
    Vilém Duha's avatar
    Vilém Duha committed
                return {'CANCELLED'}
    
    
            if props.is_private == 'PUBLIC':
    
    Vilem Duha's avatar
    Vilem Duha committed
                return context.window_manager.invoke_props_dialog(self)
            else:
                return self.execute(context)
    
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    
    class AssetDebugPrint(Operator):
        """Change verification status"""
        bl_idname = "object.blenderkit_print_asset_debug"
        bl_description = "BlenderKit print asset data for debug purposes"
        bl_label = "BlenderKit print asset data"
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
        # type of upload - model, material, textures, e.t.c.
        asset_id: StringProperty(
            name="asset id",
        )
    
        @classmethod
        def poll(cls, context):
            return True
    
    
        def execute(self, context):
            preferences = bpy.context.preferences.addons['blenderkit'].preferences
    
            if not bpy.context.scene['search results']:
                print('no search results found')
                return {'CANCELLED'};
            # update status in search results for validator's clarity
            sr = bpy.context.scene['search results']
            sro = bpy.context.scene['search results orig']['results']
    
            result = None
            for r in sr:
                if r['id'] == self.asset_id:
                    result = r.to_dict()
            if not result:
                for r in sro:
                    if r['id'] == self.asset_id:
                        result = r.to_dict()
            if not result:
                ad = bpy.context.active_object.get('asset_data')
                if ad:
                    result = ad.to_dict()
            if result:
                print(json.dumps(result, indent=4, sort_keys=True))
            return {'FINISHED'}
    
    
    
    class AssetVerificationStatusChange(Operator):
        """Change verification status"""
        bl_idname = "object.blenderkit_change_status"
        bl_description = "Change asset ststus"
        bl_label = "Change verification status"
    
    Vilém Duha's avatar
    Vilém Duha committed
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
    
        # type of upload - model, material, textures, e.t.c.
        asset_id: StringProperty(
            name="asset id",
        )
    
        state: StringProperty(
            name="verification_status",
    
            default='uploaded'
    
        @classmethod
        def poll(cls, context):
            return True
    
        def draw(self, context):
            layout = self.layout
    
            # if self.state == 'deleted':
    
            layout.label(text='Really delete asset from BlenderKit online storage?')
            # layout.prop(self, 'state')
    
        def execute(self, context):
    
            preferences = bpy.context.preferences.addons['blenderkit'].preferences
    
    
            if not bpy.context.scene['search results']:
    
                return {'CANCELLED'};
    
    Vilém Duha's avatar
    Vilém Duha committed
            # update status in search results for validator's clarity
            sr = bpy.context.scene['search results']
            sro = bpy.context.scene['search results orig']['results']
    
            for r in sr:
                if r['id'] == self.asset_id:
    
                    r['verificationStatus'] = self.state
    
    Vilém Duha's avatar
    Vilém Duha committed
            for r in sro:
                if r['id'] == self.asset_id:
                    r['verificationStatus'] = self.state
    
    
            thread = threading.Thread(target=verification_status_change_thread,
                                      args=(self.asset_id, self.state, preferences.api_key))
            thread.start()
            return {'FINISHED'}