From 41aa469bcf38aed88b5dfd22209645f17893d320 Mon Sep 17 00:00:00 2001 From: tolriq Date: Tue, 15 Jul 2025 11:02:06 +0200 Subject: [PATCH] Add new transcoding extension New extension to have proper transcoding solution in OpenAPI. --- .../en/docs/Endpoints/getTranscodeDecision.md | 140 ++++++++++++++++++ .../en/docs/Endpoints/getTranscodeStream.md | 28 ++++ content/en/docs/Extensions/transcoding.md | 21 +++ content/en/docs/Payloads/ClientInfo.md | 82 ++++++++++ content/en/docs/Payloads/CodecProfile.md | 27 ++++ content/en/docs/Payloads/DirectPlayProfile.md | 27 ++++ content/en/docs/Payloads/Limitation.md | 27 ++++ .../en/docs/Payloads/TranscodingProfile.md | 27 ++++ content/en/docs/Payloads/_index.md | 7 + content/en/docs/Responses/StreamDetails.md | 35 +++++ .../en/docs/Responses/transcodeDecision.md | 60 ++++++++ openapi/endpoints/getTranscodeDecision.json | 36 +++++ openapi/endpoints/getTranscodeStream.json | 65 ++++++++ openapi/openapi.json | 33 +++++ openapi/schemas/ClientInfo.json | 40 +++++ openapi/schemas/CodecProfile.json | 23 +++ openapi/schemas/DirectPlayProfile.json | 24 +++ openapi/schemas/Limitation.json | 43 ++++++ openapi/schemas/StreamDetails.json | 19 +++ openapi/schemas/TranscodeDecision.json | 35 +++++ .../schemas/TranscodeDecisionResponse.json | 33 +++++ openapi/schemas/Transcoding.json | 28 ++++ openapi/schemas/TranscodingProfile.json | 24 +++ 23 files changed, 884 insertions(+) create mode 100644 content/en/docs/Endpoints/getTranscodeDecision.md create mode 100644 content/en/docs/Endpoints/getTranscodeStream.md create mode 100644 content/en/docs/Extensions/transcoding.md create mode 100644 content/en/docs/Payloads/ClientInfo.md create mode 100644 content/en/docs/Payloads/CodecProfile.md create mode 100644 content/en/docs/Payloads/DirectPlayProfile.md create mode 100644 content/en/docs/Payloads/Limitation.md create mode 100644 content/en/docs/Payloads/TranscodingProfile.md create mode 100644 content/en/docs/Payloads/_index.md create mode 100644 content/en/docs/Responses/StreamDetails.md create mode 100644 content/en/docs/Responses/transcodeDecision.md create mode 100644 openapi/endpoints/getTranscodeDecision.json create mode 100644 openapi/endpoints/getTranscodeStream.json create mode 100644 openapi/schemas/ClientInfo.json create mode 100644 openapi/schemas/CodecProfile.json create mode 100644 openapi/schemas/DirectPlayProfile.json create mode 100644 openapi/schemas/Limitation.json create mode 100644 openapi/schemas/StreamDetails.json create mode 100644 openapi/schemas/TranscodeDecision.json create mode 100644 openapi/schemas/TranscodeDecisionResponse.json create mode 100644 openapi/schemas/Transcoding.json create mode 100644 openapi/schemas/TranscodingProfile.json diff --git a/content/en/docs/Endpoints/getTranscodeDecision.md b/content/en/docs/Endpoints/getTranscodeDecision.md new file mode 100644 index 0000000..f44441b --- /dev/null +++ b/content/en/docs/Endpoints/getTranscodeDecision.md @@ -0,0 +1,140 @@ +--- +title: "getTranscodeDecision" +linkTitle: "getTranscodeDecision" +categories: +- Transcoding +description: > + Returns a transcode decision for a given media file. +--- + +`http://your-server/rest/getTranscodeDecision` + +Returns a transcode decision for a given media file. This endpoint is used by clients to determine if a media file should be transcoded before streaming. The server will analyze the provided client information and return a decision. + +### Parameters + +| Parameter | Req. | OpenS. | Default | Comment | +| --- | --- | --- | --- | --- | +| `songId` | **Yes** | | | The ID of the song to be transcoded. | + +### Request Body + +The request body must be a JSON object containing the client's capabilities. See the [`ClientInfo`](../payloads/clientinfo) documentation for more details. + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="ClientInfo" lang="json">}} +{ + "name": "Pixel 8 Pro", + "platform": "Sonos", + "maxAudioBitrate": 512000, + "maxTranscodingAudioBitrate": 256000, + "directPlayProfiles": [ + { +"container": "mp3", +"audioCodec": "mp3", +"protocol": "http", +"maxAudioChannels": 2 + }, + { +"container": "flac", +"audioCodec": "flac", +"protocol": "*", +"maxAudioChannels": 2 + } + , + { +"container": "mp4", +"audioCodec": "flac,aac,alac", +"protocol": "*", +"maxAudioChannels": 2 + } + ], + "transcodingProfiles": [ + { +"container": "mp3", +"audioCodec": "mp3", +"protocol": "http", +"maxAudioChannels": 2 + }, + { +"container": "flac", +"audioCodec": "flac", +"protocol": "*", +"maxAudioChannels": 2 + } + ], + "codecProfiles": [ + { + "type": "AudioCodec", + "name": "mp3", + "limitations": [ + { "name": "audioBitrate", "comparison": "LessThanEqual", "value": "320000", "required": true } + ] + }, + { + "type": "AudioCodec", + "name": "flac", + "limitations": [ + { "name": "audioSamplerate", "comparison": "LessThanEqual", "value": "192000", "required": false }, + { "name": "audioChannels", "comparison": "LessThanEqual", "value": "2", "required": false } + ] + } + ] +} +{{< /tab >}} +{{< /tabpane >}} + +### Example + +{{< alert color="primary" >}} `http://your-server/rest/getTranscodeDecision.view?songId=123&u=demo&p=demo&v=1.16.1&c=AwesomeClientName&f=json` {{< /alert >}} + +### Result + +A [`subsonic-response`](../../responses/subsonic-response) element with a nested [`transcodeDecision`](../../responses/transcodedecision) element on success. + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "subsonic-response": { + "status": "ok", + "version": "1.16.1", + "type": "AwesomeServerName", + "serverVersion": "0.1.3 (tag)", + "openSubsonic": true, + "transcodeDecision": { + "canDirectPlay": false, + "canTranscode": true, + "transcodeReason": ["AudioCodecNotSupported"], + "errorReason": "", + "transcodeParams": "0001-0005-004",, + "sourceStream": { + "protocol": "http", + "container": "flac", + "codec": "flac", + "audioChannels": 6, + "audioBitrate": 3000000, + "audioProfile": "", + "audioSamplerate": 96000, + "audioBitdepth": 24 + }, + "transcodeStream": { + "protocol": "hls", + "container": "mp4", + "codec": "aac", + "audioChannels": 2, + "audioBitrate": 256000, + "audioProfile": "xHE-AAC", + "audioSamplerate": 48000, + "audioBitdepth": 16 + } + } + } +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `transcodeDecision` | [`transcodeDecision`](../../responses/transcodedecision) | **Yes** | | The transcode decision | diff --git a/content/en/docs/Endpoints/getTranscodeStream.md b/content/en/docs/Endpoints/getTranscodeStream.md new file mode 100644 index 0000000..bfe1c69 --- /dev/null +++ b/content/en/docs/Endpoints/getTranscodeStream.md @@ -0,0 +1,28 @@ +--- +title: "getTranscodeStream" +linkTitle: "getTranscodeStream" +categories: +- Transcoding +description: > + Returns a transcoded media stream. +--- + +`http://your-server/rest/getTranscodeStream` + +Returns a transcoded media stream. **Note:** Clients should not try to reconstruct the `transcodeParams`. Instead, they must use the `transcodeParams` provided in the response of the [`getTranscodeDecision`](../gettranscodedecision) endpoint. + +### Parameters + +| Parameter | Req. | OpenS. | Default | Comment | +| --- | --- | --- | --- | --- | +| `songId` | **Yes** | | | The ID of the track to transcode. | +| `offset` | No | | 0 | The time offset in seconds from which to start transcoding. | +| `transcodeParams` | **Yes** | | | Server-specific transcoding parameters, obtained from the `getTranscodeDecision` endpoint. The value is unique and already properly escaped. | + +### Example + +{{< alert color="primary" >}} `http://your-server/rest/getTranscodeStream.view?songId=123&offset=0¶meters=...&u=demo&p=demo&v=1.16.1&c=AwesomeClientName&f=json` {{< /alert >}} + +### Result + +The raw transcoded media stream. In case of an error, a standard HTTP error code is returned with a descriptive message. diff --git a/content/en/docs/Extensions/transcoding.md b/content/en/docs/Extensions/transcoding.md new file mode 100644 index 0000000..fdcbd01 --- /dev/null +++ b/content/en/docs/Extensions/transcoding.md @@ -0,0 +1,21 @@ +--- +title: "Transcoding" +linkTitle: "Transcoding" +OpenSubsonic: + - Extension +description: > + Adds support for clients to make transcoding decisions and retrieve transcoded media streams. +--- + +**OpenSubsonic version**: [1](../../opensubsonic-versions) + +**OpenSubsonic extension name**: `transcoding` (As returned by [`getOpenSubsonicExtensions`](../../endpoints/getopensubsonicextensions)) + +## Version 1 + +This extension provides a way for clients to get a server's decision on whether a media file should be transcoded and to retrieve the transcoded stream if necessary. + +This extension requires the following endpoints: + +- [`getTranscodeDecision`](../../endpoints/gettranscodedecision): Returns a transcode decision for a given media file. +- [`getTranscodeStream`](../../endpoints/gettranscodestream): Returns a transcoded media stream. diff --git a/content/en/docs/Payloads/ClientInfo.md b/content/en/docs/Payloads/ClientInfo.md new file mode 100644 index 0000000..7bf8f01 --- /dev/null +++ b/content/en/docs/Payloads/ClientInfo.md @@ -0,0 +1,82 @@ +--- +title: "ClientInfo" +linkTitle: "ClientInfo [OS]" +opensubsonic: +- Extension +description: > + Contains information about the client's capabilities. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "name": "Play:1", + "platform": "Sonos", + "maxAudioBitrate": 512000, + "maxTranscodingAudioBitrate": 256000, + "directPlayProfiles": [ + { +"container": "mp3", +"audioCodec": "mp3", +"protocol": "http", +"maxAudioChannels": 2 + }, + { +"container": "flac", +"audioCodec": "flac", +"protocol": "*", +"maxAudioChannels": 2 + } + , + { +"container": "mp4", +"audioCodec": "flac,aac,alac", +"protocol": "*", +"maxAudioChannels": 2 + } + ], + "transcodingProfiles": [ + { +"container": "mp3", +"audioCodec": "mp3", +"protocol": "http", +"maxAudioChannels": 2 + }, + { +"container": "flac", +"audioCodec": "flac", +"protocol": "*", +"maxAudioChannels": 2 + } + ], + "codecProfiles": [ + { + "type": "AudioCodec", + "name": "mp3", + "limitations": [ + { "name": "audioBitrate", "comparison": "LessThanEqual", "value": "320000", "required": true } + ] + }, + { + "type": "AudioCodec", + "name": "flac", + "limitations": [ + { "name": "audioSamplerate", "comparison": "LessThanEqual", "value": "192000", "required": false }, + { "name": "audioChannels", "comparison": "LessThanEqual", "value": "2", "required": false } + ] + } + ] +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `name` | string | **Yes** | **Yes** | The name of the client device. | +| `platform` | string | **Yes** | **Yes** | The platform of the client (e.g., Android, iOS). | +| `maxAudioBitrate` | integer | No | **Yes** | The maximum audio bitrate the client can handle. 0 or missing means no limitation. | +| `maxTranscodingAudioBitrate` | integer | No | **Yes** | The maximum audio bitrate for transcoded content. 0 or missing means no limitation. | +| `directPlayProfiles` | [DirectPlayProfile[]](../payloads/directplayprofile) | No | **Yes** | A list of profiles for direct playback. | +| `transcodingProfiles` | [TranscodingProfile[]](../payloads/transcodingprofile) | No | **Yes** | A list of profiles for transcoding. The server should evaluate these in the order they are listed, as a priority list. | +| `codecProfiles` | [CodecProfile[]](../payloads/codecprofile) | No | **Yes** | A list of codec-specific profiles. | diff --git a/content/en/docs/Payloads/CodecProfile.md b/content/en/docs/Payloads/CodecProfile.md new file mode 100644 index 0000000..f00727c --- /dev/null +++ b/content/en/docs/Payloads/CodecProfile.md @@ -0,0 +1,27 @@ +--- +title: "CodecProfile" +linkTitle: "CodecProfile [OS]" +opensubsonic: +- Extension +description: > + Defines a codec profile with optional limitations. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "type": "AudioCodec", + "name": "mp3", + "limitations": [ + { "name": "audioBitrate", "comparison": "LessThanEqual", "value": "320000", "required": true } + ] +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `type` | string | **Yes** | **Yes** | The type of codec profile. Currently only `AudioCodec` is supported. | +| `name` | string | **Yes** | **Yes** | The name of the codec (e.g., mp3, flac). Only a single value is accepted. | +| `limitations` | [Limitation[]](../payloads/limitation) | No | **Yes** | A list of limitations for this codec. | diff --git a/content/en/docs/Payloads/DirectPlayProfile.md b/content/en/docs/Payloads/DirectPlayProfile.md new file mode 100644 index 0000000..705bf80 --- /dev/null +++ b/content/en/docs/Payloads/DirectPlayProfile.md @@ -0,0 +1,27 @@ +--- +title: "DirectPlayProfile" +linkTitle: "DirectPlayProfile [OS]" +opensubsonic: +- Extension +description: > + Defines a direct play profile. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "container": "mp3", + "audioCodec": "mp3", + "protocol": "http", + "maxAudioChannels": 2 +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `container` | string | **Yes** | **Yes** | Comma-separated list of supported container format (e.g., mp3, flac, mp4) or `*` to not limit to a specific container. | +| `audioCodec` | string | **Yes** | **Yes** | Comma-separated list of supported audio codecs or `*` to not limit to specific codecs. | +| `protocol` | string | **Yes** | **Yes** | The streaming protocol. Can be `http`, `hls` or `*`. | +| `maxAudioChannels` | integer | No | **Yes** | The maximum number of audio channels supported. | diff --git a/content/en/docs/Payloads/Limitation.md b/content/en/docs/Payloads/Limitation.md new file mode 100644 index 0000000..81143de --- /dev/null +++ b/content/en/docs/Payloads/Limitation.md @@ -0,0 +1,27 @@ +--- +title: "Limitation" +linkTitle: "Limitation [OS]" +opensubsonic: +- Extension +description: > + Defines a limitation for a codec profile. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "name": "audioBitrate", + "comparison": "LessThanEqual", + "value": "320000", + "required": true +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `name` | string | **Yes** | | The name of the limitation. Can be `audioChannels`, `audioBitrate`, `audioProfile`, `audioSamplerate`, or `audioBitdepth`. | +| `comparison` | string | **Yes** | | The comparison operator. Can be `Equals`, `NotEquals`, `LessThanEqual`, `GreaterThanEqual`, `EqualsAny`, or `NotEqualsAny`. | +| `value` | string | **Yes** | | The value to compare against. For `EqualsAny` and `NotEqualsAny`, this should be a pipe-separated (`\|`) list of values (e.g., `44100\|48000`). | +| `required` | boolean | **Yes** | | Whether this limitation must be met. | diff --git a/content/en/docs/Payloads/TranscodingProfile.md b/content/en/docs/Payloads/TranscodingProfile.md new file mode 100644 index 0000000..8ef10ff --- /dev/null +++ b/content/en/docs/Payloads/TranscodingProfile.md @@ -0,0 +1,27 @@ +--- +title: "TranscodingProfile" +linkTitle: "TranscodingProfile [OS]" +opensubsonic: +- Extension +description: > + Defines a transcoding profile. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "container": "mp3", + "audioCodec": "mp3", + "protocol": "http", + "maxAudioChannels": 2 +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `container` | string | **Yes** | **Yes** | The container format (e.g., mp3, flac). Only a single explicit value is accepted (No `*`). | +| `audioCodec` | string | **Yes** | **Yes** | The target audio codec for transcoding. Only a single explicit value is accepted (No `*`). | +| `protocol` | string | **Yes** | **Yes** | The streaming protocol. Can be `http` or `hls`. Only a single explicit value is accepted (No `*`). | +| `maxAudioChannels` | integer | No | **Yes** | The maximum number of audio channels for the transcoded stream. | diff --git a/content/en/docs/Payloads/_index.md b/content/en/docs/Payloads/_index.md new file mode 100644 index 0000000..7020eef --- /dev/null +++ b/content/en/docs/Payloads/_index.md @@ -0,0 +1,7 @@ +--- +title: "Payloads" +linkTitle: "Payloads" +weight: 10 +description: > + Payloads documentation. +--- diff --git a/content/en/docs/Responses/StreamDetails.md b/content/en/docs/Responses/StreamDetails.md new file mode 100644 index 0000000..d4a5e98 --- /dev/null +++ b/content/en/docs/Responses/StreamDetails.md @@ -0,0 +1,35 @@ +--- +title: "StreamDetails" +linkTitle: "StreamDetails [OS]" +opensubsonic: +- Extension +description: > + Provides details about a media stream. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "protocol": "http", + "container": "flac", + "codec": "flac", + "audioChannels": 6, + "audioBitrate": 3000000, + "audioProfile": "", + "audioSamplerate": 96000, + "audioBitdepth": 24 +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `protocol` | string | **Yes** | **Yes** | The streaming protocol. Can be `http` or `hls`. | +| `container` | string | **Yes** | **Yes** | The container format. | +| `codec` | string | **Yes** | **Yes** | The audio codec. | +| `audioChannels` | integer | No | **Yes** | The number of audio channels. | +| `audioBitrate` | integer | No | **Yes** | The audio bitrate. | +| `audioProfile` | string | No | **Yes** | The audio profile. | +| `audioSamplerate` | integer | No | **Yes** | The audio sample rate. | +| `audioBitdepth` | integer | No | **Yes** | The audio bit depth. | diff --git a/content/en/docs/Responses/transcodeDecision.md b/content/en/docs/Responses/transcodeDecision.md new file mode 100644 index 0000000..465b4f7 --- /dev/null +++ b/content/en/docs/Responses/transcodeDecision.md @@ -0,0 +1,60 @@ +--- +title: "transcodeDecision" +linkTitle: "transcodeDecision [OS]" +opensubsonic: +- Extension +description: > + The response from the getTranscodeDecision endpoint. +--- + +{{< tabpane persist=false >}} +{{< tab header="**Example**:" disabled=true />}} +{{< tab header="OpenSubsonic" lang="json">}} +{ + "subsonic-response": { + "status": "ok", + "version": "1.16.1", + "type": "AwesomeServerName", + "serverVersion": "0.1.3 (tag)", + "openSubsonic": true, + "transcodeDecision": { + "canDirectPlay": false, + "canTranscode": true, + "transcodeReason": ["AudioCodecNotSupported"], + "errorReason": "", + "transcodeParams": "0001-0005-004", + "sourceStream": { + "protocol": "http", + "container": "flac", + "codec": "flac", + "audioChannels": 6, + "audioBitrate": 3000000, + "audioProfile": "", + "audioSamplerate": 96000, + "audioBitdepth": 24 + }, + "transcodeStream": { + "protocol": "hls", + "container": "mp4", + "codec": "aac", + "audioChannels": 2, + "audioBitrate": 256000, + "audioProfile": "xHE-AAC", + "audioSamplerate": 48000, + "audioBitdepth": 16 + } + } + } +} +{{< /tab >}} +{{< /tabpane >}} + +| Field | Type | Req. | OpenS. | Details | +| --- | --- | --- | --- | --- | +| `canDirectPlay` | boolean | **Yes** | **Yes** | Whether the media can be played directly by the client. | +| `canTranscode` | boolean | **Yes** | **Yes** | Whether the media can be transcoded by the server. | +| `transcodeReason` | string[] | No | **Yes** | An array of reasons why transcoding is necessary. The strings are server specific made for logging purpose, the server should return 1 string per direct play profile. | +| `errorReason` | string | No | **Yes** | A description of an error that occurred. | +| `transcodeParams` | string | No | **Yes** | An server internal value to be passed to the transcode endpoint, the value needs to be properly escaped to be reused as is in url parameter. See [`getTranscodeStream`](../endpoints/gettranscodestream). The client may not use the value instantly and should be kept valid by the server for a reasonable duration if stored in memory. | +| `sourceStream` | [StreamDetails](../responses/streamdetails) | No | **Yes** | Details about the source media stream. | +| `transcodeStream` | [StreamDetails](../responses/streamdetails) | No | **Yes** | Details about the target transcoded stream if `canTranscode` is true. | diff --git a/openapi/endpoints/getTranscodeDecision.json b/openapi/endpoints/getTranscodeDecision.json new file mode 100644 index 0000000..e4c6b82 --- /dev/null +++ b/openapi/endpoints/getTranscodeDecision.json @@ -0,0 +1,36 @@ +{ + "post": { + "summary": "Get Transcode Decision", + "description": "Returns a transcode decision for a given media file.", + "operationId": "getTranscodeDecision", + "tags": [ + "Transcoding" + ], + "parameters": [ + { + "name": "songId", + "in": "query", + "description": "The ID of the song.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/ClientInfo.json" + } + } + } + }, + "responses": { + "200": { + "$ref": "../schemas/TranscodeDecisionResponse.json" + } + } + } +} \ No newline at end of file diff --git a/openapi/endpoints/getTranscodeStream.json b/openapi/endpoints/getTranscodeStream.json new file mode 100644 index 0000000..b208039 --- /dev/null +++ b/openapi/endpoints/getTranscodeStream.json @@ -0,0 +1,65 @@ +{ + "get": { + "summary": "Get Transcoded Stream", + "description": "Returns a transcoded media stream. Clients should not try to reconstruct the `transcodeParams`. Instead, they must use the `transcodeParams` provided in the response of the `getTranscodeDecision` endpoint.", + "operationId": "getTranscodeStream", + "tags": [ + "Transcoding" + ], + "parameters": [ + { + "name": "songId", + "in": "query", + "description": "The ID of the track to transcode.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "offset", + "in": "query", + "description": "The time offset in seconds from which to start transcoding.", + "required": false, + "schema": { + "type": "integer", + "default": 0 + } + }, + { + "name": "transcodeParams", + "in": "query", + "description": "Server-specific transcoding parameters. This should be obtained from the `getTranscodeDecision` endpoint.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The transcoded media stream.", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad Request - The request was malformed or invalid." + }, + "401": { + "description": "Unauthorized - Authentication failed." + }, + "404": { + "description": "Not Found - The requested track does not exist." + }, + "500": { + "description": "Internal Server Error - The server encountered an error during transcoding." + } + } + } +} \ No newline at end of file diff --git a/openapi/openapi.json b/openapi/openapi.json index 67d8d43..bea029d 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -85,6 +85,9 @@ { "name": "Chat" }, + { + "name": "Transcoding" + }, { "name": "Extension" }, @@ -237,6 +240,12 @@ "/rest/getPlayQueueByIndex": { "$ref": "./endpoints/getPlayQueueByIndex.json" }, + "/rest/getTranscodeDecision": { + "$ref": "./endpoints/getTranscodeDecision.json" + }, + "/rest/getTranscodeStream": { + "$ref": "./endpoints/getTranscodeStream.json" + }, "/rest/getPodcastEpisode": { "$ref": "./endpoints/getPodcastEpisode.json" }, @@ -937,6 +946,30 @@ }, "GetTokenInfoResponse": { "$ref": "./endpoints/tokenInfo/GetTokenInfoResponse.json" + }, + "TranscodeDecision": { + "$ref": "./schemas/TranscodeDecision.json" + }, + "Transcoding": { + "$ref": "./schemas/Transcoding.json" + }, + "Limitation": { + "$ref": "./schemas/Limitation.json" + }, + "CodecProfile": { + "$ref": "./schemas/CodecProfile.json" + }, + "TranscodingProfile": { + "$ref": "./schemas/TranscodingProfile.json" + }, + "DirectPlayProfile": { + "$ref": "./schemas/DirectPlayProfile.json" + }, + "ClientInfo": { + "$ref": "./schemas/ClientInfo.json" + }, + "StreamDetails": { + "$ref": "./schemas/StreamDetails.json" } } } diff --git a/openapi/schemas/ClientInfo.json b/openapi/schemas/ClientInfo.json new file mode 100644 index 0000000..e7273f9 --- /dev/null +++ b/openapi/schemas/ClientInfo.json @@ -0,0 +1,40 @@ +{ + "title": "ClientInfo", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "maxAudioBitrate": { + "type": "integer" + }, + "maxTranscodingAudioBitrate": { + "type": "integer" + }, + "directPlayProfiles": { + "type": "array", + "items": { + "$ref": "./DirectPlayProfile.json" + } + }, + "transcodingProfiles": { + "type": "array", + "items": { + "$ref": "./TranscodingProfile.json" + } + }, + "codecProfiles": { + "type": "array", + "items": { + "$ref": "./CodecProfile.json" + } + } + }, + "required": [ + "name", + "platform" + ] +} \ No newline at end of file diff --git a/openapi/schemas/CodecProfile.json b/openapi/schemas/CodecProfile.json new file mode 100644 index 0000000..1d87b3c --- /dev/null +++ b/openapi/schemas/CodecProfile.json @@ -0,0 +1,23 @@ +{ + "title": "CodecProfile", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["AudioCodec"] + }, + "name": { + "type": "string" + }, + "limitations": { + "type": "array", + "items": { + "$ref": "./Limitation.json" + } + } + }, + "required": [ + "type", + "name" + ] +} \ No newline at end of file diff --git a/openapi/schemas/DirectPlayProfile.json b/openapi/schemas/DirectPlayProfile.json new file mode 100644 index 0000000..9a6efa2 --- /dev/null +++ b/openapi/schemas/DirectPlayProfile.json @@ -0,0 +1,24 @@ +{ + "title": "DirectPlayProfile", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "audioCodec": { + "type": "string" + }, + "protocol": { + "type": "string", + "enum": ["http", "hls", "*"] + }, + "maxAudioChannels": { + "type": "integer" + } + }, + "required": [ + "container", + "audioCodec", + "protocol" + ] +} \ No newline at end of file diff --git a/openapi/schemas/Limitation.json b/openapi/schemas/Limitation.json new file mode 100644 index 0000000..b81de50 --- /dev/null +++ b/openapi/schemas/Limitation.json @@ -0,0 +1,43 @@ +{ + "title": "Limitation", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the limitation.", + "enum": [ + "audioChannels", + "audioBitrate", + "audioProfile", + "audioSamplerate", + "audioBitdepth" + ] + }, + "comparison": { + "type": "string", + "description": "The comparison operator.", + "enum": [ + "Equals", + "NotEquals", + "LessThanEqual", + "GreaterThanEqual", + "EqualsAny", + "NotEqualsAny" + ] + }, + "value": { + "type": "string", + "description": "The value to compare against." + }, + "required": { + "type": "boolean", + "description": "Whether this limitation is required." + } + }, + "required": [ + "name", + "comparison", + "value", + "required" + ] +} \ No newline at end of file diff --git a/openapi/schemas/StreamDetails.json b/openapi/schemas/StreamDetails.json new file mode 100644 index 0000000..231dc9a --- /dev/null +++ b/openapi/schemas/StreamDetails.json @@ -0,0 +1,19 @@ +{ + "title": "StreamDetails", + "type": "object", + "properties": { + "protocol": { "type": "string", "enum": ["http", "hls"] }, + "container": { "type": "string" }, + "codec": { "type": "string" }, + "audioChannels": { "type": "integer" }, + "audioBitrate": { "type": "integer" }, + "audioProfile": { "type": "string" }, + "audioSamplerate": { "type": "integer" }, + "audioBitdepth": { "type": "integer" } + }, + "required": [ + "protocol", + "container", + "codec" + ] +} \ No newline at end of file diff --git a/openapi/schemas/TranscodeDecision.json b/openapi/schemas/TranscodeDecision.json new file mode 100644 index 0000000..58dc0d1 --- /dev/null +++ b/openapi/schemas/TranscodeDecision.json @@ -0,0 +1,35 @@ +{ + "title": "TranscodeDecision", + "type": "object", + "properties": { + "canDirectPlay": { + "type": "boolean" + }, + "canTranscode": { + "type": "boolean" + }, + "transcodeReason": { + "type": "array", + "items": { + "type": "string" + } + }, + "errorReason": { + "type": "string", + "default": "" + }, + "transcodeParams": { + "type": "string" + }, + "sourceStream": { + "$ref": "./StreamDetails.json" + }, + "transcodeStream": { + "$ref": "./StreamDetails.json" + } + }, + "required": [ + "canDirectPlay", + "canTranscode" + ] +} \ No newline at end of file diff --git a/openapi/schemas/TranscodeDecisionResponse.json b/openapi/schemas/TranscodeDecisionResponse.json new file mode 100644 index 0000000..8c2dd53 --- /dev/null +++ b/openapi/schemas/TranscodeDecisionResponse.json @@ -0,0 +1,33 @@ +{ + "description": "A transcode decision.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "type": "object", + "properties": { + "transcodeDecision": { + "$ref": "../schemas/TranscodeDecision.json" + } + } + } + ] + } + }, + "application/xml": { + "schema": { + "allOf": [ + { + "type": "object", + "properties": { + "transcodeDecision": { + "$ref": "../schemas/TranscodeDecision.json" + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/openapi/schemas/Transcoding.json b/openapi/schemas/Transcoding.json new file mode 100644 index 0000000..7c29496 --- /dev/null +++ b/openapi/schemas/Transcoding.json @@ -0,0 +1,28 @@ +{ + "title": "Transcoding", + "type": "object", + "description": "The transcoding extension", + "properties": { + "name": { + "type": "string", + "description": "The name of the extension.", + "enum": ["transcoding"] + }, + "version": { + "type": "string", + "description": "The version of the extension." + }, + "endpoints": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The endpoints provided by this extension." + } + }, + "required": [ + "name", + "version", + "endpoints" + ] +} \ No newline at end of file diff --git a/openapi/schemas/TranscodingProfile.json b/openapi/schemas/TranscodingProfile.json new file mode 100644 index 0000000..67e4a85 --- /dev/null +++ b/openapi/schemas/TranscodingProfile.json @@ -0,0 +1,24 @@ +{ + "title": "TranscodingProfile", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "audioCodec": { + "type": "string" + }, + "protocol": { + "type": "string", + "enum": ["http", "hls"] + }, + "maxAudioChannels": { + "type": "integer" + } + }, + "required": [ + "container", + "audioCodec", + "protocol" + ] +} \ No newline at end of file