diff --git a/surfsense_web/components/tool-ui/generate-podcast.tsx b/surfsense_web/components/tool-ui/generate-podcast.tsx
index 3b4c75a35..e11273e41 100644
--- a/surfsense_web/components/tool-ui/generate-podcast.tsx
+++ b/surfsense_web/components/tool-ui/generate-podcast.tsx
@@ -51,7 +51,7 @@ function PodcastGeneratingState({ title }: { title: string }) {
{/* Animated rings */}
-
+
{title}
@@ -113,7 +113,15 @@ function AudioLoadingState({ title }: { title: string }) {
}
/**
- * Podcast Player Component - Fetches audio with authentication
+ * Transcript entry type from the database
+ */
+interface TranscriptEntry {
+ speaker_id: number;
+ dialog: string;
+}
+
+/**
+ * Podcast Player Component - Fetches audio and transcript with authentication
*/
function PodcastPlayer({
podcastId,
@@ -127,6 +135,7 @@ function PodcastPlayer({
durationMs?: number;
}) {
const [audioSrc, setAudioSrc] = useState
(null);
+ const [transcript, setTranscript] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const objectUrlRef = useRef(null);
@@ -140,8 +149,8 @@ function PodcastPlayer({
};
}, []);
- // Fetch audio with authentication
- const loadAudio = useCallback(async () => {
+ // Fetch audio and podcast details (including transcript)
+ const loadPodcast = useCallback(async () => {
setIsLoading(true);
setError(null);
@@ -156,35 +165,43 @@ function PodcastPlayer({
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60s timeout
try {
- // Fetch audio blob with authentication
- const response = await podcastsApiService.loadPodcast({
- request: { id: podcastId },
- controller,
- });
+ // Fetch audio blob and podcast details in parallel
+ const [audioBlob, podcastDetails] = await Promise.all([
+ podcastsApiService.loadPodcast({
+ request: { id: podcastId },
+ controller,
+ }),
+ podcastsApiService.getPodcastById(podcastId),
+ ]);
// Create object URL from blob
- const objectUrl = URL.createObjectURL(response);
+ const objectUrl = URL.createObjectURL(audioBlob);
objectUrlRef.current = objectUrl;
setAudioSrc(objectUrl);
+
+ // Set transcript from podcast details
+ if (podcastDetails?.podcast_transcript) {
+ setTranscript(podcastDetails.podcast_transcript as TranscriptEntry[]);
+ }
} finally {
clearTimeout(timeoutId);
}
} catch (err) {
- console.error("Error loading podcast audio:", err);
+ console.error("Error loading podcast:", err);
if (err instanceof DOMException && err.name === "AbortError") {
setError("Request timed out. Please try again.");
} else {
- setError(err instanceof Error ? err.message : "Failed to load audio");
+ setError(err instanceof Error ? err.message : "Failed to load podcast");
}
} finally {
setIsLoading(false);
}
}, [podcastId]);
- // Load audio when component mounts
+ // Load podcast when component mounts
useEffect(() => {
- loadAudio();
- }, [loadAudio]);
+ loadPodcast();
+ }, [loadPodcast]);
if (isLoading) {
return ;
@@ -204,6 +221,24 @@ function PodcastPlayer({
durationMs={durationMs}
className="w-full"
/>
+ {/* Transcript section */}
+ {transcript && transcript.length > 0 && (
+
+
+ View transcript ({transcript.length} entries)
+
+
+ {transcript.map((entry, idx) => (
+
+
+ Speaker {entry.speaker_id + 1}:
+ {" "}
+ {entry.dialog}
+
+ ))}
+
+
+ )}
);
}
@@ -219,7 +254,6 @@ function PodcastTaskPoller({
title: string;
}) {
const [taskStatus, setTaskStatus] = useState({ status: "processing" });
- const [pollCount, setPollCount] = useState(0);
const pollingRef = useRef(null);
// Set active podcast state when this component mounts
@@ -253,9 +287,8 @@ function PodcastTaskPoller({
}
} catch (err) {
console.error("Error polling task status:", err);
- // Don't stop polling on network errors, just increment count
+ // Don't stop polling on network errors, continue polling
}
- setPollCount((prev) => prev + 1);
};
// Initial poll
diff --git a/surfsense_web/lib/apis/podcasts-api.service.ts b/surfsense_web/lib/apis/podcasts-api.service.ts
index 346c984af..0defff7d4 100644
--- a/surfsense_web/lib/apis/podcasts-api.service.ts
+++ b/surfsense_web/lib/apis/podcasts-api.service.ts
@@ -62,6 +62,13 @@ class PodcastsApiService {
);
};
+ /**
+ * Get a podcast by its ID (includes full transcript)
+ */
+ getPodcastById = async (podcastId: number) => {
+ return baseApiService.get(`/api/v1/podcasts/${podcastId}`, podcast);
+ };
+
generatePodcast = async (request: GeneratePodcastRequest) => {
// Validate the request
const parsedRequest = generatePodcastRequest.safeParse(request);