My Google Cloud Function gets an audio file from IBM Watson Text-to-Speech, saves it to Firebase Storage, and gets the signed download URL. Sometimes it will write the download URL to Firestore. Most of the time it won't. Here's the code:
exports.IBM_T2S = functions.firestore.document('Users/{userID}/Spanish/IBM_T2S_Request').onUpdate((change) => {
if (change.after.data().word != undefined) {
// get requested word
let word = change.after.data().word;
console.log(word);
let wordFileType = word + '.mp3';
var synthesizeParams = {
text: word,
accept: 'audio/mpeg',
voice: 'es-LA_SofiaVoice' // "es-ES_LauraVoice", "es-ES_EnriqueVoice", "es-US_SofiaVoice",
};
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
const bucket = storage.bucket('myApp.appspot.com');
const file = bucket.file('Audio/Spanish/Latin_America/' + wordFileType);
var TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');
var textToSpeech = new TextToSpeechV1({
username: 'groucho',
password: 'swordfish',
url: 'https://stream.watsonplatform.net/text-to-speech/api'
});
const options = { // construct the file to write
metadata: {
contentType: 'audio/mpeg',
metadata: {
source: 'IBM Watson Text-to-Speech',
languageCode: 'es-LA',
gender: 'Female'
}
}
};
var config = {
action: 'read',
expires: '03-17-2025'
};
return textToSpeech.synthesize(synthesizeParams).on('error', function(error) {
console.log(error);
}).pipe(file.createWriteStream(options))
.on('error', function(error) {
console.error(error);
})
.on('finish', function() {
console.log("Audio file written to Storage.");
// see https://stackoverflow.com/questions/42956250/get-download-url-from-file-uploaded-with-cloud-functions-for-firebase
file.getSignedUrl({
action: 'read',
expires: '03-17-2025'
})
.then(function(signedUrls) {
console.log(signedUrls[0]);
console.log("Word is now: " + word);
// Problem is here
return admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(word).collection('Pronunciations').doc('Latin_America').update({
audioFile: signedUrls[0]
})
.then(function() {
console.log("Download URL written to database, IBM Latin American, update.");
return 0;
})
.catch(function(error) {
console.error(error);
return 0;
});
// *****
})
.catch(function(error) {
console.error(error);
return 0;
});
}); // close on
} else {
console.error("Error.");
return 0;
}
});
In this answer Doug Stevenson said
You're not returning a promise that's resolved when all the async work is complete. If you don't do that, Cloud Functions assumes that all your work is complete, and will clamp down on all resources, and any pending work will be shut down.
The promise returned by translate.translate().then().catch() is being ignored. Your nested call to admin.firestore()...set() has a similar problem. It's not sufficient to just call then() and catch() on every promise because then() and catch() both return yet another promise.
I don't understand what/where to return. The first async call
return textToSpeech.synthesize(synthesizeParams).on('error', function(error) {
has to be returned to prevent the error Function returned undefined, expected Promise or value
. I.e., the cloud function finishes executing before the async functions come back, so you need a return
before the first async function.
There might be a problem in that IBM Watson returns callbacks, not promises.
I return the last async function, as Doug Stevenson said to:
return admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(word).collection('Pronunciations').doc('Latin_America').update({
audioFile: signedUrls[0]
})
.then(function() {
console.log("Download URL written to database, IBM Latin American, update.");
return 0;
})
.catch(function(error) {
console.error(error);
return 0;
});
In other words, I return the first async function and the last async function. That doesn't feel right. Is there documentation explaining where returns belong in Google Cloud Functions?
I also put return 0;
into the catches, so that the cloud functions closes even when there's an error.
The cloud function usually stops at "Audio file written to Storage."
Sometimes it goes on to log the download URL, and sometimes it writes the download URL to Firestore. Why does it sometimes write to Firestore and sometimes not?