From 2869a7daba329b06c8bf1e77506b90eea42cef84 Mon Sep 17 00:00:00 2001 From: Pushpender Saini <54404738+PushpenderSaini0@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:50:11 +0530 Subject: [PATCH] Add SRT export --- src/components/Transcript.tsx | 87 +++++++++++++++++++++-------------- src/utils/AudioUtils.ts | 57 +++++++++++++++++++---- 2 files changed, 99 insertions(+), 45 deletions(-) diff --git a/src/components/Transcript.tsx b/src/components/Transcript.tsx index 4ba47721..7aed8073 100644 --- a/src/components/Transcript.tsx +++ b/src/components/Transcript.tsx @@ -1,7 +1,7 @@ import { useRef, useEffect } from "react"; import { TranscriberData } from "../hooks/useTranscriber"; -import { formatAudioTimestamp } from "../utils/AudioUtils"; +import { formatAudioTimestamp, formatSrtTimeRange } from "../utils/AudioUtils"; interface Props { transcribedData: TranscriberData | undefined; @@ -38,14 +38,32 @@ export default function Transcript({ transcribedData }: Props) { const blob = new Blob([jsonData], { type: "application/json" }); saveBlob(blob, "transcript.json"); }; + const exportSRT = () => { + let chunks = transcribedData?.chunks ?? []; + let srt = ""; + for (let i = 0; i < chunks.length; i++) { + srt += `${i + 1}\n`; + // TODO - Check why 2nd timestamp is number | null + srt += `${formatSrtTimeRange(chunks[i].timestamp[0], chunks[i].timestamp[1] ?? chunks[i].timestamp[0])}\n`; + srt += `${chunks[i].text}\n\n`; + } + const blob = new Blob([srt], { type: "text/plain" }); + saveBlob(blob, "transcript.srt"); + } + + const exportButtons = [ + { name: "TXT", onClick: exportTXT, }, + { name: "JSON", onClick: exportJSON, }, + { name: "SRT", onClick: exportSRT, }, + ] // Scroll to the bottom when the component updates useEffect(() => { if (divRef.current) { const diff = Math.abs( divRef.current.offsetHeight + - divRef.current.scrollTop - - divRef.current.scrollHeight, + divRef.current.scrollTop - + divRef.current.scrollHeight, ); if (diff <= 64) { @@ -56,38 +74,37 @@ export default function Transcript({ transcribedData }: Props) { }); return ( -
- {transcribedData && - transcribedData.chunks.map((chunk, i) => ( -
-
- {formatAudioTimestamp(chunk.timestamp[0])} + <> +
+ {transcribedData && + transcribedData.chunks.map((chunk, i) => ( +
+
+ {formatAudioTimestamp(chunk.timestamp[0])} +
+ {chunk.text}
- {chunk.text} -
- ))} - {transcribedData && !transcribedData.isBusy && ( -
- - -
- )} -
+ ))} +
+ { + transcribedData && !transcribedData.isBusy && ( + < div className='w-full text-right mx-2'> + {exportButtons.map((button, i) => ( + + ))} +
+ ) + } + ); } diff --git a/src/utils/AudioUtils.ts b/src/utils/AudioUtils.ts index 82e848b6..307f4945 100644 --- a/src/utils/AudioUtils.ts +++ b/src/utils/AudioUtils.ts @@ -1,14 +1,51 @@ -function padTime(time: number) { - return String(time).padStart(2, "0"); +const HOURS_IN_SECONDS = 3600; +const MINUTES_IN_SECONDS = 60; + +function padTime(time: number, padding: number = 2) { + return String(time).padStart(padding, "0"); +} + +function formatTimestamp(time: number) { + + const hours = Math.floor(time / HOURS_IN_SECONDS); + const hoursRemainder = time - hours * HOURS_IN_SECONDS; + + const minutes = Math.floor(hoursRemainder / MINUTES_IN_SECONDS); + const minutesRemainder = hoursRemainder - minutes * MINUTES_IN_SECONDS; + + const seconds = Math.floor(minutesRemainder); + const secondsRemainder = minutesRemainder - seconds; + + const milliseconds = Math.floor(secondsRemainder * 1000); + + return { hours, minutes, seconds, milliseconds }; } export function formatAudioTimestamp(time: number) { - const hours = (time / (60 * 60)) | 0; - time -= hours * (60 * 60); - const minutes = (time / 60) | 0; - time -= minutes * 60; - const seconds = time | 0; - return `${hours ? padTime(hours) + ":" : ""}${padTime(minutes)}:${padTime( - seconds, - )}`; + + const { hours, minutes, seconds } = formatTimestamp(time); + + // Hide hours if not needed + const hoursString = hours ? padTime(hours) + ":" : ""; + const minutesString = padTime(minutes) + ":"; + const secondsString = padTime(seconds); + + return `${hoursString}${minutesString}${secondsString}`; } + +function formatSrtTimestamp(time: number) { + + const { hours, minutes, seconds, milliseconds } = formatTimestamp(time); + + const hoursString = padTime(hours) + ":"; + const minutesString = padTime(minutes) + ":"; + const secondsString = padTime(seconds) + ","; + const millisecondsString = padTime(milliseconds, 3); + + return `${hoursString}${minutesString}${secondsString}${millisecondsString}`; +} + + +export function formatSrtTimeRange(start: number, end: number) { + return `${formatSrtTimestamp(start)} --> ${formatSrtTimestamp(end)}`; +} \ No newline at end of file