Adding an Object to a New Cinema 4D Asset Database
-
Hi,
I'm attempting to write a script that will allow me to bulk-import the contents of a directcory full of
.c4d
files as Object Assets in a new Cinema 4D Asset Databse.I've found what feels like a perfect starting point by @ferdinand:
https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/blob/master/scripts/05_modules/assets/asset_databases_r26.pyIn the documentation for
MountDatabases()
, he mentions:When the selected path does not contain an asset database, the necessary metadata will be created in that location by Cinema 4D.
However, when I attempt to do something similar in a script, the folder mounts, but no metadata files are created and C4D doesn't react when I attempt to disable/delete the "database stub".
Here's my current source code - if it looks practically identical to Ferdinand's that's because I re-wrote a lot of his methods by hand (hoping to better understand the API).
"""Name-en-US: Create Test Database Description-en-US: Creates a new C4D Asset Database named `_my-database.db` on your desktop.""" import c4d import os import maxon 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) print(f"Created new DB for '{url}'") return database def main(): # Get the user's desktop path desktop_path = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_DESKTOP) # Define the database file path name = "_my-database.db" path = os.path.join(desktop_path, name) # Create the database if it doesn't exist if not os.path.exists(path): os.makedirs(path, exist_ok=True) database = MountAssetDatabase(path) c4d.storage.ShowInFinder(path, open=True) # Execute the script if __name__ == '__main__': main()
Am I misunderstanding what should happen - or is it not working as expected?
-
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!
-
Hey @d_keith,
I am briefly on vacation, so it will probably take a week before I can have a look. But you are in good hands with the rest of the SDK team
But, yes, you should be able to create a repo (i.e., asset database) with
CreateRepositoryFromUrl
and when the passed URL already contains a repo, it should not create a new one. That was at least its S26 behavior, it could be that Tilo has changed that since then. If I remember correctly, Daniel reported something similar about that function not respecting existing repos, or at least that it created a database asset each time for being called (not the same as an entire new repo and probably what you mean).The TLDR aside from the concrete support case is: Please create tickets for bugs, when the documentation says one thing, and the function does another thing, this is at least worthy of being a bug candidate. When this is super urgent for you and Ilia or Maxime do not have an answer right away, you could also ask Tilo or talk with Daniel what he did.
-
FYI: I also turned off the edit limit for new users of 3600 seconds, I guess the system views you as a new user with 12 postings.
-
Hi @d_keith,
I would kindly ask you to check our Support Procedures, namely the "How To Ask Questions" paragraph:
Singular Question: The initial posting of a support topic must contain a singular question. Do not ask ten things at once, that makes it extremely hard to answer topics. Break up your questions into multiple topics.
Here you effectively have 4 different questions about Asset Browser, and these are candidates for 4 different threads on the forum. In your further postings please try to follow the aforementioned rules.
Regarding your questions:
- Should I be able to mount a directory and have it auto-create a DB?
Mounting database is effectively executing the AssetDataBasesInterface.SetDatabases. It has nothing to do with creating database neither semantically, nor is this mentioned in the docu. If you need to create repository, please use maxon.AssetInterface.CreateRepositoryFromUrl(), it will scan the directory for being already a database and create proper dir structure if it's not.
- Any idea why I need to re-try creating the Repo for it to work?
If you face any errors in the script, please always attach at least the error message! In this case I assume you receive the following error, when executing the
maxon.AssetRepositoryTypes.AssetDatabase()
for the first time after Cinema 4D started. Looks like a bug to me, I've created a bug report for that (ITEM#585831).The error message:
Traceback (most recent call last): File "console", line 1, in <module> File "C:\Program Files\Maxon Cinema 4D 2025\resource\modules\python\libs\python311\maxon\interface.py", line 5049, in __call__ self._cachedObj.R = _maxon_mapping.GetAssociatedDataType(dt) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Exception: unable to find datatype
As a workaround you can just execute the following line first in your main():
maxon.AssetRepositoryTypes.AssetDatabase()
- ... about multiple DBs ...
You're giving your databases unique IDs with the line
repo_id = maxon.AssetInterface.MakeUuid(str(url), True)
. If you need them to be the same, just pick one instead, e.g.repo_id = maxon.Id('net.maxon.sdk.cool.things.repo')
- ... revealing assets doesn't work...
I've created a bug report for that, as it looks like a bug with refreshing in the Asset Browser. As a workaround you can reload databases and reopen the AB before reveling your assets, e.g.:
def RepenAssetBrowser(): CID_ASSET_BROWSER = 1054225 # Close the Asset Browser if it's alredy opened if c4d.IsCommandChecked(CID_ASSET_BROWSER): c4d.CallCommand(CID_ASSET_BROWSER) # Open again c4d.CallCommand(CID_ASSET_BROWSER) def main(): # ... # assets = [] # ... # assets.append(asset) # ... maxon.AssetDataBasesInterface.ReloadAssetRepositories(True) RepenAssetBrowser() maxon.AssetManagerInterface.RevealAsset(assets)
Cheers,
Ilia