@@ -47,7 +47,7 @@ import { useLiveTranscripts } from "@/hooks/use-live-transcripts";
4747import { PLATFORM_CONFIG , getDetailedStatus } from "@/types/vexa" ;
4848import type { MeetingStatus , Meeting } from "@/types/vexa" ;
4949import { StatusHistory } from "@/components/meetings/status-history" ;
50- import { cn } from "@/lib/utils" ;
50+ import { cn , parseUTCTimestamp } from "@/lib/utils" ;
5151import { vexaAPI } from "@/lib/api" ;
5252import { toast } from "sonner" ;
5353import { LanguagePicker } from "@/components/language-picker" ;
@@ -196,6 +196,19 @@ export default function MeetingDetailPage() {
196196
197197 const hasRecordingAudio = recordingFragments . length > 0 ;
198198
199+ // Derive recording session start time from transcript segments.
200+ // Any segment with both start_time (relative) and absolute_start_time gives us:
201+ // sessionStart = absolute_start_time - start_time * 1000
202+ // This is more reliable than recording.created_at (which is the DB insert time, not session start).
203+ const videoTimeBase = useMemo ( ( ) => {
204+ for ( const seg of transcripts ) {
205+ if ( seg . absolute_start_time && seg . start_time != null ) {
206+ return parseUTCTimestamp ( seg . absolute_start_time ) . getTime ( ) - seg . start_time * 1000 ;
207+ }
208+ }
209+ return null ;
210+ } , [ transcripts ] ) ;
211+
199212 const handlePlaybackTimeUpdate = useCallback ( ( time : number ) => {
200213 setPlaybackTime ( time ) ;
201214 setIsPlaybackActive ( true ) ;
@@ -656,17 +669,11 @@ export default function MeetingDetailPage() {
656669 // absolute timestamp so the transcript viewer can match against absolute_start_time.
657670 const playbackAbsoluteTime = useMemo ( ( ) : string | null => {
658671 if ( playbackTime == null || ! isPlaybackActive ) return null ;
659- // Video mode: currentTime is relative to meeting start (same as live audio recording).
660- // videoRecording.createdAt is the upload time, not the meeting start — use the
661- // first audio fragment's createdAt, or fall back to the meeting's start_time.
672+ // Video mode: use session start time derived from transcript segments.
673+ // (recording.created_at is the DB insert time, not when recording content started)
662674 if ( videoRecording && ! preferAudio ) {
663- const base =
664- recordingFragments [ 0 ] ?. createdAt ??
665- currentMeeting ?. start_time ??
666- null ;
667- if ( ! base ) return null ;
668- const fragStart = new Date ( base ) . getTime ( ) ;
669- return new Date ( fragStart + playbackTime * 1000 ) . toISOString ( ) ;
675+ if ( videoTimeBase == null ) return null ;
676+ return new Date ( videoTimeBase + playbackTime * 1000 ) . toISOString ( ) ;
670677 }
671678 if ( recordingFragments . length === 0 ) return null ;
672679 if ( recordingFragments . length === 1 ) {
@@ -685,7 +692,7 @@ export default function MeetingDetailPage() {
685692 remaining -= fragDur ;
686693 }
687694 return null ;
688- } , [ playbackTime , isPlaybackActive , recordingFragments , videoRecording , preferAudio , currentMeeting ] ) ;
695+ } , [ playbackTime , isPlaybackActive , recordingFragments , videoRecording , preferAudio , videoTimeBase ] ) ;
689696
690697 if ( error ) {
691698 return (
0 commit comments