diff --git a/output.mp3 b/output.mp3 new file mode 100644 index 0000000..0084bdd Binary files /dev/null and b/output.mp3 differ diff --git a/pom.xml b/pom.xml index 322fd62..fce6e83 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,12 @@ limitations under the License. 2.6.2 + + com.google.cloud + google-cloud-texttospeech + 0.80.0-beta + + diff --git a/src/main/java/com/google/codeu/data/QuickstartSample.java b/src/main/java/com/google/codeu/data/QuickstartSample.java new file mode 100644 index 0000000..166374e --- /dev/null +++ b/src/main/java/com/google/codeu/data/QuickstartSample.java @@ -0,0 +1,57 @@ +package com.google.codeu.data; + +// Imports the Google Cloud client library +import com.google.cloud.texttospeech.v1.AudioConfig; +import com.google.cloud.texttospeech.v1.AudioEncoding; +import com.google.cloud.texttospeech.v1.SsmlVoiceGender; +import com.google.cloud.texttospeech.v1.SynthesisInput; +import com.google.cloud.texttospeech.v1.SynthesizeSpeechResponse; +import com.google.cloud.texttospeech.v1.TextToSpeechClient; +import com.google.cloud.texttospeech.v1.VoiceSelectionParams; +import com.google.protobuf.ByteString; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.lang.Exception; + +public class QuickstartSample { + + /** + * Demonstrates using the Text-to-Speech API. + */ + public static void main(String... args) throws Exception { + // Instantiates a client + try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create()) { + // Set the text input to be synthesized + SynthesisInput input = SynthesisInput.newBuilder() + .setText("Hello this is really nice haha.") + .build(); + + // Build the voice request, select the language code ("en-US") and the ssml voice gender + // ("neutral") + VoiceSelectionParams voice = VoiceSelectionParams.newBuilder() + .setLanguageCode("en-US") + // Try experimenting with the different voices + .setSsmlGender(SsmlVoiceGender.NEUTRAL) // Try experimenting with t + .build(); + + // Select the type of audio file you want returned + AudioConfig audioConfig = AudioConfig.newBuilder() + .setAudioEncoding(AudioEncoding.MP3) + .build(); + + // Perform the text-to-speech request on the text input with the selected voice parameters and + // audio file type + SynthesizeSpeechResponse response = textToSpeechClient.synthesizeSpeech(input, voice, + audioConfig); + + // Get the audio contents from the response + ByteString audioContents = response.getAudioContent(); + + // Write the response to the output file. + try (OutputStream out = new FileOutputStream("/Users/minhphu/Desktop/GoogleCode/codeu-starter-project/output.mp3")) { + out.write(audioContents.toByteArray()); + System.out.println("Audio content written to file \"/Users/minhphu/Desktop/GoogleCode/codeu-starter-project/output.mp3\""); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/google/codeu/servlets/MessageServlet2.java b/src/main/java/com/google/codeu/servlets/MessageServlet2.java new file mode 100644 index 0000000..f0939b9 --- /dev/null +++ b/src/main/java/com/google/codeu/servlets/MessageServlet2.java @@ -0,0 +1,70 @@ +package com.google.codeu.servlets; + +import com.google.appengine.api.users.UserService; +import com.google.appengine.api.users.UserServiceFactory; +import com.google.codeu.data.Message; +import java.io.IOException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.jsoup.Jsoup; +import org.jsoup.safety.Whitelist; +import com.google.cloud.texttospeech.v1.AudioConfig; +import com.google.cloud.texttospeech.v1.AudioEncoding; +import com.google.cloud.texttospeech.v1.SsmlVoiceGender; +import com.google.cloud.texttospeech.v1.SynthesisInput; +import com.google.cloud.texttospeech.v1.SynthesizeSpeechResponse; +import com.google.cloud.texttospeech.v1.TextToSpeechClient; +import com.google.cloud.texttospeech.v1.VoiceSelectionParams; +import com.google.protobuf.ByteString; +import java.util.stream.Collectors; + +/** Handles fetching and saving {@link Message} instances. */ +@WebServlet("/a11y/tts") +public class MessageServlet2 extends HttpServlet { + /** Responses with a bytestream from the Google Cloud + /* Text-to-Speech API + **/ + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { + + UserService userService = UserServiceFactory.getUserService(); + if (!userService.isUserLoggedIn()) { + response.sendRedirect("/index.html"); + return; + } + // To get clean messages, removed dangerous things, html + String userMessage = Jsoup.clean(request.getReader().lines().collect(Collectors.joining(System.lineSeparator())), Whitelist.none()); + + try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create()) { + // Set the text input to be synthesized + SynthesisInput input = SynthesisInput.newBuilder() + .setText(userMessage) + .build(); + + // Build the voice request, select the language code ("en-US") and the ssml voice gender + // ("neutral") + VoiceSelectionParams voice = VoiceSelectionParams.newBuilder() + .setLanguageCode("en-US") + .setSsmlGender(SsmlVoiceGender.NEUTRAL) + .build(); + + // Select the type of audio file you want returned + AudioConfig audioConfig = AudioConfig.newBuilder() + .setAudioEncoding(AudioEncoding.MP3) + .build(); + + // Perform the text-to-speech request on the text input with the selected voice parameters and + // audio file type + SynthesizeSpeechResponse apiResponse = textToSpeechClient.synthesizeSpeech(input, voice, + audioConfig); + + // Get the audio contents from the response + ByteString audioContents = apiResponse.getAudioContent(); + + response.setContentType("audio/mpeg"); + response.getOutputStream().write(audioContents.toByteArray()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/google/codeu/servlets/QuickstartServlet.java b/src/main/java/com/google/codeu/servlets/QuickstartServlet.java new file mode 100644 index 0000000..f64f1ec --- /dev/null +++ b/src/main/java/com/google/codeu/servlets/QuickstartServlet.java @@ -0,0 +1,34 @@ +package com.google.codeu.servlets; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.codeu.data.QuickstartSample; + +/** + * Runs QuickstartSample class to demonstrate the text-to-speech API + */ +@WebServlet("/quickstart") +public class QuickstartServlet extends HttpServlet { + + private QuickstartSample quickstartSample; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + quickstartSample = new QuickstartSample(); + quickstartSample.main(); + } catch (Exception e) { + System.out.println("An exception was thrown."); + } + response.setContentType("application/json"); + + response.getWriter().println("Have printed the file"); + } + +} \ No newline at end of file diff --git a/src/main/webapp/audio/output.mp3 b/src/main/webapp/audio/output.mp3 new file mode 100644 index 0000000..2c2994c Binary files /dev/null and b/src/main/webapp/audio/output.mp3 differ diff --git a/src/main/webapp/js/user-page-loader.js b/src/main/webapp/js/user-page-loader.js index 8dec3c8..3549427 100644 --- a/src/main/webapp/js/user-page-loader.js +++ b/src/main/webapp/js/user-page-loader.js @@ -57,15 +57,14 @@ function fetchMessages() { return response.json(); }) .then((messages) => { - console.log(messages); const messagesContainer = document.getElementById('message-container'); if (messages.length == 0) { messagesContainer.innerHTML = '

This user has no posts yet.

'; } else { messagesContainer.innerHTML = ''; } - messages.forEach((message) => { - const messageDiv = buildMessageDiv(message); + messages.forEach(async (message) => { + const messageDiv = await buildMessageDiv(message); messagesContainer.appendChild(messageDiv); }); }); @@ -76,7 +75,7 @@ function fetchMessages() { * @param {Message} message * @return {Element} */ -function buildMessageDiv(message) { +async function buildMessageDiv(message) { const headerDiv = document.createElement('div'); headerDiv.classList.add('message-header'); headerDiv.appendChild(document.createTextNode( @@ -92,11 +91,48 @@ function buildMessageDiv(message) { messageDiv.classList.add('message-div'); messageDiv.appendChild(headerDiv); messageDiv.appendChild(bodyDiv); - + audio = await createAudioTag(message.text); + messageDiv.appendChild(audio); return messageDiv; } -/** Fetches about me data from user's input and adds it to the page. */ +/** + * Creates audio tag. + */ +async function createAudioTag(messageText) { + // Do nothing, can consider showing a simple error to the user. + if (messageText === "") { + return; + } + + try { + let resp = await fetch("/a11y/tts", { + method: "POST", + body: messageText, + headers: { + "Content-Type": "text/plain" + }, + }) + + let audio = await resp.blob(); + + var objectURL = URL.createObjectURL(audio); + + var sound = document.createElement('audio'); + sound.controls = 'controls'; + sound.src = objectURL; + sound.type = 'audio/mpeg'; + + return sound; + + } catch (err) { + throw new Error(`Unable to call the Text to Speech API: {err}`) + } +} + +/** + * Fetches about me data from user's input and adds it to the page. + */ function fetchAboutMe() { const url = '/about?user=' + parameterUsername; fetch(url)