""" Plugin for making an asset library of all blender file found in a folder """ import re from pathlib import Path from itertools import groupby import uuid import os import shutil import json import urllib3 import traceback import time import bpy from bpy.props import (StringProperty, IntProperty, BoolProperty) from asset_library.plugins.library_plugin import LibraryPlugin from asset_library.core.template import Template from asset_library.core.file_utils import install_module class Kitsu(LibraryPlugin): name = "Kitsu" template_name : StringProperty() template_file : StringProperty() source_directory : StringProperty(subtype='DIR_PATH') #blend_depth: IntProperty(default=1) source_template_image : StringProperty() target_template_image : StringProperty() url: StringProperty() login: StringProperty() password: StringProperty(subtype='PASSWORD') project_name: StringProperty() def connect(self, url=None, login=None, password=None): '''Connect to kitsu api using provided url, login and password''' gazu = install_module('gazu') urllib3.disable_warnings() if not self.url: print(f'Kitsu Url: {self.url} is empty') return url = self.url if not url.endswith('/api'): url += '/api' print(f'Info: Setting Host for kitsu {url}') gazu.client.set_host(url) if not gazu.client.host_is_up(): print('Error: Kitsu Host is down') try: print(f'Info: Log in to kitsu as {self.login}') res = gazu.log_in(self.login, self.password) print(f'Info: Sucessfully login to Kitsu as {res["user"]["full_name"]}') return res['user'] except Exception as e: print(f'Error: {traceback.format_exc()}') def get_asset_path(self, name, catalog, directory=None): directory = directory or self.source_directory return Path(directory, self.get_asset_relative_path(name, catalog)) def get_asset_info(self, data, asset_path): modified = time.time_ns() catalog = data['entity_type_name'].title() asset_path = self.prop_rel_path(asset_path, 'source_directory') #asset_name = self.norm_file_name(data['name']) asset_info = dict( filepath=asset_path, modified=modified, library_id=self.library.id, assets=[dict( catalog=catalog, metadata=data.get('data', {}), description=data['description'], tags=[], type=self.data_type, #image=self.library.template_image, #video=self.library.template_video, name=data['name']) ] ) return asset_info # def bundle(self, cache_diff=None): # """Group all asset in one or multiple blends for the asset browser""" # return super().bundle(cache_diff=cache_diff) def set_asset_preview(self, asset, asset_data): '''Load an externalize image as preview for an asset using the source template''' asset_path = self.format_path(Path(asset_data['filepath']).as_posix()) image_path = self.find_path(self.target_template_image, asset_data, filepath=asset_path) if image_path: with bpy.context.temp_override(id=asset): bpy.ops.ed.lib_id_load_custom_preview( filepath=str(image_path) ) else: print(f'No image found for {self.target_template_image} on {asset.name}') if asset.preview: return asset.preview def generate_previews(self, cache=None): print('Generate previews...') if cache in (None, ''): cache = self.fetch() elif isinstance(cache, (Path, str)): cache = self.read_cache(cache) #TODO Support all multiple data_type for asset_info in cache: if asset_info.get('type', self.data_type) == 'FILE': self.generate_blend_preview(asset_info) else: self.generate_asset_preview(asset_info) def generate_asset_preview(self, asset_info): data_type = self.data_type scn = bpy.context.scene vl = bpy.context.view_layer asset_path = self.format_path(asset_info['filepath']) lens = 85 if not asset_path.exists(): print(f'Blend file {asset_path} not exit') return asset_data_names = {} # First check wich assets need a preview for asset_data in asset_info['assets']: name = asset_data['name'] image_path = self.format_path(self.target_template_image, asset_data, filepath=asset_path) if image_path.exists(): continue #Store in a dict all asset_data that does not have preview asset_data_names[name] = dict(asset_data, image_path=image_path) if not asset_data_names: print(f'All previews already existing for {asset_path}') return #asset_names = [a['name'] for a in asset_info['assets']] asset_names = list(asset_data_names.keys()) assets = self.load_datablocks(asset_path, names=asset_names, link=True, type=data_type) print(asset_names) print(assets) for asset in assets: if not asset: continue print(f'Generate Preview for asset {asset.name}') asset_data = asset_data_names[asset.name] #print(self.target_template_image, asset_path) image_path = self.format_path(self.target_template_image, asset_data, filepath=asset_path) # Force redo preview # if asset.preview: # print(f'Writing asset preview to {image_path}') # self.write_preview(asset.preview, image_path) # continue if data_type == 'COLLECTION': bpy.ops.object.collection_instance_add(name=asset.name) scn.camera.data.lens = lens bpy.ops.view3d.camera_to_view_selected() scn.camera.data.lens -= 5 instance = vl.objects.active #scn.collection.children.link(asset) scn.render.filepath = str(image_path) scn.render.image_settings.file_format = self.format_from_ext(image_path.suffix) scn.render.image_settings.color_mode = 'RGBA' scn.render.image_settings.quality = 90 print(f'Render asset {asset.name} to {image_path}') bpy.ops.render.render(write_still=True) #instance.user_clear() asset.user_clear() bpy.data.objects.remove(instance) bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True) def fetch(self): """Gather in a list all assets found in the folder""" print(f'Fetch Assets for {self.library.name}') gazu = install_module('gazu') self.connect() template_file = Template(self.template_file) template_name = Template(self.template_name) project = gazu.client.fetch_first('projects', {'name': self.project_name}) entity_types = gazu.client.fetch_all('entity-types') entity_types_ids = {e['id']: e['name'] for e in entity_types} cache = self.read_cache() for asset_data in gazu.asset.all_assets_for_project(project): asset_data['entity_type_name'] = entity_types_ids[asset_data.pop('entity_type_id')] asset_name = asset_data['name'] asset_field_data = dict(asset_name=asset_name, type=asset_data['entity_type_name'], source_directory=self.source_directory) try: asset_field_data.update(template_name.parse(asset_name)) except Exception: print(f'Warning: Could not parse {asset_name} with template {template_name}') asset_path = template_file.find(asset_field_data) if not asset_path: print(f'Warning: Could not find file for {template_file.format(asset_field_data)}') continue asset_path = self.prop_rel_path(asset_path, 'source_directory') asset_cache_data = dict( catalog=asset_data['entity_type_name'].title(), metadata=asset_data.get('data', {}), description=asset_data['description'], tags=[], type=self.data_type, name=asset_data['name'] ) cache.add_asset_cache(asset_cache_data, filepath=asset_path) return cache