adding assets to a userdatabase in the assetbrower with Python
-
I'm trying to add my materials to the asset browser through a python script. I'm basing my script on the code examples from: https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/tree/master/scripts/05_modules/assets
The thing I can't seem to figure out is how to add an asset to a user database instead of one of the standard databases.
In the examples a repository is given as an argument to the storeAssetStruct function who's result is passed to the CreateMaterialAsset function that stores the asset to the assetbrowser.repository = maxon.AssetInterface.GetUserPrefsRepository() storeAssetStruct = maxon.StoreAssetStruct(assetCategoryId, repository, repository) assetDescription = maxon.AssetCreationInterface.CreateMaterialAsset(doc, mat, storeAssetStruct, assetId, assetName, assetVersion, assetMetadata, True)
From what I understand the UserPrefsRepository tells the function to use the standard database Preferences. This leads me to think I would need to find the repository for the userdatabases. I can find the database object but have no clue how to get to the repository from there. Can you point me in the right direction?
Thanks, Bart
-
Hey @wen,
Thank you for reaching out to us. Have you checked asset_databases_r26.py? That should cover all your needs for creating and mounting user asset databases. Specifically, the CreateRepositories and
MountAssetDatabase
examples are probably what you are looking for.Cheers,
Ferdinand -
Hi @ferdinand
Going over it a bit more indepth I see this could work for what I want. It does leave me with a question though:
# Iterate over all currently mounted databases and create an asset repository for each of them. # Doing this is usually not necessary as user asset databases are automatically part of the # the user preferences repository which is easier to retrieve. Creating a repository for a # specific user asset database can be useful to speed up asset searches.
I was under the assumption userdatabases are by default already part of some other repository than the standard ones. Here it's stated they automaticlally become part of the user preferences repository. Does that mean I can somehow specify which database to use if there are multiple in the user preferences repository?
-
Hey @wen,
Well, the elephant in room is probably that we misnamed the two central entities in the Asset API, which often leads to confusion.
When the Asset API talks about databases, it means physical storage locations, i.e., what is usually called a repository. And when the Asset API talks about repositories, it means the logical interface(s) to such physical storage locations, i.e., what is usually referred to as a database or DBMS. In the C++ documentation I then defused the situation a bit by explaining it, but it is still misleading.
The comment you cite uses the terminology we chose then (but is effectively inverted). When you iterate over the physical storage locations of assets ('databases'), you can construct logical interfaces ('repositories') for these storage locations individually. But for search operations this is usually not necessary because our logical interfaces ('repositories') work like Russian nesting dolls, where search interfaces can be stacked into each other to handle multiple physical locations under one interface (the bases of a repository). So, when you just want to search for an asset stored in some user database, you can use the user preferences repository, as this exposes all currently active repositories of a Cinema 4D instance.
When you want to write to a specific physical storage location, you will have to construct a logical interface for that location. Because the user preferences repository will write into the database which is stored in the
prefs
folder of the user.Cheers,
Ferdinand -
@ferdinand
Trying the CreateRepositories() function from the example I'm getting an error. I did not make any changes to the code. Did something change internally perhaps?Traceback (most recent call last): File "scriptmanager", line 42, in <module> File "scriptmanager", line 32, in CreateRepositories File "C:\Program Files\Maxon Cinema 4D 2025\resource\modules\python\libs\python311\maxon\decorators.py", line 495, in Auto ExecStaticMethod(*args) TypeError: unable to convert builtins.NativePyData to @net.maxon.interface.class-cR
This line seems to be the problem:
repository = maxon.AssetInterface.CreateRepositoryFromUrl(rid, bases, database._dbUrl, True, False, False)
-
@ferdinand Could you or someone else please confirm this code works on your end? It's from the examples. I tried it in 2023 and 2025 and can't really think of anything to get it running. I had a look at the decorators.py that's trowing the error but that stuff's a bit over my head.
Thanks, Bart
import maxon def CreateRepositories(): """Creates repositories for all user databases. Doing this is usually not necessary for performing light- to medium-sized asset operations, and the user preferences repository can then be used instead. Only when there is a substantial amount of assets that must be processed, a repository should be constructed to limit the search space for search operations. The method CreateRepositoryFromUrl() used in this example can also be used to create a repository and its underlying database from scratch when the provided URL points to location where no database has been established yet. """ # Wait for all asset databases to be loaded, abort when this is not possible. if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading(): return RuntimeError("Could not load asset databases.") # Get the default language of Cinema 4D (en-US) for retrieving the repository names. defaultLanguage = maxon.Resource.GetDefaultLanguage() # Iterate over all currently mounted databases and create an asset repository for each of them. # Doing this is usually not necessary as user asset databases are automatically part of the # the user preferences repository which is easier to retrieve. Creating a repository for a # specific user asset database can be useful to speed up asset searches. for database in maxon.AssetDataBasesInterface.GetDatabases(): # Create a unique identifier for the repository. rid = maxon.AssetInterface.MakeUuid(str(database._dbUrl), True) # Repositories can be composed out of other repositories which are called bases. In this # case no bases are used to construct the repository. But with bases a repository for all # user databases could be constructed for example. bases = maxon.BaseArray(maxon.AssetRepositoryRef) # Create a writable and persistent repository for the database URL. If #_dbUrl would point # to a location where no database has been yet stored, the necessary data would be created. repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, bases, database._dbUrl, True, False, False) if not repository: raise RuntimeError("Repository construction failed.") # Access some properties of the newly created repository. repoId = repository.GetId() isWriteable = repository.IsWritable() name = repository.GetRepositoryName(defaultLanguage) print(f"{repository} ({name}): id - {repoId}, writeable: {isWriteable}") CreateRepositories()
-
Hey, @wen I saw your message, but we do not always have the time to answer directly. Maxime will have a look on Monday. For me this runs fine, but Maxime said we internally had a similar report.
Cheers,
Ferdinand -
@ferdinand
Thanks for the clarification. I saw you replying to other post and thought: "Perhaps he didn't see or there is no easy answer"
It's nice to know someone will have a further look and come back to it later on. On some support forums that's not always the case.
I had my collegues try the function aswell and it doesn't work for them either. Let's hope Maxime know's what's happening. -
-
Hi @wen I've moved your topic to the bug section since it's indeed a bug, I will ping you on this topic once the fix is available, it should come in one of the next update.
The issue is that the internal cache is not properly updated and therefor this is failing.With that's said there is a ugly workaround which consist of calling it twice so the cache is properly updated.
Find bellow a version that is going to work in all versionsimport c4d import maxon import os def CreateRepFromUrl(url: maxon.Url) -> maxon.UpdatableAssetRepositoryRef: """Create a new repository from a given database URL. If there is no valid database at the given URL, it creates a database at the URL. It always create a new repository and the associated database asset, even if there are existing repositories for that database. """ # Make type checks if not isinstance(url, maxon.Url): raise TypeError("First argument is not a maxon.Url") # Create a unique identifier for the repository. rid = maxon.AssetInterface.MakeUuid(str(url), True) # Repositories can be composed out of other repositories which are called bases. In this # case no bases are used to construct the repository. But with bases a repository for all # user databases could be constructed for example. bases = maxon.BaseArray(maxon.AssetRepositoryRef) # Create a writable and persistent repository for the database URL. If #_dbUrl would point # to a location where no database has been yet stored, the necessary data would be created. if c4d.GetC4DVersion() < 2025200: try: repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False, None) except Exception as e: repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False, None) else: try: repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False) except Exception as e: repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False) if not repository: raise RuntimeError("Repository construction failed.") return repository if __name__ == '__main__': if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading(): raise RuntimeError("Could not load asset databases.") dbPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdb") print(CreateRepFromUrl(maxon.Url(dbPath)))
Cheers,
Maxime.