fix: video review caption sync and event listener issues

- Fix video event listeners not re-attaching when video element remounts
  (add activeTab?.videoUrl to useEffect dependency array)
- Add retimed_captions_vtt to VTT API response for accessible videos
- Use retimed captions for accessible video tab in VideoReviewPlayer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael 2025-12-26 16:23:48 -06:00
parent 6effe58dc9
commit 3cdea9dfec
4 changed files with 16 additions and 5 deletions

View file

@ -771,6 +771,15 @@ async def get_job_vtt_content(
except Exception as e:
logger.warning(f"Failed to fetch AD VTT: {e}")
# Fetch retimed captions VTT if available (for accessible videos)
if "retimed_captions_vtt_gcs" in lang_output:
blob_path = lang_output["retimed_captions_vtt_gcs"].replace(f"gs://{settings.gcs_bucket}/", "")
try:
blob = gcs_service.bucket.blob(blob_path)
response.retimed_captions_vtt = blob.download_as_text()
except Exception as e:
logger.warning(f"Failed to fetch retimed captions VTT: {e}")
return response

View file

@ -82,6 +82,7 @@ class JobDownloadsResponse(BaseModel):
class VttContentResponse(BaseModel):
captions_vtt: Optional[str] = None
audio_description_vtt: Optional[str] = None
retimed_captions_vtt: Optional[str] = None # Re-timed captions for accessible videos
class AssetValidationResponse(BaseModel):

View file

@ -54,9 +54,9 @@ export function VideoReviewPlayer({ job, downloads }: VideoReviewPlayerProps) {
const captions = useMemo<VTTCue[]>(() => {
if (!vttContent) return [];
// For accessible videos, use retimed captions if available
const vttSource = activeTab?.isAccessible && activeTab.captionsVttField === 'retimed_captions_vtt_gcs'
? vttContent.captions_vtt // The API returns captions_vtt regardless of source
// For accessible videos, use retimed captions if available, fall back to regular captions
const vttSource = activeTab?.isAccessible
? (vttContent.retimed_captions_vtt || vttContent.captions_vtt)
: vttContent.captions_vtt;
if (!vttSource) return [];
@ -114,7 +114,7 @@ export function VideoReviewPlayer({ job, downloads }: VideoReviewPlayerProps) {
};
}, []);
// Video time update handler
// Video time update handler - re-attach listeners when video element changes
useEffect(() => {
const video = videoRef.current;
if (!video) return;
@ -140,7 +140,7 @@ export function VideoReviewPlayer({ job, downloads }: VideoReviewPlayerProps) {
video.removeEventListener('pause', handlePause);
clearTimeout(timeoutId);
};
}, []);
}, [activeTab?.videoUrl]); // Re-run when video URL changes (causes element remount)
// Reset video when tab changes
useEffect(() => {

View file

@ -204,6 +204,7 @@ export interface JobDownloadsResponse {
export interface VttContentResponse {
captions_vtt?: string;
audio_description_vtt?: string;
retimed_captions_vtt?: string; // Re-timed captions for accessible videos
}
export interface VttUpdateRequest {