Read and write asset metadata to describe and organize assets.
Overview
Assets are represented as asset descriptions in asset repositories that contain the meta-information that describe an asset. An asset description exposes most importantly the Url, asset identifier, and version of an asset. The identifier and version of an asset identify it in an asset repository and the Url denotes the physical location of the primary data of the asset. The Url for an media-image asset points for example to the image file that is being wrapped by the asset. The file format of the file at the asset Url depends on the asset type. File assets for materials, objects and scenes will be stored in the Cinema 4D format and can be opened with Cinema 4D without any additional steps required. All file type assets of subtype media, wrapping images or videos, and plain file assets, wrapping arbitrary file types as for example a PDF or JSON file, will store their primary data in the wrapped file format. The file format of other asset types like preset assets or node template assets is not readable by third parties without the Asset API and hidden by the asset type implementations. When that primary data of an asset is stored in a local database, it will always be placed in a file named "asset".
An asset description also contains a dictionary like object exposing the name of an asset, an annotation, keywords are more descriptive metadata. The metadata entries that can be contained in that dictionary depends on the type of an asset. For example, only media-image assets carry metadata describing the resolution of their content and only object and scene assets carry metadata about the point count of their content. The possible metadata entries an asset can have are defined in dedicated namespaces and can be extended by third party developers.
Technical Overview
An AssetDescriptionInterface reference exposes the identifier, type identifier, version, Url, and repository of an asset with its methods GetId, GetTypeId, GetVersion, GetUrl, and GetRepository. It also provides read access to the metadata of an asset with its method GetMetaData, returning an AssetMetaDataInterface reference, a dictionary like object granting key-value pair read access to the metadata.
Metadata entries in a AssetMetaDataInterface reference must be read with AssetMetaDataInterface::Get and written with AssetDescriptionInterface::StoreMetaData of the AssetDescriptionInterface reference holding the asset metadata. An exception to that rule are metadata entries of type String which should be read with GetMetaString and written with StoreMetaString, as these methods simplify handling the containers that contain the localized String metadata entries. One of the entries in a metadata dictionary is stored under the key ASSETMETADATA::MetaProperties
and returns a sub-container of metadata entries, containing primarily the asset type dependent meta information as for example media resolution or total point count.
The keys for metadata entries are exposed as attributes in the namespace maxon::ASSETMETADATA and the keys for the meta-properties are exposed in the namespace ASSET::METAPROPERTIES. The ASSET namespace also contains other asset related attributes but they are only rarely useful for SDK users. The interfaces KeywordAssetInterface and CategoryAssetInterface provide static convenience functions which can be used to add category and keyword metadata to asset descriptions.
Related Topics
Examples
The examples shown here are all related to the "Metadata" category in the Asset API Examples plugin. With the plugin you can run these examples to see their output. Each example shown here has a link bar at its bottom, linking to the code on GitHub, as well as to a calling context on GitHub. A calling context is a function which showcases how to gather the required inputs for that specific example and is targeted at users who want to know how to carry out a specific task with zero prerequisites. The context link is named "How to call this example ...".
Access Asset Description Data
Accesses the data attached to an asset description.
Highlights the meta information provided by an asset description for its asset that is not being accessed with the asset metadata container.
maxon::Result<void> AccessAssetDescriptionData(
const maxon::AssetDescription& assetDescription)
{
const maxon::Id assetId = assetDescription.GetId();
const maxon::Id assetTypeId = assetDescription.GetTypeId();
const
maxon::
String humanReadbleAssetUrlString = assetUrl.ConvertToUiNameWithRepository(
ApplicationOutput("\tAsset Type Id: @", assetTypeId);
ApplicationOutput("\tRaw
Asset Url:
@", rawAssetUrl);
ApplicationOutput("\tAsset Url: @", assetUrl);
ApplicationOutput("\tHuman Readble
Asset Url
String:
@", humanReadbleAssetUrlString);
// Assets can have multiple versions which all then share intentionally the same id. The version
// of an asset description is also stored in its metadata, but can more easily be accessed with
// GetVersion() and GetVersionAndId. With the later returning an identifer that allows to
// distinguish multiple versions of an asset.
const maxon::Id assetVersion = assetDescription.GetVersion();
const maxon::IdAndVersion assetIdVersion = assetDescription.GetIdAndVersion();
ApplicationOutput("\tAsset Version: @", assetVersion);
ApplicationOutput("\tAsset
IdAndVersion:
@", assetIdVersion);
// Important properties of an asset are also its repository and metadata container. The latter
// contains most of the descriptive and administrative metadata associated with an asset.
const maxon::AssetRepositoryRef assetRepository = assetDescription.GetRepository();
const maxon::Id assetRepositoryId = assetDescription.GetRepositoryId();
const maxon::AssetMetaData assetMetadata = assetDescription.GetMetaData();
ApplicationOutput("\tAsset
Repository Id:
@", assetRepositoryId);
ApplicationOutput("\tAsset Metadata: @", assetMetadata);
// Also the reference to the AssetInterface for an asset description can be be loaded. It provides
// access to more data (which overlaps with the data exposed in the asset description) and gives
// access to the asset implementation. The UpdatePreviewThumbnail() example for the dots preset
// asset type implementation provides a usage scenario for both accessing the base and type
// specific asset interface to call the asset implementation. In non-implementation usage both
// the base and type specific asset interface of an asset have to be accessed only rarely.
maxon::Asset asset = assetDescription.Load() iferr_return;
ApplicationOutput("\tLoaded asset interface: @", asset);
const maxon::Id alsoAssetTypeId = asset.GetTypeId();
ApplicationOutput("\tAsset Type: @", alsoAssetTypeId);
// When necessary, the asset interface can be cast to its type specific asset interface.
if (assetTypeId == (maxon::AssetTypes::File().GetId()))
{
maxon::FileAsset fileAsset = maxon::Cast<maxon::FileAsset>(asset);
if (!fileAsset)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not cast asset to
file asset."_s);
}
return maxon::OK;
}
int base
Definition: abstract.h:611
NONE
The command asset state is not defined.
Definition: asset_command.h:0
Definition: lib_net.h:547
Definition: lib_net.h:560
Definition: c4d_string.h:39
static MAXON_METHOD Result< Url > GetAssetUrl(const AssetDescription &asset, Bool isLatest)
Definition: apibaseid.h:253
CONVERTTOUINAMEFLAGS
See ConvertToUiName() and ConvertToUiNameWithRepository()
Definition: url.h:67
#define ApplicationOutput(formatString,...)
Definition: debugdiagnostics.h:210
The maxon namespace contains all declarations of the MAXON API.
Definition: autoweight.h:14
Tuple< Id, Id > IdAndVersion
IdAndVersion is used for assets to store the asset id (first) and version (second) in a single tuple.
Definition: idandversion.h:13
#define iferr_scope
Definition: resultbase.h:1384
#define iferr_return
Definition: resultbase.h:1519
const char const char const char * file
Definition: object.h:439
Add Asset Versions
Adds a version to a file asset of subtype object.
A version of an asset is an asset that is stored under the same identifier as another asset but a different version. Such assets will be displayed as a singular asset in the Asset Browser and the different versions of the asset are only accessible in the 'Detail Area' of the Asset Browser. When that asset is loaded, and not specified differently, always the most recent version of the asset is loaded. Different versions of an asset must match in type but can otherwise be entirely different. The example adds an asset version to object assets that only contains a parametric sphere, overwriting all other content in the most recent version of the target asset.
const maxon::AssetDescription& asset)
{
if (!asset)
if (asset.GetTypeId() != maxon::AssetTypes::File().GetId())
const maxon::AssetRepositoryRef repository = asset.GetRepository();
if (!repository)
if (!repository.IsWritable())
"The repository the asset is attached to is read-only."_s);
const maxon::AssetMetaData metadata = asset.GetMetaData();
decltype(maxon::ASSETMETADATA::SubType)>().GetValueOrNull()
iferr_return;
if (subtype != maxon::ASSETMETADATA::SubType_ENUM_Object)
if (sphere == nullptr)
doc->InsertObject(sphere,
nullptr,
nullptr,
false);
decltype(maxon::ASSETMETADATA::Category)>().GetValueOrNull()
iferr_return;
const
maxon::StoreAssetStruct storeAsset{ categoryId, repository, repository };
const maxon::AssetMetaData newMetadata;
sphere,
doc, storeAsset, assetId,
"Sphere"_s, version.
ToString(), newMetadata,
true)
if (!assetVersion)
ApplicationOutput("Added asset version: @", assetVersion);
return std::move(assetVersion);
}
BaseDocument * GetActiveDocument()
Definition: c4d_basedocument.h:498
Definition: c4d_baseobject.h:225
static BaseObject * Alloc(Int32 type)
static MAXON_METHOD Result< AssetDescription > CreateObjectAsset(BaseObject *op, BaseDocument *activeDoc, const StoreAssetStruct &storeAssetStruct, const Id &assetId, const String &assetName, const String &assetVersion, const AssetMetaData ©MetaData, Bool addAssetsIfNotInThisRepository)
static MAXON_METHOD Result< Id > MakeUuid(const Char *prefix, Bool compact)
String ToString(const FormatStatement *formatStatement=nullptr) const
const Id & Get() const
Definition: apibaseid.h:185
#define Osphere
Sphere.
Definition: ge_prepass.h:1103
#define MAXON_SOURCE_LOCATION
Definition: memoryallocationbase.h:67
const char * doc
Definition: pyerrors.h:226
Search for Asset Categories by Name
Finds a all category assets with a given name and category.
Category names are not unique as there can be two assets named 'B' within the asset category 'A'. This example returns all categories that have a given name and optionally a specific parent category. Passing in the empty id for the parent category will match categories at the root level.
This approach of retrieving asset categories is error prone, since there can be multiple AssetCategoryInterface instances for a single location which live in different repositories. It should only be used when a category identifier can neither be predicted nor hard-coded by looking it up in the Asset Browser. See Related Topics - Development Tools for details on retrieving asset identifiers in the Asset Browser. See the Generate Asset Identifiers example for details on predictable asset identifier generation.
{
[&results, &serachName, &category, &testCategory, &defaultLanguage]
{
return true;
if (testCategory)
{
if (assetCategory != category)
return true;
}
return true;
return true;
{
"Could not find any category assets with the name '@' and parent '@'.", serachName, category);
}
"Found @ category assets with the '@' name '@' and the parent category '@'."_s :
"Found @ category asset with the '@' name '@' and the parent category '@'."_s;
}
Py_ssize_t count
Definition: abstract.h:640
Definition: basearray.h:412
MAXON_ATTRIBUTE_FORCE_INLINE ResultRef< T > Append(ARG &&x)
Definition: basearray.h:677
MAXON_ATTRIBUTE_FORCE_INLINE Int GetCount() const
Definition: basearray.h:573
MAXON_ATTRIBUTE_FORCE_INLINE Bool Contains(typename ByValueParam< VALUETYPE >::type v) const
Definition: collection.h:555
static MAXON_METHOD LanguageRef GetDefaultLanguage()
Definition: string.h:1235
Bool IsEmpty() const
Definition: string.h:1440
Int64 Int
signed 32/64 bit int, size depends on the platform
Definition: apibase.h:188
bool Bool
boolean type, possible values are only false/true, 8 bit
Definition: apibase.h:181
return OK
Definition: apibase.h:2690
#define FormatString(...)
Definition: string.h:2100
const Class< R > & Get(const Id &cls)
Definition: objectbase.h:2073
@ LATEST
Set this flag to obtain only the latest version of the asset.
const char const char * msg
Definition: object.h:438
Search for Asset Categories by Path
Finds all category assets that match a given category path.
This approach of retrieving asset categories is error prone, since there can be multiple AssetCategoryInterface instances for a single location which live in different repositories. It should only be used when a category identifier can neither be predicted nor hard-coded by looking it up in the Asset Browser. Categories within the path must be separated by forward slashes. The category path "A/B/C"_s
will return all categories named C
with the ancestors named A
and B
, in the order A->B->C
. See Related Topics - Development Tools for details on retrieving asset identifiers in the Asset Browser. See the Generate Asset Identifiers example for details on predictable asset identifier generation to 'guess' the identifier of a category asset which has been generated in such predictable manner.
{
{
if (isFirst)
{
isFirst = false;
continue;
}
for (maxon::AssetDescription parent : temp)
{
FindCategoryAssetsByName(repository, results,
token, parent.GetId(),
true)
iferr_return;
}
}
const
maxon::LanguageRef defaultLanguage =
maxon::Resource::GetDefaultLanguage();
"Found category asset with the '@'
label '@', matching the path '@', the
id '@' and
@"_s +
" attached assets."_s);
for (
maxon::AssetDescription categoryAsset : results)
{
repository.FindAssets(
{
maxon::ASSETMETADATA::Category,
maxon::Id()))
{
return true;
}
if (childCategory == categoryAsset.GetId())
return true;
}
message, defaultLanguage, assetName, categoryPath, categoryAsset.GetId(),
count);
}
}
void Reset()
Deletes all elements (calls destructors and frees memory).
Definition: basearray.h:541
MAXON_ATTRIBUTE_FORCE_INLINE Result< void > CopyFrom(COLLECTION2 &&other, COLLECTION_RESIZE_FLAGS resizeFlags=COLLECTION_RESIZE_FLAGS::FIT_TO_SIZE)
Definition: collection.h:261
PyObject * token
Definition: context.h:69
const char * message
Definition: pyerrors.h:189
for(i=0;i< length;i++)
Definition: unicodeobject.h:61
int32_t Int32
32 bit signed integer datatype.
Definition: apibase.h:176
#define iferr(...)
Definition: errorbase.h:388
Generate Asset Identifiers
Demonstrates how to generate asset identifiers.
Asset identifiers must be unique within a repository without its bases. UUID-based identifiers generated by the operating system assure this quality but are also hard to reproduce at a later point of time or on another system. In some cases this quality of predictable asset identifiers is required to search for assets effectively or avoid ingesting duplicate assets. The example demonstrates with the cases of a category and image asset how such identifying asset hashes could be constructed with the maxon API in a sufficiently collision free manner.
{
// The alternative is to hash the identifier for an asset manually. A category asset identifier
// could for example be hashed over its name and path; which will allow for predicting the id
// of any category asset created in this manner:
// Will be the same on each execution but will not allow multiple assets of the same name to be
// attached to the same parent category, as they will resolve to the same identifier. For
// such asset can be searched by 'predicting' its id over its known properties.
maxon::String assetName("Stone");
ApplicationOutput(msg, categoryId);
// Note that the version of an asset usually does not have to be included in such special hashes,
// as there is the interface maxon::IdAndVersion for that purpose. A more complex example which
// does hash an (image) file asset over its origin file path, resolution and bit depth is shown
// below.
maxon::Url assetOriginPath("file:
hash = maxon::GetPasswordHash(hashInput, maxon::StreamConversions::HashSHA256()) iferr_return;
// The length of an id does not have to conform to the 16 character hash length as shown in the
// category asset example when a shortened hash is not sufficient.
maxon::Id imageFileId;
"Media image asset
identifier hashed over image properties:
@", imageFileId);
// In special cases the asset id can also be a human readable id, as for example it is employed
// by the asset category "uncategorized".
maxon::Id uncategorizedId("net.
maxon.assetcategory.uncategorized");
// When declaring such identifiers the inverted domain pattern common to all human readable
// maxon::Id instances should be applied, with the first element after the second level domain
// being "assetcategory", followed by the human readable
identifier. Be careful not to introduce
maxon::Id myId("com.mycompany.assetcategory.myhumanreableid");
return maxon::OK;
}
PyObject Py_hash_t hash
Definition: dictobject.h:35
UUID
C4DUuid.
Definition: ge_prepass.h:38
maxon::Int32 Int32
Definition: ge_sys_math.h:60
Result< String > GetPasswordHash(const String &password, const StreamConversionFactory &hashClass, const DataDictionary &settings=DataDictionary())
unsigned long Py_ssize_t width
Definition: pycore_traceback.h:88
Iterate over Asset Metadata
Iterates over all existing entries in an AssetMetadata instance.
{
const maxon::AssetMetaData metadata = assetDescription.GetMetaData();
for (
const MetaDataTuple&
item : entries)
{
}
}
PyObject * value
Definition: abstract.h:715
PyObject * key
Definition: abstract.h:289
Definition: datatypebase.h:1199
Definition: datatypelib.h:31
PyObject PyObject * item
Definition: dictobject.h:42
Read Asset Metadata
Reads the metadata of an asset that are commonly required to be read.
{
const maxon::AssetMetaData metadata = assetDescription.GetMetaData();
maxon::OBJECT::BASE::NAME, currentLanguage,
""_s)
iferr_return;
const maxon::String asssetAnnotation = assetDescription.GetMetaString(
maxon::OBJECT::BASE::ANNOTATIONS, currentLanguage,
""_s)
iferr_return;
ApplicationOutput(
"\tUsage-time-stamp: @, Usage-count: @", usage.first, usage.second);
decltype(maxon::ASSETMETADATA::Dependencies)>().GetValueOrNull()
iferr_return;
ApplicationOutput(
"\t\t@: @", dependency.originalName, dependency.assetIdAndVersion);
decltype(maxon::ASSETMETADATA::Keywords)>().GetValueOrNull()
iferr_return;
decltype(maxon::ASSETMETADATA::UserKeywords)>().GetValueOrNull()
iferr_return;
decltype(maxon::ASSETMETADATA::Category)>().GetValueOrNull()
iferr_return;
if (!lookupRepo)
const maxon::AssetDescription categoryDescription = lookupRepo.FindLatestAsset(
maxon::AssetTypes::Category().GetId(), category,
maxon::Id(),
const maxon::String categoryName = categoryDescription.GetMetaString(
maxon::OBJECT::BASE::NAME, currentLanguage,
""_s)
iferr_return;
decltype(maxon::ASSETMETADATA::SubType)>().GetValueOrNull()
iferr_return;
maxon::DataDictionary metaProperties = metadata.Get(
maxon::ASSETMETADATA::MetaProperties, maxon::DataDictionary())
iferr_return;
maxon::ASSET::METAPROPERTIES::C4DFILE::POINTCOUNT, -1);
}
Definition: datadictionary.h:11
static MAXON_METHOD const UpdatableAssetRepositoryRef & GetUserPrefsRepository()
static MAXON_METHOD LanguageRef GetCurrentLanguage()
std::add_const< typename Pack::template At< I >::type >::type & Get() const
Returns the element at index I. Equivalent to TupleGet<I>(*this).
Definition: tuple.h:745
PyObject PyObject *const Py_ssize_t PyObject * keywords
Definition: abstract.h:55
Helper class to pass several arguments into asset creation functions.
Definition: assets.h:157
Write Asset Metadata
Writes the metadata of an asset that are commonly required to be written.
{
assetDescription.StoreMetaString(
maxon::OBJECT::BASE::NAME,
"Hello World!"_s, currentLanguage)
iferr_return;
assetDescription.StoreMetaString(
maxon::OBJECT::BASE::ANNOTATIONS,
"C++ SDK Annotation"_s, currentLanguage)
iferr_return;
maxon::Id categegoryId (
"category@a040014bbe784ba0ad315cbd22971f6b");
maxon::Id keywordId(
"keyword@05a41587b3094d08b77a5b6fbc61b4c6");
assetDescription, keywordId,
true, assetDescription.GetRepository())
iferr_return;
assetDescription.StoreMetaData(
}
USER
The meta data should be stored in user folder.
Definition: assets.h:5
static MAXON_METHOD Result< void > SetAssetCategory(const AssetDescription &asset, const Id &category)
static MAXON_METHOD Result< Bool > AddKeyword(const AssetDescription &target, const Id &keywordId, Bool userScope, const AssetRepositoryRef &updateUsageRepository)
OK
Ok.
Definition: ge_prepass.h:0
KIND
An enum to represent the different node space kinds (such as materials or scene nodes).
Definition: node_spaces.h:48
Declare Custom Metadata Attributes
Declares a custom metadata attribute which can be used in asset metadata dictionaries.
Formally, custom asset metadata can be written with any key object into a AssetMetaDataInterface instance, but the Asset API relies in some cases on metadata attributes properly being declared in the correct namespaces. Therefor any custom metadata attribute should be exposed in the namespace maxon::ASSETMETADATA and that attribute should only be referenced in metadata dictionaries with the key exposed in that namespace. Due to the involvement of the Source Processor in the declaration, metadata attributes must be declared in header files. See Write Asset Metadata for an example how the here declared attribute is written.
#include "examples_metadata1.hxx"
{
};
#include "examples_metadata2.hxx"
Definition: asset_command.h:170
MAXON_ATTRIBUTE(AssetCommandQueryStateDelegate, CommandQueryState, "net.maxon.asset.command.metadata.querystate")