Update: I've found found a workable solution, but still have some questions (listed at bottom)
@ferdinand - I tried to edit the original post to avoid a "diary" post, but ran into the following error:
You are only allowed to edit posts for 3600 second(s) after posting
"""Name-en-US: Import Directory as Object Assets Database
Description-en-US: Creates a new C4D Asset Database named `_my-database.db` on your desktop, and loads the first object of each .c4d project in `_assets`
References:
https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/blob/master/scripts/05_modules/assets/asset_databases_r26.py
https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/blob/master/scripts/05_modules/assets/asset_types_r26.py
"""
## Imports
import c4d
import os
import maxon
## User Inputs
# TODO: Add input paths, otherwise default paths will be used
INPUT_PROJECTS_DIR = ""
OUTPUT_ASSETS_DB = ""
## Helpers
def CreateRepoFromPath(path) -> maxon.UpdatableAssetRepositoryRef:
# Wait for all existing dbs to load
# Not sure if this is needed or superstitious
if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading():
return RuntimeError("Could not load asset databases.")
url = maxon.Url(path)
if url.IoDetect() == maxon.IODETECT.ERRORSTATE:
raise RuntimeError(f"Directory {url} is invalid")
repo_id = maxon.AssetInterface.MakeUuid(str(url), True)
bases = maxon.BaseArray(maxon.AssetRepositoryRef)
try:
repository = maxon.AssetInterface.CreateRepositoryFromUrl(
repo_id,
maxon.AssetRepositoryTypes.AssetDatabase(),
bases,
url,
True,
False,
False,
)
except Exception as e:
# If at first you don't succeed, try... try... again.
repository = maxon.AssetInterface.CreateRepositoryFromUrl(
repo_id,
maxon.AssetRepositoryTypes.AssetDatabase(),
bases,
url,
True,
False,
False,
)
if not repository:
raise RuntimeError("Could not create Repository.")
return repository
def MountAssetDatabase(path):
# Wait for all existing dbs to load
if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading():
return RuntimeError("Could not load asset databases.")
# Build a maxon url from the path
url = maxon.Url(path)
databaseCollection = maxon.AssetDataBasesInterface.GetDatabases()
# Check if DB is already mounted
for database in databaseCollection:
print(database)
if database._dbUrl == url:
database._active = True
maxon.AssetDataBasesInterface.SetDatabases(databaseCollection)
return database
database = maxon.AssetDatabaseStruct(url)
databaseCollection.append(database)
maxon.AssetDataBasesInterface.SetDatabases(databaseCollection)
return database
def AddObjectToRepository(
repo: maxon.UpdatableAssetRepositoryRef,
doc: c4d.documents.BaseDocument,
obj: c4d.BaseObject,
):
if repo is None:
raise ValueError("Invalid repo")
if obj is None:
raise ValueError("Input obj does not exist")
asset_id = maxon.AssetInterface.MakeUuid(prefix="object", compact=False)
asset_name = obj.GetName()
asset_version = (
"0.1.0" # Using Semantic Versioning, as we rarely get it right the first time.
)
asset_metadata = maxon.AssetMetaData()
asset_category_id = maxon.Id("net.maxon.assetcategory.uncategorized")
store_asset_struct = maxon.StoreAssetStruct(asset_category_id, repo, repo)
asset = maxon.AssetCreationInterface.CreateObjectAsset(
obj,
doc,
store_asset_struct,
asset_id,
asset_name,
asset_version,
asset_metadata,
True,
)
return asset
def OpenAssetBrowser():
# The command id for the Asset Browser.
CID_ASSET_BROWSER = 1054225
# Open the Asset Browser when it is not already open.
if not c4d.IsCommandChecked(CID_ASSET_BROWSER):
c4d.CallCommand(CID_ASSET_BROWSER)
## Main
def main():
# Get the user's desktop path
desktop_path = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_DESKTOP)
# Get the Input File Path
assets_dir = INPUT_PROJECTS_DIR
if not assets_dir:
assets_dir = os.path.join(desktop_path, "_assets")
# Define the output database path
database_name = "_my-database.db"
database_path = OUTPUT_ASSETS_DB
if not database_path:
database_path = os.path.abspath(os.path.join(desktop_path, database_name))
# Create the database if it doesn't exist
if not os.path.exists(database_path):
os.makedirs(database_path, exist_ok=True)
repository = CreateRepoFromPath(database_path)
doc = c4d.documents.GetActiveDocument()
obj = doc.GetActiveObject()
# Iterate through all *.c4d files in the assets directory
assets = []
for file_name in os.listdir(assets_dir):
if file_name.endswith(".c4d"):
file_path = os.path.join(assets_dir, file_name)
# Load the C4D file silently
loaded_doc = c4d.documents.LoadDocument(
file_path, c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS, None
)
if loaded_doc is None:
print(f"Failed to load {file_path}")
continue
# Get the first object in the loaded document
obj = loaded_doc.GetFirstObject()
if obj is None:
print(f"No objects found in {file_path}")
continue
# Add the object to the repository
asset = AddObjectToRepository(repository, loaded_doc, obj)
if asset is None:
raise RuntimeError(f"Unable to ingest {file_name}")
assets.append(asset)
# Unload/close the loaded document
c4d.documents.KillDocument(loaded_doc)
database = MountAssetDatabase(database_path)
maxon.AssetManagerInterface.RevealAsset(assets)
c4d.EventAdd()
c4d.storage.ShowInFinder(database_path, open=True)
# Execute the script
if __name__ == "__main__":
main()
Questions
- Should I be able to mount a directory and have it auto-create a DB?
- Any idea why I need to re-try creating the Repo for it to work?
- If I run this script multiple times, I end up with multiple DBs in the same directory - any way to get
CreateRepoFromUrl()
to detect that there's already a repo so it doesn't need to create one, and can instead just load it? - Show Assets doesn't seem to be doing what I want. I typically have to manually close/reopen the Assets Browser a couple of times to see an update. Is there an event I need to add to immediately show the assets?
Thanks!