├── .github └── workflows │ ├── json-schema.yml │ └── markdown.yml ├── LICENSE ├── README.md ├── brand ├── icon_cube.pdf ├── icon_cube.png ├── icon_cube.svg ├── icon_unfolded.pdf ├── icon_unfolded.png ├── icon_unfolded.svg ├── info.pdf ├── info.png ├── logo_square_dark.pdf ├── logo_square_dark.png ├── logo_square_dark.svg ├── logo_square_light.pdf ├── logo_square_light.png ├── logo_square_light.svg ├── logo_wide_dark.pdf ├── logo_wide_dark.png ├── logo_wide_dark.svg ├── logo_wide_light.pdf ├── logo_wide_light.png └── logo_wide_light.svg ├── json-schema ├── datablock │ ├── asset_list_query.json │ ├── authors.json │ ├── branding.json │ ├── dimensions.json │ ├── fetch.download.json │ ├── fetch.from_archive.json │ ├── format.blend.json │ ├── format.json │ ├── format.obj.json │ ├── handle.archive.json │ ├── handle.loose_environment_map.json │ ├── handle.loose_material_map.json │ ├── handle.native.json │ ├── implementation_list_query.json │ ├── keywords.json │ ├── license.json │ ├── link.loose_material.json │ ├── link.mtlx_material.json │ ├── next_query.json │ ├── preview_image_supplemental.json │ ├── preview_image_thumbnail.json │ ├── provider_configuration.json │ ├── provider_reconfiguration.json │ ├── response_statistics.json │ ├── store.json │ ├── text.json │ ├── unlock_balance.json │ ├── unlock_queries.json │ ├── user.json │ └── web_references.json ├── endpoint │ ├── asset_list.json │ ├── auto.json │ ├── connection_status.json │ ├── implementation_list.json │ ├── initialization.json │ └── unlock.json └── template │ ├── fixed_query.json │ ├── meta.json │ └── variable_query.json └── spec.md /.github/workflows/json-schema.yml: -------------------------------------------------------------------------------- 1 | name: JSON Schema Validation 2 | on: [pull_request] 3 | jobs: 4 | validate-json-schema: 5 | name: JSON Schema Check 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Check if json schema was changed 10 | id: check-for-changed-schemas 11 | uses: tj-actions/changed-files@v41 12 | with: 13 | files: | 14 | json-schema/*/*.json 15 | - name: Validate JSON Schema files 16 | if: steps.check-for-changed-schemas.outputs.any_changed == 'true' 17 | uses: cardinalby/schema-validator-action@v3 18 | with: 19 | file: "json-schema/*/*.json" 20 | schema: https://json-schema.org/draft/2020-12/schema 21 | -------------------------------------------------------------------------------- /.github/workflows/markdown.yml: -------------------------------------------------------------------------------- 1 | name: Markdown Link Validation 2 | on: [pull_request] 3 | jobs: 4 | markdown-link-check: 5 | name: Markdown Link Check 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Check if spec.md was changed 10 | id: check-for-changed-spec 11 | uses: tj-actions/changed-files@v41 12 | with: 13 | files: | 14 | spec.md 15 | - name: Validate Markdown Links 16 | uses: gaurav-nelson/github-action-markdown-link-check@v1 17 | if: steps.check-for-changed-spec.outputs.any_changed == 'true' 18 | with: 19 | use-quiet-mode: "yes" 20 | use-verbose-mode: "yes" 21 | config-file: "mlc_config.json" 22 | file-path: ./spec.md 23 | max-depth: 3 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 AssetFetch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | AssetFetch Logo 3 |

4 | 5 | # The AssetFetch Specification 6 | 7 | **AssetFetch** is an HTTP- and JSON-based system for browsing, retrieving and handling/importing digital assets for media creation. 8 | The AssetFetch Protocol aims to provide a standardized way for artists to browse libraries of downloadable assets offered by providers *outside* their current production environment/pipeline, such as those of commercial or free 3D asset vendors, marketplaces or other repositories of models, textures or adjacent kinds of digital assets. 9 | 10 | It aims to create an artist experience similar to existing proprietary import plugins with less development overhead in order to increase interoperability between vendors and applications and allow more vendors - especially smaller ones - to offer their assets to artists right in the applications where they need them. 11 | 12 | # Features 13 | 14 | - Header-based Authentication. 15 | - Browsing assets with thumbnails, server-side searching and filtering, license data, author data and more. 16 | - Support for multiple resolutions, quality levels or any other kind of variation of assets. 17 | - Support for zipped downloads: Providers can still send metadata for individual asset files, even if the actual download arrives in just one zip file. 18 | - Ways for checking compatibility with different open or vendor-native formats: The provider offers metadata for multiple versions of the same asset which the client can use to judge whether it will be able to actually import the file BEFORE downloading it, as best as possible (100% certainty will be impossible). 19 | - Purchasing assets (even composite assets that require multiple purchases) through an "asset unlocking" system. The actual payment isn't handled by AF, users still need to sign up on the provider's website. 20 | - Ways of linking loose files together to cover "loose materials" to make the distribution of PBR materials and HDRIs easier. 21 | - Theming and branding options for providers with banner images, if the client chooses to display those. 22 | - Custom metadata for common file formats. 23 | 24 | # This is a Work-In-Progress 25 | Everything here at the moment should be regarded as a starting-off point for future testing and discussion about how the transfer of 3D assets from vendors to clients can be made made more open and interoperable. 26 | There are still numerous milestones to hit before a version 1.0 can be released: 27 | 28 | - Create better examples for basically everything. This includes examples for every element of the specification as well as more general examples and user stories to better illustrate what AssetFetch is, how it works and the value it delivers both for asset vendors and users. 29 | 30 | - Create proof-of-concept implementations both for the provider- and the client-side in order to demonstrate the viability of the entire system. This will also inevitably lead to changes and clarifications in the spec as problems are encountered and (hopefully) solved. 31 | 32 | - Create more extension-specific datablock definitions for many more formats, both open and application-specific ones (`max`(3DSMax),`ma/mb`(Maya),`uasset`(Unreal Engine),`usda/c/z` (OpenUSD), `gltf/b`(GLTF),...) 33 | 34 | - ... 35 | 36 | # JSON Schema 37 | 38 | JSON-Schemas for AssetFetch are provided in the `/json-schema` subdirectory of this repository. 39 | 40 | # Contributing 41 | 42 | The best way to contribute at this stage is by opening issues with questions, contradictions in the specifications, suggestions for datablocks or any other thoughts about the specification. 43 | 44 | The specification document is written in markdown and therefore easily editable with any text editor. 45 | However, the recommended environment for editing `spec.md` is Visual Studio Code with the following extensions: 46 | 47 | - Markdown Preview Mermaid Support (`bierner.markdown-mermaid`) 48 | - Markdown All-In-One (`yzhang.markdown-all-in-one`) 49 | 50 | To maintain proper document structure, the commands "Format Document" and "Markdown-All-In-One: Add/Update section Numbers" should be used. 51 | All PRs are tested for dead markdown links and syntax issues in the JSON schema. 52 | -------------------------------------------------------------------------------- /brand/icon_cube.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/icon_cube.pdf -------------------------------------------------------------------------------- /brand/icon_cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/icon_cube.png -------------------------------------------------------------------------------- /brand/icon_cube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /brand/icon_unfolded.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/icon_unfolded.pdf -------------------------------------------------------------------------------- /brand/icon_unfolded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/icon_unfolded.png -------------------------------------------------------------------------------- /brand/icon_unfolded.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /brand/info.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/info.pdf -------------------------------------------------------------------------------- /brand/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/info.png -------------------------------------------------------------------------------- /brand/logo_square_dark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_square_dark.pdf -------------------------------------------------------------------------------- /brand/logo_square_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_square_dark.png -------------------------------------------------------------------------------- /brand/logo_square_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /brand/logo_square_light.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_square_light.pdf -------------------------------------------------------------------------------- /brand/logo_square_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_square_light.png -------------------------------------------------------------------------------- /brand/logo_square_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /brand/logo_wide_dark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_wide_dark.pdf -------------------------------------------------------------------------------- /brand/logo_wide_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_wide_dark.png -------------------------------------------------------------------------------- /brand/logo_wide_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /brand/logo_wide_light.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_wide_light.pdf -------------------------------------------------------------------------------- /brand/logo_wide_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssetFetch/spec/397ed9499887a25c46a33e9b2d68fe2c1d96913b/brand/logo_wide_light.png -------------------------------------------------------------------------------- /brand/logo_wide_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /json-schema/datablock/asset_list_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: asset_list_query", 4 | "$id": "af.datablock.asset_list_query", 5 | "description": "Describes the variable query for fetching the list of available assets from a provider.", 6 | "$ref": "../template/variable_query.json" 7 | } -------------------------------------------------------------------------------- /json-schema/datablock/authors.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "AF Datablock: authors", 4 | "$id": "af.datablock.authors", 5 | "description": "Is used communicate the author(s) of a particular asset.", 6 | "type": "array", 7 | "additionalProperties":false, 8 | "items": { 9 | "type": "object", 10 | "additionalProperties": false, 11 | "properties": { 12 | "name": { 13 | "type": "string" 14 | }, 15 | "uri": { 16 | "type": [ 17 | "string", 18 | "null" 19 | ], 20 | "format": "uri" 21 | }, 22 | "role": { 23 | "type": [ 24 | "string", 25 | "null" 26 | ] 27 | } 28 | }, 29 | "required": [ 30 | "name" 31 | ] 32 | } 33 | } -------------------------------------------------------------------------------- /json-schema/datablock/branding.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "AF Datablock: branding", 4 | "$id": "af.datablock.branding", 5 | "description": "Contains brand information about the provider, MAY be used by the client to customize the user interface.", 6 | "type": "object", 7 | "properties": { 8 | "color_accent": { 9 | "type": [ 10 | "string", 11 | "null" 12 | ], 13 | "pattern": "[0-9a-f]{6}" 14 | }, 15 | "logo_square_uri": { 16 | "type": [ 17 | "string", 18 | "null" 19 | ], 20 | "format": "uri" 21 | }, 22 | "logo_wide_uri": { 23 | "type": [ 24 | "string", 25 | "null" 26 | ], 27 | "format": "uri" 28 | }, 29 | "banner_uri": { 30 | "type": [ 31 | "string", 32 | "null" 33 | ], 34 | "format": "uri" 35 | } 36 | }, 37 | "required": [], 38 | "additionalProperties": false 39 | } -------------------------------------------------------------------------------- /json-schema/datablock/dimensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: dimensions", 4 | "$id": "af.datablock.dimensions", 5 | "description": "Contains general information about the physical dimensions of an asset.", 6 | "type": "object", 7 | "properties": { 8 | "width_m": { 9 | "type": [ 10 | "number", 11 | "null" 12 | ] 13 | }, 14 | "height_m": { 15 | "type": [ 16 | "number", 17 | "null" 18 | ] 19 | }, 20 | "depth_m": { 21 | "type": [ 22 | "number", 23 | "null" 24 | ] 25 | } 26 | }, 27 | "required": [], 28 | "additionalProperties": false 29 | } -------------------------------------------------------------------------------- /json-schema/datablock/fetch.download.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: fetch.download", 4 | "$id": "af.datablock.fetch.download", 5 | "description": "Describes how a component file can be downloaded via HTTP.", 6 | "properties": { 7 | "unlock_query_id": { 8 | "type": [ 9 | "string", 10 | "null" 11 | ] 12 | }, 13 | "download_query": { 14 | "$ref": "../template/fixed_query.json" 15 | } 16 | }, 17 | "additionalProperties": false 18 | } -------------------------------------------------------------------------------- /json-schema/datablock/fetch.from_archive.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: fetch.from_archive", 4 | "$id": "af.datablock.fetch.from_archive", 5 | "description": "This datablock indicates that this component represents a file from within an archive that needs to be downloaded separately.", 6 | "type": "object", 7 | "properties": { 8 | "archive_component_id": { 9 | "type": "string" 10 | }, 11 | "component_sub_path": { 12 | "type": "string" 13 | } 14 | }, 15 | "required": [ 16 | "archive_component_id", 17 | "component_sub_path" 18 | ], 19 | "additionalProperties": false 20 | } -------------------------------------------------------------------------------- /json-schema/datablock/format.blend.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: format.blend", 4 | "$id": "af.datablock.format.blend", 5 | "description": "Information about files with the extension `.blend`.", 6 | "type": "object", 7 | "properties": { 8 | "version": { 9 | "type": [ 10 | "string", 11 | "null" 12 | ], 13 | "pattern": "^[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?$" 14 | }, 15 | "is_asset": { 16 | "type": [ 17 | "boolean", 18 | "null" 19 | ] 20 | }, 21 | "targets": { 22 | "type": [ 23 | "array", 24 | "null" 25 | ], 26 | "items": { 27 | "type": "object", 28 | "required": [ 29 | "kind", 30 | "names" 31 | ], 32 | "properties": { 33 | "kind": { 34 | "type": "string", 35 | "enum": [ 36 | "actions", 37 | "armatures", 38 | "brushes", 39 | "cache_files", 40 | "cameras", 41 | "collections", 42 | "curves", 43 | "fonts", 44 | "grease_pencils", 45 | "hair_curves", 46 | "images", 47 | "lattices", 48 | "lightprobes", 49 | "lights", 50 | "linestyles", 51 | "masks", 52 | "materials", 53 | "meshes", 54 | "metaballs", 55 | "movieclips", 56 | "node_groups", 57 | "objects", 58 | "paint_curves", 59 | "palettes", 60 | "particles", 61 | "pointclouds", 62 | "scenes", 63 | "screens", 64 | "simulations", 65 | "sounds", 66 | "speakers", 67 | "texts", 68 | "textures", 69 | "volumes", 70 | "workspaces", 71 | "worlds" 72 | ] 73 | }, 74 | "names": { 75 | "type": "array", 76 | "items": { 77 | "type": "string" 78 | } 79 | } 80 | } 81 | } 82 | } 83 | }, 84 | "required": [] 85 | } -------------------------------------------------------------------------------- /json-schema/datablock/format.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: format", 4 | "$id": "af.datablock.format", 5 | "description": "This is the default format datablock for all file formats that do not have their own dedicated `format.*` datablock.", 6 | "type": "object", 7 | "properties": { 8 | "extension": { 9 | "type": "string", 10 | "pattern": "^\\..*" 11 | }, 12 | "mediatype": { 13 | "type": [ 14 | "string", 15 | "null" 16 | ] 17 | } 18 | }, 19 | "required": [ 20 | "extension" 21 | ], 22 | "additionalProperties": false 23 | } -------------------------------------------------------------------------------- /json-schema/datablock/format.obj.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: format.obj", 4 | "$id": "af.datablock.format.obj", 5 | "type": "object", 6 | "properties": { 7 | "up_axis": { 8 | "type": "string", 9 | "enum": [ 10 | "+x", 11 | "-x", 12 | "+y", 13 | "-y", 14 | "+z", 15 | "-z" 16 | ] 17 | }, 18 | "front_axis": { 19 | "type": "string", 20 | "enum": [ 21 | "+x", 22 | "-x", 23 | "+y", 24 | "-y", 25 | "+z", 26 | "-z" 27 | ] 28 | } 29 | }, 30 | "additionalProperties": false, 31 | "required": [] 32 | } -------------------------------------------------------------------------------- /json-schema/datablock/handle.archive.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: handle.archive", 4 | "$id": "af.datablock.handle.archive", 5 | "description": "This datablock indicates that this component represents an archive, containing other component files.", 6 | "type": "object", 7 | "properties": { 8 | "local_directory_path": { 9 | "type": [ 10 | "string", 11 | "null" 12 | ], 13 | "pattern": "^\\\/$|^[^\\.|\\\/|\\\\]((?!\\.\\.\\\/|\\\\).)*\\\/$" 14 | }, 15 | "extract_fully": { 16 | "type": "boolean" 17 | } 18 | }, 19 | "required": [ 20 | "extract_fully" 21 | ], 22 | "if": { 23 | "properties": { 24 | "extract_fully": { 25 | "const": true 26 | } 27 | } 28 | }, 29 | "then": { 30 | "properties": { 31 | "local_directory_path": { 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "local_directory_path" 37 | ] 38 | }, 39 | "additionalProperties": false 40 | } -------------------------------------------------------------------------------- /json-schema/datablock/handle.loose_environment_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: role.loose_environment_map", 4 | "$id": "af.datablock.role.loose_environment_map", 5 | "description": "Marks a component as an environment map.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "environment_name": { 10 | "type": [ 11 | "string", 12 | "null" 13 | ] 14 | }, 15 | "projection": { 16 | "type": [ 17 | "string", 18 | "null" 19 | ], 20 | "enum": [ 21 | "equirectangular", 22 | "mirror_ball" 23 | ] 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /json-schema/datablock/handle.loose_material_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: role.loose_material_map", 4 | "$id": "af.datablock.role.loose_material", 5 | "description": "Indicates that this component is part of a loose material as a material map.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "material_name": { 10 | "type": "string" 11 | }, 12 | "map": { 13 | "type": "string", 14 | "enum": [ 15 | "albedo", 16 | "roughness", 17 | "metallic", 18 | "diffuse", 19 | "glossiness", 20 | "specular", 21 | "height", 22 | "normal+y", 23 | "normal-y", 24 | "opacity", 25 | "ambient_occlusion", 26 | "emission" 27 | ] 28 | } 29 | }, 30 | "required": [ 31 | "material_name", 32 | "map" 33 | ] 34 | } -------------------------------------------------------------------------------- /json-schema/datablock/handle.native.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: role.native", 4 | "$id": "af.datablock.role.native", 5 | "description": "This datablock indicates that this file should be handled by the host application's native import functionality.", 6 | "type": "object", 7 | "properties": {}, 8 | "additionalProperties": false 9 | } -------------------------------------------------------------------------------- /json-schema/datablock/implementation_list_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: implemementation_list_query", 4 | "$id": "af.datablock.implementation_list_query", 5 | "description": "Describes the variable query for fetching the list of available implementations for an asset from a provider.", 6 | "$ref": "../template/variable_query.json" 7 | } -------------------------------------------------------------------------------- /json-schema/datablock/keywords.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: keywords", 4 | "$id": "af.datablock.keywords", 5 | "description": "Contains keywords or tags that the client MAY use for local asset organization.", 6 | "type": "array", 7 | "items": { 8 | "type": "string" 9 | } 10 | } -------------------------------------------------------------------------------- /json-schema/datablock/license.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "License Datablock", 4 | "$id": "af.datablock.license", 5 | "description": "Contains license information.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "license_spdx": { 10 | "type": [ 11 | "string", 12 | "null" 13 | ] 14 | }, 15 | "license_uri": { 16 | "type": [ 17 | "string", 18 | "null" 19 | ], 20 | "format": "uri" 21 | } 22 | }, 23 | "required": [] 24 | } -------------------------------------------------------------------------------- /json-schema/datablock/link.loose_material.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: link.loose_material", 4 | "$id": "af.datablock.link.loose_material", 5 | "description": "Indicates that this component uses one or multiple materials defined using `handle.loose_material_map` datablocks.", 6 | "additionalItems": false, 7 | "type": "object", 8 | "additionalProperties": false, 9 | "properties": { 10 | "material_name": { 11 | "type": "string" 12 | } 13 | }, 14 | "required": [ 15 | "material_name" 16 | ] 17 | } -------------------------------------------------------------------------------- /json-schema/datablock/link.mtlx_material.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: link.mtlx_material", 4 | "$id": "af.datablock.link.mtlx_material", 5 | "description": "Indicates that this component makes use of a material defined in a mtlx document represented by another component.", 6 | "type": "object", 7 | "properties": { 8 | "mtlx_component_id": { 9 | "type": "string" 10 | }, 11 | "mtlx_material": { 12 | "type": "string" 13 | } 14 | }, 15 | "required": [ 16 | "mtlx_component" 17 | ], 18 | "additionalProperties": false 19 | } -------------------------------------------------------------------------------- /json-schema/datablock/next_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: next_query", 4 | "$id": "af.datablock.next_query", 5 | "description": "This datablock describes how to fetch more results for the current query.", 6 | "$ref": "../template/fixed_query.json" 7 | } -------------------------------------------------------------------------------- /json-schema/datablock/preview_image_supplemental.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: preview_image_supplemental", 4 | "$id": "af.datablock.preview_image_supplemental", 5 | "description": "Contains a list of preview images associated to an asset.", 6 | "type": "array", 7 | "items": { 8 | "type": "object", 9 | "additionalProperties": false, 10 | "properties": { 11 | "alt": { 12 | "type": "string" 13 | }, 14 | "uri": { 15 | "type": "string", 16 | "format": "uri" 17 | } 18 | }, 19 | "required": [ 20 | "uri" 21 | ] 22 | } 23 | } -------------------------------------------------------------------------------- /json-schema/datablock/preview_image_thumbnail.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: preview_image_thumbnail", 4 | "$id": "af.datablock.preview_image_thumbnail", 5 | "description": "Contains information about a thumbnail image for an asset.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "alt": { 10 | "type": "string" 11 | }, 12 | "uris": { 13 | "properties": {}, 14 | "patternProperties": { 15 | "[0-9]+": { 16 | "type": "string", 17 | "format": "uri" 18 | } 19 | }, 20 | "additionalProperties": false, 21 | "minProperties": 1 22 | } 23 | }, 24 | "required": [ 25 | "uris" 26 | ] 27 | } -------------------------------------------------------------------------------- /json-schema/datablock/provider_configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: provider_configuration", 4 | "$id": "af.datablock.provider_configuration", 5 | "description": "Describes which headers the provider expects to receive from the client on every subsequent request.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "acquisition_uri": { 10 | "type": "string", 11 | "format": "uri" 12 | }, 13 | "acquisition_uri_title": { 14 | "type": "string" 15 | }, 16 | "headers": { 17 | "type": "array", 18 | "items": { 19 | "type": "object", 20 | "properties": { 21 | "name": { 22 | "type": "string" 23 | }, 24 | "default": { 25 | "type": "string" 26 | }, 27 | "is_required": { 28 | "type": "boolean" 29 | }, 30 | "is_sensitive": { 31 | "type": "boolean" 32 | }, 33 | "prefix": { 34 | "type": "string" 35 | }, 36 | "suffix": { 37 | "type": "string" 38 | }, 39 | "title": { 40 | "type": "string" 41 | }, 42 | "encoding": { 43 | "type": "string", 44 | "enum": [ 45 | "plain", 46 | "base64" 47 | ] 48 | } 49 | }, 50 | "additionalProperties": false, 51 | "required": [ 52 | "name", 53 | "is_sensitive", 54 | "is_required" 55 | ] 56 | } 57 | }, 58 | "connection_status_query": { 59 | "$ref": "../template/fixed_query.json" 60 | } 61 | }, 62 | "required": [ 63 | "headers", 64 | "connection_status_query" 65 | ] 66 | } -------------------------------------------------------------------------------- /json-schema/datablock/provider_reconfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: provider_reconfiguration", 4 | "$id": "af.datablock.provider_reconfiguration", 5 | "description": "This datablock can be sent by the provider to reconfigure which headers the client should send to the provider.", 6 | "type": "object", 7 | "properties": { 8 | "headers": { 9 | "type": "object", 10 | "additionalProperties": { 11 | "type": "string" 12 | } 13 | } 14 | }, 15 | "additionalProperties": false, 16 | "required": [ 17 | "headers" 18 | ] 19 | } -------------------------------------------------------------------------------- /json-schema/datablock/response_statistics.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: response_statistics", 4 | "$id": "af.datablock.response_statistics", 5 | "description": "Contains statistics about the current response.", 6 | "type": "object", 7 | "properties": { 8 | "result_count_total": { 9 | "type": "integer", 10 | "minimum": 0 11 | } 12 | }, 13 | "additionalProperties": false 14 | } -------------------------------------------------------------------------------- /json-schema/datablock/store.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: store", 4 | "$id": "af.datablock.store", 5 | "description": "Contains information about how/where to store a component file locally.", 6 | "type": "object", 7 | "properties": { 8 | "local_file_path": { 9 | "type": "string", 10 | "pattern": "^[^\\.|\\\/|\\\\]((?!\\.\\.\\\/|\\\\).)*[^\\.|\\\/|\\\\]$" 11 | }, 12 | "bytes": { 13 | "type": [ 14 | "integer", 15 | "null" 16 | ] 17 | } 18 | }, 19 | "required": [ 20 | "local_file_path", 21 | "bytes" 22 | ], 23 | "additionalProperties": false 24 | } -------------------------------------------------------------------------------- /json-schema/datablock/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: text", 4 | "$id": "af.datablock.text", 5 | "description": "Contains general text information to be displayed to the user.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "title": { 10 | "type": "string" 11 | }, 12 | "description": { 13 | "type": "string" 14 | } 15 | }, 16 | "required": [ 17 | "title" 18 | ] 19 | } -------------------------------------------------------------------------------- /json-schema/datablock/unlock_balance.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: unlock_balance", 4 | "$id": "af.datablock.unlock_balance", 5 | "description": "Information about the user's current account balance.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "balance_unit": { 10 | "type": "string" 11 | }, 12 | "balance": { 13 | "type": "number" 14 | }, 15 | "balance_refill_uri": { 16 | "type": "string", 17 | "format": "uri" 18 | } 19 | }, 20 | "required": [ 21 | "balance_unit", 22 | "balance" 23 | ] 24 | } -------------------------------------------------------------------------------- /json-schema/datablock/unlock_queries.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: unlock_queries", 4 | "$id": "af.datablock.unlock_queries", 5 | "description": "Contains the query or queries required to unlock all or some of the components in this implementation list.", 6 | "type": "array", 7 | "items": { 8 | "type": "object", 9 | "additionalProperties": false, 10 | "properties": { 11 | "id": { 12 | "type": "string" 13 | }, 14 | "unlocked": { 15 | "type": "boolean" 16 | }, 17 | "price": { 18 | "type": "number" 19 | }, 20 | "query": { 21 | "$ref": "../template/fixed_query.json" 22 | }, 23 | "child_query_ids": { 24 | "type": "array", 25 | "items": { 26 | "type": "string" 27 | } 28 | }, 29 | "query_fallback_uri": { 30 | "type": [ 31 | "string", 32 | "null" 33 | ], 34 | "format": "uri" 35 | } 36 | }, 37 | "required": [ 38 | "id", 39 | "unlocked" 40 | ], 41 | "if": { 42 | "properties": { 43 | "unlocked": { 44 | "const": false 45 | } 46 | } 47 | }, 48 | "then": { 49 | "required": [ 50 | "price", 51 | "query" 52 | ] 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /json-schema/datablock/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: user", 4 | "$id": "af.datablock.user", 5 | "description": "This datablock describes the current user, as seen by the provider.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "display_name": { 10 | "type": [ 11 | "string", 12 | "null" 13 | ] 14 | }, 15 | "display_tier": { 16 | "type": [ 17 | "string", 18 | "null" 19 | ] 20 | }, 21 | "display_icon_uri": { 22 | "type": [ 23 | "string", 24 | "null" 25 | ], 26 | "format": "uri" 27 | } 28 | }, 29 | "required": [] 30 | } -------------------------------------------------------------------------------- /json-schema/datablock/web_references.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Datablock: web_references", 4 | "$id": "af.datablock.web_references", 5 | "description": "References to external websites for documentation or support.", 6 | "type": "array", 7 | "additionalItems": false, 8 | "items": { 9 | "properties": { 10 | "title": { 11 | "type": [ 12 | "string", 13 | "null" 14 | ] 15 | }, 16 | "uri": { 17 | "type": "string", 18 | "format": "uri" 19 | }, 20 | "icon_uri": { 21 | "type": [ 22 | "string", 23 | "null" 24 | ], 25 | "format": "uri" 26 | } 27 | } 28 | }, 29 | "minItems": 1 30 | } -------------------------------------------------------------------------------- /json-schema/endpoint/asset_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Endpoint: asset_list", 4 | "$id": "af.endpoint.asset_list", 5 | "description": "This endpoint is used to query assets.", 6 | "type": "object", 7 | "properties": { 8 | "meta": { 9 | "$ref": "../template/meta.json", 10 | "properties": { 11 | "meta": { 12 | "properties": { 13 | "kind": { 14 | "const": "asset_list" 15 | } 16 | } 17 | } 18 | } 19 | }, 20 | "data": { 21 | "type": "object", 22 | "additionalProperties": false, 23 | "properties": { 24 | "next_query": { 25 | "$ref": "../datablock/next_query.json" 26 | }, 27 | "response_statistics": { 28 | "$ref": "../datablock/response_statistics.json" 29 | } 30 | } 31 | }, 32 | "assets": { 33 | "type": "array", 34 | "maxItems": 100, 35 | "items": { 36 | "type": "object", 37 | "additionalProperties": false, 38 | "required": [ 39 | "id", 40 | "data" 41 | ], 42 | "properties": { 43 | "id": { 44 | "type": "string", 45 | "pattern": "[a-zA-Z0-9\\._]+" 46 | }, 47 | "data": { 48 | "type": "object", 49 | "properties": { 50 | "implementation_list_query": { 51 | "$ref": "../datablock/implementation_list_query.json" 52 | }, 53 | "text": { 54 | "$ref": "../datablock/text.json" 55 | }, 56 | "dimensions": { 57 | "$ref": "../datablock/dimensions.json" 58 | }, 59 | "preview_image_supplemental": { 60 | "$ref": "../datablock/preview_image_supplemental.json" 61 | }, 62 | "preview_image_thumbnail": { 63 | "$ref": "../datablock/preview_image_thumbnail.json" 64 | }, 65 | "web_references": { 66 | "$ref": "../datablock/web_references.json" 67 | }, 68 | "license": { 69 | "$ref": "../datablock/license.json" 70 | }, 71 | "authors": { 72 | "$ref": "../datablock/authors.json" 73 | }, 74 | "keywords": { 75 | "$ref": "../datablock/keywords.json" 76 | } 77 | }, 78 | "required": [ 79 | "implementation_list_query" 80 | ] 81 | } 82 | } 83 | } 84 | } 85 | }, 86 | "required": [ 87 | "meta", 88 | "data", 89 | "assets" 90 | ] 91 | } -------------------------------------------------------------------------------- /json-schema/endpoint/auto.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Endpoint: (automatic)", 4 | "$id": "af.endpoint.auto", 5 | "description": "This schema validates against all possible AF endpoint types at once.", 6 | "$comment": "The 'oneOf' section ensures that the right schema is fulfilled.", 7 | "oneOf": [ 8 | { 9 | "$ref": "./asset_list.json" 10 | }, 11 | { 12 | "$ref": "./connection_status.json" 13 | }, 14 | { 15 | "$ref": "./implementation_list.json" 16 | }, 17 | { 18 | "$ref": "./initialization.json" 19 | }, 20 | { 21 | "$ref": "./unlock.json" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /json-schema/endpoint/connection_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Endpoint: connection_status", 4 | "$id": "af.endpoint.connection_status", 5 | "description": "This endpoint can be used to gather data about the current connection status.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "meta": { 10 | "$ref": "../template/meta.json", 11 | "properties": { 12 | "meta": { 13 | "properties": { 14 | "kind": { 15 | "const": "connection_status" 16 | } 17 | } 18 | } 19 | } 20 | }, 21 | "data": { 22 | "type": "object", 23 | "additionalProperties": false, 24 | "properties": { 25 | "unlock_balance": { 26 | "$ref": "../datablock/unlock_balance.json" 27 | }, 28 | "user": { 29 | "$ref": "../datablock/user.json" 30 | }, 31 | "provider_reconfiguration": { 32 | "$ref": "../datablock/provider_reconfiguration.json" 33 | } 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /json-schema/endpoint/implementation_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Endpoint: implementation_list", 4 | "$id": "af.endpoint.implementation_list", 5 | "description": "This endpoint gets the list of possible implementations for a specific asset.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "meta": { 10 | "$ref": "../template/meta.json", 11 | "properties": { 12 | "meta": { 13 | "properties": { 14 | "kind": { 15 | "const": "implementation_list" 16 | } 17 | } 18 | } 19 | } 20 | }, 21 | "data": { 22 | "type": "object", 23 | "additionalProperties": false, 24 | "properties": { 25 | "unlock_queries": { 26 | "$ref": "../datablock/unlock_queries.json" 27 | } 28 | } 29 | }, 30 | "implementations": { 31 | "type": "array", 32 | "items": { 33 | "type": "object", 34 | "additionalProperties": false, 35 | "properties": { 36 | "id": { 37 | "type": "string", 38 | "pattern": "[a-zA-Z0-9\\._]+" 39 | }, 40 | "data": { 41 | "type": "object", 42 | "text": { 43 | "$ref": "../datablock/text.json" 44 | } 45 | }, 46 | "components": { 47 | "type": "array", 48 | "items": { 49 | "type": "object", 50 | "properties": { 51 | "id": { 52 | "type": "string", 53 | "pattern": "[a-zA-Z0-9\\._]+" 54 | }, 55 | "data": { 56 | "type": "object", 57 | "additionalProperties": false, 58 | "properties": { 59 | "fetch.from_archive": { 60 | "$ref": "../datablock/fetch.from_archive.json" 61 | }, 62 | "fetch.download": { 63 | "$ref": "../datablock/fetch.download.json" 64 | }, 65 | "handle.loose_material_map": { 66 | "$ref": "../datablock/handle.loose_material_map.json" 67 | }, 68 | "handle.loose_environment_map": { 69 | "$ref": "../datablock/handle.loose_environment_map.json" 70 | }, 71 | "handle.native": { 72 | "$ref": "../datablock/handle.native.json" 73 | }, 74 | "handle.archive": { 75 | "$ref": "../datablock/handle.archive.json" 76 | }, 77 | "format": { 78 | "$ref": "../datablock/format.json" 79 | }, 80 | "format.obj": { 81 | "$ref": "../datablock/format.obj.json" 82 | }, 83 | "format.blend": { 84 | "$ref": "../datablock/format.blend.json" 85 | }, 86 | "link.loose_material": { 87 | "$ref": "../datablock/link.loose_material.json" 88 | }, 89 | "link.mtlx_material": { 90 | "$ref": "../datablock/link.mtlx_material.json" 91 | }, 92 | "text": { 93 | "$ref": "../datablock/text.json" 94 | }, 95 | "store": { 96 | "$ref": "../datablock/store.json" 97 | } 98 | }, 99 | "allOf": [ 100 | { 101 | "required": [ 102 | "store" 103 | ] 104 | }, 105 | { 106 | "oneOf": [ 107 | { 108 | "required": [ 109 | "fetch.from_archive" 110 | ] 111 | }, 112 | { 113 | "required": [ 114 | "fetch.download" 115 | ] 116 | } 117 | ] 118 | }, 119 | { 120 | "oneOf": [ 121 | { 122 | "required": [ 123 | "format" 124 | ] 125 | }, 126 | { 127 | "required": [ 128 | "format.obj" 129 | ] 130 | }, 131 | { 132 | "required": [ 133 | "format.blend" 134 | ] 135 | } 136 | ] 137 | }, 138 | { 139 | "if": { 140 | "anyOf": [ 141 | { 142 | "required": [ 143 | "link.loose_material" 144 | ] 145 | }, 146 | { 147 | "required": [ 148 | "link.mtlx_material" 149 | ] 150 | } 151 | ] 152 | }, 153 | "then": { 154 | "oneOf": [ 155 | { 156 | "required": [ 157 | "link.loose_material" 158 | ] 159 | }, 160 | { 161 | "required": [ 162 | "link.mtlx_material" 163 | ] 164 | } 165 | ] 166 | } 167 | }, 168 | { 169 | "if": { 170 | "anyOf": [ 171 | { 172 | "required": [ 173 | "handle.native" 174 | ] 175 | }, 176 | { 177 | "required": [ 178 | "handle.archive" 179 | ] 180 | }, 181 | { 182 | "required": [ 183 | "handle.loose_environment_map" 184 | ] 185 | }, 186 | { 187 | "required": [ 188 | "handle.loose_material_map" 189 | ] 190 | } 191 | ] 192 | }, 193 | "then": { 194 | "oneOf": [ 195 | { 196 | "required": [ 197 | "handle.native" 198 | ] 199 | }, 200 | { 201 | "required": [ 202 | "handle.archive" 203 | ] 204 | }, 205 | { 206 | "required": [ 207 | "handle.loose_environment_map" 208 | ] 209 | }, 210 | { 211 | "required": [ 212 | "handle.loose_material_map" 213 | ] 214 | } 215 | ] 216 | } 217 | } 218 | ] 219 | } 220 | } 221 | } 222 | } 223 | } 224 | } 225 | } 226 | }, 227 | "required": [ 228 | "meta", 229 | "data", 230 | "implementations" 231 | ] 232 | } -------------------------------------------------------------------------------- /json-schema/endpoint/initialization.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Endpoint: initialization", 4 | "$id": "af.endpoint.initialization", 5 | "description": "This is the initialization endpoint which the client first connects with.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "meta": { 10 | "$ref": "../template/meta.json", 11 | "properties": { 12 | "meta": { 13 | "properties": { 14 | "kind": { 15 | "const": "initialization" 16 | } 17 | } 18 | } 19 | } 20 | }, 21 | "id": { 22 | "type": "string", 23 | "pattern": "[a-z0-9-\\.]" 24 | }, 25 | "data": { 26 | "type": "object", 27 | "additionalProperties": false, 28 | "properties": { 29 | "text": { 30 | "$ref": "../datablock/text.json" 31 | }, 32 | "asset_list_query": { 33 | "$ref": "../datablock/asset_list_query.json" 34 | }, 35 | "provider_configuration": { 36 | "$ref": "../datablock/provider_configuration.json" 37 | }, 38 | "web_references": { 39 | "$ref": "../datablock/web_references.json" 40 | }, 41 | "branding": { 42 | "$ref": "../datablock/branding.json" 43 | }, 44 | "license": { 45 | "$ref": "../datablock/license.json" 46 | }, 47 | "authors": { 48 | "$ref": "../datablock/authors.json" 49 | } 50 | }, 51 | "required": [ 52 | "asset_list_query" 53 | ] 54 | } 55 | }, 56 | "required": [ 57 | "meta", 58 | "data", 59 | "id" 60 | ] 61 | } -------------------------------------------------------------------------------- /json-schema/endpoint/unlock.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Endpoint: unlock", 4 | "$id": "af.endpoint.unlock", 5 | "description": "This is an endpoint for unlocking components.", 6 | "type": "object", 7 | "additionalProperties":false, 8 | "properties": { 9 | "meta": { 10 | "$ref": "../template/meta.json", 11 | "properties": { 12 | "meta": { 13 | "properties": { 14 | "kind": { 15 | "const": "unlock" 16 | } 17 | } 18 | } 19 | } 20 | }, 21 | "data": { 22 | "type": "object", 23 | "additionalProperties": false, 24 | "properties": { 25 | "text": { 26 | "$ref": "../datablock/text.json" 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /json-schema/template/fixed_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "template: Fixed Query", 4 | "$id": "af.template.fixed_query", 5 | "description": "This template represents a fixed query.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "uri": { 10 | "type": "string", 11 | "format": "uri" 12 | }, 13 | "method": { 14 | "type": "string", 15 | "enum": [ 16 | "get", 17 | "post" 18 | ] 19 | }, 20 | "payload": { 21 | "type": [ 22 | "object", 23 | "null" 24 | ], 25 | "additionalProperties": { 26 | "type": "string" 27 | } 28 | } 29 | }, 30 | "required": [ 31 | "uri", 32 | "method", 33 | "payload" 34 | ] 35 | } -------------------------------------------------------------------------------- /json-schema/template/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Template: Meta", 4 | "$id": "af.template.meta", 5 | "description": "This template contains metadata for every endpoint.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "response_id": { 10 | "type": [ 11 | "string", 12 | "null" 13 | ] 14 | }, 15 | "version": { 16 | "type": "string", 17 | "pattern": "[0-9]+\\.[0-9]+" 18 | }, 19 | "kind": { 20 | "type": "string", 21 | "enum": [ 22 | "asset_list", 23 | "connection_status", 24 | "implementation_list", 25 | "initialization", 26 | "unlock" 27 | ] 28 | }, 29 | "message": { 30 | "type": [ 31 | "string", 32 | "null" 33 | ] 34 | } 35 | }, 36 | "required": [ 37 | "kind", 38 | "version" 39 | ] 40 | } -------------------------------------------------------------------------------- /json-schema/template/variable_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "title": "Template:Variable Query", 4 | "$id": "af.template.variable_query", 5 | "description": "This template describes a variable HTTP query that is configurable by the user.", 6 | "type": "object", 7 | "additionalProperties": false, 8 | "properties": { 9 | "uri": { 10 | "type": "string", 11 | "format": "uri" 12 | }, 13 | "method": { 14 | "type": "string", 15 | "enum": [ 16 | "get", 17 | "post" 18 | ] 19 | }, 20 | "parameters": { 21 | "type": "array", 22 | "items": { 23 | "type": "object", 24 | "additionalProperties": false, 25 | "properties": { 26 | "type": { 27 | "type": "string", 28 | "enum": [ 29 | "text", 30 | "boolean", 31 | "fixed", 32 | "select" 33 | ] 34 | }, 35 | "id": { 36 | "type": "string" 37 | }, 38 | "title": { 39 | "type": [ 40 | "string", 41 | "null" 42 | ] 43 | }, 44 | "default": { 45 | "type": [ 46 | "string", 47 | "null" 48 | ] 49 | }, 50 | "choices": { 51 | "type": [ 52 | "array", 53 | "null" 54 | ] 55 | } 56 | }, 57 | "$comment": "This if-clause ensures that the 'choices' array is required only if 'select' mode is used.", 58 | "if": { 59 | "properties": { 60 | "type": { 61 | "const": "select" 62 | } 63 | } 64 | }, 65 | "then": { 66 | "properties": { 67 | "choices": { 68 | "type": "array", 69 | "minItems": 1, 70 | "items": { 71 | "type": "object", 72 | "additionalProperties": false, 73 | "properties": { 74 | "value": { 75 | "type": "string" 76 | }, 77 | "title": { 78 | "type": "string" 79 | } 80 | }, 81 | "required": [ 82 | "value", 83 | "title" 84 | ] 85 | } 86 | } 87 | }, 88 | "required": [ 89 | "choices" 90 | ] 91 | }, 92 | "else": { 93 | "properties": { 94 | "choices": { 95 | "type": "null" 96 | } 97 | } 98 | }, 99 | "required": [ 100 | "type", 101 | "id" 102 | ] 103 | } 104 | } 105 | }, 106 | "required": [ 107 | "uri", 108 | "method", 109 | "parameters" 110 | ] 111 | } -------------------------------------------------------------------------------- /spec.md: -------------------------------------------------------------------------------- 1 | This document specifies **AssetFetch v0.4**, an HTTP- and JSON-based system for 3D asset discovery, retrieval and handling/import inside of Digital Content Creation (DCC) apps. 2 | The AssetFetch Protocol aims to provide a standardized way for artists to browse libraries of downloadable assets offered by providers *outside* their current production environment or pipeline, such as those of commercial or non-profit 3D asset vendors, marketplaces or other repositories of models, textures or other kinds of assets for digital media production. 3 | 4 | - [1. Introduction](#1-introduction) 5 | - [1.1. Motivation](#11-motivation) 6 | - [1.2. Vision](#12-vision) 7 | - [1.3. Goals](#13-goals) 8 | - [1.4. Non-Goals](#14-non-goals) 9 | - [2. Terminology](#2-terminology) 10 | - [2.1. Provider](#21-provider) 11 | - [2.2. Client](#22-client) 12 | - [2.3. Host Application](#23-host-application) 13 | - [2.4. User](#24-user) 14 | - [2.5. Asset](#25-asset) 15 | - [2.6. (Asset-)Implementation](#26-asset-implementation) 16 | - [2.6.1. Implementation Directory](#261-implementation-directory) 17 | - [2.7. (Implementation-)Component](#27-implementation-component) 18 | - [2.7.1. Component "Activeness"](#271-component-activeness) 19 | - [2.8. Datablock](#28-datablock) 20 | - [2.9. Component Unlocking](#29-component-unlocking) 21 | - [3. General Operation](#3-general-operation) 22 | - [3.1. Overview](#31-overview) 23 | - [3.2. Initialization](#32-initialization) 24 | - [3.3. Authentication \& Connection Status (optional)](#33-authentication--connection-status-optional) 25 | - [3.4. Browsing Assets](#34-browsing-assets) 26 | - [3.4.1. Asset Filtering](#341-asset-filtering) 27 | - [3.4.2. Asset Selection](#342-asset-selection) 28 | - [3.5. Implementation Negotiation](#35-implementation-negotiation) 29 | - [3.5.1. Implementation Filtering](#351-implementation-filtering) 30 | - [3.5.2. Implementation Selection](#352-implementation-selection) 31 | - [3.6. Component Unlocking (optional)](#36-component-unlocking-optional) 32 | - [3.7. Downloading](#37-downloading) 33 | - [3.8. Handling](#38-handling) 34 | - [3.9. Sequence Diagram](#39-sequence-diagram) 35 | - [3.9.1. Simple Version](#391-simple-version) 36 | - [3.9.2. Complete Version](#392-complete-version) 37 | - [4. HTTP Communication](#4-http-communication) 38 | - [4.1. Request Payloads](#41-request-payloads) 39 | - [4.2. Response Payloads](#42-response-payloads) 40 | - [4.3. User-Agent](#43-user-agent) 41 | - [4.4. Variable and Fixed Queries](#44-variable-and-fixed-queries) 42 | - [4.4.1. Variable Query](#441-variable-query) 43 | - [4.4.1.1. Variable Query Parameters](#4411-variable-query-parameters) 44 | - [4.4.2. Fixed Query](#442-fixed-query) 45 | - [4.5. HTTP Codes and Error Handling](#45-http-codes-and-error-handling) 46 | - [5. Endpoints](#5-endpoints) 47 | - [5.1. About Endpoints](#51-about-endpoints) 48 | - [5.2. Response Data Templates](#52-response-data-templates) 49 | - [5.2.1. The `meta` Template](#521-the-meta-template) 50 | - [5.2.2. The `datablock_collection` Template](#522-the-datablock_collection-template) 51 | - [5.2.2.1. Example](#5221-example) 52 | - [5.3. Endpoint: Initialization (`initialization`)](#53-endpoint-initialization-initialization) 53 | - [5.4. Endpoint: Asset List (`asset_list`)](#54-endpoint-asset-list-asset_list) 54 | - [5.4.1. `asset` Structure](#541-asset-structure) 55 | - [5.5. Endpoint: Implementation List (`implementation_list`)](#55-endpoint-implementation-list-implementation_list) 56 | - [5.5.1. `implementation` Structure](#551-implementation-structure) 57 | - [5.5.2. `component` Structure](#552-component-structure) 58 | - [5.6. Endpoint: Unlocking (`unlock`)](#56-endpoint-unlocking-unlock) 59 | - [5.7. Endpoint: Connection Status (`connection_status`)](#57-endpoint-connection-status-connection_status) 60 | - [6. Datablocks](#6-datablocks) 61 | - [6.1. Datablock Names](#61-datablock-names) 62 | - [6.2. Datablock Value Templates](#62-datablock-value-templates) 63 | - [6.2.1. `variable_query`](#621-variable_query) 64 | - [6.2.1.1. `parameter` Structure](#6211-parameter-structure) 65 | - [6.2.1.2. `choice` Structure](#6212-choice-structure) 66 | - [6.2.2. `fixed_query`](#622-fixed_query) 67 | - [7. Datablock Index](#7-datablock-index) 68 | - [7.1. Configuration and authentication-related datablocks](#71-configuration-and-authentication-related-datablocks) 69 | - [7.1.1. `provider_configuration`](#711-provider_configuration) 70 | - [7.1.1.1. `header` structure](#7111-header-structure) 71 | - [7.1.2. `provider_reconfiguration`](#712-provider_reconfiguration) 72 | - [7.1.3. `user`](#713-user) 73 | - [7.2. Browsing-related Datablocks](#72-browsing-related-datablocks) 74 | - [7.2.1. `asset_list_query`](#721-asset_list_query) 75 | - [7.2.2. `implementation_list_query`](#722-implementation_list_query) 76 | - [7.2.3. `next_query`](#723-next_query) 77 | - [7.2.4. `response_statistics`](#724-response_statistics) 78 | - [7.3. Display-related Datablocks](#73-display-related-datablocks) 79 | - [7.3.1. `text`](#731-text) 80 | - [7.3.2. `web_references`](#732-web_references) 81 | - [7.3.3. `branding`](#733-branding) 82 | - [7.3.4. `license`](#734-license) 83 | - [7.3.5. `authors`](#735-authors) 84 | - [7.3.6. `dimensions`](#736-dimensions) 85 | - [7.3.7. `preview_image_supplemental`](#737-preview_image_supplemental) 86 | - [7.3.8. `preview_image_thumbnail`](#738-preview_image_thumbnail) 87 | - [7.3.8.1. `uris` Structure](#7381-uris-structure) 88 | - [7.3.9. `keywords`](#739-keywords) 89 | - [7.4. Unlocking-related Datablocks](#74-unlocking-related-datablocks) 90 | - [7.4.1. `unlock_balance`](#741-unlock_balance) 91 | - [7.4.2. `unlock_queries`](#742-unlock_queries) 92 | - [7.4.2.1. `unlock_query` structure](#7421-unlock_query-structure) 93 | - [7.5. Format-related Datablocks](#75-format-related-datablocks) 94 | - [7.5.1. `format`](#751-format) 95 | - [7.5.1.1. `extension` rules](#7511-extension-rules) 96 | - [7.5.1.2. `mediatype` rules](#7512-mediatype-rules) 97 | - [7.5.2. `format.blend`](#752-formatblend) 98 | - [7.5.2.1. `target` Structure](#7521-target-structure) 99 | - [7.5.3. `format.obj`](#753-formatobj) 100 | - [7.6. Fetching- and Storage-related Datablocks](#76-fetching--and-storage-related-datablocks) 101 | - [7.6.1. `fetch.download`](#761-fetchdownload) 102 | - [7.6.2. `fetch.from_archive`](#762-fetchfrom_archive) 103 | - [7.6.3. `store`](#763-store) 104 | - [7.6.3.1. `local_file_path` rules](#7631-local_file_path-rules) 105 | - [7.7. Handling/Processing-related Datablocks](#77-handlingprocessing-related-datablocks) 106 | - [7.7.1. `handle.native`](#771-handlenative) 107 | - [7.7.2. `handle.archive`](#772-handlearchive) 108 | - [7.7.2.1. `local_directory_path` rules](#7721-local_directory_path-rules) 109 | - [7.7.3. `handle.loose_environment_map`](#773-handleloose_environment_map) 110 | - [7.7.4. `handle.loose_material_map`](#774-handleloose_material_map) 111 | - [7.8. Linking-related Datablocks](#78-linking-related-datablocks) 112 | - [7.8.1. `link.loose_material`](#781-linkloose_material) 113 | - [7.8.2. `link.mtlx_material`](#782-linkmtlx_material) 114 | - [8. Working With Asset Implementations](#8-working-with-asset-implementations) 115 | - [8.1. Overview](#81-overview) 116 | - [8.2. Implementation Analysis](#82-implementation-analysis) 117 | - [8.3. Performing Unlock Queries](#83-performing-unlock-queries) 118 | - [8.4. Choosing a Local Directory](#84-choosing-a-local-directory) 119 | - [8.5. Downloading and Storing Files](#85-downloading-and-storing-files) 120 | - [8.5.1. Storing All Component Files Based on `store` Datablock](#851-storing-all-component-files-based-on-store-datablock) 121 | - [8.5.2. Interacting With Archives (components with `handle.archive` datablock)](#852-interacting-with-archives-components-with-handlearchive-datablock) 122 | - [8.5.2.1. Handling for `extract_fully=true`](#8521-handling-for-extract_fullytrue) 123 | - [8.5.2.2. Handling for `extract_fully=false`](#8522-handling-for-extract_fullyfalse) 124 | - [8.5.2.3. Deleting Archives](#8523-deleting-archives) 125 | - [8.6. Handling Component Files](#86-handling-component-files) 126 | - [8.6.1. Handling For Components Without a `handle.*` Datablock](#861-handling-for-components-without-a--handle-datablock) 127 | - [8.6.2. Handling based on the native handling datablock (`handle.native`)](#862-handling-based-on-the-native-handling-datablock-handlenative) 128 | - [8.6.3. Handling a Loose Material Map (`handle.loose_material_map`)](#863-handling-a-loose-material-map-handleloose_material_map) 129 | - [8.6.4. Handling a Loose Environment Map (`handle.loose_environment_map`)](#864-handling-a-loose-environment-map-handleloose_environment_map) 130 | - [8.7. Handling Component Links](#87-handling-component-links) 131 | - [8.7.1. Handling MTLX Material Links (`link.mtlx_material`)](#871-handling-mtlx-material-links-linkmtlx_material) 132 | - [8.7.2. Handling loose Material Links (`link.loose_material`)](#872-handling-loose-material-links-linkloose_material) 133 | - [9. Security Considerations](#9-security-considerations) 134 | - [9.1. Storing Sensitive Headers](#91-storing-sensitive-headers) 135 | - [9.2. Avoiding Relative Paths in Local Path Fields](#92-avoiding-relative-paths-in-local-path-fields) 136 | - [9.3. Self-referential Archive Components](#93-self-referential-archive-components) 137 | 138 | 139 | # 1. Introduction 140 | 141 | ## 1.1. Motivation 142 | 143 | Acquiring pre-made assets for use in a project usually involves visiting the website of a vendor offering 3D models, material or other resources and downloading one or multiple files to local storage. 144 | These asset files are then manually imported into the desired DCC application, a process which often involves additional steps for unpacking, file organization and adjustments after the initial import such as manually setting up a material from texture maps that came with a model file. 145 | 146 | When trying to work with a large volume of third-party assets this workflow can become rather laborious, even more so when trying to experiment with multiple assets to see which works best in a scene. 147 | Recognizing this issue, multiple vendors have started creating bespoke solutions that allow artists to browse an individual vendor's asset library in a much more integrated fashion, for example through an additional window or panel integrated into the graphical user interface in a 3D suite. 148 | This vastly improves the user experience of browsing, downloading and importing assets and helps artists to focus on their core creative objective. 149 | 150 | However, these solutions are usually implemented using addons/plugins and are hard-coded to work with one 3D suite and one vendor, which creates a new set of issues: 151 | 152 | Vendors wanting to offer this improved user experience for their customers find themselves needing to build and maintain multiple pieces of software with limited opportunities for code reuse as every new plugin must be built within the language, framework and constraints presented by the host application. 153 | 154 | In light of this, many vendors choose to only offer a native integration for one or two applications or no native integrations at all. 155 | This may be because they don't have the resources and skills required or because development of such systems is not justifiable from a business perspective. 156 | 157 | Conversely, large vendors who can afford to develop and continuously maintain native integrations for many different DCC applications can benefit from a lock-in effect as only they can provide the convenience and speed that artists are accustomed to - limiting artist's choices. 158 | 159 | ## 1.2. Vision 160 | 161 | The AssetFetch system aims to create an artist experience similar to the existing native integrations with less development overhead in order to increase interoperability between vendors and DCC applications to allow more vendors - especially smaller ones - to offer their assets to artists right in the DCC applications where they need them. 162 | 163 | ## 1.3. Goals 164 | 165 | These are the goals of the AssetFetch specification, outlined in this document: 166 | 167 | - Describe a flexible, extensible way of discovering, filtering and previewing assets. 168 | - Facilitate the *one-time and one-directional transfer* of an asset with all its files from a provider to a client. 169 | - Allow providers to describe the structure of their assets (i.e. how the files they provide should work together) in a machine-readable way that allows for semi- or fully-automated handling of assets on the client-side with the smallest possible amount of manual adjustments. 170 |

171 | - Work without custom code that is specific for one vendor-application combination. 172 | - Make offering assets a low-threshold process for implementors on the provider side. 173 | - Allow implementors on the client side (for whom the implementation is likely harder) to easily get to an MVP-stage and gradually build out their implementations from there. 174 | 175 | ## 1.4. Non-Goals 176 | 177 | In order to maintain focus and make the implementation achievable with a reasonable amount of effort AssetFetch does not want to: 178 | 179 | - Act as a full asset management system for project- or studio-internal use, i.e. one that permanently tracks potentially evolving assets within an ongoing production. AssetFetch shares some ideas and data structures from [OpenAssetIO](https://openassetio.org/) but is not meant as a competitor or replacement for it, rather as a supplementary system. It might even be possible to run AssetFetch on top of OpenAssetIO in future versions. 180 | - Act as a new file format for describing complex 3D scenes in great detail. This is left to [OpenUSD](https://openusd.org) or [MaterialX](https://materialx.org/). Instead, the focus lies on describing the interactions and relationships between files with existing, well-known file formats. 181 | 182 | # 2. Terminology 183 | 184 | This section describes several key terms that will be used throughout this document. 185 | 186 | ## 2.1. Provider 187 | >The actor that offers assets by hosting an AssetFetch-compliant HTTP(S)-Endpoint. 188 | 189 | This provider can be a commercial platform that is offering 3D assets for sale or an open repository providing content for free. 190 | The provider hosts the AssetFetch API as an HTTP(s)-based service. 191 | 192 | 193 | ## 2.2. Client 194 | >A piece of software built to interact with the AssetFetch-API of a provider in order to receive resources from it. 195 | 196 | ## 2.3. Host Application 197 | >An application into which the client is integrated. 198 | 199 | A client can be a standalone application but in most implementation scenarios it will likely be integrated into another host application, like a 3D suite or other DCC application, in the form of a plugin/addon. 200 | The crucial difference to existing provider-specific plugins/addons is that only one implementation needs to be developed and maintained per application, instead of one per provider-application pairing. 201 | In reality there may of course still be multiple client plugins developed for the same application, but the choice for one of them should have less of an impact. 202 | 203 | ## 2.4. User 204 | >The human who uses an AssetFetch client. 205 | 206 | 207 | ## 2.5. Asset 208 | >A reusable *logical* media element in the context of a digital project. 209 | 210 | The emphasis is put on the term "logical" to indicate that one asset does not necessarily represent a single file. 211 | It might be composed of one or multiple meshes, textures, bones, particle systems, simulation data, etc. that are kept in multiple files. 212 | 213 | - A model of a chair with its mesh and textures is considered one asset. 214 | - An HDRI environment map is considered one asset. 215 | - A character model with textures and a rig is considered one asset. 216 | 217 | ## 2.6. (Asset-)Implementation 218 | > A concrete collection of components (files) that represents an asset in exactly one way for one specific use case, potentially even just for one specific application. 219 | 220 | When describing the transfer of assets from a provider to a client it is common for the provider to have the same asset available in many different variations. 221 | 222 | These variations may be: 223 | - Small semantic variations, like different colors or design alterations that do not change the nature of the asset so much that it becomes a new asset. 224 | - Quality variations, like multiple texture resolutions or LODs (Levels of Detail) for a mesh. 225 | - Purely technical variations, like offering the same asset with the same general level of technical fidelity in multiple file formats and file-arrangements for different applications. 226 | 227 | Some vendors allow their users to control these parameters with great precision so that they only need to download the asset in exactly the format and quality that is desired. 228 | This exact choice - or rather the collection of files with metadata that is a result of it - is considered an "**implementation** of an asset". 229 | 230 | - An OBJ file containing the LOD1 mesh of a chair along with a set of TIFF texture maps measuring 512x512 pixels each is considered one implementation of the chair asset. Using the LOD0 version instead would be considered a *new implementation* of the *same chair asset*. 231 | - An EXR image with a resolution of 8192x4096 pixels in an equirectangular projection is considered one implementation of an HDRI environment. 232 | - A BLEND file containing a character model, its rig and all its textures (again with a specific resolution) all packed into it is considered one implementation. 233 | - A UASSET file containing the same character set up for Unreal Engine instead of Blender is considered a different implementation of the same character asset. 234 | 235 | ### 2.6.1. Implementation Directory 236 | Every asset implementation has its own directory in which all component files for it are stored by the client after downloading them. 237 | This is referred to as the "implementation directory". 238 | A new one is created for every asset implementation that the client downloads. 239 | 240 | ## 2.7. (Implementation-)Component 241 | > A piece of digital information, generally a file, that is part of an asset implementation. 242 | 243 | - The 512px TIFF roughness map of the aforementioned chair implementation is one component. 244 | - The EXR file containing the panoramic environment is a component. It happens to be the only component in the implementation of that environment. 245 | - The BLEND file with the character model and textures packed into it is also considered one component since the BLEND file is a black box for any program except Blender. 246 | - When working with archives, the archive itself as well as its contents are considered components. 247 | A ZIP archive with the chair model as an FBX file and its textures as PNG files is represented as one component for the ZIP archive and then one component for each file in it (with some exceptions when using specific archive unpacking configurations). 248 | 249 | ### 2.7.1. Component "Activeness" 250 | Not all components of an implementation must be actively processed by the client in order to use them and are instead handled implicitly by the host application. 251 | 252 | - When trying to import an implementation consisting of an OBJ-file, an MTL-file and several material maps into a DCC application, then it is generally sufficient to invoke the application's native OBJ import functionality with the OBJ-file as the target. 253 | The references made inside OBJ-file will prompt the application to handle the MTL-file which then loads the supplemental texture maps without any further explicit invocation. 254 | - Some formats like [OpenUSD](https://openusd.org/) allow for more complex references between files. This way an entire scene can be represented by one "root" file which contains references to other files which in turn reference even more files. 255 | 256 | In both of the given examples, only one file would need to be "actively" handled by the user (or by a client trying to automate the user's work) with the remaining work getting delegated to the host application. 257 | 258 | When a client instructs its host to load a component and this component causes multiple other components to be loaded (for example a mesh file referencing two textures) then the first component would be called "active" (because from the client's perspective it needed active handling) whereas the components referenced by it are called "passive" (because the AssetFetch client did not need to handle them directly). 259 | 260 | ## 2.8. Datablock 261 | > A piece of metadata of a certain type and structure that can be attached to most other datastructures defined by AssetFetch. 262 | 263 | Datablocks are flexible and sometimes reusable pieces of metadata that enable the communication a broad range of metadata: 264 | 265 | - Attributes of providers, assets, implementations or other resources 266 | - Instructions for parsing or otherwise handling specific data 267 | - Relationships between resources 268 | 269 | ## 2.9. Component Unlocking 270 | > Performing a query from the client to the provider to request access to a specific component of an implementation. 271 | > The provider acknowledges the query and then grants access to the requested component(s), possibly along with a side-effect in the provider's back-end system, such as a charging the user for a purchase. 272 | 273 | The standard operating mode of an AssetFetch provider is to freely distribute the component files for any asset implementation that the client. 274 | 275 | However, the provider can configure access limitations for all or some components to require payment, impose usage quotas, or add other limitations to control distribution. 276 | To accommodate this, providers are able to define an additional endpoint to perform "unlocking" queries against. 277 | The pattern by which components are unlocked (for example on a per-asset, per-implementation or even per-component basis) can be controlled with a high degree of flexibility, accommodating many patterns commonly used by digital asset stores. 278 | 279 | **AssetFetch does not concern itself with any actual monetary transactions, users still need to perform any required account- and payment setup with the provider through external means, usually through the provider's website.** 280 | 281 | See [3.6](#36-component-unlocking-optional) and [7.4](#74-unlocking-related-datablocks) for details. 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | # 3. General Operation 292 | 293 | This section describes the steps by which AssetFetch operates in general terms. 294 | The following sections will then describe the exact implementation by defining the exact HTTP parameters and (JSON-) datastructures. 295 | 296 | ## 3.1. Overview 297 | 298 | AssetFetch models interactions between a provider and a client (controlled by a user). 299 | Generally, the following interactions are modeled: 300 | 301 | - **Initialization:** Client and provider exchange metadata to communicate basic properties and establish that an interaction can occur. 302 | - **Connection checking (optional):** If the provider requires authentication from the client, then an additional step is performed to confirm that the connection has been established and that the user has been logged in successfully. 303 | - **Browsing assets:** The client receives a list of available assets from the provider and displays it to the user. This list can be influenced using parameters to implement common filtering techniques, such as keyword- or category-based searching. The user chooses an asset they with to obtain. 304 | - **Implementation negotiation:** The client receives a list of available implementations for the chosen asset. This list can again be controlled using parameters to implement common per-asset choices like resolution or level-of-detail. The client then determines based on the metadata which of the implementations offered by the provider are viable for its environment/host application and chooses one implementation, possibly with manual support of the user. 305 | - **Component Unlocking (optional):** If the provider requires component unlocking, then the relevant information was already included in the implementation metadata. The client uses this data to inform the user about the upcoming charges and (if requested) performs the required unlocking queries to the provider. With the queries made, the provider will actually allow the download during the next step. If component unlocking is not required by the provider then this step can be skipped entirely. 306 | - **Downloading and arranging**: The client uses the download information it obtained together with the other implementation metadata to download the component files via HTTP and arrange them in a local file system directory or similar storage location. 307 | - **Local handling**: Finally, the client "handles" the files it downloaded along with aid from the implementation metadata. This "handling" is again controlled by implementation metadata can vary between clients developed for different host applications, but it usually involves importing the downloaded resources into the currently open project/scene or placing the data in a local repository, such as an application's proprietary asset management solution. 308 | 309 | ## 3.2. Initialization 310 | 311 | The first point of contact between a client and a provider is the initialization URI which the user enters into the client, comparable to the URI of an RSS feed. 312 | This URI is communicated from the provider to the user via external channels, such as the provider's website. 313 | 314 | The client establishes an initial connection to the provider by making an HTTP query to the initialization endpoint. 315 | This initialization endpoint MUST be accessible via HTTP GET without any prior communication between client and provider. 316 | It and communicates key information for further usage of the provider's AssetFetch interface, such as: 317 | 318 | - The provider's name and other general metadata. 319 | - Whether the provider requires the client to authenticate itself through additional HTTP headers. 320 | - The endpoint through which assets can be queried and its parameters. 321 | 322 | ## 3.3. Authentication & Connection Status (optional) 323 | As mentioned in the previous section, the provider MAY require custom authentication headers, in which case the client MUST send these headers along with every request it performs to that provider, unless the request is directed at the initialization endpoint. 324 | 325 | Which headers the client needs to send is communicated by the provider in the initialization data. 326 | 327 | The client obtains the required header values, such as passwords or randomly generated access tokens, from the user through a GUI, from a cache or through other mechanisms. 328 | See [9. Security considerations](#9-security-considerations) for more details about credential handling. 329 | 330 | If the provider uses authentication, then it MUST offer a connection status endpoint in the initialization data. 331 | Before attempting to perform any other actions using the credentials entered by the user, the client SHOULD contact the connection status endpoint at least once after initialization to verify the correctness of the values entered by the user. 332 | 333 | The connection status endpoint has two primary uses: 334 | 335 | - If available, the provider SHOULD respond with user-specific metadata, such as a username or account details which the client SHOULD display to the user to confirm that they are properly connected to the provider. 336 | - If the provider implements component unlocking using a prepaid balance system, then it SHOULD use this endpoint to communicate the user's remaining account balance. See [7.4](#74-unlocking-related-datablocks). 337 | 338 | ## 3.4. Browsing Assets 339 | After successful initialization (and possibly authentication) the client is ready to browse assets. 340 | 341 | ### 3.4.1. Asset Filtering 342 | The provider might send a static, unchanging list of available assets, but it MAY also require specific parameters for generating a dynamic asset list. 343 | In that case, the names and kinds of parameters were defined by the provider during the initialization step. 344 | Parameters can come in different formats, such as simple text strings or selections from a set of options, similar to what can be represented with a `
` tag in HTML. 345 | Possible examples for parameters for this query are: 346 | 347 | - A general keyword-based search field 348 | - A type- or category selection 349 | - Sorting options 350 | - Binary choices, such as limiting the selection to already purchased assets 351 | 352 | ### 3.4.2. Asset Selection 353 | After the user enters appropriate parameter values the client can request a list of available assets from the provider and display it to the user. 354 | What data is communicated is up to the provider, supported fields include: 355 | 356 | - Asset name and description, 357 | - Thumbnail image URI 358 | - Asset license 359 | - Author information 360 | - ... 361 | 362 | Every asset MUST also include information on how to query the provider for implementations of the asset. 363 | 364 | ## 3.5. Implementation Negotiation 365 | 366 | In order to actually download an asset, one of its implementations (assuming the provider offers multiple) needs to be chosen. 367 | This choice is ultimately the result of a "negotiation" process between provider, client and (depending on the client implementation) potentially also the user. 368 | 369 | ### 3.5.1. Implementation Filtering 370 | 371 | Similarly to how browsing for asset operates, the provider MAY require specific parameters for choosing an implementation. 372 | These parameters are included in the metadata for each asset during the previous step. 373 | Examples for parameters for this query are: 374 | 375 | - Texture resolution & format 376 | - possibly including variable resolutions and format choices for each map of a PBR material 377 | - Level-of-Detail selection 378 | - Small semantic choices, such as a selection of color variations 379 | 380 | After getting the parameters from the user (if necessary) the client requests the list of available implementations for this asset. 381 | 382 | ### 3.5.2. Implementation Selection 383 | 384 | The provider responds with a list of possible implementations available for the selected asset and implementation parameters chosen by the user. 385 | Every entry in this list represents one implementation that matches the user's parameter choices. 386 | 387 | At this point, the differences between the offered implementations SHOULD only be of technical nature, such as different encodings or file format representations of the same resource with roughly the same quality. 388 | The implementations each consist of a list of components, each of which have metadata attached to them, including information about file formats, relationships and downloads. 389 | The actual component files are not transmitted at this stage, only their metadata. 390 | 391 | The client analyzes the metadata of each component in every proposed implementation in order to test it for compatibility. 392 | 393 | During this check, the client SHOULD at least consider the following aspects: 394 | - File format compatibility: Are all files in the implementation using a file format that the client/host application is capable of parsing? 395 | - AssetFetch features: Does the proposed implementation only use AssetFetch features which the client/host application is able to support? 396 | 397 | If at least one implementation turns out to be compatible with the client and its host application, the process can proceed. 398 | If more than one implementation is valid for the given client and its host application, the client MAY ask the user to make the final choice or employ an internal decision mechanism to select an implementation. 399 | 400 | Overall, this process is comparable to the less commonly used [agent-driven content negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation#agent-driven_negotiation) in the HTTP standard. 401 | 402 | ## 3.6. Component Unlocking (optional) 403 | 404 | Component unlocking allows the provider to require the client to perform a special unlocking query before downloading component files. 405 | 406 | When operating *without* component unlocking, there is only one download-related piece of information that the provider MUST define: 407 | 408 | - The query to download the component file (for every component) 409 | 410 | When operating *with* component unlocking, the provider MUST instead include the following information: 411 | 412 | - A list containing one or multiple **unlocking queries** 413 | - A mapping between the components and the unlocking queries 414 | - The query to download the component file (for every component) 415 | 416 | The client SHOULD then present the required unlocking queries (along with any accompanying charges that the provider has declared) to the user. 417 | If the user confirms the action, the client MUST first perform the unlocking query (or queries) required to unlock all components it wants to download and only then performs the real download queries. 418 | 419 | 420 | ## 3.7. Downloading 421 | After choosing a suitable implementation and unlocking all of the components, the client downloads the files for every component of the implementation into a dedicated storage location. 422 | At this point the client can - either by itself or through calls to its host application - handle the files that it obtained. 423 | 424 | ## 3.8. Handling 425 | The AssetFetch data does not encode a fixed, imperative series of steps for handling an asset. 426 | Instead, it describes properties of and relationships between components which the client uses to generate an appropriate series of steps for handling the file inside its environment. 427 | This usually entails multiple steps, such as importing resources into the currently opened project or scene or importing resources into a central repository, like a software-specific local asset library. 428 | The processing is aided by the metadata in the datablocks of every component sent by the provider which describes relevant attributes, recommended vendor- or format-specific configurations or relationships between components. 429 | 430 | ## 3.9. Sequence Diagram 431 | The following diagrams illustrate the general flow of information between the user, the client software and the provider as well as the most important actions taken by each party. 432 | 433 | ### 3.9.1. Simple Version 434 | This diagram shows a simple implementation without any ability for dynamic filtering or dynamically generated implementations and without requiring any authentication or unlocking. 435 | All assets are freely available for everyone who can make an HTTP connection to the provider. 436 | 437 | ```mermaid 438 | sequenceDiagram 439 | 440 | box Local Workstation 441 | participant User 442 | participant Client 443 | end 444 | box Remote Server 445 | participant Provider 446 | end 447 | 448 | User->>Client: Requests connection to free.example.com/init 449 | Client->>Provider: Query: free.example.com/init 450 | Provider->>Client: Response: Initialization data 451 | 452 | Client->>Provider: Query: free.example.com/assets 453 | note right of Client: The asset query URI
was included in the
initialization data 454 | Provider->>Provider: Searches its database for assets
based on query parameters 455 | Provider->>Client: Response: List of assets 456 | Client->>User: Presents asset list 457 | User->>Client: Selects asset from list 458 | Client->>Provider: Query: free.example.com/implementations?asset= 459 | note right of Client: The implementations query URI and parameters
were included in the asset data 460 | Provider->>Provider: Loads implementations
for this asset from its database 461 | Provider->>Client: Returns list of possible implementations 462 | Client->>Client: Validates proposed implementations and
selects those that it can handle
(based on metadata
about file formats and relationships) 463 | Client->>User: Presents implementation(s)
and asks for confirmation 464 | User->>Client: Confirms asset import 465 | loop For every component in implementation 466 | Client->>Provider: Initiates HTTP download of component file 467 | Provider->>Client: Transmits file 468 | end 469 | Client->>Client: Processes files locally based
on implementation metadata
(usually by importing them
into the current project) 470 | Client->>User: Shows confirmation message 471 | note left of User: User can now utilize
the asset in their project. 472 | ``` 473 | 474 | ### 3.9.2. Complete Version 475 | 476 | This diagram shows a more complete interaction, including authentication and component unlocking. 477 | It also illustrates how the provider can utilize ephemeral download links hosted on a different platform, like a CDN ("Content Delivery Network") service. 478 | 479 | ```mermaid 480 | sequenceDiagram 481 | 482 | box Local Workstation 483 | participant User 484 | participant Client 485 | end 486 | box Remote Server(s) 487 | participant Provider 488 | participant CDN-Service 489 | end 490 | 491 | User->>Client: Requests connection to paid.example.com/init 492 | note left of User: The provider URI might be
bookmarked in the client,
if it supports that. 493 | Client->>Provider: Query: paid.example.com/init 494 | Provider->>Client: Response: Initialization data, containing requirement for authentication 495 | Client->>User: Presents required headers
as a GUI/form 496 | note left of User: Some data
might be cached by the
client and does not need
to be re-entered. 497 | User->>User: Fills out required header values 498 | User->>Client: Confirms inputs of provider data. 499 | 500 | Client->>Provider: Query: paid.example.com/status 501 | Provider->>Client: Responds with status data
(Login confirmation, username, account balance, ...) 502 | 503 | User->>User: Fills out asset query
as defined by provider
(tags, categories, ...) 504 | User->>Client: Confirms choices and requests asset list 505 | Client->>Provider: Query: paid.example.com/assets?q= 506 | note right of Client: The asset query URI
was included in the
initialization data 507 | Provider->>Provider: Searches its database for assets
based on query parameters 508 | Provider->>Client: Response: List of assets 509 | Client->>User: Presents asset list 510 | User->>Client: Selects asset from list 511 | 512 | Client->>User: Presents available parameters
for querying implementations
as GUI/form 513 | User->>User: Fills out implementations query
(texture resolution,LOD,...) 514 | User->>Client: Confirms choices and
requests implementations 515 | Client->>Provider: Query: paid.example.com/implementations?asset=&resolution= 516 | note right of Client: The implementations query URI
was included in the asset data 517 | Provider->>Provider: Loads implementations
for this asset from its database,
based on query 518 | Provider->>Client: Returns list of possible implementations
*without download information* 519 | Client->>Client: Validates proposed implementations and
selects those that it can handle
(based on metadata
about file formats and relationships) 520 | Client->>User: Presents implementation(s)
and asks for confirmation 521 | User->>User: Reviews suggested implementation(s)
(*price*,files, download size, etc.) 522 | User->>Client: Confirms asset import 523 | 524 | loop Possibly multiple times, depending on granularity of
provider's unlocking model 525 | Client->>Provider: Query: paid.example.com/unlock?asset=&component= 526 | Provider->>Client: Confirms the unlocking action. 527 | end 528 | 529 | loop For every component 530 | Client->>Provider: Query: paid.example.com/downloads?asset=&component= 531 | Provider->> CDN-Service: Query: storage-api.example.com/generate-temp-dl-link?file= 532 | CDN-Service->>Provider: Responds with
temporary download link 533 | Provider->>Client: Responds with HTTP redirect to (temporary) download link 534 | Client->>CDN-Service: Follows HTTP redirect and initiates HTTP download of component file 535 | CDN-Service->>Client: Responds with file 536 | end 537 | 538 | Client->>Client: Processes files locally based
on implementation metadata
(usually by importing them
into the current project) 539 | Client->>User: Confirms that import was completed 540 | note left of User: User can now utilize
the asset in their project. 541 | User->>User: Fills out asset query
again for next asset, if desired 542 | ``` 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | # 4. HTTP Communication 554 | 555 | This section describes general instructions for all HTTP communication described in this specification. 556 | The term "HTTP communication" also always includes communication via HTTPS instead of plain HTTP. 557 | 558 | ## 4.1. Request Payloads 559 | 560 | The payload of all HTTP requests from a client to a provider MUST be encoded as [`application/x-www-form-urlencoded`](https://url.spec.whatwg.org/#application/x-www-form-urlencoded), the same format that is used by standard HTML forms. 561 | 562 | Examples for a valid query payload are shown below. 563 | ``` 564 | q=wood,old&min_resolution=512 565 | lod=0 566 | query=&category=marble 567 | ``` 568 | 569 | This encoding for request data is already extremely widespread and can therefore usually be handled using standard libraries, both on the provider- and on the client-side. 570 | 571 | ## 4.2. Response Payloads 572 | 573 | The payload of all HTTP responses from a provider MUST be valid [JSON](https://www.json.org/) and SHOULD use the `Content-Type` header `application/json`. 574 | The exact structure of the data for individual endpoints and other API resources is specified in the [Endpoint section](#5-endpoints). 575 | 576 | ## 4.3. User-Agent 577 | 578 | The client SHOULD send an appropriate user-agent header as defined in [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#field.user-agent). 579 | 580 | If the client is embedded in a host application, for example as an addon inside a 3D suite, it SHOULD set its first `product` and `product-version` identifier based on the host application and then describe the product and version of the client plugin itself afterwards. 581 | 582 | ``` 583 | # Examples for plugins/addons: 584 | cinema4d/2024.2 MyAssetFetchPlugin/1.2.3 585 | 3dsmax/2023u1 AssetFetchFor3dsMax/0.5.7 586 | blender/4.0.3 BlenderAssetFetch/v17 587 | 588 | # Example for a standalone client: 589 | standaloneAssetFetchClient/1.4.2.7 590 | ``` 591 | 592 | ## 4.4. Variable and Fixed Queries 593 | 594 | In AssetFetch, there are several instances where the provider needs to describe a possible HTTP request that a client can make to perform a certain action or receive new data, such as browsing for assets, unlocking components or downloading files. 595 | In this context, the specification differentiates between "variable" and "fixed" queries. 596 | 597 | ### 4.4.1. Variable Query 598 | 599 | A **variable query** is an HTTP request defined by its URI, method and a payload _that has been (partly) configured by the user_ which is sent by the client to the provider in order to receive data in response. 600 | For this purpose, the provider sends the client a list of parameter values that the client MUST use to construct the actual HTTP query to the provider. 601 | For the client, handling a variable query usually involves drawing a GUI and asking the user to provide the values to be sent to the provider. 602 | 603 | A simple example for a variable query is a query for listing assets that allows the user to specify a list of keywords before the request is sent to the provider. 604 | 605 | #### 4.4.1.1. Variable Query Parameters 606 | 607 | The full field list of a variable query object can be found in the [`variable_query` datablock template](#621-variable_query). 608 | 609 | A variable query is composed of its URI, HTTP method and optionally one or multiple parameter definitions that are used to determine the payload of the HTTP request. 610 | 611 | Every parameter has a `title` property which the client SHOULD use to communicate the functionality of the given parameter to the user. 612 | The `id` property on the parameter dictates the actual key value that the client MUST use when composing the HTTP request. 613 | 614 | The nature of the final value of the parameter is dictated by its `type`. 615 | If the provider offers one or multiple adjustable parameters, it MUST choose one of the following parameter types for each parameter: 616 | 617 | - `text`: A string of text with no line breaks (`\r` and/or `\n`). When utilizing a GUI the client SHOULD use a one-line text input field to represent this parameter. The client MUST allow the use of an empty string. 618 | - `boolean`: A binary choice with `true` being represented by the value `1` and `false` with the value `0`. The client MUST NOT send any other response value for this parameter. When utilizing a GUI the client SHOULD use a tick-box or similar kind of menu item to represent this parameter. 619 | - `select`: A list of possible choices, each represented by a `value` which is the actual parameter value that the client MUST include in its HTTP request if the user chooses the choice in question and a `title` which the client SHOULD use to represent the choice to the user. When utilizing a GUI the client SHOULD use a drop-down or similar kind of menu to represent this parameter. 620 | - `fixed`: A fixed value that the client MUST include in its request verbatim. The client MAY reveal this value to the user, but MUST NOT allow any changes to this value. 621 | 622 | ### 4.4.2. Fixed Query 623 | 624 | The full field list of a fixed query object can be found in the [`fixed_query` datablock template](#622-fixed_query). 625 | 626 | A **fixed query** is an HTTP(S) request defined by its URI, method and a payload _that is not configurable by the user_ which is sent by the client to the provider in order to receive data in response. 627 | 628 | In this case the provider only transmits the description of the query to the client whose only decision is whether or not to actually send the query with the given parameters to the provider. 629 | 630 | A typical example for a fixed query is a download option for a file where the client only has the choice to invoke or not invoke the download. 631 | 632 | ## 4.5. HTTP Codes and Error Handling 633 | 634 | Every response sent from an AssetFetch provider MUST use HTTP Status codes appropriately. 635 | 636 | In concrete terms, this means: 637 | 638 | | Condition | Response Code | 639 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | 640 | | Query processed successfully (even with zero results in the context of a search). | `200 - OK` | 641 | | Provider cannot parse the query data. | `400 - Bad Request` | 642 | | Query sent without required headers. | `401 - Unauthorized` | 643 | | Query is rejected due to monetary reasons (e.g., lack of subscription or insufficient balance) . | `402 - Payment Required` | 644 | | Query includes all required headers but the values are not valid or do not allow the desired action. | `403 - Forbidden` | 645 | | Provider receives a query that references a specific resource which does not exist, such as a query for implementations of an asset that the provider does not recognize. | `404 - Not Found` | 646 | 647 | 648 | If a client receives a response code that indicates an error on any query (`4XX`/`5XX`) it SHOULD pause its operation and display a message regarding this incident to the user. 649 | This message SHOULD contain the contents of the `message` and `response_id` field in the response's metadata (See [5.2.1](#521-the-meta-template)), if they have content. 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | # 5. Endpoints 661 | 662 | This section outlines general information about the HTTP-endpoints required for AssetFetch along with the specific structural requirements for the JSON-response on every endpoint. 663 | 664 | ## 5.1. About Endpoints 665 | 666 | The interaction model described in the [General Operation](#3-general-operation) section principally implies that there are three kinds of HTTP(s)-based endpoints that a provider MUST the following kinds of endpoints: 667 | 668 | - An initialization endpoint (`initialization`). 669 | - An endpoint for querying assets (`asset_list`). 670 | - An endpoint for querying implementations of one specific asset (`implementation_list`). 671 | 672 | Depending on which features it wants to use, the provider MAY implement: 673 | - An endpoint for performing a connection status check (`connection_status`) if the provider wants to use user authentication. 674 | - An endpoint for unlocking resources (`unlock`) if the provider wants to use component unlocking. 675 | 676 | *The specific URIs or sub-paths for these endpoint are not prescribed by AssetFetch.* 677 | The URI and parameters for every endpoint besides the initialization endpoint are communicated by the provider to the client in the response data to a previous request. 678 | 679 | ## 5.2. Response Data Templates 680 | 681 | This section describes data structures that are used in responses from several or even all endpoints. 682 | These templates are later referenced during the description of the individual endpoints. 683 | 684 | ### 5.2.1. The `meta` Template 685 | All provider responses on all endpoints MUST carry the `meta` field to communicate key information about the current response. 686 | 687 | All instances of this template MUST have the following structure: 688 | 689 | | Field | Format | Requirement | Description | 690 | | ------------- | ------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | 691 | | `response_id` | string | MAY | An ID for this specific response from the provider. | 692 | | `version` | string | MUST | The version of AssetFetch that this response is intended for. | 693 | | `kind` | string | MUST | The kind of data that is being transmitted with this response. The exact value of this field is specified individually for each endpoint. | 694 | | `message` | string | SHOULD, if an error occurs. | An arbitrary message to attach to this response. | 695 | 696 | The `version` field MUST contain the AssetFetch version that this response is modeled after. 697 | The string MUST have the format `.`, for example `0.4`. 698 | 699 | The `response_id` field is designed to aid with logging and troubleshooting, should the need arise. 700 | The provider MAY set this field, in which case they SHOULD keep a log of the responses and their ids, especially in the case of an error. 701 | 702 | If a request fails, the provider SHOULD use the `message` field to communicate more details for troubleshooting. 703 | 704 | Clients SHOULD display the `response_id` and `message` fields to the user if a query was unsuccessful, as indicated by the HTTP status code. 705 | 706 | ### 5.2.2. The `datablock_collection` Template 707 | 708 | Nearly every piece of information in AssetFetch is communicated through a datablock, which has a name and a clearly defined structure. 709 | The datablock collection is a JSON object that uses the datablock's name as a key and its structure as the value. 710 | 711 | All instances of this template MUST have the following structure: 712 | 713 | | Field | Format | Description | 714 | | ------------------------------------ | --------------- | -------------------------------------------------------------------------- | 715 | | \ | object or array | Exact structure is defined in the [Datablocks section](#7-datablock-index) | 716 | | \ | object or array | Exact structure is defined in the [Datablocks section](#7-datablock-index) | 717 | | ... (arbitrary number of datablocks) | 718 | 719 | Every key of this data object is the identifier for the datablock stored in that key's field. 720 | 721 | #### 5.2.2.1. Example 722 | 723 | The example below illustrates a datablock collection called `data` whose structure follows the `datablock_collection` template with two datablocks (`block_type_1` and `block_type_2`) which have a varying structure. 724 | 725 | ``` 726 | { 727 | "data":{ 728 | "block_type_1":{ 729 | "example_key": "example_value" 730 | }, 731 | "block_type_2.a":{ 732 | "example_array": [1,2,4], 733 | "example_object": { 734 | "a": 7 735 | } 736 | } 737 | } 738 | } 739 | ``` 740 | 741 | ## 5.3. Endpoint: Initialization (`initialization`) 742 | 743 | Its URI is initially entered by the user into a client and this endpoint is the first point of contact between a client and a provider. 744 | It is used to communicate key details about the provider as well as how the interaction between client and provider should proceed. 745 | 746 | The response on this endpoint MUST have the following structure: 747 | 748 | | Field | Format | Requirement | Description | 749 | | ------ | ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 750 | | `meta` | `meta` | MUST | Metadata, kind: `initialization`. | 751 | | `id` | string | MUST | An id that identifies this provider. It MUST match the regular expression `[a-z0-9-\.]`. Providers SHOULD use a domain name (e.g. `example.com` or `sub.example.com`) as the ID, if applicable in their use-case. | 752 | | `data` | `datablock_collection` | MUST | | 753 | 754 | The following datablocks are to be included in the `data` field: 755 | 756 | | Requirement | Datablocks | 757 | | ------------------------------- | -------------------------------------------------- | 758 | | MUST | `asset_list_query` | 759 | | SHOULD | `text` | 760 | | MAY | `branding`, `authors`, `license`, `web_references` | 761 | | MUST, if authentication is used | `provider_configuration` | 762 | 763 | ## 5.4. Endpoint: Asset List (`asset_list`) 764 | 765 | The URI and available HTTP parameters for this endpoint are communicated by the server to the client using the `asset_list_query` datablock on the initialization endpoint. 766 | 767 | The response on this endpoint MUST have the following structure: 768 | 769 | | Field | Format | Requirement | Description | 770 | | -------- | ---------------------- | ----------- | ----------------------------- | 771 | | `meta` | `meta` | MUST | Metadata, kind: `asset_list`. | 772 | | `data` | `datablock_collection` | MUST | | 773 | | `assets` | Array of `asset` | MUST | | 774 | 775 | The following datablocks are to be included in the `data` field: 776 | 777 | | Requirement | Datablocks | 778 | | ----------- | ----------------------------------- | 779 | | MAY | `next_query`, `response_statistics` | 780 | 781 | The `assets` field MUST NOT contain more than 100 asset objects for one response. 782 | If the provider finds more assets than 100 assets which match the query it SHOULD use the `next_query` datablock to define a fixed query that the client can use to fetch more results. 783 | 784 | ### 5.4.1. `asset` Structure 785 | 786 | Every `asset` object MUST have the following structure: 787 | 788 | | Field | Format | Requirement | Description | 789 | | ------ | ---------------------- | ----------- | --------------------------------------------------------------------------- | 790 | | `id` | string | MUST | Unique id for this asset. Must match the regular expression `[a-z0-9_-\.]+` | 791 | | `data` | `datablock_collection` | MUST | | 792 | 793 | The `id` field MUST be unique among all assets for this provider. 794 | Clients MAY use this id when storing and organizing files on disk. 795 | Clients MAY use the id as a display title, but SHOULD prefer the `title` field in the asset's `text` datablock, if available. 796 | 797 | The following datablocks are to be included in the `data` field: 798 | 799 | | Requirement | Datablocks | 800 | | ----------- | -------------------------------------------------------------------------------------------- | 801 | | MUST | `implementation_list_query` | 802 | | SHOULD | `preview_image_thumbnail`, `text` | 803 | | MAY | `preview_image_supplemental`, `license`, `authors`, `dimensions`,`web_references`,`keywords` | 804 | 805 | 806 | ## 5.5. Endpoint: Implementation List (`implementation_list`) 807 | 808 | This endpoint returns one or several implementations for one specific asset. 809 | The URI and available parameters for this endpoint are communicated by the provider to the client using the `implementation_list_query` datablock on the corresponding asset in the asset list endpoint. 810 | 811 | | Field | Format | Requirement | Description | 812 | | ----------------- | ------------------------- | ----------- | -------------------------------------------------------- | 813 | | `meta` | `meta` | MUST | Metadata, kind: `implementation_list`. | 814 | | `data` | `datablock_collection` | MUST | Datablocks that apply to the entire implementation list. | 815 | | `implementations` | Array of `implementation` | MUST | | 816 | 817 | The following datablocks are to be included in the `data` field: 818 | 819 | | Requirement Level | Datablocks | 820 | | ------------------------------------------ | ---------------- | 821 | | MUST, if component unlocking is being used | `unlock_queries` | 822 | 823 | 824 | ### 5.5.1. `implementation` Structure 825 | 826 | Every `implementation` object MUST have the following structure: 827 | 828 | | Field | Format | Requirement | Description | 829 | | ------------ | ---------------------- | ----------- | --------------------------------------------------------------------------------------- | 830 | | `id` | string | MUST | A unique id for this implementation. Must match the regular expression `[a-z0-9_-\.]+`. | 831 | | `data` | `datablock_collection` | MUST | Datablocks that apply to this specific implementation. | 832 | | `components` | Array of `component` | MUST | | 833 | 834 | The `id` field MUST be unique among all possible implementations the provider can offer for this asset, *even if not all of them are included in the returned implementation list*. 835 | The id may be reused for an implementation of a *different* asset. 836 | Clients MAY use this id when storing and organizing files on disk. 837 | Clients MAY use the id as a display title, but SHOULD prefer the `title` field in the asset's `text` datablock, if available. 838 | 839 | The following datablocks are to be included in the `data` field of the response: 840 | 841 | | Requirement Level | Datablocks | 842 | | ----------------- | ---------- | 843 | | SHOULD | `text` | 844 | 845 | ### 5.5.2. `component` Structure 846 | 847 | Every `component` object MUST have the following structure: 848 | 849 | | Field | Format | Requirement | Description | 850 | | ------ | ---------- | ----------- | ---------------------------------------------------------------------------------- | 851 | | `id` | string | MUST | A unique id for this component. Must match the regular expression `[a-z0-9_-\.]+`. | 852 | | `data` | datablocks | MUST | Datablocks for this specific component. | 853 | 854 | The `id` field MUST be unique among all components inside one implementation, but MAY be reused for a component in a different implementation. 855 | Clients MAY use this id when storing and organizing files on disk. 856 | Clients MAY use this field as a display title, but SHOULD prefer the `title` field in the asset's `text` datablock, if available. 857 | 858 | The following datablocks are to be included in the `data` field of every component: 859 | 860 | | Requirement Level | Datablocks | 861 | | ---------------------------------------------------------- | --------------------------------------- | 862 | | MUST (on every component) | `store`, `fetch.*`, `format`/`format.*` | 863 | | SHOULD (on at least one component in every implementation) | `handle.*` | 864 | | MAY | `link.*`,`text` | 865 | 866 | 867 | 868 | 869 | ## 5.6. Endpoint: Unlocking (`unlock`) 870 | 871 | | Field | Format | Requirement | Description | 872 | | ------ | ------ | ----------- | ------------------------- | 873 | | `meta` | `meta` | MUST | Metadata, kind: `unlock`. | 874 | 875 | This endpoint is invoked to perform an "unlocking" action (usually meaning a "purchase") of a resource. 876 | After calling it the client can expect to be able to perform all downloads associated to this unlocking query without being rejected by the provider. 877 | 878 | The URI and parameters for this endpoint are communicated through the `unlock_queries` datablock. 879 | 880 | The HTTP status code and potentially the data in the `meta` field are used to evaluate the success of the request. 881 | The provider MAY use a `text` datablock to communicate further details about the completed locking process. 882 | 883 | | Requirement Level | Datablocks | 884 | | ----------------- | ---------- | 885 | | MAY | `text` | 886 | 887 | ## 5.7. Endpoint: Connection Status (`connection_status`) 888 | 889 | | Field | Format | Requirement | Description | 890 | | ------ | ---------------------- | ----------- | ----------------------------------- | 891 | | `meta` | `meta` | MUST | Metadata, kind: `connection_status` | 892 | | `data` | `datablock_collection` | MUST | Datablocks. | 893 | 894 | The URI and parameters for the balance endpoint are communicated by the provider to the client through the `provider_configuration` datablock. 895 | 896 | The following datablocks are to be included in the `data` field: 897 | 898 | | Requirement Level | Datablocks | 899 | | --------------------------------------------- | ---------------------------------- | 900 | | SHOULD, if the provider uses a prepaid system | `unlock_balance` | 901 | | MAY | `user`, `provider_reconfiguration` | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | # 6. Datablocks 913 | 914 | ## 6.1. Datablock Names 915 | 916 | Every datablock outlined in this specification has a name that identifies its structure. 917 | The name is a string composed of lowercase alphanumerical characters, underscores and dots. 918 | 919 | Datablock names contain either 0 or 1 instances of the dot (`.`) character which is used to indicate that a datablock has multiple variations. 920 | The part before the dot separator is considered the "base name" of the datablock and the part after it (if it exists) the "variation name". 921 | 922 | A resource MUST NOT carry two datablocks that share the same base name. 923 | 924 | The resulting regular expression for all datablock names is `^[a-z0-9_]+(\.[a-z0-9_]+)?$`. 925 | 926 | ## 6.2. Datablock Value Templates 927 | This section describes additional data types that can be used within other datablocks. 928 | They exist to eliminate the need to re-specify the same data structure in two different datablock definitions. 929 | 930 | ### 6.2.1. `variable_query` 931 | This template describes an HTTP query whose parameters are controllable by the user. 932 | See [Variable and Fixed Queries](#44-variable-and-fixed-queries) for more details. 933 | 934 | | Field | Format | Requirement | Description | 935 | | ------------ | -------------------- | ----------- | ------------------------------------------- | 936 | | `uri` | string | MUST | The URI to send the request to. | 937 | | `method` | string | MUST | One of `get`, `post` | 938 | | `parameters` | array of `parameter` | MUST | The configurable parameters for this query. | 939 | 940 | #### 6.2.1.1. `parameter` Structure 941 | A parameter describes the attributes of one parameter for the query and how the client can present this to its user. 942 | 943 | | Field | Format | Requirement | Description | 944 | | --------- | ----------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 945 | | `type` | string | MUST | One of `text`, `boolean`, `select`, `fixed` | 946 | | `id` | string | MUST | The id of the HTTP parameter. It MUST be unique among the parameters of one variable query. The client MUST use this value as a the key when sending a response using this parameter. | 947 | | `title` | string | SHOULD | The title that the client SHOULD display to the user to represent this parameter. | 948 | | `default` | string | MAY | The default value for this parameter. It MUST be one of the `value` fields outlined in `choices` if type `select` is bing used. It becomes the only possible value for this parameter if type `fixed` is being used. | 949 | | `choices` | array of `choice` | MUST, if `select` type is used | This field contains all possible choices when the `select` type is used. In that case it MUST contain at least one `choice` object, as outlined below. | 950 | 951 | #### 6.2.1.2. `choice` Structure 952 | A single choice for a `select` type parameter. 953 | 954 | | Field | Format | Requirement | Description | 955 | | ------- | ------ | ----------- | --------------------------------------------------------------------------------------------- | 956 | | `value` | string | MUST | The value that the client MUST use in its HTTP response if the user has selected this choice. | 957 | | `title` | string | MUST | The title that the client SHOULD display to the user to represent this choice. | 958 | 959 | ### 6.2.2. `fixed_query` 960 | This template describes a fixed query that can be sent by the client to the provider without additional user input or configuration. 961 | See [Variable and Fixed Queries](#44-variable-and-fixed-queries) for more details. 962 | 963 | | Field | Format | Requirement | Description | 964 | | --------- | ----------------------------- | ----------- | --------------------------------------------------- | 965 | | `uri` | string | MUST | The URI to contact for getting more results. | 966 | | `method` | string | MUST | MUST be one of `get` or `post` | 967 | | `payload` | object with string properties | MAY | The keys and values for the payload of the request. | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | # 7. Datablock Index 979 | 980 | This section displays all datablocks that are used in AssetFetch. 981 | 982 | ## 7.1. Configuration and authentication-related datablocks 983 | 984 | ### 7.1.1. `provider_configuration` 985 | Describes which headers the provider expects to receive from the client on every subsequent request. 986 | 987 | This datablock has the following structure: 988 | 989 | | Field | Format | Requirement | Description | 990 | | ------------------------------ | ----------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- | 991 | | `headers` | Array of `header` | MUST | List of headers that the client MAY or MUST (depending on configuration) send to the provider on any request. | 992 | | `connection_status_query` | `fixed_query` | MUST | Query to use for checking whether the provided headers are valid und for obtaining connection status information. | 993 | | `header_acquisition_uri` | string | MAY | A URI that the client MAY offer to open in the user's web browser to help them obtain the header values, for example from their website. | 994 | | `header_acquisition_uri_title` | string | MAY | Title for the `acquisition_uri`. | 995 | 996 | 997 | #### 7.1.1.1. `header` structure 998 | 999 | | Field | Format | Requirement | Description | 1000 | | -------------- | ------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1001 | | `name` | string | MUST | Name of the header | 1002 | | `default` | string | MAY | Default value as a suggestion to the client. | 1003 | | `is_required` | boolean | MUST | Indicates if this header is required. | 1004 | | `is_sensitive` | boolean | MUST | Indicates if this header is sensitive and instructs the client to take appropriate measures to protect it. See [Storing Sensitive Headers](#91-storing-sensitive-headers) | 1005 | | `prefix` | string | MAY | Prefix that the client should prepend to the value entered by the user when sending it to the provider. The prefix MUST match the regular expression `[a-zA-Z0-9-_\. ]*`. | 1006 | | `suffix` | string | MAY | Suffix that the client should append to the value entered by the user when sending it to the provider.The suffix MUST match the regular expression `[a-zA-Z0-9-_\. ]*`. | 1007 | | `title` | string | MAY | Title that the client SHOULD display to the user. | 1008 | | `encoding` | string | MAY, default=`plain` | The encoding that the client MUST apply to the header value and the prefix/suffix. MUST be one of `plain` or `base64`. | 1009 | 1010 | ### 7.1.2. `provider_reconfiguration` 1011 | 1012 | This datablock allows the provider to communicate to the client that a new set of headers that it MUST sent along with every request instead of those entered by the user until a new initialization is performed. 1013 | The client MUST fully replace the values defined using the requirements from the original `provider_configuration` datablock with the new values defined in this datablock. 1014 | 1015 | | Field | Format | Requirement | Description | 1016 | | --------- | ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------- | 1017 | | `headers` | Object | MUST | An object whose properties indicate the new header names, the property values represent the new header values to be used. | 1018 | 1019 | 1020 | These new headers effectively act like cookies used on web sites. 1021 | Providers SHOULD therefore only use this datablock for purposes that are strictly required for the communication to work and MUST consider the potential legal implications when deciding to use this datablock for other purposes such as tracking or analytics. 1022 | Clients MAY require the user to confirm the new header values before starting to send them. 1023 | 1024 | 1025 | ### 7.1.3. `user` 1026 | 1027 | This datablock describes the current user, as seen by the provider. 1028 | The client SHOULD show the data to the user for confirmation that they are properly connected to the provider. 1029 | 1030 | | Field | Format | Requirement | Description | 1031 | | ------------------ | ------ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1032 | | `display_name` | string | SHOULD | The name of the user to display. | 1033 | | `display_tier` | string | MAY | The name of the plan/tier/subscription/etc. that this user is part of, if applicable for the provider. | 1034 | | `display_icon_uri` | string | MAY | URI to an image with an aspect ratio of 1:1, for example a profile picture or icon representing the subscription tier. Image SHOULD be of media type `image/jpeg` or `image/png`. | 1035 | 1036 | ## 7.2. Browsing-related Datablocks 1037 | 1038 | These datablocks all relate to the process of browsing for assets or implementations. 1039 | 1040 | ### 7.2.1. `asset_list_query` 1041 | Describes the variable query for fetching the list of available assets from a provider. 1042 | It follows the `variable_query` template. 1043 | 1044 | ### 7.2.2. `implementation_list_query` 1045 | Describes the variable query for fetching the list of available implementations for an asset from a provider. 1046 | It follows the `variable_query` template. 1047 | 1048 | ### 7.2.3. `next_query` 1049 | Describes a fixed query to fetch more results using the same parameters as the current query. 1050 | The response to this query from the provider MUST be of the same `kind` as the query in which this datablock is contained. 1051 | Follows the `fixed_query` template. 1052 | 1053 | ### 7.2.4. `response_statistics` 1054 | 1055 | Contains statistics about the current response. 1056 | It can be used to communicate the total number of results in a query where not all results can be communicated in one response and are instead deferred using `next_query`. 1057 | 1058 | | Field | Format | Requirement | Description | 1059 | | -------------------- | ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 1060 | | `result_count_total` | int | MAY | The total number of results. This number should include the total number of results matching the given query, even if not all results are returned due to pagination using the `query_next` datablock. | 1061 | 1062 | 1063 | ## 7.3. Display-related Datablocks 1064 | 1065 | These datablocks relate to how assets and their details are displayed to the user. 1066 | 1067 | ### 7.3.1. `text` 1068 | Contains general text information to be displayed to the user. 1069 | Can be applied to many different resource types. 1070 | 1071 | | Field | Format | Requirement | Description | 1072 | | ------------- | ------ | ----------- | ---------------------------------------------- | 1073 | | `title` | string | MUST | A title for the datablock's subject. | 1074 | | `description` | string | MAY | A description text for the datablocks subject. | 1075 | 1076 | 1077 | ### 7.3.2. `web_references` 1078 | References to external websites for documentation or support. 1079 | 1080 | An array of objects each of which MUST follow this format: 1081 | 1082 | | Field | Format | Requirement | Description | 1083 | | ---------- | ------ | ----------- | ------------------------------------------------------------------------------------------------------------- | 1084 | | `title` | string | MUST | The title to display for this web reference. | 1085 | | `uri` | string | MUST | The URL to be opened in the users browser. | 1086 | | `icon_uri` | string | MAY | URL to an image accessible via HTTP GET. The image's media type SHOULD be one of `image/png` or `image/jpeg`. | 1087 | 1088 | ### 7.3.3. `branding` 1089 | Contains brand information about the provider, MAY be used by the client to customize the user interface. 1090 | 1091 | | Field | Format | Requirement | Description | 1092 | | ----------------- | ------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------- | 1093 | | `color_accent` | string | MAY | Color for the provider, hex string in the format 'abcdef' (no #) | 1094 | | `logo_square_uri` | string | MAY | URI to a square logo. It SHOULD be of the mediatype `image/png` and SHOULD be transparent. | 1095 | | `logo_wide_uri` | string | MAY | URI to an image with an aspect ratio between 2:1 and 4:1. SHOULD be `image/png`, it SHOULD have a transparent background. | 1096 | | `banner_uri` | string | MAY | URI to an image with an aspect ratio between 2:1 and 4:1. SHOULD be `image/png` or `image/jpg`. It SHOULD NOT be transparent. | 1097 | 1098 | ### 7.3.4. `license` 1099 | Contains license information. 1100 | When attached to an asset, it means that the license information only applies to that asset, when applied to a provider, it means that the license information applies to all assets offered through that provider. 1101 | 1102 | | Field | Format | Requirement | Description | 1103 | | -------------- | ------ | ----------- | --------------------------------------------------------------------------------------------------------- | 1104 | | `license_spdx` | string | MAY | MUST be an [SPDX license identifier](https://spdx.org/licenses/) or be left unset/null if not applicable. | 1105 | | `license_uri` | string | MAY | URI which the client SHOULD offer to open in the user's web browser to learn more about the license. | 1106 | 1107 | ### 7.3.5. `authors` 1108 | 1109 | Is used communicate the author(s) of a particular asset. 1110 | 1111 | **Array of objects** with this structure: 1112 | 1113 | | Field | Format | Requirement | Description | 1114 | | ------ | ------ | ----------- | ----------------------------------------------------------------------------- | 1115 | | `name` | string | MUST | Name of the author. | 1116 | | `uri` | string | MAY | A URI for this author, for example a profile, portfolio or social media link. | 1117 | | `role` | string | MAY | The role that the author has had in the creation of this asset. | 1118 | 1119 | ### 7.3.6. `dimensions` 1120 | Contains general information about the physical dimensions of an asset. 1121 | This is primarily intended for displaying to users, not for actually scaling meshes or textures. 1122 | 1123 | When using this datablock to describe two-dimensional assets, such as textures, providers MUST use the `width_m` and `height_m` fields and only add `depth_m` when dealing with three-dimensional data. 1124 | 1125 | | Field | Format | Requirement | Description | 1126 | | ---------- | ------ | ----------- | ------------------------------- | 1127 | | `width_m` | float | SHOULD | Width of the referenced asset. | 1128 | | `height_m` | float | SHOULD | Height of the referenced asset. | 1129 | | `depth_m` | float | MAY | Depth of the referenced asset. | 1130 | 1131 | ### 7.3.7. `preview_image_supplemental` 1132 | Contains a list of preview images associated to an asset. 1133 | 1134 | An **array** of objects that conform to the following structure: 1135 | 1136 | | Field | Format | Requirement | Description | 1137 | | ----- | ------ | ----------- | ---------------------------------------- | 1138 | | `alt` | string | SHOULD | An "alt" String for the image. | 1139 | | `uri` | string | MUST | URL to an image accessible via HTTP GET. | 1140 | 1141 | The media type of the images SHOULD be one of `image/png` or `image/jpeg`. 1142 | 1143 | ### 7.3.8. `preview_image_thumbnail` 1144 | Contains information about a thumbnail image for an asset. 1145 | The thumbnail can be provided in multiple resolutions. 1146 | 1147 | An object that MUST conform to this format: 1148 | 1149 | | Field | Format | Requirement | Description | 1150 | | ------ | ------ | ----------- | ------------------------------ | 1151 | | `alt` | string | SHOULD | An "alt" String for the image. | 1152 | | `uris` | object | MUST | See structure outlined below. | 1153 | 1154 | #### 7.3.8.1. `uris` Structure 1155 | 1156 | The `uris` field MUST be an object whose keys are strings containing an integer and whose values are strings. 1157 | The object MUST have at least one member. 1158 | The key represents the resolution of the thumbnail, the value represents the URI for the thumbnail image in this resolution. 1159 | The thumbnail image SHOULD be a square. 1160 | If the image is not a square, its key SHOULD be set based on the pixel count of its longest site. 1161 | The image's media type SHOULD be one of `image/png` or `image/jpeg`. 1162 | If the provider does not have insight into the dimensions of the thumbnail that it is referring the client to, it SHOULD use use the key `0` for the thumbnail url. 1163 | 1164 | ### 7.3.9. `keywords` 1165 | Contains keywords or tags that the client MAY use for local asset organization. 1166 | 1167 | This datablock is **an array** consisting of type `string`. 1168 | 1169 | ## 7.4. Unlocking-related Datablocks 1170 | 1171 | These datablocks are used if the provider is utilizing the component unlocking system in AssetFetch. 1172 | 1173 | ### 7.4.1. `unlock_balance` 1174 | Information about the user's current account balance. 1175 | 1176 | | Field | Format | Requirement | Description | 1177 | | -------------------- | ------ | ----------- | ----------------------------------------------------------------------------------------------------------- | 1178 | | `balance` | number | MUST | Balance. | 1179 | | `balance_unit` | string | MUST | The currency or name of token that's used by this provider to be displayed alongside the price of anything. | 1180 | | `balance_refill_uri` | string | MAY | URL to direct the user to in order to refill their prepaid balance, for example an online purchase form. | 1181 | 1182 | ### 7.4.2. `unlock_queries` 1183 | 1184 | Contains the query or queries required to unlock all or some of the components in this implementation list. 1185 | 1186 | This datablock is **an array** consisting of `unlock_query` objects. 1187 | 1188 | #### 7.4.2.1. `unlock_query` structure 1189 | 1190 | | Field | Format | Requirement | Description | 1191 | | -------------------- | ----------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1192 | | `id` | string | MUST | This is the id by which `fetch.download`-datablocks and the `child_query_ids` field (see below) a will reference this query. | 1193 | | `unlocked` | boolean | MUST | Indicates whether the subject of this datablock is already unlocked (because the user has already made this query and the associated purchase in the past ) or still locked. | 1194 | | `price` | number | MUST if `unlocked=False`, MAY otherwise | The price that the provider will charge the user in the background if they run the `unlock_query`. This price is assumed to be in the currency/unit defined in the `unlock_balance` datablock. | 1195 | | `query` | `fixed_query` | MUST if `unlocked=False`, MUST NOT otherwise | Query to actually unlock the requested resource ("make the purchase"). | 1196 | | `child_query_ids` | Array of `string` | MAY | A list containing the `id` values of other queries that the client MUST also be considered "unlocked" if this query has been executed. | 1197 | | `query_fallback_uri` | string | MAY | An optional URI for the client to open in the user's web browser in order to let them make the purchase manually if asset unlocking is not fully supported by the client. | 1198 | 1199 | 1200 | ## 7.5. Format-related Datablocks 1201 | 1202 | Format-related datablocks communicate details about the data in individual component files. 1203 | 1204 | ### 7.5.1. `format` 1205 | This is the default format datablock for all file formats that do not have their own dedicated `format.*` datablock. 1206 | 1207 | | Field | Format | Requirement | Description | 1208 | | ----------- | ------ | ----------------- | ------------------------------------------------- | 1209 | | `extension` | string | MUST | The file extension. | 1210 | | `mediatype` | string | SHOULD, see below | The mediatype string for this file, if available. | 1211 | 1212 | #### 7.5.1.1. `extension` rules 1213 | 1214 | The `extension` field MUST include a leading dot (`.obj` would be correct,`obj` would not be correct), and, if necessary to fully communicate the format, 1215 | MUST include multiple dots for properly expressing certain "combined" file formats (eg. `.tar.gz` for a gzipped tar-archive). 1216 | 1217 | #### 7.5.1.2. `mediatype` rules 1218 | 1219 | The `mediatype` field SHOULD be used if (and only if) an official mediatype definition exists for the file format of the file associated with the component. 1220 | See [the official mediatype list on the IANA website](https://www.iana.org/assignments/media-types/media-types.xhtml). 1221 | 1222 | ### 7.5.2. `format.blend` 1223 | Information about files with the extension `.blend`. 1224 | This information is intended to help the client understand the file. 1225 | 1226 | | Field | Format | Requirement | Description | 1227 | | ---------- | ----------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- | 1228 | | `version` | string | SHOULD | Blender Version in the format `Major.Minor.Patch` or `Major.Minor` or `Major` | 1229 | | `is_asset` | boolean | SHOULD | `true` if the blend file contains object(s) marked as an asset for Blender's own Asset Manager. (default=`false`) | 1230 | | `targets` | array of `target` | MAY | Array containing the blender structures inside the file that are relevant to the asset. | 1231 | 1232 | #### 7.5.2.1. `target` Structure 1233 | 1234 | | Field | Format | Requirement | Description | 1235 | | ------- | ----------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1236 | | `kind` | `string` | MUST | One of `actions`, `armatures`, `brushes`, `cache_files`, `cameras`, `collections`, `curves`, `fonts`, `grease_pencils`, `hair_curves`, `images`, `lattices`, `lightprobes`, `lights`, `linestyles`, `masks`, `materials`, `meshes`, `metaballs`, `movieclips`, `node_groups`, `objects`, `paint_curves`, `palettes`, `particles`, `pointclouds`, `scenes`, `screens`, `simulations`, `sounds`, `speakers`, `texts`, `textures`, `volumes`, `workspaces`, `worlds` | 1237 | | `names` | Array of `string` | MUST | List of the names of the resources to import. | 1238 | 1239 | ### 7.5.3. `format.obj` 1240 | Information about files with the extension `.obj`. 1241 | 1242 | | Field | Format | Requirement | Description | 1243 | | ------------ | ------ | ----------- | ---------------------------------------------------------------------------------------------------- | 1244 | | `up_axis` | string | SHOULD | Indicates which axis should be treated as "up". MUST be one of `+x`,`-x`,`+y`,`-y`,`+z`,`-z`. | 1245 | | `front_axis` | string | MAY | Indicates which axis should be treated as the "front". MUST be one of `+x`,`-x`,`+y`,`-y`,`+z`,`-z`. | 1246 | 1247 | 1248 | ## 7.6. Fetching- and Storage-related Datablocks 1249 | 1250 | These datablocks describe how a client can gain access to a component file. 1251 | 1252 | ### 7.6.1. `fetch.download` 1253 | 1254 | Describes how a component file can be downloaded via HTTP. 1255 | 1256 | If an `unlock_query_id` is defined, then the client MUST execute the referenced unlock query before attempting to perform the download, unless the query is already marked as unlocked. 1257 | 1258 | | Field | Format | Requirement | Description | 1259 | | ----------------- | ------------- | ----------- | ------------------------------------------------------------------------------------------- | 1260 | | `unlock_query_id` | string | MAY | The id of the unlocking query in the `unlock_queries` datablock required for this download. | 1261 | | `download_query` | `fixed_query` | MUST | The query to download the file. | 1262 | 1263 | 1264 | ### 7.6.2. `fetch.from_archive` 1265 | This datablock indicates that this component represents a file from within an archive that has already been downloaded separately. 1266 | 1267 | | Field | Format | Requirement | Description | 1268 | | ---------------------- | ------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1269 | | `archive_component_id` | string | MUST | The id of the component representing the archive that this component is contained in. | 1270 | | `component_sub_path` | string | MUST | The location of the file inside the referenced archive. This MUST be the path to the file starting at the root of its archive. It MUST NOT start with a leading slash and MUST include the full name of the file inside the archive. It MUST NOT contain relative path references (`./` or `../`). | 1271 | 1272 | 1273 | 1274 | ### 7.6.3. `store` 1275 | 1276 | Contains information about how/where to store a component file locally in the implementation directory (see [2.6.1](#261-implementation-directory) and [8.4](#84-choosing-a-local-directory)). 1277 | 1278 | | Field | Format | Requirement | Description | 1279 | | ----------------- | ------- | ----------- | ----------------------------------------------- | 1280 | | `bytes` | integer | MAY | The length of the file in bytes. | 1281 | | `local_file_path` | string | MUST | Local sub-path in the implementation directory. | 1282 | 1283 | #### 7.6.3.1. `local_file_path` rules 1284 | 1285 | The `local_file_path` is the sub-path in the implementation directory and MUST include the full name that the file should take. 1286 | This brings with it several rules: 1287 | - It MUST NOT end with a "trailing slash". 1288 | - It MUST NOT start with a "leading slash". 1289 | - It MUST NOT contain relative path references (`./` or `../`) anywhere within it. 1290 | - It MUST NOT use the backslash (`\`) as its directory separator (use `/` instead). 1291 | 1292 | 1293 | **Examples:** 1294 | 1295 | `example.jpg` and `sub/dir/example.jpg` are valid local file paths. 1296 | 1297 | `/example.jpg`, `./example.jpg`, `sub/dir/`, `/sub/dir/example.jpg`, `sub\dir\example.jpg` and `sub/dir/../test.jpg` are NOT valid local file paths. 1298 | 1299 | 1300 | ## 7.7. Handling/Processing-related Datablocks 1301 | 1302 | These datablocks describe the way in which a specific component should be processed when importing an asset implementation. 1303 | 1304 | ### 7.7.1. `handle.native` 1305 | This datablock indicates that this file should be handled by the host application's native import functionality using information from the `format.*` datablock, if available. 1306 | The full description of component handling can be found in [8.6](#86-handling-component-files). 1307 | 1308 | Currently, this datablock contains no fields and MUST therefore represented by the empty object: `{}`. 1309 | 1310 | ### 7.7.2. `handle.archive` 1311 | 1312 | This datablock indicates that this component represents an archive, containing other component files. 1313 | 1314 | If a component has this datablock, then the client SHOULD delete it from the local implementation directory after the import process has been completed. 1315 | 1316 | An object that MUST conform to this format: 1317 | 1318 | | Field | Format | Requirement | Description | 1319 | | ---------------------- | ------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------- | 1320 | | `extract_fully` | boolean | MUST | Indicates whether or not the entire archive should be fully extracted into the local implementation directory. | 1321 | | `local_directory_path` | string | MUST, only if `extract_fully=true` | Local (sub-)path where the file MUST be placed by the client. | 1322 | 1323 | #### 7.7.2.1. `local_directory_path` rules 1324 | 1325 | The following rules apply to the `local_directory_path`: 1326 | - It MUST end with a slash ("trailing slash") 1327 | - It MUST NOT start with a slash (unless it targets the root of the implementation directory in which case the `local_directory_path` is simply `/`). 1328 | - It MUST not contain backslashes (`\`) as directory separators 1329 | - It MUST NOT contain relative path references (`./` or `../`) anywhere within it. 1330 | 1331 | **Examples:** 1332 | 1333 | `/`, `contents/` and `my/contents/` are valid local directory paths. 1334 | 1335 | `contents`,`./contents/`,`./contents`,`my/../../contents` and `../contents` are NOT valid local directory paths. 1336 | 1337 | ### 7.7.3. `handle.loose_environment_map` 1338 | Marks a component as an environment map. 1339 | This datablock only needs to be applied if the component is a "bare file", like (HDR or EXR). 1340 | 1341 | An object that MUST conform to this format: 1342 | 1343 | | Field | Format | Requirement | Description | 1344 | | ------------------ | ------ | --------------------------------- | --------------------------------------- | 1345 | | `environment_name` | string | MAY | A name for the environment. | 1346 | | `projection` | string | SHOULD, default=`equirectangular` | One of `equirectangular`, `mirror_ball` | 1347 | 1348 | ### 7.7.4. `handle.loose_material_map` 1349 | 1350 | Indicates that this component is part of a loose material as a material map. 1351 | 1352 | | Field | Format | Requirement | Description | 1353 | | --------------- | ------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- | 1354 | | `material_name` | string | MUST | Name of the material. | 1355 | | `map` | string | MUST | `albedo` `roughness` `metallic` `diffuse` `glossiness` `specular` `height` `normal+y` `normal-y` `opacity` `ambient_occlusion` `emission` | 1356 | 1357 | ## 7.8. Linking-related Datablocks 1358 | 1359 | These datablocks are used to describe connections between different components that are not expressed through the files themselves. 1360 | 1361 | ### 7.8.1. `link.loose_material` 1362 | 1363 | Indicates that this component uses one or multiple materials defined using `handle.loose_material_map` datablocks. 1364 | 1365 | An object that MUST conform to this format: 1366 | 1367 | | Field | Format | Requirement | Description | 1368 | | --------------- | ------ | ----------- | ------------------------------------------------------------------------ | 1369 | | `material_name` | string | MUST | Name of the material used in the `handle.loose_material_map` datablocks. | 1370 | 1371 | ### 7.8.2. `link.mtlx_material` 1372 | 1373 | Indicates that this component makes use of a material defined in a mtlx document represented by another component. 1374 | 1375 | An object that MUST conform to this format: 1376 | 1377 | | Field | Format | Requirement | Description | 1378 | | ------------------- | ------ | ----------- | -------------------------------------------------------------------------------- | 1379 | | `mtlx_component_id` | string | MUST | Id of the component that represents the mtlx file. | 1380 | | `mtlx_material` | string | SHOULD | Reference for which material to use from the mtlx file, if it contains multiple. | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | # 8. Working With Asset Implementations 1394 | 1395 | ## 8.1. Overview 1396 | 1397 | Building on the description of the general operation in section [3](#3-general-operation) this section provides a more detailed look at the process of analyzing and importing asset implementations. 1398 | 1399 | When receiving the metadata of several implementations of an asset from a provider, the client SHOULD perform the following steps: 1400 | 1401 | 1. Analyze the implementations and decide if there is one (or multiple) that it can handle and choose one to *actually* import. 1402 | 2. Perform all required unlocking queries based on the information in the `unlock_queries` datablock and references in the `fetch.download` datablocks. 1403 | 3. Allocating local storage for the component files. 1404 | 4. Fetch and store all files for all components using the instructions in their `store` (and in the case of archives `handle.archive`) datablocks. 1405 | 5. Handle the component files using the instructions in their `handle.*`, `format.*`, `link.*` and other datablocks. 1406 | 1407 | ## 8.2. Implementation Analysis 1408 | 1409 | When analyzing a set of implementation sent from the provider via the implementation list endpoint (See [5.5](#55-endpoint-implementation-list-implementation_list)), the client SHOULD decide for every implementation whether it is considered "readable". 1410 | 1411 | Possible factors for this decision are: 1412 | 1413 | - The file types used in the implementation, as indicated by the `format` or `format.*` datablock. 1414 | - The use of more advanced AssetFetch features such as archive handling or component unlocking which the client might not support. 1415 | - Format-specific data in the `format.*` datablock which indicates that the given file is (in)compatible with the client/host application, for example through version-indicators. 1416 | 1417 | 1418 | 1419 | If at least one of the implementations offered by the provider has been deemed readable, the client can proceed and make an actual import attempt. 1420 | This usually involves interaction with the host application which means that client implementors SHOULD consider the steps outlined in this section only as a rough indicator for how to perform the import. 1421 | 1422 | ## 8.3. Performing Unlock Queries 1423 | 1424 | If the implementation contains components with a `fetch.download` datablock that references unlocking queries, 1425 | then the client MUST perform the unlock query referenced in that datablock before it can proceed. 1426 | Otherwise the resources may not be fully unlocked and the provider will likely deny access. 1427 | 1428 | ## 8.4. Choosing a Local Directory 1429 | 1430 | Individual asset components/files may have implicit relationships to each other that are not directly visible from any of the datablocks such as relative file paths *within* project files (i.e. a model file expecting a texture to be at `./tex.png`). 1431 | To ensure that these references are still functional after the download, AssetFetch specifies certain rules regarding how clients arrange downloaded files in the local file system. 1432 | 1433 | - The client SHOULD create a dedicated directory for every implementation of every asset that it downloads. 1434 | - The directory SHOULD be empty at the start of the component handling process. 1435 | - The location of this directory is not specified and is up to the client implementation. 1436 | - For this purpose it MAY use the `id` values in the the provider-, asset- and implementation queries. 1437 | - It MAY also be dependent on the context in which the client currently runs, for example a subfolder relative to the currently opened project. 1438 | 1439 | 1440 | ## 8.5. Downloading and Storing Files 1441 | 1442 | ### 8.5.1. Storing All Component Files Based on `store` Datablock 1443 | 1444 | Inside the implementation directory the client SHOULD place every downloaded file in the directory as specified in the `local_file_path` field of the component's `store` datablock. 1445 | 1446 | If an implementation assigns the same `local_file_path` to two different file components, then the client's behavior is undefined. 1447 | Providers MUST avoid configurations that lead to this outcome. 1448 | 1449 | ### 8.5.2. Interacting With Archives (components with `handle.archive` datablock) 1450 | 1451 | Some components may carry a `fetch.from_archive` datablock which indicates that they need to be extracted from an archive. 1452 | In that case the client SHOULD extract the file from the referenced archive based on the `component_sub_path` in the `fetch.from_archive` datablock and store it as described in its `store` datablock (as described above). 1453 | 1454 | When working with archives, additional behavior is defined by the `extract_fully` field in the `handle.archive` datablock. 1455 | 1456 | #### 8.5.2.1. Handling for `extract_fully=true` 1457 | 1458 | The client MUST unpack the full contents of the archive root into the implementation directory using the `local_directory_path` in the `handle.archive` datablock as the sub-path. 1459 | 1460 | All files extracted by this which are not referenced explicitly by a component MUST be treated as passive files (see [2.7.1](#271-component-activeness)). 1461 | 1462 | Overlapping or conflicting `local_directory_path` values have undefined behavior and MUST be avoided by the provider. 1463 | 1464 | #### 8.5.2.2. Handling for `extract_fully=false` 1465 | 1466 | In this case, the client SHOULD unpack only those files that are referenced by other components in their respective `fetch.from_archive` datablocks. 1467 | 1468 | #### 8.5.2.3. Deleting Archives 1469 | 1470 | If a component has the `handle.archive` datablock then it is assumed to be ephemeral, meaning that the client SHOULD delete it from the local implementation directory after all component files have been extracted from it. 1471 | 1472 | ## 8.6. Handling Component Files 1473 | 1474 | After downloading all component files and arranging them on disk, the client can begin to handle/import the files. 1475 | The behavior of a component is largely controlled by its `handle.*` datablock. 1476 | 1477 | ### 8.6.1. Handling For Components Without a `handle.*` Datablock 1478 | 1479 | The absence of a `handle.*` datablock indicates that a component file is passive. 1480 | Do not handle the file directly, only store it (see [8.5](#85-downloading-and-storing-files)) so that other components can reference it. 1481 | Also see [2.7.1](#271-component-activeness) for the definition and examples of component activeness. 1482 | 1483 | ### 8.6.2. Handling based on the native handling datablock (`handle.native`) 1484 | 1485 | Make an attempt to load this file through the host application's native import feature for this file's format, as indicated by the `format.*` datablock. 1486 | 1487 | ### 8.6.3. Handling a Loose Material Map (`handle.loose_material_map`) 1488 | 1489 | Many file formats for 3D content - both vendor-specific as well as open - offer native support for referencing external texture files. 1490 | Providers SHOULD use these "native" material formats whenever possible and send the relevant texture files along as passive files, as described above. 1491 | The `handle.loose_material_map` is designed for cases in which this is not possible or practical. 1492 | 1493 | It provides a basic material system where multiple components define the maps of a PBR material. 1494 | The client SHOULD handle these components by creating a new material in its host application and adding the PBR map to it in a way that represents common practice for the given host application. 1495 | 1496 | ### 8.6.4. Handling a Loose Environment Map (`handle.loose_environment_map`) 1497 | 1498 | Environments for image-based lighting face a similar challenge as PBR materials as it is common practice to only provide a singular image file without any further information. 1499 | 1500 | This the `handle.loose_environment_map` datablock indicates that this component is one such environment map with a specific projection, meaning that it should be imported as an environment or something similar within the context of the host application. 1501 | 1502 | ## 8.7. Handling Component Links 1503 | 1504 | After every component has been handled individually, it might also be necessary to consider relationships between components. 1505 | Again, providers SHOULD rely on native links right in the component files to model these relationships (for example through the use of .mtl files when working with .obj models). 1506 | For cases in which this is not possible or practical, the `link.*` family of datablocks is used. 1507 | 1508 | ### 8.7.1. Handling MTLX Material Links (`link.mtlx_material`) 1509 | The `link.mtlx_material` datablock allows references from a component representing a mesh to a component representing an MaterialX (MTLX) file. 1510 | This allows the use of `.mtlx` files with mesh file formats that do not have the native ability to reference MTLX files. 1511 | 1512 | When encountering such a link, the client SHOULD apply the referenced material from the MTLX file to the entire mesh. 1513 | 1514 | ### 8.7.2. Handling loose Material Links (`link.loose_material`) 1515 | This datablock allows references from a component representing a mesh to a loose material described through `handle.loose_material_map` datablocks on multiple other components. 1516 | 1517 | When encountering such a link, the client SHOULD apply the referenced material to the entire mesh. 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | # 9. Security Considerations 1525 | 1526 | This section describes security considerations for implementing AssetFetch. 1527 | 1528 | ## 9.1. Storing Sensitive Headers 1529 | During the initialization step providers can mark headers as sensitive. 1530 | Clients SHOULD find an appropriate solution for storing these sensitive headers. 1531 | They SHOULD consider storing secret headers through native operation system APIs for credential management. 1532 | 1533 | ## 9.2. Avoiding Relative Paths in Local Path Fields 1534 | Datablocks of the `fetch.*` family specify a local sub-path for every component that needs to be appended to a local path chosen by the client in order to assemble the correct file structure for this asset. 1535 | A malicious provider might try to insert relative references, especially back-references (`..`) as they can allow the provider to place files anywhere on the user's system ( Using a path like`../../../../example.txt`). 1536 | Clients MUST take care to ensure that components with references like `./` or `../` in their local path are rejected. 1537 | 1538 | ## 9.3. Self-referential Archive Components 1539 | The notation in the `fetch.from_archive` datablock allows a provider to (accidentally or deliberately) create loops out of one or multiple archives. 1540 | The client MUST detect cases in which archive components create loops in order to avoid instability or uncontrolled growth of the implementation directory. 1541 | --------------------------------------------------------------------------------