Creating a Dance Video with AI Using One Photo and One Reference Video: Job Design, Credit Management, and Input Validation
- AI
- Video Generation
- Design
- Backend
This article explains a general architecture. As an example of product behavior, we will take up a service that generates a dance video from a photo and a reference video Danceo, but since the internal implementation is not public, the following content is about "how I would design it," and does not explain the actual implementation of the service.
Introduction
"A service that uploads one portrait and one reference dance video, and then generates a video of the person in the photo dancing." From the outside, such a service looks simple.
However, behind the scenes, it involves an asynchronous workflow that takes a long time, uses GPU, and involves money (credits). I feel that the difficulty lies not in the model itself, but in the surrounding design.
In this article, we will cover the following three points.
- Concept of Motion Transfer
- Designing the generation job as a state machine
- Credit management that prevents double charging or refunds errors in case of failure
1. What is Motion Transfer
The text-to-video approach creates video from text, where the model "creates" the motion from the text. On the other hand, Motion Transfer splits the input into two parts.
- Motion Input: Reference video (responsible for choreography, timing, and camera movement)
- Appearance Input: Portrait (responsible for who is dancing)
Conceptually, the process involves extracting motion information from the reference video and appearance information from the photo, then passing both as conditions to the video generation model. (The specific methods vary depending on the model, and are not clear from the product page.)
As an engineer, what is important is that the output quality strongly depends on the input quality.
- If the reference video has many scene cuts or obstructions → motion information becomes incomplete
- If the portrait is blurry or dark → appearance information becomes unstable
Therefore, input validation and preview are important not only for UX but also for system design.
2. Generation jobs are handled by a state machine
Generation can take several minutes and may fail due to reasons beyond control such as GPU interruption or timeout. Design it as an asynchronous job with an explicit state, not using request/response.
This is a minimal schema example.
The points are two.
- credit_cost is saved in the job during estimation. Even if the pricing system changes later, the amount the user sees is protected.
- UNIQUE (user_id, idempotency_key) ensures that "double-clicking a button" or "client resending after timeout" becomes harmless.
Enforce state transition in one place
Aggregate transitions to prevent workers from writing any arbitrary status.
"CREATED": {"VALIDATING", "FAILED"}, "VALIDATING": {"QUEUED", "FAILED"}, "QUEUED": {"RENDERING", "FAILED"}, "RENDERING": {"POSTPROCESSING", "FAILED", "QUEUED"}, # QUEUED is retry "POSTPROCESSING": { "COMPLETED", "FAILED"}, } def transition(db, job_id, expected, new): if new not in ALLOWED: raise ValueError(f"illegal transition {expected} -> {new}") updated = db.execute( """ UPDATE render_jobs SET status = %s, updated_at = now() WHERE id = %s AND status = %s """, (new, job_id, expected), ) if updated.rowcount == 0: raise ConcurrentUpdate(job_id) # Other workers have transitioned first WHERE status = expected is using optimistic concurrency control, and even if two workers compete, only one can win. Credit is managed through 'Reserve → Confirm or Release' pattern. Common mistakes are two. Withdrawing at completion: multiple jobs can run with the same balance. Withdrawing first but forgetting to refund: inquiries increase. Stability is achieved by reserving using an append-only ledger (ledger) and confirming or releasing. ledger.sql CREATE TABLE credit_ledger ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL, job_id UUID, kind TEXT NOT NULL, -- GRANT, RESERVE, RELEASE, SETTLE amount INT NOT NULL, -- signed. RESERVE is negative created_at TIMESTAMPTZ NOT NULL DEFAULT now()(), UNIQUE (job_id, kind) -- RESERVE and RELEASE/SETTLE are processed once per job );Balance is SUM(amount), and the reserved amount is immediately treated as deducted from the balance.
This is the processing for completion and failure.
The combination of UNIQUE (job_id, kind) and ON CONFLICT DO NOTHING ensures idempotency of refunds. Even if a worker crashes and is restarted, or if failure events are duplicated, double refunds will not occur.
The promise that "credit is refunded if rendering fails" can only be upheld if this path is subtle and robust.
4. Validate input before using GPU
Since output quality depends on input and GPU time is costly, we perform cheap validation early on.
h >=
w
Failures in GPU processing are mostly transient (OOM, preemption, timeout).
Only retry for transient failures (with a limit on the number of retries). Do not retry for definitive failures such as input errors.
Set a
visibility timeout- on the queue, so that jobs from dead workers can be seen again.
- While rendering, send heartbeat
- messages, so that the reaper (cleanup process) can return old jobs back to QUEUED or FAILED status. Save
- error_code instead of just "failure". This allows support and UI to display proper messages.
- 6. Privacy and Consent are Part of the Design
The service that creates videos from photos includes the faces of real people in the generated footage. Therefore, some rules should be built into the system.
Default is private
: associate inputs and outputs with an account, and make them not enumerable to the general public.- Retention period: decide how long to keep uploaded images and videos, and enforce it via lifecycle rules.
- Consent confirmation at upload time: confirm with the user that they have obtained permission for all photos, portraits, performances, and audio sources used.
- Content policy checks: for example, rules like "do not depict real people as minors" are more effective if written in the terms of service and also checked within the pipeline.
- It's recommended to include these from the first version. Adding a consent flow later is very difficult.
7. Points to Consider for the Product
Looking at the public flow of Danceo, I felt that the following design elements are worth emulating.
Preview input before generation: Notice problematic reference videos before unnecessary credit consumption
None of them are flashy, but these aspects are what transform an AI product from 'experimental' to 'trustworthy'
End While models are often the focus, the success or failure of the photo-to-dance video product depends on subtle aspects such as explicit job status, idempotent credit ledger, low-cost early validation, and consent confirmation integrated into the pipeline .
If you want to verify these designs from the user's perspective, please try a short test rendering with Danceo. Also, if you're making a similar product, please let us know how you're handling retries and refunds in comments. I feel that the most bugs in actual use come from there
.Go to list of users who liked


