diff --git a/CHANGELOG.md b/CHANGELOG.md index fe09d79..4ac8a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ _Changes in the next release_ - Dock 3 external port configuration. - Voice Assistant support. - Select Entity support. +- Media browsing and searching ([feature-and-bug-tracker#70](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/70)). --- diff --git a/README.md b/README.md index 87c1db6..725c908 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ behaviour or automatically add or configure its entities to the users profile. ### Develop integration drivers Since we are providing an API and not an SDK for a specific programming language, one can develop external integrations -in any language which is capable running a WebSockets server and handling JSON data. +in any language that is capable of running a WebSockets server and handling JSON data. The downside of an API is that more low-level coding is required. In our case this involves running a WebSocket server, handling the connections from the Remote device, and parsing the JSON payload in the WebSocket text messages. @@ -61,19 +61,19 @@ how to develop an integration driver for the Remote devices. #### Examples -- [API models in Rust](https://github.com/unfoldedcircle/api-model-rs) - [Node.js API wrapper for the UC Integration-API](https://github.com/unfoldedcircle/integration-node-library) Integrations using the Node.js API wrapper: + - [TypeScript integration driver example](https://github.com/unfoldedcircle/integration-ts-example) - [Global CachΓ© IR integration](https://github.com/unfoldedcircle/integration-globalcache) + - [Philips Hue integration](https://github.com/unfoldedcircle/integration-philipshue) - [Roon integration](https://github.com/unfoldedcircle/integration-roon) - [Python API wrapper library for the UC Integration-API](https://github.com/unfoldedcircle/integration-python-library) Integrations using the Python API wrapper: - [Android TV integration](https://github.com/unfoldedcircle/integration-androidtv) - [Apple TV integration](https://github.com/unfoldedcircle/integration-appletv) - [Denon AVR integration](https://github.com/unfoldedcircle/integration-denonavr) -- [Home Assistant integration](https://github.com/unfoldedcircle/integration-home-assistant) written in Rust - -We plan to release more examples in the future. +- [API models in Rust](https://github.com/unfoldedcircle/api-model-rs) + - [Home Assistant integration](https://github.com/unfoldedcircle/integration-home-assistant) written in Rust ## Core APIs diff --git a/core-api/README.md b/core-api/README.md index 133c813..387443c 100644 --- a/core-api/README.md +++ b/core-api/README.md @@ -27,6 +27,7 @@ in YAML format. | UCR Firmware | Core Simulator | REST API | WS API | |--------------|----------------|----------|--------| +| | 0.70.4 | 0.45.1 | 0.35.1 | | 2.8.4-beta | 0.69.3 | 0.44.4 | 0.34.0 | | 2.8.3-beta | 0.68.1 | 0.44.1 | 0.34.0 | | 2.8.0-beta | 0.66.4 | 0.43.1 | 0.33.0 | diff --git a/core-api/rest/CHANGELOG.md b/core-api/rest/CHANGELOG.md index 989b417..483fd28 100644 --- a/core-api/rest/CHANGELOG.md +++ b/core-api/rest/CHANGELOG.md @@ -10,6 +10,12 @@ This section contains unreleased changes which will be part of an upcoming relea --- +## 0.45.1 +### Added +- Media browsing and searching ([feature-and-bug-tracker#70](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/70)). +### Fixed +- Fixed invalid schema references. + ## 0.44.4 ### Fixed - Creating a UI page returns 201 instead of 200. diff --git a/core-api/rest/UCR-core-openapi.yaml b/core-api/rest/UCR-core-openapi.yaml index 223df8d..c695359 100644 --- a/core-api/rest/UCR-core-openapi.yaml +++ b/core-api/rest/UCR-core-openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: Remote Two/3 REST Core-API - version: 0.44.4 + version: 0.45.1 contact: name: API Support url: 'https://github.com/unfoldedcircle/core-api/issues' @@ -9,7 +9,7 @@ info: license: name: Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) url: 'https://creativecommons.org/licenses/by-sa/4.0/' - description: "The Unfolded Circle REST Core-API for Remote Two/3 (_UCR REST Core-API_ in short) allows to configure the remote and\nmanage custom resource files. Furthermore, API-keys for the WebSocket & REST APIs can be created.\n\n## Overview\n\nThe Unfolded Circle Remote Core-APIs consist of:\n- this REST API\n- the [UCR WebSocket Core-API](https://github.com/unfoldedcircle/core-api/tree/main/core-api/websocket),\n providing asynchronous events.\n\nThe focus of the Core-APIs is to provide all functionality for the UI application and the web-configurator. \nThey allow to interact with the Unfolded Circle remote-core service and take full control of its features.\n\nThe Core-APIs may also be used by other external systems and integration drivers, if specific configuration or\ninteraction features are required, which are not present in the [UCR Integration-API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api).\n\n## Authentication\n\nAll API endpoints besides `/api/pub` are secured. Available authentication methods are:\n- `Basic Auth` for every request. \n This should only be used for simple testing. At the moment there's only a single user account available for the\n web-configurator.\n - User: `web-configurator`\n - Password: generated pin shown in the remote-UI.\n- `Bearer Token` for every request. \n This is the preferred authentication method for external systems communicating with the UCR REST Core-API.\n - See `/auth/api_keys` endpoints on how to create and manage API keys.\n - Only the `admin` role is supported at the moment. More roles will be added in the future.\n - Example for a curl request: \n `curl 'http://$IP/api/system' --header 'Authorization: Bearer $API_KEY'`\n- `Cookie` based session login with the `/api/pub/login` endpoint. \n This is the preferred method for web frontends like the web-configurator.\n\n## \U0001F6A7 Missing Features\n\n**This API is a preview version and does not yet contain all functionality.**\n\nThe following features will be continuously added (in no particular order):\n\n- Upload of custom certificate\n- Static network configuration\n\nPlease check the [core-api GitHub issues](https://github.com/unfoldedcircle/core-api/issues) for the current state. \n\n## API Versioning\n\nThe API is versioned according to [SemVer](https://semver.org/). \nThe initial public release will be `1.0.0` once it is considered stable enough with some initial integration\nimplementations and developer examples.\n\n**Any major version zero (`0.y.z`) is for initial development and may change at any time!** \nI.e. backward compatibility for minor releases is not yet established, anything MAY change at any time!\nWe try avoiding it, but it might still happen...\n" + description: "The Unfolded Circle REST Core-API for Remote Two/3 (_UCR REST Core-API_ in short) allows to configure the remote and\nmanage custom resource files. Furthermore, API-keys for the WebSocket & REST APIs can be created.\n\n## Overview\n\nThe Unfolded Circle Remote Core-APIs consist of:\n- this REST API\n- the [UCR WebSocket Core-API](https://github.com/unfoldedcircle/core-api/tree/main/core-api/websocket),\n providing asynchronous events.\n\nThe focus of the Core-APIs is to provide all functionality for the UI application and the web-configurator. \nThey allow to interact with the Unfolded Circle remote-core service and take full control of its features.\n\nThe Core-APIs may also be used by other external systems and integration drivers, if specific configuration or\ninteraction features are required, which are not present in the [UCR Integration-API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api).\n\n## Authentication\n\nAll API endpoints besides `/api/pub` are secured. Available authentication methods are:\n- `Basic Auth` for every request. \n This should only be used for simple testing. At the moment there's only a single user account available for the\n web-configurator.\n - User: `web-configurator`\n - Password: generated pin shown in the remote-UI.\n- `Bearer Token` for every request. \n This is the preferred authentication method for external systems communicating with the UCR REST Core-API.\n - See `/auth/api_keys` endpoints on how to create and manage API keys.\n - Only the `admin` role is supported at the moment. More roles will be added in the future.\n - Example for a curl request: \n `curl 'http://$IP/api/system' --header 'Authorization: Bearer $API_KEY'`\n- `Cookie` based session login with the `/api/pub/login` endpoint. \n This is the preferred method for web frontends like the web-configurator.\n\n## \U0001F6A7 Missing Features\n\nThe following features will be continuously added (in no particular order):\n\n- Upload of custom certificate\n- Static network configuration\n\nPlease check the [core-api GitHub issues](https://github.com/unfoldedcircle/core-api/issues) for the current state. \n\n## API Versioning\n\nThe API is versioned according to [SemVer](https://semver.org/). \nThe initial public release will be `1.0.0` once it is considered stable enough with some initial integration\nimplementations and developer examples.\n\n**Any major version zero (`0.y.z`) is for initial development and may change at any time!** \nI.e. backward compatibility for minor releases is not yet established, anything MAY change at any time!\nWe try avoiding it, but it might still happen...\n" externalDocs: description: Find out more about the Remotes url: 'https://www.unfoldedcircle.com/' @@ -2555,6 +2555,193 @@ paths: $ref: '#/components/responses/Err404NotFound' '422': $ref: '#/components/responses/Err422UnprocessableEntity' + '/entities/{entityId}/media/browse': + get: + tags: + - entities + summary: "\U0001F50D Browse media containers." + description: | + This endpoint allows browsing media containers within supported media-player entities. + + The `media_id` and `media_type` parameters are optional and can be used to restrict browsing to a specific media item + or content type. Both fields can be returned in the browse response on the root item and on every child item to describe + where the item lives in the provider’s hierarchy and how it can be addressed later. For example, for playback or further + browsing. + + **Important:** When using `media_id` and `media_type` values in further calls, they must match the values returned in + the previous `media_browse` response exactly. If an empty text (`""`) is returned as value, that exact + empty text must be sent back on subsequent requests. An empty text does not mean "no value." + + A `media_id` or `media_type` field may only be omitted from a request if it was not present in the response at all + or if it was explicitly set to `null` in the browse response. + + The `stable_ids` query parameter is a hint that the client requires stable media identifiers in the returned + `media_id` and `media_type` fields. Certain media providers may not support stable identifiers, but the integration + might be able to use a workaround generating stable identifiers on the client side. + + Roon is such an example, which generates new media keys for every new browse or search request. Use cases like + "select an identifier of my favorite playlist, so I can still play it next month" aren't possible with changing + identifiers. As a workaround, the integration driver returns a path structure (`Library/Playlists/Favorite Songs`) + that is resolved when playing an item. + + The paging query parameters can be used to retrieve a specific page of media items and to limit result size: + + - `limit`: Maximum number of items to return per page. Default is 10. + - `page`: 1-based page index. Default is 1. + + Common error status codes: + - `400 Bad Request`: if the query is invalid, for example if `media_type` is missing for a content specific query. + - `404 Not Found`: if the entity does not exist or is not a media-player entity. + - `408 Request Timeout`: if the media browse service is not responding. + - `409 Conflict`: if the media-player entity does not support media browsing. + - `500 Internal Server Error`: if an internal error occurred. + - `503 Service Unavailable`: if the media browse service is unavailable or not supported for the given location specified in `media_id` and `media_type`. + operationId: browseMedia + parameters: + - $ref: '#/components/parameters/entity_id' + - $ref: '#/components/parameters/media_id' + - $ref: '#/components/parameters/media_type' + - $ref: '#/components/parameters/stable_ids' + - $ref: '#/components/parameters/page' + - $ref: '#/components/parameters/limit' + responses: + '200': + description: Successful operation. + headers: + Pagination-Count: + description: Total number of items. + schema: + type: integer + Pagination-Limit: + description: Number of returned items. + schema: + type: integer + Pagination-Page: + description: Current page number. 1-based. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/MediaBrowseResponse' + '400': + $ref: '#/components/responses/Err400BadRequest' + '401': + $ref: '#/components/responses/Err401Unauthorized' + '403': + $ref: '#/components/responses/Err403Forbidden' + '404': + $ref: '#/components/responses/Err404NotFound' + '408': + $ref: '#/components/responses/Err408RequestTimeout' + '409': + $ref: '#/components/responses/Err409Conflict' + '500': + $ref: '#/components/responses/Err500InternalServerError' + '503': + $ref: '#/components/responses/Err503ServiceUnavailable' + '/entities/{entityId}/media/search': + get: + tags: + - entities + summary: "\U0001F50D Search for media items." + description: | + This endpoint allows searching for media within supported media-player entities. + + The `media_id` and `media_type` parameters behave the same as in the browse endpoint: they can be used to restrict the + search to a specific container or content type. + + A `media_id` or `media_type` field may only be omitted from a request if it was not present in the response at all + or if it was explicitly set to `null` in the search response. + + The `stable_ids` query parameter is a hint that the client requires stable media identifiers in the returned + `media_id` and `media_type` fields. Certain media providers may not support stable identifiers, but the integration + might be able to use a workaround generating stable identifiers on the client side. See browse endpoint for more + information. + + The paging query parameters can be used to retrieve a specific page of media items and to limit result size: + + - `limit`: Maximum number of items to return per page. Default is 10. + - `page`: 1-based page index. Default is 1. + + Common error status codes: + - `400 Bad Request`: if the query is invalid, for example if `media_type` is missing for a content specific query. + - `404 Not Found`: if the entity does not exist or is not a media-player entity. + - `408 Request Timeout`: if the media search service is not responding. + - `409 Conflict`: if the media-player entity does not support media searching. + - `500 Internal Server Error`: if an internal error occurred. + - `503 Service Unavailable`: if the media search service is unavailable or not supported for the given location specified in `media_id` and `media_type`. + operationId: searchMedia + parameters: + - $ref: '#/components/parameters/entity_id' + - name: q + in: query + description: The search string. + required: true + schema: + type: string + maxLength: 255 + - $ref: '#/components/parameters/media_id' + - $ref: '#/components/parameters/media_type' + - name: media_classes + in: query + description: Optional media classes to filter the results. Separate multiple classes with a comma. + required: false + schema: + type: string + - name: artist + in: query + description: Optional artist filter. + required: false + schema: + type: string + maxLength: 255 + - name: album + in: query + description: Optional album filter. + required: false + schema: + type: string + maxLength: 255 + - $ref: '#/components/parameters/stable_ids' + - $ref: '#/components/parameters/page' + - $ref: '#/components/parameters/limit' + responses: + '200': + description: Successful operation. + headers: + Pagination-Count: + description: Total number of items. + schema: + type: integer + Pagination-Limit: + description: Number of returned items. + schema: + type: integer + Pagination-Page: + description: Current page number. 1-based. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/MediaSearchResponse' + '400': + $ref: '#/components/responses/Err400BadRequest' + '401': + $ref: '#/components/responses/Err401Unauthorized' + '403': + $ref: '#/components/responses/Err403Forbidden' + '404': + $ref: '#/components/responses/Err404NotFound' + '408': + $ref: '#/components/responses/Err408RequestTimeout' + '409': + $ref: '#/components/responses/Err409Conflict' + '500': + $ref: '#/components/responses/Err500InternalServerError' + '503': + $ref: '#/components/responses/Err503ServiceUnavailable' /activities: head: tags: @@ -7590,13 +7777,7 @@ paths: content: application/json: schema: - type: object - properties: - has_admin_pin: - description: An administrator pin has been set to use restricted profiles. - type: boolean - required: - - has_admin_pin + $ref: '#/components/schemas/CfgProfile' '401': $ref: '#/components/responses/Err401Unauthorized' '403': @@ -7614,10 +7795,7 @@ paths: content: application/json: schema: - type: object - properties: - admin_pin: - $ref: '#/components/schemas/AdminPin' + $ref: '#/components/schemas/CfgProfileUpdate' required: true responses: '200': @@ -7625,7 +7803,7 @@ paths: content: application/json: schema: - $ref: '#/paths/~1cfg~1profile/get/responses/200/content/application~1json/schema' + $ref: '#/components/schemas/CfgProfile' '400': $ref: '#/components/responses/Err400BadRequest' '401': @@ -9261,9 +9439,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/BackupMetadata/properties/report' + $ref: '#/components/schemas/BackupReports' '400': $ref: '#/components/responses/Err400BadRequest' '401': @@ -9414,7 +9590,7 @@ paths: content: application/json: schema: - $ref: '#/paths/~1system~1backup~1restore/put/responses/200/content/application~1json/schema' + $ref: '#/components/schemas/BackupReports' '401': $ref: '#/components/responses/Err401Unauthorized' '403': @@ -10908,6 +11084,20 @@ components: required: false schema: type: string + media_id: + name: media_id + in: query + description: The media ID to browse. + required: false + schema: + type: string + media_type: + name: media_type + in: query + description: The media type to browse. + required: false + schema: + type: string merge: name: merge in: query @@ -10968,6 +11158,13 @@ components: required: false schema: type: boolean + stable_ids: + name: stable_ids + in: query + description: Use stable media identifiers. + required: false + schema: + type: boolean schemas: AccessTokenId: type: string @@ -11563,6 +11760,33 @@ components: maxLength: 36 description: | Entity identifier used in an integration driver (= available entities). + BackupReports: + type: array + items: + type: object + properties: + item: + type: string + enum: + - db + - integration_driver + - integration + - activity + - macro + - remote + - profile + - dock + - resource + available: + type: integer + format: int32 + ok: + type: integer + format: int32 + required: + - item + - available + - ok BackupSnapshot: type: object properties: @@ -11598,30 +11822,7 @@ components: version: $ref: '#/components/schemas/BackupMetadataVersion' report: - type: object - properties: - item: - type: string - enum: - - db - - integration_driver - - integration - - activity - - macro - - remote - - profile - - dock - - resource - available: - type: integer - format: int32 - ok: - type: integer - format: int32 - required: - - item - - available - - ok + $ref: '#/components/schemas/BackupReports/items' required: - id - creation_date @@ -11723,7 +11924,7 @@ components: power_saving: $ref: '#/components/schemas/CfgPowerSaving' profile: - $ref: '#/paths/~1cfg~1profile/get/responses/200/content/application~1json/schema' + $ref: '#/components/schemas/CfgProfile' software_update: $ref: '#/components/schemas/CfgSoftwareUpdate' sound: @@ -12028,6 +12229,19 @@ components: - wakeup_sensitivity - display_off_sec - standby_sec + CfgProfile: + type: object + properties: + has_admin_pin: + description: An administrator pin has been set to use restricted profiles. + type: boolean + required: + - has_admin_pin + CfgProfileUpdate: + type: object + properties: + admin_pin: + $ref: '#/components/schemas/AdminPin' CfgRemoteDevice: type: object properties: @@ -14501,6 +14715,189 @@ components: $ref: '#/components/schemas/EntityId' sequence: $ref: '#/components/schemas/CommandSequence' + MediaBrowseResponse: + type: object + properties: + media: + $ref: '#/components/schemas/BrowseMediaItem' + MediaSearchResponse: + type: array + items: + $ref: '#/components/schemas/SearchMediaItem' + MediaClass: + description: | + The media class is for browser/structure semantics. + It represents how a media item should be presented and organized in the media browser hierarchy. + + - Common values are defined as enum variants. Using these values is recommended, so the UI can show media type + specific information like icons. + - Integrations can use their custom values. Short identifiers like URIs are recommended. + type: string + maxLength: 255 + anyOf: + - enum: + - album + - app + - artist + - channel + - composer + - directory + - episode + - game + - genre + - image + - movie + - music + - playlist + - podcast + - radio + - season + - track + - tv_show + - url + - video + - {} + MediaContentType: + description: | + The media content type is for playback/content semantics. + It represents the type of the media content to play or that is currently playing. + + Notes: + - Common values are defined as enum variants. Using these values is recommended, so the UI can show media type + specific information like icons. + - Integrations can use their custom values. Short identifiers like URIs are recommended. + type: string + maxLength: 255 + anyOf: + - enum: + - album + - app + - apps + - artist + - channel + - channels + - composer + - episode + - game + - genre + - image + - movie + - music + - playlist + - podcast + - radio + - season + - track + - tv_show + - url + - video + - {} + MediaPlayerRepeatMode: + type: string + enum: + - 'OFF' + - ALL + - ONE + MediaPlayAction: + description: | + The media play action is used to specify how the media item should be played. + + Notes: + - Common values are defined as enum variants. Using these values is recommended, so the UI can show locale aware commands. + - Integrations can use their custom values, but they might not be available in the UI. + type: string + maxLength: 20 + anyOf: + - enum: + - PLAY_NOW + - PLAY_NEXT + - ADD_TO_QUEUE + - { } # extensible enum: integration can define additional values + default: PLAY_NOW + BrowseMediaItem: + type: object + properties: + media_id: + description: Unique identifier of the media item. Integration dependent. + type: string + maxLength: 255 + title: + description: Display name. + type: string + maxLength: 255 + subtitle: + description: Optional subtitle. + type: string + maxLength: 255 + artist: + description: Optional artist name. + type: string + maxLength: 255 + album: + description: Optional album name. + type: string + maxLength: 255 + media_class: + $ref: '#/components/schemas/MediaClass' + media_type: + $ref: '#/components/schemas/MediaContentType' + can_browse: + description: 'If `true`, the item can be browsed (is a container) by using `media_id` and `media_type`.' + type: boolean + default: false + can_play: + description: | + If `true`, the item can be played directly using the `play_media` command with `media_id` and `media_type`. + type: boolean + default: false + can_search: + description: | + If `true`, a search can be performed on the item using `search_media` with `media_id` and `media_type`. + type: boolean + default: false + thumbnail: + description: | + URL to download the media artwork, or a base64 encoded PNG or JPG image. + The preferred size is 480x480 pixels. + Use the following URI prefix to use a provided icon: `icon://uc:`, for example, `icon://uc:music`. + Please use a URL whenever possible. Encoded images should be as small as possible. + type: string + maxLength: 32768 + duration: + description: Duration in seconds. + type: integer + items: + description: | + Child items if this item is a container. Child items may not contain further child items (only one level + of nesting is supported). A new browse request must be sent for deeper levels. + type: array + items: + $ref: '#/components/schemas/BrowseMediaItem' + required: + - media_id + - title + SearchMediaItem: + description: | + A media item that was found by a search. Currently identical in shape to `BrowseMediaItem`. + Defined as a composition to allow search-specific fields (e.g. relevance score) to be added in the future + without a breaking change. + + A search media item may not contain any child `items`. + allOf: + - $ref: '#/components/schemas/BrowseMediaItem' + MediaQueueResponse: + description: "\U0001F6A7 initial draft, do not use!" + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/QueueItem' + current_index: + type: integer + description: Absolute index of the currently playing item within the full queue. + required: + - items MeasurementUnit: type: string enum: @@ -14686,6 +15083,45 @@ components: type: array items: $ref: '#/components/schemas/Profile' + QueueItem: + description: "\U0001F6A7 initial draft, do not use!" + type: object + properties: + queue_item_id: + type: string + description: Unique identifier for the item within this queue instance. + media_id: + type: string + description: Unique identifier for the item (opaque to Core-API). + title: + type: string + description: Display name. + media_class: + $ref: '#/components/schemas/MediaClass' + media_type: + $ref: '#/components/schemas/MediaContentType' + can_browse: + type: boolean + description: 'If `true`, the item can be browsed (is a container).' + can_play: + type: boolean + description: 'If `true`, the item can be played directly.' + thumbnail: + type: string + description: 'URL to download the image, or a base64 encoded data.' + artist: + type: string + description: Artist name. + album: + type: string + description: Album name. + duration: + type: integer + description: Duration in seconds. + required: + - queue_item_id + - media_id + - title Remote: description: | The remote entity executes a sequence of commands and at the end displays a user interface (similar to remote entity) @@ -16292,14 +16728,20 @@ components: application/json: schema: $ref: '#/components/schemas/ApiResponse' - Err409Conflict: - description: The request conflicts with the current state of the target resource. + Err404NotFound: + description: The resource does not exist or the URI is invalid. content: application/json: schema: $ref: '#/components/schemas/ApiResponse' - Err404NotFound: - description: The resource does not exist or the URI is invalid. + Err408RequestTimeout: + description: The request timed out. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + Err409Conflict: + description: The request conflicts with the current state of the target resource. content: application/json: schema: diff --git a/core-api/websocket/CHANGELOG.md b/core-api/websocket/CHANGELOG.md index fb1a2dd..15f0622 100644 --- a/core-api/websocket/CHANGELOG.md +++ b/core-api/websocket/CHANGELOG.md @@ -10,6 +10,10 @@ This section contains unreleased changes which will be part of an upcoming relea --- +## 0.35.1-beta +### Added +- Media browsing and searching ([feature-and-bug-tracker#70](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/70)). + ## 0.34.0-beta ### Added - New entity type `select`. diff --git a/core-api/websocket/UCR-core-asyncapi.yaml b/core-api/websocket/UCR-core-asyncapi.yaml index 6831eae..4430656 100644 --- a/core-api/websocket/UCR-core-asyncapi.yaml +++ b/core-api/websocket/UCR-core-asyncapi.yaml @@ -8,7 +8,7 @@ asyncapi: 2.2.0 id: 'urn:com:unfoldedcircle:core' info: title: Remote Two/3 WebSocket Core-API - version: '0.34.0-beta' + version: '0.35.1-beta' contact: name: API Support url: https://github.com/unfoldedcircle/core-api/issues @@ -50,7 +50,7 @@ info: ## 🚧 Missing Features - **This API is a work-in-progress and does not yet contain all functionality of the REST API.** + **This API does not yet contain all functionality of the REST API.** The following features will be continuously added (in no particular order): @@ -233,6 +233,8 @@ channels: - $ref: '#/components/messages/update_entity' - $ref: '#/components/messages/delete_entity' - $ref: '#/components/messages/delete_entities' + - $ref: '#/components/messages/browse_media' + - $ref: '#/components/messages/search_media' # --- Dock handling - $ref: '#/components/messages/get_dock_count' @@ -328,6 +330,8 @@ channels: - $ref: '#/components/messages/available_entities' - $ref: '#/components/messages/entity_commands' - $ref: '#/components/messages/entity' + - $ref: '#/components/messages/media_browse' + - $ref: '#/components/messages/media_search' # --- Dock handling - $ref: '#/components/messages/dock_count' @@ -1504,6 +1508,61 @@ components: x-response: $ref: '#/components/messages/result' + browse_media: + summary: πŸ” Browse media containers of a media_player entity. + description: | + Retrieves the content of a media container or the root if no `media_id` is specified. + Requires the `media_browser` feature on the target `media_player` entity. + + The `stable_ids` parameter is a hint that the client requires stable media identifiers in the returned + `media_id` and `media_type` fields. Certain media providers may not support stable identifiers, but the integration + might be able to use a workaround generating stable identifiers on the client side. + + Roon is such an example, which generates new media keys for every new browse or search request. Use cases like + "select an identifier of my favorite playlist, so I can still play it next month" aren't possible with changing + identifiers. As a workaround, the integration driver returns a path structure (`Library/Playlists/Favorite Songs`) + that is resolved when playing an item. + tags: + - name: entity + payload: + $ref: '#/components/schemas/browseMediaMsg' + x-response: + $ref: '#/components/messages/media_browse' + + search_media: + summary: πŸ” Search media items of a media_player entity. + description: | + Searches for media items within the active system of the `media_player` entity. + Requires the `media_search` feature on the target `media_player` entity. + + The `stable_ids` parameter is a hint that the client requires stable media identifiers in the returned + `media_id` and `media_type` fields. See `browse_media` for more information. + tags: + - name: entity + payload: + $ref: '#/components/schemas/searchMediaMsg' + x-response: + $ref: '#/components/messages/media_browse' + + media_browse: + summary: πŸ” Media browse result. + description: | + Response message for `browse_media` requests. + tags: + - name: entity + payload: + $ref: '#/components/schemas/mediaBrowseRespMsg' + + media_search: + summary: πŸ” Media search result. + description: | + Response message for `search_media` requests. + tags: + - name: entity + payload: + $ref: '#/components/schemas/mediaSearchRespMsg' + + # --- Dock handling get_dock_count: @@ -4673,6 +4732,129 @@ components: - msg - msg_data + browseMediaMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonReq' + - properties: + msg: + type: string + const: browse_media + msg_data: + type: object + properties: + entity_id: + description: The entity id of the media player to browse. + type: string + media_id: + description: Optional media content ID to restrict browsing. Integration dependent. + type: string + media_type: + description: | + Optional media content type to restrict browsing. + Either a defined MediaContentType value or an integration dependent identifier. + type: string + stable_ids: + description: Hint to the integration to return stable media IDs. + type: boolean + paging: + $ref: '#/components/schemas/paging' + required: + - entity_id + required: + - msg + - msg_data + + searchMediaMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonReq' + - properties: + msg: + type: string + const: search_media + msg_data: + type: object + properties: + entity_id: + description: The entity id of the media player to search + type: string + query: + description: The search string. + type: string + media_id: + description: Optional media container to scope the search. Integration dependent. + type: string + media_type: + description: | + Optional media content type to restrict browsing. + Either a defined MediaContentType value or an integration dependent identifier. + type: string + stable_ids: + description: Hint to the integration to return stable media IDs. + type: boolean + filter: + $ref: '#/components/schemas/MediaSearchFilter' + paging: + $ref: '#/components/schemas/paging' + required: + - entity_id + - query + required: + - msg + - msg_data + + mediaBrowseRespMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonResp' + - properties: + msg: + type: string + const: media_browse + msg_data: + $ref: '#/components/schemas/mediaBrowseResponse' + required: + - msg + - msg_data + + mediaSearchRespMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonResp' + - properties: + msg: + type: string + const: media_search + msg_data: + $ref: '#/components/schemas/mediaSearchResponse' + required: + - msg + - msg_data + + mediaBrowseResponse: + type: object + properties: + media: + $ref: '#/components/schemas/BrowseMediaItem' + pagination: + $ref: '#/components/schemas/pagination' + required: + - pagination + + mediaSearchResponse: + type: object + properties: + media: + type: array + items: + $ref: '#/components/schemas/SearchMediaItem' + pagination: + $ref: '#/components/schemas/pagination' + required: + - media + - pagination + # --- Dock handling getDockCountMsg: @@ -10730,3 +10912,188 @@ components: minimum: 1 required: - who + + # --- OpenAPI Media.yaml + MediaSearchFilter: + description: Media search filter + type: object + properties: + media_classes: + description: Optional list of media classes to filter the results. + type: array + items: + $ref: '#/components/schemas/MediaClass' + artist: + description: Optional artist filter. + type: string + album: + description: Optional album filter. + type: string + + MediaClass: + description: | + The media class is for browser/structure semantics. + It represents how a media item should be presented and organized in the media browser hierarchy. + + - Common values are defined as enum variants. Using these values is recommended, so the UI can show media type + specific information like icons. + - Integrations can use their custom values. Short identifiers like URIs are recommended. + type: string + maxLength: 255 + anyOf: + - enum: + - album + - app + - artist + - channel + - composer + - directory + - episode + - game + - genre + - image + - movie + - music + - playlist + - podcast + - radio + - season + - track + - tv_show + - url + - video + - { } # extensible enum: integration can define additional values + + MediaContentType: + description: | + The media content type is for playback/content semantics. + It represents the type of the media content to play or that is currently playing. + + Notes: + - Common values are defined as enum variants. Using these values is recommended, so the UI can show media type + specific information like icons. + - Integrations can use their custom values. Short identifiers like URIs are recommended. + type: string + maxLength: 255 + anyOf: + - enum: + - album + - app + - apps + - artist + - channel + - channels + - composer + - episode + - game + - genre + - image + - movie + - music + - playlist + - podcast + - radio + - season + - track + - tv_show + - url + - video + - { } # extensible enum: integration can define additional values + + MediaPlayerRepeatMode: + type: string + enum: + - OFF + - ALL + - ONE + + MediaPlayAction: + description: | + The media play action is used to specify how the media item should be played. + + Notes: + - Common values are defined as enum variants. Using these values is recommended, so the UI can show locale aware commands. + - Integrations can use their custom values, but they might not be available in the UI. + type: string + maxLength: 20 + anyOf: + - enum: + - PLAY_NOW + - PLAY_NEXT + - ADD_TO_QUEUE + - { } # extensible enum: integration can define additional values + default: PLAY_NOW + + BrowseMediaItem: + type: object + properties: + media_id: + description: Unique identifier of the media item. Integration dependent. + type: string + maxLength: 255 + title: + description: Display name. + type: string + maxLength: 255 + subtitle: + description: Optional subtitle. + type: string + maxLength: 255 + artist: + description: Optional artist name. + type: string + maxLength: 255 + album: + description: Optional album name. + type: string + maxLength: 255 + media_class: + $ref: '#/components/schemas/MediaClass' + media_type: + $ref: '#/components/schemas/MediaContentType' + can_browse: + description: If `true`, the item can be browsed (is a container) by using `media_id` and `media_type`. + type: boolean + default: false + can_play: + description: | + If `true`, the item can be played directly using the `play_media` command with `media_id` and `media_type`. + type: boolean + default: false + can_search: + description: | + If `true`, a search can be performed on the item using `search_media` with `media_id` and `media_type`. + type: boolean + default: false + thumbnail: + description: | + URL to download the media artwork, or a base64 encoded PNG or JPG image. + The preferred size is 480x480 pixels. + Use the following URI prefix to use a provided icon: `icon://uc:`, for example, `icon://uc:music`. + Please use a URL whenever possible. Encoded images should be as small as possible. + type: string + maxLength: 32768 + duration: + description: Duration in seconds. + type: integer + items: + description: | + Child items if this item is a container. Child items may not contain further child items (only one level + of nesting is supported). A new browse request must be sent for deeper levels. + type: array + items: + $ref: '#/components/schemas/BrowseMediaItem' + required: + - media_id + - title + + SearchMediaItem: + description: | + A media item that was found by a search. Currently identical in shape to `BrowseMediaItem`. + Defined as a composition to allow search-specific fields (e.g. relevance score) to be added in the future + without a breaking change. + + A search media item may not contain any child `items`. + allOf: + - $ref: '#/components/schemas/BrowseMediaItem' + diff --git a/doc/entities/entity_media_player.md b/doc/entities/entity_media_player.md index 027cba6..c154dd4 100644 --- a/doc/entities/entity_media_player.md +++ b/doc/entities/entity_media_player.md @@ -10,8 +10,13 @@ * [Commands](#commands) * [Simple Commands](#simple-commands) * [Command Name Patterns](#command-name-patterns) + * [Media Browsing](#media-browsing) + * [Media Searching](#media-searching) + * [Media Playback](#media-playback) + * [Media Play Action](#media-play-action) * [Events](#events) - * [Media types](#media-types) + * [Media Content Types](#media-content-types) + * [Media Classes](#media-classes) * [Media images](#media-images) * [Command examples](#command-examples) * [on](#on) @@ -30,56 +35,63 @@ --- A media player entity controls playback of media on a device. This could be an online music streaming service, a TV, -a stereo or a video player. +a stereo, or a video player. ## Features -Media-player features define the capabilities of the controlled device. For example the `dpad` feature will enable the +Media-player features define the capabilities of the controlled device. For example, the `dpad` feature will enable the four directional commands and the enter command. Furthermore, certain features control the appearance and functionality of the media-player UI on the Remote Two. -| Name | R | W | Description | -|-------------------|---|---|---------------------------------------------------------------------------------| -| on_off | βœ… | βœ… | The media player can be switched on and off. | -| toggle | ❌ | βœ… | The media player's power state can be toggled. | -| volume | βœ… | βœ… | The volume level can be set to a specific level. | -| volume_up_down | ❌ | βœ… | The volume can be adjusted up (louder) and down. | -| mute_toggle | ❌ | βœ… | The mute state can be toggled. | -| mute | βœ… | βœ… | The volume can be muted. | -| unmute | βœ… | βœ… | The volume can be un-muted. | -| play_pause | ❌ | βœ… | The player supports starting and pausing media playback. | -| stop | ❌ | βœ… | The player supports stopping media playback. | -| next | ❌ | βœ… | The player supports skipping to the next track. | -| previous | ❌ | βœ… | The player supports returning to the previous track. | -| fast_forward | ❌ | βœ… | The player supports fast-forwarding the current track. | -| rewind | ❌ | βœ… | The player supports rewinding the current track. | -| repeat | βœ… | βœ… | The current track or playlist can be repeated. | -| shuffle | βœ… | βœ… | The player supports random playback / shuffling the current playlist. | -| seek | ❌ | βœ… | The player supports seeking the playback position. | -| media_duration | βœ… | ❌ | The player announces the duration of the current media being played. | -| media_position | βœ… | ❌ | The player announces the current position of the media being played. | -| media_title | βœ… | ❌ | The player announces the media title. | -| media_artist | βœ… | ❌ | The player announces the media artist. | -| media_album | βœ… | ❌ | The player announces the media album if music is being played. | -| media_image_url | βœ… | ❌ | The player provides an image url of the media being played. | -| media_type | βœ… | ❌ | The player announces the type of media being played. | -| dpad | ❌ | βœ… | Directional pad navigation, provides up / down / left / right / enter commands. | -| numpad | ❌ | βœ… | Number pad, provides digit_0, .. , digit_9 commands. | -| home | ❌ | βœ… | Home navigation support with home & back commands. | -| menu | ❌ | βœ… | Menu navigation support with menu & back commands. | -| context_menu | ❌ | βœ… | Context menu (for example right clicking or long pressing an item). | -| guide | ❌ | βœ… | Program guide support with guide & back commands. | -| info | ❌ | βœ… | Information popup / menu support with info & back commands. | -| color_buttons | ❌ | βœ… | Color button support for red / green / yellow / blue function commands. | -| channel_switcher | ❌ | βœ… | Channel zapping support with channel up and down commands. | -| select_source | βœ… | βœ… | Media playback sources or inputs can be selected. | -| select_sound_mode | βœ… | βœ… | Sound modes can be selected, e.g. stereo or surround. | -| eject | ❌ | βœ… | The media can be ejected, e.g. a slot-in CD or USB stick. | -| open_close | ❌ | βœ… | The player supports opening and closing, e.g. a disc tray. | -| audio_track | ❌ | βœ… | The player supports selecting or switching the audio track. | -| subtitle | ❌ | βœ… | The player supports selecting or switching subtitles. | -| record | ❌ | βœ… | The player has recording capabilities with record, my_recordings, live commands | -| settings | ❌ | βœ… | The player supports a settings menu. | +| Name | R | W | Description | +|-------------------------|---|---|---------------------------------------------------------------------------------| +| on_off | βœ… | βœ… | The media player can be switched on and off. | +| toggle | ❌ | βœ… | The media player's power state can be toggled. | +| volume | βœ… | βœ… | The volume level can be set to a specific level. | +| volume_up_down | ❌ | βœ… | The volume can be adjusted up (louder) and down. | +| mute_toggle | ❌ | βœ… | The mute state can be toggled. | +| mute | βœ… | βœ… | The volume can be muted. | +| unmute | βœ… | βœ… | The volume can be un-muted. | +| play_pause | ❌ | βœ… | The player supports starting and pausing media playback. | +| stop | ❌ | βœ… | The player supports stopping media playback. | +| next | ❌ | βœ… | The player supports skipping to the next track. | +| previous | ❌ | βœ… | The player supports returning to the previous track. | +| fast_forward | ❌ | βœ… | The player supports fast-forwarding the current track. | +| rewind | ❌ | βœ… | The player supports rewinding the current track. | +| repeat | βœ… | βœ… | The current track or playlist can be repeated. | +| shuffle | βœ… | βœ… | The player supports random playback / shuffling the current playlist. | +| seek | ❌ | βœ… | The player supports seeking the playback position. | +| media_duration | βœ… | ❌ | The player announces the duration of the current media being played. | +| media_position | βœ… | ❌ | The player announces the current position of the media being played. | +| media_title | βœ… | ❌ | The player announces the media title. | +| media_artist | βœ… | ❌ | The player announces the media artist. | +| media_album | βœ… | ❌ | The player announces the media album if music is being played. | +| media_image_url | βœ… | ❌ | The player provides an image url of the media being played. | +| media_type | βœ… | ❌ | The player announces the content type of media being played. | +| dpad | ❌ | βœ… | Directional pad navigation, provides up / down / left / right / enter commands. | +| numpad | ❌ | βœ… | Number pad, provides digit_0, .. , digit_9 commands. | +| home | ❌ | βœ… | Home navigation support with home & back commands. | +| menu | ❌ | βœ… | Menu navigation support with menu & back commands. | +| context_menu | ❌ | βœ… | Context menu (for example right clicking or long pressing an item). | +| guide | ❌ | βœ… | Program guide support with guide & back commands. | +| info | ❌ | βœ… | Information popup / menu support with info & back commands. | +| color_buttons | ❌ | βœ… | Color button support for red / green / yellow / blue function commands. | +| channel_switcher | ❌ | βœ… | Channel zapping support with channel up and down commands. | +| select_source | βœ… | βœ… | Media playback sources or inputs can be selected. | +| select_sound_mode | βœ… | βœ… | Sound modes can be selected, e.g. stereo or surround. | +| eject | ❌ | βœ… | The media can be ejected, e.g. a slot-in CD or USB stick. | +| open_close | ❌ | βœ… | The player supports opening and closing, e.g. a disc tray. | +| audio_track | ❌ | βœ… | The player supports selecting or switching the audio track. | +| subtitle | ❌ | βœ… | The player supports selecting or switching subtitles. | +| record | ❌ | βœ… | The player has recording capabilities with record, my_recordings, live commands | +| settings | ❌ | βœ… | The player supports a settings menu. | +| play_media | ❌ | βœ… | The player supports playing a specific media item. | +| play_media_action | ❌ | βœ… | The player supports the play_media action parameter to either play or enqueue. | +| clear_playlist | ❌ | βœ… | The player allows clearing the active playlist. | +| browse_media | ❌ | βœ… | The player supports browsing media containers. | +| search_media | ❌ | βœ… | The player supports searching for media items. | +| search_media_classes | βœ… | ❌ | The player provides a list of media classes as filter for searches. | +| 🚧 search_media_filters | βœ… | ❌ | The player provides a list of input filters to limit searches. | - R: readable - βœ… Feature has a readable attribute to retrieve the current or available values. @@ -88,11 +100,11 @@ of the media-player UI on the Remote Two. - βœ… Feature has one or multiple commands to trigger an action or set a value. - ❌ No corresponding command(s), only the current value(s) of the feature can be read. -🚧 Planned features are playlist handling, media browsing and searching. +🚧 Planned features are: playlist handling. πŸ§‘β€πŸ’» Integration driver developers: - If certain features or commands are missing for your device, they can be defined with ["simple commands"](#simple-commands). -- If a feature doesn't fully match your device, for example it only has a `record` function but no `my_recordings` or +- If a feature doesn't fully match your device, for example, it only has a `record` function but no `my_recordings` or `live` functions, then the desired command(s) should also be defined as a ["simple command"](#simple-commands). - For the command name use the corresponding `cmd_id` in uppercase to keep any command related automations. (This would be `RECORD` for the record-only feature example). @@ -102,39 +114,45 @@ of the media-player UI on the Remote Two. Entity attributes are enabled by features and hold the current value of a feature or provide available options. Multiple features can act on the same attribute. -| Attribute | Features | Type | Values | Description | -|----------------------------|----------------------------|---------|---------------------|------------------------------------------------------------------------------------------| -| state | on_off | enum | [States](#states) | State of the media player, influenced by the play and power commands. | -| | toggle | | | | -| | play_pause, stop | | | | -| volume | volume | number | 0..100 | Current volume level. | -| | volume_up_down | | | | -| muted | mute_toggle | boolean | | Flag if the volume is muted. | -| | mute, unmute | | | | -| media_duration | media_duration | number | | Media duration in seconds. | -| media_position | media_position | number | | Current media position in seconds. | -| | play_pause, stop | | | | -| | fast_forward, rewind, seek | | | | -| media_position_updated_at | media_position | string | datetime (ISO 8601) | Optional timestamp when `media_position` was last updated. Requires integration support. | -| media_type | media_type | enum | _platform specific_ | The type of media being played. | -| media_image_url | media_image_url | string | | URL to retrieve the album art or an image representing what's being played. | -| | play_pause | | | | -| | next, previous | | | | -| media_title | media_title | string | | Currently playing media information. | -| | play_pause | | | | -| | next, previous | | | | -| media_artist | media_artist | string | | Currently playing media information. | -| | play_pause | | | | -| | next, previous | | | | -| media_album | media_album | string | | Currently playing media information. | -| | play_pause | | | | -| | next, previous | | | | -| repeat | repeat | enum | `OFF`, `ALL`, `ONE` | Current repeat mode. | -| shuffle | shuffle | boolean | | Shuffle mode on or off. | -| source | select_source | string | | Currently selected media or input source. | -| source_list | select_source | list | _text items_ | Available media or input sources. | -| sound_mode | select_sound_mode | string | | Currently selected sound mode. | -| sound_mode_list | select_sound_mode | list | _text items_ | Available sound modes. | +| Attribute | Features | Type | Values | Description | +|---------------------------|----------------------------|---------|-----------------------------------------|------------------------------------------------------------------------------------------------| +| state | on_off | enum | [States](#states) | State of the media player, influenced by the play and power commands. | +| | toggle | | | | +| | play_pause, stop | | | | +| volume | volume | number | 0..100 | Current volume level. | +| | volume_up_down | | | | +| muted | mute_toggle | boolean | | Flag if the volume is muted. | +| | mute, unmute | | | | +| media_duration | media_duration | number | | Media duration in seconds. | +| media_position | media_position | number | | Current media position in seconds. | +| | play_pause, stop | | | | +| | fast_forward, rewind, seek | | | | +| media_position_updated_at | media_position | string | datetime (ISO 8601) | Optional timestamp when `media_position` was last updated. Requires integration support. | +| media_id | media_type | string | | The content ID of media being played. | +| media_type | media_type | string | [Media type](#media-content-types) | The content type of media being played. | +| media_image_url | media_image_url | string | | URL to retrieve the album art or an image representing what's being played. | +| | play_pause | | | | +| | next, previous | | | | +| media_title | media_title | string | | Currently playing media information. | +| | play_pause | | | | +| | next, previous | | | | +| media_artist | media_artist | string | | Currently playing media information. | +| | play_pause | | | | +| | next, previous | | | | +| media_album | media_album | string | | Currently playing media information. | +| | play_pause | | | | +| | next, previous | | | | +| media_playlist | play_pause | string | | Title of Playlist currently playing. | +| | next, previous | | | | +| repeat | repeat | enum | `OFF`, `ALL`, `ONE` | Current repeat mode. | +| shuffle | shuffle | boolean | | Shuffle mode on or off. | +| source | select_source | string | | Currently selected media or input source. | +| source_list | select_source | list | _text items_ | Available media or input sources. | +| sound_mode | select_sound_mode | string | | Currently selected sound mode. | +| sound_mode_list | select_sound_mode | list | _text items_ | Available sound modes. | +| play_media_action | play_media_action | list | [Media Play Action](#media-play-action) | Supported media play actions. | +| search_media_classes | search_media_classes | list | [Media Classes](#media-classes) | List of media classes to use as a filter for `search_media`. Custom classes should be avoided. | +| 🚧 search_media_filters | search_media_filters | list | | | ### States @@ -180,67 +198,69 @@ Optional features of the media-player entity. The integration driver has to implement a handler for the `entity_command` message to process the following command requests in `msg_data.cmd_id`. -| cmd_id | Parameters | Description | -|-------------------|----------------|--------------------------------------------------------------------------| -| on | - | Switch on media player. | -| off | - | Switch off media player. | -| toggle | - | Toggle the current power state, either from on -> off or from off -> on. | -| play_pause | - | Toggle play / pause. | -| stop | - | Stop playback. | -| previous | - | Go back to previous track. | -| next | - | Skip to next track. | -| fast_forward | - | Fast forward current track. | -| rewind | - | Rewind current track. | -| seek | media_position | Seek to given position in current track. Position is given in seconds. | -| volume | volume | Set volume to given level. | -| volume_up | - | Increase volume. | -| volume_down | - | Decrease volume. | -| mute_toggle | - | Toggle mute state. | -| mute | - | Mute volume. | -| unmute | - | Unmute volume. | -| repeat | repeat | Repeat track or playlist. | -| shuffle | shuffle | Shuffle playlist or start random playback. | -| channel_up | - | Channel up. | -| channel_down | - | Channel down. | -| cursor_up | - | Directional pad up. | -| cursor_down | - | Directional pad down. | -| cursor_left | - | Directional pad left. | -| cursor_right | - | Directional pad right. | -| cursor_enter | - | Directional pad enter. | -| digit_0 - digit_9 | - | Number pad digits 0..9. | -| function_red | - | Function red. | -| function_green | - | Function green. | -| function_yellow | - | Function yellow. | -| function_blue | - | Function blue. | -| home | - | Home menu | -| menu | - | Menu | -| context_menu | - | Context menu | -| guide | - | Program guide menu. | -| info | - | Information menu / what's playing. | -| back | - | Back / exit function for menu navigation (to exit menu, guide, info). | -| select_source | source | Select an input source from the available sources. | -| select_sound_mode | mode | Select a sound mode from the available modes. | -| record | - | Start, stop or open recording menu (device dependant). | -| my_recordings | - | Open recordings. | -| live | - | Switch to live view. | -| eject | - | Eject media. | -| open_close | - | Open or close. | -| audio_track | - | Switch or select audio track. | -| subtitle | - | Switch or select subtitle. | -| settings | - | Settings menu | -| 🚧 search | | | +| cmd_id | Parameters | Description | +|-------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| on | - | Switch on media player. | +| off | - | Switch off media player. | +| toggle | - | Toggle the current power state, either from on -> off or from off -> on. | +| play_pause | - | Toggle play / pause. | +| stop | - | Stop playback. | +| previous | - | Go back to previous track. | +| next | - | Skip to next track. | +| fast_forward | - | Fast forward current track. | +| rewind | - | Rewind current track. | +| seek | media_position | Seek to given position in current track. Position is given in seconds. | +| volume | volume | Set volume to given level. | +| volume_up | - | Increase volume. | +| volume_down | - | Decrease volume. | +| mute_toggle | - | Toggle mute state. | +| mute | - | Mute volume. | +| unmute | - | Unmute volume. | +| repeat | repeat | Repeat track or playlist. | +| shuffle | shuffle | Shuffle playlist or start random playback. | +| channel_up | - | Channel up. | +| channel_down | - | Channel down. | +| cursor_up | - | Directional pad up. | +| cursor_down | - | Directional pad down. | +| cursor_left | - | Directional pad left. | +| cursor_right | - | Directional pad right. | +| cursor_enter | - | Directional pad enter. | +| digit_0 - digit_9 | - | Number pad digits 0..9. | +| function_red | - | Function red. | +| function_green | - | Function green. | +| function_yellow | - | Function yellow. | +| function_blue | - | Function blue. | +| home | - | Home menu | +| menu | - | Menu | +| context_menu | - | Context menu | +| guide | - | Program guide menu. | +| info | - | Information menu / what's playing. | +| back | - | Back / exit function for menu navigation (to exit menu, guide, info). | +| select_source | source | Select an input source from the available sources. | +| select_sound_mode | mode | Select a sound mode from the available modes. | +| record | - | Start, stop or open recording menu (device dependant). | +| my_recordings | - | Open recordings. | +| live | - | Switch to live view. | +| eject | - | Eject media. | +| open_close | - | Open or close. | +| audio_track | - | Switch or select audio track. | +| subtitle | - | Switch or select subtitle. | +| settings | - | Settings menu | +| play_media | media_id, media_type | Play or enqueue a media item. | +| | action | Optional `MediaPlayAction` enum if feature `play_media_action` is supported. Play now, next or enqueue. | +| clear_playlist | - | Removes all items from the playback queue. Current playback behaviour is integration-dependent (keep playing the current item or clearing everything). | ### Simple Commands -Additional commands can be enabled with the `simple_commands` options. A device exposing a media-player entity can -support all additional device commands, not covered with the standard features, as so-called "simple commands". +Additional commands can be enabled with the `simple_commands` option. A device exposing a media-player entity can +support all additional device commands, not covered with the standard features, as so-called "simple commands." These commands are comparable with the infrared remote-entity commands, where keys trigger an action on the device: - The command is fully defined by its name and doesn't have any further arguments or related attributes. - Command names must be upper case and may not contain spaces. - The upper case restriction makes them distinguishable from the pre-defined feature commands. - Only a subset of the 7-bit ASCII printable characters are allowed: `-/_.:+#*Β°@%()?` - - Maximum length is 20 characters. + - The maximum length is 20 characters. - Command name regex: `^[A-Z0-9\/_.:+#*Β°@%()?-]{1,20}$` - 🚧 Simple commands cannot be translated yet. In the web-configurator they show up as defined. @@ -256,6 +276,360 @@ This allows better integration into Remote Two, like default UI mappings and gro - `DIGIT_`: additional input digits besides the pre-defined numpad digits, e.g. `DIGIT_10`, `DIGIT_10+` - `ZONE_`: multi-room functions, e.g. `ZONE_A`, `ZONE_MULTIROOM` +### Media Browsing + +Media browsing uses a dedicated WebSocket message and is not part of the standard media-player entity commands. + +| msg | Parameters | Description | +|:-------------|:-----------|:-----------------------------------------------------------------| +| browse_media | entity_id | Browse media from a media-player entity. | +| | media_id | Optional media content ID to restrict browsing. | +| | media_type | Optional media content type to restrict browsing. | +| | stable_ids | Optional hint that the client requires stable media identifiers. | +| | paging | Optional paging object to limit returned items. | + +The `media_id` and `media_type` parameters are optional and can be used to restrict browsing to a specific media item +or content type. Both fields can be returned in the browse response on the root item and on every child item to describe +where the item lives in the provider’s hierarchy and how it can be addressed later. For example, for playback or further +browsing. + +Integrations must ensure that any `media_id` returned by a given entity is stable and uniquely identifies the item +within that entity. This ensures that it can be reused in subsequent `play_media`, `browse_media` and `search_media` +calls. Integrations may use either one of the pre-defined semantic content types (see [Media Content Types](#media-content-types)) +or provider-specific URIs for `media_type`, as long as values are stable and reused consistently. + +Certain media providers may not support stable identifiers, but the integration might be able to use a workaround +generating stable identifiers on the client side. The `stable_ids` parameter is a hint that the client really requires +stable media identifiers in the returned `media_id` and `media_type` fields, because the identifier won't be used right +now to play a media item but sometime in the future. +Roon is such an example, which generates new media keys for every new browse or search request. Use cases like +"select an identifier of my favorite playlist, so I can still play it next month" aren't possible with changing +identifiers. As a workaround, the integration driver can return a path structure (`Library/Playlists/Favorite Songs`) +or another sort of identifier that can be used to identify or resolve the item in the future. + +**Important:** When using `media_id` and `media_type` values in further calls, they must match the values returned in +the previous `media_browse` response exactly. If an integration returns an empty string (`""`) as value, that exact +empty string will be sent back on later calls and does not mean "no value." + +A field is only omitted from a request if it was not present in the response at all or if it was explicitly set to +`null` in the browse response. + +The optional `paging` object can be used to retrieve a specific page of media items and to limit result size: + +- `limit`: Maximum number of items to return per page. Default is 10. +- `page`: 1-based page index. Default is 1. + +Integrations may return fewer than `limit` items on the last page; clients must not assume the number of items equals +`limit`. + +#### Browse Response + +The `msg_data` object returned by the `media_browse` command contains the following fields: + +| Field | Type | Description | +|:------------|:--------|:------------------------------------------------------| +| media | object | `BrowseMediaItem` object. | +| pagination | object | Optional pagination information for this result page. | + +`BrowseMediaItem` object: + +| Field | Type | Description | +|:------------|:--------|:---------------------------------------------------------------------| +| title | string | Display title of the current media item/container. | +| subtitle | string | Optional subtitle of the current media item/container. | +| can_browse | boolean | Item can be used as a parent/container in a new `browse_media` call. | +| can_play | boolean | Item can be used as a target in `play_media`. | +| can_search | boolean | Item can be used as scope for a `search_media` call. | +| media_class | string | Media class of the current media item. | +| media_type | string | Provider-specific type or URI of the media item. | +| media_id | string | Provider-specific identifier of the media item. | +| artist | string | Optional artist name. | +| album | string | Optional album name. | +| thumbnail | string | Optional URL, icon URI or base64 image for artwork. | +| duration | number | Optional media duration in seconds. | +| items | array | Optional `BrowseMediaItem` child items in this container. | + +The `items` array is only present if the current item is a container and contains the same `BrowseMediaItem` objects. +Child items may not contain further child items (only one level of nesting is supported). + +- If a flag is omitted on an item, the default is `false`. +- Integrations should at least provide `can_browse` for items that can be used as `media_id` in follow-up `browse_media` + calls. +- For leaf items that can be used with `play_media`, integrations should set `can_play: true`. +- Items where all three flags are false should NOT be returned. + Otherwise, the item will have no associated command in the user interface. +- The `media_class` and `media_type` fields use the same semantics and values as documented in the [Media Classes](#media-classes) + and [Media Content Types](#media-content-types) sections. +- The `thumbnail` field can contain artwork for the current item: + - An icon URI to use a provided icon with the following prefix: `icon://uc:`, for example, `icon://uc:music`. + - A URL to download the media artwork. + - A base64 encoded PNG or JPG image. + - The preferred size is 480x480 pixels, either in JPG or PNG format. + - Please avoid using encoded images whenever possible. Payloads should be as small as possible. + - If no thumbnail is provided, a default icon will be used based on `media_class`. + +The `pagination` object in the response should contain: + +- `count`: Total number of items in the container (not just in the current page). If unknown, this field is not returned. +- `limit`: The effective page size used for this response. +- `page`: The 1-based page index used for this response. + +Please refer to the [Integration API specification](../../integration-api/UCR-integration-asyncapi.yaml) for the exact +definitions of all data structures. + +#### Home Assistant Integration Example with Spotify Media Player + +Example request to browse media at the root level: + +```json +{ + "kind": "req", + "id": 50, + "msg": "browse_media", + "msg_data": { + "entity_id": "media_player.spotify" + } +} +``` + +Example response: + +```json +{ + "kind": "resp", + "req_id": 50, + "msg": "media_browse", + "code": 200, + "msg_data": { + "media": { + "title": "Media Library", + "can_browse": true, + "media_class": "directory", + "media_id": "library", + "media_type": "spotify://library", + "items": [ + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_playlists", + "media_type": "spotify://current_user_playlists", + "title": "Playlists" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_followed_artists", + "media_type": "spotify://current_user_followed_artists", + "title": "Artists" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_saved_albums", + "media_type": "spotify://current_user_saved_albums", + "title": "Albums" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_saved_tracks", + "media_type": "spotify://current_user_saved_tracks", + "title": "Liked songs" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_saved_shows", + "media_type": "spotify://current_user_saved_shows", + "title": "Podcasts" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_recently_played", + "media_type": "spotify://current_user_recently_played", + "title": "Recently played" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_top_artists", + "media_type": "spotify://current_user_top_artists", + "title": "Top Artists" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "current_user_top_tracks", + "media_type": "spotify://current_user_top_tracks", + "title": "Top Tracks" + }, + { + "can_browse": true, + "media_class": "directory", + "media_id": "new_releases", + "media_type": "spotify://new_releases", + "title": "New Releases" + } + ] + }, + "pagination": { + "count": 9, + "limit": 9, + "page": 1 + } + } +} +``` + +A returned item can be used to continue browsing, if `"can_browse": true` is set. + +For example, to continue browsing the returned "Playlists" directory, send a new request with the returned `media_id` +and `media_type` values of that item: + +```json +{ + "kind": "req", + "id": 51, + "msg": "browse_media", + "msg_data": { + "entity_id": "media_player.spotify", + "media_id": "current_user_playlists", + "media_type": "spotify://current_user_playlists", + "paging": { + "limit": 10, + "page": 1 + } + } +} +``` + +### Media Searching + +🚧 This is a development feature and subject to change. There is no UI support yet. + +Media searching uses a dedicated WebSocket message and is not part of the standard media-player entity commands. + +Note: The filter object is not yet formally defined in the Integration-API. The fields below are examples of possible +filters and are not required. + +| msg | Parameters | Description | +|--------------|----------------------|------------------------------------------------------------------| +| search_media | entity_id | Search for media items in a media-player entity. | +| | query | Free-text search query. | +| | media_id | Optional media content ID to limit the search scope. | +| | media_type | Optional media content type to limit the search scope. | +| | stable_ids | Optional hint that the client requires stable media identifiers. | +| | filter.media_classes | Optional list of media classes to filter the results. | +| | filter.artist | 🚧 TBD if a set of well-known filters like `artist` | +| | filter.album | 🚧 and `album`, or dynamic | +| | paging | Optional paging object to limit returned items. | + +The `media_id` and `media_type` parameters behave the same as in `browse_media`: they can be used to restrict the +search to a specific container or content type, and integrations should propagate and reuse the values consistently. +Empty strings must be preserved and echoed back as-is. Only omit a field if it was not returned or was explicitly +`null`. + +- The optional `filter.media_classes` filter may only contain media classes that are supported by the integration. + - Supported media classes must be specified in the `search_media_classes` attribute by the integration. + - Only well-known [Media Classes](#media-classes) should be used without any custom media classes. +- See [Media Browsing](#media-browsing) for a description of the `stable_ids` parameter. +- 🚧 The `filter.artist` and `filter.album` fields are examples of possible future filters and are not yet formally defined. +- The optional `paging` object can be used to retrieve a specific page of media items and to limit result size. + +#### Search Response + +The `msg_data` object returned by the `media_search` command contains the following fields: + +| Field | Type | Description | +|:------------|:-------|:------------------------------------------------------| +| media | array | `SearchMediaItem` object. | +| pagination | object | Optional pagination information for this result page. | + +The `SearchMediaItem` object is identical to the `BrowseMediaItem` object, except it does not contain the `items` field: + +| Field | Type | Description | +|:------------|:--------|:---------------------------------------------------------------------| +| title | string | Display title of the current media item/container. | +| subtitle | string | Optional subtitle of the current media item/container. | +| can_browse | boolean | Item can be used as a parent/container in a new `browse_media` call. | +| can_play | boolean | Item can be used as a target in `play_media`. | +| can_search | boolean | Item can be used as scope for a `search_media` call. | +| media_class | string | Media class of the current media item. | +| media_type | string | Provider-specific type or URI of the media item. | +| media_id | string | Provider-specific identifier of the media item. | +| artist | string | Optional artist name. | +| album | string | Optional album name. | +| thumbnail | string | Optional URL, icon URI or base64 image for artwork. | +| duration | number | Optional media duration in seconds. | + +#### Example + +```json +{ + "kind": "req", + "id": 124, + "msg": "search_media", + "msg_data": { + "entity_id": "media-1", + "query": "live", + "filter": { + "media_classes": [ "artist", "album"], + "artist": "Tricky", + "album": "Pieces" + }, + "paging": { + "limit": 5, + "page": 1 + } + } +} +``` + +### Media Playback + +Starting a media playback is an optional feature and uses a standard `entity_command` request with the `play_media` +command identifier. + +The `media_id` and `media_type` parameters must be the exact same values as returned by `browse_media` or `search_media`. +The integration can support other identifiers for directly playing a specific media item in activity sequences or +UI commands. + +```json +{ + "kind": "req", + "id": 125, + "msg": "entity_command", + "msg_data": { + "entity_type": "media_player", + "entity_id": "media-1", + "cmd_id": "play_media", + "params": { + "media_id": "track-456", + "media_type": "track", + "action": "PLAY_NOW" + } + } +} +``` + +#### Media Play Action + +The `play_media_action` feature allows integrations to define the behavior of the `play_media` command in the optional +`action` parameter. Supported values are defined in the `MediaPlayAction` enum in the Integration-API: +The following actions are pre-defined in the Integration-API and are supported in the UI: + +- `PLAY_NOW`: Start playback immediately. +- `PLAY_NEXT`: Add to the queue after the current item. +- `ADD_TO_QUEUE`: Add to the end of the queue. + +The `PLAY_NOW` action is the default if no action is specified. + +Supported actions must be specified in the `play_media_action` attribute by the integration. + +Integrations may support additional actions that are not defined in the `MediaPlayAction` enum, for example, a +"radio mode." Please note that custom actions might not be exposed in the UI. + ### Events The `entity_change` event must be emitted by the integration driver if the state or an attribute of the media player @@ -277,7 +651,8 @@ The following attributes are supported: | media_image_url_small | Current media image url for the small image. | | media_image_url_medium | Current media image url for the medium size image. | | media_image_url_large | Current media image url for the large image. | -| media_type | Current media type. | +| media_type | Current media content type. | +| search_media_classes | Available search class filters. | | repeat | Current repeat mode. | | shuffle | Current shuffle mode. | | source | Selected source. | @@ -289,17 +664,75 @@ At least one attribute must be specified in the `entity_change` message. If the changed at the same time, for example if a new track starts playing, they may both be included in the same message. It's also valid to always send every attribute. -#### Media types - -The `media_type` attribute specifies the media being played: - -- `MUSIC` -- `RADIO` -- `TVSHOW` -- `MOVIE` -- `VIDEO` - -The integration may return other values, but the UI will most likely handle them as an "unknown media". +#### Media Content Types + +The media content type is for playback/content semantics. +It represents the type of the media content to play or that is **currently playing**. + +- Content type of current playing media. +- The play_media command uses `media_type` to tell the integration what semantic category of content is being requested. +- In media search, the search query includes an optional `media_type` used to constrain which content type to search in. + +The following media content types are pre-defined: + +- `album` +- `app` +- `apps` +- `artist` +- `channel` +- `channels` +- `composer` +- `episode` +- `game` +- `genre` +- `image` +- `movie` +- `music` +- `playlist` +- `podcast` +- `radio` +- `season` +- `track` +- `tv_show` +- `url` +- `video` + +The integration may return other values, but the UI will most likely handle them as an "unknown media." + +#### Media Classes + +The media class is for browser/structure semantics. +It represents how a media item should be presented and **organized in the media browser hierarchy**. + +Media classes are used by the frontend and media browser infrastructure to: +- Decide how to render items (folder vs leaf item, icons, metadata fields). +- Decide whether an item is expandable or directly playable. +- Filter results in search or browse operations. + +The following media classes are pre-defined: + +- `album` +- `app` +- `artist` +- `channel` +- `composer` +- `directory` +- `episode` +- `game` +- `genre` +- `image` +- `movie` +- `music` +- `playlist` +- `podcast` +- `radio` +- `season` +- `track` +- `tv_show` +- `url` +- `video` + +The integration may return other values, but the UI will most likely treat them as generic media without custom icons. #### Media images @@ -308,6 +741,7 @@ The integration may return other values, but the UI will most likely handle them - Images will automatically be resized to the required sizes. Either from the single image in `media_image_url` or from the individual sizes. - Images too large in pixel or file size will be rejected due to resource constraints on the remote. + - Images can be provided as a URL or as base64 encoded data. - Preferred image sizes in pixel: - Large, for full screen album art: 420 x 420 - Medium: 100 x 100 @@ -483,7 +917,4 @@ Specify a sound mode value contained in the `sound_mode_list` attribute array. ## Open issues and missing features - Define album art image limits -- Define media types - Playlist handling -- Media browsing -- Searching diff --git a/integration-api/README.md b/integration-api/README.md index fa6f87c..8d615e1 100644 --- a/integration-api/README.md +++ b/integration-api/README.md @@ -21,12 +21,13 @@ The provided Bash wrapper script [`create-html-docker.sh`](create-html-docker.sh ## API Versions -| Integration API | UCR Firmware | Core Simulator | -|-----------------|---------------|----------------| -| 0.13.0 | 2.8.0-beta | 0.64.4 | -| 0.12.1 | 2.2.4-beta | 0.58.3 | -| 0.12.0 | 1.9.3-beta | 0.48.0 | -| 0.11.0 | 1.9.2-beta | 0.47.0 | -| 0.10.0 | 1.7.12 | 0.43.0 | -| 0.9.0 | 1.7.2 | 0.41.0 | -| 0.8.0 | 1.5.2 | 0.39.7 | +| Integration API | UCR Firmware | Core Simulator | +|-----------------|--------------|----------------| +| 0.15.1 | | 0.70.4 | +| 0.13.0 | 2.8.0-beta | 0.64.4 | +| 0.12.1 | 2.2.4-beta | 0.58.3 | +| 0.12.0 | 1.9.3-beta | 0.48.0 | +| 0.11.0 | 1.9.2-beta | 0.47.0 | +| 0.10.0 | 1.7.12 | 0.43.0 | +| 0.9.0 | 1.7.2 | 0.41.0 | +| 0.8.0 | 1.5.2 | 0.39.7 | diff --git a/integration-api/UCR-integration-asyncapi.yaml b/integration-api/UCR-integration-asyncapi.yaml index 528b9c3..98d4990 100644 --- a/integration-api/UCR-integration-asyncapi.yaml +++ b/integration-api/UCR-integration-asyncapi.yaml @@ -8,7 +8,7 @@ asyncapi: 2.2.0 id: 'urn:com:unfoldedcircle:integration' info: title: Remote Two/3 WebSocket Integration API - version: '0.14.0-beta' + version: '0.15.1-beta' contact: name: API Support url: https://github.com/unfoldedcircle/core-api/issues @@ -123,6 +123,8 @@ channels: - $ref: '#/components/messages/unsubscribe_events' - $ref: '#/components/messages/get_entity_states' - $ref: '#/components/messages/entity_command' + - $ref: '#/components/messages/browse_media' + - $ref: '#/components/messages/search_media' # --- system events - $ref: '#/components/messages/enter_standby' - $ref: '#/components/messages/exit_standby' @@ -152,6 +154,8 @@ channels: # --- entity handling - $ref: '#/components/messages/entity_states' - $ref: '#/components/messages/available_entities' + - $ref: '#/components/messages/media_browse' + - $ref: '#/components/messages/media_search' # ------ events - $ref: '#/components/messages/entity_change' - $ref: '#/components/messages/entity_available' @@ -802,6 +806,59 @@ components: payload: $ref: '#/components/schemas/assistantEventMsg' + browse_media: + summary: πŸ‘· Browse media containers of a media_player entity. + description: | + Retrieves the content of a media container or the root if no `media_id` is specified. + Requires the `media_browser` feature on the target `media_player` entity. + + The `stable_ids` parameter is a hint that the client requires stable media identifiers in the returned + `media_id` and `media_type` fields. Certain media providers may not support stable identifiers, but the integration + might be able to use a workaround generating stable identifiers on the client side. + + Roon is such an example, which generates new media keys for every new browse or search request. Use cases like + "select an identifier of my favorite playlist, so I can still play it next month" aren't possible with changing + identifiers. As a workaround, the integration driver returns a path structure (`Library/Playlists/Favorite Songs`) + that is resolved when playing an item. + tags: + - name: entity + payload: + $ref: '#/components/schemas/browseMediaMsg' + x-response: + $ref: '#/components/messages/media_browse' + + search_media: + summary: πŸ‘· Search media items of a media_player entity. + description: | + Searches for media items within the active system of the `media_player` entity. + Requires the `media_search` feature on the target `media_player` entity. + + The `stable_ids` parameter is a hint that the client requires stable media identifiers in the returned + `media_id` and `media_type` fields. See `browse_media` for more information. + tags: + - name: entity + payload: + $ref: '#/components/schemas/searchMediaMsg' + x-response: + $ref: '#/components/messages/media_browse' + + media_browse: + summary: πŸ‘· Media browse result. + description: | + Response message for `browse_media` requests. + tags: + - name: entity + payload: + $ref: '#/components/schemas/mediaBrowseRespMsg' + + media_search: + summary: πŸ‘· Media search result. + description: | + Response message for `search_media` requests. + tags: + - name: entity + payload: + $ref: '#/components/schemas/mediaSearchRespMsg' # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ schemas: @@ -1388,6 +1445,129 @@ components: - msg - msg_data + browseMediaMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonReq' + - properties: + msg: + type: string + const: browse_media + msg_data: + type: object + properties: + entity_id: + description: The entity id of the media player to browse. + type: string + media_id: + description: Optional media content ID to restrict browsing. Integration dependent. + type: string + media_type: + description: | + Optional media content type to restrict browsing. + Either a defined MediaContentType value or an integration dependent identifier. + type: string + stable_ids: + description: Hint to the integration to return stable media IDs. + type: boolean + paging: + $ref: '#/components/schemas/Paging' + required: + - entity_id + required: + - msg + - msg_data + + searchMediaMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonReq' + - properties: + msg: + type: string + const: search_media + msg_data: + type: object + properties: + entity_id: + description: The entity id of the media player to search + type: string + query: + description: The search string. + type: string + media_id: + description: Optional media container to scope the search. Integration dependent. + type: string + media_type: + description: | + Optional media content type to restrict browsing. + Either a defined MediaContentType value or an integration dependent identifier. + type: string + stable_ids: + description: Hint to the integration to return stable media IDs. + type: boolean + filter: + $ref: '#/components/schemas/MediaSearchFilter' + paging: + $ref: '#/components/schemas/Paging' + required: + - entity_id + - query + required: + - msg + - msg_data + + mediaBrowseRespMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonResp' + - properties: + msg: + type: string + const: media_browse + msg_data: + $ref: '#/components/schemas/mediaBrowseResponse' + required: + - msg + - msg_data + + mediaSearchRespMsg: + type: object + allOf: + - $ref: '#/components/schemas/commonResp' + - properties: + msg: + type: string + const: media_search + msg_data: + $ref: '#/components/schemas/mediaSearchResponse' + required: + - msg + - msg_data + + mediaBrowseResponse: + type: object + properties: + media: + $ref: '#/components/schemas/BrowseMediaItem' + pagination: + $ref: '#/components/schemas/Pagination' + required: + - pagination + + mediaSearchResponse: + type: object + properties: + media: + type: array + items: + $ref: '#/components/schemas/SearchMediaItem' + pagination: + $ref: '#/components/schemas/Pagination' + required: + - media + - pagination + # ========================================================================= # Event message schemas # ========================================================================= @@ -2056,7 +2236,12 @@ components: - subtitle - record - settings - # TODO playlist handling, browsing, searching + - play_media + - play_media_action + - clear_playlist + - browse_media + - search_media + - search_media_classes options: type: object properties: @@ -2084,6 +2269,189 @@ components: minimum: 2 maximum: 100 + MediaSearchFilter: + description: Media search filter + type: object + properties: + media_classes: + description: Optional list of media classes to filter the results. + type: array + items: + $ref: '#/components/schemas/MediaClass' + artist: + description: Optional artist filter. + type: string + album: + description: Optional album filter. + type: string + + MediaClass: + description: | + The media class is for browser/structure semantics. + It represents how a media item should be presented and organized in the media browser hierarchy. + + - Common values are defined as enum variants. Using these values is recommended, so the UI can show media type + specific information like icons. + - Integrations can use their custom values. Short identifiers like URIs are recommended. + type: string + maxLength: 255 + anyOf: + - enum: + - album + - app + - artist + - channel + - composer + - directory + - episode + - game + - genre + - image + - movie + - music + - playlist + - podcast + - radio + - season + - track + - tv_show + - url + - video + - { } # extensible enum: integration can define additional values + + MediaContentType: + description: | + The media content type is for playback/content semantics. + It represents the type of the media content to play or that is currently playing. + + Notes: + - Common values are defined as enum variants. Using these values is recommended, so the UI can show media type + specific information like icons. + - Integrations can use their custom values. Short identifiers like URIs are recommended. + type: string + maxLength: 255 + anyOf: + - enum: + - album + - app + - apps + - artist + - channel + - channels + - composer + - episode + - game + - genre + - image + - movie + - music + - playlist + - podcast + - radio + - season + - track + - tv_show + - url + - video + - { } # extensible enum: integration can define additional values + + MediaPlayerRepeatMode: + type: string + enum: + - OFF + - ALL + - ONE + + MediaPlayAction: + description: | + The media play action is used to specify how the media item should be played. + + Notes: + - Common values are defined as enum variants. Using these values is recommended, so the UI can show locale aware commands. + - Integrations can use their custom values, but they might not be available in the UI. + type: string + maxLength: 20 + anyOf: + - enum: + - PLAY_NOW + - PLAY_NEXT + - ADD_TO_QUEUE + - { } # extensible enum: integration can define additional values + default: PLAY_NOW + + BrowseMediaItem: + type: object + properties: + media_id: + description: Unique identifier of the media item. Integration dependent. + type: string + maxLength: 255 + title: + description: Display name. + type: string + maxLength: 255 + subtitle: + description: Optional subtitle. + type: string + maxLength: 255 + artist: + description: Optional artist name. + type: string + maxLength: 255 + album: + description: Optional album name. + type: string + maxLength: 255 + media_class: + $ref: '#/components/schemas/MediaClass' + media_type: + $ref: '#/components/schemas/MediaContentType' + can_browse: + description: If `true`, the item can be browsed (is a container) by using `media_id` and `media_type`. + type: boolean + default: false + can_play: + description: | + If `true`, the item can be played directly using the `play_media` command with `media_id` and `media_type`. + type: boolean + default: false + can_search: + description: | + If `true`, a search can be performed on the item using `search_media` with `media_id` and `media_type`. + type: boolean + default: false + thumbnail: + description: | + URL to download the media artwork, or a base64 encoded PNG or JPG image. + The preferred size is 480x480 pixels. + Use the following URI prefix to use a provided icon: `icon://uc:`, for example, `icon://uc:music`. + Please avoid using encoded images whenever possible. Payloads should be as small as possible. + type: string + maxLength: 32768 + duration: + description: Duration in seconds. + type: integer + items: + description: | + Child items if this item is a container. Child items may not contain further child items (only one level + of nesting is supported). A new browse request must be sent for deeper levels. + type: array + items: + $ref: '#/components/schemas/BrowseMediaItem' + required: + - media_id + - title + + SearchMediaItem: + description: | + A media item that was found by a search. Currently identical in shape to `BrowseMediaItem`. + Defined as a composition to allow search-specific fields (e.g. relevance score) to be added in the future + without a breaking change. + + A search media item may not contain any child `items`. + allOf: + - $ref: '#/components/schemas/BrowseMediaItem' + remote: description: | A remote entity can send commands to a controllable device. @@ -3041,3 +3409,40 @@ components: enum: - PRONTO - HEX + + Paging: + description: Paging information for returned items. + type: object + properties: + limit: + description: Number of returned items per page. + type: integer + format: int32 + default: 10 + minimum: 1 + page: + description: Current page number. 1-based. + type: integer + format: int32 + default: 1 + minimum: 1 + + Pagination: + description: Returned pagination information. + type: object + properties: + count: + description: Total number of items. If unknown, this field is not returned. + type: integer + format: int32 + minimum: 0 + limit: + description: Number of returned items. + type: integer + format: int32 + minimum: 0 + page: + description: Current page number. 1-based. + type: integer + format: int32 + minimum: 1