No description
Find a file
dlawler489 66b62cbb1e
All checks were successful
build-and-push / docker (push) Successful in 24s
Merge sturdier AI replies with clearer errors
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 20:29:28 +10:00
.forgejo/workflows CI: push image using a Forgejo access token 2026-06-22 17:48:57 +10:00
db AI collection suggestions, pick-what-to-do runs, and a retry for bad replies 2026-09-15 17:52:41 +10:00
lib Sturdier AI replies: format example, more retries, and errors that show the reply 2026-09-15 20:26:42 +10:00
public Ask for a fresh MakerWorld token when MakerWorld refuses it, then retry 2026-09-15 18:33:10 +10:00
test Sturdier AI replies: format example, more retries, and errors that show the reply 2026-09-15 20:26:42 +10:00
tools Open models in your slicer and drag files out 2026-09-15 09:15:37 +10:00
.dockerignore AI tags and descriptions, search by meaning, and automatic tags 2026-09-15 11:12:58 +10:00
.env.example Initial commit: Printhive 3D-print file library 2026-06-22 13:15:17 +10:00
.gitignore AI tags and descriptions, search by meaning, and automatic tags 2026-09-15 11:12:58 +10:00
docker-compose.yml Make thumbnails on the server in the background 2026-09-15 07:54:26 +10:00
Dockerfile Docker: fail the build if the search model can't be fetched 2026-09-15 12:54:03 +10:00
package-lock.json AI tags and descriptions, search by meaning, and automatic tags 2026-09-15 11:12:58 +10:00
package.json AI tags and descriptions, search by meaning, and automatic tags 2026-09-15 11:12:58 +10:00
README.md Ask for a fresh MakerWorld token when MakerWorld refuses it, then retry 2026-09-15 18:33:10 +10:00
server.js Ask for a fresh MakerWorld token when MakerWorld refuses it, then retry 2026-09-15 18:33:10 +10:00

Printhive

A self-hosted web app for organizing your 3D-printing files (STL, 3MF, OBJ, FBX, STEP, 3DM, GCODE — loose or in ZIP/7Z/RAR/TAR archives) into a searchable library — with in-browser 3D previews, collections, license/commercial-use tracking, and one-paste import from popular model sites.

Features

  • Library of models, each owning one or more files; tags, collections, descriptions, print settings.
  • Import from URL:
    • Thingiverse — downloads files automatically via the official API (needs a free app token).
    • Printables — downloads files automatically via its public GraphQL API (no auth needed for free models).
    • MakerWorld — downloads files via FlareSolverr (solves Cloudflare) + your account token. If files can't be downloaded the import says why, and when MakerWorld refuses the token (they expire) Printhive asks for a fresh one and retries.
    • cubee3d (Hive) and any other URL — imports title/description/preview image; you add the files manually.
  • In-browser 3D viewer (Three.js) for STL, OBJ (with its MTL colours/textures), FBX (with textures), STEP/STP (OpenCascade WASM) and Rhino 3DM, and for 3MF via a server-side trimesh GLB conversion that streams even huge Bambu/MakerWorld projects.
  • Slicer projects plate by plate — Bambu Studio / OrcaSlicer 3MFs open on plate 1 with the slicer's plate images as a switcher, parts shown in their filament colours, and a Filaments list; negative/modifier parts are hidden.
  • Archives as folders — ZIP, 7Z, RAR and TAR/.tar.gz uploads are indexed in the background; browse what's inside and preview or download any file without unpacking. Models inside archives count for duplicate detection.
  • Pictures, PDFs & readmes — keep them with a model and view them in the app.
  • Open in slicer — pick your slicer once per browser (Settings → Slicer). Bambu Studio and OrcaSlicer open the file straight from a 10-minute download link (bambustudioopen://…, orcaslicer://open?file=…), so the server must be reachable at the address the browser uses; Bambu Studio asks before opening files that aren't from MakerWorld. PrusaSlicer (which only accepts Printables/Thingiverse links) and Cura get a normal download, and any other slicer with an open-link can be added as a template. Opening a 3MF project in a different slicer from the one that saved it asks first. Works for files inside archives too.
  • Drag out — drag a model card (its file, or a zip of all its files) or any file row, including files inside archives, onto your desktop or a folder to save it. Uses Chromium's DownloadURL drag, so it needs Chrome, Edge, Brave or similar.
  • Auto thumbnails made in the background: an embedded 3MF/plate preview, else a picture that came with the model (or one inside its archive), else a server-side render of the STL/OBJ/3MF; the browser renders FBX/STEP/3DM ones.
  • License & commercial-use tracking — captured per source, with a "sellable / non-commercial" indicator (a guide; verify before selling).
  • Collections — group models (a model can be in many); rename/delete without losing models.
  • Editing — rename/retag, edit print settings, override commercial-use, refresh metadata from source, re-download files, download all files as a zip.
  • Notes — free-form notes per model, saved as you type.
  • Search, filters & sorting — search across names, descriptions, notes, tags, file names and designers; filter by format, source and license; include or exclude tags (match any/all); sort and group by source or format. Press / to search.
  • Duplicate finder — every file gets a SHA-256 content hash plus (for STL/OBJ/3MF) a geometry fingerprint, computed in the background. Models sharing files are grouped as exact copies or same shape (renamed, re-exported, converted — or mirrored, so check those), with a recommended copy to keep. Keeping one merges the others' tags, collections and notes into it and moves them to the trash; groups can be dismissed as "not duplicates".
  • AI tags & descriptions — bring your own key for DeepSeek (the default), OpenRouter, OpenAI or any OpenAI-compatible server (Ollama, LM Studio). Tag the whole library, only models not yet tagged, or a single model from its page. Each model's name, file names and (optionally) thumbnail are sent; tags are only ever added and descriptions only filled where empty unless you ask. Optionally it also gives models with unclear names (file-name leftovers, typos, site boilerplate) a clearer one; the old name is kept and can be restored with Undo on the model page. It can also suggest collections (reusing yours where they fit): suggestions wait on the AI page, grouped by name, until you create/add or dismiss them. Each run can do any mix of tags & descriptions, names and collections, and a reply that isn't the JSON asked for is retried once. Runs show live progress, tokens and cost, can be stopped and resumed, stop at an optional spending limit, and pause (rather than keep spending) if the server restarts. "Save & test connection" in Settings checks the key, model and whether it accepts images.
  • Search by meaning — switch the search box to Meaning to find models by what they are ("wall hook" finds a "coat hanger bracket"). Uses a small embedding model (bge-small-en-v1.5, ~34 MB) that runs on the server, so it's free and private; the index updates in the background as models change.
  • Automatic tags — strict whole-word rules over names, file names and archive contents (part types, mechanisms, materials, techniques) plus your own "word → tag" rules. Switch it off (optionally removing its tags) or re-apply it any time. Tags you add yourself are never changed; AI and rule tags are marked on the model page.
  • Trash — deleting moves a model to the trash; restore it, delete it forever, or empty the trash (only then are files removed from disk).
  • Settings — API tokens / FlareSolverr URL stored in the database (with .env fallback), secrets masked.

Tech stack

  • Node.js + Express, vanilla-JS frontend (no build step), PostgreSQL.
  • Three.js (viewer) via CDN import map.
  • Python + trimesh sidecar for 3MF→GLB conversion, volume, and geometry fingerprints.
  • Optional FlareSolverr for Cloudflare-protected sources.

Setup

  1. Postgres — create a database and point DATABASE_URL at it. The schema is applied automatically on startup.
  2. Python sidecar (for 3MF previews, plates, server-side thumbnails and shape-based duplicate detection — without it, duplicates are still found by content hash):
    python3 -m venv tools/venv
    tools/venv/bin/pip install -r tools/requirements.txt
    
  3. Install deps and run:
    npm install
    cp .env.example .env   # set DATABASE_URL (+ optional tokens)
    npm start
    
  4. Open http://localhost:3000

Run the unit tests with npm test.

Configuration

Settings live in the database (editable in the app's Settings menu) and fall back to environment variables:

Setting Env var Purpose
Thingiverse API token THINGIVERSE_TOKEN Thingiverse auto-download
FlareSolverr URL FLARESOLVERR_URL Read Cloudflare-protected sites (MakerWorld)
MakerWorld auth token MAKERWORLD_TOKEN MakerWorld downloads (bearer token from your session)
Printables auth token PRINTABLES_TOKEN Optional; account-gated Printables files
AI provider AI_PROVIDER deepseek (default), openrouter, openai or custom
AI API key AI_API_KEY Your provider key (not needed for a local server)
AI model AI_MODEL Defaults to deepseek-flash for DeepSeek
AI API address AI_BASE_URL Defaults per provider; e.g. http://localhost:11434/v1 for Ollama
AI prices AI_PRICE_INPUT, AI_PRICE_OUTPUT $ per million tokens, only for showing costs (DeepSeek defaults to its peak rates)
Send thumbnails AI_SEND_IMAGES false to tag from names and files only

Notes

  • Auto-download from MakerWorld/Printables relies on their private/session APIs and can break if they change; failed/partial imports can be recovered with "Download files from source".
  • The embedding model for search is downloaded into ./models on first use locally; the Docker image ships with it. It holds ~150 MB of RAM while in use and is released after 10 idle minutes.
  • The commercial-use badge is a best-effort read of the license — always confirm on the source page before selling.