Spaces:
Running on Zero
Running on Zero
File size: 11,078 Bytes
1b93f28 591e9bc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | """
LoRA Loader for WAN 2.2 I2V - multi-repo.
Baseline: lkzd7/WAN2.2_LoraSet_NSFW. Plus curated I2V act/helper LoRAs from
Lythiga/WAN2.2_I2V_Lora, rolechar/rc_wan2.2_i2v_loraset_nsfw_private,
lopi999/Wan2.2-I2V_General-NSFW-LoRA. T2V / lightning / furry / dance / male-char filtered out.
"""
import os
import urllib.parse
import re
from huggingface_hub import hf_hub_download
LORA_REPO = "lkzd7/WAN2.2_LoraSet_NSFW"
HF_TOKEN = os.environ.get("HF_TOKEN") # authenticated downloads (covers private repos)
# Pinned commit hashes (2026-07-19) — protection from breaking upstream changes
PINNED_REVISIONS = {
"lkzd7/WAN2.2_LoraSet_NSFW": "ccfa867ce4b1",
"Lythiga/WAN2.2_I2V_Lora": "67ef542e6faa",
"rolechar/rc_wan2.2_i2v_loraset_nsfw_private": "4855a2f216ac",
"lopi999/Wan2.2-I2V_General-NSFW-LoRA": "aeef17d7fa51",
}
LORA_FILES = [
"Blink_Squatting_Cowgirl_Position_I2V_HIGH.safetensors",
"Blink_Squatting_Cowgirl_Position_I2V_LOW.safetensors",
"PENISLORA_22_i2v_HIGH_e320.safetensors",
"PENISLORA_22_i2v_LOW_e496.safetensors",
"Pornmaster_wan 2.2_14b_I2V_bukkake_v1.4_high_noise.safetensors",
"Pornmaster_wan 2.2_14b_I2V_bukkake_v1.4_low_noise.safetensors",
"W22_Multiscene_Photoshoot_Softcore_i2v_HN.safetensors",
"W22_Multiscene_Photoshoot_Softcore_i2v_LN.safetensors",
"WAN-2.2-I2V-Double-Blowjob-HIGH-v1.safetensors",
"WAN-2.2-I2V-Double-Blowjob-LOW-v1.safetensors",
"WAN-2.2-I2V-HandjobBlowjobCombo-HIGH-v1.safetensors",
"WAN-2.2-I2V-HandjobBlowjobCombo-LOW-v1.safetensors",
"WAN-2.2-I2V-SensualTeasingBlowjob-HIGH-v1.safetensors",
"WAN-2.2-I2V-SensualTeasingBlowjob-LOW-v1.safetensors",
"iGOON_Blink_Blowjob_I2V_HIGH.safetensors",
"iGOON_Blink_Blowjob_I2V_LOW.safetensors",
"iGoon - Blink_Front_Doggystyle_I2V_HIGH.safetensors",
"iGoon - Blink_Front_Doggystyle_I2V_LOW.safetensors",
"iGoon - Blink_Missionary_I2V_HIGH.safetensors",
"iGoon - Blink_Missionary_I2V_LOW v2.safetensors",
"iGoon - Blink_Missionary_I2V_LOW.safetensors",
"iGoon%20-%20Blink_Back_Doggystyle_HIGH.safetensors",
"iGoon%20-%20Blink_Back_Doggystyle_LOW.safetensors",
"iGoon%20-%20Blink_Facial_I2V_HIGH.safetensors",
"iGoon%20-%20Blink_Facial_I2V_LOW.safetensors",
"iGoon_Blink_Missionary_I2V_HIGH v2.safetensors",
"iGoon_Blink_Titjob_I2V_HIGH.safetensors",
"iGoon_Blink_Titjob_I2V_LOW.safetensors",
"lips-bj_high_noise.safetensors",
"lips-bj_low_noise.safetensors",
"mql_casting_sex_doggy_kneel_diagonally_behind_vagina_wan22_i2v_v1_high_noise.safetensors",
"mql_casting_sex_doggy_kneel_diagonally_behind_vagina_wan22_i2v_v1_low_noise.safetensors",
"mql_casting_sex_reverse_cowgirl_lie_front_vagina_wan22_i2v_v1_high_noise.safetensors",
"mql_casting_sex_reverse_cowgirl_lie_front_vagina_wan22_i2v_v1_low_noise.safetensors",
"mql_casting_sex_spoon_wan22_i2v_v1_high_noise.safetensors",
"mql_casting_sex_spoon_wan22_i2v_v1_low_noise.safetensors",
"mql_massage_tits_wan22_i2v_v1_high_noise.safetensors",
"mql_massage_tits_wan22_i2v_v1_low_noise.safetensors",
"mql_panties_aside_wan22_i2v_v1_high_noise.safetensors",
"mql_panties_aside_wan22_i2v_v1_low_noise.safetensors",
"sfbehind_v2.1_high_noise.safetensors",
"sfbehind_v2.1_low_noise.safetensors",
"sid3l3g_transition_v2.0_H.safetensors",
"sid3l3g_transition_v2.0_L.safetensors",
"wan2.2_i2v_high_ulitmate_pussy_asshole.safetensors",
"wan2.2_i2v_low_ulitmate_pussy_asshole.safetensors",
"wan22-mouthfull-140epoc-high-k3nk.safetensors",
"wan22-mouthfull-152epoc-low-k3nk.safetensors",
]
# group -> {"HIGH": (repo, file) | None, "LOW": (repo, file) | None}
LORA_PAIRS = {}
for f in LORA_FILES:
name = urllib.parse.unquote(f).replace(".safetensors", "")
is_high = bool(re.search(r'(high|HN|_H\b)', name, re.IGNORECASE))
is_low = bool(re.search(r'(low|LN|_L\b)', name, re.IGNORECASE))
group = re.sub(r'[\s_-]*(high|low|noise|HN|LN)([\s_-]*noise)?[\s_-]*(v?\d+(\.\d+)?)?\s*$', '', name, flags=re.IGNORECASE).strip()
group = re.sub(r'[\s_]+$', '', group)
LORA_PAIRS.setdefault(group, {"HIGH": None, "LOW": None})
if is_high:
LORA_PAIRS[group]["HIGH"] = (LORA_REPO, f)
elif is_low:
LORA_PAIRS[group]["LOW"] = (LORA_REPO, f)
L = "Lythiga/WAN2.2_I2V_Lora"
R = "rolechar/rc_wan2.2_i2v_loraset_nsfw_private"
P = "lopi999/Wan2.2-I2V_General-NSFW-LoRA"
# curated extra act/helper LoRAs (label -> (repo, high_file, low_file|None))
EXTRA = {
# --- deepthroat ---
"Ultimate Deepthroat": (L, "wan22-ultimatedeepthroat-i2v-102epoc-high-k3nk.safetensors", "wan22-ultimatedeepthroat-I2V-101epoc-low-k3nk.safetensors"),
"BBC Deepthroat": (L, "wan22-bbcdeepthroat-115epoc-high-k3nk.safetensors", "wan22-bbcdeepthroat-155epoc-low-720-k3nk.safetensors"),
"Throat V2": (L, "Wan22_ThroatV2_High.safetensors", "Wan22_ThroatV2_Low.safetensors"),
"JFJ Deepthroat": (L, "jfj-deepthroat-W22-I2V-HN.safetensors", None),
# --- blowjob extra ---
"BBC Blowjob Extreme": (L, "BBC Blowjobs Extreme_high_noise.safetensors", "BBC Blowjobs Extreme_low_noise.safetensors"),
"POV Blowjob": (L, "Pov_Blowjob_Wan2.2_high_I2V_v1.0.safetensors", None),
# --- cunnilingus ---
"Cunnilingus": (L, "wan22-cunilingus-I2V-106epoc-high.safetensors", "wan22-cunilingus-I2V-72epoc-low.safetensors"),
# --- cowgirl ---
"Reverse Cowgirl v2": (L, "wan22.r3v3rs3_c0wg1rl-14b-High-i2v_e70.safetensors", "wan22.r3v3rs3_c0wg1rl-14b-Low-i2v_e70.safetensors"),
# --- doggy / from behind ---
"Doggy Front View v2": (L, "doggy_style_sex_front_view_Wan2.2_I2V_high_v1.0.safetensors", "doggy_style_sex_front_view_Wan2.2_I2V_Low_v1.0.safetensors"),
"Fucked From Behind": (L, "derpfuckedfrombehind_000002125_high_noise.safetensors", "derpfuckedfrombehind_000002125_low_noise.safetensors"),
"Prone Bone": (L, "Pronebone_high_noise.safetensors", "Pronebone_low_noise.safetensors"),
"Reverse Suspended Congress": (L, "reverse_suspended_congress_I2V_high.safetensors", "reverse_suspended_congress_I2V_low.safetensors"),
# --- missionary ---
"POV Missionary": (L, "wan2.2_i2v_highnoise_pov_missionary_v1.0.safetensors", "wan2.2_i2v_lownoise_pov_missionary_v1.0.safetensors"),
"Mating Press": (L, "mating_press_high.safetensors", "mating_press_low.safetensors"),
# --- standing ---
"Standing Upright Sex": (L, "Upright_sex_Wan2.2_14B_TI2V_HIGH_v1.0.safetensors", "Upright_sex_Wan2.2_14B_TI2V_LOW_v1.0.safetensors"),
# --- anal / ass ---
"Oral Insertion": (L, "wan2.2-i2v-high-oral-insertion-v1.0.safetensors", "wan2.2-i2v-low-oral-insertion-v1.0.safetensors"),
# --- handjob / balls ---
"Handjob": (L, "WAN-2.2-I2V-Handjob-HIGH-v1.safetensors", "WAN-2.2-I2V-Handjob-LOW-v1.safetensors"),
"Balls Sucking": (L, "WAN-2.2-I2V_Balls_sucking_High_noise.safetensors", "WAN-2.2-I2V_Balls_sucking_Low_noise.safetensors"),
# --- fingering ---
"Fingering": (R, "Sensual_fingering_v1_high_noise.safetensors", "Sensual_fingering_v1_low_noise.safetensors"),
# --- cumshot / creampie ---
"Facesplash Cumshot": (L, "wan22-f4c3spl4sh-100epoc-high-k3nk.safetensors", "wan22-f4c3spl4sh-154epoc-low-k3nk.safetensors"),
"Creampie CRM": (L, "Creampie CRM-FULL-EPOCH-80-HIGH.safetensors", "Creampie CRM-FULL-EPOCH-80-LOW.safetensors"),
"Pornmaster Creampie": (L, "Pornmaster_wan 2.2_14b_I2V_Creampie_v1_high_noise.safetensors", "Pornmaster_wan 2.2_14b_I2V_Creampie_v1_low_noise.safetensors"),
"Cum (generic)": (L, "Wan22_Cum_high_noise_1.V1.safetensors", "Wan22_Cum_low_noise_1.V1.safetensors"),
# --- fetish / expression ---
"French Kiss": (L, "WAN2.2-FrenchKiss_HighNoise.safetensors", "WAN2.2-FrenchKiss_LowNoise.safetensors"),
"Twerking": (L, "Twerking_I2V_high_noise.safetensors", "Twerking_I2V_low_noise.safetensors"),
"Ahegao Drooling": (R, "middlefinger_drooling_ahegao_high.safetensors", "middlefinger_drooling_ahegao_low.safetensors"),
# --- helpers: size / penetration / anatomy / camera / booster ---
"General NSFW Booster": (P, "NSFW-22-H-e8.safetensors", "NSFW-22-L-e8.safetensors"),
"Penetration Insert": (L, "PenInsert_high_noise.safetensors", "PenInsert_low_noise.safetensors"),
"Biggest Cock (size)": (L, "WAN-2.2-I2V_BiggestCock_high_noise_V1.safetensors", "WAN-2.2-I2V_BiggestCock_low_noise_V1.safetensors"),
"Doggy Slider": (L, "I2V_doggyslider_high.safetensors", "I2V_doggyslider_low.safetensors"),
"Pussy Helper": (R, "wan2.2_i2v_ulitmate_pussy_helper_high.safetensors", "wan2.2_i2v_ulitmate_pussy_helper_low.safetensors"),
"FOV Slider (camera)": (L, "wan2.2-i2v-high-sex-fov-slider-v1.0.safetensors", "wan2.2-i2v-low-sex-fov-slider-v1.0.safetensors"),
"Smashcut (camera)": (R, "wan2.2-i2v-sex-smashcut-v1.0-high.safetensors", "wan2.2-i2v-sex-smashcut-v1.0-low.safetensors"),
}
for label, (repo, hi, lo) in EXTRA.items():
LORA_PAIRS.setdefault(label, {"HIGH": None, "LOW": None})
if hi:
LORA_PAIRS[label]["HIGH"] = (repo, hi)
if lo:
LORA_PAIRS[label]["LOW"] = (repo, lo)
def get_lora_choices():
choices = []
for group in sorted(LORA_PAIRS.keys()):
p = LORA_PAIRS[group]
if p["HIGH"] and p["LOW"]:
choices.append(group)
elif p["HIGH"]:
choices.append(f"{group} (HIGH only)")
elif p["LOW"]:
choices.append(f"{group} (LOW only)")
return choices
def download_lora(group_name):
if not group_name:
return None, None
clean_name = re.sub(r'\s*\(HIGH only\)|\s*\(LOW only\)', '', group_name)
if clean_name not in LORA_PAIRS:
return None, None
pair = LORA_PAIRS[clean_name]
high_path, low_path = None, None
if pair["HIGH"]:
repo, fn = pair["HIGH"]
high_path = hf_hub_download(repo, fn, token=HF_TOKEN, revision=PINNED_REVISIONS.get(repo))
if pair["LOW"]:
repo, fn = pair["LOW"]
low_path = hf_hub_download(repo, fn, token=HF_TOKEN, revision=PINNED_REVISIONS.get(repo))
return high_path, low_path
def load_lora_to_pipe(pipe, group_name, adapter_name="lora"):
high_path, low_path = download_lora(group_name)
if high_path and low_path:
pipe.load_lora_weights(high_path, adapter_name=f"{adapter_name}_high")
pipe.load_lora_weights(low_path, adapter_name=f"{adapter_name}_low")
print(f"Loaded LoRA pair: {group_name}")
return True
elif high_path:
pipe.load_lora_weights(high_path, adapter_name=adapter_name)
print(f"Loaded LoRA: {group_name}")
return True
elif low_path:
pipe.load_lora_weights(low_path, adapter_name=adapter_name)
print(f"Loaded LoRA (low): {group_name}")
return True
return False
def unload_lora(pipe):
"""严格卸载动态 LoRA,使调用方能够感知并处理清理失败。
Args:
pipe: 当前全局 Wan 视频推理管线。
Returns:
卸载完成后不返回数据;底层失败会原样抛给调用方。
"""
pipe.unload_lora_weights()
|