google-cloud-text_to_speech 0.7.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,41 +0,0 @@
1
- # Copyright 2020 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
-
16
- require "googleauth"
17
-
18
- module Google
19
- module Cloud
20
- module TextToSpeech
21
- module V1beta1
22
- class Credentials < Google::Auth::Credentials
23
- SCOPE = [
24
- "https://www.googleapis.com/auth/cloud-platform"
25
- ].freeze
26
- PATH_ENV_VARS = %w(TEXTTOSPEECH_CREDENTIALS
27
- TEXTTOSPEECH_KEYFILE
28
- GOOGLE_CLOUD_CREDENTIALS
29
- GOOGLE_CLOUD_KEYFILE
30
- GCLOUD_KEYFILE)
31
- JSON_ENV_VARS = %w(TEXTTOSPEECH_CREDENTIALS_JSON
32
- TEXTTOSPEECH_KEYFILE_JSON
33
- GOOGLE_CLOUD_CREDENTIALS_JSON
34
- GOOGLE_CLOUD_KEYFILE_JSON
35
- GCLOUD_KEYFILE_JSON)
36
- DEFAULT_PATHS = ["~/.config/gcloud/application_default_credentials.json"]
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,206 +0,0 @@
1
- # Copyright 2020 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
-
16
- module Google
17
- module Cloud
18
- module TextToSpeech
19
- module V1beta1
20
- # The top-level message sent by the client for the `ListVoices` method.
21
- # @!attribute [rw] language_code
22
- # @return [String]
23
- # Optional. Recommended.
24
- # [BCP-47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tag. If
25
- # specified, the ListVoices call will only return voices that can be used to
26
- # synthesize this language_code. E.g. when specifying "en-NZ", you will get
27
- # supported "en-*" voices; when specifying "no", you will get supported
28
- # "no-*" (Norwegian) and "nb-*" (Norwegian Bokmal) voices; specifying "zh"
29
- # will also get supported "cmn-*" voices; specifying "zh-hk" will also get
30
- # supported "yue-*" voices.
31
- class ListVoicesRequest; end
32
-
33
- # The message returned to the client by the `ListVoices` method.
34
- # @!attribute [rw] voices
35
- # @return [Array<Google::Cloud::TextToSpeech::V1beta1::Voice>]
36
- # The list of voices.
37
- class ListVoicesResponse; end
38
-
39
- # Description of a voice supported by the TTS service.
40
- # @!attribute [rw] language_codes
41
- # @return [Array<String>]
42
- # The languages that this voice supports, expressed as
43
- # [BCP-47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tags (e.g.
44
- # "en-US", "es-419", "cmn-tw").
45
- # @!attribute [rw] name
46
- # @return [String]
47
- # The name of this voice. Each distinct voice has a unique name.
48
- # @!attribute [rw] ssml_gender
49
- # @return [Google::Cloud::TextToSpeech::V1beta1::SsmlVoiceGender]
50
- # The gender of this voice.
51
- # @!attribute [rw] natural_sample_rate_hertz
52
- # @return [Integer]
53
- # The natural sample rate (in hertz) for this voice.
54
- class Voice; end
55
-
56
- # The top-level message sent by the client for the `SynthesizeSpeech` method.
57
- # @!attribute [rw] input
58
- # @return [Google::Cloud::TextToSpeech::V1beta1::SynthesisInput]
59
- # Required. The Synthesizer requires either plain text or SSML as input.
60
- # @!attribute [rw] voice
61
- # @return [Google::Cloud::TextToSpeech::V1beta1::VoiceSelectionParams]
62
- # Required. The desired voice of the synthesized audio.
63
- # @!attribute [rw] audio_config
64
- # @return [Google::Cloud::TextToSpeech::V1beta1::AudioConfig]
65
- # Required. The configuration of the synthesized audio.
66
- class SynthesizeSpeechRequest; end
67
-
68
- # Contains text input to be synthesized. Either `text` or `ssml` must be
69
- # supplied. Supplying both or neither returns
70
- # {Google::Rpc::Code::INVALID_ARGUMENT}. The input size is limited to 5000
71
- # characters.
72
- # @!attribute [rw] text
73
- # @return [String]
74
- # The raw text to be synthesized.
75
- # @!attribute [rw] ssml
76
- # @return [String]
77
- # The SSML document to be synthesized. The SSML document must be valid
78
- # and well-formed. Otherwise the RPC will fail and return
79
- # {Google::Rpc::Code::INVALID_ARGUMENT}. For more information, see
80
- # [SSML](https://cloud.google.com/speech/text-to-speech/docs/ssml).
81
- class SynthesisInput; end
82
-
83
- # Description of which voice to use for a synthesis request.
84
- # @!attribute [rw] language_code
85
- # @return [String]
86
- # Required. The language (and potentially also the region) of the voice expressed as a
87
- # [BCP-47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tag, e.g.
88
- # "en-US". This should not include a script tag (e.g. use
89
- # "cmn-cn" rather than "cmn-Hant-cn"), because the script will be inferred
90
- # from the input provided in the SynthesisInput. The TTS service
91
- # will use this parameter to help choose an appropriate voice. Note that
92
- # the TTS service may choose a voice with a slightly different language code
93
- # than the one selected; it may substitute a different region
94
- # (e.g. using en-US rather than en-CA if there isn't a Canadian voice
95
- # available), or even a different language, e.g. using "nb" (Norwegian
96
- # Bokmal) instead of "no" (Norwegian)".
97
- # @!attribute [rw] name
98
- # @return [String]
99
- # The name of the voice. If not set, the service will choose a
100
- # voice based on the other parameters such as language_code and gender.
101
- # @!attribute [rw] ssml_gender
102
- # @return [Google::Cloud::TextToSpeech::V1beta1::SsmlVoiceGender]
103
- # The preferred gender of the voice. If not set, the service will
104
- # choose a voice based on the other parameters such as language_code and
105
- # name. Note that this is only a preference, not requirement; if a
106
- # voice of the appropriate gender is not available, the synthesizer should
107
- # substitute a voice with a different gender rather than failing the request.
108
- class VoiceSelectionParams; end
109
-
110
- # Description of audio data to be synthesized.
111
- # @!attribute [rw] audio_encoding
112
- # @return [Google::Cloud::TextToSpeech::V1beta1::AudioEncoding]
113
- # Required. The format of the audio byte stream.
114
- # @!attribute [rw] speaking_rate
115
- # @return [Float]
116
- # Optional. Input only. Speaking rate/speed, in the range [0.25, 4.0]. 1.0 is
117
- # the normal native speed supported by the specific voice. 2.0 is twice as
118
- # fast, and 0.5 is half as fast. If unset(0.0), defaults to the native 1.0
119
- # speed. Any other values < 0.25 or > 4.0 will return an error.
120
- # @!attribute [rw] pitch
121
- # @return [Float]
122
- # Optional. Input only. Speaking pitch, in the range [-20.0, 20.0]. 20 means
123
- # increase 20 semitones from the original pitch. -20 means decrease 20
124
- # semitones from the original pitch.
125
- # @!attribute [rw] volume_gain_db
126
- # @return [Float]
127
- # Optional. Input only. Volume gain (in dB) of the normal native volume
128
- # supported by the specific voice, in the range [-96.0, 16.0]. If unset, or
129
- # set to a value of 0.0 (dB), will play at normal native signal amplitude. A
130
- # value of -6.0 (dB) will play at approximately half the amplitude of the
131
- # normal native signal amplitude. A value of +6.0 (dB) will play at
132
- # approximately twice the amplitude of the normal native signal amplitude.
133
- # Strongly recommend not to exceed +10 (dB) as there's usually no effective
134
- # increase in loudness for any value greater than that.
135
- # @!attribute [rw] sample_rate_hertz
136
- # @return [Integer]
137
- # Optional. The synthesis sample rate (in hertz) for this audio. When this is
138
- # specified in SynthesizeSpeechRequest, if this is different from the voice's
139
- # natural sample rate, then the synthesizer will honor this request by
140
- # converting to the desired sample rate (which might result in worse audio
141
- # quality), unless the specified sample rate is not supported for the
142
- # encoding chosen, in which case it will fail the request and return
143
- # {Google::Rpc::Code::INVALID_ARGUMENT}.
144
- # @!attribute [rw] effects_profile_id
145
- # @return [Array<String>]
146
- # Optional. Input only. An identifier which selects 'audio effects' profiles
147
- # that are applied on (post synthesized) text to speech. Effects are applied
148
- # on top of each other in the order they are given. See
149
- # [audio
150
- # profiles](https://cloud.google.com/text-to-speech/docs/audio-profiles) for
151
- # current supported profile ids.
152
- class AudioConfig; end
153
-
154
- # The message returned to the client by the `SynthesizeSpeech` method.
155
- # @!attribute [rw] audio_content
156
- # @return [String]
157
- # The audio data bytes encoded as specified in the request, including the
158
- # header for encodings that are wrapped in containers (e.g. MP3, OGG_OPUS).
159
- # For LINEAR16 audio, we include the WAV header. Note: as
160
- # with all bytes fields, protobuffers use a pure binary representation,
161
- # whereas JSON representations use base64.
162
- class SynthesizeSpeechResponse; end
163
-
164
- # Configuration to set up audio encoder. The encoding determines the output
165
- # audio format that we'd like.
166
- module AudioEncoding
167
- # Not specified. Will return result {Google::Rpc::Code::INVALID_ARGUMENT}.
168
- AUDIO_ENCODING_UNSPECIFIED = 0
169
-
170
- # Uncompressed 16-bit signed little-endian samples (Linear PCM).
171
- # Audio content returned as LINEAR16 also contains a WAV header.
172
- LINEAR16 = 1
173
-
174
- # MP3 audio at 32kbps.
175
- MP3 = 2
176
-
177
- # Opus encoded audio wrapped in an ogg container. The result will be a
178
- # file which can be played natively on Android, and in browsers (at least
179
- # Chrome and Firefox). The quality of the encoding is considerably higher
180
- # than MP3 while using approximately the same bitrate.
181
- OGG_OPUS = 3
182
- end
183
-
184
- # Gender of the voice as described in
185
- # [SSML voice element](https://www.w3.org/TR/speech-synthesis11/#edef_voice).
186
- module SsmlVoiceGender
187
- # An unspecified gender.
188
- # In VoiceSelectionParams, this means that the client doesn't care which
189
- # gender the selected voice will have. In the Voice field of
190
- # ListVoicesResponse, this may mean that the voice doesn't fit any of the
191
- # other categories in this enum, or that the gender of the voice isn't known.
192
- SSML_VOICE_GENDER_UNSPECIFIED = 0
193
-
194
- # A male voice.
195
- MALE = 1
196
-
197
- # A female voice.
198
- FEMALE = 2
199
-
200
- # A gender-neutral voice.
201
- NEUTRAL = 3
202
- end
203
- end
204
- end
205
- end
206
- end
@@ -1,279 +0,0 @@
1
- # Copyright 2020 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- #
15
- # EDITING INSTRUCTIONS
16
- # This file was generated from the file
17
- # https://github.com/googleapis/googleapis/blob/master/google/cloud/texttospeech/v1beta1/cloud_tts.proto,
18
- # and updates to that file get reflected here through a refresh process.
19
- # For the short term, the refresh process will only be runnable by Google
20
- # engineers.
21
-
22
-
23
- require "json"
24
- require "pathname"
25
-
26
- require "google/gax"
27
-
28
- require "google/cloud/texttospeech/v1beta1/cloud_tts_pb"
29
- require "google/cloud/text_to_speech/v1beta1/credentials"
30
- require "google/cloud/text_to_speech/version"
31
-
32
- module Google
33
- module Cloud
34
- module TextToSpeech
35
- module V1beta1
36
- # Service that implements Google Cloud Text-to-Speech API.
37
- #
38
- # @!attribute [r] text_to_speech_stub
39
- # @return [Google::Cloud::TextToSpeech::V1beta1::TextToSpeech::Stub]
40
- class TextToSpeechClient
41
- # @private
42
- attr_reader :text_to_speech_stub
43
-
44
- # The default address of the service.
45
- SERVICE_ADDRESS = "texttospeech.googleapis.com".freeze
46
-
47
- # The default port of the service.
48
- DEFAULT_SERVICE_PORT = 443
49
-
50
- # The default set of gRPC interceptors.
51
- GRPC_INTERCEPTORS = []
52
-
53
- DEFAULT_TIMEOUT = 30
54
-
55
- # The scopes needed to make gRPC calls to all of the methods defined in
56
- # this service.
57
- ALL_SCOPES = [
58
- "https://www.googleapis.com/auth/cloud-platform"
59
- ].freeze
60
-
61
-
62
- # @param credentials [Google::Auth::Credentials, String, Hash, GRPC::Core::Channel, GRPC::Core::ChannelCredentials, Proc]
63
- # Provides the means for authenticating requests made by the client. This parameter can
64
- # be many types.
65
- # A `Google::Auth::Credentials` uses a the properties of its represented keyfile for
66
- # authenticating requests made by this client.
67
- # A `String` will be treated as the path to the keyfile to be used for the construction of
68
- # credentials for this client.
69
- # A `Hash` will be treated as the contents of a keyfile to be used for the construction of
70
- # credentials for this client.
71
- # A `GRPC::Core::Channel` will be used to make calls through.
72
- # A `GRPC::Core::ChannelCredentials` for the setting up the RPC client. The channel credentials
73
- # should already be composed with a `GRPC::Core::CallCredentials` object.
74
- # A `Proc` will be used as an updater_proc for the Grpc channel. The proc transforms the
75
- # metadata for requests, generally, to give OAuth credentials.
76
- # @param scopes [Array<String>]
77
- # The OAuth scopes for this service. This parameter is ignored if
78
- # an updater_proc is supplied.
79
- # @param client_config [Hash]
80
- # A Hash for call options for each method. See
81
- # Google::Gax#construct_settings for the structure of
82
- # this data. Falls back to the default config if not specified
83
- # or the specified config is missing data points.
84
- # @param timeout [Numeric]
85
- # The default timeout, in seconds, for calls made through this client.
86
- # @param metadata [Hash]
87
- # Default metadata to be sent with each request. This can be overridden on a per call basis.
88
- # @param service_address [String]
89
- # Override for the service hostname, or `nil` to leave as the default.
90
- # @param service_port [Integer]
91
- # Override for the service port, or `nil` to leave as the default.
92
- # @param exception_transformer [Proc]
93
- # An optional proc that intercepts any exceptions raised during an API call to inject
94
- # custom error handling.
95
- def initialize \
96
- credentials: nil,
97
- scopes: ALL_SCOPES,
98
- client_config: {},
99
- timeout: DEFAULT_TIMEOUT,
100
- metadata: nil,
101
- service_address: nil,
102
- service_port: nil,
103
- exception_transformer: nil,
104
- lib_name: nil,
105
- lib_version: ""
106
- # These require statements are intentionally placed here to initialize
107
- # the gRPC module only when it's required.
108
- # See https://github.com/googleapis/toolkit/issues/446
109
- require "google/gax/grpc"
110
- require "google/cloud/texttospeech/v1beta1/cloud_tts_services_pb"
111
-
112
- credentials ||= Google::Cloud::TextToSpeech::V1beta1::Credentials.default
113
-
114
- if credentials.is_a?(String) || credentials.is_a?(Hash)
115
- updater_proc = Google::Cloud::TextToSpeech::V1beta1::Credentials.new(credentials).updater_proc
116
- end
117
- if credentials.is_a?(GRPC::Core::Channel)
118
- channel = credentials
119
- end
120
- if credentials.is_a?(GRPC::Core::ChannelCredentials)
121
- chan_creds = credentials
122
- end
123
- if credentials.is_a?(Proc)
124
- updater_proc = credentials
125
- end
126
- if credentials.is_a?(Google::Auth::Credentials)
127
- updater_proc = credentials.updater_proc
128
- end
129
-
130
- package_version = Google::Cloud::TextToSpeech::VERSION
131
-
132
- google_api_client = "gl-ruby/#{RUBY_VERSION}"
133
- google_api_client << " #{lib_name}/#{lib_version}" if lib_name
134
- google_api_client << " gapic/#{package_version} gax/#{Google::Gax::VERSION}"
135
- google_api_client << " grpc/#{GRPC::VERSION}"
136
- google_api_client.freeze
137
-
138
- headers = { :"x-goog-api-client" => google_api_client }
139
- if credentials.respond_to?(:quota_project_id) && credentials.quota_project_id
140
- headers[:"x-goog-user-project"] = credentials.quota_project_id
141
- end
142
- headers.merge!(metadata) unless metadata.nil?
143
- client_config_file = Pathname.new(__dir__).join(
144
- "text_to_speech_client_config.json"
145
- )
146
- defaults = client_config_file.open do |f|
147
- Google::Gax.construct_settings(
148
- "google.cloud.texttospeech.v1beta1.TextToSpeech",
149
- JSON.parse(f.read),
150
- client_config,
151
- Google::Gax::Grpc::STATUS_CODE_NAMES,
152
- timeout,
153
- errors: Google::Gax::Grpc::API_ERRORS,
154
- metadata: headers
155
- )
156
- end
157
-
158
- # Allow overriding the service path/port in subclasses.
159
- service_path = service_address || self.class::SERVICE_ADDRESS
160
- port = service_port || self.class::DEFAULT_SERVICE_PORT
161
- interceptors = self.class::GRPC_INTERCEPTORS
162
- @text_to_speech_stub = Google::Gax::Grpc.create_stub(
163
- service_path,
164
- port,
165
- chan_creds: chan_creds,
166
- channel: channel,
167
- updater_proc: updater_proc,
168
- scopes: scopes,
169
- interceptors: interceptors,
170
- &Google::Cloud::TextToSpeech::V1beta1::TextToSpeech::Stub.method(:new)
171
- )
172
-
173
- @list_voices = Google::Gax.create_api_call(
174
- @text_to_speech_stub.method(:list_voices),
175
- defaults["list_voices"],
176
- exception_transformer: exception_transformer
177
- )
178
- @synthesize_speech = Google::Gax.create_api_call(
179
- @text_to_speech_stub.method(:synthesize_speech),
180
- defaults["synthesize_speech"],
181
- exception_transformer: exception_transformer
182
- )
183
- end
184
-
185
- # Service calls
186
-
187
- # Returns a list of Voice supported for synthesis.
188
- #
189
- # @param language_code [String]
190
- # Optional. Recommended.
191
- # [BCP-47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tag. If
192
- # specified, the ListVoices call will only return voices that can be used to
193
- # synthesize this language_code. E.g. when specifying "en-NZ", you will get
194
- # supported "en-*" voices; when specifying "no", you will get supported
195
- # "no-*" (Norwegian) and "nb-*" (Norwegian Bokmal) voices; specifying "zh"
196
- # will also get supported "cmn-*" voices; specifying "zh-hk" will also get
197
- # supported "yue-*" voices.
198
- # @param options [Google::Gax::CallOptions]
199
- # Overrides the default settings for this call, e.g, timeout,
200
- # retries, etc.
201
- # @yield [result, operation] Access the result along with the RPC operation
202
- # @yieldparam result [Google::Cloud::TextToSpeech::V1beta1::ListVoicesResponse]
203
- # @yieldparam operation [GRPC::ActiveCall::Operation]
204
- # @return [Google::Cloud::TextToSpeech::V1beta1::ListVoicesResponse]
205
- # @raise [Google::Gax::GaxError] if the RPC is aborted.
206
- # @example
207
- # require "google/cloud/text_to_speech"
208
- #
209
- # text_to_speech_client = Google::Cloud::TextToSpeech.new(version: :v1beta1)
210
- # response = text_to_speech_client.list_voices
211
-
212
- def list_voices \
213
- language_code: nil,
214
- options: nil,
215
- &block
216
- req = {
217
- language_code: language_code
218
- }.delete_if { |_, v| v.nil? }
219
- req = Google::Gax::to_proto(req, Google::Cloud::TextToSpeech::V1beta1::ListVoicesRequest)
220
- @list_voices.call(req, options, &block)
221
- end
222
-
223
- # Synthesizes speech synchronously: receive results after all text input
224
- # has been processed.
225
- #
226
- # @param input [Google::Cloud::TextToSpeech::V1beta1::SynthesisInput | Hash]
227
- # Required. The Synthesizer requires either plain text or SSML as input.
228
- # A hash of the same form as `Google::Cloud::TextToSpeech::V1beta1::SynthesisInput`
229
- # can also be provided.
230
- # @param voice [Google::Cloud::TextToSpeech::V1beta1::VoiceSelectionParams | Hash]
231
- # Required. The desired voice of the synthesized audio.
232
- # A hash of the same form as `Google::Cloud::TextToSpeech::V1beta1::VoiceSelectionParams`
233
- # can also be provided.
234
- # @param audio_config [Google::Cloud::TextToSpeech::V1beta1::AudioConfig | Hash]
235
- # Required. The configuration of the synthesized audio.
236
- # A hash of the same form as `Google::Cloud::TextToSpeech::V1beta1::AudioConfig`
237
- # can also be provided.
238
- # @param options [Google::Gax::CallOptions]
239
- # Overrides the default settings for this call, e.g, timeout,
240
- # retries, etc.
241
- # @yield [result, operation] Access the result along with the RPC operation
242
- # @yieldparam result [Google::Cloud::TextToSpeech::V1beta1::SynthesizeSpeechResponse]
243
- # @yieldparam operation [GRPC::ActiveCall::Operation]
244
- # @return [Google::Cloud::TextToSpeech::V1beta1::SynthesizeSpeechResponse]
245
- # @raise [Google::Gax::GaxError] if the RPC is aborted.
246
- # @example
247
- # require "google/cloud/text_to_speech"
248
- #
249
- # text_to_speech_client = Google::Cloud::TextToSpeech.new(version: :v1beta1)
250
- #
251
- # # TODO: Initialize `input`:
252
- # input = {}
253
- #
254
- # # TODO: Initialize `voice`:
255
- # voice = {}
256
- #
257
- # # TODO: Initialize `audio_config`:
258
- # audio_config = {}
259
- # response = text_to_speech_client.synthesize_speech(input, voice, audio_config)
260
-
261
- def synthesize_speech \
262
- input,
263
- voice,
264
- audio_config,
265
- options: nil,
266
- &block
267
- req = {
268
- input: input,
269
- voice: voice,
270
- audio_config: audio_config
271
- }.delete_if { |_, v| v.nil? }
272
- req = Google::Gax::to_proto(req, Google::Cloud::TextToSpeech::V1beta1::SynthesizeSpeechRequest)
273
- @synthesize_speech.call(req, options, &block)
274
- end
275
- end
276
- end
277
- end
278
- end
279
- end