feat: add accessible video validation, remove AI confidence check

- Add validation for accessible_video_gcs (file exists, size 0.1MB-5GB)
- Add validation for retimed_captions_vtt_gcs when accessible video exists
- Add AD Videos count to asset validation panel
- Include retimed captions in VTT file count
- Remove AI confidence from validation panel and backend checks

🤖 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:41:57 -06:00
parent 3cdea9dfec
commit fd68d1ef54
2 changed files with 53 additions and 6 deletions

View file

@ -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()

View file

@ -240,8 +240,8 @@ export function FinalDetail() {
<span className="ml-1 font-medium">{_countMp3Files(job.outputs || {})}</span>
</div>
<div>
<span className={validation?.is_valid ? 'text-green-700' : 'text-red-700'}>AI Confidence:</span>
<span className="ml-1 font-medium">{job.ai?.confidence ? `${(job.ai.confidence * 100).toFixed(1)}%` : 'N/A'}</span>
<span className={validation?.is_valid ? 'text-green-700' : 'text-red-700'}>AD Videos:</span>
<span className="ml-1 font-medium">{_countAccessibleVideos(job.outputs || {})}</span>
</div>
</div>
</div>
@ -395,10 +395,15 @@ function _countVttFiles(outputs: Record<string, LangOutput>): 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<string, LangOutput>): number {
return Object.values(outputs).filter(lang => lang.ad_mp3_gcs).length;
}
function _countAccessibleVideos(outputs: Record<string, LangOutput>): number {
return Object.values(outputs).filter(lang => lang.accessible_video_gcs).length;
}