Create an Audio Message from text in Google Docs

Turn your meeting notes into beautiful audio in minutes.

1. Get Secrets to access your docs

  1. Create a project and enable the API in Google Developers. Follow the instructions at: https://developers.google.com/workspace/guides/create-project. In the 5th step at Enable a Google Workspace API, select "Google Docs API".
  2. Create your credentials with OAuth. Follow the instructions given at for a Desktop application: https://developers.google.com/workspace/guides/create-credentials.
  1. Go to your Google Cloud > API and Services > Credentials. Under Client ID OAuth 2.0 there is a new row with your client ID information. At the right of the row there is an arrow. Click it and download the secrets and rename the json file as "credentials.json"

❗️

Make sure to use Desktop

If you don't select Desktop you may run into authentication problems! So make sure your credentials are for Desktop.

2. Google Quickstarts

Click the following link to set up the connection with the API. Write the "quickstart.py" documents and run it. Make sure that "credentials.json" is in the same folder as "quickstart.py".
At the end of the step 3, if you can see "The title of the document is: Docs API Quickstart" the set up is ready!
python: https://developers.google.com/docs/api/quickstart/python
javascript (node.js): https://developers.google.com/docs/api/quickstart/nodejs

3. Get beautiful audio from your Document

It is easy to add this functionality to the quickstart document. Just copy the code below and chose the document you would like to listen. To find the document id, open the document and copy the secret at the URL.
As an example, in the following URL "https://docs.google.com/document/d/1xENxi5emRbFGiG1QS3Bt3Yu_vSYYQ3m7M-4xe5hqHwo/edit", the client id is "1xENxi5emRbFGiG1QS3Bt3Yu_vSYYQ3m7M-4xe5hqHwo".

Feel free to personalize your audio clip modifing the voice, speed, audio padding and more!

from __future__ import print_function
import os.path
import os
import apiaudio
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/documents.readonly']

# The ID of a sample document.
DOCUMENT_ID = '-'
APIAUDIO_API_KEY = os.environ["AFLR_API_KEY"]

def apiaudio_create(scriptName, message):
    apiaudio.api_key = APIAUDIO_API_KEY
    script = apiaudio.Orchestrator.create_audio(scriptText= message, scriptName=scriptName, voice="Emma", soundTemplate="jakarta")
    #Download the full audio file (speech with background music)
    print(script)

def main():
    creds = None
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())

    service = build('docs', 'v1', credentials=creds)

    document = service.documents().get(documentId=DOCUMENT_ID).execute()
    
    fullText = ""
    for paragraph in document["body"]["content"]:
        if paragraph != document["body"]["content"][0]:
            text = paragraph["paragraph"]["elements"][0]["textRun"]["content"]
            # print(text)
            fullText += text
    print(fullText)
    apiaudio_create("googleDocs", fullText)


if __name__ == '__main__':
    main()
const fs = require("fs");
const readline = require("readline");
const { google } = require("googleapis");
const Aflr = require("aflr").default;

const YOUR_AFLR_API_KEY = "-";
let fullText;

const SCOPES = ["https://www.googleapis.com/auth/documents.readonly"];

const TOKEN_PATH = "token.json";

// Load client secrets from a local file.
fs.readFile("credentials.json", async (err, content) => {
  if (err) return console.log("Error loading client secret file:", err);
  // Authorize a client with credentials, then call the Google Docs API.
  fullText = authorize(JSON.parse(content), printDocContent);
});

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  const { client_secret, client_id, redirect_uris } = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris[0]
  );

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return getNewToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback for the authorized client.
 */
function getNewToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: "offline",
    scope: SCOPES,
  });
  console.log("Authorize this app by visiting this url:", authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question("Enter the code from that page here: ", (code) => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return console.error("Error retrieving access token", err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) console.error(err);
        console.log("Token stored to", TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

/**
 * Prints the title of a sample doc:
 * https://docs.google.com/document/d/195j9eDD3ccgjQRttHhJPymLJUCOUjs-jmwTrekvdjFE/edit
 * @param {google.auth.OAuth2} auth The authenticated Google OAuth 2.0 client.
 */
function printDocContent(auth) {
  const docs = google.docs({ version: "v1", auth });
  docs.documents.get(
    {
      documentId: "-",
    },
    (err, res) => {
      if (err) return console.log("The API returned an error: " + err);
      var stepText = "";
      for (i = 1; i < res.data.body.content.length; i++) {
        var text =
          res.data.body.content[i].paragraph.elements[0].textRun.content;
        // console.log(text);
        stepText = stepText.concat(text);
      }
      aflr(stepText);
    }
  );
}

async function aflr(text) {
  try {
    Aflr.configure({ apiKey: YOUR_AFLR_API_KEY });
    const script = await Aflr.Script.create({
      scriptText: text,
    });
    console.log(script);
    const response = await Aflr.Speech.create({
      scriptId: script["scriptId"],
      voice: "Emma",
      speed: "90",
    });
    const audio_files = await Aflr.Speech.retrieve(script["scriptId"]); // retrieve url
    Aflr.Mastering.create({
      scriptId: script["scriptId"],
      backgroundTrackId: "full__citynights.wav", // define mastering background track
    });
    const url = await Aflr.Mastering.retrieve(script["scriptId"]); // download mastered speech
    console.log(url["url"]);
  } catch (e) {
    console.log(e);
  }
}

Your file will be downloaded locally. And will have a filename like ./8419202c-5807-483c-8100-56d934342703.mp3
You can play that in your local music player.