From upload to playback.
End-to-end video lifecycle
Each deployment uses exactly one compute path: Kubernetes workers in development, or RunPod Serverless in production. Never both at once.
Development · Local / K3s
Production / RunPod
The map is illustrative architecture, not live service status or measured processing progress.
System at a glance
Video work runs asynchronously. Spring Boot coordinates state while workers handle compute-heavy processing.
Control plane
Spring Boot coordinates the workflow and persists state.
Storage and events
MinIO stores media; Kafka carries durable work and results.
Compute plane
Python processes chunks on Local / K3s OR Production / RunPod.
Spring Boot coordinates state; workers perform compression.
Why the pipeline is built this way
Keep uploads out of the APIThe browser sends media directly to object storage.
Spring Boot registers the video and prepares the upload, then the browser sends the file straight to storage. Large transfers never pass through the application server, which stays focused on coordination.
See it in the video lifecycleTurn a video into independent workThe video is cut into short chunks, each queued as its own task.
Slicing happens in the background after the upload. Each chunk remembers its place in the original video, so chunks can be processed in any order and put back together correctly.
See it in the video lifecycleSeparate coordination from computeJava orchestrates and persists; Python carries media-processing dependencies.
Each deployment uses one compute path: Kubernetes workers in development or RunPod Serverless in production. The two are alternatives and never run side by side.
See it in the video lifecyclePreserve faces, deliver segmentsMediaPipe and OpenCV guide FFmpeg compression; completed segments become HLS.
Workers return compressed stream segments. The stream is published only after every chunk has finished successfully, so a video is either fully ready or not shown at all.
See it in the video lifecycleReliable work, explicit outcomes
Typical video lifecycle: Upload received → Sliced into chunks → Processing → Ready. Failure exits are explicit, not hidden in the player.
Failed when slicing or queuing the work fails. Processing failed when a chunk cannot be processed or handed to a worker.
Count each chunk onceDuplicates are ignored and concurrent completions are protected.
If the same result arrives twice, it is counted once, and results that arrive at the same moment cannot overwrite each other. The stream is built only when every chunk is accounted for.
Retry transient failuresShort-lived problems are retried a limited number of times.
Retries wait a little longer each time and stop after a limit. Work that still fails is set aside and reported as failed instead of being retried forever.
Make terminal failures clearSlicing and processing failures have explicit states and curated reasons.
Invalid files, slicing problems, unavailable workers and chunks that cannot be processed all end in a clear failed state. Technical error details stay on the server; the page shows a plain explanation.
Clean up after deletionDeleting a video also removes its stored files.
Once a video's record is deleted, its original upload, working files and finished stream are removed from storage as well.