continue refactoring
parent
fc405797d9
commit
575bbade7b
|
@ -1032,7 +1032,7 @@ class ACTIONLIB_OT_store_anim_pose(Operator):
|
||||||
modified=time.time_ns()
|
modified=time.time_ns()
|
||||||
)
|
)
|
||||||
|
|
||||||
lib.adapter.write_asset_description(asset_description, asset_path)
|
lib.adapter.write_description_file(asset_description, asset_path)
|
||||||
|
|
||||||
# Restore action and cleanup
|
# Restore action and cleanup
|
||||||
ob.animation_data.action = current_action
|
ob.animation_data.action = current_action
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
from asset_library.common.functions import (read_catalog, write_catalog, norm_asset_datas, get_catalog_path)
|
from asset_library.common.functions import (read_catalog, write_catalog, norm_asset_datas)
|
||||||
from asset_library.common.bl_utils import get_addon_prefs, load_datablocks
|
from asset_library.common.bl_utils import get_addon_prefs, load_datablocks
|
||||||
from asset_library.common.file_utils import read_file, write_file
|
from asset_library.common.file_utils import read_file, write_file
|
||||||
from asset_library.common.template import Template
|
from asset_library.common.template import Template
|
||||||
|
from asset_library.constants import (PREVIEW_ASSETS_SCRIPT, MODULE_DIR)
|
||||||
|
|
||||||
from asset_library import (action, collection, file)
|
from asset_library import (action, collection, file)
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ import json
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
import time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
class AssetLibraryAdapter(PropertyGroup):
|
class AssetLibraryAdapter(PropertyGroup):
|
||||||
|
@ -26,7 +28,7 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
name = "Base Adapter"
|
name = "Base Adapter"
|
||||||
#library = None
|
#library = None
|
||||||
|
|
||||||
bundle_directory : StringProperty()
|
#bundle_directory : StringProperty()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def library(self):
|
def library(self):
|
||||||
|
@ -37,29 +39,9 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
if lib.conform.adapter == self:
|
if lib.conform.adapter == self:
|
||||||
return lib
|
return lib
|
||||||
|
|
||||||
@property
|
#@property
|
||||||
def library_path(self):
|
#def library_path(self):
|
||||||
return self.library.library_path
|
# return self.library.library_path
|
||||||
|
|
||||||
@property
|
|
||||||
def image_template(self):
|
|
||||||
return Template(self.library.image_template)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def video_template(self):
|
|
||||||
return Template(self.library.video_template)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def asset_description_template(self):
|
|
||||||
return Template(self.library.asset_description_template)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data_type(self):
|
|
||||||
return self.library.data_type
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data_types(self):
|
|
||||||
return self.library.data_types
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_conform(self):
|
def is_conform(self):
|
||||||
|
@ -70,29 +52,70 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
if lib.conform.adapter == self:
|
if lib.conform.adapter == self:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_directory(self):
|
||||||
|
if self.is_conform:
|
||||||
|
return self.library.conform.directory
|
||||||
|
|
||||||
|
return self.library.bundle_dir
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def blend_depth(self):
|
def blend_depth(self):
|
||||||
if self.is_conform:
|
if self.is_conform:
|
||||||
return self.library.conform.blend_depth
|
return self.library.conform.blend_depth
|
||||||
|
|
||||||
return self.library.blend_depth
|
return self.library.blend_depth
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template_image(self):
|
||||||
|
return Template(self.library.conform.template_image)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def externalize_data(self):
|
def template_video(self):
|
||||||
return self.library.externalize_data
|
return Template(self.library.conform.template_video)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def catalog_path(self):
|
def template_description(self):
|
||||||
return self.library.catalog_path
|
return Template(self.library.conform.template_description)
|
||||||
|
|
||||||
|
|
||||||
def get_catalog_path(self, filepath):
|
@property
|
||||||
return get_catalog_path(filepath)
|
def data_type(self):
|
||||||
|
return self.library.data_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_types(self):
|
||||||
|
return self.library.data_types
|
||||||
|
|
||||||
|
#@property
|
||||||
|
#def externalize_data(self):
|
||||||
|
# return self.library.externalize_data
|
||||||
|
|
||||||
|
#@property
|
||||||
|
#def catalog_path(self):
|
||||||
|
# return self.library.catalog_path
|
||||||
|
|
||||||
|
def get_catalog_path(self, directory=None):
|
||||||
|
directory = directory or self.target_directory
|
||||||
|
return Path(directory, 'blender_assets.cats.txt')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cache_file(self):
|
def cache_file(self):
|
||||||
return Path(self.library_path) / f"blender_assets.{self.library.id}.json"
|
return Path(self.target_directory) / f"blender_assets.{self.library.id}.json"
|
||||||
#return get_asset_datas_file(self.library_path)
|
#return get_asset_datas_file(self.library_path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def diff_file(self):
|
||||||
|
return Path(bpy.app.tempdir, 'diff.json')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def preview_blend(self):
|
||||||
|
return MODULE_DIR / self.data_type.lower() / "preview.blend"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def preview_assets_file(self):
|
||||||
|
return Path(bpy.app.tempdir, "preview_assets_file.json")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def addon_prefs(self):
|
def addon_prefs(self):
|
||||||
return get_addon_prefs()
|
return get_addon_prefs()
|
||||||
|
@ -116,6 +139,12 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
def norm_file_name(self, name):
|
def norm_file_name(self, name):
|
||||||
return name.replace(' ', '_')
|
return name.replace(' ', '_')
|
||||||
|
|
||||||
|
def read_file(self, file):
|
||||||
|
return read_file(file)
|
||||||
|
|
||||||
|
def write_file(self, file, data):
|
||||||
|
return write_file(file, data)
|
||||||
|
|
||||||
def copy_file(self, source, destination):
|
def copy_file(self, source, destination):
|
||||||
src = Path(source)
|
src = Path(source)
|
||||||
dst = Path(destination)
|
dst = Path(destination)
|
||||||
|
@ -185,6 +214,31 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
|
|
||||||
return asset_path
|
return asset_path
|
||||||
|
|
||||||
|
def get_template_path(self, template, name, asset_path, catalog):
|
||||||
|
|
||||||
|
if template.startswith('.'): #the template is relative
|
||||||
|
template = Path(asset_path, template).as_posix()
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'name': name,
|
||||||
|
'asset_path': Path(asset_path),
|
||||||
|
'catalog': catalog,
|
||||||
|
'catalog_name': catalog.replace('/', '_'),
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.format_path(template, **params)
|
||||||
|
|
||||||
|
def get_description_path(self, name, asset_path, catalog) -> Path:
|
||||||
|
""""Get the path of the json or yaml describing all assets data in one file"""
|
||||||
|
return self.get_template_path(self.library.conform.template_description, name, asset_path, catalog)
|
||||||
|
|
||||||
|
def get_image_path(self, name, asset_path, catalog) -> Path:
|
||||||
|
return self.get_template_path(self.library.conform.template_image, name, asset_path, catalog)
|
||||||
|
|
||||||
|
def get_video_path(self, name, asset_path, catalog) -> Path:
|
||||||
|
return self.get_template_path(self.library.conform.template_video, name, asset_path, catalog)
|
||||||
|
|
||||||
|
'''
|
||||||
def get_path(self, type, name, asset_path, template=None) -> Path:
|
def get_path(self, type, name, asset_path, template=None) -> Path:
|
||||||
if not template:
|
if not template:
|
||||||
template = getattr(self, f'{type}_template')
|
template = getattr(self, f'{type}_template')
|
||||||
|
@ -193,27 +247,40 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
template = Template(template)
|
template = Template(template)
|
||||||
|
|
||||||
filepath = Path(asset_path)
|
filepath = Path(asset_path)
|
||||||
return (filepath / template.format(name=name, path=Path(asset_path))).resolve()
|
|
||||||
|
params = {
|
||||||
|
'bundle_dir': self.library.bundle_directory,
|
||||||
|
'conform_dir': self.library.conform.directory,
|
||||||
|
'rel_path': '',
|
||||||
|
'catalog':'',
|
||||||
|
'catalog_name':'',
|
||||||
|
'name': name
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.format_path(template, params)#(filepath / template.format(name=name, path=Path(asset_path))).resolve()
|
||||||
|
|
||||||
#def get_image_path(self, name, asset_path):
|
#def get_image_path(self, name, asset_path):
|
||||||
# filepath = Path(asset_path)
|
# filepath = Path(asset_path)
|
||||||
# image_name = self._get_file_name(name, asset_path)
|
# image_name = self._get_file_name(name, asset_path)
|
||||||
# return (filepath / self.image_template.format(name=image_name)).resolve()
|
# return (filepath / self.template_image.format(name=image_name)).resolve()
|
||||||
|
|
||||||
|
|
||||||
def get_cache_image_path(self, name, catalog) -> Path:
|
def get_cache_image_path(self, name, catalog) -> Path:
|
||||||
""""Get the the cache path of a image for asset without an externalized image"""
|
""""Get the the cache path of a image for asset without an externalized image"""
|
||||||
|
name = self.norm_file_name(name)
|
||||||
return Path(self.library_path, '.previews', f"{catalog.replace('/', '_')}_{name}").with_suffix(('.png'))
|
return Path(self.library_path, '.previews', f"{catalog.replace('/', '_')}_{name}").with_suffix(('.png'))
|
||||||
|
|
||||||
def get_cache_image(self, name, catalog):
|
def get_cache_image(self, name, catalog):
|
||||||
cache_image_path = self.get_cache_image_path(name, catalog)
|
cache_image_path = self.get_cache_image_path(name, catalog)
|
||||||
if cache_image_path.exists():
|
if cache_image_path.exists():
|
||||||
return cache_image_path
|
return cache_image_path
|
||||||
|
'''
|
||||||
|
|
||||||
#def get_video_path(self, name, asset_path):
|
#def get_video_path(self, name, asset_path):
|
||||||
# filepath = Path(asset_path)
|
# filepath = Path(asset_path)
|
||||||
# video_name = self._get_file_name(name, asset_path)
|
# video_name = self._get_file_name(name, asset_path)
|
||||||
# return (filepath / self.video_template.format(name=video_name)).resolve()
|
# return (filepath / self.template_video.format(name=video_name)).resolve()
|
||||||
|
'''
|
||||||
def get_image(self, name, asset_path):
|
def get_image(self, name, asset_path):
|
||||||
image_path = self.get_path('image', name, asset_path)
|
image_path = self.get_path('image', name, asset_path)
|
||||||
if image_path.exists():
|
if image_path.exists():
|
||||||
|
@ -223,21 +290,19 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
video_path = self.get_path('video', name, asset_path)
|
video_path = self.get_path('video', name, asset_path)
|
||||||
if video_path.exists():
|
if video_path.exists():
|
||||||
return video_path
|
return video_path
|
||||||
|
'''
|
||||||
|
|
||||||
def get_asset_description_path(self, asset_path) -> Path:
|
|
||||||
""""Get the path of the json or yaml describing all assets data in onle file"""
|
|
||||||
filepath = Path(asset_path)
|
|
||||||
return (filepath / self.asset_description_template.format(name=filepath.stem)).resolve()
|
|
||||||
|
|
||||||
def read_asset_description(self, asset_path) -> dict:
|
|
||||||
|
def read_asset_description_file(self, asset_path) -> dict:
|
||||||
"""Read the description file of the asset"""
|
"""Read the description file of the asset"""
|
||||||
|
|
||||||
asset_description_path = self.get_asset_description_path(asset_path)
|
description_path = self.get_description_path(asset_path)
|
||||||
return read_file(asset_description_path)
|
return self.read_file(description_path)
|
||||||
|
|
||||||
def write_asset_description(self, asset_data, asset_path) -> None:
|
def write_description_file(self, asset_data, asset_path) -> None:
|
||||||
asset_description_path = self.get_asset_description_path(asset_path)
|
description_path = self.get_description_path(asset_path)
|
||||||
return write_file(asset_description_path, asset_data)
|
return write_file(description_path, asset_data)
|
||||||
|
|
||||||
def write_asset(self, asset, asset_path):
|
def write_asset(self, asset, asset_path):
|
||||||
bpy.data.libraries.write(
|
bpy.data.libraries.write(
|
||||||
|
@ -247,27 +312,48 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
fake_user=True,
|
fake_user=True,
|
||||||
compress=True
|
compress=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def read_catalog(self, filepath=None):
|
def read_catalog(self, directory=None):
|
||||||
"""Read the catalog file of the library bundle path or of the specified filepath"""
|
"""Read the catalog file of the library target directory or of the specified directory"""
|
||||||
|
catalog_path = self.get_catalog_path(directory)
|
||||||
|
|
||||||
catalog_path = self.catalog_path
|
cat_data = {}
|
||||||
if filepath:
|
|
||||||
catalog_path = self.get_catalog_path(filepath)
|
|
||||||
return read_catalog(catalog_path)
|
|
||||||
|
|
||||||
def write_catalog(self, catalog_data, filepath=None):
|
for line in catalog_path.read_text(encoding="utf-8").split('\n'):
|
||||||
"""Write the catalog file in the library bundle path or of the specified filepath"""
|
if line.startswith(('VERSION', '#')) or not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cat_id, cat_path, cat_name = line.split(':')
|
||||||
|
cat_data[cat_path] = {'id':cat_id, 'name':cat_name}
|
||||||
|
|
||||||
catalog_path = self.catalog_path
|
return cat_data
|
||||||
if filepath:
|
|
||||||
catalog_path = self.get_catalog_path(filepath)
|
def write_catalog(self, catalog_data, directory=None):
|
||||||
|
"""Write the catalog file in the library target directory or of the specified directory"""
|
||||||
|
|
||||||
return write_catalog(catalog_path, catalog_data)
|
catalog_path = self.get_catalog_path(directory)
|
||||||
|
|
||||||
|
lines = ['VERSION 1', '']
|
||||||
|
|
||||||
|
# Add missing parents catalog
|
||||||
|
norm_data = {}
|
||||||
|
for cat_path, cat_data in catalog_data.items():
|
||||||
|
norm_data[cat_path] = cat_data
|
||||||
|
for p in Path(cat_path).parents[:-1]:
|
||||||
|
if p in data or p in norm_data:
|
||||||
|
continue
|
||||||
|
|
||||||
|
norm_data[p.as_posix()] = {'id': str(uuid.uuid4()), 'name': '-'.join(p.parts)}
|
||||||
|
|
||||||
|
for cat_path, cat_data in sorted(norm_data.items()):
|
||||||
|
cat_name = cat_data['name'].replace('/', '-')
|
||||||
|
lines.append(f"{cat_data['id']}:{cat_path}:{cat_name}")
|
||||||
|
|
||||||
|
print(f'Catalog writen at: {catalog_path}')
|
||||||
|
catalog_path.write_text('\n'.join(lines), encoding="utf-8")
|
||||||
|
|
||||||
def read_cache(self):
|
def read_cache(self):
|
||||||
return read_file(self.cache_file)
|
return self.read_file(self.cache_file)
|
||||||
|
|
||||||
def norm_asset_datas(self, asset_file_datas):
|
def norm_asset_datas(self, asset_file_datas):
|
||||||
''' Return a new flat list of asset data
|
''' Return a new flat list of asset data
|
||||||
|
@ -326,8 +412,148 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
|
|
||||||
return catalog_parts[:self.blend_depth]
|
return catalog_parts[:self.blend_depth]
|
||||||
|
|
||||||
|
#def transfert_preview(self, )
|
||||||
|
|
||||||
|
'''
|
||||||
|
def generate_previews(self, assets, callback):
|
||||||
|
def _generate_previews(assets, callback, src_assets=None):
|
||||||
|
if src_assets:
|
||||||
|
src_assets = []
|
||||||
|
|
||||||
|
if bpy.app.is_job_running('RENDER_PREVIEW'):
|
||||||
|
print("Waiting for render...")
|
||||||
|
return 0.2 # waiting time
|
||||||
|
|
||||||
|
while assets: # generate next preview
|
||||||
|
asset = assets.pop()
|
||||||
|
#print(f"Creating preview for world {world.name}...")
|
||||||
|
|
||||||
|
asset_path = asset.asset_data['filepath']
|
||||||
|
src_asset = self.load_datablocks(asset_path, names=asset.name, link=False, type=self.data_types)
|
||||||
|
if not src_asset:
|
||||||
|
#print(f'No asset named {asset.name} in {asset_path]}')
|
||||||
|
return
|
||||||
|
|
||||||
|
src_assets.append(src_asset)
|
||||||
|
# # set image in the preview object's material
|
||||||
|
# obj = bpy.context.active_object
|
||||||
|
# image = world.node_tree.nodes['Environment Texture'].image
|
||||||
|
# obj.material_slots[0].material.node_tree.nodes['Image Texture'].image = image
|
||||||
|
if self.data_type == 'COLLECTION':
|
||||||
|
asset.children.link(src_asset)
|
||||||
|
|
||||||
|
# start preview render
|
||||||
|
with bpy.context.temp_override(id=asset):
|
||||||
|
bpy.ops.ed.lib_id_generate_preview()
|
||||||
|
return 0.2
|
||||||
|
|
||||||
|
for asset in src_asset:
|
||||||
|
asset.user_clear()
|
||||||
|
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||||
|
|
||||||
|
callback()
|
||||||
|
return None
|
||||||
|
|
||||||
|
assets = assets.copy()
|
||||||
|
|
||||||
|
# create preview images
|
||||||
|
bpy.app.timers.register(
|
||||||
|
functools.partial(
|
||||||
|
_generate_previews,
|
||||||
|
assets,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
def generate_preview(self, asset_description):
|
||||||
|
"""Only generate preview when conforming a library"""
|
||||||
|
|
||||||
|
#print('generate_preview', filepath, asset_names, data_type)
|
||||||
|
|
||||||
|
scn = bpy.context.scene
|
||||||
|
#Creating the preview for collection, object or material
|
||||||
|
camera = scn.camera
|
||||||
|
vl = bpy.context.view_layer
|
||||||
|
|
||||||
|
data_type = self.data_type #asset_description['data_type']
|
||||||
|
asset_path = asset_description['filepath']
|
||||||
|
asset_data_names = {}
|
||||||
|
for asset_data in asset_description['assets']:
|
||||||
|
|
||||||
|
name = asset_data['name']
|
||||||
|
catalog = asset_data['catalog']
|
||||||
|
|
||||||
|
image_path = self.get_image_path(name, asset_path, catalog)
|
||||||
|
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:
|
||||||
|
# No preview to generate
|
||||||
|
return
|
||||||
|
|
||||||
|
asset_names = list(asset_data_names.keys())
|
||||||
|
assets = self.load_datablocks(asset_path, names=asset_names, link=True, type=data_type)
|
||||||
|
|
||||||
|
for asset in assets:
|
||||||
|
if not asset:
|
||||||
|
continue
|
||||||
|
|
||||||
|
asset_data = asset_data_names[asset.name]
|
||||||
|
image_path = asset_data['image_path']
|
||||||
|
if data_type == 'COLLECTION':
|
||||||
|
|
||||||
|
bpy.ops.object.collection_instance_add(name=asset.name)
|
||||||
|
|
||||||
|
bpy.ops.view3d.camera_to_view_selected()
|
||||||
|
instance = vl.objects.active
|
||||||
|
|
||||||
|
#scn.collection.children.link(asset)
|
||||||
|
|
||||||
|
scn.render.filepath = str(image_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.object.delete(use_global=False)
|
||||||
|
|
||||||
|
#scn.collection.children.unlink(asset)
|
||||||
|
|
||||||
|
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||||
|
|
||||||
|
def generate_previews(self):
|
||||||
|
cache = self.fetch()
|
||||||
|
|
||||||
|
#cache_diff.sort(key=lambda x :x['filepath'])
|
||||||
|
#blend_groups = groupby(cache_diff, key=lambda x :x['filepath'])
|
||||||
|
|
||||||
|
#TODO Support all multiple data_type
|
||||||
|
for asset_description in cache:
|
||||||
|
self.generate_preview(asset_description)
|
||||||
|
|
||||||
|
# filepath = asset_description['filepath']
|
||||||
|
|
||||||
|
# asset_datas = asset_description["assets"]
|
||||||
|
|
||||||
|
# asset_datas.sort(key=lambda x :x.get('type', self.data_type))
|
||||||
|
# data_type_groups = groupby(asset_datas, key=lambda x :x.get('type', self.data_type))
|
||||||
|
|
||||||
|
# for data_type, same_type_asset_datas in data_type_groups:
|
||||||
|
|
||||||
|
# asset_names = [a['name'] for a in same_type_asset_datas]
|
||||||
|
# self.generate_preview(filepath, asset_names, data_type)
|
||||||
|
|
||||||
def set_asset_preview(self, asset, asset_data):
|
def set_asset_preview(self, asset, asset_data):
|
||||||
"""Load an externalize image as preview for an asset"""
|
'''Load an externalize image as preview for an asset'''
|
||||||
|
|
||||||
image_path = Path(asset_data['image'])
|
image_path = Path(asset_data['image'])
|
||||||
if not image_path.is_absolute():
|
if not image_path.is_absolute():
|
||||||
|
@ -339,34 +565,52 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
bpy.ops.ed.lib_id_load_custom_preview(
|
bpy.ops.ed.lib_id_load_custom_preview(
|
||||||
filepath=str(image_path)
|
filepath=str(image_path)
|
||||||
)
|
)
|
||||||
return
|
|
||||||
|
|
||||||
if asset.preview:
|
if asset.preview:
|
||||||
return
|
return asset.preview
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
#Creating the preview for collection, object or material
|
#Creating the preview for collection, object or material
|
||||||
src_asset = self.load_datablocks(asset_data['filepath'], names=asset_data['name'], link=False, type=self.data_types)
|
src_asset = self.load_datablocks(asset_data['filepath'], names=asset_data['name'], link=False, type=self.data_types)
|
||||||
if not src_asset:
|
if not src_asset:
|
||||||
print(f'No asset named {asset_data["name"]} in {asset_data["filepath"]}')
|
print(f'No asset named {asset_data["name"]} in {asset_data["filepath"]}')
|
||||||
return
|
return
|
||||||
|
|
||||||
bpy.ops.ed.lib_id_generate_preview({"id": src_asset})
|
if not self.data_type == 'COLLECTION':
|
||||||
#time.sleep(0.01)
|
print(f'Generate preview of type {self.data_type} not supported yet')
|
||||||
|
return
|
||||||
|
|
||||||
#Transfering pixels between previews
|
# asset.children.link(src_asset)
|
||||||
w, h = src_asset.preview.image_size
|
# bpy.ops.ed.lib_id_generate_preview({"id": asset})
|
||||||
pixels = [0] * (w*h*4)
|
|
||||||
src_asset.preview.image_pixels_float.foreach_get(pixels)
|
|
||||||
|
|
||||||
asset.preview_ensure()
|
# while bpy.app.is_job_running("RENDER_PREVIEW"):
|
||||||
asset.preview.image_size = src_asset.preview.image_size
|
# print(bpy.app.is_job_running("RENDER_PREVIEW"))
|
||||||
asset.preview.image_pixels_float.foreach_set(pixels)
|
# time.sleep(0.2)
|
||||||
|
|
||||||
|
# getattr(bpy.data, self.data_types).remove(src_asset)
|
||||||
|
# return asset.preview
|
||||||
|
#src_asset.user_clear()
|
||||||
|
#return src_asset
|
||||||
|
|
||||||
|
#asset.children.unlink(src_asset)
|
||||||
|
#getattr(bpy.data, self.data_types).remove(src_asset)
|
||||||
|
# time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
# #Transfering pixels between previews
|
||||||
|
# w, h = src_asset.preview.image_size
|
||||||
|
# pixels = [0] * (w*h*4)
|
||||||
|
# src_asset.preview.image_pixels_float.foreach_get(pixels)
|
||||||
|
|
||||||
|
# asset.preview_ensure()
|
||||||
|
# asset.preview.image_size = src_asset.preview.image_size
|
||||||
|
# asset.preview.image_pixels_float.foreach_set(pixels)
|
||||||
|
|
||||||
#print('pixels transfered')
|
#print('pixels transfered')
|
||||||
|
|
||||||
bpy.app.timers.register(partial(getattr(bpy.data, self.data_types).remove, src_asset), first_interval=1)
|
#bpy.app.timers.register(partial(getattr(bpy.data, self.data_types).remove, src_asset), first_interval=1)
|
||||||
|
|
||||||
#getattr(bpy.data, self.data_types).remove(src_asset)
|
|
||||||
|
|
||||||
|
|
||||||
def set_asset_catalog(self, asset, asset_data, catalog_data):
|
def set_asset_catalog(self, asset, asset_data, catalog_data):
|
||||||
|
@ -405,6 +649,10 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
continue
|
continue
|
||||||
asset.asset_data.tags.new(tag, skip_if_exists=True)
|
asset.asset_data.tags.new(tag, skip_if_exists=True)
|
||||||
|
|
||||||
|
def set_asset_description(self, asset, asset_data):
|
||||||
|
"""Set asset description base on provided data"""
|
||||||
|
asset.asset_data.description = asset_data.get('description', '')
|
||||||
|
|
||||||
def bundle(self, cache_diff=None):
|
def bundle(self, cache_diff=None):
|
||||||
"""Group all new assets in one or multiple blends for the asset browser"""
|
"""Group all new assets in one or multiple blends for the asset browser"""
|
||||||
|
|
||||||
|
@ -412,7 +660,9 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
print(f'{self.data_type} is not supported yet')
|
print(f'{self.data_type} is not supported yet')
|
||||||
return
|
return
|
||||||
|
|
||||||
lib_path = self.library_path
|
target_dir = self.target_directory
|
||||||
|
|
||||||
|
|
||||||
catalog_data = self.read_catalog() #TODO remove unused catalog
|
catalog_data = self.read_catalog() #TODO remove unused catalog
|
||||||
|
|
||||||
write_cache = False
|
write_cache = False
|
||||||
|
@ -423,11 +673,18 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
# Only write complete cache at the end
|
# Only write complete cache at the end
|
||||||
write_cache = True
|
write_cache = True
|
||||||
|
|
||||||
|
self.generate_previews()
|
||||||
|
|
||||||
elif isinstance(cache_diff, (Path, str)):
|
elif isinstance(cache_diff, (Path, str)):
|
||||||
cache_diff = json.loads(Path(cache_diff).read_text(encoding='utf-8'))
|
cache_diff = json.loads(Path(cache_diff).read_text(encoding='utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if self.blend_depth == 0:
|
if self.blend_depth == 0:
|
||||||
groups = [(cache_diff)]
|
raise Exception('Blender depth must be 1 at min')
|
||||||
|
#groups = [(cache_diff)]
|
||||||
else:
|
else:
|
||||||
cache_diff.sort(key=self.group_key)
|
cache_diff.sort(key=self.group_key)
|
||||||
groups = groupby(cache_diff, key=self.group_key)
|
groups = groupby(cache_diff, key=self.group_key)
|
||||||
|
@ -440,9 +697,10 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
return
|
return
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
|
#assets_to_preview = []
|
||||||
for sub_path, asset_datas in groups:
|
for sub_path, asset_datas in groups:
|
||||||
blend_name = sub_path[-1].replace(' ', '_').lower()
|
blend_name = sub_path[-1].replace(' ', '_').lower()
|
||||||
blend_path = Path(lib_path, *sub_path, blend_name).with_suffix('.blend')
|
blend_path = Path(target_dir, *sub_path, blend_name).with_suffix('.blend')
|
||||||
|
|
||||||
if blend_path.exists():
|
if blend_path.exists():
|
||||||
print(f'Opening existing bundle blend: {blend_path}')
|
print(f'Opening existing bundle blend: {blend_path}')
|
||||||
|
@ -471,9 +729,10 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
elif operation == 'ADD' or not asset:
|
elif operation == 'ADD' or not asset:
|
||||||
if asset:
|
if asset:
|
||||||
#raise Exception(f"Asset {asset_data['name']} Already in Blend")
|
#raise Exception(f"Asset {asset_data['name']} Already in Blend")
|
||||||
|
print(f"Asset {asset_data['name']} Already in Blend")
|
||||||
getattr(bpy.data, self.data_types).remove(asset)
|
getattr(bpy.data, self.data_types).remove(asset)
|
||||||
|
|
||||||
#print(f"INFO: Add new asset: {asset_data['name']}")
|
print(f"INFO: Add new asset: {asset_data['name']}")
|
||||||
asset = getattr(bpy.data, self.data_types).new(name=asset_data['name'])
|
asset = getattr(bpy.data, self.data_types).new(name=asset_data['name'])
|
||||||
else:
|
else:
|
||||||
print(f'operation {operation} not supported should be in (ADD, REMOVE, MODIFIED)')
|
print(f'operation {operation} not supported should be in (ADD, REMOVE, MODIFIED)')
|
||||||
|
@ -482,26 +741,36 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
asset.asset_mark()
|
asset.asset_mark()
|
||||||
|
|
||||||
self.set_asset_preview(asset, asset_data)
|
self.set_asset_preview(asset, asset_data)
|
||||||
|
|
||||||
|
#if not asset_preview:
|
||||||
|
# assets_to_preview.append((asset_data['filepath'], asset_data['name'], asset_data['data_type']))
|
||||||
#if self.externalize_data:
|
#if self.externalize_data:
|
||||||
# self.write_preview(preview, filepath)
|
# self.write_preview(preview, filepath)
|
||||||
|
|
||||||
self.set_asset_catalog(asset, asset_data, catalog_data)
|
self.set_asset_catalog(asset, asset_data, catalog_data)
|
||||||
self.set_asset_metadata(asset, asset_data)
|
self.set_asset_metadata(asset, asset_data)
|
||||||
self.set_asset_tags(asset, asset_data)
|
self.set_asset_tags(asset, asset_data)
|
||||||
asset.asset_data.description = asset_data.get('description', '')
|
self.set_asset_description(asset, asset_data)
|
||||||
|
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
#self.write_asset_preview_file()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
print(f'Saving Blend to {blend_path}')
|
print(f'Saving Blend to {blend_path}')
|
||||||
|
|
||||||
blend_path.parent.mkdir(exist_ok=True, parents=True)
|
#blend_path.parent.mkdir(exist_ok=True, parents=True)
|
||||||
bpy.ops.wm.save_as_mainfile(filepath=str(blend_path), compress=True)
|
|
||||||
|
#bpy.ops.wm.save_as_mainfile(filepath=str(blend_path), compress=True)
|
||||||
|
|
||||||
#if write_cache:
|
#if write_cache:
|
||||||
# self.write_cache(cache)
|
# self.write_cache(cache)
|
||||||
|
|
||||||
#self.write_catalog(catalog_data)
|
self.write_catalog(catalog_data)
|
||||||
|
|
||||||
|
|
||||||
bpy.ops.wm.quit_blender()
|
bpy.ops.wm.quit_blender()
|
||||||
|
|
||||||
def norm_cache(self, cache):
|
def norm_cache(self, cache):
|
||||||
|
@ -566,4 +835,11 @@ class AssetLibraryAdapter(PropertyGroup):
|
||||||
layout.prop(self, k, text=bpy.path.display_name(k))
|
layout.prop(self, k, text=bpy.path.display_name(k))
|
||||||
|
|
||||||
def format_path(self, template, **kargs):
|
def format_path(self, template, **kargs):
|
||||||
return Template(template).format(self.to_dict(), **kargs).resolve()
|
|
||||||
|
params = dict(self.to_dict(),
|
||||||
|
bundle_dir=Path(self.library.bundle_directory),
|
||||||
|
conform_dir=Path(self.library.conform.directory),
|
||||||
|
**kargs
|
||||||
|
)
|
||||||
|
|
||||||
|
return Template(template).format(params).resolve()
|
|
@ -82,8 +82,8 @@ class KitsuLibrary(AssetLibraryAdapter):
|
||||||
description=data['description'],
|
description=data['description'],
|
||||||
tags=[],
|
tags=[],
|
||||||
type=self.data_type,
|
type=self.data_type,
|
||||||
image=str(self.image_template.format(name=asset_name)),
|
image=str(self.template_image.format(name=asset_name)),
|
||||||
video=str(self.video_template.format(name=asset_name)),
|
video=str(self.template_video.format(name=asset_name)),
|
||||||
name=data['name'])
|
name=data['name'])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -98,17 +98,12 @@ class KitsuLibrary(AssetLibraryAdapter):
|
||||||
def get_preview(self, asset_data):
|
def get_preview(self, asset_data):
|
||||||
|
|
||||||
name = asset_data['name']
|
name = asset_data['name']
|
||||||
preview = (f / image_template.format(name=name)).resolve()
|
preview = (f / template_image.format(name=name)).resolve()
|
||||||
if not preview.exists():
|
if not preview.exists():
|
||||||
preview_blend_file(f, preview)
|
preview_blend_file(f, preview)
|
||||||
|
|
||||||
return preview
|
return preview
|
||||||
|
|
||||||
def conform(self, directory, templates):
|
|
||||||
"""Split each assets per blend and externalize preview"""
|
|
||||||
|
|
||||||
print(f'Conforming {self.library.name} to {directory}')
|
|
||||||
|
|
||||||
|
|
||||||
def fetch(self):
|
def fetch(self):
|
||||||
"""Gather in a list all assets found in the folder"""
|
"""Gather in a list all assets found in the folder"""
|
||||||
|
|
|
@ -55,8 +55,8 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
metadata=dict(asset.asset_data),
|
metadata=dict(asset.asset_data),
|
||||||
tags=asset.asset_data.tags.keys(),
|
tags=asset.asset_data.tags.keys(),
|
||||||
type=self.data_type,
|
type=self.data_type,
|
||||||
image=str(self.image_template.format(name=asset_name)),
|
image=str(self.template_image.format(name=asset_name)),
|
||||||
video=str(self.video_template.format(name=asset_name)),
|
video=str(self.template_video.format(name=asset_name)),
|
||||||
name=asset.name)
|
name=asset.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
asset.asset_mark()
|
asset.asset_mark()
|
||||||
|
|
||||||
# Load external preview if exists
|
# Load external preview if exists
|
||||||
#image_template = Template(asset_data['preview'])
|
#template_image = Template(asset_data['preview'])
|
||||||
image_path = Path(asset_data['image'])
|
image_path = Path(asset_data['image'])
|
||||||
if not image_path.is_absolute():
|
if not image_path.is_absolute():
|
||||||
image_path = Path(asset_data['filepath'], image_path)
|
image_path = Path(asset_data['filepath'], image_path)
|
||||||
|
@ -242,7 +242,7 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
def get_preview(self, asset_data):
|
def get_preview(self, asset_data):
|
||||||
|
|
||||||
name = asset_data['name']
|
name = asset_data['name']
|
||||||
preview = (f / image_template.format(name=name)).resolve()
|
preview = (f / template_image.format(name=name)).resolve()
|
||||||
if not preview.exists():
|
if not preview.exists():
|
||||||
preview_blend_file(f, preview)
|
preview_blend_file(f, preview)
|
||||||
|
|
||||||
|
@ -264,8 +264,8 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
catalog_ids = {v['id']: {'path': k, 'name': v['name']} for k,v in catalog_data.items()}
|
catalog_ids = {v['id']: {'path': k, 'name': v['name']} for k,v in catalog_data.items()}
|
||||||
directory = Path(directory).resolve()
|
directory = Path(directory).resolve()
|
||||||
|
|
||||||
image_template = templates.get('image') or self.image_template
|
template_image = templates.get('image') or self.template_image
|
||||||
video_template = templates.get('video') or self.video_template
|
template_video = templates.get('video') or self.template_video
|
||||||
|
|
||||||
# Get list of all modifications
|
# Get list of all modifications
|
||||||
for blend_file in self._find_blend_files():
|
for blend_file in self._find_blend_files():
|
||||||
|
@ -295,12 +295,12 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
asset_path = self.get_asset_path(name=asset.name, catalog=catalog_path, directory=directory)
|
asset_path = self.get_asset_path(name=asset.name, catalog=catalog_path, directory=directory)
|
||||||
asset_description = self.get_asset_description(asset, catalog=catalog_path, modified=modified)
|
asset_description = self.get_asset_description(asset, catalog=catalog_path, modified=modified)
|
||||||
|
|
||||||
self.write_asset_description(asset_description, asset_path)
|
self.write_description_file(asset_description, asset_path)
|
||||||
#Write blend file containing only one asset
|
#Write blend file containing only one asset
|
||||||
self.write_asset(asset=asset, asset_path=asset_path)
|
self.write_asset(asset=asset, asset_path=asset_path)
|
||||||
|
|
||||||
# Copy image if source image found else write the asset preview
|
# Copy image if source image found else write the asset preview
|
||||||
src_image_path = self.get_path('image', name=asset.name, asset_path=blend_file, template=image_template)
|
src_image_path = self.get_path('image', name=asset.name, asset_path=blend_file, template=template_image)
|
||||||
dst_image_path = self.get_path('image', name=asset.name, asset_path=asset_path)
|
dst_image_path = self.get_path('image', name=asset.name, asset_path=asset_path)
|
||||||
|
|
||||||
if src_image_path.exists():
|
if src_image_path.exists():
|
||||||
|
@ -309,7 +309,7 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
self.write_preview(asset.preview, dst_image_path)
|
self.write_preview(asset.preview, dst_image_path)
|
||||||
|
|
||||||
# Copy video if source video found
|
# Copy video if source video found
|
||||||
src_video_path = self.get_path('video', name=asset.name, asset_path=blend_file, template=video_template)
|
src_video_path = self.get_path('video', name=asset.name, asset_path=blend_file, template=template_video)
|
||||||
|
|
||||||
#print('src_video_path', src_video_path)
|
#print('src_video_path', src_video_path)
|
||||||
if src_video_path.exists():
|
if src_video_path.exists():
|
||||||
|
@ -364,7 +364,7 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
if not field_data:
|
if not field_data:
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
#asset_data = (blend_file / prefs.asset_description_template.format(name=name)).resolve()
|
#asset_data = (blend_file / prefs.template_description.format(name=name)).resolve()
|
||||||
|
|
||||||
catalogs = [v for k,v in sorted(field_data.items()) if k.isdigit()]
|
catalogs = [v for k,v in sorted(field_data.items()) if k.isdigit()]
|
||||||
catalogs = [c.replace('_', ' ').title() for c in catalogs]
|
catalogs = [c.replace('_', ' ').title() for c in catalogs]
|
||||||
|
@ -387,7 +387,7 @@ class ScanFolderLibrary(AssetLibraryAdapter):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#First Check if there is a asset_data .json
|
#First Check if there is a asset_data .json
|
||||||
asset_description = self.read_asset_description(blend_file)
|
asset_description = self.read_asset_description_file(blend_file)
|
||||||
|
|
||||||
if not asset_description:
|
if not asset_description:
|
||||||
# Scan the blend file for assets inside and write a custom asset description for info found
|
# Scan the blend file for assets inside and write a custom asset description for info found
|
||||||
|
|
Binary file not shown.
|
@ -332,6 +332,9 @@ def load_datablocks(src, names=None, type='objects', link=True, expr=None) -> li
|
||||||
return_list = not isinstance(names, str)
|
return_list = not isinstance(names, str)
|
||||||
names = names or []
|
names = names or []
|
||||||
|
|
||||||
|
if type.isupper():
|
||||||
|
type = f'{type.lower()}s'
|
||||||
|
|
||||||
if not isinstance(names, (list, tuple)):
|
if not isinstance(names, (list, tuple)):
|
||||||
names = [names]
|
names = [names]
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,17 @@ import importlib
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def cd(path):
|
||||||
|
"""Changes working directory and returns to previous on exit."""
|
||||||
|
prev_cwd = Path.cwd()
|
||||||
|
os.chdir(path)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.chdir(prev_cwd)
|
||||||
|
|
||||||
def install_module(module_name, package_name=None):
|
def install_module(module_name, package_name=None):
|
||||||
'''Install a python module with pip or return it if already installed'''
|
'''Install a python module with pip or return it if already installed'''
|
||||||
|
|
|
@ -181,6 +181,7 @@ def get_asset_source(replace_local=False):
|
||||||
|
|
||||||
return source_path
|
return source_path
|
||||||
|
|
||||||
|
'''
|
||||||
def get_catalog_path(filepath=None):
|
def get_catalog_path(filepath=None):
|
||||||
filepath = filepath or bpy.data.filepath
|
filepath = filepath or bpy.data.filepath
|
||||||
filepath = Path(filepath)
|
filepath = Path(filepath)
|
||||||
|
@ -195,7 +196,7 @@ def get_catalog_path(filepath=None):
|
||||||
catalog.touch(exist_ok=False)
|
catalog.touch(exist_ok=False)
|
||||||
|
|
||||||
return catalog
|
return catalog
|
||||||
|
'''
|
||||||
|
|
||||||
# def read_catalog(path, key='path'):
|
# def read_catalog(path, key='path'):
|
||||||
# cat_data = {}
|
# cat_data = {}
|
||||||
|
@ -219,7 +220,7 @@ def get_catalog_path(filepath=None):
|
||||||
# cat_data[cat_name] = {'id':cat_id, 'path':cat_path}
|
# cat_data[cat_name] = {'id':cat_id, 'path':cat_path}
|
||||||
|
|
||||||
# return cat_data
|
# return cat_data
|
||||||
|
"""
|
||||||
def read_catalog(path):
|
def read_catalog(path):
|
||||||
cat_data = {}
|
cat_data = {}
|
||||||
|
|
||||||
|
@ -299,6 +300,7 @@ def create_catalog_file(json_path : str|Path, keep_existing_category : bool = Tr
|
||||||
print(f'Catalog saved at: {catalog_path}')
|
print(f'Catalog saved at: {catalog_path}')
|
||||||
|
|
||||||
return
|
return
|
||||||
|
"""
|
||||||
|
|
||||||
def clear_env_libraries():
|
def clear_env_libraries():
|
||||||
print('clear_env_libraries')
|
print('clear_env_libraries')
|
||||||
|
|
|
@ -54,14 +54,12 @@ class Template:
|
||||||
|
|
||||||
def format(self, data=None, **kargs):
|
def format(self, data=None, **kargs):
|
||||||
|
|
||||||
#print('format', self.template, data, kargs)
|
|
||||||
|
|
||||||
data = {**(data or {}), **kargs}
|
data = {**(data or {}), **kargs}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path = self.template.format(**data)
|
path = self.template.format(**data)
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
print(f'Cannot format {self.template} with {data}')
|
print(f'Cannot format {self.template} with {data}, field {e} is missing')
|
||||||
return
|
return
|
||||||
|
|
||||||
path = os.path.expandvars(path)
|
path = os.path.expandvars(path)
|
||||||
|
|
|
@ -13,4 +13,5 @@ ASSETLIB_FILENAME = "blender_assets.libs.json"
|
||||||
MODULE_DIR = Path(__file__).parent
|
MODULE_DIR = Path(__file__).parent
|
||||||
RESOURCES_DIR = MODULE_DIR / 'resources'
|
RESOURCES_DIR = MODULE_DIR / 'resources'
|
||||||
ADAPTER_DIR = MODULE_DIR / 'adapters'
|
ADAPTER_DIR = MODULE_DIR / 'adapters'
|
||||||
ADAPTERS = []
|
ADAPTERS = []
|
||||||
|
PREVIEW_ASSETS_SCRIPT = MODULE_DIR / 'common' / 'preview_assets.py'
|
||||||
|
|
|
@ -17,7 +17,7 @@ command, write_catalog)
|
||||||
|
|
||||||
|
|
||||||
@command
|
@command
|
||||||
def bundle_library(source_directory, bundle_directory, asset_description_template, thumbnail_template,
|
def bundle_library(source_directory, bundle_directory, template_description, thumbnail_template,
|
||||||
template=None, data_file=None):
|
template=None, data_file=None):
|
||||||
|
|
||||||
field_pattern = r'{(\w+)}'
|
field_pattern = r'{(\w+)}'
|
||||||
|
@ -38,7 +38,7 @@ def bundle_library(source_directory, bundle_directory, asset_description_templat
|
||||||
|
|
||||||
name = field_data.get('name', f.stem)
|
name = field_data.get('name', f.stem)
|
||||||
thumbnail = (f / thumbnail_template.format(name=name)).resolve()
|
thumbnail = (f / thumbnail_template.format(name=name)).resolve()
|
||||||
asset_data = (f / asset_description_template.format(name=name)).resolve()
|
asset_data = (f / template_description.format(name=name)).resolve()
|
||||||
|
|
||||||
catalogs = sorted([v for k,v in sorted(field_data.items()) if k.isdigit()])
|
catalogs = sorted([v for k,v in sorted(field_data.items()) if k.isdigit()])
|
||||||
catalogs = [c.replace('_', ' ').title() for c in catalogs]
|
catalogs = [c.replace('_', ' ').title() for c in catalogs]
|
||||||
|
@ -163,7 +163,7 @@ if __name__ == '__main__' :
|
||||||
bundle_library(
|
bundle_library(
|
||||||
source_directory=args.source_directory,
|
source_directory=args.source_directory,
|
||||||
bundle_directory=args.bundle_directory,
|
bundle_directory=args.bundle_directory,
|
||||||
asset_description_template=args.asset_description_template,
|
template_description=args.template_description,
|
||||||
thumbnail_template=args.thumbnail_template,
|
thumbnail_template=args.thumbnail_template,
|
||||||
template=args.template,
|
template=args.template,
|
||||||
data_file=args.data_file)
|
data_file=args.data_file)
|
||||||
|
|
77
operators.py
77
operators.py
|
@ -112,8 +112,8 @@ class ASSETLIB_OT_edit_data(Operator):
|
||||||
new_video_path = lib.adapter.get_path('video', new_name, new_asset_path)
|
new_video_path = lib.adapter.get_path('video', new_name, new_asset_path)
|
||||||
self.old_video_path.rename(new_video_path)
|
self.old_video_path.rename(new_video_path)
|
||||||
|
|
||||||
if self.old_asset_description_path.exists():
|
if self.old_description_path.exists():
|
||||||
self.old_asset_description_path.unlink()
|
self.old_description_path.unlink()
|
||||||
|
|
||||||
new_asset_description = lib.adapter.get_asset_description(
|
new_asset_description = lib.adapter.get_asset_description(
|
||||||
asset=self.asset,
|
asset=self.asset,
|
||||||
|
@ -121,7 +121,7 @@ class ASSETLIB_OT_edit_data(Operator):
|
||||||
modified=time.time_ns()
|
modified=time.time_ns()
|
||||||
)
|
)
|
||||||
|
|
||||||
lib.adapter.write_asset_description(new_asset_description, new_asset_path)
|
lib.adapter.write_description_file(new_asset_description, new_asset_path)
|
||||||
|
|
||||||
if not list(self.old_asset_path.parent.iterdir()):
|
if not list(self.old_asset_path.parent.iterdir()):
|
||||||
self.old_asset_path.parent.rmdir()
|
self.old_asset_path.parent.rmdir()
|
||||||
|
@ -187,9 +187,9 @@ class ASSETLIB_OT_edit_data(Operator):
|
||||||
self.old_image_path = lib.adapter.get_path('image', self.old_asset_name, self.old_asset_path)
|
self.old_image_path = lib.adapter.get_path('image', self.old_asset_name, self.old_asset_path)
|
||||||
self.old_video_path = lib.adapter.get_path('video', self.old_asset_name, self.old_asset_path)
|
self.old_video_path = lib.adapter.get_path('video', self.old_asset_name, self.old_asset_path)
|
||||||
|
|
||||||
self.old_asset_description_path = lib.adapter.get_asset_description_path(self.old_asset_path)
|
self.old_description_path = lib.adapter.get_description_path(self.old_asset_path)
|
||||||
|
|
||||||
self.old_asset_description = lib.adapter.read_asset_description(self.old_asset_path)
|
self.old_asset_description = lib.adapter.read_asset_description_file(self.old_asset_path)
|
||||||
self.old_asset_description = lib.adapter.norm_asset_datas([self.old_asset_description])[0]
|
self.old_asset_description = lib.adapter.norm_asset_datas([self.old_asset_description])[0]
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,8 +408,8 @@ class ASSETLIB_OT_conform_library(Operator):
|
||||||
bl_description = "Split each assets per blend and externalize preview"
|
bl_description = "Split each assets per blend and externalize preview"
|
||||||
|
|
||||||
name : StringProperty()
|
name : StringProperty()
|
||||||
image_template : StringProperty()
|
template_image : StringProperty()
|
||||||
video_template : StringProperty()
|
template_video : StringProperty()
|
||||||
directory : StringProperty(subtype='DIR_PATH', name='Filepath')
|
directory : StringProperty(subtype='DIR_PATH', name='Filepath')
|
||||||
|
|
||||||
def execute(self, context: Context) -> Set[str]:
|
def execute(self, context: Context) -> Set[str]:
|
||||||
|
@ -419,13 +419,13 @@ class ASSETLIB_OT_conform_library(Operator):
|
||||||
#lib.adapter.conform(self.directory)
|
#lib.adapter.conform(self.directory)
|
||||||
|
|
||||||
templates = {}
|
templates = {}
|
||||||
if self.image_template:
|
if self.template_image:
|
||||||
templates['image'] = self.image_template
|
templates['image'] = self.template_image
|
||||||
if self.video_template:
|
if self.template_video:
|
||||||
templates['video'] = self.video_template
|
templates['video'] = self.template_video
|
||||||
|
|
||||||
|
|
||||||
script_path = Path(gettempdir()) / 'bundle_library.py'
|
script_path = Path(bpy.app.tempdir) / 'bundle_library.py'
|
||||||
script_code = dedent(f"""
|
script_code = dedent(f"""
|
||||||
import bpy
|
import bpy
|
||||||
prefs = bpy.context.preferences.addons["asset_library"].preferences
|
prefs = bpy.context.preferences.addons["asset_library"].preferences
|
||||||
|
@ -447,6 +447,58 @@ class ASSETLIB_OT_conform_library(Operator):
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
|
||||||
|
class ASSETLIB_OT_generate_previews(Operator):
|
||||||
|
bl_idname = "assetlib.generate_previews"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_label = "Generate Previews"
|
||||||
|
bl_description = "Generate and write the image for assets"
|
||||||
|
|
||||||
|
diff : StringProperty()
|
||||||
|
preview_blend : StringProperty()
|
||||||
|
name : StringProperty()
|
||||||
|
blocking : BoolProperty(default=True)
|
||||||
|
|
||||||
|
def execute(self, context: Context) -> Set[str]:
|
||||||
|
prefs = get_addon_prefs()
|
||||||
|
lib = prefs.libraries.get(self.name)
|
||||||
|
# self.write_file(self.diff_file, self.diff)
|
||||||
|
|
||||||
|
# preview_assets = [(a.asset_data['filepath'], self.data_types, a.name) for a in assets]
|
||||||
|
|
||||||
|
# self.preview_assets_file.write_text(json.dumps(preview_assets), encoding='utf-8')
|
||||||
|
|
||||||
|
# cmd = [
|
||||||
|
# bpy.app.binary_path, '-b', '--use-system-env',
|
||||||
|
# '--python', str(PREVIEW_ASSETS_SCRIPT), '--',
|
||||||
|
# '--preview-blend', str(self.preview_blend),
|
||||||
|
# '--preview-assets-file', str(self.preview_assets_file)
|
||||||
|
# ]
|
||||||
|
# subprocess.call(cmd)
|
||||||
|
preview_blend = self.preview_blend or lib.adapter.preview_blend
|
||||||
|
|
||||||
|
script_path = Path(bpy.app.tempdir) / 'generate_previews.py'
|
||||||
|
script_code = dedent(f"""
|
||||||
|
import bpy
|
||||||
|
prefs = bpy.context.preferences.addons["asset_library"].preferences
|
||||||
|
lib = prefs.env_libraries.add()
|
||||||
|
lib.set_dict({lib.to_dict()})
|
||||||
|
|
||||||
|
bpy.ops.wm.open_mainfile(filepath='{preview_blend}', load_ui=True)
|
||||||
|
lib.conform.adapter.generate_previews()
|
||||||
|
""")
|
||||||
|
|
||||||
|
script_path.write_text(script_code)
|
||||||
|
|
||||||
|
cmd = get_bl_cmd(script=str(script_path), background=True)
|
||||||
|
|
||||||
|
if self.blocking:
|
||||||
|
subprocess.call(cmd)
|
||||||
|
else:
|
||||||
|
subprocess.Popen(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
class ASSETLIB_OT_play_preview(Operator):
|
class ASSETLIB_OT_play_preview(Operator):
|
||||||
bl_idname = "assetlib.play_preview"
|
bl_idname = "assetlib.play_preview"
|
||||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||||
|
@ -553,6 +605,7 @@ classes = (
|
||||||
ASSETLIB_OT_add_user_library,
|
ASSETLIB_OT_add_user_library,
|
||||||
ASSETLIB_OT_remove_user_library,
|
ASSETLIB_OT_remove_user_library,
|
||||||
ASSETLIB_OT_diff,
|
ASSETLIB_OT_diff,
|
||||||
|
ASSETLIB_OT_generate_previews,
|
||||||
ASSETLIB_OT_bundle_library,
|
ASSETLIB_OT_bundle_library,
|
||||||
ASSETLIB_OT_clear_asset,
|
ASSETLIB_OT_clear_asset,
|
||||||
ASSETLIB_OT_edit_data,
|
ASSETLIB_OT_edit_data,
|
||||||
|
|
69
prefs.py
69
prefs.py
|
@ -12,7 +12,7 @@ from asset_library.constants import (DATA_TYPES, DATA_TYPE_ITEMS,
|
||||||
|
|
||||||
from asset_library.common.file_utils import import_module_from_path, norm_str
|
from asset_library.common.file_utils import import_module_from_path, norm_str
|
||||||
from asset_library.common.bl_utils import get_addon_prefs
|
from asset_library.common.bl_utils import get_addon_prefs
|
||||||
from asset_library.common.functions import get_catalog_path
|
#from asset_library.common.functions import get_catalog_path
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import importlib
|
import importlib
|
||||||
|
@ -88,14 +88,16 @@ class ConformAssetLibrary(PropertyGroup):
|
||||||
adapters : bpy.props.PointerProperty(type=AssetLibraryAdapters)
|
adapters : bpy.props.PointerProperty(type=AssetLibraryAdapters)
|
||||||
adapter_name : EnumProperty(items=get_adapter_items)
|
adapter_name : EnumProperty(items=get_adapter_items)
|
||||||
directory : StringProperty(
|
directory : StringProperty(
|
||||||
name="Destination Directory",
|
name="Target Directory",
|
||||||
subtype='DIR_PATH',
|
subtype='DIR_PATH',
|
||||||
default=''
|
default=''
|
||||||
)
|
)
|
||||||
image_template : StringProperty()
|
|
||||||
video_template : StringProperty()
|
|
||||||
|
|
||||||
externalize_data: BoolProperty(default=False, name='Externalize Data')
|
template_image : StringProperty(default='', description='../{name}_image.png')
|
||||||
|
template_video : StringProperty(default='', description='../{name}_video.mov')
|
||||||
|
template_description : StringProperty(default='', description='../{name}_asset_description.json')
|
||||||
|
|
||||||
|
#externalize_data: BoolProperty(default=False, name='Externalize Data')
|
||||||
blend_depth: IntProperty(default=1, name='Blend Depth')
|
blend_depth: IntProperty(default=1, name='Blend Depth')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -228,23 +230,23 @@ class AssetLibrary(PropertyGroup):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_template(self):
|
def template_image(self):
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
return prefs.image_template
|
return prefs.template_image
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def video_template(self):
|
def template_video(self):
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
return prefs.video_template
|
return prefs.template_video
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asset_description_template(self):
|
def template_description(self):
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
return prefs.asset_description_template
|
return prefs.template_description
|
||||||
|
|
||||||
@property
|
#@property
|
||||||
def catalog_path(self):
|
#def catalog_path(self):
|
||||||
return get_catalog_path(self.library_path)
|
# return get_catalog_path(self.library_path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def options(self):
|
def options(self):
|
||||||
|
@ -376,7 +378,7 @@ class AssetLibrary(PropertyGroup):
|
||||||
if not self.use:
|
if not self.use:
|
||||||
if all(not l.use for l in self.merge_libraries):
|
if all(not l.use for l in self.merge_libraries):
|
||||||
self.clear_library_path()
|
self.clear_library_path()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create the Asset Library Path
|
# Create the Asset Library Path
|
||||||
if not lib:
|
if not lib:
|
||||||
|
@ -470,6 +472,10 @@ class AssetLibrary(PropertyGroup):
|
||||||
op.name = self.name
|
op.name = self.name
|
||||||
op.conform = True
|
op.conform = True
|
||||||
|
|
||||||
|
op = subrow.operator('assetlib.generate_previews', text='', icon='SEQ_PREVIEW')#, icon='MOD_BUILD'
|
||||||
|
op.name = self.name
|
||||||
|
#op.conform = True
|
||||||
|
|
||||||
op = subrow.operator('assetlib.bundle', text='', icon='MOD_BUILD')#, icon='MOD_BUILD'
|
op = subrow.operator('assetlib.bundle', text='', icon='MOD_BUILD')#, icon='MOD_BUILD'
|
||||||
op.name = self.name
|
op.name = self.name
|
||||||
op.directory = self.conform.directory
|
op.directory = self.conform.directory
|
||||||
|
@ -479,17 +485,22 @@ class AssetLibrary(PropertyGroup):
|
||||||
#subrow.separator(factor=3)
|
#subrow.separator(factor=3)
|
||||||
|
|
||||||
if self.expand_extra and self.conform.adapter:
|
if self.expand_extra and self.conform.adapter:
|
||||||
|
col.separator()
|
||||||
|
self.conform.adapter.draw_prefs(col)
|
||||||
|
|
||||||
|
col.separator()
|
||||||
col.separator()
|
col.separator()
|
||||||
#row = layout.row(align=True)
|
#row = layout.row(align=True)
|
||||||
#row.label(text='Conform Library')
|
#row.label(text='Conform Library')
|
||||||
col.prop(self.conform, "directory")
|
col.prop(self.conform, "directory")
|
||||||
col.prop(self.conform, "blend_depth")
|
col.prop(self.conform, "blend_depth")
|
||||||
col.prop(self.conform, "externalize_data")
|
#col.prop(self.conform, "externalize_data")
|
||||||
col.prop(self.conform, "image_template", text='Image Template')
|
subcol = col.column(align=True)
|
||||||
col.prop(self.conform, "video_template", text='Video Template')
|
subcol.prop(self.conform, "template_description", text='Template Description', icon='COPY_ID')
|
||||||
|
subcol.prop(self.conform, "template_image", text='Template Image', icon='COPY_ID')
|
||||||
|
subcol.prop(self.conform, "template_video", text='Template Video', icon='COPY_ID')
|
||||||
|
|
||||||
col.separator()
|
|
||||||
self.conform.adapter.draw_prefs(col)
|
|
||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
||||||
|
@ -651,10 +662,10 @@ class AssetLibraryPrefs(AddonPreferences):
|
||||||
update=update_all_library_path
|
update=update_all_library_path
|
||||||
)
|
)
|
||||||
|
|
||||||
use_single_path : BoolProperty(default=True)
|
#use_single_path : BoolProperty(default=True)
|
||||||
asset_description_template : StringProperty(default='../{name}_asset_description.json')
|
#template_description : StringProperty(default='../{name}_asset_description.json')
|
||||||
image_template : StringProperty(default='../{name}_image.png')
|
#template_image : StringProperty(default='../{name}_image.png')
|
||||||
video_template : StringProperty(default='../{name}_video.mov')
|
#template_video : StringProperty(default='../{name}_video.mov')
|
||||||
|
|
||||||
config_directory : StringProperty(
|
config_directory : StringProperty(
|
||||||
name="Config Path",
|
name="Config Path",
|
||||||
|
@ -745,16 +756,16 @@ class AssetLibraryPrefs(AddonPreferences):
|
||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
||||||
col.prop(self, 'asset_description_template', text='Asset Description Template', icon='COPY_ID')
|
#col.prop(self, 'template_description', text='Asset Description Template', icon='COPY_ID')
|
||||||
|
|
||||||
col.separator()
|
#col.separator()
|
||||||
|
|
||||||
col.prop(self, 'image_template', text='Image Template', icon='COPY_ID')
|
#col.prop(self, 'template_image', text='Template Image', icon='COPY_ID')
|
||||||
col.prop(self, 'image_player', text='Image Player') #icon='OUTLINER_OB_IMAGE'
|
col.prop(self, 'image_player', text='Image Player') #icon='OUTLINER_OB_IMAGE'
|
||||||
|
|
||||||
col.separator()
|
#col.separator()
|
||||||
|
|
||||||
col.prop(self, 'video_template', text='Video Template', icon='COPY_ID')
|
#col.prop(self, 'template_video', text='Template Video', icon='COPY_ID')
|
||||||
col.prop(self, 'video_player', text='Video Player') #icon='FILE_MOVIE'
|
col.prop(self, 'video_player', text='Video Player') #icon='FILE_MOVIE'
|
||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
Loading…
Reference in New Issue