thankfulcarp commited on
Commit
7ee7e08
Β·
1 Parent(s): d58eafe

lset inclusion

Browse files
Files changed (1) hide show
  1. app.py +61 -38
app.py CHANGED
@@ -64,29 +64,29 @@ except Exception as e:
64
  traceback.print_exc()
65
 
66
  # --- LoRA Discovery ---
67
- def get_available_loras(repo_id, subfolder):
68
  """
69
- Fetches the list of available LoRA files from a Hugging Face Hub repo subfolder.
70
- This version is compatible with older huggingface_hub libraries that don't support the 'subfolder' argument.
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 .safetensors files within the specified subfolder.
77
  subfolder_path = f"{subfolder}/"
78
- safetensors_files = [
79
- f.split('/')[-1]
80
  for f in all_files
81
- if f.startswith(subfolder_path) and f.endswith('.safetensors')
82
  ]
83
- print(f"βœ… Discovered {len(safetensors_files)} LoRAs in {repo_id}/{subfolder}")
84
- return ["None"] + sorted(safetensors_files)
85
  except Exception as e:
86
- print(f"⚠️ Warning: Could not fetch LoRAs from {repo_id}. LoRA selection will be disabled. Error: {e}")
87
  return ["None"]
88
 
89
- available_i2v_loras = get_available_loras(I2V_LORA_REPO_ID, I2V_LORA_SUBFOLDER) if i2v_pipe else ["None"]
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(lora_name, current_prompt):
128
  """
129
- When a LoRA is selected, this function tries to find a corresponding .lset file,
130
  parses it, and appends the generated prompt to the current prompt.
131
  """
132
- if not lora_name or lora_name == "None":
133
- return gr.update() # No LoRA selected, do not change the prompt.
134
 
135
  try:
136
- # Construct the .lset filename from the .safetensors filename
137
- lset_filename = os.path.splitext(lora_name)[0] + ".lset"
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 is expected if a .lset file doesn't exist for the selected LoRA.
160
- print(f"Info: Could not process .lset for '{lora_name}'. Reason: {e}")
161
- gr.Info(f"ℹ️ No prompt preset found for '{lora_name}'.")
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
- lora_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,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
- # Dynamically load the selected LoRA
270
- if lora_name and lora_name != "None":
271
- print(f"πŸš€ Loading LoRA: {lora_name} with weight {lora_weight}")
272
- i2v_pipe.load_lora_weights(
273
- I2V_LORA_REPO_ID,
274
- weight_name=lora_name,
275
- adapter_name=adapter_name,
276
- subfolder=I2V_LORA_SUBFOLDER
277
- )
278
- i2v_pipe.set_adapters([adapter_name], adapter_weights=[float(lora_weight)])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 lora_name and lora_name != "None" and hasattr(i2v_pipe, "unload_lora_weights"):
295
- print(f"🧹 Unloading LoRA: {lora_name}")
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
- i2v_lora_name = gr.Dropdown(label="🎨 LoRA Style", choices=available_i2v_loras, value="None", info="Dynamically loaded from Hugging Face.", interactive=len(available_i2v_loras) > 1)
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
- i2v_lora_name.change(
359
  fn=handle_lora_selection_change,
360
- inputs=[i2v_lora_name, i2v_prompt],
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, i2v_lora_name, i2v_lora_weight],
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(