()
| 175 | } |
| 176 | |
| 177 | async function pollJob() { |
| 178 | if (!activeJobId) return; |
| 179 | |
| 180 | try { |
| 181 | const eventsResult = await apiRequest(`/api/jobs/${activeJobId}/events?after=${lastEventId}`, { |
| 182 | method: "GET", |
| 183 | headers: { |
| 184 | Accept: "application/json", |
| 185 | }, |
| 186 | }); |
| 187 | |
| 188 | const events = Array.isArray(eventsResult.events) ? eventsResult.events : []; |
| 189 | events.forEach((event) => { |
| 190 | appendLogEntry({ |
| 191 | timestamp: event.timestamp, |
| 192 | message: event.message, |
| 193 | level: event.level || "info", |
| 194 | }); |
| 195 | lastEventId = Math.max(lastEventId, event.id || 0); |
| 196 | }); |
| 197 | |
| 198 | const jobResult = await apiRequest(`/api/jobs/${activeJobId}`, { |
| 199 | method: "GET", |
| 200 | headers: { |
| 201 | Accept: "application/json", |
| 202 | }, |
| 203 | }); |
| 204 | |
| 205 | const state = jobResult?.job?.state; |
| 206 | if (state === "completed") { |
| 207 | showToast("Video generated successfully.", "success"); |
| 208 | stopJobPolling(); |
| 209 | setGeneratingState(false); |
| 210 | } else if (state === "failed") { |
| 211 | showToast(jobResult?.job?.errorMessage || "Generation failed.", "error"); |
| 212 | stopJobPolling(); |
| 213 | setGeneratingState(false); |
| 214 | } else if (state === "cancelled") { |
| 215 | showToast("Generation cancelled.", "warning"); |
| 216 | stopJobPolling(); |
| 217 | setGeneratingState(false); |
| 218 | } |
| 219 | } catch { |
| 220 | // Ignore transient polling failures. |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | function startJobPolling(jobId) { |
| 225 | stopJobPolling(); |
no test coverage detected