EcoHash Dedicated Video Generate
Keep the Job ID, It's Your Undo Button
- video
- job_id
Video generation is the one place where an API node makes obvious sense even to people who otherwise refuse to leave their own GPU: the endpoint owns the async job, you own a canvas. EcoHash Dedicated Video Generate submits a text-to-video job to a deployment you configured, polls it, downloads the MP4, and hands ComfyUI a VIDEO object plus the job ID as a STRING.
That second output is the part nobody wires up and everybody needs. More on it below.
What the node is, mechanically
This is a two-stage node pretending to be one. Submission goes to /videos with model, prompt, seconds and size - the standard OpenAI-Videos-style contract, except the pack shapes seconds as a string, which tells you it was written against a real service and not a spec. Polling then reads /videos/{id}, and the finished file comes down from /videos/{id}/content into ComfyUI's temp directory. Only that constructed content route carries your inference key; any redirect or signed result URL is fetched without it, which is the right instinct.
Progress lands on the canvas over the pack's own websocket event, so you get a status line rather than a spinner and a hope.
Presets work like this: pick resolution and aspect_ratio, and the node derives pixel dimensions from them (720p at 16:9 becomes 1280×720). Pick resolution = custom and your width/height are used directly instead, 128 to 8192 each - and at that point the aspect ratio is no longer sent, because your explicit dimensions already define it. The service can still align what you asked for to what it supports, so requested and actual duration genuinely can differ.
The inputs that matter
model, free text, the exact served ID. prompt, multi-line, required and non-empty - the node raises "Enter a model ID and a nonempty video prompt" rather than submitting junk. duration is whole seconds with a huge allowed range (up to 86400) and no relationship to what your model can do; the tooltip says limits come from the service, not the model name, and it means it. aspect_ratio has six options; resolution has 480p, 720p, 768p, 1080p, 4k and custom, and those are "common size presets, not a guarantee of model support" - availability in a dropdown is not a compatibility claim.
Then the two advanced fields. wait_timeout defaults to 1800 seconds and is separate from the connection's per-request timeout: one caps each HTTP call, the other caps total waiting. Raising the connection timeout to fix a long render does nothing. Raise wait_timeout.
And job_id. Leave it blank to generate. Paste an existing ID and the node resumes polling and download without a new generation - same connection, no new charge for a job that already exists. That's the recovery path when a render times out, when ComfyUI gets interrupted, or when the download failed after the job completed remotely. Clear the field to start something new.
The one trap in this node
Don't wire job_id back into its own job_id input. It looks like a sensible loop and it's a graph cycle; the pack's docs call it out. Connect it to Preview as Text or Save Text if you want to keep the ID - those receive it after generation finishes. While it's still running, copy it from the Video task field or the server log.
The other rule: submission never auto-retries a POST. That's correct behaviour for a billable async call - an automatic retry after a lost response could bill you twice - but it means a failed submission needs you to establish whether a job was actually created before you queue again. The job ID in the log is how you find out.
Install
Manager → EcoHash → install → restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/ecohash-ai/ComfyUI-EcoHash.git
cd ComfyUI-EcoHash
pip install -r requirements.txt
requirements.txt is requests only. But video does have a real requirement the image nodes don't: a current ComfyUI with native VIDEO support and a Save Video node to receive the output. On an older build the node fails with a message telling you to update ComfyUI, and it's not wrong. The decoded file also has to be something ComfyUI can read - if the MP4 can't be decoded you'll get a codec/PyAV-flavoured error with the job ID attached, which is your cue to update rather than to regenerate.
Key and connection: sidebar EcoHash → Connections → save key → Add connection with the endpoint URL including its prefix. Non-EcoHash hosts set their own key in the advanced settings.
Troubleshooting
Job accepted, then nothing. Check wait_timeout before anything else. 1800 seconds is thirty minutes, which sounds like plenty until it isn't.
Completed remotely, download failed. Resume the same job. Result URLs expire, and downloads are capped at 512 MiB per video.
A nonstandard deployment rejects the payload. A /videos route existing is not proof the service implements the expected create contract. The pack's answer is an explicit server-side request mapping tied to that connection and model, not a node-level protocol switch - there isn't one, and there won't be.
No audio in the result. The dedicated node doesn't request audio independently; it preserves whatever the service returns. If your model doesn't generate a soundtrack, you get silence and need a separate audio stage.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| model | STRING | Exact model ID served by your dedicated endpoint; it need not appear in the public EcoHash catalog. | |
| prompt | STRING | — | |
| duration | INT | 41–86400 | Requested seconds; model limits come from the service, not the model name. |
| aspect_ratio | COMBO | 16:9 | 6 options: 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 |
| resolution | COMBO | 720p | Common size presets, not a guarantee of model support. Hover over the model selector for capabilities or use a supported custom size. |
| widthopt | INT | 1280128–8192 | Only used for custom resolution. |
| heightopt | INT | 720128–8192 | Only used for custom resolution. |
| job_idopt | STRING | Leave blank to generate. Paste an existing job ID to resume polling/download without a new generation. Keep its original connection. | |
| wait_timeoutopt | INT | 180010–14400 | Total wait for video completion in seconds; separate from the connection's per-request timeout. |
| connectionopt | COMBO | 1 options: |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | — |
| job_id | STRING | — |