Skip to content
Snippets Groups Projects
download.py 54.3 KiB
Newer Older
  • Learn to ignore specific revisions
  •     nobs = bpy.context.selected_objects[:]
    
    Vilém Duha's avatar
    Vilém Duha committed
        # get asset main object
    
        for ob in nobs:
            if ob.parent not in nobs:
    
    Vilém Duha's avatar
    Vilém Duha committed
                asset_main = ob
    
    Vilém Duha's avatar
    Vilém Duha committed
    
        # in case of replacement,there might be a paarent relationship that can be restored
        if kwargs.get('parent'):
            parent = bpy.data.objects[kwargs['parent']]
            asset_main.parent = parent  # even if parent is None this is ok without if condition
        else:
            asset_main.parent = None
    
        # restore original selection
        utils.selection_set(sel)
    
    Vilém Duha's avatar
    Vilém Duha committed
        return asset_main, nobs
    
    Vilem Duha's avatar
    Vilem Duha committed
    def asset_in_scene(asset_data):
    
        '''checks if the asset is already in scene. If yes, modifies asset data so the asset can be reached again.'''
    
    Vilem Duha's avatar
    Vilem Duha committed
        scene = bpy.context.scene
        au = scene.get('assets used', {})
    
    
    Vilém Duha's avatar
    Vilém Duha committed
        print(id)
    
    Vilem Duha's avatar
    Vilem Duha committed
            ad = au[id]
    
    Vilém Duha's avatar
    Vilém Duha committed
            if ad.get('files'):
                for fi in ad['files']:
                    if fi.get('file_name') != None:
    
                        for fi1 in asset_data['files']:
                            if fi['fileType'] == fi1['fileType']:
                                fi1['file_name'] = fi['file_name']
                                fi1['url'] = fi['url']
    
                                # browse all collections since linked collections can have same name.
                                if asset_data['assetType'] == 'MODEL':
                                    for c in bpy.data.collections:
                                        if c.name == ad['name']:
                                            # there can also be more linked collections with same name, we need to check id.
                                            if c.library and c.library.get('asset_data') and c.library['asset_data'][
                                                'assetBaseId'] == id:
                                                print('asset linked')
                                                return 'LINKED', ad.get('resolution')
                                elif asset_data['assetType'] == 'MATERIAL':
                                    for m in bpy.data.materials:
                                        if not m.get('asset_data'):
                                            continue
                                        if m['asset_data']['assetBaseId'] == asset_data[
                                            'assetBaseId'] and bpy.context.active_object.active_material.library:
                                            return 'LINKED', ad.get('resolution')
    
                                print('asset appended')
                                return 'APPENDED', ad.get('resolution')
        return False, None
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    def fprint(text):
        print('###################################################################################')
        print('\n\n\n')
        print(text)
        print('\n\n\n')
        print('###################################################################################')
    
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    def get_download_url(asset_data, scene_id, api_key, tcom=None, resolution='blend'):
    
    Vilem Duha's avatar
    Vilem Duha committed
        ''''retrieves the download url. The server checks if user can download the item.'''
        mt = time.time()
    
    Vilém Duha's avatar
    Vilém Duha committed
        utils.pprint('getting download url')
    
    
        headers = utils.get_headers(api_key)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        data = {
            'scene_uuid': scene_id
        }
    
    Vilém Duha's avatar
    Vilém Duha committed
        r = None
    
    
    Vilém Duha's avatar
    Vilém Duha committed
        res_file_info, resolution = paths.get_res_file(asset_data, resolution)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        try:
    
    Vilém Duha's avatar
    Vilém Duha committed
            r = rerequests.get(res_file_info['downloadUrl'], params=data, headers=headers)
    
    Vilem Duha's avatar
    Vilem Duha committed
        except Exception as e:
            print(e)
            if tcom is not None:
                tcom.error = True
        if r == None:
    
            if tcom is not None:
                tcom.report = 'Connection Error'
                tcom.error = True
    
    Vilem Duha's avatar
    Vilem Duha committed
            return 'Connection Error'
    
    Vilem Duha's avatar
    Vilem Duha committed
        if r.status_code < 400:
            data = r.json()
            url = data['filePath']
    
    Vilém Duha's avatar
    Vilém Duha committed
    
            res_file_info['url'] = url
            res_file_info['file_name'] = paths.extract_filename_from_url(url)
    
            # print(res_file_info, url)
            print(url)
    
    Vilem Duha's avatar
    Vilem Duha committed
            return True
    
    
    Vilém Duha's avatar
    Vilém Duha committed
        # let's print it into UI
        tasks_queue.add_task((ui.add_report, (str(r), 10, colors.RED)))
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        if r.status_code == 403:
    
            r = 'You need Full plan to get this item.'
    
            # r1 = 'All materials and brushes are available for free. Only users registered to Standard plan can use all models.'
            # tasks_queue.add_task((ui.add_report, (r1, 5, colors.RED)))
    
            if tcom is not None:
                tcom.report = r
                tcom.error = True
    
    Vilém Duha's avatar
    Vilém Duha committed
        if r.status_code == 404:
            r = 'Url not found - 404.'
            # r1 = 'All materials and brushes are available for free. Only users registered to Standard plan can use all models.'
            if tcom is not None:
                tcom.report = r
                tcom.error = True
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        elif r.status_code >= 500:
    
            # bk_logger.debug(r.text)
    
            if tcom is not None:
                tcom.report = 'Server error'
                tcom.error = True
    
        return False
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    def start_download(asset_data, **kwargs):
        '''
        check if file isn't downloading or doesn't exist, then start new download
        '''
    
        # first check if the asset is already in scene. We can use that asset without checking with server
    
    Vilém Duha's avatar
    Vilém Duha committed
        ain, resolution = asset_in_scene(asset_data)
        # quota_ok = ain is not False
    
    Vilém Duha's avatar
    Vilém Duha committed
        # if resolution:
        #     kwargs['resolution'] = resolution
    
    Vilem Duha's avatar
    Vilem Duha committed
        # otherwise, check on server
    
        s = bpy.context.scene
        done = False
        # is the asseet being currently downloaded?
        downloading = check_downloading(asset_data, **kwargs)
        if not downloading:
    
            # check if there are files already. This check happens 2x once here(for free assets),
    
    Vilem Duha's avatar
    Vilem Duha committed
            # once in thread(for non-free)
    
    Vilém Duha's avatar
    Vilém Duha committed
            fexists = check_existing(asset_data, resolution=kwargs['resolution'])
    
            bk_logger.debug('does file exist?'+ str( fexists))
            bk_logger.debug('asset is in scene' + str(ain))
    
    Vilém Duha's avatar
    Vilém Duha committed
            if ain and not kwargs.get('replace_resolution'):
                # this goes to appending asset - where it should duplicate the original asset already in scene.
    
    Vilem Duha's avatar
    Vilem Duha committed
                done = try_finished_append(asset_data, **kwargs)
            # else:
            #     props = utils.get_search_props()
            #     props.report = str('asset ')
            if not done:
    
    Vilem Duha's avatar
    Vilem Duha committed
                if at in ('model', 'material'):
                    downloader = {'location': kwargs['model_location'],
                                  'rotation': kwargs['model_rotation']}
                    download(asset_data, downloaders=[downloader], **kwargs)
    
    
    Vilém Duha's avatar
    Vilém Duha committed
                    download(asset_data, **kwargs)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    asset_types = (
        ('MODEL', 'Model', 'set of objects'),
        ('SCENE', 'Scene', 'scene'),
    
        ('HDR', 'Hdr', 'hdr'),
    
    Vilem Duha's avatar
    Vilem Duha committed
        ('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 BlenderkitKillDownloadOperator(bpy.types.Operator):
    
        """Kill a download"""
    
        bl_idname = "scene.blenderkit_download_kill"
        bl_label = "BlenderKit Kill Asset Download"
    
    Vilém Duha's avatar
    Vilém Duha committed
        bl_options = {'REGISTER', 'INTERNAL'}
    
    
        thread_index: IntProperty(name="Thread index", description='index of the thread to kill', default=-1)
    
        def execute(self, context):
            global download_threads
            td = download_threads[self.thread_index]
            download_threads.remove(td)
            td[0].stop()
            return {'FINISHED'}
    
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    def available_resolutions_callback(self, context):
        '''
        Returns
        checks active asset for available resolutions and offers only those available
        TODO: this currently returns always the same list of resolutions, make it actually work
        '''
        # print('callback called', self.asset_data)
        pat_items = (
            ('512', '512', '', 1),
            ('1024', '1024', '', 2),
            ('2048', '2048', '', 3),
            ('4096', '4096', '', 4),
            ('8192', '8192', '', 5),
        )
        items = []
        for item in pat_items:
            if int(self.max_resolution) >= int(item[0]):
                items.append(item)
        items.append(('ORIGINAL', 'Original', '', 6))
        return items
    
    
    def show_enum_values(obj, prop_name):
        print([item.identifier for item in obj.bl_rna.properties[prop_name].enum_items])
    
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    class BlenderkitDownloadOperator(bpy.types.Operator):
    
        """Download and link asset to scene. Only link if asset already available locally."""
    
    Vilem Duha's avatar
    Vilem Duha committed
        bl_idname = "scene.blenderkit_download"
        bl_label = "BlenderKit Asset Download"
    
    Vilém Duha's avatar
    Vilém Duha committed
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
    
    Vilém Duha's avatar
    Vilém Duha committed
        # asset_type: EnumProperty(
        #     name="Type",
        #     items=asset_types,
        #     description="Type of download",
        #     default="MODEL",
        # )
    
    Vilem Duha's avatar
    Vilem Duha committed
        asset_index: IntProperty(name="Asset Index", description='asset index in search results', default=-1)
    
    
        asset_base_id: StringProperty(
            name="Asset base Id",
            description="Asset base id, used instead of search result index.",
            default="")
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        target_object: StringProperty(
    
            name="Target Object",
            description="Material or object target for replacement",
    
    Vilem Duha's avatar
    Vilem Duha committed
            default="")
    
    Vilem Duha's avatar
    Vilem Duha committed
        material_target_slot: IntProperty(name="Asset Index", description='asset index in search results', default=0)
        model_location: FloatVectorProperty(name='Asset Location', default=(0, 0, 0))
        model_rotation: FloatVectorProperty(name='Asset Rotation', default=(0, 0, 0))
    
    
        replace: BoolProperty(name='Replace', description='replace selection with the asset', default=False)
    
    
    Vilém Duha's avatar
    Vilém Duha committed
        replace_resolution: BoolProperty(name='Replace resolution', description='replace resolution of the active asset',
                                         default=False)
    
        invoke_resolution: BoolProperty(name='Replace resolution popup',
                                        description='pop up to ask which resolution to download', default=False)
    
        resolution: EnumProperty(
            items=available_resolutions_callback,
            default=0,
            description='Replace resolution'
        )
    
        max_resolution: IntProperty(
            name="Max resolution",
            description="",
            default=0)
        # has_res_0_5k: BoolProperty(name='512',
        #                                 description='', default=False)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        cast_parent: StringProperty(
            name="Particles Target Object",
            description="",
            default="")
    
        # @classmethod
        # def poll(cls, context):
        #     return bpy.context.window_manager.BlenderKitModelThumbnails is not ''
    
    Vilém Duha's avatar
    Vilém Duha committed
        def get_asset_data(self, context):
            # get asset data - it can come from scene, or from search results.
    
    Vilem Duha's avatar
    Vilem Duha committed
            s = bpy.context.scene
    
    
            if self.asset_index > -1:
                # either get the data from search results
                sr = s['search results']
                asset_data = sr[
                    self.asset_index].to_dict()  # TODO CHECK ALL OCCURRENCES OF PASSING BLENDER ID PROPS TO THREADS!
                asset_base_id = asset_data['assetBaseId']
            else:
                # or from the scene.
                asset_base_id = self.asset_base_id
    
    
    Vilem Duha's avatar
    Vilem Duha committed
            au = s.get('assets used')
            if au == None:
                s['assets used'] = {}
    
            if asset_base_id in s.get('assets used'):
                # already used assets have already download link and especially file link.
                asset_data = s['assets used'][asset_base_id].to_dict()
    
    Vilém Duha's avatar
    Vilém Duha committed
            return asset_data
    
        def execute(self, context):
            sprops = utils.get_search_props()
    
    Vilém Duha's avatar
    Vilém Duha committed
            self.asset_data = self.get_asset_data(context)
    
            # print('after getting asset data')
            # print(self.asset_base_id)
    
            atype = self.asset_data['assetType']
    
    Vilem Duha's avatar
    Vilem Duha committed
            if bpy.context.mode != 'OBJECT' and (
    
                    atype == 'model' or atype == 'material') and bpy.context.view_layer.objects.active is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                bpy.ops.object.mode_set(mode='OBJECT')
    
    Vilém Duha's avatar
    Vilém Duha committed
            if self.resolution == 0 or self.resolution == '':
                resolution = sprops.resolution
            else:
                resolution = self.resolution
    
            resolution = resolutions.resolution_props_to_server[resolution]
    
            if self.replace:  # cleanup first, assign later.
    
                obs = utils.get_selected_replace_adepts()
    
                # print(obs)
    
    Vilém Duha's avatar
    Vilém Duha committed
                    # print('replace attempt ', ob.name)
    
                    if self.asset_base_id != '':
    
    Vilém Duha's avatar
    Vilém Duha committed
                        # this is for a case when replace is called from a panel,
                        # this uses active object as replacement source instead of target.
                        if ob.get('asset_data') is not None and \
                                (ob['asset_data']['assetBaseId'] == self.asset_base_id and ob['asset_data'][
                                    'resolution'] == resolution):
                            # print('skipping this one')
    
    Vilém Duha's avatar
    Vilém Duha committed
                    parent = ob.parent
                    if parent:
                        parent = ob.parent.name  # after this, parent is either name or None.
    
                    kwargs = {
                        'cast_parent': self.cast_parent,
                        'target_object': ob.name,
                        'material_target_slot': ob.active_material_index,
                        'model_location': tuple(ob.matrix_world.translation),
                        'model_rotation': tuple(ob.matrix_world.to_euler()),
    
    Vilém Duha's avatar
    Vilém Duha committed
                        'replace': True,
                        'replace_resolution': False,
                        'parent': parent,
                        'resolution': resolution
    
    Vilém Duha's avatar
    Vilém Duha committed
                    # TODO - move this After download, not before, so that the replacement
    
    Vilém Duha's avatar
    Vilém Duha committed
                    start_download(self.asset_data, **kwargs)
    
    Vilém Duha's avatar
    Vilém Duha committed
                # replace resolution needs to replace all instances of the resolution in the scene
                # and deleting originals has to be thus done after the downlaod
    
    
                kwargs = {
                    'cast_parent': self.cast_parent,
                    'target_object': self.target_object,
                    'material_target_slot': self.material_target_slot,
                    'model_location': tuple(self.model_location),
                    'model_rotation': tuple(self.model_rotation),
    
    Vilém Duha's avatar
    Vilém Duha committed
                    'replace': False,
                    'replace_resolution': self.replace_resolution,
                    'resolution': resolution
    
    Vilém Duha's avatar
    Vilém Duha committed
                start_download(self.asset_data, **kwargs)
    
    Vilem Duha's avatar
    Vilem Duha committed
            return {'FINISHED'}
    
    
    Vilém Duha's avatar
    Vilém Duha committed
        def draw(self, context):
            layout = self.layout
            layout.prop(self, 'resolution', expand=True, icon_only=False)
    
        def invoke(self, context, event):
            print(self.asset_base_id)
            wm = context.window_manager
            # only make a pop up in case of switching resolutions
            if self.invoke_resolution:
                # show_enum_values(self, 'resolution')
                # print('ENUM VALUES')
                self.asset_data = self.get_asset_data(context)
                sprops = utils.get_search_props()
                if int(sprops.resolution) <= int(self.max_resolution):
                    self.resolution = sprops.resolution
                elif int(self.max_resolution) > 0:
                    self.resolution = self.max_resolution
                else:
                    self.resolution = 'ORIGINAL'
                return wm.invoke_props_dialog(self)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    def register_download():
        bpy.utils.register_class(BlenderkitDownloadOperator)
    
        bpy.utils.register_class(BlenderkitKillDownloadOperator)
    
    Vilem Duha's avatar
    Vilem Duha committed
        bpy.app.handlers.load_post.append(scene_load)
        bpy.app.handlers.save_pre.append(scene_save)
    
        user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
        if user_preferences.use_timers:
            bpy.app.timers.register(timer_update)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    def unregister_download():
        bpy.utils.unregister_class(BlenderkitDownloadOperator)
    
        bpy.utils.unregister_class(BlenderkitKillDownloadOperator)
    
    Vilem Duha's avatar
    Vilem Duha committed
        bpy.app.handlers.load_post.remove(scene_load)
        bpy.app.handlers.save_pre.remove(scene_save)
    
        if bpy.app.timers.is_registered(timer_update):
            bpy.app.timers.unregister(timer_update)