Часть 01

Как создать «говорящее приложение» с Amazon Polly

Начиная

Часть 02 доступна здесь.

Добро пожаловать в еще одно практическое руководство по AWS. Это письменная версия следующего видео на YouTube. Я бы порекомендовал вам посмотреть видео перед чтением блога. Используйте этот блог как источник, чтобы скопировать код и попрактиковаться в создании приложения самостоятельно.

Сервисы AWS, используемые в приложении

  • Амазонка Полли
  • Amazon S3
  • AWS IAM
  • AWS Lambda

Создание бессерверного проекта / службы

Установите бессерверный фреймворк с помощью npm и создайте новый проект / сервис nodejs под названием backend.

npm install serverless -g
serverless create --template aws-nodejs --path backend

Теперь замените файл serverless.yml следующим кодом, который создает лямбда-функцию с именем «Speak».

service: talking-backend 
provider:
  name: aws
  runtime: nodejs8.10
  region: us-east-1 
  role: arn:aws:iam::<account-id>:role/talking-app-role
      
functions:
  speak:
    handler: handler.speak
    events:
      - http: 
          path: speak
          method: post
          cors: true

Лямбда-функция «говорит» отправит полезные текстовые данные в AWS Polly и вернет голосовой файл из корзины S3.

Создание сегмента S3

Нам нужна корзина S3 для хранения всех голосовых клипов, возвращаемых AWS Polly. Используйте консоль AWS, чтобы создать корзину с уникальным именем. В моем случае корзина S3 называется «мое-говорящее-приложение».

Создать роль IAM

Бессерверная структура создает две функции Lambda, которые взаимодействуют с сервисами AWS Polly и AWS S3. (Мы увидим код позже в блоге). Чтобы взаимодействовать с этими службами, нашей функции Lambda должна быть назначена роль IAM, у которой есть разрешение на общение с S3 и Polly. Итак, создайте роль IAM с предпочтительным именем, например, «говорящее-приложение-роль», со следующей политикой IAM.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "polly:*",
                "s3:PutAccountPublicAccessBlock",
                "s3:GetAccountPublicAccessBlock",
                "s3:ListAllMyBuckets",
                "s3:HeadBucket"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::my-talking-app",
                "arn:aws:s3:::my-talking-app/*"
            ]
        }
    ]
}

Скопируйте ARN роли IAM и добавьте его в раздел провайдера файла serverless.yml.

provider:
  name: aws
  runtime: nodejs8.10
  region: us-east-1 
  role: arn:aws:iam::885121665536:role/talking-app-role

«Говорить» лямбда-функция

Функция Speak Lambda выполняет три основные задачи.

  1. Вызовите AWS Polly synthesizeSpeech API и получите аудиопоток (формат mp3) для текста, введенного пользователем.
  2. Сохраните указанный выше аудиопоток в сегменте S3.
  3. Получите подписанный URL-адрес для сохраненного mp3-файла в S3 и отправьте его обратно во внешнее приложение.

Прежде всего, давайте установим необходимые модули npm внутри внутренней папки.

npm install aws-sdk 
npm install uuid

API AWS Polly synthesizeSpeech требует ввода текста и голосового идентификатора для преобразования текста в речь. Здесь мы используем голос «Джоанны» для озвучивания текста, передаваемого из внешнего интерфейса.

let AWS = require("aws-sdk");
let polly = new AWS.Polly();
let s3 = new AWS.S3();
const uuidv1 = require('uuid/v1');
module.exports.speak = (event, context, callback) => {
  let data = JSON.parse(event.body);
  const pollyParams = {
    OutputFormat: "mp3",
    Text: data.text,
    VoiceId: data.voice
  };
  // 1. Getting the audio stream for the text that user entered
  polly.synthesizeSpeech(pollyParams)
    .on("success", function (response) {
      let data = response.data;
      let audioStream = data.AudioStream;
      let key = uuidv1();
      let s3BucketName = 'my-talking-app';  
      // 2. Saving the audio stream to S3
      let params = {
        Bucket: s3BucketName,
        Key: key + '.mp3',
        Body: audioStream
      };
      s3.putObject(params)
        .on("success", function (response) {
          console.log("S3 Put Success!");
        })
        .on("complete", function () {
          console.log("S3 Put Complete!");
          let s3params = {
            Bucket: s3BucketName,
            Key: key + '.mp3',
          };
          // 3. Getting a signed URL for the saved mp3 file 
          let url = s3.getSignedUrl("getObject", s3params);
          // Sending the result back to the user
          let result = {
            bucket: s3BucketName,
            key: key + '.mp3',
            url: url
          };
          callback(null, {
            statusCode: 200,
            headers: {
              "Access-Control-Allow-Origin" : "*"
            },
            body: JSON.stringify(result)
          });
        })
        .on("error", function (response) {
          console.log(response);
        })
        .send();
    })
    .on("error", function (err) {
      callback(null, {
        statusCode: 500,
        headers: {
          "Access-Control-Allow-Origin" : "*"
        },
        body: JSON.stringify(err)
      });
    })
    .send();
};

Теперь разверните серверный API и функцию Lambda.

sls deploy

Фронтенд Angular-приложение

Чтобы протестировать наш бэкэнд, нам нужен интерфейс, который делает запрос речи с введенным пользователем текстом. Итак, давайте создадим угловое приложение.

ng new client
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? SCSS

Давайте создадим сервис angular, который общается с нашим сервером Amazon Polly.

ng g s API

Добавьте следующий код в файл api.service.ts. Он создаст функцию речи, которая вызывает лямбда-функцию с выбранным голосом и вставленным пользователем текстом.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
  providedIn: 'root'
})
export class APIService {
  ENDPOINT = '<YOUR_ENDPOINT_HERE>';
  constructor(private http:HttpClient) {}
  speak(data) {
    return this.http.post(this.ENDPOINT, data);
  }
}

Давайте воспользуемся основным компонентом приложения для визуализации пользовательского интерфейса Talking App. Перейдите к app.component.html и замените файл следующим HTML-кодом. Он добавит основную текстовую область, выбор предпочтительного голоса и кнопку действия произнесения.

<div style="margin: auto; padding: 10px; text-align: center;">
  <h2>My Talking App</h2>
  <div>
    <textarea #userInput style="font-size: 15px; padding: 10px;" cols="60" rows="10"></textarea>
  </div>
  <div>
    <select [(ngModel)]="selectedVoice">
      <option *ngFor="let voice of voices" [ngValue]="voice">{{voice}}</option>
    </select>
  </div>
  <div style="margin-top: 10px">
    <button style="font-size: 15px;" (click)="speakNow(userInput.value)">Speak Now</button>
  </div>
</div>

Перейдите к файлу app.component.ts и добавьте соответствующую функцию-обработчик для представления. Замените его следующим кодом.

import { Component } from '@angular/core';
import { APIService }  from './api.service'
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {  
  voices = ["Matthew", "Joanna", "Ivy", "Justin"];
  selectedVoice = "Mattew";
   
  constructor(private api: APIService){}
  playAudio(url){
    let audio = new Audio();
    audio.src = url;
    audio.load();
    audio.play();
  }
  speakNow(input){
    let data = {
      text: input,
      voice: this.selectedVoice
    }
    this.api.speak(data).subscribe((result:any) => {
      this.playAudio(result.url);
    });
  }
}

Поскольку мы используем ngModel в app.component.html, нам необходимо импортировать FormsModule в файл app.module.ts. Перейдите к файлу app.module.ts и замените содержимое на,

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    FormsModule,
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Запуск приложения

Теперь, когда наш бэкэнд и интерфейс готовы, давайте поиграем с нашим приложением.

Перейдите в каталог клиента и запустите приложение angular локально,

ng serve

Введите текст в текстовой области и выберите голос из раскрывающегося списка. Когда вы нажимаете Говорить, текст должен произноситься вслух!

Часть 02 доступна здесь.

Ваше здоровье!