Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
C
coderai
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
coderai
Commits
0ce4d5af
Commit
0ce4d5af
authored
May 07, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Better progression stats during generation from web
parent
cb542996
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
235 additions
and
16 deletions
+235
-16
chat.html
codai/admin/templates/chat.html
+101
-8
audio_gen.py
codai/api/audio_gen.py
+57
-1
images.py
codai/api/images.py
+20
-7
video.py
codai/api/video.py
+57
-0
No files found.
codai/admin/templates/chat.html
View file @
0ce4d5af
...
@@ -797,6 +797,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
...
@@ -797,6 +797,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
<div
id=
"vi-dialog-section"
></div>
<div
id=
"vi-dialog-section"
></div>
<button
class=
"btn btn-primary"
onclick=
"genVideo('i2v')"
>
Animate
</button>
<button
class=
"btn btn-primary"
onclick=
"genVideo('i2v')"
>
Animate
</button>
<div
class=
"progress"
id=
"vi-prog"
></div>
<div
class=
"progress"
id=
"vi-prog"
></div>
<div
class=
"gen-progress-wrap"
id=
"vi-pbar-wrap"
>
<div
class=
"gen-progress-bar-bg"
><div
class=
"gen-progress-bar-fill"
id=
"vi-pbar-fill"
></div></div>
<div
class=
"gen-progress-label"
id=
"vi-pbar-label"
></div>
</div>
</div>
</div>
<div
class=
"gen-out"
id=
"vi-out"
><div
class=
"gen-empty"
>
Animated video will appear here
</div></div>
<div
class=
"gen-out"
id=
"vi-out"
><div
class=
"gen-empty"
>
Animated video will appear here
</div></div>
</div></div>
</div></div>
...
@@ -842,6 +846,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
...
@@ -842,6 +846,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
<div
id=
"vv-dialog-section"
></div>
<div
id=
"vv-dialog-section"
></div>
<button
class=
"btn btn-primary"
onclick=
"genVideo('v2v')"
>
Transform
</button>
<button
class=
"btn btn-primary"
onclick=
"genVideo('v2v')"
>
Transform
</button>
<div
class=
"progress"
id=
"vv-prog"
></div>
<div
class=
"progress"
id=
"vv-prog"
></div>
<div
class=
"gen-progress-wrap"
id=
"vv-pbar-wrap"
>
<div
class=
"gen-progress-bar-bg"
><div
class=
"gen-progress-bar-fill"
id=
"vv-pbar-fill"
></div></div>
<div
class=
"gen-progress-label"
id=
"vv-pbar-label"
></div>
</div>
</div>
</div>
<div
class=
"gen-out"
id=
"vv-out"
><div
class=
"gen-empty"
>
Transformed video will appear here
</div></div>
<div
class=
"gen-out"
id=
"vv-out"
><div
class=
"gen-empty"
>
Transformed video will appear here
</div></div>
</div></div>
</div></div>
...
@@ -945,6 +953,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
...
@@ -945,6 +953,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
</details>
</details>
<button
class=
"btn btn-primary"
onclick=
"genTi2V()"
style=
"margin-top:.25rem"
>
Generate
</button>
<button
class=
"btn btn-primary"
onclick=
"genTi2V()"
style=
"margin-top:.25rem"
>
Generate
</button>
<div
class=
"progress"
id=
"ti-prog"
></div>
<div
class=
"progress"
id=
"ti-prog"
></div>
<div
class=
"gen-progress-wrap"
id=
"ti-pbar-wrap"
>
<div
class=
"gen-progress-bar-bg"
><div
class=
"gen-progress-bar-fill"
id=
"ti-pbar-fill"
></div></div>
<div
class=
"gen-progress-label"
id=
"ti-pbar-label"
></div>
</div>
</div>
</div>
<div
class=
"gen-out"
id=
"ti-out"
><div
class=
"gen-empty"
>
Video will appear here
</div></div>
<div
class=
"gen-out"
id=
"ti-out"
><div
class=
"gen-empty"
>
Video will appear here
</div></div>
</div></div>
</div></div>
...
@@ -1100,6 +1112,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
...
@@ -1100,6 +1112,10 @@ a.dl { display:inline-block; margin-top:.4rem; }
<div
class=
"req-preview"
id=
"ag-preview"
></div>
<div
class=
"req-preview"
id=
"ag-preview"
></div>
<button
class=
"btn btn-primary"
onclick=
"genAudio()"
>
Generate
</button>
<button
class=
"btn btn-primary"
onclick=
"genAudio()"
>
Generate
</button>
<div
class=
"progress"
id=
"ag-prog"
></div>
<div
class=
"progress"
id=
"ag-prog"
></div>
<div
class=
"gen-progress-wrap"
id=
"ag-pbar-wrap"
>
<div
class=
"gen-progress-bar-bg"
><div
class=
"gen-progress-bar-fill"
id=
"ag-pbar-fill"
></div></div>
<div
class=
"gen-progress-label"
id=
"ag-pbar-label"
></div>
</div>
</div>
</div>
<div
class=
"gen-out"
id=
"ag-out"
><div
class=
"gen-empty"
>
Generated audio will appear here
</div></div>
<div
class=
"gen-out"
id=
"ag-out"
><div
class=
"gen-empty"
>
Generated audio will appear here
</div></div>
</div>
</div>
...
@@ -2067,6 +2083,65 @@ a.dl { display:inline-block; margin-top:.4rem; }
...
@@ -2067,6 +2083,65 @@ a.dl { display:inline-block; margin-top:.4rem; }
// ─────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────
let
models
=
[],
activeModel
=
null
,
chatHistory
=
[],
chatBusy
=
false
,
attachedImage
=
null
;
let
models
=
[],
activeModel
=
null
,
chatHistory
=
[],
chatBusy
=
false
,
attachedImage
=
null
;
let
_imgPollTimer
=
null
;
let
_imgPollTimer
=
null
;
let
_vidPollTimer
=
null
;
let
_audPollTimer
=
null
;
function
_startVidPoll
(
prefix
)
{
if
(
_vidPollTimer
)
{
clearInterval
(
_vidPollTimer
);
_vidPollTimer
=
null
;
}
const
wrap
=
$
(
prefix
+
'-pbar-wrap'
),
fill
=
$
(
prefix
+
'-pbar-fill'
),
lbl
=
$
(
prefix
+
'-pbar-label'
);
if
(
!
wrap
)
return
;
wrap
.
classList
.
add
(
'active'
);
fill
.
style
.
width
=
'0%'
;
lbl
.
textContent
=
''
;
_vidPollTimer
=
setInterval
(
async
()
=>
{
try
{
const
p
=
await
(
await
fetch
(
'/v1/video/progress'
)).
json
();
if
(
p
.
total
>
0
)
{
fill
.
style
.
width
=
p
.
pct
+
'%'
;
const
spd
=
p
.
it_per_s
>
0
?
` ·
${
p
.
it_per_s
}
it/s`
:
(
p
.
elapsed
>
0
?
` ·
${
p
.
elapsed
}
s`
:
''
);
lbl
.
textContent
=
`
${
p
.
current
}
/
${
p
.
total
}
steps
${
spd
}
`
;
}
else
if
(
p
.
elapsed
>
0
)
{
lbl
.
textContent
=
`
${
p
.
elapsed
}
s`
;
}
if
(
!
p
.
active
)
{
clearInterval
(
_vidPollTimer
);
_vidPollTimer
=
null
;
}
}
catch
(
_
)
{}
},
500
);
}
function
_stopVidPoll
(
prefix
,
done
)
{
if
(
_vidPollTimer
)
{
clearInterval
(
_vidPollTimer
);
_vidPollTimer
=
null
;
}
const
wrap
=
$
(
prefix
+
'-pbar-wrap'
),
fill
=
$
(
prefix
+
'-pbar-fill'
),
lbl
=
$
(
prefix
+
'-pbar-label'
);
if
(
!
wrap
)
return
;
if
(
done
)
{
fill
.
style
.
width
=
'100%'
;
lbl
.
textContent
=
'Done'
;
setTimeout
(()
=>
wrap
.
classList
.
remove
(
'active'
),
2000
);
}
else
{
wrap
.
classList
.
remove
(
'active'
);
}
}
function
_startAudPoll
(
prefix
)
{
if
(
_audPollTimer
)
{
clearInterval
(
_audPollTimer
);
_audPollTimer
=
null
;
}
const
wrap
=
$
(
prefix
+
'-pbar-wrap'
),
fill
=
$
(
prefix
+
'-pbar-fill'
),
lbl
=
$
(
prefix
+
'-pbar-label'
);
if
(
!
wrap
)
return
;
wrap
.
classList
.
add
(
'active'
);
fill
.
style
.
width
=
'0%'
;
lbl
.
textContent
=
''
;
_audPollTimer
=
setInterval
(
async
()
=>
{
try
{
const
p
=
await
(
await
fetch
(
'/v1/audio/progress'
)).
json
();
if
(
p
.
total
>
0
)
{
fill
.
style
.
width
=
p
.
pct
+
'%'
;
const
unit
=
p
.
unit
||
'it'
;
const
spd
=
p
.
it_per_s
>
0
?
` ·
${
p
.
it_per_s
}
${
unit
}
/s`
:
(
p
.
elapsed
>
0
?
` ·
${
p
.
elapsed
}
s`
:
''
);
lbl
.
textContent
=
`
${
p
.
current
}
/
${
p
.
total
}
steps
${
spd
}
`
;
}
else
if
(
p
.
elapsed
>
0
)
{
lbl
.
textContent
=
`Elapsed:
${
p
.
elapsed
}
s`
;
}
if
(
!
p
.
active
)
{
clearInterval
(
_audPollTimer
);
_audPollTimer
=
null
;
}
}
catch
(
_
)
{}
},
500
);
}
function
_stopAudPoll
(
prefix
,
done
)
{
if
(
_audPollTimer
)
{
clearInterval
(
_audPollTimer
);
_audPollTimer
=
null
;
}
const
wrap
=
$
(
prefix
+
'-pbar-wrap'
),
fill
=
$
(
prefix
+
'-pbar-fill'
),
lbl
=
$
(
prefix
+
'-pbar-label'
);
if
(
!
wrap
)
return
;
if
(
done
)
{
fill
.
style
.
width
=
'100%'
;
lbl
.
textContent
=
'Done'
;
setTimeout
(()
=>
wrap
.
classList
.
remove
(
'active'
),
2000
);
}
else
{
wrap
.
classList
.
remove
(
'active'
);
}
}
let
apiToken
=
null
;
let
apiToken
=
null
;
let
charSlots
=
{};
// prefix → [{name:'', images:[b64...]}]
let
charSlots
=
{};
// prefix → [{name:'', images:[b64...]}]
let
_charProfiles
=
[];
// cached list from /v1/characters
let
_charProfiles
=
[];
// cached list from /v1/characters
...
@@ -3543,6 +3618,7 @@ async function sendChat() {
...
@@ -3543,6 +3618,7 @@ async function sendChat() {
attachedImage
=
null
;
updateAttachBar
();
attachedImage
=
null
;
updateAttachBar
();
chatBusy
=
true
;
$
(
'send-btn'
).
disabled
=
true
;
chatBusy
=
true
;
$
(
'send-btn'
).
disabled
=
true
;
$
(
'typing'
).
textContent
=
'Thinking…'
;
$
(
'typing'
).
textContent
=
'Thinking…'
;
const
_chatT0
=
Date
.
now
();
try
{
try
{
const
r
=
await
fetch
(
'/v1/chat/completions'
,{
const
r
=
await
fetch
(
'/v1/chat/completions'
,{
method
:
'POST'
,
headers
:{
'Content-Type'
:
'application/json'
},
method
:
'POST'
,
headers
:{
'Content-Type'
:
'application/json'
},
...
@@ -3551,10 +3627,15 @@ async function sendChat() {
...
@@ -3551,10 +3627,15 @@ async function sendChat() {
if
(
!
r
.
ok
)
throw
new
Error
(
'HTTP '
+
r
.
status
+
': '
+
await
r
.
text
());
if
(
!
r
.
ok
)
throw
new
Error
(
'HTTP '
+
r
.
status
+
': '
+
await
r
.
text
());
const
d
=
await
r
.
json
();
const
d
=
await
r
.
json
();
const
reply
=
d
.
choices
[
0
].
message
.
content
;
const
reply
=
d
.
choices
[
0
].
message
.
content
;
const
elapsed
=
(
Date
.
now
()
-
_chatT0
)
/
1000
;
const
toks
=
d
.
usage
?.
completion_tokens
;
if
(
toks
&&
elapsed
>
0
)
{
$
(
'typing'
).
textContent
=
`
${
toks
}
tok ·
${(
toks
/
elapsed
).
toFixed
(
1
)}
tok/s`
;
}
addMsg
(
'assistant'
,
reply
);
addMsg
(
'assistant'
,
reply
);
chatHistory
.
push
({
role
:
'assistant'
,
content
:
reply
});
chatHistory
.
push
({
role
:
'assistant'
,
content
:
reply
});
}
catch
(
e
)
{
addMsg
(
'assistant'
,
'Error: '
+
e
.
message
);
}
}
catch
(
e
)
{
addMsg
(
'assistant'
,
'Error: '
+
e
.
message
);
}
finally
{
chatBusy
=
false
;
$
(
'send-btn'
).
disabled
=
false
;
$
(
'typing'
).
textContent
=
''
;
}
finally
{
chatBusy
=
false
;
$
(
'send-btn'
).
disabled
=
false
;
setTimeout
(()
=>
{
$
(
'typing'
).
textContent
=
''
;
},
3000
)
;
}
}
}
$
(
'chat-in'
).
addEventListener
(
'keydown'
,
e
=>
{
if
(
e
.
key
===
'Enter'
&&!
e
.
shiftKey
){
e
.
preventDefault
();
sendChat
();}
});
$
(
'chat-in'
).
addEventListener
(
'keydown'
,
e
=>
{
if
(
e
.
key
===
'Enter'
&&!
e
.
shiftKey
){
e
.
preventDefault
();
sendChat
();}
});
...
@@ -3977,7 +4058,8 @@ async function genImage() {
...
@@ -3977,7 +4058,8 @@ async function genImage() {
const
p
=
await
(
await
fetch
(
'/v1/images/progress'
)).
json
();
const
p
=
await
(
await
fetch
(
'/v1/images/progress'
)).
json
();
if
(
p
.
total
>
0
){
if
(
p
.
total
>
0
){
fill
.
style
.
width
=
p
.
pct
+
'%'
;
fill
.
style
.
width
=
p
.
pct
+
'%'
;
lbl
.
textContent
=
p
.
current
+
' / '
+
p
.
total
+
' steps'
;
const
spd
=
p
.
it_per_s
>
0
?
` ·
${
p
.
it_per_s
}
it/s`
:
(
p
.
elapsed
>
0
?
` ·
${
p
.
elapsed
}
s`
:
''
);
lbl
.
textContent
=
`
${
p
.
current
}
/
${
p
.
total
}
steps
${
spd
}
`
;
}
}
if
(
!
p
.
active
){
clearInterval
(
_imgPollTimer
);
_imgPollTimer
=
null
;
}
if
(
!
p
.
active
){
clearInterval
(
_imgPollTimer
);
_imgPollTimer
=
null
;
}
}
catch
(
_
){}
}
catch
(
_
){}
...
@@ -4104,10 +4186,12 @@ async function genSegment() {
...
@@ -4104,10 +4186,12 @@ async function genSegment() {
// ─────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────
async
function
genVideo
(
mode
)
{
async
function
genVideo
(
mode
)
{
if
(
!
activeModel
)
return
;
if
(
!
activeModel
)
return
;
const
progMap
=
{
t2v
:
'vt-prog'
,
i2v
:
'vi-prog'
,
v2v
:
'vv-prog'
};
const
progMap
=
{
t2v
:
'vt-prog'
,
i2v
:
'vi-prog'
,
v2v
:
'vv-prog'
};
const
outMap
=
{
t2v
:
'vt-out'
,
i2v
:
'vi-out'
,
v2v
:
'vv-out'
};
const
outMap
=
{
t2v
:
'vt-out'
,
i2v
:
'vi-out'
,
v2v
:
'vv-out'
};
const
prog
=
progMap
[
mode
],
outId
=
outMap
[
mode
];
const
prefixMap
=
{
t2v
:
'vt'
,
i2v
:
'vi'
,
v2v
:
'vv'
};
const
prog
=
progMap
[
mode
],
outId
=
outMap
[
mode
],
prefix
=
prefixMap
[
mode
];
$
(
prog
).
textContent
=
'Generating… (this may take several minutes)'
;
$
(
prog
).
textContent
=
'Generating… (this may take several minutes)'
;
_startVidPoll
(
prefix
);
const
subId
=
{
t2v
:
'vid-t2v'
,
i2v
:
'vid-i2v'
,
v2v
:
'vid-v2v'
}[
mode
];
const
subId
=
{
t2v
:
'vid-t2v'
,
i2v
:
'vid-i2v'
,
v2v
:
'vid-v2v'
}[
mode
];
const
body
=
{
model
:
modelForSub
(
subId
),
mode
};
const
body
=
{
model
:
modelForSub
(
subId
),
mode
};
if
(
mode
===
't2v'
)
{
if
(
mode
===
't2v'
)
{
...
@@ -4160,8 +4244,9 @@ async function genVideo(mode) {
...
@@ -4160,8 +4244,9 @@ async function genVideo(mode) {
}
}
try
{
try
{
const
d
=
await
post
(
'/v1/video/generations'
,
body
);
const
d
=
await
post
(
'/v1/video/generations'
,
body
);
_stopVidPoll
(
prefix
,
true
);
showVideo
(
outId
,
vidSrc
(
d
.
data
[
0
]),
prog
);
showVideo
(
outId
,
vidSrc
(
d
.
data
[
0
]),
prog
);
}
catch
(
e
)
{
$
(
prog
).
textContent
=
'Error: '
+
e
.
message
;
}
}
catch
(
e
)
{
_stopVidPoll
(
prefix
,
false
);
$
(
prog
).
textContent
=
'Error: '
+
e
.
message
;
}
}
}
// ─────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────
...
@@ -4172,6 +4257,7 @@ async function genTi2V() {
...
@@ -4172,6 +4257,7 @@ async function genTi2V() {
$
(
'ti-prog'
).
textContent
=
'Generating… (may take several minutes)'
;
$
(
'ti-prog'
).
textContent
=
'Generating… (may take several minutes)'
;
const
[
initImg
,
endImg
,
srcVid
]
=
await
Promise
.
all
([
b64OrNull
(
'ti-init'
),
b64OrNull
(
'ti-end'
),
b64OrNull
(
'ti-vid'
)]);
const
[
initImg
,
endImg
,
srcVid
]
=
await
Promise
.
all
([
b64OrNull
(
'ti-init'
),
b64OrNull
(
'ti-end'
),
b64OrNull
(
'ti-vid'
)]);
if
(
!
srcVid
&&
!
initImg
)
{
$
(
'ti-prog'
).
textContent
=
'Select an initial image or source video.'
;
return
;
}
if
(
!
srcVid
&&
!
initImg
)
{
$
(
'ti-prog'
).
textContent
=
'Select an initial image or source video.'
;
return
;
}
_startVidPoll
(
'ti'
);
const
body
=
{
const
body
=
{
model
:
modelForSub
(
'vid-ti2v'
),
model
:
modelForSub
(
'vid-ti2v'
),
...
@@ -4217,8 +4303,9 @@ async function genTi2V() {
...
@@ -4217,8 +4303,9 @@ async function genTi2V() {
try
{
try
{
const
d
=
await
post
(
'/v1/video/generations'
,
body
);
const
d
=
await
post
(
'/v1/video/generations'
,
body
);
_stopVidPoll
(
'ti'
,
true
);
showVideo
(
'ti-out'
,
vidSrc
(
d
.
data
[
0
]),
'ti-prog'
);
showVideo
(
'ti-out'
,
vidSrc
(
d
.
data
[
0
]),
'ti-prog'
);
}
catch
(
e
)
{
$
(
'ti-prog'
).
textContent
=
'Error: '
+
e
.
message
;
}
}
catch
(
e
)
{
_stopVidPoll
(
'ti'
,
false
);
$
(
'ti-prog'
).
textContent
=
'Error: '
+
e
.
message
;
}
}
}
// ─────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────
...
@@ -4928,6 +5015,7 @@ async function runPipeline4() {
...
@@ -4928,6 +5015,7 @@ async function runPipeline4() {
async
function
genAudio
()
{
async
function
genAudio
()
{
if
(
!
activeModel
)
return
;
if
(
!
activeModel
)
return
;
$
(
'ag-prog'
).
textContent
=
'Generating audio…'
;
$
(
'ag-prog'
).
textContent
=
'Generating audio…'
;
_startAudPoll
(
'ag'
);
const
melody
=
await
b64OrNull
(
'ag-melody'
);
const
melody
=
await
b64OrNull
(
'ag-melody'
);
const
body
=
{
const
body
=
{
model
:
modelForSub
(
'aud-gen'
),
prompt
:
val
(
'ag-prompt'
),
model
:
modelForSub
(
'aud-gen'
),
prompt
:
val
(
'ag-prompt'
),
...
@@ -4939,6 +5027,7 @@ async function genAudio() {
...
@@ -4939,6 +5027,7 @@ async function genAudio() {
};
};
try
{
try
{
const
d
=
await
post
(
'/v1/audio/generate'
,
body
);
const
d
=
await
post
(
'/v1/audio/generate'
,
body
);
_stopAudPoll
(
'ag'
,
true
);
const
item
=
d
.
data
[
0
];
const
item
=
d
.
data
[
0
];
const
src
=
audSrc
(
item
);
const
src
=
audSrc
(
item
);
showAudio
(
'ag-out'
,
src
,
'ag-prog'
,
'wav'
);
showAudio
(
'ag-out'
,
src
,
'ag-prog'
,
'wav'
);
...
@@ -4949,7 +5038,7 @@ async function genAudio() {
...
@@ -4949,7 +5038,7 @@ async function genAudio() {
summary
:
buildAudioHistorySummary
(
val
(
'ag-prompt'
),
body
.
duration
),
summary
:
buildAudioHistorySummary
(
val
(
'ag-prompt'
),
body
.
duration
),
links
:
buildAudioLinks
(
item
),
links
:
buildAudioLinks
(
item
),
});
});
}
catch
(
e
)
{
$
(
'ag-prog'
).
textContent
=
'Error: '
+
e
.
message
;
}
}
catch
(
e
)
{
_stopAudPoll
(
'ag'
,
false
);
$
(
'ag-prog'
).
textContent
=
'Error: '
+
e
.
message
;
}
}
}
// ─────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────
...
@@ -5289,6 +5378,10 @@ document.getElementById('panel-vid-t2v').innerHTML = `<div class="gen-wrap">
...
@@ -5289,6 +5378,10 @@ document.getElementById('panel-vid-t2v').innerHTML = `<div class="gen-wrap">
<div id="vt-dialog-section"></div>
<div id="vt-dialog-section"></div>
<button class="btn btn-primary" onclick="genVideo('t2v')">Generate Video</button>
<button class="btn btn-primary" onclick="genVideo('t2v')">Generate Video</button>
<div class="progress" id="vt-prog"></div>
<div class="progress" id="vt-prog"></div>
<div class="gen-progress-wrap" id="vt-pbar-wrap">
<div class="gen-progress-bar-bg"><div class="gen-progress-bar-fill" id="vt-pbar-fill"></div></div>
<div class="gen-progress-label" id="vt-pbar-label"></div>
</div>
</div>
</div>
<div class="gen-out" id="vt-out"><div class="gen-empty">Video will appear here</div></div>
<div class="gen-out" id="vt-out"><div class="gen-empty">Video will appear here</div></div>
</div>`
;
</div>`
;
...
...
codai/api/audio_gen.py
View file @
0ce4d5af
...
@@ -37,6 +37,32 @@ router = APIRouter()
...
@@ -37,6 +37,32 @@ router = APIRouter()
global_args
=
None
global_args
=
None
global_file_path
=
None
global_file_path
=
None
# =============================================================================
# Audio generation progress tracking
# =============================================================================
_aud_progress
:
dict
=
{
"current"
:
0
,
"total"
:
0
,
"active"
:
False
,
"started_at"
:
0.0
,
"it_per_s"
:
0.0
,
"unit"
:
"it"
,
}
def
_aud_progress_reset
(
total
:
int
,
unit
:
str
=
"it"
):
_aud_progress
[
"current"
]
=
0
_aud_progress
[
"total"
]
=
total
_aud_progress
[
"active"
]
=
True
_aud_progress
[
"started_at"
]
=
time
.
monotonic
()
_aud_progress
[
"it_per_s"
]
=
0.0
_aud_progress
[
"unit"
]
=
unit
def
_aud_progress_done
():
_aud_progress
[
"current"
]
=
max
(
_aud_progress
[
"current"
],
_aud_progress
[
"total"
])
_aud_progress
[
"active"
]
=
False
def
_aud_progress_step
(
step
:
int
):
_aud_progress
[
"current"
]
=
step
elapsed
=
time
.
monotonic
()
-
_aud_progress
[
"started_at"
]
if
elapsed
>
0
and
step
>
0
:
_aud_progress
[
"it_per_s"
]
=
round
(
step
/
elapsed
,
2
)
def
set_global_args
(
args
):
def
set_global_args
(
args
):
global
global_args
global
global_args
...
@@ -124,6 +150,8 @@ def _generate_audio(pipe, model_name: str, request: AudioGenerationRequest):
...
@@ -124,6 +150,8 @@ def _generate_audio(pipe, model_name: str, request: AudioGenerationRequest):
temperature
=
request
.
temperature
,
temperature
=
request
.
temperature
,
cfg_coef
=
request
.
cfg_coef
,
cfg_coef
=
request
.
cfg_coef
,
)
)
# MusicGen/AudioGen generate in one shot — track elapsed only
_aud_progress_reset
(
0
,
unit
=
"s"
)
if
request
.
melody
and
model_type
==
'musicgen'
:
if
request
.
melody
and
model_type
==
'musicgen'
:
import
torchaudio
,
torch
import
torchaudio
,
torch
raw
=
_decode_b64_or_url
(
request
.
melody
)
raw
=
_decode_b64_or_url
(
request
.
melody
)
...
@@ -135,10 +163,18 @@ def _generate_audio(pipe, model_name: str, request: AudioGenerationRequest):
...
@@ -135,10 +163,18 @@ def _generate_audio(pipe, model_name: str, request: AudioGenerationRequest):
sr
=
pipe
.
sample_rate
sr
=
pipe
.
sample_rate
elif
model_type
==
'audioldm'
:
elif
model_type
==
'audioldm'
:
num_steps
=
50
_aud_progress_reset
(
num_steps
,
unit
=
"it"
)
def
_aud_step_cb
(
pipe
,
step_index
,
timestep
,
callback_kwargs
):
_aud_progress_step
(
step_index
+
1
)
return
callback_kwargs
result
=
pipe
(
result
=
pipe
(
request
.
prompt
,
request
.
prompt
,
num_inference_steps
=
50
,
num_inference_steps
=
num_steps
,
audio_length_in_s
=
request
.
duration
,
audio_length_in_s
=
request
.
duration
,
callback_on_step_end
=
_aud_step_cb
,
)
)
audio_np
=
result
.
audios
[
0
]
audio_np
=
result
.
audios
[
0
]
sr
=
16000
sr
=
16000
...
@@ -162,6 +198,23 @@ def _decode_b64_or_url(data: str) -> bytes:
...
@@ -162,6 +198,23 @@ def _decode_b64_or_url(data: str) -> bytes:
return
base64
.
b64decode
(
data
)
return
base64
.
b64decode
(
data
)
@
router
.
get
(
"/v1/audio/progress"
)
async
def
get_audio_progress
():
"""Return current audio generation progress including speed."""
elapsed
=
time
.
monotonic
()
-
_aud_progress
[
"started_at"
]
if
_aud_progress
[
"active"
]
else
0.0
total
=
_aud_progress
[
"total"
]
current
=
_aud_progress
[
"current"
]
return
{
"current"
:
current
,
"total"
:
total
,
"active"
:
_aud_progress
[
"active"
],
"pct"
:
int
(
current
/
total
*
100
)
if
total
>
0
else
0
,
"it_per_s"
:
_aud_progress
[
"it_per_s"
],
"elapsed"
:
round
(
elapsed
,
1
),
"unit"
:
_aud_progress
[
"unit"
],
}
@
router
.
post
(
"/v1/audio/generate"
,
response_model
=
AudioGenerationResponse
)
@
router
.
post
(
"/v1/audio/generate"
,
response_model
=
AudioGenerationResponse
)
async
def
audio_generate
(
request
:
AudioGenerationRequest
,
http_request
:
Request
=
None
):
async
def
audio_generate
(
request
:
AudioGenerationRequest
,
http_request
:
Request
=
None
):
"""
"""
...
@@ -196,7 +249,10 @@ async def audio_generate(request: AudioGenerationRequest, http_request: Request
...
@@ -196,7 +249,10 @@ async def audio_generate(request: AudioGenerationRequest, http_request: Request
audio_bytes
,
ext
=
await
asyncio
.
get_event_loop
()
.
run_in_executor
(
audio_bytes
,
ext
=
await
asyncio
.
get_event_loop
()
.
run_in_executor
(
None
,
_generate_audio
,
pipe
,
model_name
,
request
)
None
,
_generate_audio
,
pipe
,
model_name
,
request
)
except
Exception
as
e
:
except
Exception
as
e
:
_aud_progress_done
()
raise
HTTPException
(
status_code
=
500
,
detail
=
f
"Audio generation failed: {e}"
)
raise
HTTPException
(
status_code
=
500
,
detail
=
f
"Audio generation failed: {e}"
)
finally
:
_aud_progress_done
()
result
=
_save_audio_response
(
audio_bytes
,
ext
,
http_request
)
result
=
_save_audio_response
(
audio_bytes
,
ext
,
http_request
)
...
...
codai/api/images.py
View file @
0ce4d5af
...
@@ -118,12 +118,19 @@ queue_flags = {}
...
@@ -118,12 +118,19 @@ queue_flags = {}
# =============================================================================
# =============================================================================
# Generation progress tracking
# Generation progress tracking
# =============================================================================
# =============================================================================
_gen_progress
:
dict
=
{
"current"
:
0
,
"total"
:
0
,
"active"
:
False
}
import
time
as
_time
_gen_progress
:
dict
=
{
"current"
:
0
,
"total"
:
0
,
"active"
:
False
,
"started_at"
:
0.0
,
"it_per_s"
:
0.0
,
}
def
_progress_reset
(
total
:
int
):
def
_progress_reset
(
total
:
int
):
_gen_progress
[
"current"
]
=
0
_gen_progress
[
"current"
]
=
0
_gen_progress
[
"total"
]
=
total
_gen_progress
[
"total"
]
=
total
_gen_progress
[
"active"
]
=
True
_gen_progress
[
"active"
]
=
True
_gen_progress
[
"started_at"
]
=
_time
.
monotonic
()
_gen_progress
[
"it_per_s"
]
=
0.0
def
_progress_done
():
def
_progress_done
():
_gen_progress
[
"current"
]
=
_gen_progress
[
"total"
]
_gen_progress
[
"current"
]
=
_gen_progress
[
"total"
]
...
@@ -131,6 +138,9 @@ def _progress_done():
...
@@ -131,6 +138,9 @@ def _progress_done():
def
_progress_step
(
step
:
int
):
def
_progress_step
(
step
:
int
):
_gen_progress
[
"current"
]
=
step
_gen_progress
[
"current"
]
=
step
elapsed
=
_time
.
monotonic
()
-
_gen_progress
[
"started_at"
]
if
elapsed
>
0
and
step
>
0
:
_gen_progress
[
"it_per_s"
]
=
round
(
step
/
elapsed
,
2
)
# =============================================================================
# =============================================================================
...
@@ -884,13 +894,16 @@ router = APIRouter()
...
@@ -884,13 +894,16 @@ router = APIRouter()
@
router
.
get
(
"/v1/images/progress"
)
@
router
.
get
(
"/v1/images/progress"
)
async
def
get_image_progress
():
async
def
get_image_progress
():
"""Return current image generation step progress."""
"""Return current image generation step progress including speed."""
elapsed
=
_time
.
monotonic
()
-
_gen_progress
[
"started_at"
]
if
_gen_progress
[
"active"
]
else
0.0
return
{
return
{
"current"
:
_gen_progress
[
"current"
],
"current"
:
_gen_progress
[
"current"
],
"total"
:
_gen_progress
[
"total"
],
"total"
:
_gen_progress
[
"total"
],
"active"
:
_gen_progress
[
"active"
],
"active"
:
_gen_progress
[
"active"
],
"pct"
:
int
(
_gen_progress
[
"current"
]
/
_gen_progress
[
"total"
]
*
100
)
"pct"
:
int
(
_gen_progress
[
"current"
]
/
_gen_progress
[
"total"
]
*
100
)
if
_gen_progress
[
"total"
]
>
0
else
0
,
if
_gen_progress
[
"total"
]
>
0
else
0
,
"it_per_s"
:
_gen_progress
[
"it_per_s"
],
"elapsed"
:
round
(
elapsed
,
1
),
}
}
...
...
codai/api/video.py
View file @
0ce4d5af
...
@@ -51,6 +51,31 @@ router = APIRouter()
...
@@ -51,6 +51,31 @@ router = APIRouter()
global_args
=
None
global_args
=
None
global_file_path
=
None
global_file_path
=
None
# =============================================================================
# Video generation progress tracking
# =============================================================================
_vid_progress
:
dict
=
{
"current"
:
0
,
"total"
:
0
,
"active"
:
False
,
"started_at"
:
0.0
,
"it_per_s"
:
0.0
,
}
def
_vid_progress_reset
(
total
:
int
):
_vid_progress
[
"current"
]
=
0
_vid_progress
[
"total"
]
=
total
_vid_progress
[
"active"
]
=
True
_vid_progress
[
"started_at"
]
=
time
.
monotonic
()
_vid_progress
[
"it_per_s"
]
=
0.0
def
_vid_progress_done
():
_vid_progress
[
"current"
]
=
_vid_progress
[
"total"
]
_vid_progress
[
"active"
]
=
False
def
_vid_progress_step
(
step
:
int
):
_vid_progress
[
"current"
]
=
step
elapsed
=
time
.
monotonic
()
-
_vid_progress
[
"started_at"
]
if
elapsed
>
0
and
step
>
0
:
_vid_progress
[
"it_per_s"
]
=
round
(
step
/
elapsed
,
2
)
def
set_global_args
(
args
):
def
set_global_args
(
args
):
global
global_args
global
global_args
...
@@ -322,6 +347,17 @@ def _generate_video(pipe, request: VideoGenerationRequest):
...
@@ -322,6 +347,17 @@ def _generate_video(pipe, request: VideoGenerationRequest):
kw
.
setdefault
(
'guidance_scale'
,
7.5
)
kw
.
setdefault
(
'guidance_scale'
,
7.5
)
kw
.
setdefault
(
'num_frames'
,
16
)
kw
.
setdefault
(
'num_frames'
,
16
)
_vid_progress_reset
(
kw
[
'num_inference_steps'
])
def
_vid_step_cb
(
pipe
,
step_index
,
timestep
,
callback_kwargs
):
_vid_progress_step
(
step_index
+
1
)
return
callback_kwargs
try
:
kw
[
'callback_on_step_end'
]
=
_vid_step_cb
except
Exception
:
pass
_apply_camera_motion
(
kw
,
request
.
camera_motion
)
_apply_camera_motion
(
kw
,
request
.
camera_motion
)
char_images
,
char_names
=
_resolve_character_inputs
(
request
)
char_images
,
char_names
=
_resolve_character_inputs
(
request
)
...
@@ -355,6 +391,7 @@ def _generate_video(pipe, request: VideoGenerationRequest):
...
@@ -355,6 +391,7 @@ def _generate_video(pipe, request: VideoGenerationRequest):
kw
[
'strength'
]
=
request
.
strength
kw
[
'strength'
]
=
request
.
strength
frames
=
_run_pipeline
(
pipe
,
kw
)
frames
=
_run_pipeline
(
pipe
,
kw
)
_vid_progress_done
()
return
frames
,
fps
return
frames
,
fps
...
@@ -781,6 +818,25 @@ def _translate_srt(srt_path: str, target_lang: str, temps: list) -> str:
...
@@ -781,6 +818,25 @@ def _translate_srt(srt_path: str, target_lang: str, temps: list) -> str:
return
srt_path
return
srt_path
# =============================================================================
# Progress endpoint
# =============================================================================
@
router
.
get
(
"/v1/video/progress"
)
async
def
get_video_progress
():
"""Return current video generation step progress including speed."""
elapsed
=
time
.
monotonic
()
-
_vid_progress
[
"started_at"
]
if
_vid_progress
[
"active"
]
else
0.0
return
{
"current"
:
_vid_progress
[
"current"
],
"total"
:
_vid_progress
[
"total"
],
"active"
:
_vid_progress
[
"active"
],
"pct"
:
int
(
_vid_progress
[
"current"
]
/
_vid_progress
[
"total"
]
*
100
)
if
_vid_progress
[
"total"
]
>
0
else
0
,
"it_per_s"
:
_vid_progress
[
"it_per_s"
],
"elapsed"
:
round
(
elapsed
,
1
),
}
# =============================================================================
# =============================================================================
# Main generation endpoint
# Main generation endpoint
# =============================================================================
# =============================================================================
...
@@ -836,6 +892,7 @@ async def video_generations(request: VideoGenerationRequest,
...
@@ -836,6 +892,7 @@ async def video_generations(request: VideoGenerationRequest,
frames
,
fps
=
await
asyncio
.
get_event_loop
()
.
run_in_executor
(
frames
,
fps
=
await
asyncio
.
get_event_loop
()
.
run_in_executor
(
None
,
_generate_video
,
pipe
,
request
)
None
,
_generate_video
,
pipe
,
request
)
except
Exception
as
e
:
except
Exception
as
e
:
_vid_progress_done
()
raise
HTTPException
(
status_code
=
500
,
detail
=
f
"Video generation failed: {e}"
)
raise
HTTPException
(
status_code
=
500
,
detail
=
f
"Video generation failed: {e}"
)
# Encode raw frames to MP4
# Encode raw frames to MP4
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment