@kablamo/react-transcript-editor
Version:
A React component to make transcribing audio and video easier and faster.
1,330 lines (1,167 loc) • 43.5 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 17);
/******/ })
/************************************************************************/
/******/ ({
/***/ 17:
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXPORTS
__webpack_require__.d(__webpack_exports__, "createEntityMap", function() { return /* reexport */ create_entity_map; });
// EXTERNAL MODULE: ./packages/stt-adapters/generate-entities-ranges/index.js
var generate_entities_ranges = __webpack_require__(7);
// CONCATENATED MODULE: ./packages/stt-adapters/bbc-kaldi/group-words-by-speakers.js
/**
edge cases
- more segments then words - not an issue if you start by matching words with segment
and handle edge case where it doesn't find a match
- more words then segments - orphan words
*/
function groupWordsInParagraphsBySpeakers(words, segments) {
// add speakers to each word
var wordsWithSpeakers = addSpeakerToEachWord(words, segments.segments); // group words by speakers sequentially
var result = groupWordsBySpeaker(wordsWithSpeakers);
return result;
}
;
/**
* Add speakers to each words
* if it doesn't have add unknown attribute `U_UKN`
* @param {*} words
* @param {*} segments
*/
function addSpeakerToEachWord(words, segments) {
var tmpWordsWithSpeakers = [];
words.forEach(function (word) {
var tmpSpeakerSegment = findSegmentForWord(word, segments);
word.speaker = formatSpeakerName(tmpSpeakerSegment.speaker);
tmpWordsWithSpeakers.push(word);
});
return tmpWordsWithSpeakers;
}
/**
* Groups Words by speaker attribute
* @param {array} wordsWithSpeakers - same as kaldi words list but with a `speaker` label attribute on each word
* @return {array} - list of paragraph objcts, with words, text and sepaker attributes.
* where words is an array and the other two are strings.
*/
function groupWordsBySpeaker(wordsWithSpeakers) {
var currentSpeaker = wordsWithSpeakers[0].speaker;
var results = [];
var paragraph = {
words: [],
text: '',
speaker: ''
};
wordsWithSpeakers.forEach(function (word) {
// if current speaker same as word speaker add words to paragraph
if (currentSpeaker === word.speaker) {
paragraph.words.push(word);
paragraph.text += word.punct + ' ';
paragraph.speaker = currentSpeaker;
} // if it's not same speaker
else {
// update current speaker
currentSpeaker = word.speaker; // remove spacing in text
paragraph.text = paragraph.text.trim(); //save previous paragraph
results.push(paragraph); // reset paragraph
paragraph = {
words: [],
text: '',
speaker: 'U_UKN'
}; // add words attributes to new
paragraph.words.push(word);
paragraph.text += word.punct + ' ';
}
}); // add last paragraph
results.push(paragraph);
return results;
}
/**
* Helper functions
*/
/**
* given word start and end time attributes
* looks for segment range that contains that word
* if it doesn't find any it returns a segment with `UKN`
* speaker attributes.
* @param {object} word - word object
* @param {array} segments - list of segments objects
* @return {object} - a single segment whose range contains the word
*/
function findSegmentForWord(word, segments) {
var tmpSegment = segments.find(function (seg) {
var segEnd = seg.start + seg.duration;
return word.start >= seg.start && word.end <= segEnd;
}); // if find doesn't find any matches it returns an undefined
if (tmpSegment === undefined) {
// covering edge case orphan word not belonging to any segments
// adding UKN speaker label
return {
'@type': 'Segment',
// keeping both speaker id and gender as this is used later
// to format speaker label combining the two
speaker: {
'@id': 'UKN',
gender: 'U'
}
};
} else {
// find returns the first element that matches the criteria
return tmpSegment;
}
}
/**
* formats kaldi speaker object into a string
* Combining Gender and speaker Id
* @param {object} speaker - BBC kaldi speaker object
* @return {string} -
*/
function formatSpeakerName(speaker) {
return speaker.gender + '_' + speaker['@id'];
}
/* harmony default export */ var group_words_by_speakers = (groupWordsInParagraphsBySpeakers);
// CONCATENATED MODULE: ./packages/stt-adapters/bbc-kaldi/index.js
/**
* Convert BBC Kaldi json to draftJs
* see `sample` folder for example of input and output as well as `example-usage.js`
*
*/
/**
* groups words list from kaldi transcript based on punctuation.
* @todo To be more accurate, should introduce an honorifics library to do the splitting of the words.
* @param {array} words - array of words opbjects from kaldi transcript
*/
var groupWordsInParagraphs = function groupWordsInParagraphs(words) {
var results = [];
var paragraph = {
words: [],
text: []
};
words.forEach(function (word) {
// if word contains punctuation
if (/[.?!]/.test(word.punct)) {
paragraph.words.push(word);
paragraph.text.push(word.punct);
paragraph.text = paragraph.text.join(' ');
results.push(paragraph); // reset paragraph
paragraph = {
words: [],
text: []
};
} else {
paragraph.words.push(word);
paragraph.text.push(word.punct);
}
});
return results;
};
var bbc_kaldi_bbcKaldiToDraft = function bbcKaldiToDraft(bbcKaldiJson) {
var results = [];
var tmpWords;
var speakerSegmentation = null;
var wordsByParagraphs = []; // BBC Octo Labs API Response wraps Kaldi response around retval,
// while kaldi contains word attribute at root
if (bbcKaldiJson.retval !== undefined) {
tmpWords = bbcKaldiJson.retval.words;
if (bbcKaldiJson.retval.segmentation !== undefined) {
speakerSegmentation = bbcKaldiJson.retval.segmentation;
}
} else {
tmpWords = bbcKaldiJson.words;
if (bbcKaldiJson.segmentation !== undefined) {
speakerSegmentation = bbcKaldiJson.segmentation;
}
}
if (speakerSegmentation === null) {
wordsByParagraphs = groupWordsInParagraphs(tmpWords);
} else {
wordsByParagraphs = group_words_by_speakers(tmpWords, speakerSegmentation);
}
wordsByParagraphs.forEach(function (paragraph, i) {
// if paragraph contain words
// eg sometimes the speaker segmentation might not contain words :man-shrugging:
if (paragraph.words[0] !== undefined) {
var speakerLabel = "TBC ".concat(i);
if (speakerSegmentation !== null) {
speakerLabel = paragraph.speaker;
}
var draftJsContentBlockParagraph = {
text: paragraph.text,
type: 'paragraph',
data: {
speaker: speakerLabel,
words: paragraph.words,
start: paragraph.words[0].start
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(paragraph.words, 'punct') // wordAttributeName
};
results.push(draftJsContentBlockParagraph);
}
});
return results;
};
/* harmony default export */ var bbc_kaldi = (bbc_kaldi_bbcKaldiToDraft);
// CONCATENATED MODULE: ./packages/stt-adapters/autoEdit2/index.js
/**
* Convert autoEdit2 Json to draftJS
* see `sample` folder for example of input and output as well as `example-usage.js`
*/
/**
* groups words list from autoEdit transcript based on punctuation.
* @todo To be more accurate, should introduce an honorifics library to do the splitting of the words.
* @param {array} words - array of words objects from autoEdit transcript
*/
var autoEdit2_groupWordsInParagraphs = function groupWordsInParagraphs(autoEditText) {
var results = [];
var paragraph = {
words: [],
text: []
};
autoEditText.forEach(function (autoEditparagraph) {
autoEditparagraph.paragraph.forEach(function (autoEditLine) {
autoEditLine.line.forEach(function (word) {
// adjusting time reference attributes from
// `startTime` `endTime` to `start` `end`
// for word object
var tmpWord = {
text: word.text,
start: word.startTime,
end: word.endTime
}; // if word contains punctuation
if (/[.?!]/.test(word.text)) {
paragraph.words.push(tmpWord);
paragraph.text.push(word.text);
results.push(paragraph); // reset paragraph
paragraph = {
words: [],
text: []
};
} else {
paragraph.words.push(tmpWord);
paragraph.text.push(word.text);
}
});
});
});
return results;
};
var autoEdit2_autoEdit2ToDraft = function autoEdit2ToDraft(autoEdit2Json) {
var results = [];
var tmpWords = autoEdit2Json.text;
var wordsByParagraphs = autoEdit2_groupWordsInParagraphs(tmpWords);
wordsByParagraphs.forEach(function (paragraph, i) {
var draftJsContentBlockParagraph = {
text: paragraph.text.join(' '),
type: 'paragraph',
data: {
speaker: "TBC ".concat(i),
words: paragraph.words,
start: paragraph.words[0].start
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(paragraph.words, 'text')
}; // console.log(JSON.stringify(draftJsContentBlockParagraph,null,2))
results.push(draftJsContentBlockParagraph);
}); // console.log(JSON.stringify(results,null,2))
return results;
};
/* harmony default export */ var autoEdit2 = (autoEdit2_autoEdit2ToDraft);
// CONCATENATED MODULE: ./packages/stt-adapters/speechmatics/index.js
/**
* Convert Speechmatics Json to DraftJs
* see `sample` folder for example of input and output as well as `example-usage.js`
*/
/**
* Determines the speaker of a paragraph by comparing the start time of the paragraph with
* the speaker times.
* @param {float} start - Starting point of paragraph
* @param {array} speakers - list of all speakers with start and end time
*/
var getSpeaker = function getSpeaker(start, speakers) {
for (var speakerIdx in speakers) {
var speaker = speakers[speakerIdx];
var segmentStart = parseFloat(start);
if (segmentStart >= speaker.start & segmentStart < speaker.end) {
return speaker.name;
}
}
return 'UNK';
};
/**
* groups words list from speechmatics based on speaker change and paragraph length.
* @param {array} words - array of words objects from speechmatics transcript
* @param {array} speakers - array of speaker objects from speechmatics transcript
* @param {int} words - number of words which trigger a paragraph break
*/
var speechmatics_groupWordsInParagraphs = function groupWordsInParagraphs(words, speakers, maxParagraphWords) {
var results = [];
var paragraph = {
words: [],
text: [],
speaker: ''
};
var oldSpeaker = getSpeaker(words[0].start, speakers);
var newSpeaker;
var sentenceEnd = false;
words.forEach(function (word) {
newSpeaker = getSpeaker(word.start, speakers); // if speaker changes
if (newSpeaker !== oldSpeaker || paragraph.words.length > maxParagraphWords && sentenceEnd) {
paragraph.speaker = oldSpeaker;
results.push(paragraph);
oldSpeaker = newSpeaker; // reset paragraph
paragraph = {
words: [],
text: []
};
}
paragraph.words.push(word);
paragraph.text.push(word.punct);
sentenceEnd = /[.?!]/.test(word.punct) ? true : false;
});
paragraph.speaker = oldSpeaker;
results.push(paragraph);
return results;
};
/**
* Speechmatics treats punctuation as own words. This function merges punctuations with
* the pevious word and adjusts the total duration of the word.
* @param {array} words - array of words objects from speechmatics transcript
*/
var curatePunctuation = function curatePunctuation(words) {
var curatedWords = [];
words.forEach(function (word) {
if (/[.?!]/.test(word.name)) {
curatedWords[curatedWords.length - 1].name = curatedWords[curatedWords.length - 1].name + word.name;
curatedWords[curatedWords.length - 1].duration = (parseFloat(curatedWords[curatedWords.length - 1].duration) + parseFloat(word.duration)).toString();
} else {
curatedWords.push(word);
}
});
return curatedWords;
};
var speechmatics_speechmaticsToDraft = function speechmaticsToDraft(speechmaticsJson) {
var results = [];
var tmpWords;
tmpWords = curatePunctuation(speechmaticsJson.words);
tmpWords = tmpWords.map(function (element, index) {
return {
start: element.time,
end: (parseFloat(element.time) + parseFloat(element.duration)).toString(),
confidence: element.confidence,
word: element.name.toLowerCase().replace(/[.?!]/g, ''),
punct: element.name,
index: index
};
});
var tmpSpeakers;
tmpSpeakers = speechmaticsJson.speakers;
tmpSpeakers = tmpSpeakers.map(function (element) {
return {
start: parseFloat(element.time),
end: parseFloat(element.time) + parseFloat(element.duration),
name: element.name
};
});
var wordsByParagraphs = speechmatics_groupWordsInParagraphs(tmpWords, tmpSpeakers, 150);
wordsByParagraphs.forEach(function (paragraph) {
var paragraphStart = paragraph.words[0].start;
var draftJsContentBlockParagraph = {
text: paragraph.text.join(' '),
type: 'paragraph',
data: {
speaker: paragraph.speaker,
words: paragraph.words,
start: paragraphStart
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(paragraph.words, 'punct') // wordAttributeName
};
results.push(draftJsContentBlockParagraph);
});
return results;
};
/* harmony default export */ var speechmatics = (speechmatics_speechmaticsToDraft);
// CONCATENATED MODULE: ./packages/stt-adapters/amazon-transcribe/group-words-by-speakers.js
var groupWordsBySpeakerLabel = function groupWordsBySpeakerLabel(words) {
var groupedWords = [];
var currentSpeaker = '';
words.forEach(function (word) {
if (word.speaker_label === currentSpeaker) {
groupedWords[groupedWords.length - 1].words.push(word);
} else {
currentSpeaker = word.speaker_label; // start new speaker block
groupedWords.push({
speaker: word.speaker_label,
words: [word]
});
}
});
return groupedWords;
};
var findSpeakerForWord = function findSpeakerForWord(word, segments) {
var startTime = parseFloat(word.start_time);
var endTime = parseFloat(word.end_time);
var firstMatchingSegment = segments.find(function (seg) {
return startTime >= parseFloat(seg.start_time) && endTime <= parseFloat(seg.end_time);
});
if (firstMatchingSegment === undefined) {
return 'UKN';
} else {
return firstMatchingSegment.speaker_label.replace('spk_', '');
}
};
var addSpeakerLabelToWords = function addSpeakerLabelToWords(words, segments) {
return words.map(function (w) {
return Object.assign(w, {
'speaker_label': findSpeakerForWord(w, segments)
});
});
};
var group_words_by_speakers_groupWordsBySpeaker = function groupWordsBySpeaker(words, speakerLabels) {
var wordsWithSpeakers = addSpeakerLabelToWords(words, speakerLabels.segments);
return groupWordsBySpeakerLabel(wordsWithSpeakers);
};
// CONCATENATED MODULE: ./packages/stt-adapters/amazon-transcribe/index.js
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* Converts AWS Transcribe Json to DraftJs
* see `sample` folder for example of input and output as well as `example-usage.js`
*/
var stripLeadingSpace = function stripLeadingSpace(word) {
return word.replace(/^\s/, '');
};
/**
* @param {json} words - List of words
* @param {string} wordAttributeName - eg 'punct' or 'text' or etc.
* attribute for the word object containing the text. eg word ={ punct:'helo', ... }
* or eg word ={ text:'helo', ... }
*/
var getBestAlternativeForWord = function getBestAlternativeForWord(word) {
if (/punctuation/.test(word.type)) {
return Object.assign(word.alternatives[0], {
confidence: 1
}); //Transcribe doesn't provide a confidence for punctuation
}
var wordWithHighestConfidence = word.alternatives.reduce(function (prev, current) {
return parseFloat(prev.confidence) > parseFloat(current.confidence) ? prev : current;
});
return wordWithHighestConfidence;
};
/**
* Normalizes words so they can be used in
* the generic generateEntitiesRanges() method
**/
var normalizeWord = function normalizeWord(currentWord) {
var bestAlternative = getBestAlternativeForWord(currentWord);
return {
start: parseFloat(currentWord.start_time),
end: parseFloat(currentWord.end_time),
text: bestAlternative.content,
confidence: parseFloat(bestAlternative.confidence)
};
};
var appendPunctuationToPreviousWord = function appendPunctuationToPreviousWord(punctuation, previousWord) {
var punctuationContent = punctuation.alternatives[0].content;
return _objectSpread({}, previousWord, {
alternatives: previousWord.alternatives.map(function (w) {
return _objectSpread({}, w, {
content: w.content + stripLeadingSpace(punctuationContent)
});
})
});
};
var mapPunctuationItemsToWords = function mapPunctuationItemsToWords(words) {
var itemsToRemove = [];
var dirtyArray = words.map(function (word, index) {
var previousWord = {};
if (word.type === 'punctuation') {
itemsToRemove.push(index - 1);
previousWord = words[index - 1];
return appendPunctuationToPreviousWord(word, previousWord);
} else {
return word;
}
});
return dirtyArray.filter(function (item, index) {
return !itemsToRemove.includes(index);
});
};
/**
* groups words list from amazon transcribe transcript based on punctuation.
* @todo To be more accurate, should introduce an honorifics library to do the splitting of the words.
* @param {array} words - array of words objects from kaldi transcript
*/
var amazon_transcribe_groupWordsInParagraphs = function groupWordsInParagraphs(words) {
var results = [];
var paragraph = {
words: [],
text: []
};
words.forEach(function (word) {
var content = getBestAlternativeForWord(word).content;
var normalizedWord = normalizeWord(word);
if (/[.?!]/.test(content)) {
paragraph.words.push(normalizedWord);
paragraph.text.push(content);
results.push(paragraph); // reset paragraph
paragraph = {
words: [],
text: []
};
} else {
paragraph.words.push(normalizedWord);
paragraph.text.push(content);
}
});
return results;
};
var amazon_transcribe_groupSpeakerWordsInParagraphs = function groupSpeakerWordsInParagraphs(words, speakerLabels) {
var wordsBySpeaker = group_words_by_speakers_groupWordsBySpeaker(words, speakerLabels);
return wordsBySpeaker.map(function (speakerGroup) {
return {
words: speakerGroup.words.map(normalizeWord),
text: speakerGroup.words.map(function (w) {
return getBestAlternativeForWord(w).content;
}),
speaker: speakerGroup.speaker
};
});
};
var amazon_transcribe_amazonTranscribeToDraft = function amazonTranscribeToDraft(amazonTranscribeJson) {
var results = [];
var tmpWords = amazonTranscribeJson.results.items;
var speakerLabels = amazonTranscribeJson.results.speaker_labels;
var wordsWithRemappedPunctuation = mapPunctuationItemsToWords(tmpWords);
var speakerSegmentation = typeof speakerLabels != 'undefined';
var wordsByParagraphs = speakerSegmentation ? amazon_transcribe_groupSpeakerWordsInParagraphs(wordsWithRemappedPunctuation, speakerLabels) : amazon_transcribe_groupWordsInParagraphs(wordsWithRemappedPunctuation);
wordsByParagraphs.forEach(function (paragraph, i) {
var draftJsContentBlockParagraph = {
text: paragraph.text.join(' '),
type: 'paragraph',
data: {
speaker: paragraph.speaker ? "Speaker ".concat(paragraph.speaker) : "TBC ".concat(i),
words: paragraph.words,
start: parseFloat(paragraph.words[0].start)
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(paragraph.words, 'text') // wordAttributeName
};
results.push(draftJsContentBlockParagraph);
});
return results;
};
/* harmony default export */ var amazon_transcribe = (amazon_transcribe_amazonTranscribeToDraft);
// CONCATENATED MODULE: ./packages/stt-adapters/ibm/index.js
/**
* Convert IBM json to draftJS
* see `sample` folder for example of input and output as well as `example-usage.js`
*
*/
var ibm_ibmToDraft = function ibmToDraft(ibmJson) {
// helper function to normalise IBM words at line level
var normalizeTimeStampsToWords = function normalizeTimeStampsToWords(timestamps) {
return timestamps.map(function (ibmWord) {
return {
text: ibmWord[0],
start: ibmWord[1],
end: ibmWord[2]
};
});
}; //
var normalizeIBMWordsList = function normalizeIBMWordsList(ibmResults) {
var normalisedResults = [];
ibmResults.forEach(function (result) {
// nested array to keep paragraph segmentation same as IBM lines
normalisedResults.push(normalizeTimeStampsToWords(result.alternatives[0].timestamps)); // TODO: can be revisited - as separate PR by flattening the array like this
// normalisedResults = normalisedResults.concact(normalizeTimeStampsToWords(result.alternatives[0].timestamps));
// addSpeakersToWords function would need adjusting as would be dealing with a 1D array instead of 2D
// if edge case, like in example file, that there's one speaker recognised through all of speaker segemtnation info
// could break into paragraph when is over a minute? at end of IBM line?
// or punctuation, altho IBM does not seem to provide punctuation?
});
return normalisedResults;
}; // TODO: could be separate file
var findSpeakerSegmentForWord = function findSpeakerSegmentForWord(word, speakerSegments) {
var tmpSegment = speakerSegments.find(function (seg) {
var segStart = seg.from;
var segEnd = seg.to;
return word.start === segStart && word.end === segEnd;
}); // if find doesn't find any matches it returns an undefined
if (tmpSegment === undefined) {
// covering edge case orphan word not belonging to any segments
// adding UKN speaker label
return 'UKN';
} else {
// find returns the first element that matches the criteria
return "S_".concat(tmpSegment.speaker);
}
}; // add speakers to words
var addSpeakersToWords = function addSpeakersToWords(ibmWords, ibmSpeakers) {
return ibmWords.map(function (lines) {
return lines.map(function (word) {
word.speaker = findSpeakerSegmentForWord(word, ibmSpeakers);
return word;
});
});
};
var ibmNormalisedWordsToDraftJs = function ibmNormalisedWordsToDraftJs(ibmNormalisedWordsWithSpeakers) {
var draftJsParagraphsResults = [];
ibmNormalisedWordsWithSpeakers.forEach(function (ibmParagraph) {
var draftJsContentBlockParagraph = {
text: ibmParagraph.map(function (word) {
return word.text;
}).join(' '),
type: 'paragraph',
data: {
// Assuming each paragraph in IBM line is the same
// for context it just seems like the IBM data structure gives you word level speakers,
// but also gives you "lines" so assuming each word in a line has the same speaker.
speaker: ibmParagraph[0].speaker,
words: ibmParagraph,
start: ibmParagraph[0].start
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(ibmParagraph, 'text') // wordAttributeName
};
draftJsParagraphsResults.push(draftJsContentBlockParagraph);
});
return draftJsParagraphsResults;
};
var normalisedWords = normalizeIBMWordsList(ibmJson.results[0].results); // TODO: nested array of words, to keep some sort of paragraphs, in case there's only one speaker
// can be refactored/optimised later
var ibmNormalisedWordsWithSpeakers = addSpeakersToWords(normalisedWords, ibmJson.results[0].speaker_labels);
var ibmDratJs = ibmNormalisedWordsToDraftJs(ibmNormalisedWordsWithSpeakers);
return ibmDratJs;
};
/* harmony default export */ var ibm = (ibm_ibmToDraft);
// EXTERNAL MODULE: ./packages/stt-adapters/digital-paper-edit/group-words-by-speakers.js
var digital_paper_edit_group_words_by_speakers = __webpack_require__(22);
// CONCATENATED MODULE: ./packages/stt-adapters/digital-paper-edit/index.js
/**
* Convert Digital Paper Edit transcript json format to DraftJS
* More details see
* https://github.com/bbc/digital-paper-edit
*/
/**
* groups words list from kaldi transcript based on punctuation.
* @todo To be more accurate, should introduce an honorifics library to do the splitting of the words.
* @param {array} words - array of words opbjects from kaldi transcript
*/
var digital_paper_edit_groupWordsInParagraphs = function groupWordsInParagraphs(words) {
var results = [];
var paragraph = {
words: [],
text: []
};
words.forEach(function (word) {
paragraph.words.push(word);
paragraph.text.push(word.text); // if word contains punctuation
if (/[.?!]/.test(word.text)) {
paragraph.text = paragraph.text.join(' ');
results.push(paragraph); // reset paragraph
paragraph = {
words: [],
text: []
};
}
});
return results;
};
var digital_paper_edit_generateDraftJsContentBlock = function generateDraftJsContentBlock(paragraph) {
var words = paragraph.words,
text = paragraph.text,
speaker = paragraph.speaker;
var start = words.length > 0 ? words[0].start : 0;
return {
text: text,
type: 'paragraph',
data: {
speaker: speaker,
words: words,
start: start
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(words, 'text') // wordAttributeName
};
};
var digital_paper_edit_digitalPaperEditToDraft = function digitalPaperEditToDraft(digitalPaperEditTranscriptJson) {
var wordsByParagraphs = [];
var words = digitalPaperEditTranscriptJson.words,
paragraphs = digitalPaperEditTranscriptJson.paragraphs;
if (!paragraphs) {
wordsByParagraphs = digital_paper_edit_groupWordsInParagraphs(words);
} else {
wordsByParagraphs = Object(digital_paper_edit_group_words_by_speakers["default"])(words, paragraphs);
}
var results = wordsByParagraphs.map(function (paragraph, i) {
if (!paragraph.speaker) {
paragraph.speaker = "TBC ".concat(i);
}
return digital_paper_edit_generateDraftJsContentBlock(paragraph);
});
return results;
};
/* harmony default export */ var digital_paper_edit = (digital_paper_edit_digitalPaperEditToDraft);
// CONCATENATED MODULE: ./packages/stt-adapters/create-entity-map/index.js
/**
* Helper function to generate draft.js entityMap from draftJS blocks,
*/
/**
* helper function to flatten a list.
* converts nested arrays into one dimensional array
* @param {array} list
*/
var flatten = function flatten(list) {
return list.reduce(function (a, b) {
return a.concat(Array.isArray(b) ? flatten(b) : b);
}, []);
};
/**
* helper function to create createEntityMap
* @param {*} blocks - draftJs blocks
*/
var createEntityMap = function createEntityMap(blocks) {
var entityRanges = blocks.map(function (block) {
return block.entityRanges;
});
var flatEntityRanges = flatten(entityRanges);
var entityMap = {};
flatEntityRanges.forEach(function (data) {
entityMap[data.key] = {
type: 'WORD',
mutability: 'MUTABLE',
data: data
};
});
return entityMap;
};
/* harmony default export */ var create_entity_map = (createEntityMap);
// CONCATENATED MODULE: ./packages/stt-adapters/google-stt/index.js
/**
* Converts GCP Speech to Text Json to DraftJs
* see `sample` folder for example of input and output as well as `example-usage.js`
*/
var NANO_SECOND = 1000000000;
/**
* attribute for the sentences object containing the text. eg sentences ={ punct:'helo', ... }
* or eg sentences ={ text:'hello', ... }
* @param sentences
*/
var getBestAlternativeSentence = function getBestAlternativeSentence(sentences) {
if (sentences.alternatives.length === 0) {
return sentences[0];
}
var sentenceWithHighestConfidence = sentences.alternatives.reduce(function (prev, current) {
return parseFloat(prev.confidence) > parseFloat(current.confidence) ? prev : current;
});
return sentenceWithHighestConfidence;
};
var trimLeadingAndTailingWhiteSpace = function trimLeadingAndTailingWhiteSpace(text) {
return text.trim();
};
/**
* GCP does not provide a nanosecond attribute if the word starts at 0 nanosecond
* @param startSecond
* @param nanoSecond
* @returns {number}
*/
var computeTimeInSeconds = function computeTimeInSeconds(startSecond, nanoSecond) {
var seconds = parseFloat(startSecond);
if (nanoSecond !== undefined) {
seconds = seconds + parseFloat(nanoSecond / NANO_SECOND);
}
return seconds;
};
/**
* Normalizes words so they can be used in
* the generic generateEntitiesRanges() method
**/
var google_stt_normalizeWord = function normalizeWord(currentWord, confidence) {
return {
start: computeTimeInSeconds(currentWord.startTime.seconds, currentWord.startTime.nanos),
end: computeTimeInSeconds(currentWord.endTime.seconds, currentWord.endTime.nanos),
text: currentWord.word,
confidence: confidence
};
};
/**
* groups words list from GCP Speech to Text response.
* @param {array} sentences - array of sentence objects from GCP STT
*/
var google_stt_groupWordsInParagraphs = function groupWordsInParagraphs(sentences) {
var results = [];
var paragraph = {
words: [],
text: []
};
sentences.forEach(function (sentence) {
var bestAlternative = getBestAlternativeSentence(sentence);
paragraph.text.push(trimLeadingAndTailingWhiteSpace(bestAlternative.transcript));
bestAlternative.words.forEach(function (word) {
paragraph.words.push(google_stt_normalizeWord(word, bestAlternative.confidence));
});
results.push(paragraph);
paragraph = {
words: [],
text: []
};
});
return results;
};
var google_stt_gcpSttToDraft = function gcpSttToDraft(gcpSttJson) {
var results = []; // const speakerLabels = gcpSttJson.results[0]['alternatives'][0]['words'][0]['speakerTag']
// let speakerSegmentation = typeof(speakerLabels) != 'undefined';
var wordsByParagraphs = google_stt_groupWordsInParagraphs(gcpSttJson.results);
wordsByParagraphs.forEach(function (paragraph, i) {
var draftJsContentBlockParagraph = {
text: paragraph.text.join(' '),
type: 'paragraph',
data: {
speaker: paragraph.speaker ? "Speaker ".concat(paragraph.speaker) : "TBC ".concat(i),
words: paragraph.words,
start: parseFloat(paragraph.words[0].start)
},
// the entities as ranges are each word in the space-joined text,
// so it needs to be compute for each the offset from the beginning of the paragraph and the length
entityRanges: Object(generate_entities_ranges["a" /* default */])(paragraph.words, 'text') // wordAttributeName
};
results.push(draftJsContentBlockParagraph);
});
return results;
};
/* harmony default export */ var google_stt = (google_stt_gcpSttToDraft);
// CONCATENATED MODULE: ./packages/stt-adapters/index.js
/**
* Adapters for STT conversion
* @param {json} transcriptData - A json transcript with some word accurate timecode
* @param {string} sttJsonType - the type of transcript supported by the available adapters
*/
var stt_adapters_sttJsonAdapter = function sttJsonAdapter(transcriptData, sttJsonType) {
var blocks;
switch (sttJsonType) {
case 'bbckaldi':
blocks = bbc_kaldi(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
case 'autoedit2':
blocks = autoEdit2(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
case 'speechmatics':
blocks = speechmatics(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
case 'ibm':
blocks = ibm(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
case 'draftjs':
return transcriptData;
// (typeof transcriptData === 'string')? JSON.parse(transcriptData): transcriptData;
case 'amazontranscribe':
blocks = amazon_transcribe(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
case 'digitalpaperedit':
blocks = digital_paper_edit(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
case 'google-stt':
blocks = google_stt(transcriptData);
return {
blocks: blocks,
entityMap: create_entity_map(blocks)
};
default:
// code block
console.error('Did not recognize the stt engine.');
}
};
/* harmony default export */ var stt_adapters = __webpack_exports__["default"] = (stt_adapters_sttJsonAdapter);
/***/ }),
/***/ 22:
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
__webpack_require__.r(__webpack_exports__);
/**
edge cases
- more segments then words - not an issue if you start by matching words with segment
and handle edge case where it doesn't find a match
- more words then segments - orphan words?
*
* Takes in list of words and list of paragraphs (paragraphs have speakers info associated with it)
```js
{
"words": [
{
"id": 0,
"start": 13.02,
"end": 13.17,
"text": "There"
},
{
"id": 1,
"start": 13.17,
"end": 13.38,
"text": "is"
},
...
],
"paragraphs": [
{
"id": 0,
"start": 13.02,
"end": 13.86,
"speaker": "TBC 00"
},
{
"id": 1,
"start": 13.86,
"end": 19.58,
"speaker": "TBC 1"
},
...
]
}
```
* and returns a list of words grouped into paragraphs, with words, text and speaker attribute
```js
[
{
"words": [
{
"id": 0,
"start": 13.02,
"end": 13.17,
"text": "There"
},
{
"id": 1,
"start": 13.17,
"end": 13.38,
"text": "is"
},
{
"id": 2,
"start": 13.38,
"end": 13.44,
"text": "a"
},
{
"id": 3,
"start": 13.44,
"end": 13.86,
"text": "day."
}
],
"text": "There is a day.",
"speaker": "TBC 00"
},
...
]
```
*/
function groupWordsInParagraphsBySpeakers(words, segments) {
var result = addWordsToSpeakersParagraphs(words, segments);
return result;
}
;
function addWordsToSpeakersParagraphs(words, segments) {
var results = [];
var currentSegment = 'UKN';
var currentSegmentIndex = 0;
var previousSegmentIndex = 0;
var paragraph = {
words: [],
text: '',
speaker: ''
};
words.forEach(function (word) {
currentSegment = findSegmentForWord(word, segments); // if a segment exists for the word
if (currentSegment) {
currentSegmentIndex = segments.indexOf(currentSegment);
if (currentSegmentIndex === previousSegmentIndex) {
paragraph.words.push(word);
paragraph.text += word.text + ' ';
paragraph.speaker = currentSegment.speaker;
} else {
previousSegmentIndex = currentSegmentIndex;
paragraph.text.trim();
results.push(paragraph);
paragraph = {
words: [],
text: '',
speaker: ''
};
paragraph.words.push(word);
paragraph.text += word.text + ' ';
paragraph.speaker = currentSegment.speaker;
}
}
});
results.push(paragraph);
return results;
}
/**
* Helper functions
*/
/**
* given word start and end time attributes
* looks for segment range that contains that word
* if it doesn't find any it returns a segment with `UKN`
* speaker attributes.
* @param {object} word - word object
* @param {array} segments - list of segments objects
* @return {object} - a single segment whose range contains the word
*/
function findSegmentForWord(word, segments) {
var tmpSegment = segments.find(function (seg) {
if (word.start >= seg.start && word.end <= seg.end) {
return seg;
}
});
return tmpSegment;
}
/* harmony default export */ __webpack_exports__["default"] = (groupWordsInParagraphsBySpeakers);
/***/ }),
/***/ 7:
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
/**
* Helper function to generate draft.js entities,
* see unit test for example data structure
* it adds offset and length to recognise word in draftjs
*/
/**
* @param {json} words - List of words
* @param {string} wordAttributeName - eg 'punct' or 'text' or etc.
* attribute for the word object containing the text. eg word ={ punct:'helo', ... }
* or eg word ={ text:'helo', ... }
*/
var generateEntitiesRanges = function generateEntitiesRanges(words, wordAttributeName) {
var position = 0;
return words.map(function (word) {
var result = {
start: word.start,
end: word.end,
confidence: word.confidence,
text: word[wordAttributeName],
offset: position,
length: word[wordAttributeName].length,
key: Math.random().toString(36).substring(6)
}; // increase position counter - to determine word offset in paragraph
position = position + word[wordAttributeName].length + 1;
return result;
});
};
/* harmony default export */ __webpack_exports__["a"] = (generateEntitiesRanges);
/***/ })
/******/ });
//# sourceMappingURL=sttJsonAdapter.js.map