import { store, history, dispatch } from 'app/store' import { TEXT_ANNOTATION_TYPES, MEDIA_ANNOTATION_TYPES, INLINE_UTILITY_ANNOTATION_TYPES, FULLSCREEN_UTILITY_ANNOTATION_TYPES, } from 'app/constants' import { timestampToSeconds } from 'app/utils' export const buildParagraphs = (annotationOrder, sectionCount, footnoteCount) => { const state = store.getState() const { lookup: annotationLookup } = state.annotation.index const { lookup: paragraphLookup } = state.paragraph.index let currentParagraph = {} const paragraphs = [] const footnotes = [] sectionCount = (sectionCount || 0) footnoteCount = (footnoteCount || 0) // loop over the annotations in time order annotationOrder.forEach((annotation_id, i) => { // fetch annotation and paragraph const annotation = annotationLookup[annotation_id] const paragraph = paragraphLookup[annotation.paragraph_id] // if this annotation is a utility (curtain, gallery instructions), then skip it if (FULLSCREEN_UTILITY_ANNOTATION_TYPES.has(annotation.type)) { return } // if this annotation is an inline utility (intro div) don't skip it if (INLINE_UTILITY_ANNOTATION_TYPES.has(annotation.type)) { if (!annotation.settings.hideCitation) { const item = { id: ('index_' + annotation.id), type: annotation.type, start_ts: annotation.start_ts, end_ts: 0, annotations: [annotation], } if (annotation.type === 'intro') { paragraphs.unshift(item) } else { paragraphs.push(item) } } return } // if this annotation is media, then insert it after the current paragraph if (MEDIA_ANNOTATION_TYPES.has(annotation.type)) { // add option to hide the citation from the transcript if (!annotation.settings.hideCitation) { paragraphs.push({ id: ('index_' + i), type: annotation.type, start_ts: annotation.start_ts, end_ts: 0, isMedia: true, annotations: [annotation], }) } return } // if this annotation is from a different paragraph, make a new paragraph if (annotation.type === 'section_heading' || annotation.type === 'heading_text' || annotation.paragraph_id !== currentParagraph.id) { const paragraph_type = getParagraphType(annotation, paragraph) currentParagraph = { id: annotation.paragraph_id || ('index_' + i), type: paragraph_type, start_ts: annotation.start_ts, end_ts: 0, annotations: [], settings: paragraph ? paragraph.settings : {}, } if (paragraph && paragraph.type === 'hidden') { currentParagraph.hidden = true } if (annotation.type === 'section_heading') { currentParagraph.sectionIndex = sectionCount++ currentParagraph.id = 'section_' + currentParagraph.sectionIndex if (annotation.settings.hidden) { currentParagraph.hidden = true } } paragraphs.push(currentParagraph) } // accumulate footnotes if (annotation.type === 'footnote') { // bump footnote count and attach it to this annotation, so we can find it later footnoteCount += 1 if (!annotation.footnote_id || annotation.footnote_id === 1) { annotation.footnote_id = footnoteCount } // set annotation start point to the start of the previous sentence if (annotation.settings.actual_ts) { annotation.start_ts = timestampToSeconds(annotation.settings.actual_ts) } else if (currentParagraph.annotations[currentParagraph.annotations.length - 1]) { annotation.start_ts = currentParagraph.annotations[currentParagraph.annotations.length - 1].start_ts } footnotes.push(annotation) } // if this annotation is a paragraph_end, set the end timestamp if (annotation.type === 'paragraph_end') { currentParagraph.end_ts = annotation.start_ts } // otherwise, just append this annotation to the paragraph else { currentParagraph.annotations.push(annotation) } }) // finally, go over the paragraphs to fix some timestamps const len = paragraphs.length paragraphs.forEach((paragraph, i) => { // update the end_ts, if none is set if (!paragraph.end_ts) { // successive paragraphs get it from the next paragraph if (i < len - 1) { paragraph.end_ts = paragraphs[i+1].start_ts - 0.1 } // the last paragraph checks its last annotation. else { const { annotations } = paragraph const lastAnnotation = annotations[annotations.length - 1] paragraph.end_ts = lastAnnotation.end_ts || (lastAnnotation.start_ts + 0.1) } } // push the timestamp for media up a tick so it sorts above paragraphs if (paragraph.isMedia) { paragraph.start_ts = paragraph.start_ts - 0.01 } }) return { paragraphs, footnotes } } const getParagraphType = (annotation, paragraph) => { if (annotation.type === 'section_heading') { return annotation.type } if (annotation.type === 'heading_text') { return annotation.type } if (!paragraph) { return annotation.type } return paragraph.type } const REGEXP_ALL_COMMAS = new RegExp(',', 'g') export const parseSubtitles = (mediaItem, timeOffset) => { if (!mediaItem || !mediaItem.settings.subtitles) return const groups = mediaItem.settings.subtitles.split("\n\n") const subtitles = groups.map((group) => { if (!group) return const lines = group.trim().split("\n") if (!lines.length || !parseInt(lines[0])) { return null } let ts_parts = lines[1].replace(REGEXP_ALL_COMMAS, '.').split(" --> ").map(timestampToSeconds) return { id: parseInt(lines[0]), start_ts: ts_parts[0] + timeOffset, end_ts: ts_parts[1] + timeOffset, lines: lines.slice(2), } }).filter(a => !!a) return subtitles }