2

I want to know how can I include the speaker labelling in my sentiment analysis application using assemblyai API end-point. I'm having trouble on outputting the speaker label so I hope someone here could help.

I'm using reactjs in this application. credits to: Marius Espajo as a reference in creating this app. YouTube link of the tutorial: https://www.youtube.com/watch?v=OKJiSsBulRo&t=1934s

App.js

import React from 'react';
import {
  Avatar,
  Box,
  Text,
  VStack,
  Grid,
  HStack,
} from '@chakra-ui/react';
import { useState, useEffect } from 'react';
import { Recorder } from 'react-voice-recorder';
import 'react-voice-recorder/dist/index.css';
import axios from 'axios';
import Status from './Status';
import Result from './Result';




const assemblyApi = axios.create({
  baseURL: 'https://api.assemblyai.com/v2',
  headers: {
    authorization: process.env.REACT_APP_ASSEMBLY_API_KEY,
    'content-type': 'application/json',
  },
});

const initialState = {
  url: null,
  blob: null,
  chunks: null,
  duration: {
    h: 0,
    m: 0, 
    s: 0,
  },
};

function App() {
  const [audioDetails, setAudioDetails] = useState(initialState);
  const [transcript, setTranscript] = useState({ id: '' });
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const interval = setInterval(async () => {
      if (transcript.id && transcript.status !== 'completed' && isLoading) {
        try {
          const { data: transcriptData } = await assemblyApi.get(
            `/transcript/${transcript.id}`
          );
          setTranscript({ ...transcript, ...transcriptData }); // have stats = 'completed'
        } catch (err) {
          console.error(err);
        }
      } else {
        setIsLoading(false);
        clearInterval(interval);
      }
    }, 1000);
    return () => clearInterval(interval);
  }, [isLoading, transcript]);

  const handleAudioStop = data => {
    setAudioDetails(data);
  };

  const handleReset = () => {
    setAudioDetails({ ...initialState });
    setTranscript({ id: '' });
  };

  const handleAudioUpload = async (audioFile) => {
    setIsLoading(true);

    const { data: uploadResponse } = await assemblyApi.post('/upload', audioFile);

    const { data } = await assemblyApi.post('/transcript', {
      audio_url: uploadResponse.upload_url,
      speaker_labels: true,
      sentiment_analysis: true,
      entity_detection: true,
      iab_categories: true,
    });

    setTranscript({ id: data.id });
  };
  return (
      <Box textAlign="center" fontSize="xl">
        <Grid
          minH={{ base: '50vh', md: '70vh', lg: '100vh' }}
          p={{ base: '30px', md: '54px', lg: '90px' }}
        >
          <VStack spacing={8}>
            <Avatar
              size="2xl"
              name="Assembly AI"
              src="https://images.unsplash.com/photo-1535378620166-273708d44e4c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=957&q=80"
            />

            <Box>
              {transcript.text && transcript.status === 'completed' ? (
              <Result transcript={transcript} /> 
              ) : (
                <Status isLoading={isLoading} status={transcript.status} />
              )}
            </Box>

            <Box>
              <HStack spacing="15px">
                <Box
                  w={{ base: '13px', md: '15px', lg: '20px' }}
                  h={{ base: '13px', md: '15px', lg: '20px' }}
                  bg="lightgreen">
                </Box>{' '}
                <Text fontSize="sm" pr="-100px">
                  Positive
                </Text>

                <Box
                  w={{ base: '13px', md: '15px', lg: '20px' }}
                  h={{ base: '13px', md: '15px', lg: '20px' }}
                  bg={'lightgray'}>
                </Box>{' '}
                <Text fontSize="sm">Neutral</Text>
                <Box
                  w={{ base: '13px', md: '15px', lg: '20px' }}
                  h={{ base: '13px', md: '15px', lg: '20px' }}
                  bg="pink.200">
                </Box>{' '}
                <Text fontSize="sm">Negative</Text>
              </HStack>
            </Box>

            <Box width={{ base: '300px', md: '360px', lg: '900px' }} pt="-1">
              <Recorder
                record={true}
                audioURL={audioDetails.url}
                handleAudioStop={handleAudioStop}
                handleAudioUpload={handleAudioUpload}
                handleReset={handleReset}
              />
            </Box>
          </VStack>
        </Grid>
      </Box>
  );
}

export default App;

Status.js

import { Text, Progress } from '@chakra-ui/react';

const Status = ({ isLoading, status }) => {
  return (
    <div>
      <Text>
        {isLoading
          ? `Calculating... ${status || 'uploading'}...`
          : 'Give me audio!'}
      </Text>
      <Progress
        size="sm"
        width={ { base: 200, md:300, lg:500}}
        isIndeterminate={isLoading}
        colorScheme="green"
      />
    </div>
  );
};

export default Status;

Results.js

import {Box, Text} from '@chakra-ui/react'
import Highlighted from './Highlighted'

const Result = ({ transcript }) => {
  return (
    <div>
      <Box width={{ base: '300px', md: '700px', lg:'1000px'}} textAlign='justify'>
        <Text fontSize={{ base: '15px', md: '20px', lg: '25px'}}>
            {transcript.sentiment_analysis_results.map(result => (
            <Highlighted text={result.text} sentiment={result.sentiment} />
        ))}
        </Text>
        </Box>
    </div>
  )
}   

export default Result 

Highlighted.js

import { Box, Text, Tooltip} from '@chakra-ui/react'

const sentimentColor = {
    POSITIVE: 'lightgreen',
    NEGATIVE: 'pink',
    NEUTRAL: 'lightgray',
};

const Highlighted = ({ text, sentiment}) => {
  return (
    <Box as="mark" bg={sentimentColor[sentiment]} mr="1" textAlign="left"  > {text} </Box>
  )
}

export default Highlighted
1
  • Up Up Up Up Up Up Up Up Up Commented Nov 19, 2022 at 15:19

1 Answer 1

1

I have added a speaker label sample to this repository if you would like to check it out. You can add a new flag in the API responseData as follows:

  const { data } = await assemblyAPI.post("/transcript", {
  audio_url: uploadResponse.upload_url,
  //features
  sentiment_analysis: true,
  entity_detection: true,
  iab_categories: true,
  speaker_labels: true,
});

This will allow you to retrieve an "utterances" object that contains information like:

`utterances:[
    0:{
    confidence:0.9359033333333334,
    end:26950,
    speaker:"A",
    start:250,
    text:"Smoke from hundreds of wildfires in Canada is triggering air qua 
    ...",
    words:[...]`

Additionally, I have created a new component for displaying speakers:

const Speakers = ({ transcript }) => {
  return (
    <div>
      <h3>Speakers: </h3>
      <div>
        {transcript.utterances.map((utterance) => (
          <div key={utterance.start}>
            <span style={{ fontWeight: "bolder", paddingRight: "8px" }}>
              {utterance.speaker}:
            </span>
            <span>{utterance.text}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Speakers;

For more details about the implementation, you can check my GitHub repository.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.