Allow txt export with timestampts
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { exportFilename, transcriptToSrt, transcriptToTxt } from "../lib/export";
|
||||
import {
|
||||
exportFilename,
|
||||
transcriptToSrt,
|
||||
transcriptToTimestampedTxt,
|
||||
transcriptToTxt,
|
||||
} from "../lib/export";
|
||||
import type { TranscriptChunk } from "../worker/protocol";
|
||||
|
||||
interface TranscriptViewProps {
|
||||
@@ -121,6 +126,22 @@ export function TranscriptView({
|
||||
.srt
|
||||
</button>
|
||||
)}
|
||||
{result && result.chunks.length > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className="copy-button"
|
||||
title="Text with timestamps"
|
||||
onClick={() =>
|
||||
download(
|
||||
transcriptToTimestampedTxt(result.chunks),
|
||||
exportFilename(exportName, "timestamps.txt"),
|
||||
"text/plain",
|
||||
)
|
||||
}
|
||||
>
|
||||
.txt+ts
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="copy-button"
|
||||
|
||||
+30
-1
@@ -59,10 +59,39 @@ export function transcriptToSrt(chunks: TranscriptChunk[]): string {
|
||||
return cues.length === 0 ? "" : `${cues.join("\n\n")}\n`;
|
||||
}
|
||||
|
||||
/** Seconds → compact clock timestamp, e.g. 3.21 → "00:03.2" (mm:ss.s). */
|
||||
export function formatClockTimestamp(seconds: number): string {
|
||||
const totalTenths = Math.max(0, Math.round(seconds * 10));
|
||||
const tenths = totalTenths % 600;
|
||||
const m = (totalTenths - tenths) / 600;
|
||||
return `${String(m).padStart(2, "0")}:${(tenths / 10).toFixed(1).padStart(4, "0")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamped chunks → plain text, one line per chunk:
|
||||
* "[00:03.2 – 00:07.9] text". A null end time — Whisper sometimes never
|
||||
* closes the last segment — omits the range end: "[00:03.2] text".
|
||||
* Empty-text chunks are skipped, mirroring transcriptToSrt.
|
||||
*/
|
||||
export function transcriptToTimestampedTxt(chunks: TranscriptChunk[]): string {
|
||||
const lines = chunks
|
||||
.filter((chunk) => chunk.text.trim() !== "")
|
||||
.map((chunk) => {
|
||||
const [start, end] = chunk.timestamp;
|
||||
const range =
|
||||
end === null
|
||||
? formatClockTimestamp(start)
|
||||
: `${formatClockTimestamp(start)} – ${formatClockTimestamp(end)}`;
|
||||
return `[${range}] ${chunk.text.trim()}`;
|
||||
});
|
||||
return lines.length === 0 ? "" : `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download filename for an export: the source file's base name with the
|
||||
* extension swapped ("talk.m4a" → "talk.srt"), or "transcript.<ext>" when no
|
||||
* usable source name is known.
|
||||
* usable source name is known. `ext` may be compound ("timestamps.txt") so
|
||||
* sibling exports of the same format get distinct names.
|
||||
*/
|
||||
export function exportFilename(sourceName: string | null | undefined, ext: string): string {
|
||||
const trimmed = sourceName?.trim() ?? "";
|
||||
|
||||
Reference in New Issue
Block a user