asset_library/adapters/scan_folder.py

166 lines
6.3 KiB
Python

"""
Plugin for making an asset library of all blender file found in a folder
"""
from asset_library.adapters.adapter import AssetLibraryAdapter
from asset_library.common.bl_utils import load_datablocks
from asset_library.common.template import Template
import bpy
from bpy.props import (StringProperty, IntProperty, BoolProperty)
import re
from pathlib import Path
from itertools import groupby
import uuid
import os
import shutil
import json
import time
class ScanFolderLibrary(AssetLibraryAdapter):
name = "Scan Folder"
source_directory : StringProperty(subtype='DIR_PATH')
template_file : StringProperty()
template_image : StringProperty()
template_video : StringProperty()
template_description : StringProperty()
def get_asset_path(self, name, catalog, directory=None):
directory = directory or self.source_directory
catalog = self.norm_file_name(catalog)
name = self.norm_file_name(name)
return Path(directory, self.get_asset_relative_path(name, catalog))
def get_image_path(self, name, catalog, filepath):
catalog = self.norm_file_name(catalog)
name = self.norm_file_name(name)
return self.format_path(self.template_image, dict(name=name, catalog=catalog, filepath=filepath))
def get_video_path(self, name, catalog, filepath):
catalog = self.norm_file_name(catalog)
name = self.norm_file_name(name)
return self.format_path(self.template_video, dict(name=name, catalog=catalog, filepath=filepath))
def format_asset_description(self, asset_description, asset_path):
asset_path = self.prop_rel_path(asset_path, 'source_directory')
modified = asset_description.get('modified', time.time_ns())
if self.data_type == 'FILE':
return dict(
filepath=asset_path,
author=asset_description.get('author'),
modified=modified,
catalog=asset_description['catalog'],
tags=[],
description=asset_description.get('description', ''),
type=self.data_type,
image=self.template_image,
name=asset_description['name']
)
return dict(
filepath=asset_path,
modified=modified,
library_id=self.library.id,
assets=[dict(
catalog=asset_data.get('catalog', asset_description['catalog']),
author=asset_data.get('author'),
metadata=asset_data.get('metadata', {}),
description=asset_data.get('description', ''),
tags=asset_data.get('tags', []),
type=self.data_type,
image=self.template_image,
video=self.template_video,
name=asset_data['name']) for asset_data in asset_description['assets']
]
)
def fetch(self):
"""Gather in a list all assets found in the folder"""
print(f'Fetch Assets for {self.library.name}')
source_directory = Path(self.source_directory)
template_file = Template(self.template_file)
catalog_data = self.read_catalog(directory=source_directory)
catalog_ids = {v['id']: k for k, v in catalog_data.items()}
cache = self.read_cache() or []
print(f'Search for blend using glob template: {template_file.glob_pattern}')
print(f'Scanning Folder {source_directory}...')
new_cache = []
for asset_path in template_file.glob(source_directory):#sorted(blend_files):
source_rel_path = self.prop_rel_path(asset_path, 'source_directory')
modified = asset_path.stat().st_mtime_ns
# Check if the asset description as already been cached
asset_description = next((a for a in cache if a['filepath'] == source_rel_path), None)
if asset_description and asset_description['modified'] >= modified:
print(asset_path, 'is skipped because not modified')
new_cache.append(asset_description)
continue
rel_path = asset_path.relative_to(source_directory).as_posix()
field_data = template_file.parse(rel_path)
catalogs = [v for k,v in sorted(field_data.items()) if k.isdigit()]
#catalogs = [c.replace('_', ' ').title() for c in catalogs]
asset_name = field_data.get('asset_name', asset_path.stem)
asset_description = {
"name": asset_name,
"catalog": '/'.join(catalogs),
"assets": [],
'modified': modified
}
if self.data_type == 'FILE':
asset_description = self.format_asset_description(asset_description, asset_path)
new_cache.append(asset_description)
continue
# Now check if there is a asset description file
asset_description_path = self.find_path(self.template_description, asset_description, filepath=asset_path)
if asset_description_path:
new_cache.append(self.read_file(asset_description_path))
continue
# Scan the blend file for assets inside and write a custom asset description for info found
print(f'Scanning blendfile {asset_path}...')
assets = self.load_datablocks(asset_path, type=self.data_types, link=True, assets_only=True)
print(f'Found {len(assets)} {self.data_types} inside')
for asset in assets:
#catalog_path = catalog_ids.get(asset.asset_data.catalog_id)
#if not catalog_path:
# print(f'No catalog found for asset {asset.name}')
#catalog_path = asset_description['catalog']#asset_path.relative_to(self.source_directory).as_posix()
# For now the catalog used is the one extract from the template file
asset_description['assets'].append(self.get_asset_data(asset))
getattr(bpy.data, self.data_types).remove(asset)
asset_description = self.format_asset_description(asset_description, asset_path)
new_cache.append(asset_description)
new_cache.sort(key=lambda x:x['filepath'])
return new_cache