Spaces:
Running
on
Zero
Running
on
Zero
Commit
Β·
7ee7e08
1
Parent(s):
d58eafe
lset inclusion
Browse files
app.py
CHANGED
@@ -64,29 +64,29 @@ except Exception as e:
|
|
64 |
traceback.print_exc()
|
65 |
|
66 |
# --- LoRA Discovery ---
|
67 |
-
def
|
68 |
"""
|
69 |
-
Fetches the list of available LoRA
|
70 |
-
This
|
71 |
"""
|
72 |
try:
|
73 |
# Fetch all files from the repo to maintain compatibility with older library versions.
|
74 |
all_files = list_repo_files(repo_id=repo_id, repo_type='model')
|
75 |
|
76 |
-
# Manually filter for .
|
77 |
subfolder_path = f"{subfolder}/"
|
78 |
-
|
79 |
-
f.split('/')[-1]
|
80 |
for f in all_files
|
81 |
-
if f.startswith(subfolder_path) and f.endswith('.
|
82 |
]
|
83 |
-
print(f"β
Discovered {len(
|
84 |
-
return ["None"] + sorted(
|
85 |
except Exception as e:
|
86 |
-
print(f"β οΈ Warning: Could not fetch
|
87 |
return ["None"]
|
88 |
|
89 |
-
|
90 |
|
91 |
|
92 |
# --- Constants and Configuration ---
|
@@ -124,17 +124,17 @@ def parse_lset_prompt(lset_prompt):
|
|
124 |
|
125 |
return resolved_prompt
|
126 |
|
127 |
-
def handle_lora_selection_change(
|
128 |
"""
|
129 |
-
When a
|
130 |
parses it, and appends the generated prompt to the current prompt.
|
131 |
"""
|
132 |
-
if not
|
133 |
-
return gr.update() # No
|
134 |
|
135 |
try:
|
136 |
-
#
|
137 |
-
lset_filename =
|
138 |
|
139 |
# Download the .lset file from the same subfolder as the LoRA
|
140 |
lset_path = hf_hub_download(
|
@@ -156,9 +156,9 @@ def handle_lora_selection_change(lora_name, current_prompt):
|
|
156 |
gr.Info(f"β
Appended prompt from '{lset_filename}'")
|
157 |
return gr.update(value=new_prompt)
|
158 |
except Exception as e:
|
159 |
-
# This
|
160 |
-
print(f"Info: Could not process .lset for '{
|
161 |
-
gr.Info(f"βΉοΈ
|
162 |
return gr.update()
|
163 |
|
164 |
# --- Helper Functions ---
|
@@ -244,8 +244,8 @@ def get_t2v_duration(steps, duration_seconds):
|
|
244 |
@spaces.GPU(duration_from_args=get_i2v_duration)
|
245 |
def generate_i2v_video(input_image, prompt, height, width,
|
246 |
negative_prompt, duration_seconds,
|
247 |
-
guidance_scale, steps, seed, randomize_seed,
|
248 |
-
|
249 |
progress=gr.Progress(track_tqdm=True)):
|
250 |
"""Generates a video from an initial image and a prompt."""
|
251 |
if input_image is None:
|
@@ -264,18 +264,41 @@ def generate_i2v_video(input_image, prompt, height, width,
|
|
264 |
resized_image = input_image.resize((target_w, target_h))
|
265 |
enhanced_prompt = f"{prompt}, cinematic quality, smooth motion, detailed animation, dynamic lighting"
|
266 |
|
|
|
267 |
adapter_name = "i2v_lora"
|
268 |
try:
|
269 |
-
#
|
270 |
-
if
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
|
280 |
with torch.inference_mode():
|
281 |
output_frames_list = i2v_pipe(
|
@@ -291,8 +314,8 @@ def generate_i2v_video(input_image, prompt, height, width,
|
|
291 |
).frames[0]
|
292 |
finally:
|
293 |
# Unload the LoRA to ensure a clean state for the next run
|
294 |
-
if
|
295 |
-
print(f"π§Ή Unloading LoRA: {
|
296 |
i2v_pipe.unload_lora_weights()
|
297 |
# Clear GPU cache to free up memory for the next run
|
298 |
if torch.cuda.is_available():
|
@@ -336,7 +359,7 @@ with gr.Blocks() as demo:
|
|
336 |
i2v_neg_prompt = gr.Textbox(label="β Negative Prompt", value=default_negative_prompt, lines=4)
|
337 |
i2v_seed = gr.Slider(label="π² Seed", minimum=0, maximum=MAX_SEED, step=1, value=42, interactive=True)
|
338 |
i2v_rand_seed = gr.Checkbox(label="π Randomize seed", value=True, interactive=True)
|
339 |
-
|
340 |
i2v_lora_weight = gr.Slider(label="πͺ LoRA Weight", minimum=0.0, maximum=2.0, step=0.1, value=0.8, interactive=True)
|
341 |
with gr.Row():
|
342 |
i2v_height = gr.Slider(minimum=SLIDER_MIN_H, maximum=SLIDER_MAX_H, step=MOD_VALUE, value=DEFAULT_H_SLIDER_VALUE, label=f"π Height ({MOD_VALUE}px steps)")
|
@@ -355,9 +378,9 @@ with gr.Blocks() as demo:
|
|
355 |
|
356 |
# --- Event Handlers ---
|
357 |
# I2V Handlers
|
358 |
-
|
359 |
fn=handle_lora_selection_change,
|
360 |
-
inputs=[
|
361 |
outputs=[i2v_prompt]
|
362 |
)
|
363 |
i2v_input_image.upload(
|
@@ -372,7 +395,7 @@ with gr.Blocks() as demo:
|
|
372 |
)
|
373 |
i2v_generate_btn.click(
|
374 |
fn=generate_i2v_video,
|
375 |
-
inputs=[i2v_input_image, i2v_prompt, i2v_height, i2v_width, i2v_neg_prompt, i2v_duration, i2v_guidance, i2v_steps, i2v_seed, i2v_rand_seed,
|
376 |
outputs=[i2v_output_video, i2v_seed, i2v_download]
|
377 |
)
|
378 |
i2v_height.release(
|
|
|
64 |
traceback.print_exc()
|
65 |
|
66 |
# --- LoRA Discovery ---
|
67 |
+
def get_available_presets(repo_id, subfolder):
|
68 |
"""
|
69 |
+
Fetches the list of available LoRA presets by looking for .lset files.
|
70 |
+
This is more robust as it ensures a preset and prompt info exists.
|
71 |
"""
|
72 |
try:
|
73 |
# Fetch all files from the repo to maintain compatibility with older library versions.
|
74 |
all_files = list_repo_files(repo_id=repo_id, repo_type='model')
|
75 |
|
76 |
+
# Manually filter for .lset files and get their names without the extension.
|
77 |
subfolder_path = f"{subfolder}/"
|
78 |
+
lset_files = [
|
79 |
+
os.path.splitext(f.split('/')[-1])[0] # Get filename without extension
|
80 |
for f in all_files
|
81 |
+
if f.startswith(subfolder_path) and f.endswith('.lset')
|
82 |
]
|
83 |
+
print(f"β
Discovered {len(lset_files)} LoRA presets in {repo_id}/{subfolder}")
|
84 |
+
return ["None"] + sorted(lset_files)
|
85 |
except Exception as e:
|
86 |
+
print(f"β οΈ Warning: Could not fetch LoRA presets from {repo_id}. LoRA selection will be disabled. Error: {e}")
|
87 |
return ["None"]
|
88 |
|
89 |
+
available_i2v_presets = get_available_presets(I2V_LORA_REPO_ID, I2V_LORA_SUBFOLDER) if i2v_pipe else ["None"]
|
90 |
|
91 |
|
92 |
# --- Constants and Configuration ---
|
|
|
124 |
|
125 |
return resolved_prompt
|
126 |
|
127 |
+
def handle_lora_selection_change(preset_name, current_prompt):
|
128 |
"""
|
129 |
+
When a preset is selected, this function finds the corresponding .lset file,
|
130 |
parses it, and appends the generated prompt to the current prompt.
|
131 |
"""
|
132 |
+
if not preset_name or preset_name == "None":
|
133 |
+
return gr.update() # No preset selected, do not change the prompt.
|
134 |
|
135 |
try:
|
136 |
+
# The preset_name is the filename without extension
|
137 |
+
lset_filename = f"{preset_name}.lset"
|
138 |
|
139 |
# Download the .lset file from the same subfolder as the LoRA
|
140 |
lset_path = hf_hub_download(
|
|
|
156 |
gr.Info(f"β
Appended prompt from '{lset_filename}'")
|
157 |
return gr.update(value=new_prompt)
|
158 |
except Exception as e:
|
159 |
+
# This should be less common now, but good to keep for safety.
|
160 |
+
print(f"Info: Could not process .lset for '{preset_name}'. Reason: {e}")
|
161 |
+
gr.Info(f"βΉοΈ Error processing preset '{preset_name}'.")
|
162 |
return gr.update()
|
163 |
|
164 |
# --- Helper Functions ---
|
|
|
244 |
@spaces.GPU(duration_from_args=get_i2v_duration)
|
245 |
def generate_i2v_video(input_image, prompt, height, width,
|
246 |
negative_prompt, duration_seconds,
|
247 |
+
guidance_scale, steps, seed, randomize_seed,
|
248 |
+
preset_name, lora_weight,
|
249 |
progress=gr.Progress(track_tqdm=True)):
|
250 |
"""Generates a video from an initial image and a prompt."""
|
251 |
if input_image is None:
|
|
|
264 |
resized_image = input_image.resize((target_w, target_h))
|
265 |
enhanced_prompt = f"{prompt}, cinematic quality, smooth motion, detailed animation, dynamic lighting"
|
266 |
|
267 |
+
lora_filename = None # Will be extracted from the .lset file
|
268 |
adapter_name = "i2v_lora"
|
269 |
try:
|
270 |
+
# If a preset is selected, load the corresponding LoRA
|
271 |
+
if preset_name and preset_name != "None":
|
272 |
+
lset_filename = f"{preset_name}.lset"
|
273 |
+
print(f"π Processing preset: {preset_name}")
|
274 |
+
try:
|
275 |
+
lset_path = hf_hub_download(
|
276 |
+
repo_id=I2V_LORA_REPO_ID,
|
277 |
+
filename=lset_filename,
|
278 |
+
subfolder=I2V_LORA_SUBFOLDER,
|
279 |
+
repo_type='model'
|
280 |
+
)
|
281 |
+
with open(lset_path, 'r', encoding='utf-8') as f:
|
282 |
+
lset_data = json.load(f)
|
283 |
+
|
284 |
+
# Extract the LoRA filename from the .lset file
|
285 |
+
loras_list = lset_data.get("loras")
|
286 |
+
if not loras_list or not isinstance(loras_list, list) or len(loras_list) == 0:
|
287 |
+
raise gr.Error(f"Preset file '{lset_filename}' is invalid or does not specify a LoRA file.")
|
288 |
+
|
289 |
+
lora_filename = loras_list[0] # Use the first LoRA in the list
|
290 |
+
print(f" - Found LoRA file: {lora_filename}")
|
291 |
+
|
292 |
+
i2v_pipe.load_lora_weights(
|
293 |
+
I2V_LORA_REPO_ID,
|
294 |
+
weight_name=lora_filename,
|
295 |
+
adapter_name=adapter_name,
|
296 |
+
subfolder=I2V_LORA_SUBFOLDER
|
297 |
+
)
|
298 |
+
i2v_pipe.set_adapters([adapter_name], adapter_weights=[float(lora_weight)])
|
299 |
+
print(f" - LoRA '{lora_filename}' loaded successfully with weight {lora_weight}.")
|
300 |
+
except Exception as e:
|
301 |
+
raise gr.Error(f"Failed to load LoRA for preset '{preset_name}'. Reason: {e}")
|
302 |
|
303 |
with torch.inference_mode():
|
304 |
output_frames_list = i2v_pipe(
|
|
|
314 |
).frames[0]
|
315 |
finally:
|
316 |
# Unload the LoRA to ensure a clean state for the next run
|
317 |
+
if lora_filename and hasattr(i2v_pipe, "unload_lora_weights"):
|
318 |
+
print(f"π§Ή Unloading LoRA: {lora_filename}")
|
319 |
i2v_pipe.unload_lora_weights()
|
320 |
# Clear GPU cache to free up memory for the next run
|
321 |
if torch.cuda.is_available():
|
|
|
359 |
i2v_neg_prompt = gr.Textbox(label="β Negative Prompt", value=default_negative_prompt, lines=4)
|
360 |
i2v_seed = gr.Slider(label="π² Seed", minimum=0, maximum=MAX_SEED, step=1, value=42, interactive=True)
|
361 |
i2v_rand_seed = gr.Checkbox(label="π Randomize seed", value=True, interactive=True)
|
362 |
+
i2v_preset_name = gr.Dropdown(label="π¨ LoRA Preset", choices=available_i2v_presets, value="None", info="Select a preset to apply a LoRA and a suggested prompt.", interactive=len(available_i2v_presets) > 1)
|
363 |
i2v_lora_weight = gr.Slider(label="πͺ LoRA Weight", minimum=0.0, maximum=2.0, step=0.1, value=0.8, interactive=True)
|
364 |
with gr.Row():
|
365 |
i2v_height = gr.Slider(minimum=SLIDER_MIN_H, maximum=SLIDER_MAX_H, step=MOD_VALUE, value=DEFAULT_H_SLIDER_VALUE, label=f"π Height ({MOD_VALUE}px steps)")
|
|
|
378 |
|
379 |
# --- Event Handlers ---
|
380 |
# I2V Handlers
|
381 |
+
i2v_preset_name.change(
|
382 |
fn=handle_lora_selection_change,
|
383 |
+
inputs=[i2v_preset_name, i2v_prompt],
|
384 |
outputs=[i2v_prompt]
|
385 |
)
|
386 |
i2v_input_image.upload(
|
|
|
395 |
)
|
396 |
i2v_generate_btn.click(
|
397 |
fn=generate_i2v_video,
|
398 |
+
inputs=[i2v_input_image, i2v_prompt, i2v_height, i2v_width, i2v_neg_prompt, i2v_duration, i2v_guidance, i2v_steps, i2v_seed, i2v_rand_seed, i2v_preset_name, i2v_lora_weight],
|
399 |
outputs=[i2v_output_video, i2v_seed, i2v_download]
|
400 |
)
|
401 |
i2v_height.release(
|