Show HN:macOS 上针对每张照片和每帧视频的 AI 搜索
摘要
SCM(Screen Memories)是一款本地优先的 macOS 应用,为任意文件夹中的每张照片和每个视频帧提供 AI 驱动的搜索,支持场景级视频搜索、OCR、基于 Whisper 的对白搜索以及可选的本地 LLM 对话——全部在设备上离线运行。该应用开源,可通过 Homebrew 安装,使用 Bun、Electron 和视觉模型(默认 CLIP)构建。
查看缓存全文
缓存时间: 2026/10/04 14:34
allenv0/SCM
来源:https://github.com/allenv0/SCM
SCM — Screen Memories
针对 macOS 上任意文件夹中的每一张照片、每一帧视频的深度 AI 搜索。本地优先 —— 无需账号、无需云端、无需上传。推理全部在你的 Mac 上运行。
有什么不同
- 像思考一样搜索 —— 用自然语言描述一段记忆,剩下的交给本地视觉模型完成。
- 视频,精确到时刻 —— 场景被切分并生成向量嵌入,让你直接命中那一镜头,而不只是那个文件。
- 文字与对白同样可搜 —— 对画面可见文字进行 OCR;通过 Whisper 实现精确台词搜索,两者都是独立模式。
- 标签页与提示词归你 —— 任何查询都能保存为标签页;「截图」和「邮件」标签页可开关。
- 自维护的媒体库 —— 被监控的文件夹自动导入,内容哈希对重命名去重,切换模型时在后台重新嵌入,不阻塞搜索。
- 真正私密 —— 你的媒体文件从不离开本机。模型权重只需下载一次,之后完全离线。
五种搜索方式
| 模式 | 能找到什么 |
|---|---|
| 文件 | 按语义匹配整个照片/视频 —— 视觉排序,并有文件名与短语加成 |
| 场景 | 视频内部的片段 —— 搜索一个镜头,跳转到它的时间码 |
| OCR | 图像与帧中可见的文字,逐字面匹配(Tesseract;英语 + 35 种语言可开关) |
| 对白 | 视频中精确说出的台词(Whisper),分精确度层级 |
| LLM (可选启用) | 基于 Mac 已提取的对白、OCR 文本和文件名进行本地对话 —— 回答附引用出处 |
环境要求
- macOS(使用 electron-builder 打包;菜单栏/托盘功能仅限 macOS)
- Bun(https://bun.sh)—— 本项目使用
bun作为包管理器和运行器 - 已安装 Node 依赖:
bun install - 首次使用某个模型时会下载其权重(默认 CLIP 约 435MB);之后完全离线。
下载
最简单的安装方式是通过 Homebrew(Apple Silicon,macOS 12+)。
该 tap 中的 cask 会在每次安装和升级时自动清除 macOS 隔离标记(quarantine flag),因此应用无需任何手动 Gatekeeper 操作即可启动:
brew tap allenv0/scm
brew trust allenv0/scm
brew install --cask allenv0/scm/scm
升级时行为相同:
brew upgrade --cask allenv0/scm/scm
偏好最小权限?只信任该 cask 而不是整个 tap:
brew tap allenv0/scm
brew trust --cask allenv0/scm/scm
brew install --cask scm
该 tap 位于 allenv0/homebrew-scm(https://github.com/allenv0/homebrew-scm)。
开发
bun run dev # 构建渲染进程 bundle,然后启动 Electron 应用
bun start # 不重新构建,直接启动 Electron 应用
bun run build # 仅重新构建渲染进程 bundle 到 dist/
构建应用包(DMG / ZIP)
bun run dist # 若钥匙串中有身份则进行签名
bun run dist:unsigned # 跳过代码签名查找
该命令按顺序执行两个步骤:
vite build—— 将 React 渲染层编译到dist/(会拾取src/下的所有更改)。electron-builder --mac—— 打包应用。它会将新生成的dist/bundle 与main.js、preload.js、main-lib/和indexer/一起打包(文件列表在package.json的build.files中配置),然后生成安装程序。
输出: 安装程序生成在 dist-app/ 目录(参见 package.json 中的 build.directories.output)—— 找 SCM-0.2.4.dmg 和 SCM-0.2.4.zip。
功能特性
文件
开始输入时会先进行即时的文件名关键词预筛,随后由视觉模型接管:结果通过与图像嵌入的余弦相似度打分,并有受控的短语和文件名加成、按模型校准的诚实底线(honesty floor),以及近重复多样性过滤。每个图块都带有一个「为何匹配」徽章(视觉匹配 / 文件名匹配 / …),悬停时显示各分项得分的详细拆解。中文(CJK)查询以重叠二元词切分搜索(“台北車站” 同时匹配台北、車站)。
场景
所有视频中的每个场景片段都会被评分,因此命中即精确到镜头:图块显示场景海报图并带时间码徽章,打开视频会直接跳转到该时刻。噪声门控会返回「无场景匹配」,而不是用乱码淹没网格;每个视频最多贡献 3 个场景。
OCR
逐字面匹配查询词元中在每张图像 OCR 文本里可见的比例 —— 文件名被忽略,不涉及任何视觉模型,因此即使 AI 引擎预热中或处于离线状态也能工作。匹配到的词在图块和灯箱中以琥珀色框标出。
对白
对 Whisper 转写文本进行精确的逐字面检索 —— 无嵌入、无阈值,AI 引擎关闭也能用。结果分三个层级:精确台词(单次发言中的连续短语)、精确词(单次发言或 ≤8 秒窗口内的全部词),以及说出的词(同一视频中的全部词)。匹配词在语音片段中高亮显示;打开结果会直接定位到该行。
LLM(Ask)
可选启用 —— 在设置 → LLMs Chat 中启用前不会下载或运行任何东西。绑定在回环地址上的 llama.cpp sidecar 会基于应用已提取的证据回答你的问题 —— 对白台词、OCR 文本和文件名关键词命中 —— 并给出可点击的编号引用,逐 token 流式输出,带实时 tok/s 读数。以 /screenshots、/videos、/email 开头可缩小语料范围;Stop 可保留部分回答;证据为空时会在模型运行前短路退出。
| 聊天模型 | 大小 | 说明 |
|---|---|---|
| Qwen3 1.7B(默认) | ~1.1GB | 日常对话快速;适合 8GB 内存的 Mac |
| Llama 3.2 3B | ~2GB | 更强的长回答能力;需要留有余量 |
标签页与媒体库视图
- 内置浏览标签页:全部、视频,以及截图和邮件 —— 后两者可在设置 → Smart Tabs 中开关。选择「视频」时会自动启用场景模式。
- 任何查询都可保存为标签页:搜索栏下方的图钉按钮会保存当前提示词及其模式(文件/场景/OCR/对白)—— 最多 20 个标签页,可重命名,每个都会按保存时的状态精确恢复。
- 五个语义视图对应可重映射的快捷键(默认 ⌘1–⌘5),另有 ⌘I 导入 / AI Insights,⌘, 打开设置。
- 搜索保持在所选标签页内:选择截图、邮件、视频或任何已保存的标签页,结果会被过滤到该范围内 —— 先定范围,再搜索。在 LLM 聊天中同样明确:以
/screenshots、/videos、/email开头可在模型运行前缩小语料范围。
邮件标签页
展示可见 OCR 文本中包含电子邮件地址的照片 —— 这是一个叠加视图(照片仍保留其原有分类)。检测对 OCR 容错:它会重新拼接 Tesseract 被词框拆散的地址,并处理逗号代替点号的噪声(“gmail,com”)、被拆分的 TLD(“gmail. com”)、括号混淆(“allen [at] gmail [dot] com”)以及口述的地址(“allen at gmail dot com”)。图块显示联系人条,展开后可复制或撰写邮件。
截图标签页
截图分类对重命名免疫。四个信号,按优先级排列:手动覆盖(右键任意图块)→ 文件名词汇(覆盖 20+ 种语言中的 30+ 种本地化系统截图名称)→ PNG/JPEG 元数据探测(从 PNG 文本块 / EXIF UserComment 中读取 “screenshot”,因此重命名后的 Bildschirmfoto 仍能正确分类)→ 来源文件夹提示。其余全部归入「项目」。
视觉模型
通过 ONNX Runtime 提供四个可切换的模型;针对每个媒体库独立选择当前激活模型:
| 模型 | 角色 | 速度(CPU) | 下载量 |
|---|---|---|---|
| CLIP ViT-L/14@336(默认) | 真实场景下最佳的视频场景搜索 | ~480–570ms/张 | ~435MB |
| SigLIP-2-B/16 | 最快的批量导入 | ~50–100ms/张 | ~412MB |
| SigLIP-2-L/16@256 | 高细节(1024 维)—— 小物体、标牌、屏幕文字 | ~200ms/张 | ~850MB |
| SigLIP-B/16@384 | 极致细节 | ~480ms/张 | ~214MB |
切换模型会对整个媒体库重新嵌入:切换立即生效,剩余部分在后台填充,期间搜索会回退到文件名关键词。每个模型的文字均值居中处理会消除文本嵌入的偏差,使相似度分数在不同模型间保持诚实可比。
视频搜索管线
ffmpeg 扫描每个视频的镜头边界,并根据你在设置 → 视频搜索中选择的密度生成片段计划 —— 每个预设都会在你确认前展示实测的时间与磁盘开销:
| 预设 | 每个采样点间隔秒数 | 片段预算 |
|---|---|---|
| Eco | 60 | 4–32 |
| Balanced(默认) | 30 | 8–128 |
| Detailed | 15 | 12–256 |
| Ultra | 5 | 16–1024 |
| Ultra Pro | 2.5 | 24–2048(需要确认) |
每个片段会对其中点帧生成嵌入并保留一张海报图;镜头计划按文件缓存(路径 + 大小 + 修改时间 + 配置指纹),因此重复导入会完全跳过检测。
对白转写: Whisper tiny.en(~150MB,默认)或 base.en(~300MB)—— 切换后会对每个视频重新转写。整段视频会对三帧(20/50/80%)生成嵌入后取平均;GIF 则取中间帧的平均嵌入。
OCR
Tesseract 运行在独立 worker 中,与视觉模型分离。英语始终开启;另有 35 种语言可在设置 → 照片搜索中开关(默认:简体 + 繁体中文、日语、韩语)。每个语言包只需下载一次(~2.4–5MB;默认集合约 ~17MB),之后完全离线。词框与文本一同存储,因此匹配时可在原位高亮;中文(CJK)文本无空格拼接,被词框拆散的邮件片段会被重新拼接。
导入与媒体库管理
- 导入方式:⌘I、拖放,或受监控的文件夹 —— 导入一个文件夹即开始监控它(实时
fs.watch加上每次启动时的重新同步)。问题文件最多重试 3 次,之后会在 watch 同步中暂缺,直到文件发生变化。 - 对重命名免疫的去重:每个文件在复制前都会进行内容哈希(SHA-256),伪装的扩展名会被 MIME 嗅探规范化。
- 命名的嵌入版本(设置 → 库):整个可搜索状态的时间点快照 —— 索引、每个模型的嵌入 bin、场景和转写 sidecar —— 支持恢复(先自动备份)以及「全新开始」危险区。上限:10 个。
- 所有内容位于
~/Library/Application Support/scm下(MEMORIES_DATA_DIR可覆盖此路径):索引 JSON、每个模型的 Float32 嵌入 bin、场景和转写 sidecar、缩略图与海报图。
构造即隐私
- 渲染进程是沙箱化的
app://bundle ——contextIsolation、操作系统沙箱,以及锁定为'self'的 CSP(外加用于展示字体的 Google Fonts CDN,离线时回退到等宽字体)。 - 只有主进程 worker 会执行下载,且每种内容仅下载一次:视觉模型权重(Hugging Face)、OCR 语言包(Tesseract CDN)、Whisper 权重,以及 —— 仅当你选择启用 —— llama.cpp sidecar 和 GGUF 聊天模型(GitHub + Hugging Face),下载时进行 sha256 校验。
- 媒体文件被复制到应用管理的媒体库中,并从磁盘流式读取。无遥测、无账号、无上传。
设置与细节打磨
一个 macOS 风格的设置面板,含十个标签页(库、外观、网格、Smart Tabs、照片搜索、视频搜索、LLMs Chat、全局快捷键、键盘、菜单栏)。核心之外还有:浅色/深色/系统主题、灯箱的 CRT 屏幕效果、6 个可选应用图标、仅菜单栏模式、可录制的全局快捷键、首次运行引导导览、后台任务托盘(场景 / 转写 / OCR)带全局暂停,以及显示版本号和已索引视频数的状态栏。
注意事项
- 首次构建较慢: electron-builder 首次运行时会下载 Electron 二进制文件和 ffmpeg;后续构建会快得多。
- 代码签名: 若钥匙串中未配置 Apple Developer 身份,DMG 将以未签名方式构建。应用仍可在本地运行,但 macOS 首次启动时可能要求右键 → 打开。
- 重建会自动拾取更改: 由于
main.js、preload.js、main-lib/和indexer/都是从源码打包(而非使用缓存),在修改其中任何一个后执行bun run dist即可生成全新的安装包。
测试
bun run test:all # 完整验证套件(单元 + 冒烟测试)
bun run smoke:indexer # CLIP 索引器的无头冒烟测试
bun test test/ # 单元测试(纯模块,无需 Electron)
bun run lint # eslint
bun run format:check # prettier
- 单元测试覆盖纯核心模块:排序、对白精确匹配、中文(CJK)词元切分、Whisper 模型阶梯、MIME 嗅探、转写融合等(参见
package.json中的test:*脚本)。 - E2E 冒烟测试通过
ELECTRON_SMOKE_*环境变量在 Electron 应用内部运行 —— 十余个场景,从启动/协议检查到搜索矩阵、模型迁移以及 Ask 模式(驱动脚本位于scripts/e2e/,在main.js中调度;例如bun run smoke:ask、smoke:deep、smoke:grid)。 - 基准测试:
bun run bench:inference、bench:enrich和bench:detect会将 JSON 报告写入MDs/bench-*。
项目结构
main.js Electron 主进程:媒体库、IPC、索引器 worker 池、app:// 协议
preload.js contextBridge —— 向渲染进程暴露 window.memories
main-lib/ 从 main.js 拆分出的主进程模块(设置、媒体库存储、排序搜索、Ask 检索、LLM sidecar 配置、嵌入版本……)
indexer/ 视觉/OCR/ASR worker(utility 进程)+ 视频工具 + 模型注册表
src/ React 渲染层(网格、搜索模式、灯箱、标签页、设置、引导导览)
scripts/ 基准测试脚本、E2E 驱动、冒烟测试套件(smoke-all.sh)
test/ 单元测试 + 集成测试
MDs/ 设计文档、基准测试报告、计划
dist/ vite 构建输出(渲染进程 bundle)
dist-app/ electron-builder 输出(DMG / ZIP)
相似文章
Invenio
Invenio 为 Mac 视频和照片库提供本地 AI 搜索。
Show HN:Breadcrumb——记录你 Mac 上的一切 + 面向 AI 的上下文管理器
Breadcrumb 是 Innerloop 推出的一款 Mac 原生 AI 插件,能够记录你在 Mac 上的一切活动,并提供一个上下文管理器帮助开发者掌控自己的 AI。它本地运行、加密存储,无需注册账号。
展示 HN:无需截图,只用文本记录屏幕记忆到 Markdown
Ambient Context 是一款 macOS 菜单栏应用,它通过捕捉聚焦窗口的文本,将其转换为 Markdown 文件供 LLMs 阅读,并具备隐私编辑功能且无网络调用。
展示 HN: RxFilm Studio–使用 AI 代理创建和编辑您的产品视频
RxFilm Studio 是一款 macOS 应用程序,集成 AI 功能,协助创建和编辑产品视频,提供音乐配乐、旁白、字幕以及高质量渲染等功能,并配备 AI 代理用于指导更改。
标记你的照片
一款Mac应用,利用AI为Apple Photos生成关键词,完全在设备上运行。