feat: backend callback E2E upload pipeline#3
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a direct video upload and highlight processing pipeline, including a new /upload endpoint, background processing tasks using ffmpeg, and progress reporting routed to separate Redis channels. It also adds comprehensive local E2E testing scripts and documentation. Feedback on the changes includes improving the robustness of the video duration parsing and segment cutting logic in the pipeline, ensuring incomplete files are cleaned up upon failure, and moving directory creation in app/main.py from import time to a startup event handler to prevent potential startup failures.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| segments: list[dict[str, Any]] = [] | ||
| for index, center in enumerate(centers, start=1): | ||
| start = max(0.0, min(center - (clip_seconds / 2.0), max(0.0, duration - clip_seconds))) | ||
| segment_duration = min(clip_seconds, max(0.5, duration - start)) |
There was a problem hiding this comment.
영상 전체 길이가 clip_seconds보다 짧거나 남은 구간이 0.5초 미만인 경우, segment_duration이 실제 남은 길이(duration - start)보다 커지게 되어 ffmpeg 실행 시 오류가 발생하거나 비정상적인 파일이 생성될 수 있습니다. max(0.5, ...) 제약을 제거하고 실제 남은 길이로 제한하는 것이 안전합니다.
| segment_duration = min(clip_seconds, max(0.5, duration - start)) | |
| segment_duration = min(clip_seconds, duration - start) |
| text=True, | ||
| timeout=min(settings.FFMPEG_TIMEOUT_SEC, 30), | ||
| ) | ||
| return max(0.1, float(result.stdout.strip())) |
There was a problem hiding this comment.
ffprobe 실행 결과에서 duration을 파싱할 때, 비디오 파일이 손상되었거나 특정 포맷의 경우 result.stdout이 비어있거나 숫자가 아닌 값(예: N/A)을 반환할 수 있습니다. 이 경우 float() 변환 시 ValueError가 발생하므로, 예외 처리를 추가하여 더 명확한 에러 메시지를 제공하는 것이 좋습니다.
| return max(0.1, float(result.stdout.strip())) | |
| try: | |
| return max(0.1, float(result.stdout.strip())) | |
| except ValueError as exc: | |
| raise RuntimeError(f"Failed to parse video duration from ffprobe output: {result.stdout!r}") from exc |
| Path(settings.SAVE_ROOT).mkdir(parents=True, exist_ok=True) | ||
| Path(settings.HIGHLIGHT_OUTPUT_ROOT).mkdir(parents=True, exist_ok=True) |
| except Exception as exc: | ||
| logger.exception("[%s] direct upload E2E pipeline failed", job_id) | ||
| await report_progress_to_spring( | ||
| job_id, | ||
| "COMPLETE", | ||
| None, | ||
| member_id=member_id, | ||
| success=False, | ||
| message=f"{type(exc).__name__}: {exc}", | ||
| ) |
There was a problem hiding this comment.
비디오 분할 및 콜백 처리 중 예외가 발생하여 파이프라인이 실패하는 경우, output_dir에 이미 생성되었거나 생성 중이던 불완전한 하이라이트 세그먼트 파일들이 그대로 남게 됩니다. 이는 디스크 공간 낭비 및 잘못된 파일 서빙으로 이어질 수 있으므로, except Exception 블록에서 output_dir을 정리(cleanup)하는 로직을 추가하는 것이 좋습니다.
| except Exception as exc: | |
| logger.exception("[%s] direct upload E2E pipeline failed", job_id) | |
| await report_progress_to_spring( | |
| job_id, | |
| "COMPLETE", | |
| None, | |
| member_id=member_id, | |
| success=False, | |
| message=f"{type(exc).__name__}: {exc}", | |
| ) | |
| except Exception as exc: | |
| logger.exception("[%s] direct upload E2E pipeline failed", job_id) | |
| try: | |
| if output_dir.exists(): | |
| import shutil | |
| shutil.rmtree(output_dir, ignore_errors=True) | |
| except Exception as cleanup_exc: | |
| logger.warning("[%s] Failed to cleanup output directory: %s", job_id, cleanup_exc) | |
| await report_progress_to_spring( | |
| job_id, | |
| "COMPLETE", | |
| None, | |
| member_id=member_id, | |
| success=False, | |
| message=f"{type(exc).__name__}: {exc}", | |
| ) |
🍀 이슈 번호
없음
✅ 작업 사항
/api/pre-signed가 발급하는/upload?signature=...URL을 OpenCV에서 실제 multipart 영상 업로드로 수신하도록 구현opencv-progress-upload:{jobId}, 하이라이트 처리/완료 진행률은opencv-progress-highlight:{jobId}로 분리 발행/api/highlight/upload-resultcallback까지 수행LocalDateTime호환 createdAt 형식으로 조정tools/e2e_local_upload.pysmoke E2E 스크립트,docs/local-e2e.md재현 문서 추가⌨ 기타
검증 완료:
.venv/bin/python -m compileall app ai_worker toolstools/e2e_local_upload.py --backend-url http://127.0.0.1:9000 --opencv-url http://127.0.0.1:8000/api/highlight/latest?jobId=...에서 3개 highlight row 확인HEAD /highlight/{jobId}/{jobId}_segment_01.mp4 -> 200 OK