diff --git a/backend/app/services/validation.py b/backend/app/services/validation.py index 174d6af..453ff33 100644 --- a/backend/app/services/validation.py +++ b/backend/app/services/validation.py @@ -58,10 +58,23 @@ class AssetValidationService: if mp3_error: errors.append(mp3_error) - # Check minimum quality requirements - ai_confidence = job_doc.get("ai", {}).get("confidence", 0) - if ai_confidence < 0.7: - errors.append(f"AI confidence too low: {ai_confidence:.1%} (minimum: 70%)") + # Validate accessible video if present + if lang_output.get("accessible_video_gcs"): + video_error = await AssetValidationService._validate_video_asset( + lang_output.get("accessible_video_gcs"), + f"{language} accessible video" + ) + if video_error: + errors.append(video_error) + + # Validate retimed captions if accessible video uses pause-insert method + if lang_output.get("retimed_captions_vtt_gcs"): + retimed_error = await AssetValidationService._validate_vtt_asset( + lang_output.get("retimed_captions_vtt_gcs"), + f"{language} retimed captions VTT" + ) + if retimed_error: + errors.append(retimed_error) return len(errors) == 0, errors @@ -125,6 +138,35 @@ class AssetValidationService: return None + @staticmethod + async def _validate_video_asset(gcs_uri: str, asset_name: str) -> str | None: + """Validate a video asset exists and has reasonable properties""" + if not gcs_uri: + return f"Missing {asset_name}" + + try: + blob_path = gcs_uri.replace(f"gs://{gcs_service.bucket.name}/", "") + blob = gcs_service.bucket.blob(blob_path) + + if not blob.exists(): + return f"{asset_name} file not found in storage" + + # Reload blob to get metadata (including size) + blob.reload() + + # Check file size (should be reasonable for video) + size_mb = blob.size / (1024 * 1024) if blob.size else 0 + if size_mb < 0.1: # Less than 100KB + return f"{asset_name} file too small (likely empty or corrupted)" + elif size_mb > 5000: # More than 5GB + return f"{asset_name} file too large ({size_mb:.1f}MB)" + + except Exception as e: + logger.error(f"Failed to validate {asset_name}: {e}") + return f"{asset_name} validation error: {str(e)}" + + return None + # Global service instance asset_validation_service = AssetValidationService() \ No newline at end of file diff --git a/frontend/src/routes/admin/FinalDetail.tsx b/frontend/src/routes/admin/FinalDetail.tsx index 0fd6560..cb33530 100644 --- a/frontend/src/routes/admin/FinalDetail.tsx +++ b/frontend/src/routes/admin/FinalDetail.tsx @@ -240,8 +240,8 @@ export function FinalDetail() { {_countMp3Files(job.outputs || {})}
- AI Confidence: - {job.ai?.confidence ? `${(job.ai.confidence * 100).toFixed(1)}%` : 'N/A'} + AD Videos: + {_countAccessibleVideos(job.outputs || {})}
@@ -395,10 +395,15 @@ function _countVttFiles(outputs: Record): number { Object.values(outputs).forEach(lang => { if (lang.captions_vtt_gcs) count++; if (lang.ad_vtt_gcs) count++; + if (lang.retimed_captions_vtt_gcs) count++; }); return count; } function _countMp3Files(outputs: Record): number { return Object.values(outputs).filter(lang => lang.ad_mp3_gcs).length; +} + +function _countAccessibleVideos(outputs: Record): number { + return Object.values(outputs).filter(lang => lang.accessible_video_gcs).length; } \ No newline at end of file