Skip to content
highercomvePublic

About

GhostReel: search inside your video folders

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GhostReel

GhostReel

Search inside your videos — and cut them into a story — locally.

Website · Guide · Download

GhostReel watches folders of video footage and makes everything in them searchable: it transcribes the speech with Whisper, describes keyframes with a local vision model, and embeds both so you can search by what was said, shown or written on screen and jump straight to that moment. Organise footage in projects, then chat with the model to script a video from your clips, preview the cut, and hand it to Premiere Pro as an FCP XML timeline (via OpenTimelineIO) — or export an MP4 to send as a quick demo.

It runs fully standalone — models run on your GPU (CUDA or Vulkan on Linux/Windows, Metal on macOS) inside the app, nothing else to install — on Windows, Linux and macOS. If you already run model servers, it can use them instead so the same GPU never holds a model twice: highllama for vision and embeddings and GhostPen for transcription.

Built with Tauri v2 (Rust backend + React/TypeScript frontend) and a ghostreel CLI sharing the same core.

Design docs: .agents/plan.md (decisions, runtime profiles, milestones), .agents/TODO.md (build status).


Features

  • Folder indexing: add folders (whole tree, recursively) to a project; GhostReel finds every video and identifies it by content hash (moved or duplicated files are indexed once). Indexing is a resumable queue with progress and ETA; ghostreel index --watch keeps indexing new footage as it arrives.
  • Transcripts with timestamps: Whisper (tiny → large-v3-turbo), language detection, click any line to jump there.
  • Keyframes described by a vision model: scene changes plus a frame at least every N seconds (configurable), each described (subject, setting, shot, on-screen text). Scene detection decodes on the GPU. Frame descriptions run with configurable sampling resolution (512px–1280px, defaulting to 768px) and concurrent multi-sequence batching in the local helper, achieving sub-second describe times (~0.75s per frame).
  • Camera steadiness & shake analysis: measures camera movement and handheld tremor asynchronously, identifying shaky stretches so the script generation and editor cut around them.
  • Hybrid search: keywords (SQLite FTS5) + meaning (embeddinggemma vectors, sqlite-vec), merged into moments you can play from the right second.
  • Script chat: ask for "a 60 second promo about the neighbourhood using the interviews" — the model explores the footage with tools (search, transcripts, keyframes), drafts beats with clips and voice-over, and revises from your feedback. Every clip is checked against the footage; talking clips keep whole sentences with a breath before the cut; the editor instructions are editable in Settings.
  • Reference edits: mark a finished video (e.g. an edit someone already made by hand) as a reference — the chat studies its length, pacing and structure but never uses it as footage. Or just remove a video from a project's library.
  • Timeline preview & export: render a low-res preview of any script version, burn titles and narration, export MP4, FCP 7 XML (Premiere Pro) or .otio.
  • Model manager: download and choose models per capability (speech, vision & chat, embeddings) and where each runs: Auto, This computer or Server.
  • CLI for everything: index, search, transcripts, scripts, exports and models from a terminal (ghostreel --help).

Install

Download the latest build from Releases:

Platform File Notes
Windows 10/11 GhostReel_x.y.z_x64-setup.exe NVIDIA driver 570+ for GPU (CUDA 12.8 runtime is bundled)
Linux .AppImage NVIDIA driver 570+ for GPU
Windows 10/11 · Vulkan GhostReel_x.y.z_x64_vulkan-setup.exe AMD, Intel or NVIDIA GPU with a Vulkan 1.2-capable driver
Linux · Vulkan GhostReel_x.y.z_amd64_vulkan.AppImage AMD, Intel or NVIDIA GPU with a Vulkan 1.2-capable driver
macOS 14+ · Apple Silicon GhostReel_x.y.z_aarch64.dmg MLX vision/chat, Metal Whisper, self-signed
macOS 12+ · Intel GhostReel_x.y.z_x64.dmg llama.cpp / Whisper with Metal, self-signed
CLI only ghostreel-cli-*.zip / .tar.gz same helpers and ffmpeg, no desktop app

On macOS, open the DMG and drag GhostReel to Applications. The app is self-signed and is not notarized by Apple: after the first launch is blocked, open System Settings → Privacy & Security → Open Anyway. Subsequent releases reuse the same signing certificate.

ffmpeg/ffprobe are bundled. On first start open Models and download what you need:

Capability Default model Size Alternatives
Speech Whisper large-v3-turbo 1.6 GB small (488 MB), quantized turbo q8_0 / q5_0 (874 / 574 MB), .en variants
Frame descriptions & script chat Qwen3.5 9B Native: 6.9 GB with projector; Apple Silicon: pinned MLX bundle Qwen2.5-VL 7B/3B, Gemma 3 4B
Search embeddings embeddinggemma-300M Q8_0 334 MB fixed, so indexes stay compatible between computers

GhostReel also picks up models you already have (GhostPen's whisper models, LM Studio) instead of downloading them again. The whole setup fits an 8 GB GPU (RTX 3070).

Frame descriptions and the script chat are configured separately (Models page, or ghostreel config set vision.* / chat_model.*): describing a keyframe is a short prompt plus one image, while the chat needs room for search results and a whole draft. Defaults: 8k context for descriptions, 32k for the chat, both with a q4_0 KV cache and flash attention, which is what fits a 32k window on an 8 GB card (an f16 cache would need 4 GB for the cache alone).

The llama.cpp helper fits GPU layers and MoE expert placement to available device memory, leaving headroom for the vision projector. Settings exposes flash attention (auto, on, off) and chat_model.cpu_moe_layers: 0 keeps automatic fitting; a positive value keeps the first N blocks' expert weights in system RAM. This only affects MoE models, and trades generation speed for GPU memory; it does not turn a dense model into an MoE. These controls do not apply to MLX.

The helper caps the context at the model's trained limit and retries smaller windows on allocation failure. Script chat uses that actual loaded window, counts prompt tokens before generation, and drops old exchanges while retaining the rules, current request and latest evidence to reserve space for a complete draft. Text requests reuse the common prompt prefix, with a bounded snapshot of recurrent/SWA state so Qwen3.5 can reuse it too. Image requests and incompatible cache states rebuild safely.

python3 scripts/smoke-llm-cache.py --helper <ghostreel-llm> --model <model.gguf> --mmproj <projector.gguf> [--cpu] checks cached results against fresh inference without downloading models. A local Qwen3.5 9B CPU check took 1.85 s for a growing conversation versus 14.38 s fresh (322 reused tokens, one generated token, excluding model load). This measures prompt reuse rather than whole-script or GPU throughput.

For NVIDIA hardware validation, build the CUDA helper with GHOSTREEL_GPU=cuda bash scripts/build-helpers.sh llm, then run:

python3 scripts/smoke-llm-nvidia.py \
  --helper target/release/ghostreel-llm \
  --model /path/to/dense-model.gguf --mmproj /path/to/dense-projector.gguf \
  --moe-model /path/to/moe-model.gguf --moe-mmproj /path/to/moe-projector.gguf \
  --cpu-moe 32 --output-dir /tmp/ghostreel-nvidia-validation

The MoE arguments are optional. The check verifies real CUDA memory use, attention and expert placement from runtime logs, a 32k context, and a structured script response. The helper reports the selected expert override patterns in its ready message. Logs and report.json remain in the output directory for review; the check does not change the app's model settings.

Validated on Linux with an RTX 4070 (12 GB), driver 615.78.08 and a CUDA 13.4 release build:

Model / mode Loaded context Helper GPU memory Result
Qwen3.5 9B, automatic flash attention + q4 KV 32k 7256 MiB 11,095-token prompt and structured script passed
Qwen3.5 9B, flash attention off + f16 KV 8k 7224 MiB Structured script passed
Ornith 1.5 35B A3B, automatic expert placement 32k 10512 MiB Expert tensors in host RAM; structured script passed
Ornith 1.5 35B A3B, 32 expert blocks in RAM 32k 7164 MiB Explicit host placement and structured script passed

For an apples-to-apples decode benchmark with the same AgentWorld GGUF and expert split:

python3 scripts/bench-llm-speed.py --helper target/release/ghostreel-llm \
  --model /path/to/Qwen-AgentWorld-35B-A3B-UD-Q4_K_XL.gguf \
  --mmproj /path/to/mmproj-Qwen-AgentWorld-35B-A3B-BF16.gguf \
  --context 131072 --cpu-moe 32 --output-dir /tmp/ghostreel-agentworld-speed

On the same RTX 4070, a 54-token prompt and 256-token response measured 43.2 → 55.3 decode tokens/s at an allocated 128k context, with 32 of 40 expert blocks in host RAM. The comparison holds the model, prompt, quantization and placement constant, changing the old 4-thread, per-decode pool/mmap settings to 8 threads, a resident thread pool and non-mapped host experts. At an allocated 16k context with 26 host expert blocks, it measured 39.6 → 57.6 tokens/s. These are short-prompt, single-run measurements, excluding model load and prompt processing; a full 128k input or a multi-pass script takes longer. The benchmark writes both reports and runtime logs. The UI's expert slider shows the share of AgentWorld's 40 blocks in RAM; a value of 0 lets automatic fitting choose placement.

A real Greet Mag script-agent trial used an initial 28,275-token prompt, 128k allocated context, 32 host expert blocks, and thinking disabled. Six model calls averaged 44.7 decode tokens/s (1,839 generated tokens); the full turn took 87.0 seconds, excluding model load. Initial prefill took 39.7 seconds; cached later prefills took 0.6–2.5 seconds. The saved 57.6-second draft passed structural validation after repairs. This checks the agent pipeline, not editorial quality.

Local script agents default to 30 research tool calls. Planning replies do not use this budget; a separate model-call ceiling prevents endless retries. Proposed shots are checked with get_video, spoken quotes with get_transcript, and the model reviews the resulting evidence before finalizing. Revisions introducing new ranges are checked again within the same budget. Missing coverage after repairs is flagged as incomplete research. Adjust the limit in Settings or chat_model.max_tool_rounds; 0 uses script.local_tool_rounds / script.roomy_tool_rounds.

New chat answers persist the actual model name, token count, decode rate and whole-turn time. When a server supplies token usage but no decode time, the footer explicitly labels its rate as overall, including prompt processing and request overhead. Older answers and providers without counters show that metrics are unavailable.

For a larger CUDA cache check, add --context 32768 --notes 1800 to smoke-llm-cache.py. The growing 9063-token prompt reused 9022 tokens and took 0.096 s versus 2.949 s fresh, with matching output. This is a one-token-response prompt-reuse check, excluding model load, not a whole-script throughput claim. Prefer automatic placement; explicit expert offload frees more VRAM at the cost of additional work in system RAM.


Search selected footage

In a project's Library, check videos to search just those files. Shift-click a checkbox for a range, Ctrl/⌘-click rows to add or remove files, or turn on Select videos and drag a rectangle across rows. The header checkbox selects all visible rows; category filters can hide selected rows, and the selection count makes that explicit. Clear selection returns to the folder or whole-project scope. Normal row clicks still open previews outside selection mode.

Choose a watched folder under Search in, or click Search here beside its path, to restrict both the library and search to that folder's indexed videos, including its indexed subfolders. Changing folders clears the file selection. Keyword and semantic search both honor these scopes.

Two ways to run the models

Standalone (default on a fresh machine) Shared servers
Speech ghostreel-asr helper (whisper.cpp, CUDA) GhostPen STT server :8771
Vision & chat ghostreel-llm helper (llama.cpp, CUDA) highllama :8089 (OpenAI-compatible)
Embeddings ghostreel-llm helper highllama embeddings :8091

Each capability is set to Auto, This computer or Server on the Models page (or ghostreel config set vision.backend server). Auto uses a server only when it is reachable and capable (vision support, timestamped transcripts, the exact embedding model) and falls back to the local helper otherwise. ghostreel doctor shows what would be used and why.

Any OpenAI-compatible vision server works for frame descriptions and chat (URL, model and API key are configurable).


Using a coding-agent CLI instead of a model

No GPU headroom for a vision model? Point a capability at a coding-agent CLI you already have — claude, agy or opencode — on the Models page (backend CLI agent) or from the terminal:

ghostreel config set vision.backend cli        # frame descriptions through a CLI
ghostreel config set vision.cli.tool claude
ghostreel config set chat_model.backend local  # …while the script chat stays local
ghostreel config test-cli vision               # one call, prints what it answered

The two capabilities are independent: claude for the descriptions and a local model for the script, or the other way round, or a server for one and a CLI for the other.

calls what it costs
Frame descriptions one per keyframe claude bills per call (~$0.04 a frame, so ~$60 for a 1500-frame library); agy and opencode spend their own quota
Script chat a handful per script cents with claude

Auto never picks a CLI — it spends money or quota, so it has to be chosen explicitly. ghostreel doctor lists which CLIs it can find, and the Models page has a Test one call button that describes a generated image so you can check the setup (and the latency) before indexing.


Usage

  1. New project → name, frame rate and resolution (used for timeline exports).
  2. Add folder → pick the footage folder; Index now. Progress shows in the project and on the Activity page. Stop any time; indexing again resumes where it left off.
  3. Search the project: "people talking about the park", "for sale sign", on-screen text. Click a hit to play from that moment; click a video to see its keyframes and transcript.
  4. Scripts tab → describe the video you want. Revise in the chat ("slower, let people finish talking") or edit beats and clips by hand; each change is a new version.
  5. Render preview to watch the cut, then Export for Premiere (FCP XML), Export .otio or Export MP4.

In Premiere Pro: File → Import the .xml; the sequence links to your original files.

CLI

ghostreel doctor                                   # GPU, ffmpeg, models, servers
ghostreel project create "Greet Mag" --fps 30 --width 3840 --height 2160
ghostreel folder add ~/Videos/shoot -p "Greet Mag"
ghostreel index -p "Greet Mag"                     # --watch to keep indexing new files
ghostreel search -p "Greet Mag" "neighbours talking about the community"
ghostreel script chat --project "Greet Mag" "a 60 second promo using the interviews"
ghostreel script export <script-id> --format fcp_xml -o promo.xml
ghostreel models list | download <id> | use <id>
ghostreel config set frames.max_interval_s 5 && ghostreel index -p "Greet Mag" --redo frames

For AI agents (MCP)

ghostreel mcp speaks the Model Context Protocol on stdio, so an agent can index footage, search it and build an edit. Register it once:

{
  "mcpServers": {
    "ghostreel": { "command": "ghostreel", "args": ["mcp"] }
  }
}

Or let the agent's own command add it:

Agent Command
Claude Code claude mcp add --scope user ghostreel -- ghostreel mcp
Codex codex mcp add ghostreel -- ghostreel mcp, then set tool_timeout_sec = 1800 under [mcp_servers.ghostreel] (the 60 s default is shorter than index and draft_script take)
Antigravity agy mcp add ghostreel ghostreel mcp
OpenCode opencode mcp add --global ghostreel -- ghostreel mcp

Claude Desktop, Cursor, Gemini CLI and most other clients take the JSON above in their own settings file; give the full path to ghostreel where the client doesn't read your shell's PATH. Add "env": {"GHOSTREEL_HOME": "/path"} to work on a different library. The guide has the details for each.

Tool What it does
doctor ffmpeg, GPU, database, models and which AI backends would be used
list_projects · create_project · delete_project projects with their indexing progress; create one (fps/size = export timeline); delete one (video files are never touched)
add_folder watch a footage folder (whole tree; editor render/cache folders are skipped)
index · index_status run pending indexing for up to max_seconds (resumable — call again while work is pending), or just report progress
list_videos one row per video: category (interview, broll, drone…), camera style, speech, keyframes and what it shows
search hybrid keyword + meaning search; returns moments with video_id and timestamps
get_transcript timestamped speech of one or more videos; off-mic (interviewer) lines marked [off], or dropped with speech_only
get_video · get_frames each keyframe's description and on-screen text; the keyframe pictures themselves (JPEG)
get_steadiness camera shake in a stretch of a video, a verdict, and the nearest steady stretch when it is shaky
list_scripts · get_script script versions of a project
draft_script let GhostReel's editing model draft or revise a script from the footage (minutes)
save_script save your own script JSON as a new version (dry_run only checks it); returns the issues found
preview_script render a preview MP4 of a script version and return its path
export_script write fcp_xml (Premiere Pro) or otio
get_settings · set_settings read or change the model settings (same keys as ghostreel config set)
set_video_role take a video out of the library (removed) or keep it as a reference edit

Typical run: create_project → add_folder → index until nothing is pending → list_videos / search / get_transcript / get_video / get_frames to learn the footage → draft_script or save_script → preview_script / export_script. The agent can write the edit itself without GhostReel's chat model, checking its picks with get_steadiness. Clips must lie inside footage the tools reported: save_script returns an error issue per clip that doesn't and a warning per clip on a shaky stretch (and still saves the version, so it can be fixed; dry_run checks without saving).

A script is plain JSON:

{
  "title": "NW Hills promo",
  "target_duration_s": 60,
  "beats": [
    {
      "id": "hook",
      "purpose": "Open with a resident on why the neighbourhood is special",
      "narration": "",
      "on_screen_text": "Northwest Hills",
      "clips": [{ "video_id": 8, "in_s": 12.4, "out_s": 28.9, "audio": "source" }]
    }
  ]
}

audio is source (someone speaking) or mute (b-roll under narration). narration is the voice-over for that beat — about 2.5 words per second of its clips.

A beat can also have a bed, { "video_id": 3, "in_s": 40.0, "out_s": 52.5 }, whose audio runs under the whole beat: its clips should be mute and add up to the bed's length, so an interview answer plays over b-roll (a J-cut).


Configuration & privacy

Everything lives in one folder: ~/.ghostreel/ (%USERPROFILE%\.ghostreel\ on Windows).

Path What
config.toml backends, models, frame interval, chat instructions
ghostreel.db projects, transcripts, frame descriptions, vectors, scripts (SQLite)
frames/ keyframe JPEGs (thumbnails, descriptions)
proxies/, previews/ preview renders (safe to delete; regenerated)
models/ downloaded models

GHOSTREEL_HOME moves the whole folder. Your videos are only read, never modified or copied.

  • In standalone mode nothing leaves your machine.
  • With servers, frames, transcripts and prompts go to the URLs you configured — point them at machines you trust. API keys are stored in plaintext in config.toml.

Updates

GhostReel checks for new releases from github.com/highercomve/GhostReel on the Status page. Click Check for updates — if a newer version is available you will see the release notes and a Download and install button with a progress bar. Once the download finishes, Restart to finish applies the update immediately.

Updates are signed with a minisign key; the public key is embedded in the app and the installer. The updater manifest (latest.json) is published to the GitHub release by CI and consumed over HTTPS — no external update server required.


Build from source

Requirements: Rust (stable), Node.js 22, the Tauri prerequisites and ffmpeg on PATH for development. For GPU helpers, install the CUDA Toolkit 12.x or the Vulkan SDK (including glslc and SPIRV-Headers); Windows also needs MSVC. On macOS, install the Xcode command-line tools (xcode-select --install) and CMake (brew install cmake); native helpers use Metal automatically. On Apple Silicon, also install Python 3.12 for building the frozen MLX helper: GHOSTREEL_MLX_PYTHON=python3.12 scripts/build-mlx.sh. Installers bundle Python and MLX; users do not need either installed.

npm install
cargo test --workspace                 # core tests (no GPU, no servers)
cargo run -p ghostreel-cli -- doctor
npx tauri dev                          # desktop app with hot reload

Release bundles (see AGENTS.md → Packaging):

scripts/build-helpers.sh               # ghostreel-asr + ghostreel-llm (CUDA if the toolkit is present)
npm run bundle:linux                   # AppImage (+ CLI tarball)
npm run bundle:windows                 # NSIS installer (on Windows)
npm run bundle:macos                   # .app + DMG (on macOS; ad-hoc signed locally)

CI builds CUDA and Vulkan variants for Linux/Windows, plus native Apple Silicon / Intel macOS installers on every v* tag (.github/workflows/release.yml). Vulkan assets include _vulkan in their filenames. Updates preserve the installed backend using separate linux-x86_64-vulkan / windows-x86_64-vulkan entries in latest.json; existing CUDA and macOS updater targets stay compatible.

Master builds warm separate Rust and compiled-helper caches for each OS/backend. Exact helper cache hits skip native compilation and Vulkan SDK setup. Cache keys include helper sources, vendored bindings, external locked dependencies, workspace build settings, Rust/C++ toolchains, runner image, and GPU SDK version; workspace version bumps reuse the helpers. SDK downloads and npm dependencies are cached too. Tags restore the default branch's caches; master saves them for reuse across releases.

To build Vulkan helpers locally, set up the Vulkan SDK environment, then run GHOSTREEL_GPU=vulkan scripts/build-helpers.sh before the normal bundle command. For a Vulkan app that preserves its backend on update, also set GHOSTREEL_UPDATER_TARGET=linux-x86_64-vulkan (or windows-x86_64-vulkan) when bundling.

Apple Silicon models

Apple Silicon uses MLX for local frame descriptions and script chat. The Models page selects MLX versions of Qwen2.5-VL 3B/7B, Gemma 3 4B, and Qwen3.5 9B. Downloads are pinned to a Hugging Face revision and include every weight shard, tokenizer, and processor configuration. Interrupted downloads resume, and a model is only marked installed once every file is complete. Qwen3.5 9B is the default for both frame descriptions and script chat on every platform. Apple Silicon uses its pinned MLX bundle; Linux, Windows and Intel Macs use the GGUF model and projector. The old Bonsai selection on Apple Silicon migrates to Qwen3.5 9B because Bonsai has no matching MLX release in this catalog.

Whisper and embeddinggemma retain their existing model files and helpers. Intel Macs, Linux, and Windows retain GGUF for vision/chat. MLX processes frames serially to bound memory usage; structured JSON replies use a grammar and skip free-form reasoning. MLX selects its own attention implementation, so llama.cpp's flash-attention setting is hidden on Apple Silicon. MLX requires macOS 14+; the Intel installer still supports macOS 12+.

CI freezes scripts/ghostreel_mlx.py with Python 3.12, includes Metal shaders and processor implementations, and runs the standalone helper's Metal smoke test and a real-model vision/chat integration test. To test it locally after building: target/release/ghostreel-mlx --self-test. There is no claim of a speed advantage until frame-description and chat workloads have been benchmarked on the same Mac.

macOS release signing

Like Oriel, GhostReel uses a persistent self-signed code-signing certificate. Create it once on any host with OpenSSL, outside the repository, and save both files securely:

scripts/macos-signing.sh create ~/.config/ghostreel/keys
openssl base64 -A -in ~/.config/ghostreel/keys/ghostreel-codesign.p12 | gh secret set MACOS_CERTIFICATE
gh secret set MACOS_CERT_PASSWORD < ~/.config/ghostreel/keys/ghostreel-codesign.password

Run the gh commands from this repository. Reuse these secrets for every release; rotating the certificate changes the app's signing identity. CI imports it into a temporary keychain, signs the app, its sidecars and the DMG with Tauri, verifies signatures and removes the keychain even on failure. Release builds require both macOS secrets; master cache builds need neither. The existing TAURI_SIGNING_PRIVATE_KEY still signs updater payloads separately. No Apple account or notarization credentials are required. For a local build with this identity, import the .p12 into your login keychain and set APPLE_SIGNING_IDENTITY to its SHA-1 fingerprint before npm run bundle:macos (see Tauri signing).

macOS FFmpeg/ffprobe are pinned standalone builds from Martin Riedl (Apple Silicon) and Evermeet (Intel), verified by SHA-256. They include the software encoder, drawtext and subtitle filters used by previews. Their upstream license and source notice are included in the app's resources.


Project layout

crates/ghostreel-core   config, DB (SQLite + FTS5 + sqlite-vec), probing, indexing, frames,
                        search, models, script chat, preview, OTIO export
crates/ghostreel-cli    the `ghostreel` binary
crates/ghostreel-asr    whisper helper process (CUDA)
crates/ghostreel-llm    vision / chat / embeddings helper process (CUDA)
src-tauri/              desktop app: commands, task queue, local media server
src/                    React + TypeScript frontend
scripts/                helper builds, sidecar fetch/staging, packaging
.agents/                plan and TODO

whisper.cpp and llama.cpp run in separate helper processes because their ggml builds can't share one binary.

License

MIT

About

GhostReel: search inside your video folders

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages