{"id":"f29a4068-9bf8-4c05-915e-e28c7b679b82","slug":"binquery","kind":"skill","name":"binquery","summary":"Local CLI: turn an editing intent into a ranked clip shortlist from your own footage index.","owner":"jennifferweslowski-design","repoUrl":"https://github.com/jennifferweslowski-design/binquery","license":"MIT","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-08-14T09:31:01.000Z","addedAt":"2026-10-21T11:24:55.049Z","logoUrl":"https://avatars.githubusercontent.com/u/290654201?u=a5226541437f2a4b2bcafdce921972bdac9a2f1e&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["design-media"],"tags":["cli","clip","ffmpeg","python","video"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T04:00:09.033Z","sample":false,"url":"https://conduits.dev/tools/binquery","language":"Python","transports":["stdio"],"install":{"pypi":"binquery"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# binquery v0.3.0\n\n**Describe a shot in one sentence; get back a ranked shortlist of clips from your own\nfootage folder.** Local CLI, local ffmpeg, local OpenCLIP ViT-B-32. Search shortlists\ncandidates for review. It does not assemble a timeline or export a finished edit.\nInstall from PyPI with `pip install binquery`, or from source (see 安裝 below).\nDocs are in Traditional Chinese.\n\n剪輯助理的痛是素材盒很大：想用一句意圖先縮成可看的排序短名單，而不是把整庫翻一遍。\n\n本機 CLI。對自己的影片資料夾建盒，再用一句意圖撈短名單 JSON。查詢只編碼句子，不重抽幀、不重算整庫。建盒用本機 ffmpeg + 本機 OpenCLIP。\n\n短名單長這樣（本機 10 條自有預覽）：\n\n![binquery shortlist from 10 local self-made clips](https://raw.githubusercontent.com/jennifferweslowski-design/binquery/main/docs/shortlist-phoenix-preview.svg)\n\n主要欄位包括：\n\n- `path` — 相對檔名\n- `score` — 排序用分數，不是過關門檻\n- `gate` — 這次查詢走的閘門\n- `reasons` — **排名訊號，不是可靠的語義解釋。**它交代分數的組成，不保證能說明「為什麼這條適合你的剪輯」\n\n## 範圍與限制\n\n**它不是：**\n\n- 自動成片、自動剪預告、把結果貼進時間軸的工具\n- Premiere 或任何 NLE 的替代或整合（盒不會從檔案還原 Premiere 順序）\n- 素材瀏覽器\n- highlight 偵測、依靜音切、YouTube／TikTok 自動 clip\n\n**尚未驗證的範圍：**\n\n- 沒有對真實剪輯專案規模的素材盒做過品質驗證\n- 沒有對實況錄影跑過\n- CI 的 lavfi 測試圖案只是機械煙霧測試，不是實況 clipping 演示，也不是 rushes／場次序列演示\n\n真演示需要將用戶自己的本機長片先 `split` 再 `index`，或將本機影片資料夾直接 `index` 成盒。\n\n**已知邊界：**\n\n- 分數只用來排短名單，不是過關。人還是要看\n- CLIP 看靜幀，長得像的會排在一起（例如白天岩縫透天，分數可以像月亮）\n- 長片仍可能排高；時長是加分，不是硬切\n- `motion_label` 目前只是索引 metadata，不是查詢條件\n- 多場次的查詢順序留給用戶自己當場次記憶\n\n## 安裝\n\n```\npython3 -m venv .venv\n.venv/bin/pip install binquery\n```\n\n開發或從原始碼執行時，clone 本倉庫後改用 `.venv/bin/pip install -e .`。\n也可以 `pip install -r requirements.txt` 再跑 `./binquery`。\n\nPyPI 套件名是 `binquery`。\n\n還要本機有 `ffmpeg` / `ffprobe`。\n\nCPU。模型是 OpenCLIP ViT-B-32 / `laion2b_s34b_b79k`。權重快取：\n\n- `$BINQUERY_CLIP_CACHE`，或\n- `~/.cache/binquery/open_clip`\n\n第一次跑可能把權重下載進這個快取（本機檔，不是查詢 API）。之後可設 `BINQUERY_OFFLINE=1`。\n\nPython 順序：`$BINQUERY_PYTHON` → `./.venv/bin/python` → `python3`。\n\n## 零素材 demo\n\n不用準備影片。下面這一條命令會用 ffmpeg `lavfi` 生成一條本機合成測試片，\n再完整執行 `split → index → doctor → query`：\n\n```\nbinquery demo --out /tmp/binquery-demo\n```\n\n結果留在 `/tmp/binquery-demo/`：\n\n- `synthetic-long.mp4` — 生成的 30 秒測試片\n- `clips/` — time-grid split 結果\n- `box/index/` — 本機 OpenCLIP index\n- `query.json` — `color test pattern` 的排序短名單\n\n`--out` 必須是新目錄或空目錄，既有內容不會被覆寫。第一次執行仍可能下載\nOpenCLIP 權重。這個 demo 只證明完整流程能跑，不是語意品質基準。\n\n## 用法\n\n```\nexport BINQUERY_INDEX=./my-box\n\n./binquery split --input ./long.mp4 --out ./split-out --seconds 8\n./binquery index --input ./split-out --index \"$BINQUERY_INDEX\"\n./binquery doctor --index \"$BINQUERY_INDEX\"\n./binquery query --index \"$BINQUERY_INDEX\" \"<intent>\" [--limit 12] [--out path.json]\n./binquery list\n```\n\n已有影片資料夾就跳過 `split`，直接 `index --input ./my-videos`。\n\n`split --input` 是一條本機長片。`--out` 要空目錄或新目錄（預設在長片旁邊的 `split-out`，不寫進程式樹）。`--seconds` 預設 8，夾在 4–60。預設 `-c copy`，切在 keyframe，時長不會剛好。可選 `--reencode`（本機 libx264 + aac）會比較接近目標時長。`split` 不建盒、不查詢。\n\n`index --input` 是你自己的影片資料夾（split 出來的夾，或本來就有的夾）。`index --index` 要給**盒根**，程式會在底下建立並寫入 `index/`。\n\n`query` 用一句 pictorial 意圖回短名單。`--limit` 預設 12，夾在 8–15：傳小於 8 當 8、大於 15 當 15；候選不足時結果可以少於 8。`--out` 會把結果寫成 JSON 檔。未對上凍結導演句也當 pictorial 跑，不必改 `src/binquery/intents.py`。\n\n對已存在的路徑，`query --index` 與 `doctor --index` 會優先使用底下的 `index/`；若沒有該子目錄，就把給定路徑本身視為 index 目錄。\n\n`doctor` 只檢查檔在不在、能不能讀。缺必備 index 就列檔名、exit 2，不假裝能查、不解包。\n\n多場次就一場一句 `query`。預告從 rushes 找鏡頭：對自己的 rushes 資料夾 `index`，再丟一句預告意圖。\n\n## 自備盒\n\n1. 影片放在你自己的資料夾，不要拷進這個倉庫。\n2. 建盒：\n\n```\nexport BINQUERY_INDEX=./my-box\n./binquery index --input ./my-videos --index \"$BINQUERY_INDEX\"\n```\n\n會用本機 ffmpeg 每條抽 3 幀（約 1s／中點／結尾前 1s），再用本機 ViT-B-32 算向量，寫出：\n\n- `index/clip.json`\n- `index/clip_vectors.npy`\n- `index/mechanical.json`\n- `index/empty_hard.json`（`person_clip`：每條 max(cos 人 − cos 空)，空鏡 gate 直接讀。不必自己加 json。）\n- `index/motion.json`（包含 coarse 三幀幀差＋phaseCorrelate 產生的 `motion_label`：lock／pan／handheld／moving，以及複製的 `person_clip`。目前查詢只讀其中的 `person_clip`，不依 `motion_label` 排名或篩選。）\n\n3. 先 `doctor`，再 `query`。MISS 就停。\n4. pictorial 不需要 person。空鏡 gate 用 `person_clip`。\n5. 影片、幀、向量留在盒裡。本倉庫只放程式和說明。`.gitignore` 已擋 `.DS_Store`、Python 快取與虛擬環境、`*.npy`、支援的影片格式、`index/`。\n\n## 例子\n\n本機 10 條自有預覽。`doctor` 0 之後：\n\n```\n./binquery query --index \"$BINQUERY_INDEX\" \"wide dark water rain\" --limit 8\n```\n\n```\nexport BINQUERY_INDEX=./my-box\n./binquery index --input ./my-videos --index \"$BINQUERY_INDEX\"\n./binquery doctor --index \"$BINQUERY_INDEX\"\n./binquery query --index \"$BINQUERY_INDEX\" \"工人與車\" --limit 12\n```\n\n凍結分數見 [test-run.md](https://github.com/jennifferweslowski-design/binquery/blob/main/test-run.md)（相對檔名，不含素材）。\n\n未對上凍結句的場次名／預告意圖／clip 意圖也可以直接丟給 `query`，走 pictorial：\n\n```\n./binquery query --index \"$BINQUERY_INDEX\" \"scene 2 empty hallway lock-off\"\n./binquery query --index \"$BINQUERY_INDEX\" \"第三場：傍晚空街遠景\"\n./binquery query --index \"$BINQUERY_INDEX\" \"cold open wide empty night\"\n./binquery query --index \"$BINQUERY_INDEX\" \"close-up reaction pause\"\n./binquery query --index \"$BINQUERY_INDEX\" \"excited reaction close-up\"\n./binquery query --index \"$BINQUERY_INDEX\" \"talking to chat medium\"\n```\n\n一條長錄影要拆成可發的 clips：先 `split` 成資料夾（本機 ffmpeg 時間格，切在 keyframe），再 `index` 那個資料夾 → `doctor` → 一句「哪種 clip」查短名單。這是 **this kind of ask**（r/NewTubers, 2026-08-17: [How do small streamers handle clipping workflows](https://www.reddit.com/r/NewTubers/comments/1vqdvah/how_do_small_streamers_handle_clipping_workflows/)）。\n\n## Agent Skill\n\n給 agent 用：一句剪輯意圖 → 本機短名單。裝這個倉庫的 Skill：\n\n```\nnpx -y skills add jennifferweslowski-design/binquery\n```\n\n或把 `skills/binquery/` 拷進專案的 `.agents/skills/binquery` 或 `.claude/skills/binquery`（夾裡要有 `SKILL.md`）。\n\n這只裝給 agent 的說明，不會裝 Python 套件。CLI 仍要另外執行 `pip install binquery`。\n\n場次很多、或預告要從自己的 rushes／dailies 找鏡頭：Skill 裡有未對上凍結句的示例意圖（一場一句，或一句預告意圖）。\n\nagent 的行為約束寫在 `skills/binquery/SKILL.md`。使用時遵守上方的[範圍與限制](#範圍與限制)。\n\n## 禁止\n\n雲端視覺查詢 API、unpack 素材進本倉庫、把影片或 index 向量提交上來。\n\n## CI\n\n推 `main` 或開 PR 時，[ci](https://github.com/jennifferweslowski-design/binquery/blob/main/.github/workflows/ci.yml) 先跑 query 單元測試，再在官方 runner 上用 lavfi 自製 30s 測試圖案，`split` 成至少 2 段，接著跑 `index` → `doctor` → `query` 煙霧測試。素材和 index 只活在 runner，不進倉庫。\n\n本機也可先用 lavfi 試 `split`：\n\n```\nffmpeg -f lavfi -i testsrc=duration=30:size=320x240:rate=25 \\\n  -f lavfi -i sine=frequency=440:duration=30 \\\n  -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest /tmp/long.mp4\n./binquery split --input /tmp/long.mp4 --out /tmp/split-out --seconds 8\n```\n\n常見結果是 3 個檔、各約 10s（`-c copy` 切在 keyframe）。然後可 `index` 那個資料夾再 `doctor`。\n\n怎麼提 issue／PR 見 [CONTRIBUTING.md](https://github.com/jennifferweslowski-design/binquery/blob/main/CONTRIBUTING.md)。\n"}