black format
This commit is contained in:
+2
-4
@@ -1,12 +1,10 @@
|
||||
|
||||
|
||||
from asset_library.common import file_utils
|
||||
from asset_library.common import functions
|
||||
from asset_library.common import synchronize
|
||||
from asset_library.common import template
|
||||
from asset_library.common import catalog
|
||||
|
||||
if 'bpy' in locals():
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
|
||||
importlib.reload(file_utils)
|
||||
@@ -15,4 +13,4 @@ if 'bpy' in locals():
|
||||
importlib.reload(template)
|
||||
importlib.reload(catalog)
|
||||
|
||||
import bpy
|
||||
import bpy
|
||||
|
||||
+133
-86
@@ -1,4 +1,3 @@
|
||||
|
||||
"""
|
||||
Generic Blender functions
|
||||
"""
|
||||
@@ -6,29 +5,31 @@ Generic Blender functions
|
||||
from pathlib import Path
|
||||
from fnmatch import fnmatch
|
||||
from typing import Any, List, Iterable, Optional, Tuple
|
||||
|
||||
Datablock = Any
|
||||
|
||||
import bpy
|
||||
from bpy_extras import asset_utils
|
||||
from asset_library.constants import RESOURCES_DIR
|
||||
#from asset_library.common.file_utils import no
|
||||
|
||||
# from asset_library.common.file_utils import no
|
||||
from os.path import abspath
|
||||
import subprocess
|
||||
|
||||
|
||||
class attr_set():
|
||||
'''Receive a list of tuple [(data_path, "attribute" [, wanted value)] ]
|
||||
class attr_set:
|
||||
"""Receive a list of tuple [(data_path, "attribute" [, wanted value)] ]
|
||||
entering with-statement : Store existing values, assign wanted value (if any)
|
||||
exiting with-statement: Restore values to their old values
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, attrib_list):
|
||||
self.store = []
|
||||
# item = (prop, attr, [new_val])
|
||||
for item in attrib_list:
|
||||
prop, attr = item[:2]
|
||||
self.store.append( (prop, attr, getattr(prop, attr)) )
|
||||
|
||||
self.store.append((prop, attr, getattr(prop, attr)))
|
||||
|
||||
for item in attrib_list:
|
||||
prop, attr = item[:2]
|
||||
|
||||
@@ -36,7 +37,7 @@ class attr_set():
|
||||
try:
|
||||
setattr(prop, attr, item[2])
|
||||
except TypeError:
|
||||
print(f'Cannot set attribute {attr} to {prop}')
|
||||
print(f"Cannot set attribute {attr} to {prop}")
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
@@ -48,25 +49,38 @@ class attr_set():
|
||||
for prop, attr, old_val in self.store:
|
||||
setattr(prop, attr, old_val)
|
||||
|
||||
|
||||
def get_overriden_col(ob, scene=None):
|
||||
scn = scene or bpy.context.scene
|
||||
|
||||
cols = [c for c in bpy.data.collections if scn.user_of_id(c)]
|
||||
|
||||
return next((c for c in cols if ob in c.all_objects[:]
|
||||
if all(not c.override_library for c in get_col_parents(c))), None)
|
||||
|
||||
return next(
|
||||
(
|
||||
c
|
||||
for c in cols
|
||||
if ob in c.all_objects[:]
|
||||
if all(not c.override_library for c in get_col_parents(c))
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def get_view3d_persp():
|
||||
windows = bpy.context.window_manager.windows
|
||||
view_3ds = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
|
||||
view_3d = next((a for a in view_3ds if a.spaces.active.region_3d.view_perspective == 'PERSP'), view_3ds[0])
|
||||
view_3ds = [a for w in windows for a in w.screen.areas if a.type == "VIEW_3D"]
|
||||
view_3d = next(
|
||||
(a for a in view_3ds if a.spaces.active.region_3d.view_perspective == "PERSP"),
|
||||
view_3ds[0],
|
||||
)
|
||||
return view_3d
|
||||
|
||||
|
||||
def get_viewport():
|
||||
screen = bpy.context.screen
|
||||
|
||||
areas = [a for a in screen.areas if a.type == 'VIEW_3D']
|
||||
areas.sort(key=lambda x : x.width*x.height)
|
||||
areas = [a for a in screen.areas if a.type == "VIEW_3D"]
|
||||
areas.sort(key=lambda x: x.width * x.height)
|
||||
|
||||
return areas[-1]
|
||||
|
||||
@@ -81,7 +95,7 @@ def biggest_asset_browser_area(screen: bpy.types.Screen) -> Optional[bpy.types.A
|
||||
|
||||
def area_sorting_key(area: bpy.types.Area) -> Tuple[bool, int]:
|
||||
"""Return area size in pixels."""
|
||||
return (area.width * area.height)
|
||||
return area.width * area.height
|
||||
|
||||
areas = list(suitable_areas(screen))
|
||||
if not areas:
|
||||
@@ -89,6 +103,7 @@ def biggest_asset_browser_area(screen: bpy.types.Screen) -> Optional[bpy.types.A
|
||||
|
||||
return max(areas, key=area_sorting_key)
|
||||
|
||||
|
||||
def suitable_areas(screen: bpy.types.Screen) -> Iterable[bpy.types.Area]:
|
||||
"""Generator, yield Asset Browser areas."""
|
||||
|
||||
@@ -98,6 +113,7 @@ def suitable_areas(screen: bpy.types.Screen) -> Iterable[bpy.types.Area]:
|
||||
continue
|
||||
yield area
|
||||
|
||||
|
||||
def area_from_context(context: bpy.types.Context) -> Optional[bpy.types.Area]:
|
||||
"""Return an Asset Browser suitable for the given category.
|
||||
|
||||
@@ -122,6 +138,7 @@ def area_from_context(context: bpy.types.Context) -> Optional[bpy.types.Area]:
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def activate_asset(
|
||||
asset: bpy.types.Action, asset_browser: bpy.types.Area, *, deferred: bool
|
||||
) -> None:
|
||||
@@ -131,19 +148,25 @@ def activate_asset(
|
||||
assert asset_utils.SpaceAssetInfo.is_asset_browser(space_data)
|
||||
space_data.activate_asset_by_id(asset, deferred=deferred)
|
||||
|
||||
|
||||
def active_catalog_id(asset_browser: bpy.types.Area) -> str:
|
||||
"""Return the ID of the catalog shown in the asset browser."""
|
||||
return params(asset_browser).catalog_id
|
||||
|
||||
def get_asset_space_params(asset_browser: bpy.types.Area) -> bpy.types.FileAssetSelectParams:
|
||||
|
||||
def get_asset_space_params(
|
||||
asset_browser: bpy.types.Area,
|
||||
) -> bpy.types.FileAssetSelectParams:
|
||||
"""Return the asset browser parameters given its Area."""
|
||||
space_data = asset_browser.spaces[0]
|
||||
assert asset_utils.SpaceAssetInfo.is_asset_browser(space_data)
|
||||
return space_data.params
|
||||
|
||||
|
||||
def refresh_asset_browsers():
|
||||
for area in suitable_areas(bpy.context.screen):
|
||||
bpy.ops.asset.library_refresh({"area": area, 'region': area.regions[3]})
|
||||
bpy.ops.asset.library_refresh({"area": area, "region": area.regions[3]})
|
||||
|
||||
|
||||
def tag_redraw(screen: bpy.types.Screen) -> None:
|
||||
"""Tag all asset browsers for redrawing."""
|
||||
@@ -151,6 +174,7 @@ def tag_redraw(screen: bpy.types.Screen) -> None:
|
||||
for area in suitable_areas(screen):
|
||||
area.tag_redraw()
|
||||
|
||||
|
||||
# def get_blender_command(file=None, script=None, background=True, **args):
|
||||
# '''Return a Blender Command as a list to be used in a subprocess'''
|
||||
|
||||
@@ -166,9 +190,10 @@ def tag_redraw(screen: bpy.types.Screen) -> None:
|
||||
# cmd += ['--']
|
||||
# for k, v in args.items():
|
||||
# cmd += [f"--{k.replace('_', '-')}", str(v)]
|
||||
|
||||
|
||||
# return cmd
|
||||
|
||||
|
||||
def norm_value(value):
|
||||
if isinstance(value, (tuple, list)):
|
||||
values = []
|
||||
@@ -177,7 +202,7 @@ def norm_value(value):
|
||||
v = json.dumps(v)
|
||||
values.append(v)
|
||||
|
||||
return values
|
||||
return values
|
||||
|
||||
if isinstance(value, Path):
|
||||
return str(value)
|
||||
@@ -186,31 +211,35 @@ def norm_value(value):
|
||||
value = json.dumps(value)
|
||||
return value
|
||||
|
||||
def norm_arg(arg_name, format=str.lower, prefix='--', separator='-'):
|
||||
|
||||
def norm_arg(arg_name, format=str.lower, prefix="--", separator="-"):
|
||||
arg_name = norm_str(arg_name, format=format, separator=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, focus=True, blendfile=None, script=None, **kargs
|
||||
):
|
||||
cmd = [str(blender)] if blender else [bpy.app.binary_path]
|
||||
|
||||
if background:
|
||||
cmd += ['--background']
|
||||
cmd += ["--background"]
|
||||
|
||||
if not focus and not background:
|
||||
cmd += ['--no-window-focus']
|
||||
cmd += ['--window-geometry', '5000', '0', '10', '10']
|
||||
cmd += ["--no-window-focus"]
|
||||
cmd += ["--window-geometry", "5000", "0", "10", "10"]
|
||||
|
||||
cmd += ['--python-use-system-env']
|
||||
cmd += ["--python-use-system-env"]
|
||||
|
||||
if blendfile:
|
||||
cmd += [str(blendfile)]
|
||||
|
||||
|
||||
if script:
|
||||
cmd += ['--python', str(script)]
|
||||
|
||||
cmd += ["--python", str(script)]
|
||||
|
||||
if kargs:
|
||||
cmd += ['--']
|
||||
cmd += ["--"]
|
||||
for k, v in kargs.items():
|
||||
k = norm_arg(k)
|
||||
v = norm_value(v)
|
||||
@@ -223,18 +252,18 @@ def get_bl_cmd(blender=None, background=False, focus=True, blendfile=None, scrip
|
||||
|
||||
return cmd
|
||||
|
||||
def get_addon_prefs():
|
||||
addon_name = __package__.split('.')[0]
|
||||
return bpy.context.preferences.addons[addon_name].preferences
|
||||
|
||||
def get_addon_prefs():
|
||||
addon_name = __package__.split(".")[0]
|
||||
return bpy.context.preferences.addons[addon_name].preferences
|
||||
|
||||
|
||||
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'
|
||||
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)
|
||||
|
||||
@@ -243,16 +272,17 @@ def thumbnail_blend_file(input_blend, output_img):
|
||||
success = output_img.exists()
|
||||
|
||||
if not success:
|
||||
empty_preview = RESOURCES_DIR / 'empty_preview.png'
|
||||
empty_preview = RESOURCES_DIR / "empty_preview.png"
|
||||
shutil.copy(str(empty_preview), str(output_img))
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def get_col_parents(col, root=None, cols=None):
|
||||
'''Return a list of parents collections of passed col
|
||||
"""Return a list of parents collections of passed col
|
||||
root : Pass a collection to search in (recursive)
|
||||
else search in master collection
|
||||
'''
|
||||
"""
|
||||
if cols is None:
|
||||
cols = []
|
||||
|
||||
@@ -267,14 +297,23 @@ def get_col_parents(col, root=None, cols=None):
|
||||
cols = get_col_parents(col, root=sub, cols=cols)
|
||||
return cols
|
||||
|
||||
|
||||
def get_overriden_col(ob, scene=None):
|
||||
'''Get the collection use for making the override'''
|
||||
"""Get the collection use for making the override"""
|
||||
scn = scene or bpy.context.scene
|
||||
|
||||
cols = [c for c in bpy.data.collections if scn.user_of_id(c)]
|
||||
|
||||
return next((c for c in cols if ob in c.all_objects[:]
|
||||
if all(not c.override_library for c in get_col_parents(c))), None)
|
||||
|
||||
return next(
|
||||
(
|
||||
c
|
||||
for c in cols
|
||||
if ob in c.all_objects[:]
|
||||
if all(not c.override_library for c in get_col_parents(c))
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def load_assets_from(filepath: Path) -> List[Datablock]:
|
||||
if not has_assets(filepath):
|
||||
@@ -306,6 +345,7 @@ def load_assets_from(filepath: Path) -> List[Datablock]:
|
||||
loaded_assets.append(datablock)
|
||||
return loaded_assets
|
||||
|
||||
|
||||
def has_assets(filepath: Path) -> bool:
|
||||
with bpy.data.libraries.load(str(filepath), assets_only=True) as (
|
||||
data_from,
|
||||
@@ -318,51 +358,49 @@ def has_assets(filepath: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def copy_frames(start, end, offset, path):
|
||||
for i in range (start, end):
|
||||
src = path.replace('####', f'{i:04d}')
|
||||
dst = src.replace(src.split('_')[-1].split('.')[0], f'{i+offset:04d}')
|
||||
for i in range(start, end):
|
||||
src = path.replace("####", f"{i:04d}")
|
||||
dst = src.replace(src.split("_")[-1].split(".")[0], f"{i+offset:04d}")
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
def split_path(path) :
|
||||
try :
|
||||
|
||||
def split_path(path):
|
||||
try:
|
||||
bone_name = path.split('["')[1].split('"]')[0]
|
||||
except :
|
||||
except:
|
||||
bone_name = None
|
||||
try :
|
||||
try:
|
||||
prop_name = path.split('["')[2].split('"]')[0]
|
||||
except :
|
||||
prop_name = path.split('.')[-1]
|
||||
except:
|
||||
prop_name = path.split(".")[-1]
|
||||
|
||||
return bone_name, prop_name
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def load_datablocks(src, names=None, type='objects', link=True, expr=None, assets_only=False) -> list:
|
||||
def load_datablocks(
|
||||
src, names=None, type="objects", link=True, expr=None, assets_only=False
|
||||
) -> list:
|
||||
return_list = not isinstance(names, str)
|
||||
names = names or []
|
||||
|
||||
if not isinstance(names, (list, tuple)):
|
||||
names = [names]
|
||||
|
||||
|
||||
if isinstance(expr, str):
|
||||
pattern = expr
|
||||
expr = lambda x : fnmatch(x, pattern)
|
||||
|
||||
with bpy.data.libraries.load(str(src), link=link,assets_only=assets_only) as (data_from, data_to):
|
||||
expr = lambda x: fnmatch(x, pattern)
|
||||
|
||||
with bpy.data.libraries.load(str(src), link=link, assets_only=assets_only) as (
|
||||
data_from,
|
||||
data_to,
|
||||
):
|
||||
datablocks = getattr(data_from, type)
|
||||
if expr:
|
||||
names += [i for i in datablocks if expr(i)]
|
||||
elif not names:
|
||||
names = datablocks
|
||||
|
||||
|
||||
setattr(data_to, type, names)
|
||||
|
||||
datablocks = getattr(data_to, type)
|
||||
@@ -373,23 +411,26 @@ def load_datablocks(src, names=None, type='objects', link=True, expr=None, asset
|
||||
elif datablocks:
|
||||
return datablocks[0]
|
||||
|
||||
|
||||
"""
|
||||
# --- Collection handling
|
||||
"""
|
||||
|
||||
|
||||
def col_as_asset(col, verbose=False):
|
||||
if col is None:
|
||||
return
|
||||
if verbose:
|
||||
print('linking:', col.name)
|
||||
print("linking:", col.name)
|
||||
pcol = bpy.data.collections.new(col.name)
|
||||
bpy.context.scene.collection.children.link(pcol)
|
||||
pcol.children.link(col)
|
||||
pcol.asset_mark()
|
||||
return pcol
|
||||
|
||||
|
||||
def load_col(filepath, name, link=True, override=True, rig_pattern=None, context=None):
|
||||
'''Link a collection by name from a file and override if has armature'''
|
||||
"""Link a collection by name from a file and override if has armature"""
|
||||
|
||||
# with bpy.data.libraries.load(filepath, link=link) as (data_from, data_to):
|
||||
# data_to.collections = [c for c in data_from.collections if c == name]
|
||||
@@ -398,12 +439,12 @@ def load_col(filepath, name, link=True, override=True, rig_pattern=None, context
|
||||
# return data_to.collections[0]
|
||||
context = context or bpy.context
|
||||
|
||||
col = load_datablocks(filepath, name, link=link, type='collections')
|
||||
|
||||
col = load_datablocks(filepath, name, link=link, type="collections")
|
||||
|
||||
## create instance object
|
||||
inst = bpy.data.objects.new(col.name, None)
|
||||
inst.instance_collection = col
|
||||
inst.instance_type = 'COLLECTION'
|
||||
inst.instance_type = "COLLECTION"
|
||||
context.scene.collection.objects.link(inst)
|
||||
|
||||
# make active
|
||||
@@ -413,26 +454,29 @@ def load_col(filepath, name, link=True, override=True, rig_pattern=None, context
|
||||
## simple object (no armatures)
|
||||
if not link or not override:
|
||||
return inst
|
||||
if not next((o for o in col.all_objects if o.type == 'ARMATURE'), None):
|
||||
if not next((o for o in col.all_objects if o.type == "ARMATURE"), None):
|
||||
return inst
|
||||
|
||||
|
||||
## Create the override
|
||||
# Search
|
||||
parent_cols = inst.users_collection
|
||||
child_cols = [child for pcol in parent_cols for child in pcol.children]
|
||||
|
||||
params = {'active_object': inst, 'selected_objects': [inst]}
|
||||
params = {"active_object": inst, "selected_objects": [inst]}
|
||||
try:
|
||||
bpy.ops.object.make_override_library(params)
|
||||
|
||||
## check which collection is new in parents collection
|
||||
asset_col = next((c for pcol in parent_cols for c in pcol.children if c not in child_cols), None)
|
||||
asset_col = next(
|
||||
(c for pcol in parent_cols for c in pcol.children if c not in child_cols),
|
||||
None,
|
||||
)
|
||||
if not asset_col:
|
||||
print('Overriden, but no collection found !!')
|
||||
print("Overriden, but no collection found !!")
|
||||
return
|
||||
|
||||
|
||||
for ob in asset_col.all_objects:
|
||||
if ob.type != 'ARMATURE':
|
||||
if ob.type != "ARMATURE":
|
||||
continue
|
||||
if rig_pattern and not fnmatch(ob.name, rig_pattern):
|
||||
continue
|
||||
@@ -444,37 +488,40 @@ def load_col(filepath, name, link=True, override=True, rig_pattern=None, context
|
||||
return ob
|
||||
|
||||
except Exception as e:
|
||||
print(f'Override failed on {col.name}')
|
||||
print(f"Override failed on {col.name}")
|
||||
print(e)
|
||||
|
||||
|
||||
return inst
|
||||
|
||||
|
||||
def get_preview(asset_path='', asset_name=''):
|
||||
def get_preview(asset_path="", asset_name=""):
|
||||
asset_preview_dir = Path(asset_path).parents[1]
|
||||
name = asset_name.lower()
|
||||
return next((f for f in asset_preview_dir.rglob('*') if f.stem.lower().endswith(name)), None)
|
||||
return next(
|
||||
(f for f in asset_preview_dir.rglob("*") if f.stem.lower().endswith(name)), None
|
||||
)
|
||||
|
||||
|
||||
def get_object_libraries(ob):
|
||||
if ob is None:
|
||||
return []
|
||||
|
||||
|
||||
libraries = [ob.library]
|
||||
if ob.data:
|
||||
libraries += [ob.data.library]
|
||||
|
||||
if ob.type in ('MESH', 'CURVE'):
|
||||
if ob.type in ("MESH", "CURVE"):
|
||||
libraries += [m.library for m in ob.data.materials if m]
|
||||
|
||||
|
||||
filepaths = []
|
||||
for l in libraries:
|
||||
if not l or not l.filepath:
|
||||
continue
|
||||
|
||||
|
||||
absolute_filepath = abspath(bpy.path.abspath(l.filepath, library=l))
|
||||
if absolute_filepath in filepaths:
|
||||
continue
|
||||
|
||||
filepaths.append(absolute_filepath)
|
||||
|
||||
return filepaths
|
||||
return filepaths
|
||||
|
||||
+43
-36
@@ -1,4 +1,3 @@
|
||||
|
||||
from pathlib import Path
|
||||
import uuid
|
||||
import bpy
|
||||
@@ -6,6 +5,7 @@ import bpy
|
||||
|
||||
class CatalogItem:
|
||||
"""Represent a single item of a catalog"""
|
||||
|
||||
def __init__(self, catalog, path=None, name=None, id=None):
|
||||
|
||||
self.catalog = catalog
|
||||
@@ -16,7 +16,7 @@ class CatalogItem:
|
||||
|
||||
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)
|
||||
|
||||
@@ -25,10 +25,10 @@ class CatalogItem:
|
||||
|
||||
def norm_name(self, name):
|
||||
"""Get a norm name from a catalog_path entry"""
|
||||
return name.replace('/', '-')
|
||||
return name.replace("/", "-")
|
||||
|
||||
def __repr__(self):
|
||||
return f'CatalogItem(name={self.name}, path={self.path}, id={self.id})'
|
||||
return f"CatalogItem(name={self.name}, path={self.path}, id={self.id})"
|
||||
|
||||
|
||||
class CatalogContext:
|
||||
@@ -60,11 +60,12 @@ class CatalogContext:
|
||||
if self.active_item:
|
||||
return self.active_item.path
|
||||
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
class Catalog:
|
||||
"""Represent the catalog of the blender asset browser library"""
|
||||
|
||||
def __init__(self, directory=None):
|
||||
|
||||
self.directory = None
|
||||
@@ -72,14 +73,14 @@ class Catalog:
|
||||
|
||||
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'
|
||||
return self.directory / "blender_assets.cats.txt"
|
||||
|
||||
def read(self):
|
||||
"""Read the catalog file of the library target directory or of the specified directory"""
|
||||
@@ -88,47 +89,49 @@ class Catalog:
|
||||
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:
|
||||
|
||||
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)
|
||||
|
||||
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', '']
|
||||
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)
|
||||
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")
|
||||
|
||||
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):
|
||||
@@ -140,7 +143,7 @@ class Catalog:
|
||||
if catalog_item:
|
||||
return self._data.pop(catalog_item.id)
|
||||
|
||||
print(f'Warning: {catalog_item} cannot be remove, not in {self}')
|
||||
print(f"Warning: {catalog_item} cannot be remove, not in {self}")
|
||||
return None
|
||||
|
||||
def add(self, catalog_path):
|
||||
@@ -151,7 +154,7 @@ class Catalog:
|
||||
print(parent, self.get(parent))
|
||||
if self.get(parent):
|
||||
continue
|
||||
|
||||
|
||||
cat_item = CatalogItem(self, path=parent)
|
||||
self._data[cat_item.id] = cat_item
|
||||
|
||||
@@ -161,19 +164,23 @@ class Catalog:
|
||||
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
|
||||
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')
|
||||
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')
|
||||
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)
|
||||
@@ -183,7 +190,7 @@ class Catalog:
|
||||
|
||||
def __iter__(self):
|
||||
return self._data.values().__iter__()
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, int):
|
||||
return self._data.values()[key]
|
||||
@@ -191,10 +198,10 @@ class Catalog:
|
||||
return self._data[key]
|
||||
|
||||
def __contains__(self, item):
|
||||
if isinstance(item, str): # item is the id
|
||||
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})'
|
||||
return f"Catalog(filepath={self.filepath})"
|
||||
|
||||
+134
-93
@@ -1,4 +1,3 @@
|
||||
|
||||
"""Generic python functions to make operation on file and names"""
|
||||
|
||||
import fnmatch
|
||||
@@ -15,6 +14,7 @@ import shutil
|
||||
|
||||
import contextlib
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def cd(path):
|
||||
"""Changes working directory and returns to previous on exit."""
|
||||
@@ -25,20 +25,24 @@ 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'''
|
||||
"""Install a python module with pip or return it if already installed"""
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
except ModuleNotFoundError:
|
||||
print(f'Installing Module {module_name} ....')
|
||||
print(f"Installing Module {module_name} ....")
|
||||
|
||||
subprocess.call([sys.executable, "-m", "ensurepip"])
|
||||
subprocess.call(
|
||||
[sys.executable, "-m", "pip", "install", package_name or module_name]
|
||||
)
|
||||
|
||||
subprocess.call([sys.executable, '-m', 'ensurepip'])
|
||||
subprocess.call([sys.executable, '-m', 'pip', 'install', package_name or module_name])
|
||||
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
|
||||
return module
|
||||
|
||||
|
||||
def import_module_from_path(path):
|
||||
from importlib import util
|
||||
|
||||
@@ -46,50 +50,64 @@ def import_module_from_path(path):
|
||||
path = Path(path)
|
||||
spec = util.spec_from_file_location(path.stem, str(path))
|
||||
mod = util.module_from_spec(spec)
|
||||
|
||||
|
||||
spec.loader.exec_module(mod)
|
||||
|
||||
return mod
|
||||
except Exception as e:
|
||||
print(f'Cannot import file {path}')
|
||||
print(f"Cannot import file {path}")
|
||||
print(e)
|
||||
|
||||
def norm_str(string, separator='_', format=str.lower, padding=0):
|
||||
|
||||
def norm_str(string, separator="_", format=str.lower, padding=0):
|
||||
string = str(string)
|
||||
string = string.replace('_', ' ')
|
||||
string = string.replace('-', ' ')
|
||||
string = re.sub('[ ]+', ' ', string)
|
||||
string = re.sub('[ ]+\/[ ]+', '/', string)
|
||||
string = string.replace("_", " ")
|
||||
string = string.replace("-", " ")
|
||||
string = re.sub("[ ]+", " ", string)
|
||||
string = re.sub("[ ]+\/[ ]+", "/", string)
|
||||
string = string.strip()
|
||||
|
||||
if format:
|
||||
string = format(string)
|
||||
|
||||
|
||||
# Padd rightest number
|
||||
string = re.sub(r'(\d+)(?!.*\d)', lambda x : x.group(1).zfill(padding), string)
|
||||
|
||||
string = string.replace(' ', separator)
|
||||
string = unicodedata.normalize('NFKD', string).encode('ASCII', 'ignore').decode("utf-8")
|
||||
string = re.sub(r"(\d+)(?!.*\d)", lambda x: x.group(1).zfill(padding), string)
|
||||
|
||||
string = string.replace(" ", separator)
|
||||
string = (
|
||||
unicodedata.normalize("NFKD", string).encode("ASCII", "ignore").decode("utf-8")
|
||||
)
|
||||
|
||||
return string
|
||||
|
||||
|
||||
def remove_version(filepath):
|
||||
pattern = '_v[0-9]+\.'
|
||||
pattern = "_v[0-9]+\."
|
||||
search = re.search(pattern, filepath)
|
||||
|
||||
if search:
|
||||
filepath = filepath.replace(search.group()[:-1], '')
|
||||
filepath = filepath.replace(search.group()[:-1], "")
|
||||
|
||||
return Path(filepath).name
|
||||
|
||||
|
||||
def is_exclude(name, patterns) -> bool:
|
||||
# from fnmatch import fnmatch
|
||||
if not isinstance(patterns, (list,tuple)) :
|
||||
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
|
||||
|
||||
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.
|
||||
pattern -> str: Regex pattern to group files.
|
||||
only_matching -> bool: Discard files that aren't matched by regex pattern.
|
||||
@@ -97,7 +115,7 @@ def get_last_files(root, pattern=r'_v\d{3}\.\w+', only_matching=False, ex_file=N
|
||||
ex_dir -> list : List of fn_match pattern of directory name to skip.
|
||||
keep -> int: Number of lasts versions to keep when there are mutliple versionned files (e.g: 1 keep only last).
|
||||
verbose -> bool: Print infos in console.
|
||||
'''
|
||||
"""
|
||||
|
||||
files = []
|
||||
if ex_file is None:
|
||||
@@ -111,7 +129,9 @@ def get_last_files(root, pattern=r'_v\d{3}\.\w+', only_matching=False, ex_file=N
|
||||
|
||||
dirs = [f for f in all_items if f.is_dir()]
|
||||
|
||||
for i in range(len(allfiles)-1,-1,-1):# fastest way to iterate on index in reverse
|
||||
for i in range(
|
||||
len(allfiles) - 1, -1, -1
|
||||
): # fastest way to iterate on index in reverse
|
||||
if not re.search(pattern, allfiles[i].name):
|
||||
if only_matching:
|
||||
allfiles.pop(i)
|
||||
@@ -119,7 +139,10 @@ def get_last_files(root, pattern=r'_v\d{3}\.\w+', only_matching=False, ex_file=N
|
||||
files.append(allfiles.pop(i).path)
|
||||
|
||||
# separate remaining files in prefix grouped lists
|
||||
lilist = [list(v) for k, v in groupby(allfiles, key=lambda x: re.split(pattern, x.name)[0])]
|
||||
lilist = [
|
||||
list(v)
|
||||
for k, v in groupby(allfiles, key=lambda x: re.split(pattern, x.name)[0])
|
||||
]
|
||||
|
||||
# get only item last of each sorted grouplist
|
||||
for l in lilist:
|
||||
@@ -128,17 +151,26 @@ def get_last_files(root, pattern=r'_v\d{3}\.\w+', only_matching=False, ex_file=N
|
||||
files.append(f.path)
|
||||
|
||||
if verbose and len(l) > 1:
|
||||
print(f'{root}: keep {str([x.name for x in versions])} out of {len(l)} elements')
|
||||
print(
|
||||
f"{root}: keep {str([x.name for x in versions])} out of {len(l)} elements"
|
||||
)
|
||||
|
||||
for d in dirs: # recursively treat all detected directory
|
||||
for d in dirs: # recursively treat all detected directory
|
||||
if ex_dir and is_exclude(d.name, ex_dir):
|
||||
# skip folder with excluded name
|
||||
# skip folder with excluded name
|
||||
continue
|
||||
files += get_last_files(
|
||||
d.path, pattern=pattern, only_matching=only_matching, ex_file=ex_file, ex_dir=ex_dir, keep=keep)
|
||||
d.path,
|
||||
pattern=pattern,
|
||||
only_matching=only_matching,
|
||||
ex_file=ex_file,
|
||||
ex_dir=ex_dir,
|
||||
keep=keep,
|
||||
)
|
||||
|
||||
return sorted(files)
|
||||
|
||||
|
||||
def copy_file(src, dst, only_new=False, only_recent=False):
|
||||
if dst.exists():
|
||||
if only_new:
|
||||
@@ -147,19 +179,20 @@ def copy_file(src, dst, only_new=False, only_recent=False):
|
||||
return
|
||||
|
||||
dst.parent.mkdir(exist_ok=True, parents=True)
|
||||
print(f'Copy file from {src} to {dst}')
|
||||
if platform.system() == 'Windows':
|
||||
subprocess.call(['copy', str(src), str(dst)], shell=True)
|
||||
print(f"Copy file from {src} to {dst}")
|
||||
if platform.system() == "Windows":
|
||||
subprocess.call(["copy", str(src), str(dst)], shell=True)
|
||||
else:
|
||||
subprocess.call(['cp', str(src), str(dst)])
|
||||
subprocess.call(["cp", str(src), str(dst)])
|
||||
|
||||
def copy_dir(src, dst, only_new=False, only_recent=False, excludes=['.*'], includes=[]):
|
||||
|
||||
def copy_dir(src, dst, only_new=False, only_recent=False, excludes=[".*"], includes=[]):
|
||||
src, dst = Path(src), Path(dst)
|
||||
|
||||
if includes:
|
||||
includes = r'|'.join([fnmatch.translate(x) for x in includes])
|
||||
includes = r"|".join([fnmatch.translate(x) for x in includes])
|
||||
if excludes:
|
||||
excludes = r'|'.join([fnmatch.translate(x) for x in excludes])
|
||||
excludes = r"|".join([fnmatch.translate(x) for x in excludes])
|
||||
|
||||
if dst.is_dir():
|
||||
dst.mkdir(exist_ok=True, parents=True)
|
||||
@@ -170,149 +203,157 @@ def copy_dir(src, dst, only_new=False, only_recent=False, excludes=['.*'], inclu
|
||||
copy_file(src, dst, only_new=only_new, only_recent=only_recent)
|
||||
|
||||
elif src.is_dir():
|
||||
src_files = list(src.rglob('*'))
|
||||
src_files = list(src.rglob("*"))
|
||||
if excludes:
|
||||
src_files = [f for f in src_files if not re.match(excludes, f.name)]
|
||||
|
||||
if includes:
|
||||
src_files = [f for f in src_files if re.match(includes, f.name)]
|
||||
|
||||
dst_files = [dst/f.relative_to(src) for f in src_files]
|
||||
dst_files = [dst / f.relative_to(src) for f in src_files]
|
||||
|
||||
for src_file, dst_file in zip(src_files, dst_files) :
|
||||
for src_file, dst_file in zip(src_files, dst_files):
|
||||
if src_file.is_dir():
|
||||
dst_file.mkdir(exist_ok=True, parents=True)
|
||||
else:
|
||||
copy_file(src_file, dst_file, only_new=only_new, only_recent=only_recent)
|
||||
copy_file(
|
||||
src_file, dst_file, only_new=only_new, only_recent=only_recent
|
||||
)
|
||||
|
||||
|
||||
def open_file(filepath, select=False):
|
||||
'''Open a filepath inside the os explorer'''
|
||||
|
||||
if platform.system() == 'Darwin': # macOS
|
||||
cmd = ['open']
|
||||
elif platform.system() == 'Windows': # Windows
|
||||
cmd = ['explorer']
|
||||
"""Open a filepath inside the os explorer"""
|
||||
|
||||
if platform.system() == "Darwin": # macOS
|
||||
cmd = ["open"]
|
||||
elif platform.system() == "Windows": # Windows
|
||||
cmd = ["explorer"]
|
||||
if select:
|
||||
cmd += ['/select,']
|
||||
else: # linux variants
|
||||
cmd = ['xdg-open']
|
||||
cmd += ["/select,"]
|
||||
else: # linux variants
|
||||
cmd = ["xdg-open"]
|
||||
if select:
|
||||
cmd = ['nemo']
|
||||
|
||||
cmd = ["nemo"]
|
||||
|
||||
cmd += [str(filepath)]
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
|
||||
def open_blender_file(filepath=None):
|
||||
filepath = filepath or bpy.data.filepath
|
||||
|
||||
|
||||
cmd = sys.argv
|
||||
|
||||
|
||||
# if no filepath, use command as is to reopen blender
|
||||
if filepath != '':
|
||||
if len(cmd) > 1 and cmd[1].endswith('.blend'):
|
||||
if filepath != "":
|
||||
if len(cmd) > 1 and cmd[1].endswith(".blend"):
|
||||
cmd[1] = str(filepath)
|
||||
else:
|
||||
cmd.insert(1, str(filepath))
|
||||
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
def read_file(path):
|
||||
'''Read a file with an extension in (json, yaml, yml, txt)'''
|
||||
|
||||
exts = ('.json', '.yaml', '.yml', '.txt')
|
||||
def read_file(path):
|
||||
"""Read a file with an extension in (json, yaml, yml, txt)"""
|
||||
|
||||
exts = (".json", ".yaml", ".yml", ".txt")
|
||||
|
||||
if not path:
|
||||
print('Try to read empty file')
|
||||
|
||||
print("Try to read empty file")
|
||||
|
||||
path = Path(path)
|
||||
if not path.exists():
|
||||
print('File not exist', path)
|
||||
print("File not exist", path)
|
||||
return
|
||||
|
||||
|
||||
if path.suffix not in exts:
|
||||
print(f'Cannot read file {path}, extension must be in {exts}')
|
||||
print(f"Cannot read file {path}, extension must be in {exts}")
|
||||
return
|
||||
|
||||
txt = path.read_text()
|
||||
data = None
|
||||
|
||||
if path.suffix.lower() in ('.yaml', '.yml'):
|
||||
yaml = install_module('yaml')
|
||||
if path.suffix.lower() in (".yaml", ".yml"):
|
||||
yaml = install_module("yaml")
|
||||
try:
|
||||
data = yaml.safe_load(txt)
|
||||
except Exception:
|
||||
print(f'Could not load yaml file {path}')
|
||||
print(f"Could not load yaml file {path}")
|
||||
return
|
||||
elif path.suffix.lower() == '.json':
|
||||
elif path.suffix.lower() == ".json":
|
||||
try:
|
||||
data = json.loads(txt)
|
||||
except Exception:
|
||||
print(f'Could not load json file {path}')
|
||||
print(f"Could not load json file {path}")
|
||||
return
|
||||
else:
|
||||
data = txt
|
||||
|
||||
return data
|
||||
|
||||
def write_file(path, data, indent=4):
|
||||
'''Read a file with an extension in (json, yaml, yml, text)'''
|
||||
|
||||
exts = ('.json', '.yaml', '.yml', '.txt')
|
||||
def write_file(path, data, indent=4):
|
||||
"""Read a file with an extension in (json, yaml, yml, text)"""
|
||||
|
||||
exts = (".json", ".yaml", ".yml", ".txt")
|
||||
|
||||
if not path:
|
||||
print('Try to write empty file')
|
||||
|
||||
print("Try to write empty file")
|
||||
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if path.suffix not in exts:
|
||||
print(f'Cannot read file {path}, extension must be in {exts}')
|
||||
print(f"Cannot read file {path}, extension must be in {exts}")
|
||||
return
|
||||
|
||||
if path.suffix.lower() in ('.yaml', '.yml'):
|
||||
yaml = install_module('yaml')
|
||||
if path.suffix.lower() in (".yaml", ".yml"):
|
||||
yaml = install_module("yaml")
|
||||
try:
|
||||
path.write_text(yaml.dump(data), encoding='utf8')
|
||||
path.write_text(yaml.dump(data), encoding="utf8")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(f'Could not write yaml file {path}')
|
||||
print(f"Could not write yaml file {path}")
|
||||
return
|
||||
elif path.suffix.lower() == '.json':
|
||||
elif path.suffix.lower() == ".json":
|
||||
try:
|
||||
path.write_text(json.dumps(data, indent=indent), encoding='utf8')
|
||||
path.write_text(json.dumps(data, indent=indent), encoding="utf8")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(f'Could not write json file {path}')
|
||||
print(f"Could not write json file {path}")
|
||||
return
|
||||
else:
|
||||
data = path.write_text(data, encoding='utf8')
|
||||
data = path.write_text(data, encoding="utf8")
|
||||
|
||||
|
||||
def synchronize(src, dst, only_new=False, only_recent=False, clear=False):
|
||||
|
||||
#actionlib_dir = get_actionlib_dir(custom=custom)
|
||||
#local_actionlib_dir = get_actionlib_dir(local=True, custom=custom)
|
||||
# actionlib_dir = get_actionlib_dir(custom=custom)
|
||||
# local_actionlib_dir = get_actionlib_dir(local=True, custom=custom)
|
||||
|
||||
try:
|
||||
if clear and Path(dst).exists():
|
||||
shutil.rmtree(dst)
|
||||
|
||||
#set_actionlib_dir(custom=custom)
|
||||
# set_actionlib_dir(custom=custom)
|
||||
|
||||
script = Path(__file__).parent / 'synchronize.py'
|
||||
script = Path(__file__).parent / "synchronize.py"
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
script,
|
||||
'--src', str(src),
|
||||
'--dst', str(dst),
|
||||
'--only-new', json.dumps(only_new),
|
||||
'--only-recent', json.dumps(only_recent),
|
||||
"--src",
|
||||
str(src),
|
||||
"--dst",
|
||||
str(dst),
|
||||
"--only-new",
|
||||
json.dumps(only_new),
|
||||
"--only-recent",
|
||||
json.dumps(only_recent),
|
||||
]
|
||||
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
+48
-53
@@ -11,7 +11,8 @@ import os
|
||||
import re
|
||||
|
||||
import time
|
||||
#from asset_library.constants import ASSETLIB_FILENAME
|
||||
|
||||
# from asset_library.constants import ASSETLIB_FILENAME
|
||||
import inspect
|
||||
from asset_library.common.file_utils import read_file
|
||||
from asset_library.common.bl_utils import get_addon_prefs
|
||||
@@ -21,34 +22,37 @@ import bpy
|
||||
|
||||
|
||||
def command(func):
|
||||
'''Decorator to be used from printed functions argument and run time'''
|
||||
func_name = func.__name__.replace('_', ' ').title()
|
||||
|
||||
"""Decorator to be used from printed functions argument and run time"""
|
||||
func_name = func.__name__.replace("_", " ").title()
|
||||
|
||||
def _command(*args, **kargs):
|
||||
|
||||
bound = inspect.signature(func).bind(*args, **kargs)
|
||||
bound.apply_defaults()
|
||||
|
||||
args_str = ', '.join([f'{k}={v}' for k, v in bound.arguments.items()])
|
||||
print(f'\n[>-] {func_name} ({args_str}) --- Start ---')
|
||||
args_str = ", ".join([f"{k}={v}" for k, v in bound.arguments.items()])
|
||||
print(f"\n[>-] {func_name} ({args_str}) --- Start ---")
|
||||
|
||||
t0 = time.time()
|
||||
result = func(*args, **kargs)
|
||||
|
||||
print(f'[>-] {func_name} --- Finished (total time : {time.time() - t0:.2f}s) ---')
|
||||
print(
|
||||
f"[>-] {func_name} --- Finished (total time : {time.time() - t0:.2f}s) ---"
|
||||
)
|
||||
return result
|
||||
|
||||
return _command
|
||||
|
||||
return _command
|
||||
|
||||
|
||||
def asset_warning_callback(self, context):
|
||||
"""Callback function to display a warning message when ading or modifying an asset"""
|
||||
self.warning = ''
|
||||
self.warning = ""
|
||||
|
||||
if not self.name:
|
||||
self.warning = 'You need to specify a name'
|
||||
self.warning = "You need to specify a name"
|
||||
return
|
||||
if not self.catalog:
|
||||
self.warning = 'You need to specify a catalog'
|
||||
self.warning = "You need to specify a catalog"
|
||||
return
|
||||
|
||||
lib = get_active_library()
|
||||
@@ -60,30 +64,33 @@ def asset_warning_callback(self, context):
|
||||
lib = prefs.libraries[lib.store_library]
|
||||
|
||||
if not lib.library_type.get_asset_path(self.name, self.catalog).parents[1].exists():
|
||||
self.warning = 'A new folder will be created'
|
||||
self.warning = "A new folder will be created"
|
||||
|
||||
|
||||
def get_active_library():
|
||||
'''Get the pref library properties from the active library of the asset browser'''
|
||||
"""Get the pref library properties from the active library of the asset browser"""
|
||||
prefs = get_addon_prefs()
|
||||
asset_lib_ref = bpy.context.space_data.params.asset_library_ref
|
||||
|
||||
#Check for merged library
|
||||
# Check for merged library
|
||||
for l in prefs.libraries:
|
||||
if l.library_name == asset_lib_ref:
|
||||
return l
|
||||
|
||||
|
||||
def get_active_catalog():
|
||||
'''Get the active catalog path'''
|
||||
"""Get the active catalog path"""
|
||||
|
||||
lib = get_active_library()
|
||||
cat_data = lib.library_type.read_catalog()
|
||||
cat_data = {v['id']:k for k,v in cat_data.items()}
|
||||
cat_data = {v["id"]: k for k, v in cat_data.items()}
|
||||
|
||||
cat_id = bpy.context.space_data.params.catalog_id
|
||||
if cat_id in cat_data:
|
||||
return cat_data[cat_id]
|
||||
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
"""
|
||||
def norm_asset_datas(asset_file_datas):
|
||||
@@ -181,7 +188,7 @@ def get_asset_source(replace_local=False):
|
||||
|
||||
return source_path
|
||||
"""
|
||||
'''
|
||||
"""
|
||||
def get_catalog_path(filepath=None):
|
||||
filepath = filepath or bpy.data.filepath
|
||||
filepath = Path(filepath)
|
||||
@@ -196,7 +203,7 @@ def get_catalog_path(filepath=None):
|
||||
catalog.touch(exist_ok=False)
|
||||
|
||||
return catalog
|
||||
'''
|
||||
"""
|
||||
|
||||
# def read_catalog(path, key='path'):
|
||||
# cat_data = {}
|
||||
@@ -218,7 +225,7 @@ def get_catalog_path(filepath=None):
|
||||
# cat_data[cat_path] = {'id':cat_id, 'name':cat_name}
|
||||
# elif key =='name':
|
||||
# cat_data[cat_name] = {'id':cat_id, 'path':cat_path}
|
||||
|
||||
|
||||
# return cat_data
|
||||
"""
|
||||
def read_catalog(path):
|
||||
@@ -302,27 +309,29 @@ def create_catalog_file(json_path : str|Path, keep_existing_category : bool = Tr
|
||||
return
|
||||
"""
|
||||
|
||||
|
||||
def clear_env_libraries():
|
||||
print('clear_env_libraries')
|
||||
print("clear_env_libraries")
|
||||
|
||||
prefs = get_addon_prefs()
|
||||
asset_libraries = bpy.context.preferences.filepaths.asset_libraries
|
||||
|
||||
for env_lib in prefs.env_libraries:
|
||||
name = env_lib.get('asset_library')
|
||||
name = env_lib.get("asset_library")
|
||||
if not name:
|
||||
continue
|
||||
|
||||
|
||||
asset_lib = asset_libraries.get(name)
|
||||
if not asset_lib:
|
||||
continue
|
||||
|
||||
index = list(asset_libraries).index(asset_lib)
|
||||
bpy.ops.preferences.asset_library_remove(index=index)
|
||||
|
||||
|
||||
prefs.env_libraries.clear()
|
||||
|
||||
'''
|
||||
|
||||
"""
|
||||
env_libs = get_env_libraries()
|
||||
paths = [Path(l['path']).resolve().as_posix() for n, l in env_libs.items()]
|
||||
|
||||
@@ -331,16 +340,17 @@ def clear_env_libraries():
|
||||
|
||||
if (l.name in env_libs or lib_path in paths):
|
||||
libs.remove(i)
|
||||
'''
|
||||
|
||||
def set_env_libraries(path=None) -> list:
|
||||
'''Read the environments variables and create the libraries'''
|
||||
"""
|
||||
|
||||
#from asset_library.prefs import AssetLibraryOptions
|
||||
|
||||
def set_env_libraries(path=None) -> list:
|
||||
"""Read the environments variables and create the libraries"""
|
||||
|
||||
# from asset_library.prefs import AssetLibraryOptions
|
||||
prefs = get_addon_prefs()
|
||||
path = path or prefs.config_directory
|
||||
|
||||
#print('Read', path)
|
||||
# print('Read', path)
|
||||
library_data = read_file(path)
|
||||
|
||||
clear_env_libraries()
|
||||
@@ -359,7 +369,8 @@ def set_env_libraries(path=None) -> list:
|
||||
|
||||
return libs
|
||||
|
||||
'''
|
||||
|
||||
"""
|
||||
def get_env_libraries():
|
||||
env_libraries = {}
|
||||
|
||||
@@ -391,21 +402,17 @@ def get_env_libraries():
|
||||
}
|
||||
|
||||
return env_libraries
|
||||
'''
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def resync_lib(name, waiting_time):
|
||||
bpy.app.timers.register(
|
||||
lambda: bpy.ops.assetlib.synchronize(only_recent=True, name=name),
|
||||
first_interval=waiting_time
|
||||
first_interval=waiting_time,
|
||||
)
|
||||
|
||||
|
||||
|
||||
'''
|
||||
"""
|
||||
def set_assetlib_paths():
|
||||
prefs = bpy.context.preferences
|
||||
|
||||
@@ -452,16 +459,4 @@ def set_actionlib_paths():
|
||||
|
||||
prefs.filepaths.asset_libraries[lib_id].name = actionlib_name
|
||||
#prefs.filepaths.asset_libraries[lib_id].path = str(actionlib_dir)
|
||||
'''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
+76
-63
@@ -10,7 +10,7 @@ class AssetCache:
|
||||
def __init__(self, file_cache, data=None):
|
||||
|
||||
self.file_cache = file_cache
|
||||
|
||||
|
||||
self.catalog = None
|
||||
self.author = None
|
||||
self.description = None
|
||||
@@ -32,10 +32,7 @@ class AssetCache:
|
||||
|
||||
@property
|
||||
def metadata(self):
|
||||
metadata = {
|
||||
'.library_id': self.library_id,
|
||||
'.filepath': self.filepath
|
||||
}
|
||||
metadata = {".library_id": self.library_id, ".filepath": self.filepath}
|
||||
|
||||
metadata.update(self._metadata)
|
||||
|
||||
@@ -43,23 +40,23 @@ class AssetCache:
|
||||
|
||||
@property
|
||||
def norm_name(self):
|
||||
return self.name.replace(' ', '_').lower()
|
||||
return self.name.replace(" ", "_").lower()
|
||||
|
||||
def unique_name(self):
|
||||
return (self.filepath / self.name).as_posix()
|
||||
|
||||
def set_data(self, data):
|
||||
catalog = data['catalog']
|
||||
catalog = data["catalog"]
|
||||
if isinstance(catalog, (list, tuple)):
|
||||
catalog = '/'.join(catalog)
|
||||
catalog = "/".join(catalog)
|
||||
|
||||
self.catalog = catalog
|
||||
self.author = data.get('author', '')
|
||||
self.description = data.get('description', '')
|
||||
self.tags = data.get('tags', [])
|
||||
self.type = data.get('type')
|
||||
self.name = data['name']
|
||||
self._metadata = data.get('metadata', {})
|
||||
self.author = data.get("author", "")
|
||||
self.description = data.get("description", "")
|
||||
self.tags = data.get("tags", [])
|
||||
self.type = data.get("type")
|
||||
self.name = data["name"]
|
||||
self._metadata = data.get("metadata", {})
|
||||
|
||||
def to_dict(self):
|
||||
return dict(
|
||||
@@ -69,11 +66,11 @@ class AssetCache:
|
||||
description=self.description,
|
||||
tags=self.tags,
|
||||
type=self.type,
|
||||
name=self.name
|
||||
name=self.name,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f'AssetCache(name={self.name}, catalog={self.catalog})'
|
||||
return f"AssetCache(name={self.name}, catalog={self.catalog})"
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.to_dict() == other.to_dict()
|
||||
@@ -81,7 +78,7 @@ class AssetCache:
|
||||
|
||||
class AssetsCache:
|
||||
def __init__(self, file_cache):
|
||||
|
||||
|
||||
self.file_cache = file_cache
|
||||
self._data = []
|
||||
|
||||
@@ -111,12 +108,12 @@ class AssetsCache:
|
||||
return next((a for a in self if a.name == name), None)
|
||||
|
||||
def __repr__(self):
|
||||
return f'AssetsCache({list(self)})'
|
||||
return f"AssetsCache({list(self)})"
|
||||
|
||||
|
||||
class FileCache:
|
||||
def __init__(self, library_cache, data=None):
|
||||
|
||||
|
||||
self.library_cache = library_cache
|
||||
|
||||
self.filepath = None
|
||||
@@ -132,15 +129,15 @@ class FileCache:
|
||||
|
||||
def set_data(self, data):
|
||||
|
||||
if 'filepath' in data:
|
||||
self.filepath = Path(data['filepath'])
|
||||
if "filepath" in data:
|
||||
self.filepath = Path(data["filepath"])
|
||||
|
||||
self.modified = data.get('modified', time.time_ns())
|
||||
self.modified = data.get("modified", time.time_ns())
|
||||
|
||||
if data.get('type') == 'FILE':
|
||||
if data.get("type") == "FILE":
|
||||
self.assets.add(data)
|
||||
|
||||
for asset_cache_data in data.get('assets', []):
|
||||
for asset_cache_data in data.get("assets", []):
|
||||
self.assets.add(asset_cache_data)
|
||||
|
||||
def to_dict(self):
|
||||
@@ -148,7 +145,7 @@ class FileCache:
|
||||
filepath=self.filepath.as_posix(),
|
||||
modified=self.modified,
|
||||
library_id=self.library_id,
|
||||
assets=[asset_cache.to_dict() for asset_cache in self]
|
||||
assets=[asset_cache.to_dict() for asset_cache in self],
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
@@ -158,14 +155,14 @@ class FileCache:
|
||||
return self._data[key]
|
||||
|
||||
def __repr__(self):
|
||||
return f'FileCache(filepath={self.filepath})'
|
||||
return f"FileCache(filepath={self.filepath})"
|
||||
|
||||
|
||||
class AssetCacheDiff:
|
||||
def __init__(self, library_cache, asset_cache, operation):
|
||||
|
||||
self.library_cache = library_cache
|
||||
#self.filepath = data['filepath']
|
||||
# self.filepath = data['filepath']
|
||||
self.operation = operation
|
||||
self.asset_cache = asset_cache
|
||||
|
||||
@@ -189,32 +186,49 @@ class LibraryCacheDiff:
|
||||
self._data += new_asset_diffs
|
||||
|
||||
return new_asset_diffs
|
||||
|
||||
|
||||
def compare(self, old_cache, new_cache):
|
||||
if old_cache is None or new_cache is None:
|
||||
print('Cannot Compare cache with None')
|
||||
print("Cannot Compare cache with None")
|
||||
|
||||
cache_dict = {a.unique_name : a for a in old_cache.asset_caches}
|
||||
new_cache_dict = {a.unique_name : a for a in new_cache.asset_caches}
|
||||
cache_dict = {a.unique_name: a for a in old_cache.asset_caches}
|
||||
new_cache_dict = {a.unique_name: a for a in new_cache.asset_caches}
|
||||
|
||||
assets_added = self.add([v for k, v in new_cache_dict.items() if k not in cache_dict], 'ADD')
|
||||
assets_removed = self.add([v for k, v in cache_dict.items() if k not in new_cache_dict], 'REMOVED')
|
||||
assets_modified = self.add([v for k, v in cache_dict.items() if v not in assets_removed and v!= new_cache_dict[k]], 'MODIFIED')
|
||||
assets_added = self.add(
|
||||
[v for k, v in new_cache_dict.items() if k not in cache_dict], "ADD"
|
||||
)
|
||||
assets_removed = self.add(
|
||||
[v for k, v in cache_dict.items() if k not in new_cache_dict], "REMOVED"
|
||||
)
|
||||
assets_modified = self.add(
|
||||
[
|
||||
v
|
||||
for k, v in cache_dict.items()
|
||||
if v not in assets_removed and v != new_cache_dict[k]
|
||||
],
|
||||
"MODIFIED",
|
||||
)
|
||||
|
||||
if assets_added:
|
||||
print(f'{len(assets_added)} Assets Added \n{tuple(a.name for a in assets_added[:10])}...\n')
|
||||
print(
|
||||
f"{len(assets_added)} Assets Added \n{tuple(a.name for a in assets_added[:10])}...\n"
|
||||
)
|
||||
if assets_removed:
|
||||
print(f'{len(assets_removed)} Assets Removed \n{tuple(a.name for a in assets_removed[:10])}...\n')
|
||||
print(
|
||||
f"{len(assets_removed)} Assets Removed \n{tuple(a.name for a in assets_removed[:10])}...\n"
|
||||
)
|
||||
if assets_modified:
|
||||
print(f'{len(assets_modified)} Assets Modified \n{tuple(a.name for a in assets_modified[:10])}...\n')
|
||||
|
||||
print(
|
||||
f"{len(assets_modified)} Assets Modified \n{tuple(a.name for a in assets_modified[:10])}...\n"
|
||||
)
|
||||
|
||||
if len(self) == 0:
|
||||
print('No change in the library')
|
||||
print("No change in the library")
|
||||
|
||||
return self
|
||||
|
||||
def group_by(self, key):
|
||||
'''Return groups of file cache diff using the key provided'''
|
||||
"""Return groups of file cache diff using the key provided"""
|
||||
data = list(self).sort(key=key)
|
||||
return groupby(data, key=key)
|
||||
|
||||
@@ -228,17 +242,17 @@ class LibraryCacheDiff:
|
||||
return len(self._data)
|
||||
|
||||
def __repr__(self):
|
||||
return f'LibraryCacheDiff(operations={[o for o in self][:2]}...)'
|
||||
return f"LibraryCacheDiff(operations={[o for o in self][:2]}...)"
|
||||
|
||||
|
||||
class LibraryCache:
|
||||
def __init__(self, filepath):
|
||||
|
||||
|
||||
self.filepath = Path(filepath)
|
||||
self._data = []
|
||||
|
||||
@classmethod
|
||||
def from_library(cls, library):
|
||||
def from_library(cls, library):
|
||||
filepath = library.library_path / f"blender_assets.{library.id}.json"
|
||||
return cls(filepath)
|
||||
|
||||
@@ -248,28 +262,28 @@ class LibraryCache:
|
||||
|
||||
@property
|
||||
def library_id(self):
|
||||
return self.filepath.stem.split('.')[-1]
|
||||
return self.filepath.stem.split(".")[-1]
|
||||
|
||||
#@property
|
||||
#def filepath(self):
|
||||
# @property
|
||||
# def filepath(self):
|
||||
# """Get the filepath of the library json file relative to the library"""
|
||||
# return self.directory / self.filename
|
||||
|
||||
|
||||
def catalogs(self):
|
||||
return set(a.catalog for a in self.asset_caches)
|
||||
|
||||
@property
|
||||
def asset_caches(self):
|
||||
'''Return an iterator to get all asset caches'''
|
||||
"""Return an iterator to get all asset caches"""
|
||||
return (asset_cache for file_cache in self for asset_cache in file_cache)
|
||||
|
||||
|
||||
@property
|
||||
def tmp_filepath(self):
|
||||
return Path(bpy.app.tempdir) / self.filename
|
||||
|
||||
def read(self):
|
||||
print(f'Read cache from {self.filepath}')
|
||||
|
||||
print(f"Read cache from {self.filepath}")
|
||||
|
||||
for file_cache_data in read_file(self.filepath):
|
||||
self.add(file_cache_data)
|
||||
|
||||
@@ -280,7 +294,7 @@ class LibraryCache:
|
||||
if tmp:
|
||||
filepath = self.tmp_filepath
|
||||
|
||||
print(f'Write cache file to {filepath}')
|
||||
print(f"Write cache file to {filepath}")
|
||||
write_file(filepath, self._data)
|
||||
return filepath
|
||||
|
||||
@@ -293,7 +307,7 @@ class LibraryCache:
|
||||
|
||||
def add_asset_cache(self, asset_cache_data, filepath=None):
|
||||
if filepath is None:
|
||||
filepath = asset_cache_data['filepath']
|
||||
filepath = asset_cache_data["filepath"]
|
||||
|
||||
file_cache = self.get(filepath)
|
||||
if not file_cache:
|
||||
@@ -334,28 +348,28 @@ class LibraryCache:
|
||||
if new_cache is None:
|
||||
new_cache = self
|
||||
|
||||
return LibraryCacheDiff(old_cache, new_cache)
|
||||
return LibraryCacheDiff(old_cache, new_cache)
|
||||
|
||||
def update(self, cache_diff):
|
||||
#Update the cache with the operations
|
||||
# Update the cache with the operations
|
||||
for asset_cache_diff in cache_diff:
|
||||
file_cache = self.get(asset_cache_diff.filepath)
|
||||
if not asset_cache:
|
||||
print(f'Filepath {asset_cache_diff.filepath} not in {self}' )
|
||||
print(f"Filepath {asset_cache_diff.filepath} not in {self}")
|
||||
continue
|
||||
|
||||
asset_cache = file_cache.get(asset_cache_diff.name)
|
||||
|
||||
if not asset_cache:
|
||||
print(f'Asset {asset_cache_diff.name} not in file_cache {file_cache}' )
|
||||
print(f"Asset {asset_cache_diff.name} not in file_cache {file_cache}")
|
||||
continue
|
||||
|
||||
if asset_cache_diff.operation == 'REMOVE':
|
||||
if asset_cache_diff.operation == "REMOVE":
|
||||
file_cache.assets.remove(asset_cache_diff.name)
|
||||
|
||||
elif asset_cache_diff.operation in ('MODIFY', 'ADD'):
|
||||
elif asset_cache_diff.operation in ("MODIFY", "ADD"):
|
||||
asset_cache.set_data(asset_cache_diff.asset_cache.to_dict())
|
||||
|
||||
|
||||
return self
|
||||
|
||||
def __len__(self):
|
||||
@@ -363,7 +377,7 @@ class LibraryCache:
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._data)
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, str):
|
||||
return self.to_dict()[key]
|
||||
@@ -377,5 +391,4 @@ class LibraryCache:
|
||||
return next((a for a in self if a.filepath == filepath), None)
|
||||
|
||||
def __repr__(self):
|
||||
return f'LibraryCache(library_id={self.library_id})'
|
||||
|
||||
return f"LibraryCache(library_id={self.library_id})"
|
||||
|
||||
+19
-15
@@ -1,4 +1,3 @@
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import importlib.util
|
||||
@@ -11,7 +10,7 @@ from pathlib import Path
|
||||
|
||||
# import module utils without excuting __init__
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"utils", Path(__file__).parent/"file_utils.py"
|
||||
"utils", Path(__file__).parent / "file_utils.py"
|
||||
)
|
||||
utils = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(utils)
|
||||
@@ -19,24 +18,29 @@ spec.loader.exec_module(utils)
|
||||
|
||||
def synchronize(src, dst, only_new=False, only_recent=False):
|
||||
|
||||
excludes=['*.sync-conflict-*', '.*']
|
||||
includes=['*.blend', 'blender_assets.cats.txt']
|
||||
|
||||
excludes = ["*.sync-conflict-*", ".*"]
|
||||
includes = ["*.blend", "blender_assets.cats.txt"]
|
||||
|
||||
utils.copy_dir(
|
||||
src, dst,
|
||||
only_new=only_new, only_recent=only_recent,
|
||||
excludes=excludes, includes=includes
|
||||
src,
|
||||
dst,
|
||||
only_new=only_new,
|
||||
only_recent=only_recent,
|
||||
excludes=excludes,
|
||||
includes=includes,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__' :
|
||||
parser = argparse.ArgumentParser(description='Add Comment To the tracker',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Add Comment To the tracker",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument('--src')
|
||||
parser.add_argument('--dst')
|
||||
parser.add_argument('--only-new', type=json.loads, default='false')
|
||||
parser.add_argument('--only-recent', type=json.loads, default='false')
|
||||
parser.add_argument("--src")
|
||||
parser.add_argument("--dst")
|
||||
parser.add_argument("--only-new", type=json.loads, default="false")
|
||||
parser.add_argument("--only-recent", type=json.loads, default="false")
|
||||
|
||||
args = parser.parse_args()
|
||||
synchronize(**vars(args))
|
||||
|
||||
+30
-29
@@ -9,51 +9,52 @@ import string
|
||||
class TemplateFormatter(string.Formatter):
|
||||
def format_field(self, value, format_spec):
|
||||
if isinstance(value, str):
|
||||
spec, sep = [*format_spec.split(':'), None][:2]
|
||||
|
||||
spec, sep = [*format_spec.split(":"), None][:2]
|
||||
|
||||
if sep:
|
||||
value = value.replace('_', ' ')
|
||||
value = value = re.sub(r'([a-z])([A-Z])', rf'\1{sep}\2', value)
|
||||
value = value.replace(' ', sep)
|
||||
|
||||
if spec == 'u':
|
||||
value = value.replace("_", " ")
|
||||
value = value = re.sub(r"([a-z])([A-Z])", rf"\1{sep}\2", value)
|
||||
value = value.replace(" ", sep)
|
||||
|
||||
if spec == "u":
|
||||
value = value.upper()
|
||||
elif spec == 'l':
|
||||
elif spec == "l":
|
||||
value = value.lower()
|
||||
elif spec == 't':
|
||||
elif spec == "t":
|
||||
value = value.title()
|
||||
|
||||
return super().format(value, format_spec)
|
||||
|
||||
|
||||
class Template:
|
||||
field_pattern = re.compile(r'{(\w+)\*{0,2}}')
|
||||
field_pattern_recursive = re.compile(r'{(\w+)\*{2}}')
|
||||
field_pattern = re.compile(r"{(\w+)\*{0,2}}")
|
||||
field_pattern_recursive = re.compile(r"{(\w+)\*{2}}")
|
||||
|
||||
def __init__(self, template):
|
||||
#asset_data_path = Path(lib_path) / ASSETLIB_FILENAME
|
||||
# asset_data_path = Path(lib_path) / ASSETLIB_FILENAME
|
||||
|
||||
self.raw = template
|
||||
self.formatter = TemplateFormatter()
|
||||
|
||||
@property
|
||||
def glob_pattern(self):
|
||||
pattern = self.field_pattern_recursive.sub('**', self.raw)
|
||||
pattern = self.field_pattern.sub('*', pattern)
|
||||
pattern = self.field_pattern_recursive.sub("**", self.raw)
|
||||
pattern = self.field_pattern.sub("*", pattern)
|
||||
return pattern
|
||||
|
||||
@property
|
||||
def re_pattern(self):
|
||||
pattern = self.field_pattern_recursive.sub('([\\\w -_.\/]+)', self.raw)
|
||||
pattern = self.field_pattern.sub('([\\\w -_.]+)', pattern)
|
||||
pattern = pattern.replace('?', '.')
|
||||
pattern = pattern.replace('*', '.*')
|
||||
pattern = self.field_pattern_recursive.sub("([\\\w -_.\/]+)", self.raw)
|
||||
pattern = self.field_pattern.sub("([\\\w -_.]+)", pattern)
|
||||
pattern = pattern.replace("?", ".")
|
||||
pattern = pattern.replace("*", ".*")
|
||||
|
||||
return re.compile(pattern)
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
return self.field_pattern.findall(self.raw)
|
||||
#return [f or '0' for f in fields]
|
||||
# return [f or '0' for f in fields]
|
||||
|
||||
def parse(self, path):
|
||||
|
||||
@@ -61,7 +62,7 @@ class Template:
|
||||
|
||||
res = self.re_pattern.findall(path)
|
||||
if not res:
|
||||
print('Could not parse {path} with {self.re_pattern}')
|
||||
print("Could not parse {path} with {self.re_pattern}")
|
||||
return {}
|
||||
|
||||
fields = self.fields
|
||||
@@ -71,7 +72,7 @@ class Template:
|
||||
else:
|
||||
field_values = res[0]
|
||||
|
||||
return {k:v for k,v in zip(fields, field_values)}
|
||||
return {k: v for k, v in zip(fields, field_values)}
|
||||
|
||||
def norm_data(self, data):
|
||||
norm_data = {}
|
||||
@@ -81,7 +82,7 @@ class Template:
|
||||
v = v.as_posix()
|
||||
|
||||
norm_data[k] = v
|
||||
|
||||
|
||||
return norm_data
|
||||
|
||||
def format(self, data=None, **kargs):
|
||||
@@ -89,17 +90,17 @@ class Template:
|
||||
data = {**(data or {}), **kargs}
|
||||
|
||||
try:
|
||||
#print('FORMAT', self.raw, data)
|
||||
# print('FORMAT', self.raw, data)
|
||||
path = self.formatter.format(self.raw, **self.norm_data(data))
|
||||
except KeyError as e:
|
||||
print(f'Cannot format {self.raw} with {data}, field {e} is missing')
|
||||
print(f"Cannot format {self.raw} with {data}, field {e} is missing")
|
||||
return
|
||||
|
||||
path = os.path.expandvars(path)
|
||||
return Path(path)
|
||||
|
||||
def glob(self, directory, pattern=None):
|
||||
'''If pattern is given it need to be absolute'''
|
||||
"""If pattern is given it need to be absolute"""
|
||||
if pattern is None:
|
||||
pattern = Path(directory, self.glob_pattern).as_posix()
|
||||
|
||||
@@ -114,14 +115,14 @@ class Template:
|
||||
pattern = self.format(data, **kargs)
|
||||
|
||||
pattern_str = str(pattern)
|
||||
if '*' not in pattern_str and '?' not in pattern_str:
|
||||
if "*" not in pattern_str and "?" not in pattern_str:
|
||||
return pattern
|
||||
|
||||
paths = glob(pattern.as_posix())
|
||||
if paths:
|
||||
return Path(paths[0])
|
||||
|
||||
#return pattern
|
||||
|
||||
# return pattern
|
||||
|
||||
def __repr__(self):
|
||||
return f'Template({self.raw})'
|
||||
return f"Template({self.raw})"
|
||||
|
||||
Reference in New Issue
Block a user