{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# HyperFlow H3 Flash \u2014 Colab Notebook (Pro A100 / H100)\n",
    "\n",
    "Generate video + synchronized stereo audio with `VideoRebirthAI/hyperflow-h3-flash` (14B, few-step MiniMax-H3).\n",
    "\n",
    "| Task | Input | Output |\n",
    "|------|-------|--------|\n",
    "| `t2va` | text prompt | video + audio |\n",
    "| `fl2va` | prompt + first keyframe (\u00b1 last) | video + audio |\n",
    "| `ref2va` | prompt + up to 12 references | video + audio |\n",
    "\n",
    "**Hardware reality check**\n",
    "\n",
    "- **Free Colab T4 (16 GB VRAM, ~12 GB RAM): cannot run this model.** Q4_K_M GGUF is 9.1 GB but the text encoder alone is 15.7 GB, and the model needs 24 GB+ VRAM.\n",
    "- **Colab Pro / Pro+ A100 40 GB**: GGUF path works (recommended below).\n",
    "- **A100 80 GB / H100**: GGUF or the INT8 `hyperflow-h3-flash` package (needs 69+ GiB system RAM with `fp8` text encoder).\n",
    "\n",
    "\u26a0\ufe0f **License:** [MiniMax H3 Community License](https://huggingface.co/VideoRebirthAI/hyperflow-h3-flash/tree/main/LICENSE) \u2014 excluded territories: **EU, UK, South Korea, US** (prior MiniMax authorization required). Publicly distributed outputs must be disclosed as **AI-generated**.\n",
    "\n",
    "\ud83d\udca1 **No GPU at all?** Free hosted routes with daily quota: [fal.ai](https://fal.ai/sandbox?op=video.text_to_video&free=true&models=da5j8ccregja812b831g) (5\u00d7 H3 Max/day, 15s, native audio), Google Flow (~2/day), PixVerse (~2\u20133/day).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 1. Runtime detection\n",
    "import torch, os, subprocess, sys\n",
    "\n",
    "def gpu_info():\n",
    "    if not torch.cuda.is_available():\n",
    "        return \"CPU\", 0.0, 0.0\n",
    "    p = torch.cuda.get_device_properties(0)\n",
    "    ram = 0.0\n",
    "    with open(\"/proc/meminfo\") as f:\n",
    "        for line in f:\n",
    "            if line.startswith(\"MemTotal:\"):\n",
    "                ram = int(line.split()[1]) / 1e6\n",
    "                break\n",
    "    return p.name, p.total_memory / 1e9, ram\n",
    "\n",
    "GPU_NAME, VRAM_GB, RAM_GB = gpu_info()\n",
    "print(f\"GPU: {GPU_NAME} | VRAM: {VRAM_GB:.1f} GB | RAM: {RAM_GB:.1f} GB\")\n",
    "\n",
    "if \"cpu\" in GPU_NAME.lower() or VRAM_GB < 20:\n",
    "    print(\"\\nWARNING: this model needs 24 GB+ VRAM. Free T4 (16 GB) cannot run it.\")\n",
    "    print(\"Use Colab Pro A100, or a free hosted route (fal.ai / Google Flow / PixVerse).\")\n",
    "    RESIZE = False\n",
    "elif VRAM_GB < 60:\n",
    "    print(\"\\nA100 40 GB detected: GGUF path recommended.\")\n",
    "    RESIZE = True\n",
    "else:\n",
    "    print(\"\\n80 GB+ GPU detected: GGUF or INT8 path available.\")\n",
    "    RESIZE = True\n",
    "\n",
    "cap = torch.cuda.get_device_capability(0) if torch.cuda.is_available() else (0, 0)\n",
    "CMAKE_ARCH = f\"{cap[0]}{cap[1]}\"\n",
    "print(f\"CUDA compute capability: {CMAKE_ARCH}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 2. (Optional) Cache weights in Google Drive\n",
    "USE_DRIVE = True  # set False to download to /content each session\n",
    "WEIGHTS_DIR = \"/content/hyperflow_weights\"\n",
    "if USE_DRIVE:\n",
    "    try:\n",
    "        from google.colab import drive\n",
    "        drive.mount(\"/content/drive\")\n",
    "        WEIGHTS_DIR = \"/content/drive/MyDrive/hyperflow_weights\"\n",
    "    except Exception as e:\n",
    "        print(\"Drive mount failed, using /content:\", e)\n",
    "os.makedirs(WEIGHTS_DIR, exist_ok=True)\n",
    "print(\"Weights dir:\", WEIGHTS_DIR)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 3. Install dependencies\n",
    "import subprocess\n",
    "def sh(cmd):\n",
    "    print(\">>>\", cmd)\n",
    "    r = subprocess.run(cmd, shell=True, text=True)\n",
    "    if r.returncode != 0:\n",
    "        raise RuntimeError(f\"Command failed: {cmd}\")\n",
    "    return r\n",
    "\n",
    "sh(\"pip install -q -U pip\")\n",
    "sh('pip install -q \"huggingface_hub>=0.30\" safetensors tqdm gradio')\n",
    "print(\"Dependencies ready.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 4. Download GGUF weights (cached)\n",
    "from huggingface_hub import hf_hub_download\n",
    "\n",
    "QUANT = \"Q4_K_M\"  # Q3_K_M (7.2GB) | Q4_K_M (9.1GB, default) | Q6_K (11.6GB) | Q8_0 (14.9GB)\n",
    "\n",
    "def get(repo_id, filename, subfolder):\n",
    "    dest = os.path.join(WEIGHTS_DIR, subfolder)\n",
    "    os.makedirs(dest, exist_ok=True)\n",
    "    path = os.path.join(dest, filename)\n",
    "    if os.path.exists(path) and os.path.getsize(path) > 1024:\n",
    "        print(\"cached:\", path)\n",
    "        return path\n",
    "    print(\"downloading:\", f\"{repo_id}/{subfolder}/{filename}\")\n",
    "    return hf_hub_download(repo_id=repo_id, filename=filename, subfolder=subfolder, local_dir=dest)\n",
    "\n",
    "GGUF_MODEL = get(\"VideoRebirthAI/hyperflow-h3-flash\",\n",
    "                 f\"minimax_h3_fl2va_hyperflow_flash_14b-{QUANT}.gguf\", \"fl2va_gguf\")\n",
    "TEXT_ENCODER = get(\"Comfy-Org/MiniMax-H3\",\n",
    "                   \"qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors\", \"text_encoders\")\n",
    "VIDEO_VAE = get(\"Comfy-Org/MiniMax-H3\",\n",
    "                \"minimax_h3_video_vae_fp16.safetensors\", \"vae\")\n",
    "AUDIO_VAE = get(\"Comfy-Org/MiniMax-H3\",\n",
    "                \"minimax_h3_audio_vae_fp32.safetensors\", \"vae\")\n",
    "print(\"\\nAll GGUF weights ready.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 5. Build stable-diffusion.cpp with the HyperFlow patch\n",
    "SD_DIR = \"/content/stable-diffusion.cpp\"\n",
    "SD_CLI = f\"{SD_DIR}/build/bin/sd-cli\"\n",
    "PATCH_URL = \"https://github.com/Video-Rebirth/hyperflow-h3-flash/raw/main/integrations/stable-diffusion.cpp/hyperflow-two-time.patch\"\n",
    "\n",
    "if not os.path.exists(SD_CLI):\n",
    "    sh(f\"rm -rf {SD_DIR}\")\n",
    "    sh(f\"git clone --depth 1 https://github.com/leejet/stable-diffusion.cpp {SD_DIR}\")\n",
    "    sh(f\"cd {SD_DIR} && git fetch --depth 50 origin 3be2378 && git checkout 3be2378\")\n",
    "    sh(f\"cd {SD_DIR} && curl -L {PATCH_URL} | git apply\")\n",
    "    sh(f\"cd {SD_DIR} && cmake -B build -DSD_CUDA=ON -DSD_METAL=OFF \"\n",
    "       f\"-DCMAKE_CUDA_ARCHITECTURES={CMAKE_ARCH} -DCMAKE_BUILD_TYPE=Release\")\n",
    "    sh(f\"cd {SD_DIR} && cmake --build build --config Release -j$(nproc)\")\n",
    "else:\n",
    "    print(\"sd-cli already built:\", SD_CLI)\n",
    "\n",
    "if not os.path.exists(SD_CLI):\n",
    "    raise FileNotFoundError(\"sd-cli build failed\")\n",
    "print(\"sd-cli ready.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 6. Generation helpers (GGUF via sd-cli)\n",
    "import uuid\n",
    "\n",
    "def _run(args):\n",
    "    cmd = [SD_CLI, \"-M\", \"vid_gen\",\n",
    "           \"--diffusion-model\", GGUF_MODEL,\n",
    "           \"--llm\", TEXT_ENCODER,\n",
    "           \"--vae\", VIDEO_VAE,\n",
    "           \"--audio-vae\", AUDIO_VAE,\n",
    "           \"--sampling-method\", \"euler\",\n",
    "           \"--cfg-scale\", \"1.0\"] + args\n",
    "    print(\" \".join(cmd))\n",
    "    r = subprocess.run(cmd, capture_output=True, text=True)\n",
    "    if r.returncode != 0:\n",
    "        raise RuntimeError(r.stderr[-3000:])\n",
    "    return r.stdout\n",
    "\n",
    "def generate(prompt, task=\"t2va\", steps=6, frames=124, width=None, height=None,\n",
    "             fps=24, seed=42, first_image=None, last_image=None,\n",
    "             references=None, offload=None, out=None):\n",
    "    width = width or (960 if RESIZE else 1344)\n",
    "    height = height or (544 if RESIZE else 768)\n",
    "    out = out or f\"/content/{task}_{uuid.uuid4().hex[:8]}.webm\"\n",
    "    args = [\"--steps\", str(steps), \"-W\", str(width), \"-H\", str(height),\n",
    "            \"--video-frames\", str(frames), \"--fps\", str(fps), \"-s\", str(seed),\n",
    "            \"-p\", prompt, \"-o\", out]\n",
    "    if first_image:\n",
    "        args += [\"-i\", first_image]\n",
    "    if last_image:\n",
    "        args += [\"--end-img\", last_image]\n",
    "    if references:\n",
    "        for ref in references:\n",
    "            args += [\"--ref-image\", ref]\n",
    "    if offload or (VRAM_GB and VRAM_GB < 24):\n",
    "        args += [\"--offload-to-cpu\"]\n",
    "    _run(args)\n",
    "    if not os.path.exists(out):\n",
    "        raise FileNotFoundError(out)\n",
    "    return out\n",
    "\n",
    "print(\"generate() ready. Tasks: t2va | fl2va (first_image/last_image) | ref2va (references=[images])\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 7. Generate a clip\n",
    "from IPython.display import Video\n",
    "\n",
    "prompt = \"A chef slides a steaming bowl of ramen across a wooden counter; rain taps on the window.\"\n",
    "video_path = generate(prompt, task=\"t2va\", steps=6, frames=124, seed=42)\n",
    "print(\"Saved:\", video_path)\n",
    "Video(video_path, embed=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 8. INT8 convrot path (A100 80GB / H100 only, optional)\n",
    "if VRAM_GB >= 60:\n",
    "    sh('pip install -q \"hyperflow-h3-flash @ git+https://github.com/Video-Rebirth/hyperflow-h3-flash\"')\n",
    "    import torch\n",
    "    from diffusers.utils.export_utils import encode_video\n",
    "    from hyperflow_h3_flash import load_pipeline\n",
    "\n",
    "    pipe = load_pipeline(\"t2va\")\n",
    "    out = pipe(\n",
    "        prompt=\"A chef slides a steaming bowl of ramen across a wooden counter; rain taps on the window.\",\n",
    "        num_frames=124,\n",
    "        num_inference_steps=6,\n",
    "        generator=torch.Generator().manual_seed(42),\n",
    "        output=[\"videos\", \"audio\", \"sampling_rate\"],\n",
    "    )\n",
    "    encode_video(out[\"videos\"][0], fps=24, output_path=\"/content/int8_t2va.mp4\",\n",
    "                 audio=out[\"audio\"][0], audio_sample_rate=out[\"sampling_rate\"])\n",
    "    print(\"Saved /content/int8_t2va.mp4\")\n",
    "else:\n",
    "    print(\"Skipped: INT8 path needs 80 GB VRAM + 69+ GiB RAM (fp8 text encoder).\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 9. Batch generation\n",
    "def batch_generate(prompts, out_dir=\"/content/batch\", **kwargs):\n",
    "    os.makedirs(out_dir, exist_ok=True)\n",
    "    results = []\n",
    "    for i, p in enumerate(prompts, 1):\n",
    "        print(f\"[{i}/{len(prompts)}] {p[:70]}\")\n",
    "        try:\n",
    "            path = generate(p, out=f\"{out_dir}/clip_{i:03d}.webm\", **kwargs)\n",
    "            results.append((p, path, None))\n",
    "            print(\"  ok:\", path)\n",
    "        except Exception as e:\n",
    "            results.append((p, None, str(e)))\n",
    "            print(\"  failed:\", e)\n",
    "    return results\n",
    "\n",
    "# Example:\n",
    "# batch_generate([\n",
    "#     \"A chef slides a steaming bowl of ramen across a wooden counter; rain taps on the window.\",\n",
    "#     \"A cyberpunk city at night with neon reflections on wet streets; distant synth music.\",\n",
    "# ], steps=6, frames=124, seed=42)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title 10. Gradio UI with public link (optional)\n",
    "import gradio as gr\n",
    "\n",
    "def ui_generate(prompt, steps, frames, seed, first_img, last_img):\n",
    "    try:\n",
    "        first_p = last_p = None\n",
    "        if first_img is not None:\n",
    "            first_p = \"/content/first.png\"; first_img.save(first_p)\n",
    "        if last_img is not None:\n",
    "            last_p = \"/content/last.png\"; last_img.save(last_p)\n",
    "        task = \"fl2va\" if first_p else \"t2va\"\n",
    "        path = generate(prompt, task=task, steps=int(steps), frames=int(frames),\n",
    "                        seed=int(seed), first_image=first_p, last_image=last_p)\n",
    "        return path, \"Done\"\n",
    "    except Exception as e:\n",
    "        return None, f\"Error: {e}\"\n",
    "\n",
    "with gr.Blocks(title=\"HyperFlow H3 Flash\") as demo:\n",
    "    gr.Markdown(\"# HyperFlow H3 Flash \u2014 local GGUF demo\\nAI-generated content must be disclosed.\")\n",
    "    with gr.Row():\n",
    "        with gr.Column():\n",
    "            prompt = gr.Textbox(label=\"Prompt\", lines=3)\n",
    "            first_img = gr.Image(type=\"pil\", label=\"First keyframe (optional)\")\n",
    "            last_img = gr.Image(type=\"pil\", label=\"Last keyframe (optional)\")\n",
    "            steps = gr.Slider(4, 8, value=6, step=2, label=\"Steps\")\n",
    "            frames = gr.Slider(124, 362, value=124, step=17, label=\"Frames\")\n",
    "            seed = gr.Number(value=42, label=\"Seed\", precision=0)\n",
    "            btn = gr.Button(\"Generate\", variant=\"primary\")\n",
    "        with gr.Column():\n",
    "            out = gr.Video(label=\"Output\")\n",
    "            status = gr.Textbox(label=\"Status\")\n",
    "    btn.click(ui_generate, [prompt, steps, frames, seed, first_img, last_img], [out, status])\n",
    "\n",
    "demo.launch(share=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Free daily alternatives (no GPU needed)\n",
    "\n",
    "| Route | Free quota | Notes |\n",
    "|-------|-----------|-------|\n",
    "| [fal.ai sandbox \u2014 H3 Max](https://fal.ai/sandbox?op=video.text_to_video&free=true&models=da5j8ccregja812b831g) | 5 clips/day, up to 15s | Native stereo audio, 768p, flagship quality |\n",
    "| Google Flow (Veo 3.1 Fast) | 50 credits/day \u2248 2 clips | Audio included |\n",
    "| PixVerse | 30 credits/day \u2248 2\u20133 clips | No native stereo audio |\n",
    "\n",
    "Stacked \u2248 9\u201310 free clips/day. HyperFlow H3 Flash itself has no hosted API yet \u2014 [vote for provider support](https://huggingface.co/spaces/huggingface/InferenceSupport/discussions/new?title=VideoRebirthAI/hyperflow-h3-flash).\n",
    "\n",
    "## License reminder\n",
    "\n",
    "MiniMax H3 Community License: excluded territories (EU, UK, South Korea, US), AI-generated disclosure required, commercial use >$20M/yr needs MiniMax authorization, outputs may not train other models.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10"
  },
  "accelerator": "GPU",
  "colab": {
   "provenance": []
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}