Spaces:
Sleeping
Sleeping
| import { generateId } from "@designcombo/timeline"; | |
| import { ICaption } from "@designcombo/types"; | |
| interface Word { | |
| start: number; | |
| end: number; | |
| word: string; | |
| } | |
| interface ICaptionLine { | |
| text: string; | |
| words: Word[]; | |
| width: number; | |
| start: number; | |
| end: number; | |
| } | |
| export const generateCaption = ( | |
| captionLine: ICaptionLine, | |
| fontInfo: FontInfo, | |
| options: Options, | |
| sourceUrl: string | |
| ): ICaption => { | |
| const caption = { | |
| id: generateId(), | |
| type: "caption", | |
| name: "Caption", | |
| display: { | |
| from: options.displayFrom + captionLine.start * 1000, | |
| to: options.displayFrom + captionLine.end * 1000 | |
| }, | |
| metadata: { | |
| sourceUrl, | |
| parentId: options.parentId | |
| }, | |
| details: { | |
| // top: 100, | |
| appearedColor: "#FFFFFF", | |
| activeColor: "#50FF12", | |
| activeFillColor: "#7E12FF", | |
| color: "#DADADA", | |
| backgroundColor: "transparent", | |
| borderColor: "#000000", | |
| borderWidth: 5, | |
| text: captionLine.text, | |
| fontSize: fontInfo.fontSize, | |
| width: options.containerWidth, | |
| fontFamily: fontInfo.fontFamily, | |
| fontUrl: fontInfo.fontUrl, | |
| textAlign: "center", | |
| linesPerCaption: options.linesPerCaption, | |
| words: captionLine.words.map((w) => ({ | |
| ...w, | |
| start: w.start * 1000, | |
| end: w.end * 1000 | |
| })) | |
| } as unknown | |
| }; | |
| return caption as ICaption; | |
| }; | |
| interface Word { | |
| word: string; | |
| start: number; | |
| end: number; | |
| confidence: number; | |
| } | |
| interface CaptionsInput { | |
| sourceUrl: string; | |
| results: { | |
| main: { | |
| words: Word[]; | |
| }; | |
| }; | |
| } | |
| function createCaptionLines( | |
| input: CaptionsInput, | |
| fontInfo: FontInfo, | |
| options: Options | |
| ): ICaptionLine[] { | |
| const canvas = document.createElement("canvas"); | |
| const context = canvas.getContext("2d"); | |
| if (!context) return []; | |
| context.font = `${fontInfo.fontSize}px ${fontInfo.fontFamily}`; | |
| const captionLines: ICaptionLine[] = []; | |
| const words: Word[] = input.results.main.words; | |
| let currentLine: ICaptionLine = { | |
| text: "", | |
| words: [], | |
| width: 0, | |
| start: words.length > 0 ? words[0].start : 0, | |
| end: 0 | |
| }; | |
| let linesCount = 0; | |
| words.forEach((wordObj, index) => { | |
| const wordWidth = context.measureText(wordObj.word).width; | |
| if ( | |
| currentLine.width + wordWidth > options.containerWidth - 100 || | |
| currentLine.text.endsWith(".") | |
| ) { | |
| const advance = currentLine.text.endsWith("."); | |
| // Check if it's time to start a new caption set | |
| if (linesCount + 1 === options.linesPerCaption || advance) { | |
| // Only push when lines count is correct | |
| captionLines.push(currentLine); | |
| linesCount = 0; | |
| // Reset currentLine for the next set of lines | |
| currentLine = { | |
| text: "", | |
| words: [], | |
| width: 0, | |
| start: wordObj.start, | |
| end: wordObj.end | |
| }; | |
| } else { | |
| linesCount += 1; | |
| // Reset currentLine.width but keep other details to continue accumulation | |
| currentLine.width = 0; | |
| } | |
| } | |
| // Accumulate words and width for the current line | |
| currentLine.text += (currentLine.text ? " " : "") + wordObj.word; | |
| currentLine.words.push(wordObj); | |
| currentLine.width += wordWidth; | |
| currentLine.end = wordObj.end; | |
| // Push the final line if it's the last word | |
| if (index === words.length - 1 && currentLine.text) { | |
| captionLines.push(currentLine); | |
| } | |
| }); | |
| return captionLines; | |
| } | |
| interface FontInfo { | |
| fontFamily: string; | |
| fontUrl: string; | |
| fontSize: number; | |
| } | |
| interface Options { | |
| containerWidth: number; | |
| linesPerCaption: number; | |
| parentId: string; | |
| displayFrom: number; | |
| } | |
| export function generateCaptions( | |
| input: CaptionsInput, | |
| fontInfo: FontInfo, | |
| options: Options | |
| ): ICaption[] { | |
| const captionLines = createCaptionLines(input, fontInfo, options); | |
| const captions = captionLines.map((line) => | |
| generateCaption(line, fontInfo, options, input.sourceUrl) | |
| ); | |
| return captions; | |
| } | |