yandex_speech_api 1.1.2 → 1.4.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.
@@ -2,51 +2,33 @@
2
2
  # frozen_string_literal: true
3
3
  module YandexSpeechApi
4
4
  module Connection # no-doc
5
- class << self
6
5
 
7
- ##
8
- # Sends :get request.
9
- #
10
- # @param [Hash] params
11
- # @option params [String] :text
12
- # @option params [Symbol] :format
13
- # @option params [String] :lang
14
- # @option params [String] :speaker
15
- # @option params [Symbol] :emotion
16
- # @option params [Float] :speed
17
- # @option params [String, Symbol] :key
18
- #
19
- # @exception ConnectionError
20
- # Raised when responce is not successful.
21
- #
22
- # @return [String]
23
- # Binary data.
24
-
25
- def send(**params)
26
- uri = URI.parse URL
27
- uri.query = URI.encode_www_form params
28
- response = Net::HTTP.get_response uri
29
-
30
- case response
31
- when Net::HTTPSuccess
32
- return response.body
33
- else
34
- raise ConnectionError.new response.code, response.message
35
- end
6
+ # @param [Hash] params
7
+ #
8
+ # @exception ConnectionError
9
+ # Raised when responce is not successful.
10
+ #
11
+ # @return [String]
12
+ # Response as binary data.
13
+
14
+ def self.send(**params)
15
+ uri = URI.parse(ENDPOINT_OFFICIAL)
16
+ uri.query = URI.encode_www_form params
17
+ response = Net::HTTP.get_response uri
18
+
19
+ unless response.is_a? Net::HTTPSuccess
20
+ raise ConnectionError.new(response.code, response.message)
36
21
  end
37
22
 
38
- private
39
-
40
- ##
41
- # YandexAPI endpoint.
23
+ return response.body
24
+ end
42
25
 
43
- URL = "https://tts.voicetech.yandex.net/generate"
44
- end # class << self
26
+ ENDPOINT_OFFICIAL = "https://tts.voicetech.yandex.net/generate"
27
+ #ENDPOINT_NON_OFFICIAL = "https://tts.voicetech.yandex.net/tts?&platform=web&application=translate"
45
28
 
46
- ##
47
- # Raised when connection failed.
29
+ private_constant :ENDPOINT_OFFICIAL
48
30
 
49
- class ConnectionError < YandexSpeechError
31
+ class ConnectionError < StandardError
50
32
  def initialize(code, message); super "Connection refused by remote server. Error code: '#{code}', Exception message: '#{message}'." end; end
51
33
  end # module Connection
52
34
  end # module YandexSpeechApi
@@ -2,29 +2,17 @@
2
2
  # frozen_string_literal: true
3
3
  module YandexSpeechApi
4
4
  # dependencies from core lib
5
- require 'rbconfig'
6
5
  require 'uri'
7
6
  require 'net/http'
8
7
 
9
- ##
10
- # Core class for all exceptions that can be raised in YandexSpeechApi lib..
11
-
12
- class YandexSpeechError < StandardError; end
13
-
14
- # project structure
15
-
16
8
  ##
17
9
  # loads *.rb files in requested order
18
10
 
19
- def self.load(**params)
11
+ def self.req(**params)
20
12
  params[:files].each do |f|
21
13
  require File.join(__dir__, params[:folder].to_s, f)
22
14
  end
23
- end
24
- private_class_method :load
25
-
26
- load folder: 'mp3_player',
27
- files: %w(base mac_mp3_player linux_mp3_player)
15
+ end; private_class_method :req
28
16
 
29
- load files: %w(helpers mp3_player key language format voice emotion speed connection speaker)
17
+ req files: %w(setters sounds connection speaker)
30
18
  end # module YandexSpeechApi
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ module YandexSpeechApi
4
+ module Setters
5
+
6
+ def voice=(new_voice)
7
+ voice = new_voice.to_s.downcase
8
+ unless allowed_voices.include? voice
9
+ raise ArgumentError, "Unexpected voice: #{new_voice}"
10
+ end
11
+ @voice = voice
12
+ end
13
+
14
+ def speed=(new_speed)
15
+ unless allowed_range.cover? new_speed
16
+ raise ArgumentError, "Incorrect speech speed value: #{new_speed}"
17
+ end
18
+ @speed = new_speed.round(2)
19
+ end
20
+
21
+ def emotion=(new_emotion)
22
+ emotion = new_emotion.to_s.downcase
23
+ unless allowed_emotions.include? emotion
24
+ raise ArgumentError, "Unexpected emotion: #{new_emotion}"
25
+ end
26
+
27
+ @emotion = emotion
28
+ end
29
+
30
+ def language=(new_language)
31
+ @language = allowed_languages[new_language.to_s.downcase]
32
+
33
+ unless language
34
+ raise ArgumentError, "Unexpected language: #{new_language}"
35
+ end
36
+ end
37
+
38
+ def format=(new_format)
39
+ format = new_format.to_s.downcase
40
+ unless allowed_formats.include? format
41
+ raise ArgumentError, "Unknown parameter: #{new_format}"
42
+ end
43
+
44
+ @format = format
45
+ end
46
+
47
+ def key=(new_key)
48
+ @key = new_key.to_s if new_key
49
+ end
50
+
51
+ private
52
+
53
+ def allowed_emotions
54
+ %w(evil good neutral)
55
+ end
56
+
57
+ def allowed_voices
58
+ %w(jane oksana alyss omazh zahar ermil)
59
+ end
60
+
61
+ def allowed_languages
62
+ {
63
+ "english" => 'en-US',
64
+ "russian" => 'ru‑RU',
65
+ "turkey" => 'tr-TR',
66
+ "ukrain" => 'uk-UK'
67
+ }
68
+ end
69
+
70
+ def allowed_formats
71
+ %w(mp3 wav opus)
72
+ end
73
+
74
+ def allowed_range
75
+ 0.1..3
76
+ end
77
+ end # module Validations
78
+ end # module YandexSpeechApi
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ module YandexSpeechApi
4
+ module Sounds
5
+
6
+ # @param [String] srs
7
+
8
+ def self.play(srs)
9
+ file = Tempfile.new 'yandex_speech_temp_file.mp3'
10
+ file << srs
11
+ file.close
12
+
13
+ case operation_system
14
+ when :linux
15
+ exec "mpg123 -q '#{file.path}'"
16
+ when :mac_os
17
+ exec "afplay '#{file.path}'"
18
+ end
19
+
20
+ file.unlink
21
+ end
22
+
23
+ # @return [Symbol]
24
+
25
+ def self.operation_system
26
+ case RbConfig::CONFIG['host_os']
27
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
28
+ :windows
29
+ when /darwin|mac os/
30
+ :mac_os
31
+ when /linux/
32
+ :linux
33
+ else
34
+ :unknown
35
+ end
36
+ end; private_class_method :operation_system
37
+ end # module Sounds
38
+ end # module YandexSpeechApi
@@ -4,49 +4,21 @@ module YandexSpeechApi
4
4
  class Speaker
5
5
  private
6
6
 
7
- #
8
- # @param [Proc] callback
9
- # Used to set object attributes throw {do...end} block.
10
- #
11
- # @example Block syntax
12
- # key = 'Your secret key'
13
- # message = "one two three. one two three. one two three four."
14
- #
15
- # speaker = YandexSpeechApi::Speaker.init do |s|
16
- # s.key = key
17
- # s.voice = :jane
18
- # s.language = :english
19
- # s.speed = :slow
20
- # s.emotion = :good
21
- # end
22
- # speaker.say message
23
- #
24
- # @return [Speaker]
25
-
26
- def initialize(settings, &callback)
27
- yield self if block_given?
28
-
29
- @key ||= Key.new settings[:key] || :unknown
30
- @voice ||= Voice.new settings[:voice] || :jane
31
- @speed ||= Speed.new settings[:speed] || :standard
32
- @emotion ||= Emotion.new settings[:emotion] || :neutral
33
- @language ||= Language.new settings[:language] || :english
34
- @format ||= Format.new settings[:format] || :mp3
35
- end
36
-
37
- ##
38
- # Prepares and sends request on Yandex Servers.
39
- #
40
7
  # @param [String] text
41
8
  # Something that should been said.
42
- # @param [Hash] params
43
- # Overrides object settings (only for this request)
44
9
  #
45
10
  # @return [String]
46
11
 
47
- def request(text, params = {})
48
- tmp_params = generate_params_for_request text, params
49
- Connection.send tmp_params
12
+ def request(text)
13
+ params = generate_params text
14
+
15
+ response = if key
16
+ Connection.send params
17
+ else
18
+ raise StandardError, "Request cannot been completed without key."
19
+ end
20
+
21
+ return response
50
22
  end
51
23
 
52
24
  ##
@@ -54,50 +26,37 @@ module YandexSpeechApi
54
26
  #
55
27
  # @param [String] text
56
28
  #
57
- # @param [Hash] params ({})
58
- # @option params [Format] :format (nil).
59
- # @option params [Language] :language (nil).
60
- # @option params [Voice] :voice (nil).
61
- # @option params [Key] :key (nil).
62
- # @option params [Emotion] :emotion (nil).
63
- # @option params [Speed] :speed (nil).
64
- #
65
29
  # @exception TextTooBig
66
30
  # Raised when param +text+ too big (>2000 symbols)
67
31
  #
68
32
  # @return [Hash]
69
33
 
70
- def generate_params_for_request(text, params = {})
71
- tmp_text = text.dup.encode(Encoding::UTF_8, invalid: :replace,
72
- undef: :replace, replace: '')
73
- raise TextTooBig if tmp_text.length > 2000
34
+ def generate_params(text)
35
+ txt = text.dup.encode(Encoding::UTF_8, invalid: :replace,
36
+ undef: :replace, replace: '')
37
+
38
+ if txt.length > 2000
39
+ raise TextTooBig, 'Text too big. Only 2000 symbols per request are allowed.'
40
+ end
74
41
 
75
42
  tmp = {
76
- text: tmp_text,
77
- format: params[:format] ? params[:format].type : format.type,
78
- lang: params[:language] ? params[:language].code : language.code,
79
- speaker: params[:voice] ? params[:voice].name : voice.name,
80
- key: params[:key] ? params[:key].get : key.get,
81
- emotion: params[:emotion] ? params[:emotion].type : emotion.type,
82
- speed: params[:speed] ? params[:speed].value : speed.value
43
+ text: txt,
44
+ format: format,
45
+ lang: language,
46
+ speaker: voice,
47
+ key: key,
48
+ emotion: emotion,
49
+ speed: speed
83
50
  }
84
51
 
85
52
  return tmp
86
53
  end
87
54
 
88
- def generate_path # no-doc
89
- dir_path = File.join ENV['HOME'], 'downloads'
90
-
91
- Dir.mkdir(dir_path) unless Dir.exist?(dir_path)
55
+ def generate_path
56
+ dir_path = Dir.pwd
92
57
  filename = "yandex_speech_audio_#{Time.now.strftime "%Y-%m-%d_%H-%M-%S"}"
93
58
 
94
59
  return File.join(dir_path, filename)
95
60
  end
96
-
97
- ##
98
- # Raised when user tries to #say too big text.
99
-
100
- class TextTooBig < YandexSpeechError
101
- def initialize; super 'Text message length limited by 2000 symbols per request' end; end
102
61
  end # class Speaker
103
62
  end # module YandexSpeechApi
@@ -2,10 +2,9 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
-
6
5
  module YandexSpeechApi
7
6
  module MP3_Player
8
- describe Base do
7
+ describe Base do # no-doc
9
8
  context '#play' do
10
9
  it 'raises an exception when file not found' do
11
10
  expect{described_class.new.play('lalalala.')}
@@ -19,9 +19,10 @@ module YandexSpeechApi
19
19
  .each(&:unlink)
20
20
  end
21
21
 
22
+
22
23
  context "Speaker#save_to_file" do
23
24
  it 'saves audio-file' do
24
- speaker = Speaker.init key: "xxxxx-xxxxx-xxxxx-xxxxx"
25
+ speaker = Speaker.new key: "xxxxx-xxxxx-xxxxx-xxxxx"
25
26
 
26
27
  path = speaker.save_to_file("Не будите спящего кота.",
27
28
  "spec/tmp/not_today")
@@ -30,9 +31,9 @@ module YandexSpeechApi
30
31
  end
31
32
 
32
33
  it 'works fine when global key is set' do
33
- Key.global_key = "xxxxx-xxxxx-xxxxx-xxxxx"
34
+ described_class.key = "xxxxx-xxxxx-xxxxx-xxxxx"
34
35
 
35
- bobby = Speaker.init
36
+ bobby = Speaker.new
36
37
  path_to_bob_file =
37
38
  bobby.save_to_file "Не будите спящего кота.", "spec/tmp/bobby"
38
39
 
@@ -43,57 +44,38 @@ module YandexSpeechApi
43
44
  expect_any_instance_of(Speaker).to receive(:generate_path)
44
45
  .and_return(File.join(TEMP_PATH, "bobby.mp3"))
45
46
 
46
- bobby = Speaker.init(key: "xxxx")
47
+ bobby = Speaker.new(key: "xxxx")
47
48
  path_to_bob_file = bobby.save_to_file "Не будите спящего кота."
48
49
 
49
50
  expect(File.exist?(path_to_bob_file)).to be_truthy
50
51
  end
51
52
 
52
53
  it 'sets correct params for object when block syntax is used' do
53
- jane = Speaker.init do |j|
54
+ jane = Speaker.new do |j|
54
55
  j.voice = :jane
55
56
  j.language = :english
56
- j.speed = :slow
57
+ j.speed = 1.2
57
58
  j.emotion = :good
58
59
  j.format = :opus
59
60
  end
60
61
 
61
- expect(jane.voice.name).to be_eql :jane
62
- expect(jane.language.code).to be_eql 'en-US'
63
- expect(jane.speed.value).to be_eql 0.5
64
- expect(jane.emotion.type).to be_eql :good
65
- expect(jane.format.type).to be_eql :opus
62
+ expect(jane.voice).to be_eql "jane"
63
+ expect(jane.language).to be_eql 'en-US'
64
+ expect(jane.speed).to be_eql 1.2
65
+ expect(jane.emotion).to be_eql "good"
66
+ expect(jane.format).to be_eql "opus"
66
67
  end
67
68
 
68
69
  it 'sets correct params for object when hash syntax is used' do
69
- jane = Speaker.init(voice: :jane, language: :english,
70
- speed: :slow, emotion: :good)
71
-
72
- expect(jane.voice.name).to be_eql :jane
73
- expect(jane.language.code).to be_eql 'en-US'
74
- expect(jane.speed.value).to be_eql 0.5
75
- expect(jane.emotion.type).to be_eql :good
76
- end
77
- end # context "Speaker#save_to_file"
78
-
79
- # ----------------------------------------------------
80
-
81
- context "Speaker#say" do
82
- it 'calls mp3 player' do
83
- expect_any_instance_of(MP3_Player::Base).to receive(:play)
84
-
85
- speaker = Speaker.init key: "xxxxx-xxxxx-xxxxx-xxxxx"
86
- speaker.say("Не будите спящего кота.")
87
- end
88
-
89
- it 'raises an exception when text too long' do
90
- speaker = Speaker.init key: "xxxxx-xxxxx-xxxxx-xxxxx"
91
- phrase = 'some phrase' * 5000
70
+ jane = Speaker.new(voice: :jane, language: :english,
71
+ speed: 1.2, emotion: :good)
92
72
 
93
- expect{ speaker.say phrase }.to raise_exception Speaker::TextTooBig
73
+ expect(jane.voice).to be_eql "jane"
74
+ expect(jane.language).to be_eql 'en-US'
75
+ expect(jane.speed).to be_eql 1.2
76
+ expect(jane.emotion).to be_eql "good"
94
77
  end
95
78
  end # context "Speaker#save_to_file"
96
- end # context "Going to be tested with successful Net:HTTP requests"
97
79
 
98
80
  # ----------------------------------------------------
99
81
 
@@ -102,11 +84,12 @@ module YandexSpeechApi
102
84
  stub_request(:get, /https:\/\/tts.voicetech.yandex.net\/.*/)
103
85
  .to_return status: 400, body: "Unreachable body"
104
86
 
105
- bobby = Speaker.init key: "xxxxx-xxxxx-xxxxx-xxxxx"
87
+ bobby = Speaker.new key: "xxxxx-xxxxx-xxxxx-xxxxx"
106
88
 
107
89
  expect{bobby.say "313"}
108
90
  .to raise_exception Connection::ConnectionError
109
91
  end
110
- end # context "Going to be tested with failed Net:HTTP requests"
92
+ end # context "Going to be tested with failed Net:HTTP requests"
93
+ end # context
111
94
  end # describe YandexSpeechApi
112
95
  end # module YandexSpeechApi