start refacto update
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
"""
|
||||
Util function for this addon
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from . bl_utils import get_addon_prefs
|
||||
|
||||
|
||||
def thumbnail_blend_file(input_blend, output_img):
|
||||
input_blend = Path(input_blend).resolve()
|
||||
output_img = Path(output_img).resolve()
|
||||
|
||||
print(f'Thumbnailing {input_blend} to {output_img}')
|
||||
blender_thumbnailer = Path(bpy.app.binary_path).parent / 'blender-thumbnailer'
|
||||
|
||||
output_img.parent.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
subprocess.call([blender_thumbnailer, str(input_blend), str(output_img)])
|
||||
|
||||
success = output_img.exists()
|
||||
|
||||
if not success:
|
||||
empty_preview = RESOURCES_DIR / 'empty_preview.png'
|
||||
shutil.copy(str(empty_preview), str(output_img))
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def get_active_library():
|
||||
'''Get the pref library properties from the active library of the asset browser'''
|
||||
prefs = get_addon_prefs()
|
||||
lib_ref = bpy.context.space_data.params.asset_library_reference
|
||||
|
||||
#Check for merged library
|
||||
for l in prefs.libraries:
|
||||
if l.name == lib_ref:
|
||||
return l
|
||||
|
||||
|
||||
def update_library_path(self, context):
|
||||
"""Removing all asset libraries and recreate them"""
|
||||
|
||||
addon_prefs = get_addon_prefs()
|
||||
libs = context.preferences.filepaths.asset_libraries
|
||||
|
||||
for i, lib in reversed(list(enumerate(libs))):
|
||||
if (addon_lib := addon_prefs.libraries.get(lib.name)) and addon_lib.path == lib.path:
|
||||
bpy.ops.preferences.asset_library_remove(index=i)
|
||||
|
||||
for addon_lib in addon_prefs.libraries:
|
||||
if not addon_lib.use:
|
||||
continue
|
||||
bpy.ops.preferences.asset_library_add(directory=str(addon_lib.path))
|
||||
libs[-1].name = addon_lib.name
|
||||
+97
-4
@@ -2,7 +2,7 @@
|
||||
"""
|
||||
Generic Blender functions
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from fnmatch import fnmatch
|
||||
from typing import Any, List, Iterable, Optional, Tuple
|
||||
@@ -15,6 +15,8 @@ from bpy_extras import asset_utils
|
||||
from os.path import abspath
|
||||
import subprocess
|
||||
|
||||
from .file_utils import norm_str
|
||||
|
||||
|
||||
class attr_set():
|
||||
'''Receive a list of tuple [(data_path, "attribute" [, wanted value)] ]
|
||||
@@ -82,6 +84,9 @@ def get_viewport():
|
||||
screen = bpy.context.screen
|
||||
|
||||
areas = [a for a in screen.areas if a.type == 'VIEW_3D']
|
||||
if not areas:
|
||||
return
|
||||
|
||||
areas.sort(key=lambda x : x.width*x.height)
|
||||
|
||||
return areas[-1]
|
||||
@@ -216,7 +221,7 @@ def norm_arg(arg_name, format=str.lower, prefix='--', separator='-'):
|
||||
return prefix + arg_name
|
||||
|
||||
|
||||
def get_bl_cmd(blender=None, background=False, focus=True, blendfile=None, script=None, **kargs):
|
||||
def get_bl_cmd(blender=None, background=False, factory_startup=False, focus=True, blendfile=None, script=None, **kargs):
|
||||
cmd = [str(blender)] if blender else [bpy.app.binary_path]
|
||||
|
||||
if background:
|
||||
@@ -228,6 +233,9 @@ def get_bl_cmd(blender=None, background=False, focus=True, blendfile=None, scrip
|
||||
|
||||
cmd += ['--python-use-system-env']
|
||||
|
||||
if factory_startup:
|
||||
cmd += ['--factory-startup']
|
||||
|
||||
if blendfile:
|
||||
cmd += [str(blendfile)]
|
||||
|
||||
@@ -347,7 +355,10 @@ def split_path(path) :
|
||||
return bone_name, prop_name
|
||||
|
||||
|
||||
|
||||
def get_asset_type(asset_type):
|
||||
data_types = { p.fixed_type.identifier: p.identifier for p in
|
||||
bpy.types.BlendData.bl_rna.properties if hasattr(p, 'fixed_type')}
|
||||
return data_types[asset_type]
|
||||
|
||||
|
||||
def load_datablocks(src, names=None, type='objects', link=True, expr=None, assets_only=False) -> list:
|
||||
@@ -482,4 +493,86 @@ def get_object_libraries(ob):
|
||||
|
||||
filepaths.append(absolute_filepath)
|
||||
|
||||
return filepaths
|
||||
return filepaths
|
||||
|
||||
|
||||
def clean_name(name):
|
||||
if re.match(r'(.*)\.\d{3}$', name):
|
||||
return name[:-4]
|
||||
return name
|
||||
|
||||
|
||||
def is_node_groups_duplicate(node_groups):
|
||||
node_group_types = sorted([n.type for n in node_groups[0].nodes])
|
||||
return all( sorted([n.type for n in ng.nodes]) ==
|
||||
node_group_types for ng in node_groups[1:])
|
||||
|
||||
|
||||
def is_images_duplicate(images):
|
||||
return all( img.filepath == images[0].filepath for image in images)
|
||||
|
||||
|
||||
def is_materials_duplicate(materials):
|
||||
node_group_types = sorted([n.type for n in materials[0].node_tree.nodes])
|
||||
return all( sorted([n.type for n in mat.node_tree.nodes]) ==
|
||||
node_group_types for mat in materials[1:])
|
||||
|
||||
|
||||
def merge_datablock_duplicates(datablocks, blend_data, force=False):
|
||||
"""Merging materials, node_groups or images based on name .001, .002"""
|
||||
|
||||
failed = []
|
||||
merged = []
|
||||
|
||||
datablocks = list(datablocks)
|
||||
|
||||
#blend_data = get_asset_type(datablocks[0].bl_rna.identifier)
|
||||
if blend_data == 'materials':
|
||||
is_datablock_duplicate = is_materials_duplicate
|
||||
elif blend_data == 'node_groups':
|
||||
is_datablock_duplicate = is_node_groups_duplicate
|
||||
elif blend_data == 'images':
|
||||
is_datablock_duplicate = is_images_duplicate
|
||||
else:
|
||||
raise Exception(f'Type, {blend_data} not supported')
|
||||
|
||||
# Group by name
|
||||
groups = {}
|
||||
for datablock in images:
|
||||
groups.setdefault(clean_name(datablock.name), []).append(datablock)
|
||||
|
||||
for datablock in blend_data:
|
||||
name = clean_name(datablock.name)
|
||||
if name in groups and datablock not in groups[name]:
|
||||
groups[name].append(datablock)
|
||||
|
||||
print("\nMerge Duplicate Datablocks...")
|
||||
|
||||
for group in groups.values():
|
||||
if len(group) == 1:
|
||||
continue
|
||||
|
||||
if not force:
|
||||
datablocks.sort(key=lambda x : x.name, reverse=True)
|
||||
|
||||
for datablock in datablocks[1:]:
|
||||
is_duplicate = is_datablock_duplicate((datablock, datablocks[0]))
|
||||
|
||||
if not is_duplicate and not force:
|
||||
failed.append((datablock.name, datablocks[0].name))
|
||||
print(f'Cannot merge Datablock {datablocks.name} with {datablocks[0].name} they are different')
|
||||
continue
|
||||
|
||||
merged.append((datablock.name, datablocks[0].name))
|
||||
print(f'Merge Datablock {datablock.name} into {datablocks[0].name}')
|
||||
|
||||
datablock.user_remap(datablocks[0])
|
||||
datablocks.remove(datablock)
|
||||
blend_data.remove(datablock)
|
||||
|
||||
# Rename groups if it has no duplicate left
|
||||
for datablocks in groups.values():
|
||||
if len(datablocks) == 1 and not datablocks[0].library:
|
||||
datablocks[0].name = clean_name(datablocks[0].name)
|
||||
|
||||
return merged, failed
|
||||
@@ -2,6 +2,14 @@
|
||||
from pathlib import Path
|
||||
import uuid
|
||||
import bpy
|
||||
from .file_utils import cache
|
||||
|
||||
@cache(1)
|
||||
def read_catalog(library_path):
|
||||
catalog = Catalog(library_path)
|
||||
catalog.read()
|
||||
|
||||
return catalog
|
||||
|
||||
|
||||
class CatalogItem:
|
||||
|
||||
+40
-1
@@ -12,9 +12,12 @@ from pathlib import Path
|
||||
import importlib
|
||||
import sys
|
||||
import shutil
|
||||
from functools import wraps
|
||||
from time import perf_counter
|
||||
|
||||
import contextlib
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def cd(path):
|
||||
"""Changes working directory and returns to previous on exit."""
|
||||
@@ -25,6 +28,7 @@ def cd(path):
|
||||
finally:
|
||||
os.chdir(prev_cwd)
|
||||
|
||||
|
||||
def install_module(module_name, package_name=None):
|
||||
'''Install a python module with pip or return it if already installed'''
|
||||
try:
|
||||
@@ -39,6 +43,7 @@ def install_module(module_name, package_name=None):
|
||||
|
||||
return module
|
||||
|
||||
|
||||
def import_module_from_path(path):
|
||||
from importlib import util
|
||||
|
||||
@@ -54,6 +59,7 @@ def import_module_from_path(path):
|
||||
print(f'Cannot import file {path}')
|
||||
print(e)
|
||||
|
||||
|
||||
def norm_str(string, separator='_', format=str.lower, padding=0):
|
||||
string = str(string)
|
||||
string = string.replace('_', ' ')
|
||||
@@ -73,6 +79,7 @@ def norm_str(string, separator='_', format=str.lower, padding=0):
|
||||
|
||||
return string
|
||||
|
||||
|
||||
def remove_version(filepath):
|
||||
pattern = '_v[0-9]+\.'
|
||||
search = re.search(pattern, filepath)
|
||||
@@ -82,12 +89,14 @@ def remove_version(filepath):
|
||||
|
||||
return Path(filepath).name
|
||||
|
||||
|
||||
def is_exclude(name, patterns) -> bool:
|
||||
# from fnmatch import fnmatch
|
||||
if not isinstance(patterns, (list,tuple)) :
|
||||
patterns = [patterns]
|
||||
return any([fnmatch(name, p) for p in patterns])
|
||||
|
||||
|
||||
def get_last_files(root, pattern=r'_v\d{3}\.\w+', only_matching=False, ex_file=None, ex_dir=None, keep=1, verbose=False) -> list:
|
||||
'''Recursively get last(s) file(s) (when there is multiple versions) in passed directory
|
||||
root -> str: Filepath of the folder to scan.
|
||||
@@ -139,6 +148,7 @@ def get_last_files(root, pattern=r'_v\d{3}\.\w+', only_matching=False, ex_file=N
|
||||
|
||||
return sorted(files)
|
||||
|
||||
|
||||
def copy_file(src, dst, only_new=False, only_recent=False):
|
||||
if dst.exists():
|
||||
if only_new:
|
||||
@@ -153,6 +163,7 @@ def copy_file(src, dst, only_new=False, only_recent=False):
|
||||
else:
|
||||
subprocess.call(['cp', str(src), str(dst)])
|
||||
|
||||
|
||||
def copy_dir(src, dst, only_new=False, only_recent=False, excludes=['.*'], includes=[]):
|
||||
src, dst = Path(src), Path(dst)
|
||||
|
||||
@@ -203,6 +214,7 @@ def open_file(filepath, select=False):
|
||||
cmd += [str(filepath)]
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
|
||||
def open_blender_file(filepath=None):
|
||||
filepath = filepath or bpy.data.filepath
|
||||
|
||||
@@ -217,6 +229,32 @@ def open_blender_file(filepath=None):
|
||||
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
|
||||
def cache(timeout):
|
||||
_cache = {}
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
current_time = perf_counter()
|
||||
cache_key = (*args, *kwargs.items()) # Assuming the first argument is the file path
|
||||
|
||||
# Check if the cache is valid
|
||||
if cache_key in _cache:
|
||||
cached_content, cache_time = _cache[cache_key]
|
||||
if (current_time - cache_time) < timeout:
|
||||
|
||||
return cached_content
|
||||
|
||||
# Execute the function and update the cache
|
||||
result = func(*args, **kwargs)
|
||||
_cache[cache_key] = (result, current_time)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def read_file(path):
|
||||
'''Read a file with an extension in (json, yaml, yml, txt)'''
|
||||
|
||||
@@ -255,6 +293,7 @@ def read_file(path):
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def write_file(path, data, indent=4):
|
||||
'''Read a file with an extension in (json, yaml, yml, text)'''
|
||||
|
||||
@@ -315,4 +354,4 @@ def synchronize(src, dst, only_new=False, only_recent=False, clear=False):
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
"""
|
||||
Util function for this addon
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import inspect
|
||||
from datetime import datetime
|
||||
from time import perf_counter
|
||||
import platform
|
||||
|
||||
import bpy
|
||||
from .catalog import Catalog, read_catalog
|
||||
from .bl_utils import get_addon_prefs, get_asset_type
|
||||
from .file_utils import read_file, write_file, cache, import_module_from_path
|
||||
|
||||
|
||||
def thumbnail_blend_file(input_blend, output_img):
|
||||
input_blend = Path(input_blend).resolve()
|
||||
output_img = Path(output_img).resolve()
|
||||
|
||||
print(f'Thumbnailing {input_blend} to {output_img}')
|
||||
blender_thumbnailer = Path(bpy.app.binary_path).parent / 'blender-thumbnailer'
|
||||
|
||||
output_img.parent.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
subprocess.call([blender_thumbnailer, str(input_blend), str(output_img)])
|
||||
|
||||
success = output_img.exists()
|
||||
|
||||
if not success:
|
||||
empty_preview = RESOURCES_DIR / 'empty_preview.png'
|
||||
shutil.copy(str(empty_preview), str(output_img))
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def get_active_library():
|
||||
'''Get the pref library properties from the active library of the asset browser'''
|
||||
prefs = get_addon_prefs()
|
||||
lib_ref = bpy.context.space_data.params.asset_library_reference
|
||||
|
||||
#Check for merged library
|
||||
for l in prefs.libraries:
|
||||
if l.name == lib_ref:
|
||||
return l
|
||||
|
||||
|
||||
def update_library_path():
|
||||
"""Removing all asset libraries and recreate them"""
|
||||
|
||||
print("update_library_path")
|
||||
|
||||
addon_prefs = get_addon_prefs()
|
||||
libs = bpy.context.preferences.filepaths.asset_libraries
|
||||
|
||||
for i, lib in reversed(list(enumerate(libs))):
|
||||
if (addon_lib := addon_prefs.libraries.get(lib.name)): # and addon_lib.path == lib.path
|
||||
bpy.ops.preferences.asset_library_remove(index=i)
|
||||
|
||||
for addon_lib in addon_prefs.libraries:
|
||||
if not addon_lib.use:
|
||||
continue
|
||||
bpy.ops.preferences.asset_library_add(directory=str(addon_lib.path))
|
||||
libs[-1].name = addon_lib.name
|
||||
|
||||
|
||||
def load_library_config(config_path):
|
||||
""""Load library prefs from config path"""
|
||||
if not config_path:
|
||||
return []
|
||||
|
||||
config_path = Path(config_path)
|
||||
|
||||
if not config_path.exists():
|
||||
print(f'Config {config_path} not exist')
|
||||
return []
|
||||
|
||||
prefs = get_addon_prefs()
|
||||
|
||||
libs = []
|
||||
for lib_dict in read_file(config_path):
|
||||
lib = prefs.libraries.add()
|
||||
lib.is_user = False
|
||||
lib.set_dict(lib_dict)
|
||||
libs.append(lib)
|
||||
|
||||
return libs
|
||||
|
||||
|
||||
def load_libraries():
|
||||
""""Load library prefs from config pref and env"""
|
||||
prefs = get_addon_prefs()
|
||||
|
||||
bl_libs = bpy.context.preferences.filepaths.asset_libraries
|
||||
for i, bl_lib in reversed(list(enumerate(bl_libs))):
|
||||
addon_lib = prefs.libraries.get(bl_lib.name)
|
||||
if not addon_lib or addon_lib.is_user:
|
||||
continue
|
||||
|
||||
bpy.ops.preferences.asset_library_remove(index=i)
|
||||
|
||||
# Remove lib from addons preferences
|
||||
for i, addon_lib in reversed(list(enumerate(prefs.libraries))):
|
||||
if not addon_lib.is_user:
|
||||
prefs.libraries.remove(i)
|
||||
|
||||
env_config = os.getenv('ASSET_LIBRARY_CONFIG')
|
||||
libs = load_library_config(env_config) + load_library_config(prefs.config_path)
|
||||
|
||||
return libs
|
||||
|
||||
|
||||
def clear_time_tag(asset):
|
||||
# Created time tag
|
||||
for tag in list(asset.asset_data.tags):
|
||||
try:
|
||||
datetime.strptime(tag.name, "%Y-%m-%d %H:%M")
|
||||
asset.asset_data.tags.remove(tag)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
|
||||
def create_time_tag(asset):
|
||||
asset.asset_data.tags.new(datetime.now().strftime("%Y-%m-%d %H:%M"))
|
||||
|
||||
|
||||
def version_file(path, save_versions=3):
|
||||
if path.exists():
|
||||
for i in range(save_versions):
|
||||
version = save_versions - i
|
||||
version_path = path.with_suffix(f'.blend{version}')
|
||||
if not version_path.exists():
|
||||
continue
|
||||
if i == 0:
|
||||
version_path.unlink()
|
||||
else:
|
||||
version_path.rename(path.with_suffix(f'.blend{version+1}'))
|
||||
|
||||
path.rename(path.with_suffix(f'.blend1'))
|
||||
|
||||
|
||||
def list_datablocks(blend_file, asset_types={"objects", "materials", "node_groups"}):
|
||||
blend_data = {}
|
||||
with bpy.data.temp_data(filepath=str(blend_file)) as temp_data:
|
||||
with temp_data.libraries.load(str(blend_file), link=True) as (data_from, data_to):
|
||||
for asset_type in asset_types:
|
||||
blend_data[asset_type] = getattr(data_from, asset_type)
|
||||
|
||||
return blend_data
|
||||
|
||||
|
||||
def get_asset_data(blend_file, asset_type, name, preview=False):
|
||||
with bpy.data.temp_data(filepath=str(blend_file)) as temp_data:
|
||||
with temp_data.libraries.load(str(blend_file), link=True) as (data_from, data_to):
|
||||
if name not in getattr(data_from, asset_type):
|
||||
return
|
||||
setattr(data_to, asset_type, [name])
|
||||
|
||||
if assets := getattr(data_to, asset_type):
|
||||
asset = assets[0]
|
||||
asset_data = asset.asset_data
|
||||
|
||||
data = {
|
||||
"description": asset_data.description,
|
||||
"catalog_id": asset_data.catalog_id,
|
||||
"catalog_simple_name": asset_data.catalog_simple_name,
|
||||
"path": blend_file,
|
||||
"tags": list(asset_data.tags.keys())
|
||||
}
|
||||
|
||||
if asset.preview and preview:
|
||||
image_size = asset.preview.image_size
|
||||
preview_pixels = [0] * image_size[0] * image_size[1] * 4
|
||||
asset.preview.image_pixels_float.foreach_get(preview_pixels)
|
||||
|
||||
data["preview_pixels"] = preview_pixels
|
||||
data["preview_size"] = list(image_size)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def find_asset_data(name, asset_type, preview=False):
|
||||
"""Find info about an asset found in library"""
|
||||
|
||||
bl_libs = bpy.context.preferences.filepaths.asset_libraries
|
||||
|
||||
# First search for a blend with the same name
|
||||
for bl_lib in bl_libs:
|
||||
for blend_file in Path(bl_lib.path).glob(f"**/{name}.blend"):
|
||||
if asset_data := get_asset_data(blend_file, asset_type, name, preview=preview):
|
||||
asset_data['library'] = bl_lib
|
||||
return asset_data
|
||||
|
||||
# for bl_lib in bl_libs:
|
||||
# for blend_file in Path(bl_lib.path).glob("**/*.blend"):
|
||||
# if asset_data := get_asset_data(blend_file, asset_type, name):
|
||||
# return bl_lib, asset_data
|
||||
|
||||
|
||||
def get_filepath_library(filepath):
|
||||
for lib in bpy.context.preferences.filepaths.asset_libraries:
|
||||
if bpy.path.is_subdir(filepath, lib.path):
|
||||
return lib
|
||||
|
||||
|
||||
def get_asset_catalog_path(asset, fallback=''):
|
||||
if asset.local_id:
|
||||
path = Path(bpy.data.filepath).parent
|
||||
|
||||
elif (lib := get_filepath_library(asset.full_library_path)):
|
||||
if lib:
|
||||
path = lib.path
|
||||
else:
|
||||
return fallback
|
||||
|
||||
if not (catalog := read_catalog(path)):
|
||||
return fallback
|
||||
|
||||
if not (catalog_item := catalog.get(id=asset.metadata.catalog_id, fallback=fallback)):
|
||||
return fallback
|
||||
|
||||
return catalog_item.path
|
||||
|
||||
|
||||
def get_asset_full_path(asset):
|
||||
"""Get a path that represent all informations about an asset path/type/catalog/name"""
|
||||
|
||||
asset_path = asset.full_library_path
|
||||
if asset.local_id:
|
||||
asset_path = f'{bpy.data.filepath}/{asset_path}'
|
||||
|
||||
asset_type, asset_name = Path(asset.full_path).parts[-2:]
|
||||
asset_type = get_asset_type(asset_type)
|
||||
|
||||
return Path(asset_path, asset_type, asset_name).as_posix()
|
||||
|
||||
|
||||
def get_asset_source(datablock):
|
||||
weak_reference = datablock.library_weak_reference
|
||||
if isinstance(datablock, bpy.types.Object) and datablock.data:
|
||||
weak_reference = datablock.data.library_weak_reference
|
||||
|
||||
if weak_reference and (source_path := Path(weak_reference.filepath)).exists():
|
||||
return source_path
|
||||
|
||||
asset_libraries = context.preferences.filepaths.asset_libraries
|
||||
for asset_library in asset_libraries:
|
||||
library_path = Path(asset_library.path)
|
||||
if blend_files := list(library_path.glob(f"**/{datablock.name}.blend")):
|
||||
return
|
||||
|
||||
return datablock.library_weak_reference
|
||||
|
||||
|
||||
def get_blender_cache_dir():
|
||||
if platform.system() == 'Linux':
|
||||
cache_folder = os.path.expandvars('$HOME/.cache/blender')
|
||||
elif platform.system() == 'Windows':
|
||||
cache_folder = os.path.expanduser('%USERPROFILE%/AppData/Local/Blender Foundation/Blender')
|
||||
elif platform.system() == 'Darwin':
|
||||
cache_folder = '/Library/Caches/Blender'
|
||||
|
||||
return Path(cache_folder)
|
||||
|
||||
|
||||
def find_asset_source(library_map, asset_type, name):
|
||||
return next( (l for l, blend_data in sorted(library_map.items(), key=lambda x: x[1]['st_mtime'], reverse=True)
|
||||
if name in blend_data['node_groups']), None)
|
||||
|
||||
|
||||
def asset_library_map():
|
||||
""""Get a mapping of all datablocks of the blend files from the libraries"""
|
||||
asset_libraries = bpy.context.preferences.filepaths.asset_libraries
|
||||
|
||||
cache_file = get_blender_cache_dir() / 'asset-library.json'
|
||||
cache = None
|
||||
if cache_file.exists():
|
||||
cache = read_file(cache_file)
|
||||
|
||||
if cache is None:
|
||||
cache = {}
|
||||
|
||||
file_keys = []
|
||||
|
||||
for asset_library in asset_libraries:
|
||||
library_path = Path(asset_library.path)
|
||||
|
||||
for bl_file in library_path.glob("**/*.blend"):
|
||||
file_keys.append(file_key := bl_file.as_posix())
|
||||
|
||||
st_mtime = bl_file.stat().st_mtime
|
||||
if (bl_cache := cache.get(file_key)) and bl_cache.get('st_mtime') >= st_mtime:
|
||||
continue
|
||||
|
||||
datablocks = list_datablocks(bl_file)
|
||||
cache[file_key] = dict(st_mtime=st_mtime, **datablocks)
|
||||
|
||||
# Remove map when the blend not exists anymore
|
||||
for file_key in list(cache.keys()):
|
||||
if file_key not in file_keys:
|
||||
del cache[file_key]
|
||||
|
||||
write_file(cache_file, cache)
|
||||
|
||||
return cache
|
||||
|
||||
# print(perf_counter() - t0)
|
||||
|
||||
# t0 = perf_counter()
|
||||
# for asset_library in asset_libraries:
|
||||
# library_path = Path(asset_library.path)
|
||||
|
||||
# for blend_file in library_path.glob("**/*.blend"):
|
||||
# with bpy.data.libraries.load(str(blend_file), link=True) as (data_from, data_to):
|
||||
# node_groups = data_from.node_groups
|
||||
# print(node_groups)
|
||||
|
||||
# print(perf_counter() - t0)
|
||||
Reference in New Issue
Block a user