[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-84050":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":17,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":19,"hasPages":19,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":22,"readmeContent":23,"aiSummary":10,"trendingCount":16,"starSnapshotCount":16,"syncStatus":14,"lastSyncTime":24,"discoverSource":25},84050,"EasyAIVid","codeteacher330\u002FEasyAIVid","codeteacher330","Generate videos from text prompts locally with WAN 2.2 — a zero-config, cross-platform desktop app. No command line required.","",null,"Python",83,3,2,1,0,30,1.81,false,"master",[],"2026-06-12 02:04:37","\u003Cdiv align=\"center\">\n\n# 🎬 EasyAIVid\n\n![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-green)\n![Platform](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fplatform-Windows%20%7C%20macOS-blue)\n![Tauri](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTauri-v2-24C8DB?logo=tauri&logoColor=white)\n![React](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FReact-18-61DAFB?logo=react&logoColor=black)\n![FastAPI](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FFastAPI-Python%203.11-009688?logo=fastapi&logoColor=white)\n![WAN 2.2](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmodel-WAN%202.2-8b5cf6)\n\n**Local text-to-video generation with WAN 2.2 — a cross-platform desktop app.**\n\nType a prompt, get an MP4. The app bootstraps its own Python runtime, FFmpeg, GPU-correct PyTorch, and the model on first launch — no Python, FFmpeg, Git, CUDA setup, or command line required.\n\n[Install](#install-and-use) · [Screenshots](#screenshots) · [Architecture](#architecture) · [Inference engine](#inference-engine) · [Runtime & dependency manager](#zero-configuration-runtime) · [REST API](#rest-api) · [Security](#security-model) · [Build](#build--source-protection)\n\n\u003C\u002Fdiv>\n\n---\n\n## Screenshots\n\n### Generating a video\n\n![EasyAIVid — generating a video](sample_1.png)\n\nThe main workspace, mid-generation. From left to right:\n\n- **History sidebar** — every past generation, with a one-click **+ New Video** button. Statuses (Done \u002F Failed \u002F Cancelled \u002F Running) and relative timestamps are shown at a glance; an in-progress job hides its delete control so it can't be interrupted.\n- **Prompt & settings** — a multi-line prompt with a character counter, plus a **\"Recommended for your hardware\"** card that reads the detected GPU\u002FVRAM (here an RTX 3090 · 24 GB) and one-click applies the best resolution, duration, fps and steps, with a rough time estimate. Resolutions are **gated to what the GPU can actually run**; advanced controls (guidance scale, inference steps, fps) live behind **Advanced Settings**.\n- **Live progress** — a real-time bar with a **continuous ETA across all stages** (model load → denoise → encode) and Cancel, plus a **Resources** monitor showing live CPU, RAM, and per-GPU utilization \u002F VRAM \u002F temperature.\n\n### Previewing & saving the result\n\n![EasyAIVid — previewing a finished video](sample_2.png)\n\nOn completion the result loads into an **embedded `\u003Cvideo>` player** (play\u002Fpause, seek, volume), streamed from the local backend over an authenticated URL. **Save** copies the MP4 anywhere (filename auto-derived from the prompt), **Open Folder** reveals it on disk, **Delete** removes it after a confirm dialog. The whole flow — prompt → generate → preview → save — never touches a terminal.\n\n> Dark-mode by default, frameless custom titlebar, resizable panels (persisted to `localStorage`), inspired by ChatGPT Desktop \u002F Cursor \u002F LM Studio.\n\n---\n\n## Install and use\n\nNo Python, no command line, no setup — it works like a normal app.\n\n**1. Download** the installer for your system from the [**Releases**](..\u002F..\u002Freleases) page:\n\n- **Windows** → `EasyAIVid_x64-setup.exe` (or the `.msi`)\n- **macOS** → `EasyAIVid.dmg`\n\n**2. Install** — run the installer and follow the prompts (on Windows, if SmartScreen warns about an unsigned app, choose *More info → Run anyway*).\n\n**3. Launch.** The first time you open it, EasyAIVid sets everything up automatically — checks your hardware, then downloads its runtime and the AI model:\n\n```\nChecking system…   ✓ OS  ✓ CPU  ✓ Memory  ✓ Storage  ✓ GPU\nInstalling…        ✓ Python  ✓ FFmpeg  ✓ Runtime\nDownloading Model… ████████████░░░░  → ✓ Ready\n```\n\n> ⏳ First-run setup downloads several gigabytes (one time) and can take a while depending on your connection. After that, launches are instant.\n\n**4. Make a video.** Type a prompt (e.g. *\"a cinematic astronaut walking on Mars at sunset\"*), pick a resolution \u002F duration, and click **Generate Video**. Watch the live progress, then **Save** the MP4 or **Open Folder**.\n\n### What you need\n\n| | Minimum | Recommended |\n|---|---|---|\n| OS | Windows 10\u002F11 · macOS 11+ | latest |\n| Disk | ~35 GB free (runtime + model) | 60 GB+ |\n| GPU | runs on most modern GPUs; **low-VRAM laptops work too** (slower) | NVIDIA ≥ 12 GB VRAM or Apple Silicon |\n| Internet | required for first-run download only | — |\n\nNo supported GPU? It still runs a labeled **preview** so you can try the full flow. Got an RTX 50-series and hit a CUDA error? Run [`scripts\u002Ffix-gpu-torch.ps1`](scripts\u002Ffix-gpu-torch.ps1) once. Low VRAM and out-of-memory? Use **480p**, a short duration, and ~20 steps — the app's recommendations already steer you there.\n\n---\n\n## Why this is non-trivial\n\nShipping a local diffusion model to non-technical users means solving a stack of systems problems that a notebook or CLI never has to: bootstrapping a relocatable Python + native deps with **zero** user steps, picking the **right CUDA wheel for the exact GPU**, fitting a multi-billion-parameter video model into whatever VRAM is present, surviving flaky multi-GB downloads, and never leaking a process or a path. EasyAIVid is built around those constraints.\n\n---\n\n## Architecture\n\nThree processes, one strict trust boundary. The WebView can reach the OS **only** through typed Tauri commands; it never spawns processes or reads arbitrary paths.\n\n```\n┌───────────────────────────────────────────────────────────────────────┐\n│  WebView  —  React 18 + TypeScript (Vite · Tailwind · shadcn\u002Fui)        │\n│  Zustand (UI state) · React Query (server state\u002Fpolling)                │\n└───────────────┬───────────────────────────────────────────────────────┘\n        invoke()│  IPC — the ONLY OS bridge (no shell, scoped capabilities)\n┌───────────────▼───────────────────────────────────────────────────────┐\n│  Desktop core  —  Tauri v2 \u002F Rust                                       │\n│   • system        hardware\u002FOS detection (nvidia-smi → WMI →             │\n│                   system_profiler), compute-capability probe            │\n│   • installer     embedded Python (python-build-standalone) + static    │\n│                   FFmpeg; zip\u002Ftar.gz extraction (path-traversal safe)   │\n│   • downloads     resumable HTTP (Range + SHA-256) + model orchestration│\n│   • backend       spawn\u002Fhealth-poll\u002Flifecycle, dynamic free port,       │\n│                   orphan reaper, graceful + crash-safe shutdown          │\n│   • updater       Tauri updater (signed)                                 │\n│   • paths         auto data-drive selection, per-OS app-data            │\n└───────────────┬───────────────────────────────────────────────────────┘\n   spawn + HTTP  │  loopback 127.0.0.1, per-launch bearer token\n┌───────────────▼───────────────────────────────────────────────────────┐\n│  Inference service  —  FastAPI on embedded Python 3.11 (uvicorn)        │\n│   • REST API      \u002Fgenerate \u002Fstatus \u002Fvideo \u002Fcancel \u002Fhistory \u002Fmodel …    │\n│   • queue         FIFO worker, one active job, cooperative cancel        │\n│   • inference     hardware-aware WAN 2.2 loader + segmented generation   │\n│   • media         FFmpeg encode (H.264 + faststart) + thumbnail         │\n│   • db            SQLite (WAL) generation history                        │\n│   • watchdog      self-terminates if the desktop core dies              │\n└───────────────────────────────────────────────────────────────────────┘\n```\n\n**Single generation, end to end:** React submits `POST \u002Fgenerate` → job enqueued (UUID) → worker loads\u002Fkeeps the WAN pipeline resident → denoise (per-step progress) → frames written → FFmpeg encodes H.264 MP4 + JPEG thumbnail → SQLite updated → React polls `GET \u002Fstatus\u002F{id}` → embedded player streams `GET \u002Fvideo\u002F{id}`.\n\nThe three layers keep **the same contract in three places** — Pydantic (`backend\u002Fmodels\u002Fschemas.py`), TypeScript (`src\u002Ftypes\u002Findex.ts`), and Rust structs (`src-tauri\u002Fsrc\u002Fsystem`, `downloads`) — changed together.\n\n---\n\n## Inference engine\n\nThe hard part. `backend\u002Finference\u002Fwan_pipeline.py` adapts a 5–14 B-parameter video diffusion model to whatever GPU is present.\n\n### Hardware-aware load strategy\n\nOn first generation, `_choose_strategy()` sizes the model against detected VRAM and picks a load path (newest GPUs and large models drove every branch here):\n\n| Strategy | When | How |\n|---|---|---|\n| **Resident fp16** | model fits across the GPU(s) | `device_map=\"balanced\"` keeps everything on-GPU — fastest. Benchmarked ~5× faster than offload on dual RTX 3090s. |\n| **Sequential CPU offload** | total VRAM \u003C 16 GB (laptops \u002F small GPUs) | streams layers one at a time — **peaks ~2 GB**, runs on virtually any GPU; slower. |\n| **int8 quantization** | fits in 8-bit but not fp16 | per-expert `bitsandbytes` `load_in_8bit` with `device_map` so weights stream to GPU (no full fp32 load into RAM). |\n| **Model CPU offload \u002F preview fallback** | edge \u002F no-GPU | module-level offload, or a clearly-labeled animated **preview** so the full app flow works without a GPU. |\n\n`AIVS_FORCE_OFFLOAD=1` forces the low-VRAM path on any machine. The VAE always runs with **tiling + slicing** so the decode (the peak-memory step) fits at higher resolutions.\n\n### Long video via segmented generation\n\nWAN denoises the *entire* clip in one tensor, so VRAM and compute scale with total frames — a 30 s clip won't fit on any consumer GPU in one pass. EasyAIVid generates it in **VRAM-sized chunks** and continues each from the previous chunk's last frame:\n\n```\nchunk 1:  WanPipeline (text→video)              ── frames[0 … B]\nchunk 2:  WanImageToVideoPipeline(image=last)   ── continue, drop dup frame\n   …      (shares the loaded weights, no reload)\nstitch →  one MP4\n```\n\nEach chunk is sized by `_max_frames_for()` (a VRAM pixel-frame budget), so length is bounded by **time, not memory** — even a low-VRAM laptop can render up to 30 s. The UI raises the duration cap to 30 s and shows *\"Rendered in N segments — proportionally longer.\"*\n\n### Correctness details\n\n- Frame count snapped to `4k+1` (WAN's VAE temporal compression factor of 4).\n- Dimensions snapped to multiples of 32 so text-to-video and image-to-video chunks are identical sizes and stitch cleanly.\n- `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` to reduce fragmentation; CUDA OOM is caught, translated to an actionable message, and the cache is freed for the next job.\n\n---\n\n## Zero-configuration runtime\n\nFirst launch performs a system check, then installs everything into a per-user app-data directory — no admin rights, no system Python, no PATH edits.\n\n- **Embedded Python** — a relocatable [python-build-standalone](https:\u002F\u002Fgithub.com\u002Fastral-sh\u002Fpython-build-standalone) 3.11 distribution is downloaded, extracted, and its layout normalized by the Rust `installer\u002F`.\n- **FFmpeg \u002F FFprobe** — static builds fetched per-OS; archive extraction is path-traversal-guarded.\n- **GPU-correct PyTorch** — `backend\u002Finstallers\u002Ftorch_installer.py` reads the GPU **compute capability** (`nvidia-smi --query-gpu=compute_cap`) and picks the wheel: **Blackwell (≥ 12.0 \u002F RTX 50-series) → CUDA 12.8 + torch 2.7**, Ampere\u002FAda → CUDA 12.4 + torch 2.5, AMD\u002FLinux → ROCm, Apple → MPS, else CPU. This is what makes an RTX 5070 work without the *\"no kernel image\"* error.\n- **Resilient model download** — `backend\u002Finference\u002Fmodel_manager.py` runs the download in a **killable subprocess** (`hf_download.py`) using HuggingFace `hf_transfer` (parallel-chunked, ~100 MB\u002Fs vs. ~0.2 MB\u002Fs single-stream). A **stall-watchdog** monitors on-disk progress and, if a connection hangs (a known `hf_transfer` failure mode), kills and restarts — resuming from the partial files. Completed files are never re-fetched; the `.ready` marker is written only after full completion, and a size-based manifest avoids hashing 100+ GB.\n\n---\n\n## Process lifecycle & storage\n\n- **Dynamic port** — the backend binds the first free loopback port from 8723 up; the frontend learns it via `get_api_config`. A stuck port can never block startup.\n- **Orphan reaper** — on launch the core kills any leftover backend process (matched by interpreter + `backend.main`) from a previous crash.\n- **Parent watchdog** — the backend is handed the core's PID and **self-terminates within ~2 s if the app disappears** (crash, Task-Manager kill, abrupt close). Combined with a graceful `RunEvent::Exit` handler, no Python \u002F WebView2 \u002F console-host processes linger.\n- **Automatic data drive** — `paths.rs` picks the local fixed drive with the most free space on first run (models + videos are large), persists the choice, and never strands an existing install. Override with `AIVS_DATA_DIR`.\n\nStorage layout (per-OS app-data root):\n\n```\n%APPDATA%\u002FAIVideoStudio        (Windows)  ·  ~\u002FLibrary\u002FApplication Support\u002FAIVideoStudio  (macOS)\n├── runtime\u002F   python\u002F · ffmpeg\u002F · cache\u002F        embedded interpreter + binaries\n├── models\u002F    wan-2.2-ti2v-5b\u002F                   weights + .ready marker + manifest\n├── generated\u002F \u003Cjob-uuid>\u002F output.mp4 + thumbnail.jpg\n├── logs\u002F      backend.log\n└── aivideostudio.sqlite3                          history (WAL)\n```\n\n---\n\n## REST API\n\nLoopback only; every route except `\u002Fhealth` requires the per-launch `X-AIVS-Token` header (media routes also accept it as a `?t=` query param so `\u003Cvideo>`\u002F`\u003Cimg>` can authenticate). Validated by Pydantic at the boundary.\n\n| Method | Route | Purpose |\n|---|---|---|\n| `POST` | `\u002Fgenerate` | enqueue a job → `{ job_id }`. Body: `prompt`, `negative_prompt?`, `duration` (1–30 s), `resolution` (`480p`\u002F`720p`\u002F`1080p`), `aspect_ratio` (`16:9`\u002F`9:16`\u002F`1:1`), `seed?`, `fps` (8–30), `guidance_scale` (1–20), `inference_steps` (10–60) |\n| `GET` | `\u002Fstatus\u002F{id}` | `{ status, progress, stage, eta_seconds, error }` |\n| `GET` | `\u002Fvideo\u002F{id}?t=` | MP4 (Range-enabled) |\n| `GET` | `\u002Fthumbnail\u002F{id}?t=` | JPEG poster |\n| `POST` | `\u002Fcancel\u002F{id}` | cooperatively cancel |\n| `GET` | `\u002Fhistory` · `DELETE \u002Fhistory\u002F{id}` | list \u002F delete generations (+ files) |\n| `GET` | `\u002Fqueue` | `{ active_job_id, queued_job_ids }` |\n| `GET` | `\u002Fmodel` · `POST \u002Fmodel\u002Fdownload` · `POST \u002Fmodel\u002Fverify` · `DELETE \u002Fmodel` | model lifecycle |\n| `GET` | `\u002Fsystem` · `GET \u002Fhealth` | device\u002Fmodel introspection · unauth liveness probe |\n\nOpenAPI explorer at `http:\u002F\u002F127.0.0.1:8723\u002Fdocs` in dev (auth disabled when the token is empty).\n\n---\n\n## Tech stack\n\n| Layer | Technology |\n|---|---|\n| Frontend | React 18 · TypeScript · Vite · Tailwind CSS · shadcn\u002Fui (Radix) · Zustand · TanStack Query · sonner |\n| Desktop core | Tauri v2 · Rust (tokio, reqwest, sysinfo, wmi, zip\u002Ftar\u002Fflate2) |\n| Backend | Python 3.11 · FastAPI · Uvicorn · Pydantic v2 |\n| AI inference | WAN 2.2 (diffusers `WanPipeline` \u002F `WanImageToVideoPipeline`) · PyTorch · transformers · accelerate · bitsandbytes · hf_transfer |\n| Media | FFmpeg \u002F FFprobe (H.264, `+faststart`) |\n| Database | SQLite (WAL) |\n\n---\n\n## GPU support & performance\n\n| GPU | Build selected | Default load path |\n|---|---|---|\n| RTX 50-series (Blackwell, sm_120) | CUDA 12.8 · torch 2.7 | resident \u002F offload by VRAM |\n| RTX 30\u002F40-series (Ampere\u002FAda) | CUDA 12.4 · torch 2.5 | resident fp16 if it fits |\n| ≥ ~24 GB single \u002F multi-GPU | — | `device_map=\"balanced\"` resident |\n| \u003C 16 GB (laptops) | — | sequential offload (~2 GB peak, slower) |\n| Apple Silicon | MPS wheels | resident |\n| No CUDA\u002FMPS | CPU | preview fallback |\n\nThe default model is **WAN 2.2 TI2V-5B** (good quality, fits 24 GB resident \u002F any GPU via offload). **T2V-A14B** is supported on ≥ 48 GB-class GPUs (int8 quantization path for tighter cards) via `AIVS_MODEL_ID` \u002F `AIVS_HF_REPO`.\n\n---\n\n## Quick start\n\n**Prerequisites (development only):** Node 18+, Rust (`rustup`, see [tauri prerequisites](https:\u002F\u002Ftauri.app\u002Fstart\u002Fprerequisites\u002F)), Python 3.11 (bundled in production), platform build tools (WebView2 + MSVC on Windows · Xcode CLT on macOS).\n\n```bash\nnpm install\nnpm run tauri:dev          # or  .\u002Fscripts\u002Fdev.ps1  on Windows\n```\n\nVite serves the UI, Rust compiles the shell, the shell spawns the backend, and the app opens to the first-run **setup** flow (system check → runtime → model). See [docs\u002FDEVELOPMENT.md](docs\u002FDEVELOPMENT.md) for running each layer independently.\n\n**Useful env vars:** `AIVS_DATA_DIR` (storage location) · `AIVS_FORCE_OFFLOAD=1` (low-VRAM mode) · `AIVS_MODEL_ID` \u002F `AIVS_HF_REPO` (model) · `AIVS_API_TOKEN` · `HF_TOKEN` (gated repos).\n\n---\n\n## Build & source protection\n\n```powershell\n.\u002Fscripts\u002Fbuild-release.ps1     # Windows → NSIS .exe + MSI (source-protected)\n.\u002Fscripts\u002Fbuild.sh              # macOS   → .dmg\n```\n\nThe release pipeline ships **no readable source** to customers:\n\n- **Rust core** → native binary.\n- **React UI** → minified, embedded in the binary; sourcemaps off, DevTools disabled.\n- **Python backend** → compiled to **sourceless `.pyc`** (`scripts\u002Fpackage_backend.py`, `optimize=2`) and bundled instead of `.py`; the Rust launcher resolves `.pyc`\u002F`.py` transparently. Swap in **Nuitka** for native `.pyd` if you want stronger protection — the rest of the pipeline is unchanged.\n- **SQLite** → created at runtime in app-data (user data), never shipped.\n\nInstallers stay small (~3 MB) because the runtime, FFmpeg, PyTorch, and weights are fetched on first launch. See [docs\u002FDEPLOYMENT.md](docs\u002FDEPLOYMENT.md) for code signing, notarization, and updater keys.\n\n---\n\n## Security model\n\n- **No shell in the WebView.** The Tauri capability set (`src-tauri\u002Fcapabilities\u002Fdefault.json`) grants only specific window\u002Fdialog\u002Fopener\u002Fupdater permissions; all OS access goes through audited, typed commands.\n- **Loopback + token.** The backend binds `127.0.0.1` only and requires a per-launch random bearer token on every request (`X-AIVS-Token`).\n- **Input validation.** All generation parameters are Pydantic-validated; the frontend is never trusted.\n- **Path-traversal guards.** File serving and folder-open commands resolve and confirm paths stay inside the `generated\u002F` directory.\n- **CSP** restricts the WebView to `self`, the asset protocol, and the loopback backend. Release builds are signed.\n\n---\n\n## Project structure\n\n```\nAIVideoStudio\u002F\n├── src\u002F                      # React + TypeScript frontend\n│   ├── components\u002F  ui\u002F (shadcn) · setup\u002F · studio\u002F · history\u002F · ResourceMonitor · ResizeHandle\n│   ├── pages\u002F       SetupPage · StudioPage\n│   ├── hooks\u002F       useSetup · useGeneration · useHistory · useCapabilities · useResources · useResizable\n│   ├── stores\u002F      Zustand (setup · generation)\n│   ├── services\u002F    tauri.ts (invoke) · api.ts (REST)\n│   └── types\u002F       shared domain types\n│\n├── src-tauri\u002F                # Tauri v2 \u002F Rust desktop core\n│   ├── src\u002F commands\u002F · system\u002F · installer\u002F · downloads\u002F · backend\u002F · updater\u002F · paths.rs · state.rs\n│   ├── capabilities\u002F         least-privilege permission set\n│   └── tauri.conf.json\n│\n├── backend\u002F                  # FastAPI service (embedded Python)\n│   ├── api\u002F         routers + token dependency\n│   ├── inference\u002F   wan_pipeline · model_manager · hf_download\n│   ├── services\u002F    generation queue \u002F worker\n│   ├── installers\u002F  PyTorch wheel selection (compute-cap aware)\n│   ├── models\u002F      Pydantic schemas + enums\n│   ├── database\u002F    SQLite access + schema.sql\n│   ├── utils\u002F       paths · config · logging · ffmpeg\n│   └── main.py\n│\n├── runtime\u002F · models\u002F · generated\u002F    # populated at runtime (git-ignored)\n├── scripts\u002F                            # setup · dev · build · packaging · benchmarks · fix-gpu-torch\n└── docs\u002F   DEVELOPMENT.md · DEPLOYMENT.md\n```\n\n---\n\n## Data model\n\n`generations` (SQLite, WAL): `id, prompt, negative_prompt, duration, resolution, aspect_ratio, seed, fps, guidance_scale, inference_steps, status, progress, stage, eta_seconds, error, output_path, thumbnail_path, created_at, updated_at`. Jobs left `running`\u002F`queued` by a crash are reconciled to `failed` on startup. Full DDL in [backend\u002Fdatabase\u002Fschema.sql](backend\u002Fdatabase\u002Fschema.sql).\n\n---\n\n## Documentation\n\n- [docs\u002FDEVELOPMENT.md](docs\u002FDEVELOPMENT.md) — environment setup, running each layer, code map, conventions.\n- [docs\u002FDEPLOYMENT.md](docs\u002FDEPLOYMENT.md) — building, source protection, signing, notarization, updater keys.\n\n## License\n\nMIT\n","2026-06-11 04:12:10","CREATED_QUERY"]