From a1f1fb2109ac046a5808817a86e69b69f81330e2 Mon Sep 17 00:00:00 2001
From: jaesybr <144313377+jaesybr@users.noreply.github.com>
Date: Fri, 15 May 2026 00:33:29 -0400
Subject: [PATCH] Update Wan2_1_14B_T2V_GGUF_Free.ipynb
---
Wan2_1_14B_T2V_GGUF_Free.ipynb | 421 +++++++++++++++++----------------
1 file changed, 211 insertions(+), 210 deletions(-)
diff --git a/Wan2_1_14B_T2V_GGUF_Free.ipynb b/Wan2_1_14B_T2V_GGUF_Free.ipynb
index 30238c8..7649b98 100644
--- a/Wan2_1_14B_T2V_GGUF_Free.ipynb
+++ b/Wan2_1_14B_T2V_GGUF_Free.ipynb
@@ -19,7 +19,25 @@
{
"cell_type": "markdown",
"source": [
- "# **Wan2.1-14b Quantized GGUF Models for Text to Video**"
+ "# **Wan2.1-14b GGUF Porn/NSFW Text-to-Video Generator**\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## High-Quality, NSFW-ready, LoRA-Supported T2V Colab\n",
+ "\n",
+ "- This notebook is focused on generating high-quality NSFW and porn videos using the Wan2.1-14b quantized GGUF models with **full LoRA support** out of the box.\n",
+ "- **One or MORE LoRAs** can be loaded and blended for your style! Simply provide names and strengths in the UI below.\n",
+ "- **Stable, memory efficient and easy-to-use** NSFW defaults for dimensions, steps, CFG, and more.\n",
+ "- Full comments and tips on adding further extensions like IPAdapter, ControlNet, etc.\n",
+ "\n",
+ "## Why this notebook?\n",
+ "- Huge: Full LoRA support, batch LoRA loading, any LoRA strength\n",
+ "- Best defaults for NSFW (frames, res, cfg, steps)\n",
+ "- Reduce OOM errors on T4 with aggressive memory clearing\n",
+ "- Optionally randomize seed for variety\n",
+ "- Switch Q5/Q6 with a toggle\n",
+ "- Place for your custom hacks/plugins (see code comments)\n",
+ "---"
],
"metadata": {
"id": "f4p1ysFKMbs_"
@@ -28,10 +46,11 @@
{
"cell_type": "markdown",
"source": [
- "- You can use the free T4 GPU to run this. For faster video generation, use higher GPUs.\n",
- "- Generating a video with 32 frames at 512 by 910 resolution can take up to 28 minutes on the T4 GPU using the Q5 model.\n",
- "- It's possible to generate up to 24 frames at 480 by 832 resolution for free using the Q6 model. The generation takes about 20 minutes.\n",
- "- Set `frames` to 1 to generate an image. You can generate high quality images with 720 by 1280 resolution using either the Q5 or Q6 gguf models. The generation takes less than 7 minutes."
+ "- You can use the free T4 GPU to run this. T4 is slow for large frame counts or high resolution; Q6 is faster/leaner but slightly less quality than Q5.\n",
+ "- **Default video: 8-12 frames at 480x832, reasonable CFG, steps 22.**\n",
+ "- **To avoid OOM, keep frames below 12 at high res; Q6 recommended for 10-16 frames unless on bigger GPU.**\n",
+ "- Set frames=1 for image-only. PNGs are high quality even at 720x1280, but need fewer VRAM.\n",
+ "- LoRAs must be copied to `/content/ComfyUI/models/lora` before (see comments in the code for easy expansion!)."
],
"metadata": {
"id": "EBB00lC6q-DA"
@@ -40,12 +59,14 @@
{
"cell_type": "code",
"source": [
- "# @title Prepare Environment\n",
+ "# @title 🛠️ Environment & Model Download (including LoRA support)\n",
+ "\n",
+ "# --- Base Library Installs ---\n",
"!pip install torch==2.6.0 torchvision==0.21.0\n",
- "%cd /content\n",
+ "!pip install -q torchsde einops diffusers accelerate xformers==0.0.29.post2 av imageio\n",
"\n",
- "!pip install -q torchsde einops diffusers accelerate xformers==0.0.29.post2\n",
- "!pip install av\n",
+ "# --- Download base code + custom nodes ---\n",
+ "%cd /content\n",
"!git clone https://github.com/Isi-dev/ComfyUI\n",
"%cd /content/ComfyUI/custom_nodes\n",
"!git clone https://github.com/Isi-dev/ComfyUI_GGUF.git\n",
@@ -54,50 +75,52 @@
"%cd /content/ComfyUI\n",
"!apt -y install -qq aria2 ffmpeg\n",
"\n",
- "useQ6 = False # @param {\"type\":\"boolean\"}\n",
+ "# --- Q5/Q6 MODEL SWITCH ---\n",
+ "useQ6 = False # @param {\"type\":\"boolean\"} # Set True to use Q6 (Faster, less VRAM, slightly less quality)\n",
"\n",
- "if useQ6:\n",
- " !aria2c --console-log-level=error -c -x 16 -s 16 -k 1M https://huggingface.co/city96/Wan2.1-T2V-14B-gguf/resolve/main/wan2.1-t2v-14b-Q6_K.gguf -d /content/ComfyUI/models/unet -o wan2.1-t2v-14b-Q6_K.gguf\n",
- "else:\n",
- " !aria2c --console-log-level=error -c -x 16 -s 16 -k 1M https://huggingface.co/city96/Wan2.1-T2V-14B-gguf/resolve/main/wan2.1-t2v-14b-Q5_0.gguf -d /content/ComfyUI/models/unet -o wan2.1-t2v-14b-Q5_0.gguf\n",
+ "# Set Model Names (can edit for custom models)\n",
+ "WAN_Q5_MODEL = \"wan2.1-t2v-14b-Q5_0.gguf\"\n",
+ "WAN_Q6_MODEL = \"wan2.1-t2v-14b-Q6_K.gguf\"\n",
"\n",
+ "# Download Unet\n",
+ "unet_target = WAN_Q6_MODEL if useQ6 else WAN_Q5_MODEL\n",
+ "unet_url = f\"https://huggingface.co/city96/Wan2.1-T2V-14B-gguf/resolve/main/{unet_target}\"\n",
+ "!aria2c --console-log-level=error -c -x 16 -s 16 -k 1M \"$unet_url\" -d /content/ComfyUI/models/unet -o \"$unet_target\"\n",
+ "\n",
+ "# --- Always Download Text Encoder and VAE (shared for Q5/Q6) ---\n",
"!aria2c --console-log-level=error -c -x 16 -s 16 -k 1M https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors -d /content/ComfyUI/models/text_encoders -o umt5_xxl_fp8_e4m3fn_scaled.safetensors\n",
"!aria2c --console-log-level=error -c -x 16 -s 16 -k 1M https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors -d /content/ComfyUI/models/vae -o wan_2.1_vae.safetensors\n",
"\n",
+ "# --- (Optional) Download LoRAs below for your favorite NSFW styles ----\n",
+ "# Example: !wget -O /content/ComfyUI/models/lora/myNSFWlora.safetensors https://huggingface.co/path/to/your_lora.safetensors\n",
+ "# You may add as many LoRAs as you like here!\n",
+ "\n",
"import torch\n",
"import numpy as np\n",
"from PIL import Image\n",
"import gc\n",
- "import sys\n",
- "import random\n",
- "import os\n",
+ "import sys, random, os\n",
"import imageio\n",
- "import subprocess\n",
"from google.colab import files\n",
"from IPython.display import display, HTML, Image as IPImage\n",
"sys.path.insert(0, '/content/ComfyUI')\n",
"\n",
+ "# --- ComfyUI Model/Node Imports ---\n",
"from comfy import model_management\n",
- "\n",
"from nodes import (\n",
- " CheckpointLoaderSimple,\n",
- " CLIPLoader,\n",
- " CLIPTextEncode,\n",
- " VAEDecode,\n",
- " VAELoader,\n",
- " KSampler,\n",
- " UNETLoader\n",
+ " CheckpointLoaderSimple, CLIPLoader, CLIPTextEncode, VAEDecode, VAELoader, KSampler, UNETLoader\n",
")\n",
- "\n",
"from custom_nodes.ComfyUI_GGUF.nodes import UnetLoaderGGUF\n",
"from comfy_extras.nodes_model_advanced import ModelSamplingSD3\n",
"from comfy_extras.nodes_hunyuan import EmptyHunyuanLatentVideo\n",
"from comfy_extras.nodes_images import SaveAnimatedWEBP\n",
"from comfy_extras.nodes_video import SaveWEBM\n",
"\n",
- "# unet_loader = UNETLoader()\n",
+ "from comfy.nodes_lora import LoraLoader # <-- Built-in LoRA loader\n",
+ "\n",
+ "# --- Node Instantiation ---\n",
"unet_loader = UnetLoaderGGUF()\n",
- "# model_sampling = ModelSamplingSD3()\n",
+ "# model_sampling = ModelSamplingSD3() # For SDXL/patching, not needed in Wan 2.1\n",
"clip_loader = CLIPLoader()\n",
"clip_encode_positive = CLIPTextEncode()\n",
"clip_encode_negative = CLIPTextEncode()\n",
@@ -107,130 +130,167 @@
"vae_decode = VAEDecode()\n",
"save_webp = SaveAnimatedWEBP()\n",
"save_webm = SaveWEBM()\n",
+ "lora_loader = LoraLoader()\n",
"\n",
- "def clear_memory():\n",
+ "def clear_memory(verbose=False):\n",
" gc.collect()\n",
" if torch.cuda.is_available():\n",
" torch.cuda.empty_cache()\n",
" torch.cuda.ipc_collect()\n",
- " for obj in list(globals().values()):\n",
- " if torch.is_tensor(obj) or (hasattr(obj, \"data\") and torch.is_tensor(obj.data)):\n",
- " del obj\n",
+ " count = 0\n",
+ " for objname, obj in list(globals().items()):\n",
+ " try:\n",
+ " if torch.is_tensor(obj) or (hasattr(obj, \"data\") and torch.is_tensor(obj.data)):\n",
+ " del globals()[objname]\n",
+ " count += 1\n",
+ " except:\n",
+ " continue\n",
+ " if verbose:\n",
+ " print(f\"[clear_memory] Objects cleaned up: {count}\")\n",
" gc.collect()\n",
"\n",
+ "# --- Video/Image Save Utilities ---\n",
"def save_as_mp4(images, filename_prefix, fps, output_dir=\"/content/ComfyUI/output\"):\n",
" os.makedirs(output_dir, exist_ok=True)\n",
" output_path = f\"{output_dir}/{filename_prefix}.mp4\"\n",
- "\n",
" frames = [(img.cpu().numpy() * 255).astype(np.uint8) for img in images]\n",
- "\n",
" with imageio.get_writer(output_path, fps=fps) as writer:\n",
" for frame in frames:\n",
" writer.append_data(frame)\n",
- "\n",
" return output_path\n",
"\n",
- "def save_as_webp(images, filename_prefix, fps, quality=90, lossless=False, method=4, output_dir=\"/content/ComfyUI/output\"):\n",
- " \"\"\"Save images as animated WEBP using imageio.\"\"\"\n",
- " os.makedirs(output_dir, exist_ok=True)\n",
- " output_path = f\"{output_dir}/{filename_prefix}.webp\"\n",
- "\n",
- "\n",
- " frames = [(img.cpu().numpy() * 255).astype(np.uint8) for img in images]\n",
- "\n",
- "\n",
- " kwargs = {\n",
- " 'fps': int(fps),\n",
- " 'quality': int(quality),\n",
- " 'lossless': bool(lossless),\n",
- " 'method': int(method)\n",
- " }\n",
- "\n",
- " with imageio.get_writer(\n",
- " output_path,\n",
- " format='WEBP',\n",
- " mode='I',\n",
- " **kwargs\n",
- " ) as writer:\n",
- " for frame in frames:\n",
- " writer.append_data(frame)\n",
- "\n",
- " return output_path\n",
- "\n",
- "def save_as_webm(images, filename_prefix, fps, codec=\"vp9\", quality=32, output_dir=\"/content/ComfyUI/output\"):\n",
- " \"\"\"Save images as WEBM using imageio.\"\"\"\n",
+ "def save_as_webm(images, filename_prefix, fps, codec=\"vp9\", quality=16, output_dir=\"/content/ComfyUI/output\"):\n",
" os.makedirs(output_dir, exist_ok=True)\n",
" output_path = f\"{output_dir}/{filename_prefix}.webm\"\n",
- "\n",
- "\n",
" frames = [(img.cpu().numpy() * 255).astype(np.uint8) for img in images]\n",
- "\n",
- "\n",
" kwargs = {\n",
- " 'fps': int(fps),\n",
- " 'quality': int(quality),\n",
- " 'codec': str(codec),\n",
- " 'output_params': ['-crf', str(int(quality))]\n",
+ " 'fps': int(fps), 'quality': int(quality), 'codec': str(codec), 'output_params': ['-crf', str(int(quality))]\n",
" }\n",
- "\n",
- " with imageio.get_writer(\n",
- " output_path,\n",
- " format='FFMPEG',\n",
- " mode='I',\n",
- " **kwargs\n",
- " ) as writer:\n",
+ " with imageio.get_writer(output_path, format='FFMPEG', mode='I', **kwargs) as writer:\n",
" for frame in frames:\n",
" writer.append_data(frame)\n",
- "\n",
" return output_path\n",
"\n",
"def save_as_image(image, filename_prefix, output_dir=\"/content/ComfyUI/output\"):\n",
- " \"\"\"Save single frame as PNG image.\"\"\"\n",
" os.makedirs(output_dir, exist_ok=True)\n",
" output_path = f\"{output_dir}/{filename_prefix}.png\"\n",
- "\n",
" frame = (image.cpu().numpy() * 255).astype(np.uint8)\n",
- "\n",
" Image.fromarray(frame).save(output_path)\n",
- "\n",
" return output_path\n",
"\n",
+ "def display_video(video_path):\n",
+ " from base64 import b64encode\n",
+ " video_data = open(video_path,'rb').read()\n",
+ " # Detect MIME for embed\n",
+ " if video_path.lower().endswith('.mp4'): mime_type = \"video/mp4\"\n",
+ " elif video_path.lower().endswith('.webm'): mime_type = \"video/webm\"\n",
+ " elif video_path.lower().endswith('.webp'): mime_type = \"image/webp\"\n",
+ " else: mime_type = \"video/mp4\"\n",
+ " data_url = f\"data:{mime_type};base64,\" + b64encode(video_data).decode()\n",
+ " display(HTML(f\"\"\"\n",
+ " \"\"\"))\n",
+ "\n",
+ "print(\"\\u2705 Environment Setup Complete!\")\n",
+ "# --- CODE EXTENSIONS: To add ControlNet, IPAdapter, Reactor etc, add their repo/clone and models above and import/patch usage in the generate_video() function and the parameter list below. See project wiki/readmes for more info. ---"
+ ],
+ "metadata": {
+ "id": "rrXFIT4fMfyJ",
+ "cellView": "form"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "# @title 🍑 Generate NSFW Video/Image - Full LoRA, Q5/Q6, and SFW/NSFW Prompts\n",
+ "\n",
+ "\"\"\"\n",
+ "Tips for best results:\n",
+ "- For fast porn loops: use 8-10 frames, 480x832 or 512x832 (keep <12 for T4)\n",
+ "- Default CFG 1.2, steps 22: more natural, less overdone skin\n",
+ "- Use seed = -1 for FULL randomness every run, else fix seed for reroll\n",
+ "- To load multiple LoRAs: supply comma list in lora_name, and a comma/space-list lora_strength. Example:\n",
+ " lora_name = 'sexy_butt_v12.safetensors,super_gigaporn_mix1.0.safetensors'\n",
+ " lora_strength = '0.7, 0.5'\n",
+ "- LoRAs must be present in /content/ComfyUI/models/lora (see cell above for wget)\n",
+ "\"\"\"\n",
+ "\n",
+ "positive_prompt = \"ultra high res, explicit uncensored, stunning beautiful nude female pornstar, erotic pose, perfect skin, photoreal, 8K pop, sweaty, detailed anatomy, seductive, cumshot, realism\" # @param {\"type\":\"string\"}\n",
+ "negative_prompt = \"censored, mosaic, watermark, worst quality, ugly, deformed, jpeg, extra limbs, mutilated, blurry, cropped, jpeg artifacts, out of frame, poorly drawn, signature, artist name, missing finger, extra hands, extra foot, nipples hidden, bad composition, poorly rendered genitals\" # @param {\"type\":\"string\"}\n",
+ "\n",
+ "width = 480 # @param {\"type\":\"integer\"}\n",
+ "height = 832 # @param {\"type\":\"integer\"}\n",
+ "frames = 10 # @param {\"type\":\"integer\", \"min\":1, \"max\":32} # Careful above 12 on T4 GPU\n",
+ "fps = 14 # @param {\"type\":\"integer\", \"min\":1, \"max\":30}\n",
+ "\n",
+ "steps = 22 # @param {\"type\":\"integer\", \"min\":10, \"max\":60}\n",
+ "cfg_scale = 1.2 # @param {\"type\":\"number\", \"min\":0.8, \"max\":3.0, \"step\":0.05}\n",
+ "sampler_name = \"uni_pc\" # @param [\"uni_pc\", \"euler\", \"dpmpp_2m\", \"ddim\", \"lms\"]\n",
+ "scheduler = \"simple\" # @param [\"simple\", \"normal\", \"karras\", \"exponential\"]\n",
+ "\n",
+ "# --- LoRA Parameters ---\n",
+ "lora_name = \"\" # @param {\"type\":\"string\"} # CSV of .safetensors names (in /content/ComfyUI/models/lora/)\n",
+ "lora_strength = \"0.7\" # @param {\"type\":\"string\"} # CSV or Space-separated for multiple LoRAs (must match lora_name count)\n",
+ "\n",
+ "output_format = \"mp4\" # @param [\"mp4\", \"webm\"]\n",
+ "useQ6 = False # @param {\"type\":\"boolean\"} # Q6 is lighter/faster (set above if you want globally)\n",
+ "\n",
+ "seed = -1 # @param {\"type\":\"integer\"} # If -1, a random new seed is chosen per run\n",
+ "\n",
+ "# Advanced: Feel free to add parameters for ControlNet, Reactor, etc. Add here and pass into the function below!\n",
+ "\n",
"def generate_video(\n",
- " positive_prompt: str = \"a fox moving quickly in a beautiful winter scenery nature trees mountains daytime tracking camera\",\n",
- " negative_prompt: str = \"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走\",\n",
- " width: int = 832,\n",
- " height: int = 480,\n",
- " seed: int = 82628696717253,\n",
- " steps: int = 30,\n",
- " cfg_scale: float = 1.0,\n",
- " sampler_name: str = \"uni_pc\",\n",
- " scheduler: str = \"simple\",\n",
- " frames: int = 33,\n",
- " fps: int = 16,\n",
+ " positive_prompt: str, negative_prompt: str,\n",
+ " width: int, height: int, seed: int, steps: int, cfg_scale: float,\n",
+ " sampler_name: str, scheduler: str, frames: int, fps: int,\n",
+ " lora_name: str = \"\", lora_strength: str = \"1.0\", useQ6: bool = False,\n",
" output_format: str = \"mp4\"\n",
"):\n",
+ " \"\"\"Main generation logic with LoRA support and aggressive memory management.\"\"\"\n",
+ " if seed == -1:\n",
+ " seed = random.randint(0, 2**63 - 1)\n",
+ " print(f\"[INFO] Random seed chosen: {seed}\")\n",
"\n",
" with torch.inference_mode():\n",
- " print(\"Loading Text_Encoder...\")\n",
+ " # --- Text Encoder ---\n",
+ " print(\"[INFO] Loading Text Encoder...\")\n",
" clip = clip_loader.load_clip(\"umt5_xxl_fp8_e4m3fn_scaled.safetensors\", \"wan\", \"default\")[0]\n",
- "\n",
" positive = clip_encode_positive.encode(clip, positive_prompt)[0]\n",
" negative = clip_encode_negative.encode(clip, negative_prompt)[0]\n",
+ " clear_memory()\n",
"\n",
- " del clip\n",
- " torch.cuda.empty_cache()\n",
- " gc.collect()\n",
- "\n",
+ " # --- Empty Latent ---\n",
" empty_latent = empty_latent_video.generate(width, height, frames, 1)[0]\n",
- "\n",
- " print(\"Loading Unet Model...\")\n",
- " if useQ6:\n",
- " model = unet_loader.load_unet(\"wan2.1-t2v-14b-Q6_K.gguf\")[0]\n",
- " else:\n",
- " model = unet_loader.load_unet(\"wan2.1-t2v-14b-Q5_0.gguf\")[0]\n",
- " # model = model_sampling.patch(model, 8)[0]\n",
- "\n",
- " print(\"Generating video...\")\n",
+ " clear_memory()\n",
+ "\n",
+ " # --- Load Unet Model (Q5 or Q6 as selected) ---\n",
+ " print(f\"[INFO] Loading Unet Model ({'Q6' if useQ6 else 'Q5'}) ...\")\n",
+ " unet_path = \"wan2.1-t2v-14b-Q6_K.gguf\" if useQ6 else \"wan2.1-t2v-14b-Q5_0.gguf\"\n",
+ " model = unet_loader.load_unet(unet_path)[0]\n",
+ "\n",
+ " # --- LoRA Support: Multiple LoRAs with custom strengths ---\n",
+ " applied_loras = []\n",
+ " if lora_name.strip():\n",
+ " names = [n.strip() for n in lora_name.split(',') if n.strip()]\n",
+ " strengths = [float(x.strip()) for x in lora_strength.replace(',', ' ').split()]\n",
+ " if len(strengths) < len(names):\n",
+ " strengths += [strengths[-1]] * (len(names)-len(strengths))\n",
+ " print(f\"[INFO] Loading {len(names)} LoRA(s): {names} w/ strengths {strengths}\")\n",
+ " for l_name, l_strength in zip(names, strengths):\n",
+ " lora_path = os.path.join('/content/ComfyUI/models/lora', l_name)\n",
+ " if not os.path.exists(lora_path):\n",
+ " print(f\"[WARN] Could not find LoRA: {lora_path}. Skipping.\")\n",
+ " continue\n",
+ " # LoRA Loader: returns patched model\n",
+ " model = lora_loader.load_lora(model, lora_path, l_strength)[0]\n",
+ " applied_loras.append(l_name)\n",
+ " if not applied_loras:\n",
+ " print('[WARN] No LoRA(s) actually loaded!')\n",
+ " clear_memory()\n",
+ "\n",
+ " # --- Main Sampling ---\n",
+ " print(\"[INFO] Sampling video latents...\")\n",
" sampled = ksampler.sample(\n",
" model=model,\n",
" seed=seed,\n",
@@ -242,108 +302,36 @@
" negative=negative,\n",
" latent_image=empty_latent\n",
" )[0]\n",
+ " del model; clear_memory()\n",
"\n",
- " del model\n",
- " torch.cuda.empty_cache()\n",
- " gc.collect()\n",
- "\n",
- " print(\"Loading VAE...\")\n",
+ " # --- VAE decode ---\n",
+ " print(\"[INFO] Loading VAE and decoding latents...\")\n",
" vae = vae_loader.load_vae(\"wan_2.1_vae.safetensors\")[0]\n",
- "\n",
- " try:\n",
- " print(\"Decoding latents...\")\n",
- " decoded = vae_decode.decode(vae, sampled)[0]\n",
- "\n",
- " del vae\n",
- " torch.cuda.empty_cache()\n",
- " gc.collect()\n",
- "\n",
- " output_path = \"\"\n",
- " if frames == 1:\n",
- " print(\"Single frame detected - saving as PNG image...\")\n",
- " output_path = save_as_image(decoded[0], \"ComfyUI\")\n",
- " # print(f\"Image saved as PNG: {output_path}\")\n",
- "\n",
- " display(IPImage(filename=output_path))\n",
+ " decoded = vae_decode.decode(vae, sampled)[0]\n",
+ " del vae; clear_memory()\n",
+ "\n",
+ " # --- Save/Display Output ---\n",
+ " output_path = \"\"\n",
+ " if frames == 1:\n",
+ " print(\"[INFO] Saving single PNG image...\")\n",
+ " output_path = save_as_image(decoded[0], \"ComfyUI\")\n",
+ " display(IPImage(filename=output_path))\n",
+ " else:\n",
+ " if output_format.lower() == \"webm\":\n",
+ " print(\"[INFO] Saving as WEBM...\")\n",
+ " output_path = save_as_webm(\n",
+ " decoded, \"ComfyUI\", fps=fps, codec=\"vp9\")\n",
+ " elif output_format.lower() == \"mp4\":\n",
+ " print(\"[INFO] Saving as MP4...\")\n",
+ " output_path = save_as_mp4(decoded, \"ComfyUI\", fps)\n",
" else:\n",
- " if output_format.lower() == \"webm\":\n",
- " print(\"Saving as WEBM...\")\n",
- " output_path = save_as_webm(\n",
- " decoded,\n",
- " \"ComfyUI\",\n",
- " fps=fps,\n",
- " codec=\"vp9\",\n",
- " quality=10\n",
- " )\n",
- " elif output_format.lower() == \"mp4\":\n",
- " print(\"Saving as MP4...\")\n",
- " output_path = save_as_mp4(decoded, \"ComfyUI\", fps)\n",
- " else:\n",
- " raise ValueError(f\"Unsupported output format: {output_format}\")\n",
- "\n",
- " # print(f\"Video saved as {output_format.upper()}: {output_path}\")\n",
- "\n",
- " display_video(output_path)\n",
- "\n",
- " except Exception as e:\n",
- " print(f\"Error during decoding/saving: {str(e)}\")\n",
- " raise\n",
- " finally:\n",
- " clear_memory()\n",
- "\n",
- "def display_video(video_path):\n",
- " from IPython.display import HTML\n",
- " from base64 import b64encode\n",
- "\n",
- " video_data = open(video_path,'rb').read()\n",
- "\n",
- " # Determine MIME type based on file extension\n",
- " if video_path.lower().endswith('.mp4'):\n",
- " mime_type = \"video/mp4\"\n",
- " elif video_path.lower().endswith('.webm'):\n",
- " mime_type = \"video/webm\"\n",
- " elif video_path.lower().endswith('.webp'):\n",
- " mime_type = \"image/webp\"\n",
- " else:\n",
- " mime_type = \"video/mp4\" # default\n",
+ " raise ValueError(f\"Unsupported output format: {output_format}\")\n",
+ " display_video(output_path)\n",
"\n",
- " data_url = f\"data:{mime_type};base64,\" + b64encode(video_data).decode()\n",
- "\n",
- " display(HTML(f\"\"\"\n",
- " \n",
- " \"\"\"))\n",
- "\n",
- "print(\"✅ Environment Setup Complete!\")\n",
- "\n"
- ],
- "metadata": {
- "id": "rrXFIT4fMfyJ",
- "cellView": "form"
- },
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "code",
- "source": [
- "# @title Generate Video/Image\n",
- "\n",
- "positive_prompt = \"Close up view of a stunning, confident girl with curvaceous, wide hips, medium-weight figure, and a beautiful face walking with her partner down an empty street.\" # @param {\"type\":\"string\"}\n",
- "negative_prompt = \"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走\" # @param {\"type\":\"string\"}\n",
- "width = 832 # @param {\"type\":\"number\"}\n",
- "height = 480 # @param {\"type\":\"number\"}\n",
- "seed = 82628696717258 # @param {\"type\":\"integer\"}\n",
- "steps = 20 # @param {\"type\":\"integer\", \"min\":1, \"max\":100}\n",
- "cfg_scale = 3 # @param {\"type\":\"number\", \"min\":1, \"max\":20}\n",
- "sampler_name = \"uni_pc\" # @param [\"uni_pc\", \"euler\", \"dpmpp_2m\", \"ddim\", \"lms\"]\n",
- "scheduler = \"simple\" # @param [\"simple\", \"normal\", \"karras\", \"exponential\"]\n",
- "frames = 24 # @param {\"type\":\"integer\", \"min\":1, \"max\":120}\n",
- "fps = 16 # @param {\"type\":\"integer\", \"min\":1, \"max\":60}\n",
- "output_format = \"mp4\" # @param [\"mp4\", \"webm\"]\n",
+ " # Always clean up as much RAM as possible after run\n",
+ " clear_memory(verbose=True)\n",
"\n",
- "# with torch.inference_mode():\n",
+ "# --- MAIN TRIGGER: Call generation function ---\n",
"generate_video(\n",
" positive_prompt=positive_prompt,\n",
" negative_prompt=negative_prompt,\n",
@@ -356,9 +344,22 @@
" scheduler=scheduler,\n",
" frames=frames,\n",
" fps=fps,\n",
+ " lora_name=lora_name,\n",
+ " lora_strength=lora_strength,\n",
+ " useQ6=useQ6,\n",
" output_format=output_format\n",
")\n",
- "clear_memory()"
+ "# --- Final memory cleanup for next run ---\n",
+ "clear_memory(verbose=True)\n",
+ "\n",
+ "# ---\n",
+ "# [EXTENSION COMMENT]:\n",
+ "# To add features like ControlNet, Reactor, IPAdapter, DreamBooth finetune, etc, simply:\n",
+ "# - Install their code/repos at the top cell\n",
+ "# - Download their weights\n",
+ "# - Pass extra params in generate_video() and call in the main function accordingly.\n",
+ "# - For inspiration see networks like ComfyUI IPAdapter node or custom nodes for ControlNet/Adapter/Segment-Anything.\n",
+ "# - WAN 2.1 is patchable, so you can even add SD3-style adapters in the same vein! ---"
],
"metadata": {
"cellView": "form",
@@ -368,4 +369,4 @@
"outputs": []
}
]
-}
\ No newline at end of file
+}