You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FrameScout — Offline AI Search: v3.1.0 — Codebase Refactor, GPU Inference & Full Source Release
Release date: 2026-09-23
This is primarily a maintenance and consolidation release, but it also ships two real improvements: GPU-accelerated embeddings (via DirectML) and video frame indexing (previously broken). It does not change the AI models or search quality, but it substantially cleans up the codebase, fixes a long list of latent bugs, refactors the Tauri/Rust entry structure, and — importantly — publishes the complete project source tree to the repository.
✨ What's New
⚡ GPU-Accelerated Embeddings (DirectML) — The Python worker now runs on onnxruntime-directml. SigLIP 2 embedding inference is executed on the GPU via the DirectML execution provider on Windows, delivering a substantial speedup over the previous CPU path (most noticeable for video frame extraction and bulk image encoding). This works across NVIDIA / AMD / Intel GPUs with zero CUDA or driver-version juggling. (EasyOCR text recognition still runs on CPU — only the embedding step is GPU-accelerated.)
🎬 Video Frame Indexing (previously broken) — _handle_batch now detects video files and extracts frames via extract_video_frames; images still go through load_image_pil. Each path yields multiple timestamped FrameResult entries, so videos finally enter the index instead of being silently dropped.
🗄️ Multi-Frame Data Model — The frame_vectors table primary key changed from path to (path, timestamp). Old single-frame databases are migrated automatically inside a transactional block (rename → create → copy → drop, with rollback on any failure).
📦 Complete Source Tree Published — The full Rust, Vue, and Python source — including the model_code module, all build configs (Cargo.toml, ai_worker.spec), docs (BUILD.md, READMEs), and the bundled protoc.exe — is now committed. This is a true full-source Community Edition.
🔀 Smart Folder Result Consistency — applySmartFolder now reuses normalizeResults, so smart-folder results behave identically to manual search (videos get a 25s load timeout with "File Unavailable" fallback; low-confidence images auto-collapse). Applying a folder from "Show All" mode no longer leaves the pagination bar stuck.
🐛 Bug Fixes
"Show All" mode exited on page turn — While in "Show All" (full-file preview) mode, turning pages previously triggered a background re-search/refresh that reset the view back to paginated browsing, kicking you out of Show All. The view mode is now preserved across page turns.
Worker crash on startup with missing models — main.py now catches FileNotFoundError and prints a clean FATAL message then exits, instead of dumping a raw traceback.
Latent UnboundLocalError in the worker loop — task_id is now initialized at the top of the request loop, fixing a crash when an exception occurred during recv()/ParseFromString() (where req was never assigned).
OCR config never honored — The long-dormant single_file_ocr_config proto field (field 6) is now actually read; when set it drives per-file enable_ocr / languages, otherwise behavior is unchanged (always OCR, default en).
Stale data & inconsistent dedup — Search dedup was verified to preserve each file's highest-scoring frame and its timestamp (used for instant seek); clarifying comments added. index_cmd.rs now refreshes vector_json on ON CONFLICT(path, timestamp) to avoid mixing a first-frame vector with a last-frame OCR text.
HTML injection in results — escapeHtml now escapes &, ", ' in addition to </>, preventing path text from breaking the v-html rendering.
HMR event-loss — Four Vue composables (useSearch, useEngineStatus, useLicense, useScanner) now reset lifecycleBound in onUnmounted, so engine-status / scan-progress / key listeners re-bind correctly after hot reload.
Redundant license refresh — Removed a duplicate emit('refresh-license') + checkLicense() in LicenseModal and the corresponding binding in App.vue.
🔧 Under the Hood
Tauri entry refactor — lib.rs now owns all 7 module declarations and the Tauri Builder (run() carries #[cfg_attr(mobile, tauri::mobile_entry_point)], Apache header preserved); main.rs is a 4-line shell calling framescout_ui_lib::run().
Dependency & metadata cleanup — Removed unused tauri-plugin-opener (no frontend calls, no capability); Cargo.toml version 3.0.0 → 3.1.0 (aligned with tauri.conf.json); keywords trimmed to the crates.io 5-item limit.
Zero-warning builds — Fixed several cfg-gated dead-code/unused-import warnings (guard.rs, verifier.rs, index_cmd.rs, storage/mod.rs); merged the duplicate PaginatedResult struct into PagedResponse; removed a deprecated _state param from ping_engine.
Python worker hygiene — Replaced __import__("PIL") hacks with a proper from PIL import Image; removed an unused EMBEDDING_DIM import; cleaned leftover comments and a duplicate docstring in video_extractor.py; added a cap.isOpened() guard.
Packaging robustness — ai_worker.spec no longer hardcodes an absolute .venv path (now resolved relative to SPECPATH), so the build works after moving the venv or the repo. requirements.txt is now pinned to a single ONNX Runtime package (onnxruntime-directml) — the CPU onnxruntime and DirectML packages both provide the onnxruntime module and overwrite each other, so they must never be co-installed. The DirectML build also bundles DirectML.dll for the GPU path.
Type safety / IDE — Added pyrightconfig.json (correct filename for basedpyright) plus a hand-written search_pb2.pyi stub and type annotations across server.py / config.py / engines / video_extractor.py, bringing the whole inference-worker to 0 errors / 0 warnings under basedpyright.
Frame-extraction guards — Added MAX_FRAMES_PER_VIDEO and MAX_FRAMES_PER_ENCODING_BATCH (config + constants) so long videos are bounded in time/memory and large batches don't overflow GPU memory; batching also isolates a single-file failure from the rest of the batch.
Extension whitelist single-source — IMAGE_EXTENSIONS / VIDEO_EXTENSIONS now live in Rust constants.rs and Python video_extractor.py with a cross-language "keep in sync" note; scan_folder uses the constant instead of inline literals.
📋 Upgrade Notes
No model re-download required — SigLIP 2 ONNX models are unchanged since v3.0.1; existing models/ remain valid.
Existing databases auto-migrate — Single-frame frame_vectors tables are upgraded to the composite (path, timestamp) key on first launch; your previously indexed images are preserved. (Videos were never stored before, so there is no legacy video data to migrate.)
If you build from source: re-run pyinstaller per BUILD.md with onnxruntime-directml active (so DirectML.dll is bundled), then copy the completedist/ai_worker (including its _internal/ folder) into src-tauri/bin/ai_worker — the onedir layout requires _internal/python312.dll to be present.
The main Tauri application executable stays lightweight (~5 MB); the Python AI worker is bundled as a separate sidecar. Embedding inference now runs on your GPU via DirectML — noticeably faster than before — while OCR remains on CPU.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
FrameScout — Offline AI Search: v3.1.0 — Codebase Refactor, GPU Inference & Full Source Release
Release date: 2026-09-23
This is primarily a maintenance and consolidation release, but it also ships two real improvements: GPU-accelerated embeddings (via DirectML) and video frame indexing (previously broken). It does not change the AI models or search quality, but it substantially cleans up the codebase, fixes a long list of latent bugs, refactors the Tauri/Rust entry structure, and — importantly — publishes the complete project source tree to the repository.
✨ What's New
onnxruntime-directml. SigLIP 2 embedding inference is executed on the GPU via the DirectML execution provider on Windows, delivering a substantial speedup over the previous CPU path (most noticeable for video frame extraction and bulk image encoding). This works across NVIDIA / AMD / Intel GPUs with zero CUDA or driver-version juggling. (EasyOCR text recognition still runs on CPU — only the embedding step is GPU-accelerated.)_handle_batchnow detects video files and extracts frames viaextract_video_frames; images still go throughload_image_pil. Each path yields multiple timestampedFrameResultentries, so videos finally enter the index instead of being silently dropped.frame_vectorstable primary key changed frompathto(path, timestamp). Old single-frame databases are migrated automatically inside a transactional block (rename → create → copy → drop, with rollback on any failure).model_codemodule, all build configs (Cargo.toml,ai_worker.spec), docs (BUILD.md,READMEs), and the bundledprotoc.exe— is now committed. This is a true full-source Community Edition.applySmartFoldernow reusesnormalizeResults, so smart-folder results behave identically to manual search (videos get a 25s load timeout with "File Unavailable" fallback; low-confidence images auto-collapse). Applying a folder from "Show All" mode no longer leaves the pagination bar stuck.🐛 Bug Fixes
main.pynow catchesFileNotFoundErrorand prints a cleanFATALmessage then exits, instead of dumping a raw traceback.UnboundLocalErrorin the worker loop —task_idis now initialized at the top of the request loop, fixing a crash when an exception occurred duringrecv()/ParseFromString()(wherereqwas never assigned).single_file_ocr_configproto field (field 6) is now actually read; when set it drives per-fileenable_ocr/languages, otherwise behavior is unchanged (always OCR, defaulten).timestamp(used for instant seek); clarifying comments added.index_cmd.rsnow refreshesvector_jsononON CONFLICT(path, timestamp)to avoid mixing a first-frame vector with a last-frame OCR text.escapeHtmlnow escapes&,",'in addition to</>, preventing path text from breaking thev-htmlrendering.useSearch,useEngineStatus,useLicense,useScanner) now resetlifecycleBoundinonUnmounted, so engine-status / scan-progress / key listeners re-bind correctly after hot reload.emit('refresh-license')+checkLicense()inLicenseModaland the corresponding binding inApp.vue.🔧 Under the Hood
lib.rsnow owns all 7 module declarations and the TauriBuilder(run()carries#[cfg_attr(mobile, tauri::mobile_entry_point)], Apache header preserved);main.rsis a 4-line shell callingframescout_ui_lib::run().tauri-plugin-opener(no frontend calls, no capability);Cargo.tomlversion3.0.0 → 3.1.0(aligned withtauri.conf.json);keywordstrimmed to the crates.io 5-item limit.cfg-gated dead-code/unused-import warnings (guard.rs,verifier.rs,index_cmd.rs,storage/mod.rs); merged the duplicatePaginatedResultstruct intoPagedResponse; removed a deprecated_stateparam fromping_engine.__import__("PIL")hacks with a properfrom PIL import Image; removed an unusedEMBEDDING_DIMimport; cleaned leftover comments and a duplicate docstring invideo_extractor.py; added acap.isOpened()guard.ai_worker.specno longer hardcodes an absolute.venvpath (now resolved relative toSPECPATH), so the build works after moving the venv or the repo.requirements.txtis now pinned to a single ONNX Runtime package (onnxruntime-directml) — the CPUonnxruntimeand DirectML packages both provide theonnxruntimemodule and overwrite each other, so they must never be co-installed. The DirectML build also bundlesDirectML.dllfor the GPU path.pyrightconfig.json(correct filename for basedpyright) plus a hand-writtensearch_pb2.pyistub and type annotations acrossserver.py/config.py/engines/video_extractor.py, bringing the wholeinference-workerto 0 errors / 0 warnings under basedpyright.MAX_FRAMES_PER_VIDEOandMAX_FRAMES_PER_ENCODING_BATCH(config + constants) so long videos are bounded in time/memory and large batches don't overflow GPU memory; batching also isolates a single-file failure from the rest of the batch.IMAGE_EXTENSIONS/VIDEO_EXTENSIONSnow live in Rustconstants.rsand Pythonvideo_extractor.pywith a cross-language "keep in sync" note;scan_folderuses the constant instead of inline literals.📋 Upgrade Notes
models/remain valid.frame_vectorstables are upgraded to the composite(path, timestamp)key on first launch; your previously indexed images are preserved. (Videos were never stored before, so there is no legacy video data to migrate.)pyinstallerperBUILD.mdwithonnxruntime-directmlactive (soDirectML.dllis bundled), then copy the completedist/ai_worker(including its_internal/folder) intosrc-tauri/bin/ai_worker— the onedir layout requires_internal/python312.dllto be present.🙏 Tech Stack
Google SigLIP 2 · ONNX Runtime (DirectML / GPU) · EasyOCR (CPU) · Tauri · ZeroMQ · Protobuf · Rust + Vue 3
Full Changelog: v3.0.3...v3.1.0
This discussion was created from the release FrameScout — Offline AI Search: v3.1.0 — Codebase Refactor, GPU Inference & Full Source Release.
All reactions