updated entry point and lib

This commit is contained in:
surajshivakumar
2024-06-12 06:53:39 -04:00
parent 4a07596c45
commit 81e13e39b9
8 changed files with 181 additions and 191 deletions

View File

@@ -1,67 +1,9 @@
const {audioProcess} = require('./lib/transcription');
const axios = require('axios');
const {redactSensitiveInfo} = require('./lib/audioRedaction');
const path = require('path');
const fs = require('fs-extra');
require('dotenv').config();
const AudioProcessor = require('./lib/utils');
const DeepgramTranscriber = require('./lib/deepgramTranscriber');
const DeepgramRedactor = require('./lib/deepgramRedactor');
const audioDir = process.env.AUDIO_DIR;
const accountSid = process.env.ACCOUNT_SID;
const callSid = process.env.CALL_SID;
const jambonzApiToken = process.env.JAMBONZ_API_TOKEN;
const recordingDate = '2024/06/06';
const audioURL = `https://jambonz.one/api/v1/Accounts/${accountSid}/RecentCalls/${callSid}/record/${recordingDate}/mp3`;
console.log(audioURL);
const config = {
responseType: 'stream',
headers: {
Authorization: `Bearer ${jambonzApiToken}`
}
module.exports = {
AudioProcessor,
DeepgramTranscriber,
DeepgramRedactor
};
// Function to download and process audio
async function downloadAndProcessAudio() {
try {
const response = await axios.get(audioURL, config);
const audioFilePath = path.join(audioDir, 'recording.mp3');
const writer = fs.createWriteStream(audioFilePath);
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
console.log('File downloaded successfully');
await processAndRedactAudio(audioFilePath);
} catch (error) {
console.error('Error downloading the file:', error);
}
}
// Function to process and redact audio
async function processAndRedactAudio(audioFilePath) {
try {
const dG = new audioProcess(process.env.DEEPGRAM_API_KEY, audioFilePath);
const results = await dG.processAudio();
const jsonContent = JSON.stringify(results, null, 2);
const outputPathJson = path.join(__dirname, 'redacts', `${path.basename(audioFilePath, '.mp3')}-redacts.json`);
const outputPathAudio = path.join(__dirname, 'redacts', `${path.basename(audioFilePath, '.mp3')}-redacted.wav`);
await fs.ensureDir(path.dirname(outputPathJson));
await fs.writeFile(outputPathJson, jsonContent, 'utf-8');
console.dir({analysis: [results]}, {depth: null});
if (results && results.redactionTimestamps) {
await redactSensitiveInfo(results.redactionTimestamps, audioFilePath, outputPathAudio);
}
} catch (error) {
console.error('Error in processing audio:', error);
}
}
// Start the process
downloadAndProcessAudio();

View File

@@ -1,44 +0,0 @@
const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs').promises;
function redactSensitiveInfo(transcriptionData, audioPath, audioOutputPath, delta = 0.05) {
const command = ffmpeg(audioPath)
.outputFormat('wav'); // Ensure output format is WAV
// Iterate over transcription data to apply audio filters
for (let i = 0; i < transcriptionData.length; i++) {
const {word, start} = transcriptionData[i];
let end = transcriptionData[i].end; // Default end time
// Check if the word needs redaction
if (word.startsWith('[') && word.endsWith(']')) {
// Find the start of the next non-redacted word
for (let j = i + 1; j < transcriptionData.length; j++) {
if (!(transcriptionData[j].word.startsWith('[') && transcriptionData[j].word.endsWith(']'))) {
end = transcriptionData[j].start;
break;
}
}
// Apply the volume filter to silence from the start of the current word to the start of the next non-redacted word
command.audioFilters({
filter: 'volume',
options: `volume=0:enable='between(t,${start - delta},${end})'` // Applying silence
});
// Log the redacted segments
console.log(`Redacting from ${start}s to ${end}s: "${word}"`);
}
}
// Handlers for command execution
command.on('end', () => {
console.log(`Redacted audio saved at ${audioOutputPath}`);
}).on('error', (err, stdout, stderr) => {
console.error('Error processing audio file:', err.message);
console.error('ffmpeg stdout:', stdout);
console.error('ffmpeg stderr:', stderr);
}).saveToFile(audioOutputPath);
}
module.exports = {redactSensitiveInfo};

42
lib/deepgramRedactor.js Normal file
View File

@@ -0,0 +1,42 @@
const Redactor = require('./redactor');
const ffmpeg = require('fluent-ffmpeg');
class DeepGramRedactor extends Redactor {
async redactAudio(transcriptionData, audioPath, audioOutputPath, { delta = 0.05 } = {}) {
const command = ffmpeg(audioPath)
.outputFormat('wav'); // Ensure output format is WAV
// Iterate over transcription data to apply audio filters
for (let i = 0; i < transcriptionData.length; i++) {
const {word, start} = transcriptionData[i];
let end = transcriptionData[i].end; // Default end time
// Check if the word needs redaction
if (word.startsWith('[') && word.endsWith(']')) {
// Find the start of the next non-redacted word
for (let j = i + 1; j < transcriptionData.length; j++) {
if (!(transcriptionData[j].word.startsWith('[') && transcriptionData[j].word.endsWith(']'))) {
end = transcriptionData[j].start;
break;
}
}
command.audioFilters({
filter: 'volume',
options: `volume=0:enable='between(t,${start - delta},${end})'` // Applying silence
});
// Log the redacted segments
console.log(`Redacting from ${start}s to ${end}s: "${word}"`);
}
}
// Handlers for command execution
command.on('end', () => {
console.log(`Redacted audio saved at ${audioOutputPath}`);
}).on('error', (err, stdout, stderr) => {
console.error('Error processing audio file:', err.message);
console.error('ffmpeg stdout:', stdout);
console.error('ffmpeg stderr:', stderr);
}).saveToFile(audioOutputPath);
}
}
module.exports = DeepGramRedactor;

View File

@@ -0,0 +1,66 @@
// deepgramTranscriber.js
const Transcriber = require('./transcriber');
const { createClient } = require('@deepgram/sdk');
const fs = require('fs');
class DeepgramTranscriber extends Transcriber {
constructor(apiKey, audioFilePath) {
super(audioFilePath);
this.deepgram = createClient(apiKey);
}
async transcribeFile(filePath) {
const { result } = await this.deepgram.listen.prerecorded.transcribeFile(
fs.readFileSync(filePath),
{ model: 'nova-2', smart_format: true, detect_entities: true }
);
return result;
}
async redactFile(filePath) {
const { result } = await this.deepgram.listen.prerecorded.transcribeFile(
fs.readFileSync(filePath),
{ model: 'nova-2', smart_format: true, redact: 'pii' }
);
return result;
}
async analyzeText(text) {
const { result } = await this.deepgram.read.analyzeText(
{ text },
{ language: 'en', sentiment: true, intents: true, summarize: true }
);
return result;
}
async processAudio(filePath = this.audioDir) {
try {
const transcription = await this.transcribeFile(filePath);
const redaction = await this.redactFile(filePath);
const transcript = transcription.results.channels[0].alternatives[0].transcript;
const timestamps = transcription.results.channels[0].alternatives[0].words;
const redactionTimestamps = redaction.results.channels[0].alternatives[0].words;
const redacted = redaction.results.channels[0].alternatives[0].transcript;
const entities = transcription.results.channels[0].alternatives[0].entities;
const analysisResult = await this.analyzeText(transcript);
const sentimentSegment = analysisResult.results.sentiments.segments[0];
const sentiment = sentimentSegment.sentiment;
const sentimentScore = sentimentSegment.sentiment_score;
return {
transcript,
timestamps,
redactionTimestamps,
redacted,
sentiment,
sentimentScore,
entities
};
} catch (error) {
throw error;
}
}
}
module.exports = DeepgramTranscriber;

13
lib/redactor.js Normal file
View File

@@ -0,0 +1,13 @@
class Redactor {
constructor() {
if (new.target === Redactor) {
throw new TypeError('Cannot construct Redactor instances directly');
}
}
async redactAudio(transcriptionData, audioPath, audioOutputPath, options) {
throw new Error("Method 'redactAudio' must be implemented.");
}
}
module.exports = Redactor;

27
lib/transcriber.js Normal file
View File

@@ -0,0 +1,27 @@
// transcriber.js
class Transcriber {
constructor(audioFilePath) {
if (new.target === Transcriber) {
throw new TypeError('Cannot construct Transcriber instances directly');
}
this.audioDir = audioFilePath;
}
async transcribeFile(filePath) {
throw new Error("Method 'transcribeFile' must be implemented.");
}
async redactFile(filePath) {
throw new Error("Method 'redactFile' must be implemented.");
}
async analyzeText(text) {
throw new Error("Method 'analyzeText' must be implemented.");
}
async processAudio(filePath) {
throw new Error("Method 'processAudio' must be implemented.");
}
}
module.exports = Transcriber;

View File

@@ -1,82 +0,0 @@
const {createClient} = require("@deepgram/sdk");
const fs = require("fs");
class audioProcess {
constructor(apiKey, audioFilePath) {
this.deepgram = createClient(apiKey);
this.audioDir = audioFilePath
}
async transcribeFile(filePath) {
const {result, error} = await this.deepgram.listen.prerecorded.transcribeFile(
fs.readFileSync(filePath),
{
model: "nova-2",
smart_format: true,
detect_entities: true
}
);
return result;
}
async redactFile(filePath) {
const {result, error} = await this.deepgram.listen.prerecorded.transcribeFile(
fs.readFileSync(filePath),
{
model: "nova-2",
smart_format: true,
redact: "pii"
}
);
return result;
}
async analyzeText(text) {
const {result, error} = await this.deepgram.read.analyzeText(
{
text,
},
{
language: "en",
sentiment: true,
intents: true,
summarize: true
}
);
return result;
}
async processAudio(filePath = this.audioDir) {
try {
const transcription = await this.transcribeFile(filePath);
const redaction = await this.redactFile(filePath);
const transcript = transcription.results.channels[0].alternatives[0].transcript;
const timestamps = transcription.results.channels[0].alternatives[0].words;
const redactionTimestamps = redaction.results.channels[0].alternatives[0].words;
const redacted = redaction.results.channels[0].alternatives[0].transcript;
const entities = transcription.results.channels[0].alternatives[0].entities;
const analysisResult = await this.analyzeText(transcript);
const sentimentSegment = analysisResult.results.sentiments.segments[0];
const sentiment = sentimentSegment.sentiment;
const sentimentScore = sentimentSegment.sentiment_score;
return {
transcript,
timestamps,
redactionTimestamps,
redacted,
sentiment,
sentimentScore,
entities
};
} catch (error) {
throw error;
}
}
}
module.exports = {audioProcess};

26
lib/utils.js Normal file
View File

@@ -0,0 +1,26 @@
const DeepgramTranscriber = require('./deepgramTranscriber');
const DeepgramRedactor = require('./deepgramRedactor');
class AudioProcessing {
static getTranscriber(serviceType,DEEPGRAM_API_KEY, audioFilePath) {
switch (serviceType) {
case 'deepgram':
return new DeepgramTranscriber(DEEPGRAM_API_KEY, audioFilePath);
default:
throw new Error('Unknown transcription service');
}
}
static getRedactor(serviceType, audioFilePath) {
switch (serviceType) {
case 'deepgram':
return new DeepgramRedactor(audioFilePath);
default:
throw new Error('Unknown redaction service');
}
}
}
module.exports = AudioProcessing;