diff --git a/app/analyze/[videoId]/page.tsx b/app/analyze/[videoId]/page.tsx
index 8ba3af1..31e8d72 100644
--- a/app/analyze/[videoId]/page.tsx
+++ b/app/analyze/[videoId]/page.tsx
@@ -1909,6 +1909,42 @@ export default function AnalyzePage() {
)}
+ {videoId && topics.length === 0 && pageState === 'IDLE' && !error && (
+
+
+
+
+
+ No highlights found
+
+
+ We processed the transcript but couldn't find any standout highlights matching your criteria. This sometimes happens if the video is very short, has no dialogue, or if the AI filters were too strict.
+
+
+
+
+ Go to home
+
+ {
+ // Force regenerate logic
+ processVideo(normalizedUrl, mode);
+ }}
+ className="inline-flex items-center justify-center rounded-full bg-slate-900 px-4 py-2 text-xs font-medium text-white transition hover:bg-slate-800 disabled:pointer-events-none disabled:opacity-50"
+ disabled={isModeLoading}
+ >
+ Try again
+
+
+
+
+
+ )}
+
{videoId && topics.length > 0 && pageState === 'IDLE' && (
{error && (
diff --git a/lib/ai-processing.ts b/lib/ai-processing.ts
index 7974cd9..dce60e9 100644
--- a/lib/ai-processing.ts
+++ b/lib/ai-processing.ts
@@ -14,7 +14,7 @@ import {
import { generateAIResponse } from '@/lib/ai-client';
import { getProviderKey } from '@/lib/ai-providers';
import { topicGenerationSchema } from '@/lib/schemas';
-import { parseTimestampRange } from '@/lib/timestamp-utils';
+import { parseTimestampRange, formatTimestamp } from '@/lib/timestamp-utils';
import { getLanguageName } from '@/lib/language-utils';
import { repairJson } from '@/lib/json-utils';
import { z } from 'zod';
@@ -210,7 +210,7 @@ function buildChunkPrompt(
language?: string
): string {
const transcript = formatTranscriptWithTimestamps(chunk.segments);
- const chunkWindow = `[${formatTime(chunk.start)}-${formatTime(chunk.end)}]`;
+ const chunkWindow = `[${formatTimestamp(chunk.start)}-${formatTimestamp(chunk.end)}]`;
const videoInfoBlock = formatVideoInfoForPrompt(videoInfo);
const themeInstruction = theme
? ` - Focus exclusively on material that clearly expresses the theme "${theme}". Skip anything unrelated.
\n`
@@ -283,7 +283,7 @@ function buildReducePrompt(
.map((candidate, idx) => {
const timestamp = candidate.quote?.timestamp ?? '[??:??-??:??]';
const quoteText = candidate.quote?.text ?? '';
- const chunkWindow = `[${formatTime(candidate.chunkStart)}-${formatTime(
+ const chunkWindow = `[${formatTimestamp(candidate.chunkStart)}-${formatTimestamp(
candidate.chunkEnd
)}]`;
return `Candidate ${idx + 1}
@@ -464,7 +464,7 @@ function buildFallbackTopics(
fallbackTopics.push({
title: theme ? `${theme} — part ${i + 1}` : `Part ${i + 1}`,
quote: {
- timestamp: `[${formatTime(startTime)}-${formatTime(endTime)}]`,
+ timestamp: `[${formatTimestamp(startTime)}-${formatTimestamp(endTime)}]`,
text:
chunkSegments
.map((s) => s.text)
@@ -602,7 +602,7 @@ ${transcriptWithTimestamps}
{
title: fallbackLabel,
quote: {
- timestamp: `[00:00-${formatTime(fallbackEnd)}]`,
+ timestamp: `[00:00-${formatTimestamp(fallbackEnd)}]`,
text: fullText.substring(0, 200)
}
}
@@ -629,21 +629,13 @@ function combineTranscript(segments: TranscriptSegment[]): string {
function formatTranscriptWithTimestamps(segments: TranscriptSegment[]): string {
return segments
.map((s) => {
- const startTime = formatTime(s.start);
- const endTime = formatTime(s.start + s.duration);
+ const startTime = formatTimestamp(s.start);
+ const endTime = formatTimestamp(s.start + s.duration);
return `[${startTime}-${endTime}] ${s.text}`;
})
.join('\n');
}
-function formatTime(seconds: number): string {
- const mins = Math.floor(seconds / 60);
- const secs = Math.floor(seconds % 60);
- return `${mins.toString().padStart(2, '0')}:${secs
- .toString()
- .padStart(2, '0')}`;
-}
-
async function findExactQuotes(
transcript: TranscriptSegment[],
quotes: Array<{ timestamp: string; text: string }>,
diff --git a/lib/timestamp-utils.ts b/lib/timestamp-utils.ts
index 75e1233..9fea845 100644
--- a/lib/timestamp-utils.ts
+++ b/lib/timestamp-utils.ts
@@ -27,7 +27,10 @@ export function parseTimestamp(timestamp: string): number | null {
// Validate time values
if (hours < 0 || hours >= 24) return null;
- if (minutes < 0 || minutes >= 60) return null;
+ if (minutes < 0) return null;
+ // Relax minute check to allow MM:SS where MM >= 60 (fallback scenarios)
+ // unless hours are present, in which case strict 0-59 applies
+ if (hours > 0 && minutes >= 60) return null;
if (seconds < 0 || seconds >= 60) return null;
return hours * 3600 + minutes * 60 + seconds;