200 lines
5.6 KiB
Python
200 lines
5.6 KiB
Python
|
|
from pathlib import Path
|
|
import uuid
|
|
import bpy
|
|
|
|
|
|
class CatalogItem:
|
|
"""Represent a single item of a catalog"""
|
|
def __init__(self, catalog, path=None, name=None, id=None):
|
|
|
|
self.catalog = catalog
|
|
|
|
self.path = path
|
|
self.name = name
|
|
self.id = str(id or uuid.uuid4())
|
|
|
|
if isinstance(self.path, Path):
|
|
self.path = self.path.as_posix()
|
|
|
|
if self.path and not self.name:
|
|
self.name = self.norm_name(self.path)
|
|
|
|
def parts(self):
|
|
return Path(self.name).parts
|
|
|
|
def norm_name(self, name):
|
|
"""Get a norm name from a catalog_path entry"""
|
|
return name.replace('/', '-')
|
|
|
|
def __repr__(self):
|
|
return f'CatalogItem(name={self.name}, path={self.path}, id={self.id})'
|
|
|
|
|
|
class CatalogContext:
|
|
"""Utility class to get catalog relative to the current context asset browser area"""
|
|
|
|
@staticmethod
|
|
def poll():
|
|
return asset_utils.SpaceAssetInfo.is_asset_browser(bpy.context.space_data)
|
|
|
|
@property
|
|
def id(self):
|
|
if not self.poll():
|
|
return
|
|
|
|
return bpy.context.space_data.params.catalog_id
|
|
|
|
@property
|
|
def item(self):
|
|
if not self.poll():
|
|
return
|
|
|
|
return self.get(id=self.active_id)
|
|
|
|
@property
|
|
def path(self):
|
|
if not self.poll():
|
|
return
|
|
|
|
if self.active_item:
|
|
return self.active_item.path
|
|
|
|
return ''
|
|
|
|
|
|
class Catalog:
|
|
"""Represent the catalog of the blender asset browser library"""
|
|
def __init__(self, directory=None):
|
|
|
|
self.directory = None
|
|
self._data = {}
|
|
|
|
if directory:
|
|
self.directory = Path(directory)
|
|
|
|
self.context = CatalogContext()
|
|
|
|
@property
|
|
def filepath(self):
|
|
"""Get the filepath of the catalog text file relative to the directory"""
|
|
if self.directory:
|
|
return self.directory /'blender_assets.cats.txt'
|
|
|
|
def read(self):
|
|
"""Read the catalog file of the library target directory or of the specified directory"""
|
|
|
|
if not self.filepath or not self.filepath.exists():
|
|
return {}
|
|
|
|
self._data.clear()
|
|
|
|
print(f'Read catalog from {self.filepath}')
|
|
for line in self.filepath.read_text(encoding="utf-8").split('\n'):
|
|
if line.startswith(('VERSION', '#')) or not line:
|
|
continue
|
|
|
|
cat_id, cat_path, cat_name = line.split(':')
|
|
self._data[cat_id] = CatalogItem(self, name=cat_name, id=cat_id, path=cat_path)
|
|
|
|
return self
|
|
|
|
def write(self, sort=True):
|
|
"""Write the catalog file in the library target directory or of the specified directory"""
|
|
|
|
if not self.filepath:
|
|
raise Exception(f'Cannot write catalog {self} no filepath setted')
|
|
|
|
lines = ['VERSION 1', '']
|
|
|
|
catalog_items = list(self)
|
|
if sort:
|
|
catalog_items.sort(key=lambda x : x.path)
|
|
|
|
for catalog_item in catalog_items:
|
|
lines.append(f"{catalog_item.id}:{catalog_item.path}:{catalog_item.name}")
|
|
|
|
print(f'Write Catalog at: {self.filepath}')
|
|
self.filepath.write_text('\n'.join(lines), encoding="utf-8")
|
|
|
|
def get(self, path=None, id=None, fallback=None):
|
|
"""Found a catalog item by is path or id"""
|
|
if isinstance(path, Path):
|
|
path = path.as_posix()
|
|
|
|
if id:
|
|
return self._data.get(id)
|
|
|
|
for catalog_item in self:
|
|
if catalog_item.path == path:
|
|
return catalog_item
|
|
|
|
return fallback
|
|
|
|
def remove(self, catalog_item):
|
|
"""Get a CatalogItem with is path and removing it if found"""
|
|
|
|
if not isinstance(catalog_item, CatalogItem):
|
|
catalog_item = self.get(catalog_item)
|
|
|
|
if catalog_item:
|
|
return self._data.pop(catalog_item.id)
|
|
|
|
print(f'Warning: {catalog_item} cannot be remove, not in {self}')
|
|
return None
|
|
|
|
def add(self, catalog_path):
|
|
"""Adding a CatalogItem with the missing parents"""
|
|
|
|
# Add missing parents catalog
|
|
for parent in Path(catalog_path).parents[:-1]:
|
|
print(parent, self.get(parent))
|
|
if self.get(parent):
|
|
continue
|
|
|
|
cat_item = CatalogItem(self, path=parent)
|
|
self._data[cat_item.id] = cat_item
|
|
|
|
cat_item = self.get(catalog_path)
|
|
if not cat_item:
|
|
cat_item = CatalogItem(self, path=catalog_path)
|
|
self._data[cat_item.id] = cat_item
|
|
|
|
return cat_item
|
|
|
|
def update(self, catalogs):
|
|
'Add or remove catalog entries if on the list given or not'
|
|
|
|
catalogs = set(catalogs) # Remove doubles
|
|
|
|
added = [c for c in catalogs if not self.get(path=c)]
|
|
removed = [c.path for c in self if c.path not in catalogs]
|
|
|
|
if added:
|
|
print(f'{len(added)} Catalog Entry Added \n{tuple(c.name for c in added[:10])}...\n')
|
|
if removed:
|
|
print(f'{len(removed)} Catalog Entry Removed \n{tuple(c.name for c in removed[:10])}...\n')
|
|
|
|
for catalog_item in removed:
|
|
self.remove(catalog_item)
|
|
|
|
for catalog_item in added:
|
|
self.add(catalog_item)
|
|
|
|
def __iter__(self):
|
|
return self._data.values().__iter__()
|
|
|
|
def __getitem__(self, key):
|
|
if isinstance(key, int):
|
|
return self._data.values()[key]
|
|
|
|
return self._data[key]
|
|
|
|
def __contains__(self, item):
|
|
if isinstance(item, str): # item is the id
|
|
return item in self._data
|
|
else:
|
|
return item in self
|
|
|
|
def __repr__(self):
|
|
return f'Catalog(filepath={self.filepath})' |