A run of downloads gets challenged by Cloudflare partway through: the answer
comes back as an ordinary reply, so nothing retried it and the model landed
with only its metadata — looking, on the watch page, like a success.
resolveDownload now recognises a challenge and gives FlareSolverr's browser a
couple more goes; what still fails is recorded as 'partial' and shown with its
reason and a "Get files" button, which clears the flag once the files arrive.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- A collection page lists only its first ~20 models. Checks now page through the
same load-more call the page uses (offset + a shared seed, 64 at a time, up to
~640 models), so a watch sees the whole collection.
- A private collection read without a valid token comes back as a placeholder
(id -1, no models), which was being treated as an empty collection. It now
reports the token problem instead.
- Adding a watch starts a check, and "Check now" could start a second one
alongside it, importing models twice. One check per collection at a time now,
and a check interrupted by a restart no longer looks like it's still running.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A watch reads the collection page through FlareSolverr carrying your token, so
private collections work, and imports the models it hasn't seen into a Printhive
collection of the same name (optionally AI-tagging them). Checks run on your
chosen interval, a few models at a time with pauses between, and each design is
remembered so nothing is imported twice. A refused token stops the check and the
Watched page offers to update it; models removed on MakerWorld stay here.
The import endpoint's work moved into importFromUrl() so watches reuse it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
When a download fails because MakerWorld refused (or is missing) the auth
token, the error carries reason makerworld_token. The import and "Download
files from source" then open an "Update your MakerWorld token" prompt with
steps to copy the token cookie; saving it retries the download straight away
and says if MakerWorld still refuses it. A missing FlareSolverr URL is reported
separately, since a token alone won't help.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MakerWorld answers an expired or missing auth token with {"error": "Please log
in to download models."} and no download link, which was skipped without a
word, so imports and "Download files from source" just ended with no files.
Now the reason comes back: missing settings, a refused token (probably expired),
no printable files on the page, or a failed download. The import screen shows
it, and the Settings hint explains where the token comes from.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Runs can now do any mix of: tags & descriptions, clearer names, and
collection suggestions. A run without tags doesn't mark models as tagged.
- Collection ideas reuse your collections and earlier ideas where they fit and
wait on the AI page, grouped by name, until you create/add or dismiss them.
Dismissed ideas aren't suggested for that model again; spelling variants
group together under the capitalised spelling.
- A reply that isn't the JSON asked for is asked again once; both requests
count towards the run's tokens and cost.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A run option asks the AI for a clearer name only when the current one is messy
(file-name leftovers, typos, site boilerplate). The previous name is kept, the
run feed shows what was renamed, and the model page offers Undo. Giving a model
a name yourself clears the undo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- AI tagging through any OpenAI-compatible API: DeepSeek by default, or
OpenRouter, OpenAI, or a local server (Ollama, LM Studio). Settings → AI
with presets, prices and "Save & test connection"; the key never leaves
the server except to the provider.
- Runs over untagged models, all models, or one model from its page: live
progress, tokens and cost, stop/resume (retrying failures), an optional
spending limit, and paused rather than resumed after a restart. Adapts to
models without JSON mode or image input; stops on a rejected key or no
credit. Tags are only added; descriptions filled only where empty unless
asked.
- Search by meaning: local bge-small-en-v1.5 embeddings (transformers.js),
indexed in the background whenever a model's text changes; Words/Meaning
switch on the search box. Stored as REAL[] in Postgres, no pgvector needed.
- Automatic tags: strict whole-word rules (part types, mechanisms, materials,
techniques) over names, file names and archive contents, plus your own
word -> tag rules; on/off (optionally removing its tags) and re-apply.
- Tags remember where they came from (user / rule / ai); edits keep that,
rule and AI passes never touch your own tags, and the model page marks them.
- Docker: ships the embedding model and only Linux onnxruntime binaries.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Settings → Slicer: pick Bambu Studio, OrcaSlicer, PrusaSlicer, Cura, a
custom open-link template, or plain download (remembered per browser)
- Bambu Studio / OrcaSlicer launch via their URL schemes with a 10-minute
signed download link (/dl/<token>/<name>) that needs no session cookie
- PrusaSlicer and Cura fall back to a download (they can't open our links)
- 3MF projects report which app saved them; opening one in a different
slicer asks first
- "slicer" action on every file row and archive entry
- Drag model cards (file, or zip of all files) and file/entry rows out to
the desktop via Chromium's DownloadURL
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- The background worker now also gives models without a thumbnail one:
an embedded 3MF/plate preview, else a picture uploaded with the model,
else a 3MF preview or picture inside one of its archives, else a render
of an STL/OBJ/3MF (loose or inside an archive).
- tools/render_thumbnail.py renders without a GPU: meshes are simplified
per distinct part (the 8M-face lion project renders in ~8 s / ~200 MB),
3MF projects show their first plate in filament colours, and only
camera-facing faces are painted (inside-out meshes and mirrored parts are
re-oriented first).
- models.thumbnail_status stops retries when there's nothing to use; adding
files clears it. The browser now only renders FBX/STEP/3DM thumbnails.
- The library checks back while fingerprints or thumbnails are pending and
refreshes when they land.
- Python sidecar needs pillow and fast-simplification; compose keeps the
archive extraction cache in a volume. README covers the phase 2 features.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- threemf_stream reads Bambu Studio / OrcaSlicer project metadata: plates
(name, objects, preview images), filaments, and each part's type and
extruder. Placements now record which build item and part they belong to.
- convert_3mf.py leaves out negative/modifier/support parts, colours each
part by its filament (a part's extruder 0 falls back to its object's), and
takes --plate N. Geometry is still stored once per (part, colour).
- New threemf_info.py lists plates and filaments without loading geometry
and extracts plate preview images; served via /api/{files,entries}/:id/project.
- /glb?plate=N builds and caches a per-plate preview. Cached GLB names carry
a converter version, so previews made before colours are rebuilt.
- Model detail: plate chips (with the slicer's plate images) under the
viewer — multi-plate projects open on plate 1 — plus "All plates", and a
Filaments section with colour swatches.
- Fingerprints leave out negative/modifier parts, so a project matches the
model it prints.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Archives
- ZIP, 7Z, RAR and TAR/.tar.gz uploads are indexed in the background into
archive_entries (7-Zip compiled to WASM in a worker thread; node-tar for
tar). macOS/Windows clutter and paths that could escape the extraction
folder are skipped.
- Models inside archives are extracted once per archive to hash and
geometry-fingerprint them, so duplicates are found inside packs too.
Pictures, PDFs and readmes are listed but never matched.
- New routes list an archive's entries and serve/convert a single entry,
extracted on first use and cached under extracted/.
Viewer
- Model detail lists what's inside each archive with view/download links;
opening an entry adds a viewer tab.
- New 3D formats: FBX (with sibling textures), OBJ with its MTL and
textures, STEP/STP via occt-import-js, and Rhino 3DM via rhino3dm — the
WASM readers are served from node_modules, loaded on first use.
- Pictures, PDFs and text files (jpg/png/gif/webp/bmp/svg, pdf, txt/md)
can be uploaded and preview in the viewer; a model without an embedded
3MF preview uses its first picture as the card thumbnail.
- Inline file responses send nosniff, and SVGs are sandboxed.
- GLB conversions write to a temp file and rename, so a half-written
preview is never served; a later tab can't be overdrawn by a slow load.
- Better 3MF card image: the project thumbnail or plate 1 render, not a
slicer pick map.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Fingerprint every file in the background: SHA-256 for all formats plus a
trimesh geometry fingerprint for STL/OBJ/3MF, so renamed, re-exported or
converted copies are still matched. Backfills existing files on startup.
- Duplicates view groups models that share files (exact copies vs same
shape), recommends which to keep, merges tags/collections/notes into the
kept model and trashes the rest; groups can be dismissed.
- Deleting now moves models to a restorable trash; files are only removed
from disk on permanent delete or when the trash is emptied.
- Per-model notes with autosave.
- Search across names, notes, tags, files and designers; filter by format,
source and license; include/exclude tags with any/all matching; sort and
group by source or format. Preferences persist per browser.
- Add node:test unit tests for duplicate grouping and fingerprints.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Changing a license policy applies to all models with that license, and
opening Settings reconciles old imports to current rules (store.reconcileModelsToRules)
- Editing a model's license re-resolves its sellability from the rule
- Removed the per-model commercial-use override (set by license now)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- license_rules table (name -> commercial); licenses auto-register on
import with a best-effort seed, overridable in Settings (applies the
choice to every model with that license)
- resolveCommercial() replaces ad-hoc inferCommercial across import/refresh
- GET/PUT /api/licenses; "License types" section in Settings
- Fix CC short codes: BY / BY-SA / BY-ND now classed sellable (MakerWorld
stores e.g. "BY-SA"); NC variants stay non-commercial
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- users + password_resets tables; bcrypt hashing; JWT session in an
httpOnly cookie (auth_secret auto-generated in settings)
- /api gated behind auth (except /api/config and /api/auth/*)
- First-run creates the account (no open registration after that);
login, logout, forgot/reset-password flows
- SMTP settings (host/port/secure/user/pass/from) in Settings menu;
nodemailer sends reset links
- Auth screen (sign in / create account / forgot / reset) gates the SPA;
Sign out button; COOKIE_SECURE=true in compose for HTTPS
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On a fresh install (no integration settings yet), prompt once for the
Thingiverse/MakerWorld/Printables tokens and FlareSolverr URL, with a
"skip for now" option. /api/config exposes setupComplete (true if any
setting is configured or setup was dismissed); POST /api/setup/complete
records dismissal. DB password stays a deploy-time env var by necessity.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>