IbmWatsonTtsApi::prepareTextToSpeechResponse()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
namespace GinoPane\PHPolyglot\API\Implementation\TTS\IbmWatson;
4
5
use GinoPane\NanoRest\Request\RequestContext;
6
use GinoPane\NanoRest\Response\JsonResponseContext;
7
use GinoPane\NanoRest\Response\DummyResponseContext;
8
use GinoPane\PHPolyglot\API\Response\TTS\TtsResponse;
9
use GinoPane\NanoRest\Response\ResponseContextAbstract;
10
use GinoPane\PHPolyglot\Supplemental\Language\Language;
11
use GinoPane\NanoRest\Exceptions\RequestContextException;
12
use GinoPane\PHPolyglot\API\Supplemental\TTS\TtsAudioFormat;
13
use GinoPane\PHPolyglot\Exception\InvalidVoiceCodeException;
14
use GinoPane\PHPolyglot\API\Implementation\TTS\TtsApiAbstract;
15
use GinoPane\PHPolyglot\Exception\BadResponseContextException;
16
use GinoPane\PHPolyglot\Exception\InvalidContentTypeException;
17
use GinoPane\PHPolyglot\Exception\InvalidAudioFormatCodeException;
18
use GinoPane\PHPolyglot\Exception\InvalidVoiceParametersException;
19
use GinoPane\PHPolyglot\Exception\InvalidAudioFormatParameterException;
20
use GinoPane\PHPolyglot\API\Implementation\TTS\IbmWatson\Voice\IbmWatsonVoicesTrait;
21
use GinoPane\PHPolyglot\API\Implementation\TTS\IbmWatson\AudioFormat\IbmWatsonAudioFormatsTrait;
22
23
/**
24
 * Class IbmWatsonTtsApi
25
 *
26
 * @link https://www.ibm.com/watson/services/text-to-speech/
27
 *
28
 * @author Sergey <Gino Pane> Karavay
29
 */
30
class IbmWatsonTtsApi extends TtsApiAbstract
31
{
32
    use IbmWatsonVoicesTrait;
33
    use IbmWatsonAudioFormatsTrait;
34
35
    /**
36
     * URL path for text-to-speech action
37
     */
38
    const TEXT_TO_SPEECH_API_PATH = 'synthesize';
39
40
    /**
41
     * Main API endpoint
42
     *
43
     * @var string
44
     */
45
    protected $apiEndpoint = 'https://stream.watsonplatform.net/text-to-speech/api/v1';
46
47
    /**
48
     * API username required for authorisation
49
     *
50
     * @var string
51
     */
52
    protected $username = '';
53
54
    /**
55
     * API password required for authorisation
56
     *
57
     * @var string
58
     */
59
    protected $password = '';
60
61
    /**
62
     * Mapping of properties to environment variables which must supply these properties
63
     *
64
     * @var array
65
     */
66
    protected $envProperties = [
67
        'username' => 'IBM_WATSON_TTS_API_USERNAME',
68
        'password' => 'IBM_WATSON_TTS_API_PASSWORD',
69
    ];
70
71
    /**
72
     * Create request context for text-to-speech request
73
     *
74
     * @param string         $text
75
     * @param Language       $language
76
     * @param TtsAudioFormat $format
77
     * @param array          $additionalData
78
     *
79
     * @return RequestContext
80
     *
81
     * @throws RequestContextException
82
     * @throws InvalidVoiceCodeException
83
     * @throws InvalidAudioFormatCodeException
84
     * @throws InvalidVoiceParametersException
85
     * @throws InvalidAudioFormatParameterException
86
     */
87
    protected function createTextToSpeechContext(
88
        string $text,
89
        Language $language,
90
        TtsAudioFormat $format,
91
        array $additionalData = []
92
    ): RequestContext {
93
        $requestContext = (new RequestContext(sprintf("%s/%s", $this->apiEndpoint, self::TEXT_TO_SPEECH_API_PATH)))
94
            ->setRequestParameters(
95
                array_filter(
96
                    [
97
                        'accept' => $this->getAcceptParameter($format, $additionalData),
98
                        'voice' => $this->getVoiceParameter($language, $additionalData)
99
                    ]
100
                )
101
            )
102
            ->setData(json_encode(['text' => $text]))
103
            ->setMethod(RequestContext::METHOD_POST)
104
            ->setContentType(RequestContext::CONTENT_TYPE_JSON)
105
            ->setResponseContextClass(DummyResponseContext::class);
106
107
        return $this->authorizeRequest($requestContext);
108
    }
109
110
    /**
111
     * Process response of text-to-speech request and prepare valid response
112
     *
113
     * @param ResponseContextAbstract $context
114
     *
115
     * @throws InvalidContentTypeException
116
     *
117
     * @return TtsResponse
118
     */
119
    protected function prepareTextToSpeechResponse(ResponseContextAbstract $context): TtsResponse
120
    {
121
        $response = new TtsResponse(
122
            $context->getRaw(),
123
            $this->getAudioFormatByContentTypeHeader($context->headers()),
124
            json_decode($context->getRequestContext()->getData(), true)['text']
125
        );
126
127
        return $response;
128
    }
129
130
    /**
131
     * @param RequestContext $context
132
     *
133
     * @return RequestContext
134
     * @throws RequestContextException
135
     */
136
    private function authorizeRequest(RequestContext $context): RequestContext
137
    {
138
        $context->setCurlOption(CURLOPT_USERPWD, "{$this->username}:{$this->password}");
139
140
        return $context;
141
    }
142
143
    /**
144
     * Filters ResponseContext from common HTTP errors
145
     *
146
     * @param ResponseContextAbstract $responseContext
147
     *
148
     * @throws BadResponseContextException
149
     */
150
    protected function processApiResponseContextErrors(ResponseContextAbstract $responseContext): void
151
    {
152
        if ($responseContext->hasHttpError()) {
153
            $jsonResponse = new JsonResponseContext($responseContext->getRaw());
154
155
            $responseArray = $jsonResponse->getArray();
156
157
            $this->filterIbmWatsonSpecificErrors($responseArray);
158
        }
159
160
        parent::processApiResponseContextErrors($responseContext);
161
    }
162
163
    /**
164
     * @param array $data
165
     *
166
     * @throws BadResponseContextException
167
     */
168
    private function filterIbmWatsonSpecificErrors(array $data)
169
    {
170
        if (isset($data['error']) && $error = $data['error']) {
171
            if (!empty($data['code_description'])) {
172
                $error = "{$data['code_description']}: $error";
173
            }
174
175
            throw new BadResponseContextException($error, (int)$data['code'] ?? 0);
176
        }
177
    }
178
}
179