Skip to content

Commit e172186

Browse files
committed
Refine transcript action buttons
1 parent 229239d commit e172186

1 file changed

Lines changed: 127 additions & 35 deletions

File tree

src/components/transcript/TranscriptPanel.tsx

Lines changed: 127 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -373,14 +373,16 @@ const TranscriptControlsPanel = () => {
373373
};
374374

375375
export const TranscriptPanel = () => {
376+
const LARGE_TRANSCRIPTION_FILE_SIZE = 25 * 1024 * 1024;
376377
const { t } = useTranslation();
377378
const navigate = useNavigate();
378379
const {
379380
getCurrentMediaTranscripts,
380381
isTranscribing,
381382
currentFile,
382383
currentYouTube,
383-
toggleTranscribing,
384+
startTranscribing,
385+
stopTranscribing,
384386
addTranscriptSegment,
385387
clearTranscript,
386388
exportTranscript,
@@ -391,6 +393,9 @@ export const TranscriptPanel = () => {
391393
setSelectedBookmarkId,
392394
setCurrentTime,
393395
setIsPlaying,
396+
loopStart,
397+
loopEnd,
398+
duration,
394399
} = usePlayerStore();
395400

396401
const transcriptSegments = getCurrentMediaTranscripts();
@@ -426,7 +431,6 @@ export const TranscriptPanel = () => {
426431
})
427432
: transcriptSegments;
428433

429-
430434
const handleTabSelect = (id: string | null) => {
431435
if (id) {
432436
// If switching to a specific bookmark tab
@@ -489,8 +493,22 @@ export const TranscriptPanel = () => {
489493
}
490494
};
491495

496+
const getPreferredTranscriptRange = () => {
497+
if (loopStart !== null && loopEnd !== null && loopEnd > loopStart) {
498+
return { start: loopStart, end: loopEnd };
499+
}
500+
501+
return undefined;
502+
};
503+
504+
const handleTranscribeDefault = () => {
505+
transcribeMedia(getPreferredTranscriptRange());
506+
};
507+
492508
const handleTranscribeFull = () => {
493-
transcribeMedia();
509+
transcribeMedia(duration > 0 ? { start: 0, end: duration } : undefined, {
510+
forceFullRange: true,
511+
});
494512
};
495513

496514
const [isProcessing, setIsProcessing] = useState(false);
@@ -501,6 +519,14 @@ export const TranscriptPanel = () => {
501519
const [currentProvider, setCurrentProvider] = useState<TranscriptionProvider>("openai");
502520
const transcriptRef = useRef<HTMLDivElement>(null);
503521

522+
const isBookmarkTranscriptEmpty = Boolean(activeTabId) && filteredSegments.length === 0;
523+
const isFullTranscriptEmpty = !activeTabId && transcriptSegments.length === 0;
524+
const shouldShowTranscribeActionsInHeader =
525+
!isProcessing &&
526+
!showApiKeyInput &&
527+
!isBookmarkTranscriptEmpty &&
528+
!isFullTranscriptEmpty;
529+
504530
// Load API key and transcription provider from localStorage on component mount
505531
useEffect(() => {
506532
const loadSettings = () => {
@@ -530,8 +556,23 @@ export const TranscriptPanel = () => {
530556
navigate("/ai-settings");
531557
};
532558

559+
type TimeRange = { start: number; end: number };
560+
561+
const normalizeRange = (range?: Partial<TimeRange>): TimeRange | undefined => {
562+
if (
563+
range &&
564+
typeof range.start === "number" &&
565+
typeof range.end === "number" &&
566+
range.end > range.start
567+
) {
568+
return { start: range.start, end: range.end };
569+
}
570+
571+
return undefined;
572+
};
573+
533574
// Function to extract audio from the media file
534-
const extractAudioFromMedia = async (range?: { start: number; end: number }): Promise<Blob> => {
575+
const extractAudioFromMedia = async (range?: TimeRange): Promise<Blob> => {
535576
return new Promise((resolve, reject) => {
536577
if (!currentFile) {
537578
reject(new Error(t("transcript.noFileLoaded")));
@@ -541,10 +582,18 @@ export const TranscriptPanel = () => {
541582
// For audio files, we can use them directly or slice them if range provided
542583
if (currentFile.type.includes("audio")) {
543584
fetch(currentFile.url)
544-
.then((response) => response.arrayBuffer())
585+
.then(async (response) => {
586+
if (!range) {
587+
resolve(await response.blob());
588+
return;
589+
}
590+
591+
return response.arrayBuffer();
592+
})
545593
.then(async (arrayBuffer) => {
546-
// If no range, return original blob if possible, or decode/encode to ensure WAV
547-
// But to support detailed range slicing, we should always decode
594+
if (!range || !arrayBuffer) {
595+
return;
596+
}
548597

549598
try {
550599
const audioContext = new AudioContext();
@@ -588,6 +637,7 @@ export const TranscriptPanel = () => {
588637

589638
// Encode to WAV
590639
const wavBlob = encodeWAV(slicedData, audioBuffer.sampleRate);
640+
audioContext.close();
591641
resolve(wavBlob);
592642

593643
} catch (err) {
@@ -661,7 +711,10 @@ export const TranscriptPanel = () => {
661711
};
662712

663713
// Function to transcribe the current media using the selected transcription service
664-
const transcribeMedia = async (range?: { start: number; end: number }) => {
714+
const transcribeMedia = async (
715+
requestedRange?: Partial<TimeRange>,
716+
options?: { forceFullRange?: boolean }
717+
) => {
665718
// Check if we have media to transcribe
666719
if (!currentFile && !currentYouTube) {
667720
toast.error(t("transcript.noMediaToTranscribe"));
@@ -674,16 +727,28 @@ export const TranscriptPanel = () => {
674727
return;
675728
}
676729

730+
const range = options?.forceFullRange
731+
? normalizeRange(requestedRange)
732+
: normalizeRange(requestedRange) || getPreferredTranscriptRange();
733+
734+
if (
735+
currentFile &&
736+
!range &&
737+
currentFile.size > LARGE_TRANSCRIPTION_FILE_SIZE
738+
) {
739+
toast(t("transcript.largeFileRangeRecommended"));
740+
}
741+
677742
try {
678743
setIsProcessing(true);
679744
setErrorMessage("");
680745

681746
// Only clear if doing full transcript
682-
if (!range) {
747+
if (!range || options?.forceFullRange) {
683748
clearTranscript();
684749
}
685750

686-
toggleTranscribing(); // Set isTranscribing to true
751+
startTranscribing();
687752
setProcessingProgress(10);
688753

689754
// For YouTube videos, we can't directly access the audio
@@ -790,6 +855,7 @@ export const TranscriptPanel = () => {
790855
await simulateTranscription();
791856
} finally {
792857
setIsProcessing(false);
858+
stopTranscribing();
793859
}
794860
};
795861

@@ -1220,31 +1286,49 @@ export const TranscriptPanel = () => {
12201286
<Settings size={16} />
12211287
</button>
12221288

1223-
{/* Show transcribe button behavior based on active tab */}
1224-
{activeTabId ? (
1225-
<button
1226-
onClick={handleTranscribeBookmark}
1227-
className={`p-1.5 rounded-full ${isTranscribing
1228-
? "bg-green-100 text-green-600 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
1229-
: "bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
1230-
}`}
1231-
title={t("transcript.transcribeBookmark")}
1232-
disabled={isProcessing || (!currentFile && !currentYouTube)}
1233-
>
1234-
<FileAudio size={16} />
1235-
</button>
1236-
) : (
1237-
<button
1238-
onClick={handleTranscribeFull}
1239-
className={`p-1.5 rounded-full ${isTranscribing
1240-
? "bg-green-100 text-green-600 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
1241-
: "bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
1242-
}`}
1243-
title={t("transcript.transcribeWithWhisper")}
1244-
disabled={isProcessing || (!currentFile && !currentYouTube)}
1245-
>
1246-
<FileAudio size={16} />
1247-
</button>
1289+
{shouldShowTranscribeActionsInHeader && (
1290+
<>
1291+
{activeTabId ? (
1292+
<button
1293+
onClick={handleTranscribeBookmark}
1294+
className={`p-1.5 rounded-full ${isTranscribing
1295+
? "bg-green-100 text-green-600 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
1296+
: "bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
1297+
}`}
1298+
title={t("transcript.transcribeBookmark")}
1299+
disabled={isProcessing || (!currentFile && !currentYouTube)}
1300+
>
1301+
<FileAudio size={16} />
1302+
</button>
1303+
) : (
1304+
<button
1305+
onClick={handleTranscribeDefault}
1306+
className={`p-1.5 rounded-full ${isTranscribing
1307+
? "bg-green-100 text-green-600 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
1308+
: "bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
1309+
}`}
1310+
title={
1311+
loopStart !== null && loopEnd !== null
1312+
? t("transcript.transcribeLoopRange")
1313+
: t("transcript.transcribeWithWhisper")
1314+
}
1315+
disabled={isProcessing || (!currentFile && !currentYouTube)}
1316+
>
1317+
<FileAudio size={16} />
1318+
</button>
1319+
)}
1320+
1321+
{!activeTabId && currentFile && (
1322+
<button
1323+
onClick={handleTranscribeFull}
1324+
className="p-1.5 rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
1325+
title={t("transcript.transcribeFullRange")}
1326+
disabled={isProcessing || (!currentFile && !currentYouTube)}
1327+
>
1328+
<Brain size={16} />
1329+
</button>
1330+
)}
1331+
</>
12481332
)}
12491333

12501334
<button
@@ -1341,6 +1425,14 @@ export const TranscriptPanel = () => {
13411425
</div>
13421426
{(currentFile || currentYouTube) && (
13431427
<div className="space-y-2">
1428+
<button
1429+
onClick={handleTranscribeDefault}
1430+
className="px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-md text-sm font-medium transition-colors"
1431+
>
1432+
{loopStart !== null && loopEnd !== null
1433+
? t("transcript.transcribeLoopRangeButton")
1434+
: t("transcript.transcribeWithWhisper")}
1435+
</button>
13441436
<div className="text-sm">{t("common.or")}</div>
13451437
<div className="flex justify-center">
13461438
<TranscriptUploader variant="prominent" />

0 commit comments

Comments
 (0)