asset_library/plugins/kitsu.py

270 lines
8.7 KiB
Python

"""
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 .library_plugin import LibraryPlugin
from ..core.template import Template
from ..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