Beispiel #1
0
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
    def run(self):
        '''try to download file from blenderkit'''
        asset_data = self.asset_data
        tcom = self.tcom
        scene_id = self.scene_id
        api_key = self.api_key

        # TODO get real link here...
        has_url = get_download_url(asset_data, scene_id, api_key, tcom=tcom)

        if not has_url:
            tasks_queue.add_task(
                (ui.add_report,
                 ('Failed to obtain download URL for %s.' % asset_data['name'],
                  5, colors.RED)))
            return
        if tcom.error:
            return
        # only now we can check if the file already exists. This should have 2 levels, for materials and for brushes
        # different than for the non free content. delete is here when called after failed append tries.
        if check_existing(asset_data) and not tcom.passargs.get('delete'):
            # this sends the thread for processing, where another check should occur, since the file might be corrupted.
            tcom.downloaded = 100
            utils.p('not downloading, trying to append again')
            return

        file_name = paths.get_download_filenames(asset_data)[
            0]  # prefer global dir if possible.
        # for k in asset_data:
        #    print(asset_data[k])
        if self.stopped():
            utils.p('stopping download: ' + asset_data['name'])
            return

        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:
                tcom.file_size = int(total_length)
                dl = 0
                totdata = []
                for data in response.iter_content(
                        chunk_size=4096 *
                        32):  #crashed here... why? investigate:
                    dl += len(data)
                    tcom.downloaded = dl
                    tcom.progress = int(100 * tcom.downloaded / tcom.file_size)
                    f.write(data)
                    if self.stopped():
                        utils.p('stopping download: ' + asset_data['name'])
                        os.remove(file_name)
                        return
Beispiel #3
0
def check_existing(asset_data):
    ''' check if the object exists on the hard drive'''
    fexists = False

    file_names = paths.get_download_filenames(asset_data)

    utils.p('check if file already exists')
    if len(file_names) == 2:
        # TODO this should check also for failed or running downloads.
        # If download is running, assign just the running thread. if download isn't running but the file is wrong size,
        #  delete file and restart download (or continue downoad? if possible.)
        if os.path.isfile(file_names[0]) and not os.path.isfile(file_names[1]):
            shutil.copy(file_names[0], file_names[1])
        elif not os.path.isfile(file_names[0]) and os.path.isfile(
                file_names[1]):  # only in case of changed settings or deleted/moved global dict.
            shutil.copy(file_names[1], file_names[0])

    if len(file_names) > 0 and os.path.isfile(file_names[0]):
        fexists = True
    return fexists
Beispiel #4
0
def try_finished_append(asset_data, **kwargs):  # location=None, material_target=None):
    ''' try to append asset, if not successfully delete source files.
     This means probably wrong download, so download should restart'''
    file_names = paths.get_download_filenames(asset_data)
    done = False
    utils.p('try to append already existing asset')
    if len(file_names) > 0:
        if os.path.isfile(file_names[-1]):
            kwargs['name'] = asset_data['name']
            try:
                append_asset(asset_data, **kwargs)
                done = True
            except Exception as e:
                print(e)
                for f in file_names:
                    try:
                        os.remove(f)
                    except:
                        e = sys.exc_info()[0]
                        print(e)
                        pass;
                done = False
    return done
def main_thread(asset_data, tcom, scene_id, api_key):
    '''try to download file from blenderkit'''

    # TODO get real link here...
    get_download_url(asset_data, scene_id, api_key, tcom=tcom)
    if tcom.error:
        return
    # only now we can check if the file allready exists. This should have 2 levels, for materials and for brushes
    # different than for the non free content. delete is here when called after failed append tries.
    if check_existing(asset_data) and not tcom.passargs.get('delete'):
        # this sends the thread for processing, where another check should occur, since the file might be corrupted.
        tcom.downloaded = 100
        print('not downloading, trying to append again')
        return;
    file_name = paths.get_download_filenames(asset_data)[0]  # prefer global dir if possible.
    # for k in asset_data:
    #    print(asset_data[k])

    with open(file_name, "wb") as f:
        print("Downloading %s" % file_name)
        headers = {"accept": "application/json",
                   "Authorization": "Bearer %s" % 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:
            tcom.file_size = int(total_length)
            dl = 0
            for data in response.iter_content(chunk_size=4096):
                dl += len(data)
                tcom.downloaded = dl
                tcom.progress = int(100 * tcom.downloaded / tcom.file_size)
                f.write(data)
Beispiel #6
0
def timer_update(
):  # TODO might get moved to handle all blenderkit stuff, not to slow down.
    '''check for running and finished downloads and react. write progressbars too.'''
    global download_threads
    if len(download_threads) == 0:
        return 1
    s = bpy.context.scene
    for threaddata in download_threads:
        t = threaddata[0]
        asset_data = threaddata[1]
        tcom = threaddata[2]

        progress_bars = []
        downloaders = []

        if t.is_alive():  # set downloader size
            sr = bpy.context.scene.get('search results')
            if sr is not None:
                for r in sr:
                    if asset_data['id'] == r['id']:
                        r['downloaded'] = tcom.progress

        if not t.is_alive():
            if tcom.error:
                sprops = utils.get_search_props()
                sprops.report = tcom.report
                download_threads.remove(threaddata)
                return
            file_names = paths.get_download_filenames(asset_data)
            wm = bpy.context.window_manager

            at = asset_data['asset_type']
            if ((bpy.context.mode == 'OBJECT' and (at == 'model' \
                                                   or at == 'material'))) \
                    or ((at == 'brush') \
                        and wm.get(
                        'appendable') == True) or at == 'scene':  # don't do this stuff in editmode and other modes, just wait...
                download_threads.remove(threaddata)

                # duplicate file if the global and subdir are used in prefs
                if len(
                        file_names
                ) == 2:  # todo this should try to check if both files exist and are ok.
                    shutil.copyfile(file_names[0], file_names[1])

                utils.p('appending asset')
                # progress bars:

                # we need to check if mouse isn't down, which means an operator can be running.
                # Especially for sculpt mode, where appending a brush during a sculpt stroke causes crasehes
                #

                if tcom.passargs.get('redownload'):
                    # handle lost libraries here:
                    for l in bpy.data.libraries:
                        if l.get('asset_data') is not None and l['asset_data'][
                                'id'] == asset_data['id']:
                            l.filepath = file_names[-1]
                            l.reload()
                else:
                    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
                        if at in ('model', 'material'):
                            download(asset_data, **tcom.passargs)
                        elif asset_data['asset_type'] == 'material':
                            download(asset_data, **tcom.passargs)
                        elif asset_data['asset_type'] == 'scene':
                            download(asset_data, **tcom.passargs)
                        elif asset_data['asset_type'] == 'brush' or asset_data[
                                'asset_type'] == 'texture':
                            download(asset_data, **tcom.passargs)
                    if bpy.context.scene['search results'] is not None and done:
                        for sres in bpy.context.scene['search results']:
                            if asset_data['id'] == sres['id']:
                                sres['downloaded'] = 100

                utils.p('finished download thread')
    return .5
Beispiel #7
0
def append_asset(asset_data, **kwargs):  # downloaders=[], location=None,
    '''Link asset to the scene'''

    file_names = paths.get_download_filenames(asset_data)
    props = None
    #####
    # how to do particle  drop:
    # link the group we are interested in( there are more groups in File!!!! , have to get the correct one!)
    #
    scene = bpy.context.scene

    user_preferences = bpy.context.preferences.addons['blenderkit'].preferences

    if user_preferences.api_key == '':
        user_preferences.asset_counter += 1

    if asset_data['asset_type'] == 'scene':
        scene = append_link.append_scene(file_names[0],
                                         link=False,
                                         fake_user=False)
        props = scene.blenderkit
        parent = scene

    if asset_data['asset_type'] == 'model':
        s = bpy.context.scene
        downloaders = kwargs.get('downloaders')
        s = bpy.context.scene
        sprops = s.blenderkit_models
        # TODO this is here because combinations of linking objects or appending groups are rather not-usefull
        if sprops.append_method == 'LINK_COLLECTION':
            sprops.append_link = 'LINK'
            sprops.import_as = 'GROUP'
        else:
            sprops.append_link = 'APPEND'
            sprops.import_as = 'INDIVIDUAL'

        #copy for override
        al = sprops.append_link
        import_as = sprops.import_as
        # set consistency for objects already in scene, otherwise this literally breaks blender :)
        ain = asset_in_scene(asset_data)
        #override based on history
        if ain is not False:
            if ain == 'LINKED':
                al = 'LINK'
                import_as = 'GROUP'
            else:
                al = 'APPEND'
                import_as = 'INDIVIDUAL'

        # first get conditions for append link
        link = al == 'LINK'
        # then append link
        if downloaders:
            for downloader in downloaders:
                # this cares for adding particle systems directly to target mesh, but I had to block it now,
                # because of the sluggishnes of it. Possibly re-enable when it's possible to do this faster?
                if 0:  # 'particle_plants' in asset_data['tags']:
                    append_link.append_particle_system(
                        file_names[-1],
                        target_object=kwargs['target_object'],
                        rotation=downloader['rotation'],
                        link=False,
                        name=asset_data['name'])
                    return

                if sprops.import_as == 'GROUP':
                    parent, newobs = append_link.link_collection(
                        file_names[-1],
                        location=downloader['location'],
                        rotation=downloader['rotation'],
                        link=link,
                        name=asset_data['name'],
                        parent=kwargs.get('parent'))

                else:
                    parent, newobs = append_link.append_objects(
                        file_names[-1],
                        location=downloader['location'],
                        rotation=downloader['rotation'],
                        link=link,
                        name=asset_data['name'],
                        parent=kwargs.get('parent'))
                if parent.type == 'EMPTY' and link:
                    bmin = asset_data['bbox_min']
                    bmax = asset_data['bbox_max']
                    size_min = min(1.0, (bmax[0] - bmin[0] + bmax[1] -
                                         bmin[1] + bmax[2] - bmin[2]) / 3)
                    parent.empty_display_size = size_min

        elif kwargs.get('model_location') is not None:
            if sprops.import_as == 'GROUP':
                parent, newobs = append_link.link_collection(
                    file_names[-1],
                    location=kwargs['model_location'],
                    rotation=kwargs['model_rotation'],
                    link=link,
                    name=asset_data['name'],
                    parent=kwargs.get('parent'))
            else:
                parent, newobs = append_link.append_objects(
                    file_names[-1],
                    location=kwargs['model_location'],
                    rotation=kwargs['model_rotation'],
                    link=link,
                    parent=kwargs.get('parent'))
            if parent.type == 'EMPTY' and link:
                bmin = asset_data['bbox_min']
                bmax = asset_data['bbox_max']
                size_min = min(1.0, (bmax[0] - bmin[0] + bmax[1] - bmin[1] +
                                     bmax[2] - bmin[2]) / 3)
                parent.empty_display_size = size_min

        if link:
            group = parent.instance_collection

            lib = group.library
            lib['asset_data'] = asset_data

    elif asset_data['asset_type'] == 'brush':

        # TODO if already in scene, should avoid reappending.
        inscene = False
        for b in bpy.data.brushes:

            if b.blenderkit.id == asset_data['id']:
                inscene = True
                brush = b
                break
        if not inscene:
            brush = append_link.append_brush(file_names[-1],
                                             link=False,
                                             fake_user=False)

            thumbnail_name = asset_data['thumbnail'].split(os.sep)[-1]
            tempdir = paths.get_temp_dir('brush_search')
            thumbpath = os.path.join(tempdir, thumbnail_name)
            asset_thumbs_dir = paths.get_download_dirs('brush')[0]
            asset_thumb_path = os.path.join(asset_thumbs_dir, thumbnail_name)
            shutil.copy(thumbpath, asset_thumb_path)
            brush.icon_filepath = asset_thumb_path

        if bpy.context.view_layer.objects.active.mode == 'SCULPT':
            bpy.context.tool_settings.sculpt.brush = brush
        elif bpy.context.view_layer.objects.active.mode == 'TEXTURE_PAINT':  # could be just else, but for future possible more types...
            bpy.context.tool_settings.image_paint.brush = brush
        # TODO set brush by by asset data(user can be downloading while switching modes.)

        # bpy.context.tool_settings.image_paint.brush = brush
        props = brush.blenderkit
        parent = brush

    elif asset_data['asset_type'] == 'material':
        inscene = False
        for m in bpy.data.materials:
            if m.blenderkit.id == asset_data['id']:
                inscene = True
                material = m
                break
        if not inscene:
            material = append_link.append_material(file_names[-1],
                                                   link=False,
                                                   fake_user=False)
        target_object = bpy.data.objects[kwargs['target_object']]

        if len(target_object.material_slots) == 0:
            target_object.data.materials.append(material)
        else:
            target_object.material_slots[
                kwargs['material_target_slot']].material = material

        parent = material

    scene['assets used'] = scene.get('assets used', {})
    scene['assets used'][asset_data['asset_base_id']] = asset_data.copy()

    scene['assets rated'] = scene.get('assets rated', {})

    id = asset_data['asset_base_id']
    scene['assets rated'][id] = scene['assets rated'].get(id, False)

    parent[
        'asset_data'] = asset_data  # TODO remove this??? should write to blenderkit Props?
def download_blenderkit_material(asset_ref):
    from blenderkit import paths, append_link, utils, version_checker, rerequests
    import requests

    def create_asset_data(rdata, asset_type):
        for r in rdata['results']:
            if r['assetType'] == asset_type and len(r['files']) > 0:
                furl = None
                tname = None
                allthumbs = []
                durl, tname = None, None
                for f in r['files']:
                    if f['fileType'] == 'thumbnail':
                        tname = paths.extract_filename_from_url(
                            f['fileThumbnailLarge'])
                        small_tname = paths.extract_filename_from_url(
                            f['fileThumbnail'])
                        allthumbs.append(
                            tname)  # TODO just first thumb is used now.
                    tdict = {}
                    for i, t in enumerate(allthumbs):
                        tdict['thumbnail_%i'] = t
                    if f['fileType'] == 'blend':
                        durl = f['downloadUrl'].split('?')[0]
                        # fname = paths.extract_filename_from_url(f['filePath'])
                if durl and tname:
                    tooltip = blenderkit.search.generate_tooltip(r)
                    r['author']['id'] = str(r['author']['id'])
                    asset_data = {
                        'thumbnail': tname,
                        'thumbnail_small': small_tname,
                        # 'thumbnails':allthumbs,
                        'download_url': durl,
                        'id': r['id'],
                        'asset_base_id': r['assetBaseId'],
                        'name': r['name'],
                        'asset_type': r['assetType'],
                        'tooltip': tooltip,
                        'tags': r['tags'],
                        'can_download': r.get('canDownload', True),
                        'verification_status': r['verificationStatus'],
                        'author_id': r['author']['id'],
                        # 'author': r['author']['firstName'] + ' ' + r['author']['lastName']
                        # 'description': r['description'],
                    }
                    asset_data['downloaded'] = 0
                    # parse extra params needed for blender here
                    params = utils.params_to_dict(r['parameters'])
                    if asset_type == 'model':
                        if params.get('boundBoxMinX') != None:
                            bbox = {
                                'bbox_min': (float(params['boundBoxMinX']),
                                             float(params['boundBoxMinY']),
                                             float(params['boundBoxMinZ'])),
                                'bbox_max': (float(params['boundBoxMaxX']),
                                             float(params['boundBoxMaxY']),
                                             float(params['boundBoxMaxZ']))
                            }
                        else:
                            bbox = {
                                'bbox_min': (-.5, -.5, 0),
                                'bbox_max': (.5, .5, 1)
                            }
                        asset_data.update(bbox)
                    if asset_type == 'material':
                        asset_data['texture_size_meters'] = params.get(
                            'textureSizeMeters', 1.0)
                    asset_data.update(tdict)
            r.update(asset_data)

    # main
    asset_base_id_str, asset_type_str = asset_ref.split()
    asset_type = asset_type_str.split(':')[1]
    scene_id = blenderkit.download.get_scene_id()
    reqstr = '?query=%s+%s+order:_score' % (asset_base_id_str, asset_type_str)
    reqstr += '&addon_version=%s' % version_checker.get_addon_version()
    reqstr += '&scene_uuid=%s' % scene_id
    url = paths.get_api_url() + 'search/' + reqstr
    api_key = user_preferences = C.preferences.addons[
        'blenderkit'].preferences.api_key
    headers = utils.get_headers(api_key)
    r = rerequests.get(url, headers=headers)
    rdata = r.json()
    create_asset_data(rdata, asset_type)
    asset_data = rdata['results'][0]
    has_url = blenderkit.download.get_download_url(asset_data, scene_id,
                                                   api_key)
    file_names = paths.get_download_filenames(asset_data)
    file_name = file_names[0]
    if not os.path.exists(file_name):
        with open(file_name, "wb") as f:
            print("Downloading %s" % file_name)
            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)
                    f.write(data)
    material = append_link.append_material(file_names[-1])
    return material
Beispiel #9
0
def append_asset2(asset_data, **kwargs):
    '''Link asset to the scene'''

    file_names = paths.get_download_filenames(asset_data)
    scene = bpy.context.scene

    user_preferences = bpy.context.preferences.addons['blenderkit'].preferences

    if user_preferences.api_key == '':
        user_preferences.asset_counter += 1

    if asset_data['asset_type'] == 'scene':
        scene = append_link.append_scene(file_names[0],
                                         link=False,
                                         fake_user=False)
        parent = scene

    if asset_data['asset_type'] == 'model':
        s = bpy.context.scene
        downloaders = kwargs.get('downloaders')
        s = bpy.context.scene
        sprops = s.blenderkit_models
        if sprops.append_method == 'LINK_COLLECTION':
            sprops.append_link = 'LINK'
            sprops.import_as = 'GROUP'
        else:
            sprops.append_link = 'APPEND'
            sprops.import_as = 'INDIVIDUAL'

        al = sprops.append_link
        ain = asset_in_scene(asset_data)
        if ain is not False:
            if ain == 'LINKED':
                al = 'LINK'
            else:
                al = 'APPEND'

        link = al == 'LINK'
        if downloaders:
            for downloader in downloaders:
                if link is True:
                    parent, newobs = append_link.link_collection(
                        file_names[-1],
                        location=downloader['location'],
                        rotation=downloader['rotation'],
                        link=link,
                        name=asset_data['name'],
                        parent=kwargs.get('parent'))
                else:
                    parent, newobs = append_link.append_objects(
                        file_names[-1],
                        location=downloader['location'],
                        rotation=downloader['rotation'],
                        link=link,
                        name=asset_data['name'],
                        parent=kwargs.get('parent'))

                if parent.type == 'EMPTY' and link:
                    bmin = asset_data['bbox_min']
                    bmax = asset_data['bbox_max']
                    size_min = min(1.0, (bmax[0] - bmin[0] + bmax[1] -
                                         bmin[1] + bmax[2] - bmin[2]) / 3)
                    parent.empty_display_size = size_min

        elif kwargs.get('model_location') is not None:
            if link is True:
                parent, newobs = append_link.link_collection(
                    file_names[-1],
                    location=kwargs['model_location'],
                    rotation=kwargs['model_rotation'],
                    link=link,
                    name=asset_data['name'],
                    parent=kwargs.get('parent'))
            else:
                parent, newobs = append_link.append_objects(
                    file_names[-1],
                    location=kwargs['model_location'],
                    rotation=kwargs['model_rotation'],
                    link=link,
                    parent=kwargs.get('parent'))
            if parent.type == 'EMPTY' and link:
                bmin = asset_data['bbox_min']
                bmax = asset_data['bbox_max']
                size_min = min(1.0, (bmax[0] - bmin[0] + bmax[1] - bmin[1] +
                                     bmax[2] - bmin[2]) / 3)
                parent.empty_display_size = size_min

        if link:
            group = parent.instance_collection

            lib = group.library
            lib['asset_data'] = asset_data

    elif asset_data['asset_type'] == 'brush':

        inscene = False
        for b in bpy.data.brushes:
            if b.blenderkit.id == asset_data['id']:
                inscene = True
                brush = b
                break
        if not inscene:
            brush = append_link.append_brush(file_names[-1],
                                             link=False,
                                             fake_user=False)

            thumbnail_name = asset_data['thumbnail'].split(os.sep)[-1]
            tempdir = paths.get_temp_dir('brush_search')
            thumbpath = os.path.join(tempdir, thumbnail_name)
            asset_thumbs_dir = paths.get_download_dirs('brush')[0]
            asset_thumb_path = os.path.join(asset_thumbs_dir, thumbnail_name)
            shutil.copy(thumbpath, asset_thumb_path)
            brush.icon_filepath = asset_thumb_path

        if bpy.context.view_layer.objects.active.mode == 'SCULPT':
            bpy.context.tool_settings.sculpt.brush = brush
        elif bpy.context.view_layer.objects.active.mode == 'TEXTURE_PAINT':
            bpy.context.tool_settings.image_paint.brush = brush

        parent = brush

    elif asset_data['asset_type'] == 'material':
        inscene = False
        for m in bpy.data.materials:
            if m.blenderkit.id == asset_data['id']:
                inscene = True
                material = m
                break
        if not inscene:
            material = append_link.append_material(file_names[-1],
                                                   link=False,
                                                   fake_user=False)
        target_object = bpy.data.objects[kwargs['target_object']]

        if len(target_object.material_slots) == 0:
            target_object.data.materials.append(material)
        else:
            target_object.material_slots[
                kwargs['material_target_slot']].material = material

        parent = material

    scene['assets used'] = scene.get('assets used', {})
    scene['assets used'][asset_data['asset_base_id']] = asset_data.copy()

    scene['assets rated'] = scene.get('assets rated', {})

    id = asset_data['asset_base_id']
    scene['assets rated'][id] = scene['assets rated'].get(id, False)

    parent['asset_data'] = asset_data

    if hasattr(parent.blenderkit, 'tags') and 'tags' in asset_data:
        asset_data['tags'].remove('non-manifold')
        parent.blenderkit.tags = ','.join(asset_data['tags'])
    if hasattr(parent.blenderkit,
               'description') and 'description' in asset_data:
        if asset_data['description'] is not None:
            parent.blenderkit.description = asset_data['description']
    if hasattr(parent.blenderkit, 'custom_props') and 'metadata' in asset_data:
        if 'product_info' in asset_data['metadata']:
            product_info = asset_data['metadata'].pop('product_info')
            clients = []
            skus = []
            for client_sku in product_info:
                clients.append(client_sku['client'])
                skus.append(client_sku['sku'])
            if hasattr(parent.blenderkit, 'client') and hasattr(
                    parent.blenderkit, 'sku'):
                parent.blenderkit.client = ','.join(clients)
                parent.blenderkit.sku = ','.join(skus)
            else:
                parent.blenderkit.custom_props['client'] = ','.join(clients)
                parent.blenderkit.custom_props['sku'] = ','.join(skus)

        for key, value in asset_data['metadata'].items():
            parent.blenderkit.custom_props[key] = value

    bpy.ops.wm.undo_push_context(message='add %s to scene' %
                                 asset_data['name'])