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(
|