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
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
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)
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
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
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'])