forked from lightningpixel/modly
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuseGeneration.ts
More file actions
106 lines (90 loc) · 3.36 KB
/
Copy pathuseGeneration.ts
File metadata and controls
106 lines (90 loc) · 3.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { useCallback, useRef } from 'react'
import { useAppStore } from '@shared/stores/appStore'
import { useApi } from './useApi'
import { showCompletionNotification } from '@shared/utils/notification'
export function useGeneration() {
const { currentJob, setCurrentJob, updateCurrentJob, generationOptions, selectedImageData, pushMeshUrl, clearMeshHistory } = useAppStore()
const { generateFromImage, pollJobStatus, cancelJob } = useApi()
const cancelledRef = useRef(false)
const abortControllerRef = useRef<AbortController | null>(null)
const startGeneration = useCallback(
async (imagePath: string) => {
cancelledRef.current = false
abortControllerRef.current = new AbortController()
clearMeshHistory()
const job = {
id: crypto.randomUUID(),
imageFile: imagePath,
status: 'uploading' as const,
progress: 0,
createdAt: Date.now(),
modelId: generationOptions.modelId,
generationOptions,
}
setCurrentJob(job)
try {
const { jobId } = await generateFromImage(imagePath, generationOptions, selectedImageData ?? undefined, abortControllerRef.current.signal)
if (cancelledRef.current) {
await cancelJob(jobId)
setCurrentJob(null)
return
}
updateCurrentJob({ status: 'generating', progress: 0 })
await pollUntilDone(jobId)
} catch (err) {
if (cancelledRef.current) {
setCurrentJob(null)
return
}
let errorMessage: string
if (err && typeof err === 'object' && 'response' in err) {
const axiosErr = err as { response?: { data?: { detail?: string } }; message: string }
errorMessage = axiosErr.response?.data?.detail ?? axiosErr.message
} else {
errorMessage = err instanceof Error ? err.message : String(err)
}
updateCurrentJob({
status: 'error',
error: errorMessage
})
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps -- useApi re-creates its fns each render, so this re-memoizes anyway (values stay fresh)
[generateFromImage, pollJobStatus, cancelJob, setCurrentJob, updateCurrentJob]
)
const pollUntilDone = async (jobId: string) => {
while (true) {
await new Promise((r) => setTimeout(r, 1000))
if (cancelledRef.current) {
await cancelJob(jobId)
setCurrentJob(null)
break
}
const result = await pollJobStatus(jobId)
if (result.status === 'cancelled') {
setCurrentJob(null)
break
}
if (result.status === 'done') {
updateCurrentJob({ status: 'done', progress: 100, outputUrl: result.outputUrl, originalOutputUrl: result.outputUrl })
if (result.outputUrl) pushMeshUrl(result.outputUrl)
void showCompletionNotification('Generation complete')
break
}
if (result.status === 'error') {
updateCurrentJob({ status: 'error', error: result.error })
break
}
updateCurrentJob({
progress: result.progress,
step: result.step,
})
}
}
const cancelGeneration = useCallback(() => {
cancelledRef.current = true
abortControllerRef.current?.abort()
}, [])
const reset = useCallback(() => setCurrentJob(null), [setCurrentJob])
return { currentJob, startGeneration, cancelGeneration, reset }
}