UNPKG

@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
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__) { "use strict"; // 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__) { "use strict"; __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__) { "use strict"; /** * 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