<audio> тег не работает с интерфейсом IBM Watson Text-to-Speech Node.js - PullRequest
0 голосов
/ 05 июля 2018

Я создаю чат-бота с использованием IBM Watson на Node.js, и у меня возникли некоторые проблемы при интеграции API-интерфейса преобразования текста в речь. Я клонировал пример на github, который работает, когда я его запускаю, но когда я пытаюсь внедрить его в свой код, звук не воспроизводится.

app.js (на стороне сервера):

/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

require( 'dotenv' ).config( {silent: true} );

const TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');

var express = require( 'express' );  // app server
var bodyParser = require( 'body-parser' );  // parser for post requests
var Watson = require( 'watson-developer-cloud/conversation/v1' );  // watson sdk

// The following requires are needed for logging purposes
var uuid = require( 'uuid' );
var vcapServices = require( 'vcap_services' );
var basicAuth = require( 'basic-auth-connect' );


// The app owner may optionally configure a cloudand db to track user input.
// This cloudand db is not required, the app will operate without it.
// If logging is enabled the app must also enable basic auth to secure logging
// endpoints
var cloudantCredentials = vcapServices.getCredentials( 'cloudantNoSQLDB' );
var cloudantUrl = null;
if ( cloudantCredentials ) {
  cloudantUrl = cloudantCredentials.url;
}
cloudantUrl = cloudantUrl || process.env.CLOUDANT_URL; // || '<cloudant_url>';
var logs = null;
var app = express();

// set up routes
var token = require('./routes/token');
app.use('/token', token);


// Bootstrap application settings
app.use( express.static( './public' ) ); // load UI from public folder
app.use( bodyParser.json() );

// Create the service wrapper
var conversation = new Watson( {
  // If unspecified here, the CONVERSATION_USERNAME and CONVERSATION_PASSWORD env properties will be checked
  // After that, the SDK will fall back to the bluemix-provided VCAP_SERVICES environment property
  // username: '<username>',
  // password: '<password>',
    username: process.env.ASSISTANT_USERNAME || '<username>',
  password: process.env.ASSISTANT_PASSWORD || '<password>',
  url: 'https://gateway.watsonplatform.net/conversation/api',
  version_date: '2016-09-20',
  version: 'v1'
} );

const textToSpeech = new TextToSpeechV1({
    username: process.env.TEXT_TO_SPEECH_USERNAME,
    password: process.env.TEXT_TO_SPEECH_PASSWORD,
    version: 'v1'
  // If unspecified here, the TEXT_TO_SPEECH_USERNAME and
  // TEXT_TO_SPEECH_PASSWORD env properties will be checked
  // After that, the SDK will fall back to the bluemix-provided VCAP_SERVICES environment property
  // username: '<username>',
  // password: '<password>',
});


/************************************************
 * Conversation services
 ************************************************/

// Endpoint to be call from the client side
app.post( '/api/message', function(req, res) {
  var workspace = process.env.WORKSPACE_ID || '<workspace-id>';
  if ( !workspace || workspace === '<workspace-id>' ) {
    return res.json( {
      'output': {
        'text': 'The app has not been configured with a <b>WORKSPACE_ID</b> environment variable. Please refer to the ' +
        '<a href="https://github.com/watson-developer-cloud/conversation-simple">README</a> documentation on how to set this variable. <br>' +
        'Once a workspace has been defined the intents may be imported from ' +
        '<a href="https://github.com/watson-developer-cloud/conversation-simple/blob/master/training/car_workspace.json">here</a> in order to get a working application.'
      }
    } );
  }
  var payload = {
    workspace_id: workspace,
    context: {},
    input: {}
  };
  if ( req.body ) {
    if ( req.body.input ) {
      payload.input = req.body.input;
    }
    if ( req.body.context ) {
      // The client must maintain context/state
      payload.context = req.body.context;
    }
  }
  // Send the input to the conversation service
  conversation.message( payload, function(err, data) {
    if ( err ) {
      return res.status( err.code || 500 ).json( err );
    }
    return res.json( updateMessage( payload, data ) );
  } );
} );

/**
 * Updates the response text using the intent confidence
 * @param  {Object} input The request to the Conversation service
 * @param  {Object} response The response from the Conversation service
 * @return {Object}          The response with the updated message
 */
function updateMessage(input, response) {
  var responseText = null;
  var id = null;
  if ( !response.output ) {
    response.output = {};
  } else {
    if ( logs ) {
      // If the logs db is set, then we want to record all input and responses
      id = uuid.v4();
      logs.insert( {'_id': id, 'request': input, 'response': response, 'time': new Date()});
    }
    return response;
  }
  if ( response.intents && response.intents[0] ) {
    var intent = response.intents[0];
    // Depending on the confidence of the response the app can return different messages.
    // The confidence will vary depending on how well the system is trained. The service will always try to assign
    // a class/intent to the input. If the confidence is low, then it suggests the service is unsure of the
    // user's intent . In these cases it is usually best to return a disambiguation message
    // ('I did not understand your intent, please rephrase your question', etc..)
    if ( intent.confidence >= 0.75 ) {
      responseText = 'I understood your intent was ' + intent.intent;
    } else if ( intent.confidence >= 0.5 ) {
      responseText = 'I think your intent was ' + intent.intent;
    } else {
      responseText = 'I did not understand your intent';
    }
  }
  response.output.text = responseText;
  if ( logs ) {
    // If the logs db is set, then we want to record all input and responses
    id = uuid.v4();
    logs.insert( {'_id': id, 'request': input, 'response': response, 'time': new Date()});
  }
  return response;
}

if ( cloudantUrl ) {
  // If logging has been enabled (as signalled by the presence of the cloudantUrl) then the
  // app developer must also specify a LOG_USER and LOG_PASS env vars.
  if ( !process.env.LOG_USER || !process.env.LOG_PASS ) {
    throw new Error( 'LOG_USER OR LOG_PASS not defined, both required to enable logging!' );
  }
  // add basic auth to the endpoints to retrieve the logs!
  var auth = basicAuth( process.env.LOG_USER, process.env.LOG_PASS );
  // If the cloudantUrl has been configured then we will want to set up a nano client
  var nano = require( 'nano' )( cloudantUrl );
  // add a new API which allows us to retrieve the logs (note this is not secure)
  nano.db.get( 'car_logs', function(err) {
    if ( err ) {
      console.error(err);
      nano.db.create( 'car_logs', function(errCreate) {
        console.error(errCreate);
        logs = nano.db.use( 'car_logs' );
      } );
    } else {
      logs = nano.db.use( 'car_logs' );
    }
  } );

  // Endpoint which allows deletion of db
  app.post( '/clearDb', auth, function(req, res) {
    nano.db.destroy( 'car_logs', function() {
      nano.db.create( 'car_logs', function() {
        logs = nano.db.use( 'car_logs' );
      } );
    } );
    return res.json( {'message': 'Clearing db'} );
  } );

  // Endpoint which allows conversation logs to be fetched
  app.get( '/chats', auth, function(req, res) {
    logs.list( {include_docs: true, 'descending': true}, function(err, body) {
      console.error(err);
      // download as CSV
      var csv = [];
      csv.push( ['Question', 'Intent', 'Confidence', 'Entity', 'Output', 'Time'] );
      body.rows.sort( function(a, b) {
        if ( a && b && a.doc && b.doc ) {
          var date1 = new Date( a.doc.time );
          var date2 = new Date( b.doc.time );
          var t1 = date1.getTime();
          var t2 = date2.getTime();
          var aGreaterThanB = t1 > t2;
          var equal = t1 === t2;
          if (aGreaterThanB) {
            return 1;
          }
          return  equal ? 0 : -1;
        }
      } );
      body.rows.forEach( function(row) {
        var question = '';
        var intent = '';
        var confidence = 0;
        var time = '';
        var entity = '';
        var outputText = '';
        if ( row.doc ) {
          var doc = row.doc;
          if ( doc.request && doc.request.input ) {
            question = doc.request.input.text;
          }
          if ( doc.response ) {
            intent = '<no intent>';
            if ( doc.response.intents && doc.response.intents.length > 0 ) {
              intent = doc.response.intents[0].intent;
              confidence = doc.response.intents[0].confidence;
            }
            entity = '<no entity>';
            if ( doc.response.entities && doc.response.entities.length > 0 ) {
              entity = doc.response.entities[0].entity + ' : ' + doc.response.entities[0].value;
            }
            outputText = '<no dialog>';
            if ( doc.response.output && doc.response.output.text ) {
              outputText = doc.response.output.text.join( ' ' );
            }
          }
          time = new Date( doc.time ).toLocaleString();
        }
        csv.push( [question, intent, confidence, entity, outputText, time] );
      } );
      res.csv( csv );
    } );
  } );
}

/************************************************
 * Text-to-Speech services
 ************************************************/

/**
 * Pipe the synthesize method
 */
app.get('/api/synthesize', (req, res, next) => {
  const transcript = textToSpeech.synthesize(req.query);
  transcript.on('response', (response) => {
    if (req.query.download) {
      if (req.query.accept && req.query.accept === 'audio/wav') {
        response.headers['content-disposition'] = 'attachment; filename=transcript.wav';
      } else {
        response.headers['content-disposition'] = 'attachment; filename=transcript.ogg';
      }
    }
  });
  transcript.on('error', next);
  transcript.pipe(res);
});

// Return the list of voices
app.get('/api/voices', (req, res, next) => {
  textToSpeech.voices(null, (error, voices) => {
    if (error) {
      return next(error);
    }
    res.json(voices);
  });
});

require('./config/error-handler')(app);

module.exports = app;

tts-custom.js (на стороне клиента):

function text_to_speech(text) {
  console.log(text);
//  text = '<express-as type="GoodNews">'+text+'</express-as>';
  text = encodeURIComponent(text);
  console.log(text);
  $.ajax({
    method: 'GET',
    url: '/api/synthesize?voice=en-US_AllisonVoice&download=true&text='+text,

    dataType: 'native',
    xhrFields: {
      responseType: 'blob'
    },
    success: function(blob) {
      var url = (URL || webkitURL).createObjectURL(blob);
      $('#audio').attr('src', url);
      $('#audio').attr('type', 'audio/ogg;codecs=opus');
    }
  })
}

chat.html:

<html>
<head>
<title> Chat </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta property="og:image" content="conversation.svg" />
  <meta property="og:title" content="Conversation Chat Simple" />
  <meta property="og:description" content="Sample application that shows how to use the Watson Assistant API to identify user intents"
  />

  <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
  <link rel="stylesheet" href="css/app.css">
  <!-- <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.1/css/font-awesome.min.css" /> -->
  <link rel="stylesheet" href="css/speech-input.css">
   <script src="js/conversation.js"></script>
<!--      <script src="js/bundle.js"></script>
 -->  
  <style type="text/css">
    #result{
      height: 200px;
      border: 1px solid #ccc;
      padding: 10px;
      box-shadow: 0 0 10px 0 #bbb;
      margin-bottom: 30px;
      font-size: 14px;
      line-height: 25px;
      font-family: verdana;
    }
    button{
      font-size: 20px;
      position: absolute;
      bottom: 10%;
      right: 10%;
    }
  </style>
  <style>
    .mystyle {
      display:none;
    }
  </style>  
  <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.3.min.js" integrity="sha256-aaODHAgvwQW1bFOGXMeX+pC4PZIPsvn2h1sArYOhgXQ=" crossorigin="anonymous"></script>
  <script src="js/tts-custom.js"></script>
</head>


<body>
<!-- <div id="contentParent" class="responsive-columns-wrapper"> -->
  <div id="view-change-button" class="button" onclick="hideChat(this)">
    <img class="option full" src="../img/Chat Button.png">
    <img class="option not-full" src="../img/Code Button.png">
  </div>
  <div id="chat-column-holder" class="responsive-column content-column">
    <div class="chat-column">
      <div id="scrollingChat"></div>
      <label for="textInput" class="inputOutline">
        <input id="textInput" class= "text" lang="es" placeholder="Type something" type="text" onkeydown="ConversationPanel.inputKeyDown(event, this, false)">
        <!-- add this to class in textInput to put back google mic:, speech-input -->

      </label>
      <audio autoPlay="true" id="audio"
               className="audio" 
               controls="controls">
              Your browser does not support the audio element.
        </audio>
      <span id="microphone"></span>


    </div>
  </div>
    <!-- PAYLOAD DON'T TOUCH/FOR TEXT->JSON -->
  <div id="payload-column" class="fixed-column content-column">
       <div id="payload-request" class="payload"></div>
    <div id="payload-response" class="payload"></div>
  </div>

    <!-- SCRIPTS -->
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"
  integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
  crossorigin="anonymous"></script>
  <script src="js/jquery-ajax-native.js"></script>

<script type="text/javascript" src="js/Microphone.js"></script>
  <script type="text/javascript" src="js/SpeechToText.js"></script>
  <script type="text/javascript" src="js/speechsearch.js"></script>
  <script src="js/hideChat.js"></script>
  <script src="js/speech-input.js"></script>
  <script src="js/common.js"></script>
  <script src="js/api.js"></script>

  <script src="js/payload.js"></script>
  <script src="js/global.js"></script>
  <script src="js/analytics.js"></script>
</body>
</html>

Я не уверен, что делать, но так как ajax называется и jquery используется в js на стороне клиента, помощь приветствуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...