Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
# FILE: app.py
|
3 |
# Description: Image-to-Video generation server with Gradio UI and FastAPI for Hugging Face Spaces
|
4 |
# Version: 1.2.8
|
5 |
-
# Timestamp: 2025-07-01 20:
|
6 |
# Author: Grok 3, built by xAI (based on GhostAI's ghostpack_gradio_f1.py)
|
7 |
# NOTE: Optimized for Hugging Face Spaces with H200 GPU, 25 min/day render time
|
8 |
# Loads models from Hugging Face Hub to avoid HDD costs
|
@@ -623,7 +623,7 @@ async def stop_render(job_id: str, api_key: str = Depends(verify_api_key)):
|
|
623 |
stream.stop()
|
624 |
active_jobs.pop(job_id, None)
|
625 |
job_status[job_id]["status"] = "stopped"
|
626 |
-
job_status[
|
627 |
logger.info(f"Stopped job {job_id}")
|
628 |
print(f"{yellow(f'Stopped job {job_id}')}")
|
629 |
return JSONResponse(content={"message": f"Job {job_id} stopped"})
|
@@ -1024,4 +1024,357 @@ def process(img, prm, npr, sd, sec, win, stp, cfg, gsc, rsc, kee, tea, crf, disa
|
|
1024 |
yield None, None, "", "", gr.update(interactive=False), gr.update(interactive=True)
|
1025 |
stream = AsyncStream()
|
1026 |
jid = str(uuid.uuid4())
|
1027 |
-
async_run(worker, img, prm, npr, sd, sec, win,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
# FILE: app.py
|
3 |
# Description: Image-to-Video generation server with Gradio UI and FastAPI for Hugging Face Spaces
|
4 |
# Version: 1.2.8
|
5 |
+
# Timestamp: 2025-07-01 20:41 CDT
|
6 |
# Author: Grok 3, built by xAI (based on GhostAI's ghostpack_gradio_f1.py)
|
7 |
# NOTE: Optimized for Hugging Face Spaces with H200 GPU, 25 min/day render time
|
8 |
# Loads models from Hugging Face Hub to avoid HDD costs
|
|
|
623 |
stream.stop()
|
624 |
active_jobs.pop(job_id, None)
|
625 |
job_status[job_id]["status"] = "stopped"
|
626 |
+
job_status[jid]["progress"] = 0.0
|
627 |
logger.info(f"Stopped job {job_id}")
|
628 |
print(f"{yellow(f'Stopped job {job_id}')}")
|
629 |
return JSONResponse(content={"message": f"Job {job_id} stopped"})
|
|
|
1024 |
yield None, None, "", "", gr.update(interactive=False), gr.update(interactive=True)
|
1025 |
stream = AsyncStream()
|
1026 |
jid = str(uuid.uuid4())
|
1027 |
+
async_run(worker, img, prm, npr, sd, sec, win, stp, cfg, gsc, rsc, kee, tea, crf, disable_prompt_mods, link_steps_window, stream, jid)
|
1028 |
+
out, log = None, ""
|
1029 |
+
try:
|
1030 |
+
while True:
|
1031 |
+
flag, data = stream.output_queue.next()
|
1032 |
+
if job_status.get(jid, {}).get("status") == "complete":
|
1033 |
+
break
|
1034 |
+
if flag == "file":
|
1035 |
+
out = data
|
1036 |
+
yield out, gr.update(), gr.update(), log, gr.update(interactive=False), gr.update(interactive=True)
|
1037 |
+
if flag == "progress":
|
1038 |
+
pv, desc, html = data
|
1039 |
+
log = desc
|
1040 |
+
yield gr.update(), gr.update(visible=True, value=pv), desc, html, gr.update(interactive=False), gr.update(interactive=True)
|
1041 |
+
if flag == "complete":
|
1042 |
+
yield data, gr.update(visible=False), "Generation complete", "", gr.update(interactive=True), gr.update(interactive=False)
|
1043 |
+
break
|
1044 |
+
if flag == "end":
|
1045 |
+
yield out, gr.update(visible=False), f"Error: {data}", "", gr.update(interactive=True), gr.update(interactive=False)
|
1046 |
+
break
|
1047 |
+
except Exception as e:
|
1048 |
+
logger.error(f"Process loop failed: {e}")
|
1049 |
+
yield out, gr.update(visible=False), f"Error: {str(e)}", "", gr.update(interactive=True), gr.update(interactive=False)
|
1050 |
+
job_status[jid]["status"] = "error"
|
1051 |
+
finally:
|
1052 |
+
clear_queue(stream.input_queue)
|
1053 |
+
clear_queue(stream.output_queue)
|
1054 |
+
torch.cuda.empty_cache()
|
1055 |
+
|
1056 |
+
def end_process():
|
1057 |
+
global stream
|
1058 |
+
if stream:
|
1059 |
+
stream.input_queue.push("end")
|
1060 |
+
logger.info("Gradio: Render stop requested")
|
1061 |
+
print(f"{red('Gradio: Render stop requested')}")
|
1062 |
+
|
1063 |
+
# Gradio UI (same as original)
|
1064 |
+
quick_prompts = [
|
1065 |
+
["Smooth animation: A character waves for 3 seconds, then stands still for 2 seconds, static camera, silent."],
|
1066 |
+
["Smooth animation: A character moves for 5 seconds, static camera, silent."]
|
1067 |
+
]
|
1068 |
+
css = make_progress_bar_css() + """
|
1069 |
+
.orange-button{background:#ff6200;color:#fff;border-color:#ff6200;}
|
1070 |
+
.load-button{background:#4CAF50;color:#fff;border-color:#4CAF50;margin-left:10px;}
|
1071 |
+
.big-setting-button{background:#0066cc;color:#fff;border:none;padding:14px 24px;font-size:18px;width:100%;border-radius:6px;margin:8px 0;}
|
1072 |
+
.styled-dropdown{width:250px;padding:5px;border-radius:4px;}
|
1073 |
+
.viewer-column{width:100%;max-width:900px;margin:0 auto;}
|
1074 |
+
.media-preview img,.media-preview video{max-width:100%;height:380px;object-fit:contain;border:1px solid #444;border-radius:6px;}
|
1075 |
+
.media-container{display:flex;gap:20px;align-items:flex-start;}
|
1076 |
+
.control-box{min-width:220px;}
|
1077 |
+
.control-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
|
1078 |
+
.image-gallery{display:grid!important;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))!important;gap:10px;padding:10px!important;overflow-y:auto!important;max-height:360px!important;}
|
1079 |
+
.image-gallery .gallery-item{padding:10px;height:360px!important;width:300px!important;}
|
1080 |
+
.image-gallery img{object-fit:contain;height:360px!important;width:300px!important;}
|
1081 |
+
.video-gallery{display:grid!important;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))!important;gap:10px;padding:10px!important;overflow-y:auto!important;max-height:360px!important;}
|
1082 |
+
.video-gallery .gallery-item{padding:10px;height:360px!important;width:300px!important;}
|
1083 |
+
.video-gallery video{object-fit:contain;height:360px!important;width:300px!important;}
|
1084 |
+
.stop-button {background-color: #ff4d4d !important; color: white !important;}
|
1085 |
+
"""
|
1086 |
+
|
1087 |
+
blk = gr.Blocks(css=css, title="GhostPack F1 Pro").queue()
|
1088 |
+
with blk:
|
1089 |
+
gr.Markdown("# π» GhostPack F1 Pro")
|
1090 |
+
with gr.Tabs():
|
1091 |
+
with gr.TabItem("π» Generate"):
|
1092 |
+
with gr.Row():
|
1093 |
+
with gr.Column():
|
1094 |
+
img_in = gr.Image(sources="upload", type="numpy", label="Image", height=320)
|
1095 |
+
generate_button = gr.Button("Generate Video", elem_id="generate_button")
|
1096 |
+
stop_button = gr.Button("Stop Generation", elem_id="stop_button", elem_classes="stop-button")
|
1097 |
+
prm = gr.Textbox(
|
1098 |
+
label="Prompt",
|
1099 |
+
value="Smooth animation: A female stands with subtle, sensual micro-movements, breathing gently, slight head tilt, static camera, silent",
|
1100 |
+
elem_id="prompt_input",
|
1101 |
+
)
|
1102 |
+
npr = gr.Textbox(
|
1103 |
+
label="Negative Prompt",
|
1104 |
+
value="low quality, blurry, speaking, talking, moaning, vocalizing, lip movement, mouth animation, sound, dialogue, speech, whispering, shouting, lip sync, facial animation, expressive face, verbal expression, animated mouth",
|
1105 |
+
elem_id="negative_prompt_input",
|
1106 |
+
)
|
1107 |
+
save_msg = gr.Markdown("")
|
1108 |
+
disable_prompt_mods = gr.Checkbox(label="Disable Prompt Modifications", value=False)
|
1109 |
+
link_steps_window = gr.Checkbox(label="Link Steps and Latent Window", value=True)
|
1110 |
+
btn_save = gr.Button("Save Prompt")
|
1111 |
+
btn1, btn2, btn3 = (
|
1112 |
+
gr.Button("Load Most Recent"),
|
1113 |
+
gr.Button("Load 2nd Recent"),
|
1114 |
+
gr.Button("Load 3rd Recent"),
|
1115 |
+
)
|
1116 |
+
ds = gr.Dataset(samples=quick_prompts, label="Quick List", components=[prm])
|
1117 |
+
ds.click(lambda x: x[0], [ds], [prm])
|
1118 |
+
btn_save.click(save_prompt_fn, [prm, npr], [save_msg])
|
1119 |
+
btn1.click(lambda: load_prompt_fn(0), [], [prm])
|
1120 |
+
btn2.click(lambda: load_prompt_fn(1), [], [prm])
|
1121 |
+
btn3.click(lambda: load_prompt_fn(2), [], [prm])
|
1122 |
+
camera_action_input = gr.Dropdown(
|
1123 |
+
choices=[
|
1124 |
+
"Static Camera", "Slight Orbit Left", "Slight Orbit Right",
|
1125 |
+
"Slight Orbit Up", "Slight Orbit Down", "Top-Down View",
|
1126 |
+
"Slight Zoom In", "Slight Zoom Out",
|
1127 |
+
],
|
1128 |
+
label="Camera Action",
|
1129 |
+
value="Static Camera",
|
1130 |
+
elem_id="camera_action_input",
|
1131 |
+
info="Select a camera movement to append to the prompt.",
|
1132 |
+
)
|
1133 |
+
camera_action_input.change(
|
1134 |
+
fn=lambda prompt, camera_action: update_prompt(prompt, camera_action),
|
1135 |
+
inputs=[prm, camera_action_input],
|
1136 |
+
outputs=prm,
|
1137 |
+
)
|
1138 |
+
with gr.Column():
|
1139 |
+
pv = gr.Image(label="Next Latents", height=200, visible=False)
|
1140 |
+
vid = gr.Video(label="Finished", autoplay=True, height=500, loop=True, show_share_button=False)
|
1141 |
+
log_md = gr.Markdown("")
|
1142 |
+
bar = gr.HTML("")
|
1143 |
+
with gr.Column():
|
1144 |
+
se = gr.Number(label="Seed", value=31337, precision=0, elem_id="seed_input")
|
1145 |
+
sec = gr.Slider(label="Video Length (s)", minimum=1, maximum=10, value=8.0, step=0.1, elem_id="video_length_input")
|
1146 |
+
win = gr.Slider(label="Latent Window", minimum=1, maximum=10, value=3, step=1, elem_id="latent_window_input")
|
1147 |
+
stp = gr.Slider(label="Steps", minimum=1, maximum=100, value=12, step=1, elem_id="steps_input")
|
1148 |
+
cfg = gr.Slider(label="CFG", minimum=1, maximum=32, value=1.7, step=0.01, elem_id="cfg_input")
|
1149 |
+
gsc = gr.Slider(label="Distilled CFG", minimum=1, maximum=32, value=4.0, step=0.01, elem_id="distilled_cfg_input")
|
1150 |
+
rsc = gr.Slider(label="CFG Re-Scale", minimum=0, maximum=1, value=0.5, step=0.01, elem_id="cfg_rescale_input")
|
1151 |
+
kee = gr.Slider(label="GPU Keep (GB)", minimum=6, maximum=free_mem, value=6.5, step=0.1, elem_id="gpu_keep_input")
|
1152 |
+
crf = gr.Slider(label="MP4 CRF", minimum=0, maximum=100, value=20, step=1, elem_id="mp4_crf_input")
|
1153 |
+
tea = gr.Checkbox(label="Use TeaCache", value=True, elem_id="use_teacache_input")
|
1154 |
+
generate_button.click(
|
1155 |
+
fn=process,
|
1156 |
+
inputs=[img_in, prm, npr, se, sec, win, stp, cfg, gsc, rsc, kee, tea, crf, disable_prompt_mods, link_steps_window],
|
1157 |
+
outputs=[vid, pv, log_md, bar, generate_button, stop_button],
|
1158 |
+
)
|
1159 |
+
stop_button.click(fn=end_process)
|
1160 |
+
gr.Button("Update Progress").click(fn=lambda: get_progress(), outputs=[log_md, bar])
|
1161 |
+
|
1162 |
+
with gr.TabItem("πΌοΈ Image Gallery"):
|
1163 |
+
with gr.Row(elem_classes="media-container"):
|
1164 |
+
with gr.Column(scale=3):
|
1165 |
+
image_preview = gr.Image(
|
1166 |
+
label="Viewer", value=(list_images()[0] if list_images() else None),
|
1167 |
+
interactive=False, elem_classes="media-preview",
|
1168 |
+
)
|
1169 |
+
with gr.Column(elem_classes="control-box"):
|
1170 |
+
image_dropdown = gr.Dropdown(
|
1171 |
+
choices=[os.path.basename(i) for i in list_images()],
|
1172 |
+
value=(os.path.basename(list_images()[0]) if list_images() else None),
|
1173 |
+
label="Select", elem_classes="styled-dropdown",
|
1174 |
+
)
|
1175 |
+
with gr.Row(elem_classes="control-grid"):
|
1176 |
+
load_btn = gr.Button("Load", elem_classes="load-button")
|
1177 |
+
next_btn = gr.Button("Next", elem_classes="load-button")
|
1178 |
+
with gr.Row(elem_classes="control-grid"):
|
1179 |
+
refresh_btn = gr.Button("Refresh")
|
1180 |
+
delete_btn = gr.Button("Delete", elem_classes="orange-button")
|
1181 |
+
image_gallery = gr.Gallery(
|
1182 |
+
value=list_images(), label="Thumbnails", columns=6, height=360,
|
1183 |
+
allow_preview=False, type="filepath", elem_classes="image-gallery",
|
1184 |
+
)
|
1185 |
+
load_btn.click(load_image, [image_dropdown], [image_preview, image_dropdown])
|
1186 |
+
next_btn.click(next_image_and_load, [image_dropdown], [image_preview, image_dropdown])
|
1187 |
+
refresh_btn.click(
|
1188 |
+
lambda: (
|
1189 |
+
gr.update(choices=[os.path.basename(i) for i in list_images()], value=os.path.basename(list_images()[0]) if list_images() else None),
|
1190 |
+
gr.update(value=list_images()[0] if list_images() else None),
|
1191 |
+
gr.update(value=list_images()),
|
1192 |
+
),
|
1193 |
+
[], [image_dropdown, image_preview, image_gallery],
|
1194 |
+
)
|
1195 |
+
delete_btn.click(
|
1196 |
+
lambda sel: (
|
1197 |
+
os.remove(os.path.join(VIDEO_IMG_DIR, sel)) if sel and os.path.exists(os.path.join(VIDEO_IMG_DIR, sel)) else None
|
1198 |
+
) or load_image(""),
|
1199 |
+
[image_dropdown], [image_preview, image_dropdown],
|
1200 |
+
)
|
1201 |
+
image_gallery.select(gallery_image_select, [], [image_preview, image_dropdown])
|
1202 |
+
|
1203 |
+
with gr.TabItem("π¬ Video Gallery"):
|
1204 |
+
with gr.Row(elem_classes="media-container"):
|
1205 |
+
with gr.Column(scale=3):
|
1206 |
+
video_preview = gr.Video(
|
1207 |
+
label="Viewer", value=(list_videos()[0] if list_videos() else None),
|
1208 |
+
autoplay=True, loop=True, interactive=False, elem_classes="media-preview",
|
1209 |
+
)
|
1210 |
+
with gr.Column(elem_classes="control-box"):
|
1211 |
+
video_dropdown = gr.Dropdown(
|
1212 |
+
choices=[os.path.basename(v) for v in list_videos()],
|
1213 |
+
value=(os.path.basename(list_videos()[0]) if list_videos() else None),
|
1214 |
+
label="Select", elem_classes="styled-dropdown",
|
1215 |
+
)
|
1216 |
+
with gr.Row(elem_classes="control-grid"):
|
1217 |
+
load_vbtn = gr.Button("Load", elem_classes="load-button")
|
1218 |
+
next_vbtn = gr.Button("Next", elem_classes="load-button")
|
1219 |
+
with gr.Row(elem_classes="control-grid"):
|
1220 |
+
refresh_v = gr.Button("Refresh")
|
1221 |
+
delete_v = gr.Button("Delete", elem_classes="orange-button")
|
1222 |
+
video_gallery = gr.Gallery(
|
1223 |
+
value=list_videos(), label="Thumbnails", columns=6, height=360,
|
1224 |
+
allow_preview=False, type="filepath", elem_classes="video-gallery",
|
1225 |
+
)
|
1226 |
+
load_vbtn.click(load_video, [video_dropdown], [video_preview, video_dropdown])
|
1227 |
+
next_vbtn.click(next_video_and_load, [video_dropdown], [video_preview, video_dropdown])
|
1228 |
+
refresh_v.click(
|
1229 |
+
lambda: (
|
1230 |
+
gr.update(choices=[os.path.basename(v) for v in list_videos()], value=os.path.basename(list_videos()[0]) if list_videos() else None),
|
1231 |
+
gr.update(value=list_videos()[0] if list_videos() else None),
|
1232 |
+
gr.update(value=list_videos()),
|
1233 |
+
),
|
1234 |
+
[], [video_dropdown, video_preview, video_gallery],
|
1235 |
+
)
|
1236 |
+
delete_v.click(
|
1237 |
+
lambda sel: (
|
1238 |
+
os.remove(os.path.join(VIDEO_OUTPUT_DIR, sel)) if sel and os.path.exists(os.path.join(VIDEO_OUTPUT_DIR, sel)) else None
|
1239 |
+
) or load_video(""),
|
1240 |
+
[video_dropdown], [video_preview, video_dropdown],
|
1241 |
+
)
|
1242 |
+
video_gallery.select(gallery_video_select, [], [video_preview, video_dropdown])
|
1243 |
+
|
1244 |
+
with gr.TabItem("π» About"):
|
1245 |
+
gr.Markdown("## GhostPack F1 Pro")
|
1246 |
+
with gr.Row():
|
1247 |
+
with gr.Column():
|
1248 |
+
gr.Markdown("**π οΈ Description**\nImage-to-Video toolkit powered by HunyuanVideo & FramePack-F1")
|
1249 |
+
with gr.Column():
|
1250 |
+
gr.Markdown(f"**π¦ Version**\n{VERSION}")
|
1251 |
+
with gr.Column():
|
1252 |
+
gr.Markdown("**βοΈ Author**\nGhostAI")
|
1253 |
+
with gr.Column():
|
1254 |
+
gr.Markdown("**π Repo**\nhttps://huggingface.co/spaces/ghostai1/GhostPack")
|
1255 |
+
|
1256 |
+
with gr.TabItem("βοΈ Settings"):
|
1257 |
+
ct = gr.Button("Clear Temp", elem_classes="big-setting-button")
|
1258 |
+
ctmsg = gr.Markdown("")
|
1259 |
+
co = gr.Button("Clear Old", elem_classes="big-setting-button")
|
1260 |
+
comsg = gr.Markdown("")
|
1261 |
+
ci = gr.Button("Clear Images", elem_classes="big-setting-button")
|
1262 |
+
cimg = gr.Markdown("")
|
1263 |
+
cv = gr.Button("Clear Videos", elem_classes="big-setting-button")
|
1264 |
+
cvid = gr.Markdown("")
|
1265 |
+
ct.click(clear_temp_videos, [], ctmsg)
|
1266 |
+
co.click(clear_old_files, [], comsg)
|
1267 |
+
ci.click(clear_images, [], cimg)
|
1268 |
+
cv.click(clear_videos, [], cvid)
|
1269 |
+
|
1270 |
+
with gr.TabItem("π οΈ Install"):
|
1271 |
+
xs = gr.Textbox(value=status_xformers(), interactive=False, label="xformers")
|
1272 |
+
bx = gr.Button("Install xformers", elem_classes="big-setting-button")
|
1273 |
+
ss = gr.Textbox(value=status_sage(), interactive=False, label="sage-attn")
|
1274 |
+
bs = gr.Button("Install sage-attn", elem_classes="big-setting-button")
|
1275 |
+
fs = gr.Textbox(value=status_flash(), interactive=False, label="flash-attn")
|
1276 |
+
bf = gr.Button("Install flash-attn", elem_classes="big-setting-button")
|
1277 |
+
cs = gr.Textbox(value=status_colorama(), interactive=False, label="colorama")
|
1278 |
+
bc = gr.Button("Install colorama", elem_classes="big-setting-button")
|
1279 |
+
bx.click(install_xformers, [], xs)
|
1280 |
+
bs.click(install_sage_attn, [], ss)
|
1281 |
+
bf.click(install_flash_attn, [], fs)
|
1282 |
+
bc.click(install_colorama, [], cs)
|
1283 |
+
|
1284 |
+
with gr.TabItem("π Logs"):
|
1285 |
+
logs = gr.Textbox(lines=20, interactive=False, label="Install Logs")
|
1286 |
+
rl = gr.Button("Refresh", elem_classes="big-setting-button")
|
1287 |
+
cl = gr.Button("Clear", elem_classes="big-setting-button")
|
1288 |
+
rl.click(refresh_logs, [], logs)
|
1289 |
+
cl.click(clear_logs, [], logs)
|
1290 |
+
|
1291 |
+
gr.HTML(
|
1292 |
+
"""
|
1293 |
+
<script>
|
1294 |
+
document.querySelectorAll('.video-gallery video').forEach(v => {
|
1295 |
+
v.addEventListener('loadedmetadata', () => {
|
1296 |
+
if (v.duration > 2) v.currentTime = 2;
|
1297 |
+
});
|
1298 |
+
});
|
1299 |
+
</script>
|
1300 |
+
"""
|
1301 |
+
)
|
1302 |
+
|
1303 |
+
def update_prompt(prompt, camera_action):
|
1304 |
+
camera_actions = [
|
1305 |
+
"static camera", "slight camera orbit left", "slight camera orbit right",
|
1306 |
+
"slight camera orbit up", "slight camera orbit down", "top-down view",
|
1307 |
+
"slight camera zoom in", "slight camera zoom out",
|
1308 |
+
]
|
1309 |
+
for action in camera_actions:
|
1310 |
+
prompt = re.sub(rf",\s*{re.escape(action)}\b", "", prompt, flags=re.IGNORECASE).strip()
|
1311 |
+
if camera_action and camera_action != "None":
|
1312 |
+
camera_phrase = f", {camera_action.lower()}"
|
1313 |
+
if len(prompt.split()) + len(camera_phrase.split()) <= 50:
|
1314 |
+
return prompt + camera_phrase
|
1315 |
+
else:
|
1316 |
+
logger.warning(f"Prompt exceeds 50 words after adding camera action: {prompt}")
|
1317 |
+
print(f"{yellow(f'API: Warning: Prompt exceeds 50 words with camera action')}")
|
1318 |
+
return prompt
|
1319 |
+
|
1320 |
+
def get_progress():
|
1321 |
+
return f"Status: {job_status.get('latest', {'status': 'idle'})['status']}\nProgress: {job_status.get('latest', {'progress': 0.0})['progress']:.1f}%\nLast Render Time: {job_status.get('latest', {'render_time': 0})['render_time']:.1f}s"
|
1322 |
+
|
1323 |
+
# Check for port conflicts
|
1324 |
+
if is_port_in_use(args.port):
|
1325 |
+
logger.error(f"Port {args.port} is already in use")
|
1326 |
+
print(f"{red(f'Error: Port {args.port} is already in use. Please stop other instances or change ports.')}")
|
1327 |
+
sys.exit(1)
|
1328 |
+
|
1329 |
+
# Run FastAPI and optional Gradio
|
1330 |
+
def run_api():
|
1331 |
+
try:
|
1332 |
+
logger.info(f"Starting FastAPI on {args.server}:{args.port}")
|
1333 |
+
print(f"{green(f'Starting FastAPI on {args.server}:{args.port}')}")
|
1334 |
+
uvicorn.run(app, host=args.server, port=args.port)
|
1335 |
+
except Exception as e:
|
1336 |
+
logger.error(f"Failed to start FastAPI: {e}", exc_info=True)
|
1337 |
+
print(f"{red(f'Error: Failed to start FastAPI: {e}')}")
|
1338 |
+
sys.exit(1)
|
1339 |
+
|
1340 |
+
if __name__ == "__main__":
|
1341 |
+
try:
|
1342 |
+
logger.info(f"Starting GhostPack F1 Pro Server version {VERSION}")
|
1343 |
+
print(f"Starting GhostPack F1 Pro Server version {VERSION}")
|
1344 |
+
api_thread = Thread(target=run_api)
|
1345 |
+
api_thread.daemon = True
|
1346 |
+
api_thread.start()
|
1347 |
+
time.sleep(5)
|
1348 |
+
try:
|
1349 |
+
response = requests.get(f"http://{args.server}:{args.port}/health", timeout=10)
|
1350 |
+
if response.status_code != 200:
|
1351 |
+
raise RuntimeError("FastAPI health check failed")
|
1352 |
+
logger.info("FastAPI health check passed")
|
1353 |
+
print(f"{green('FastAPI health check passed')}")
|
1354 |
+
except Exception as e:
|
1355 |
+
logger.error(f"FastAPI not ready: {e}")
|
1356 |
+
print(f"{red(f'Error: FastAPI not ready: {e}')}")
|
1357 |
+
sys.exit(1)
|
1358 |
+
|
1359 |
+
if args.gradio:
|
1360 |
+
logger.info(f"Starting Gradio UI on {args.server}:7860")
|
1361 |
+
print(f"{green(f'Starting Gradio UI on {args.server}:7860')}")
|
1362 |
+
server = blk.launch(
|
1363 |
+
server_name=args.server,
|
1364 |
+
server_port=7860,
|
1365 |
+
share=args.share,
|
1366 |
+
inbrowser=args.inbrowser,
|
1367 |
+
prevent_thread_lock=True,
|
1368 |
+
allowed_paths=["/"]
|
1369 |
+
)
|
1370 |
+
if args.share and server.share_url:
|
1371 |
+
logger.info(f"Public Gradio URL: {server.share_url}")
|
1372 |
+
print(f"{yellow(f'Public Gradio URL: {server.share_url}')}")
|
1373 |
+
logger.info(f"Gradio UI running on http://{args.server}:7860")
|
1374 |
+
print(f"{green(f'Gradio UI running on http://{args.server}:7860')}")
|
1375 |
+
while True:
|
1376 |
+
time.sleep(1)
|
1377 |
+
except KeyboardInterrupt:
|
1378 |
+
logger.info("Shutting down gracefully")
|
1379 |
+
print(f"{green('Shutting down gracefully')}")
|
1380 |
+
sys.exit(0)
|