mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Assets: Make remote library listing work with custom properties
The remote library listing already included custom properties. However, the format they used didn't match what the C++ indexer expected at all. This means the custom properties would essentially get lost. Sometimes they contain important information though, for example, the type of node tree so the compositor asset shelf can display only compositing assets. The C++ indexer expected the same format as for the local asset index. This avoids duplicating the reading code. This commit makes the listing match that. Another issue is, that a top level custom property should always be a group property (this is the one iterated within when doing `bpy.types.AssetData.items(), like the listing generator does). Otherwise adding more properties isn't supported, and property lookups assumed this. We can handle that by letting the indexer create the top level group if needed, that way it can be implicit in the remote library listing. Pull Request: https://projects.blender.org/blender/blender/pulls/156305
This commit is contained in:
parent
bc89fff217
commit
5556e04509
6 changed files with 76 additions and 48 deletions
|
|
@ -29,13 +29,13 @@ type AssetIDTypeV1 = str
|
|||
|
||||
|
||||
class CustomPropertyTypeV1(StrEnum):
|
||||
STRING = "STRING"
|
||||
INT = "INT"
|
||||
FLOAT = "FLOAT"
|
||||
ARRAY = "ARRAY"
|
||||
GROUP = "GROUP"
|
||||
DOUBLE = "DOUBLE"
|
||||
BOOLEAN = "BOOLEAN"
|
||||
IDP_STRING = "IDP_STRING"
|
||||
IDP_INT = "IDP_INT"
|
||||
IDP_FLOAT = "IDP_FLOAT"
|
||||
IDP_ARRAY = "IDP_ARRAY"
|
||||
IDP_GROUP = "IDP_GROUP"
|
||||
IDP_DOUBLE = "IDP_DOUBLE"
|
||||
IDP_BOOLEAN = "IDP_BOOLEAN"
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -96,14 +96,15 @@ class AssetMetadataV1:
|
|||
description: str | None = None
|
||||
license: str | None = None
|
||||
copyright: str | None = None
|
||||
custom: CustomPropertiesV1 | None = None
|
||||
properties: CustomPropertiesV1 | None = None
|
||||
|
||||
|
||||
type CustomPropertiesV1 = dict[str, CustomPropertyV1]
|
||||
type CustomPropertiesV1 = list[CustomPropertyV1]
|
||||
|
||||
|
||||
@dataclass
|
||||
class CustomPropertyV1:
|
||||
name: str
|
||||
type: CustomPropertyTypeV1
|
||||
value: CustomPropertiesV1 | list[Any] | float | int | str | bool
|
||||
itemtype: CustomPropertyTypeV1 | None = None
|
||||
|
|
|
|||
|
|
@ -287,14 +287,14 @@ components:
|
|||
"description": { type: string }
|
||||
"license": { type: string }
|
||||
"copyright": { type: string }
|
||||
"custom": { $ref: "#/components/schemas/CustomPropertiesV1" }
|
||||
"properties": { $ref: "#/components/schemas/CustomPropertiesV1" }
|
||||
|
||||
CustomPropertiesV1:
|
||||
type: object
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/CustomPropertyV1"
|
||||
description: >
|
||||
Arbitrary custom properties of the asset. Keys are the property names.
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/CustomPropertyV1"
|
||||
|
||||
CustomPropertyV1:
|
||||
type: object
|
||||
|
|
@ -304,6 +304,7 @@ components:
|
|||
as `CustomPropertiesV1` object again. Arrays should specify an
|
||||
`itemtype`.
|
||||
properties:
|
||||
"name": { type: string }
|
||||
"type": { $ref: "#/components/schemas/CustomPropertyTypeV1" }
|
||||
"itemtype": { $ref: "#/components/schemas/CustomPropertyTypeV1" }
|
||||
"value":
|
||||
|
|
@ -314,14 +315,23 @@ components:
|
|||
- { type: integer }
|
||||
- { type: string }
|
||||
- { type: boolean }
|
||||
required: [type, value]
|
||||
required: [name, type, value]
|
||||
|
||||
CustomPropertyTypeV1:
|
||||
type: string
|
||||
description: >
|
||||
Type of IDProperty without the `IDP_` prefix, see `eIDPropertyType` in `DNA_ID_enumms.h`.
|
||||
For now, type `ID` and `IDPARRAY` are not supported.
|
||||
enum: [STRING, INT, FLOAT, ARRAY, GROUP, DOUBLE, BOOLEAN]
|
||||
Type of IDProperty, see `eIDPropertyType` in `DNA_ID_enumms.h`. For now,
|
||||
type `ID` and `IDPARRAY` are not supported.
|
||||
enum:
|
||||
[
|
||||
IDP_STRING,
|
||||
IDP_INT,
|
||||
IDP_FLOAT,
|
||||
IDP_ARRAY,
|
||||
IDP_GROUP,
|
||||
IDP_DOUBLE,
|
||||
IDP_BOOLEAN,
|
||||
]
|
||||
|
||||
CatalogV1:
|
||||
type: object
|
||||
|
|
|
|||
|
|
@ -144,37 +144,40 @@ def _get_asset_meta(asset_data: bpy.types.AssetData) -> api_models.AssetMetadata
|
|||
# Convert custom properties.
|
||||
import rna_prop_ui
|
||||
|
||||
custom_props: api_models.CustomPropertiesV1 = {}
|
||||
custom_props: api_models.CustomPropertiesV1 = []
|
||||
for prop_name, prop_value in asset_data.items():
|
||||
is_array = isinstance(prop_value, rna_prop_ui.ARRAY_TYPES) and len(prop_value) > 0
|
||||
item_value = prop_value[0] if is_array else prop_value
|
||||
|
||||
match item_value:
|
||||
case bool():
|
||||
value_type = api_models.CustomPropertyTypeV1.BOOLEAN
|
||||
value_type = api_models.CustomPropertyTypeV1.IDP_BOOLEAN
|
||||
case int():
|
||||
value_type = api_models.CustomPropertyTypeV1.INT
|
||||
value_type = api_models.CustomPropertyTypeV1.IDP_INT
|
||||
case str():
|
||||
value_type = api_models.CustomPropertyTypeV1.STRING
|
||||
value_type = api_models.CustomPropertyTypeV1.IDP_STRING
|
||||
case float():
|
||||
value_type = api_models.CustomPropertyTypeV1.FLOAT
|
||||
value_type = api_models.CustomPropertyTypeV1.IDP_FLOAT
|
||||
case _:
|
||||
# Unsupported type, just ignore it.
|
||||
continue
|
||||
|
||||
if is_array:
|
||||
custom_prop = api_models.CustomPropertyV1(
|
||||
type=api_models.CustomPropertyTypeV1.ARRAY,
|
||||
name=prop_name,
|
||||
type=api_models.CustomPropertyTypeV1.IDP_ARRAY,
|
||||
value=list(prop_value),
|
||||
itemtype=value_type,
|
||||
)
|
||||
else:
|
||||
custom_prop = api_models.CustomPropertyV1(type=value_type, value=prop_value)
|
||||
custom_prop = api_models.CustomPropertyV1(
|
||||
name=prop_name, type=value_type, value=prop_value
|
||||
)
|
||||
|
||||
custom_props[prop_name] = custom_prop
|
||||
custom_props.append(custom_prop)
|
||||
|
||||
if custom_props:
|
||||
meta.custom = custom_props
|
||||
meta.properties = custom_props
|
||||
|
||||
if meta == api_models.AssetMetadataV1():
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -469,7 +469,9 @@ std::unique_ptr<io::serialize::ArrayValue> convert_to_serialize_values(
|
|||
const IDProperty *properties);
|
||||
|
||||
/**
|
||||
* \brief Convert the given `value` to an `IDProperty`.
|
||||
* \brief Convert the given `value` (which must be an array) to an `IDProperty`.
|
||||
* \return The first successfully converted property with further array elements in the
|
||||
* `IDProperty.next` chain, or null if none could be converted or \a value is not an array.
|
||||
*/
|
||||
IDProperty *convert_from_serialize_value(const io::serialize::Value &value);
|
||||
|
||||
|
|
|
|||
|
|
@ -252,7 +252,19 @@ AssetMetaData *asset_metadata_from_dictionary(const DictionaryValue &entry)
|
|||
}
|
||||
|
||||
if (const std::shared_ptr<Value> *value = entry.lookup(ATTRIBUTE_ENTRIES_PROPERTIES)) {
|
||||
asset_data->properties = convert_from_serialize_value(**value);
|
||||
IDProperty *properties = convert_from_serialize_value(**value);
|
||||
|
||||
/* The top level property must be a group, further asset metadata property lookups assume
|
||||
* that. This is also the only way to support more than a single property. */
|
||||
if (properties->next || properties->type != IDP_GROUP) {
|
||||
asset_data->properties = bke::idprop::create_group("AssetMetaData.properties").release();
|
||||
for (IDProperty *property = properties; property != nullptr; property = property->next) {
|
||||
IDP_AddToGroup(asset_data->properties, property);
|
||||
}
|
||||
}
|
||||
else {
|
||||
asset_data->properties = properties;
|
||||
}
|
||||
}
|
||||
|
||||
return asset_data;
|
||||
|
|
|
|||
|
|
@ -45,19 +45,19 @@ class CustomPropertiesTest(unittest.TestCase):
|
|||
|
||||
Types = api_models.CustomPropertyTypeV1
|
||||
Prop = api_models.CustomPropertyV1
|
||||
expected_custom = {
|
||||
'amazing': Prop(type=Types.BOOLEAN, value=True),
|
||||
'barcode': Prop(type=Types.STRING, value='155366'),
|
||||
'count': Prop(type=Types.INT, value=47),
|
||||
'location': Prop(type=Types.STRING, value='café'),
|
||||
'size': Prop(type=Types.FLOAT, value=32.7),
|
||||
'dimensions': Prop(type=Types.ARRAY,
|
||||
value=[2.0, 2.0, 2.0],
|
||||
itemtype=Types.FLOAT),
|
||||
}
|
||||
# autopep8: off
|
||||
expected_props = [
|
||||
Prop(name='dimensions', type=Types.IDP_ARRAY, value=[2.0, 2.0, 2.0], itemtype=Types.IDP_FLOAT),
|
||||
Prop(name='barcode', type=Types.IDP_STRING, value='155366'),
|
||||
Prop(name='location', type=Types.IDP_STRING, value='café'),
|
||||
Prop(name='size', type=Types.IDP_FLOAT, value=32.7),
|
||||
Prop(name='count', type=Types.IDP_INT, value=47),
|
||||
Prop(name='amazing', type=Types.IDP_BOOLEAN, value=True),
|
||||
]
|
||||
# autopep8: on
|
||||
|
||||
assert meta is not None
|
||||
self.assertEqual(expected_custom, meta.custom)
|
||||
self.assertEqual(expected_props, meta.properties)
|
||||
|
||||
def test_array_properties(self) -> None:
|
||||
asset_data = self.cube.asset_data
|
||||
|
|
@ -70,17 +70,17 @@ class CustomPropertiesTest(unittest.TestCase):
|
|||
|
||||
Types = api_models.CustomPropertyTypeV1
|
||||
Prop = api_models.CustomPropertyV1
|
||||
expected_custom = {
|
||||
'agents': Prop(type=Types.ARRAY, value=["007", "47", "327"], itemtype=Types.STRING),
|
||||
'locations': Prop(type=Types.ARRAY, value=["Hokkaido", "Santa Fortuna", "Sapienza"], itemtype=Types.STRING),
|
||||
'boundingbox': Prop(type=Types.ARRAY, value=[-3.0, -4.0, -0.1, 1.0, 2.0, 3.0], itemtype=Types.FLOAT),
|
||||
'dimensions': Prop(type=Types.ARRAY,
|
||||
value=[2.0, 2.0, 2.0],
|
||||
itemtype=Types.FLOAT),
|
||||
}
|
||||
# autopep8: off
|
||||
expected_prop = [
|
||||
Prop(name='dimensions', type=Types.IDP_ARRAY, value=[2.0, 2.0, 2.0], itemtype=Types.IDP_FLOAT),
|
||||
Prop(name='agents', type=Types.IDP_ARRAY, value=["007", "47", "327"], itemtype=Types.IDP_STRING),
|
||||
Prop(name='locations', type=Types.IDP_ARRAY, value=["Hokkaido", "Santa Fortuna", "Sapienza"], itemtype=Types.IDP_STRING),
|
||||
Prop(name='boundingbox', type=Types.IDP_ARRAY, value=[-3.0, -4.0, -0.1, 1.0, 2.0, 3.0], itemtype=Types.IDP_FLOAT),
|
||||
]
|
||||
# autopep8: on
|
||||
|
||||
assert meta is not None
|
||||
self.assertEqual(expected_custom, meta.custom)
|
||||
self.assertEqual(expected_prop, meta.properties)
|
||||
|
||||
def test_serialize_to_json(self) -> None:
|
||||
meta = asset_finder._get_asset_meta(self.cube.asset_data)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue