filament-meter
Measure 3D-print filament usage from sliced
.3mf/.gcodefiles — grams, metres, print time and cost — from a single file or a whole library of models. OrcaSlicer is provisioned automatically when a model still needs slicing.
filament-meter answers one question and answers it well:
given a path (a file or a directory), how much filament will this print consume?
It reads the numbers that slicers already write into sliced files
(Metadata/slice_info.config, or the G-code footer as a fallback),
aggregates them per plate, per file and per run, and prints them — or
writes them to a file in JSON / CSV / Markdown. If a model has not been
sliced yet, filament-meter finds (or downloads) OrcaSlicer, slices it in
a throw-away directory, and reports the result without ever touching your
model folder.
Highlights
- One flat command —
filament-meter PATH. A file, a directory or a glob pattern; no subcommands to remember. - Fast path first — already-sliced
.3mffiles are parsed locally; OrcaSlicer is never launched when it is not needed. - Automatic OrcaSlicer provisioning — a portable build is downloaded
lazily into the user cache on first slice, or eagerly with
--install-orca. No admin rights, nopip install-time downloads. - Never pollutes your models — slicing happens in a temporary
directory (or
--keep-sliced DIR); no large files are written next to your sources. - Battle-tested slicing — the same two OrcaSlicer workarounds proven
on real Bambu Studio exports:
--allow-newer-fileand the adaptive--<key>=<min>retry fornot in rangeerrors. - Four output formats — human table, JSON, CSV (
;-separated, Excel-friendly) and Markdown. - Pure stdlib +
uvx— the only runtime dependency isplatformdirs; run it anywhere Python 3.11+ exists.
Table of contents
- Requirements
- Installation
- Quick start
- Usage
- Output formats
- Configuration
- How it works
- Project layout
- Troubleshooting
- Roadmap
- Contributing
- Releasing
- License
Requirements
- Python ≥ 3.11
uv/uvxfor the recommended install (install guide)- OrcaSlicer — optional. If it is not installed,
filament-meterdownloads a portable build into its cache the first time it needs to slice something. Pre-installed locations are auto-detected. - Outbound HTTPS to
api.github.comandgithub.comonly when OrcaSlicer has to be downloaded.
The project is Windows / macOS / Linux friendly.
Installation
From PyPI (recommended for users):
uv tool install filament-meter
filament-meter --help
Requires uv (or substitute pipx / pip).
For a one-shot run from a local checkout, uvx builds a temporary,
isolated environment that does not pollute the global Python:
# From the repository root
uvx --from . filament-meter "D:/models/part.3mf"
To install the checkout as a long-lived tool:
uv tool install . # installs the `filament-meter` binary
filament-meter --version
For development, install in editable mode with all extras:
git clone https://github.com/nimblemo/filament-meter.git
cd filament-meter
uv sync --all-extras
uv run filament-meter --help
Quick start
# 1. A single, already-sliced file — instant, no OrcaSlicer needed
filament-meter "D:/models/Gesha_sliced.3mf"
# 2. A whole folder, with cost estimation
filament-meter "D:/models" --price 2200 --currency RUB
# 3. A folder of *unsliced* projects — OrcaSlicer is fetched on demand
filament-meter "D:/projects" --price 2200
# 4. Machine-readable output to a file
filament-meter "D:/models" --price 2200 -o report.csv
A typical table run looks like this:
========================================================================
Gesha_Bambu_sliced.3mf (25.40 MB)
D:\WOOD\locus-products\sliced\Gesha_Bambu_sliced.3mf
Plate 1 | objects: 1 | layer: 0.20 mm | nozzle: 0.4 mm
bed: textured_plate
# type color grams metres
4 PLA #8E9089 96.54 31.85
plate total: 96.54 g | time: 5h 57m
TOTAL: 96.54 g, 31.85 m, time ~5h 57m
cost at 2200 RUB/kg: 212.39 RUB
========================================================================
files: 1 | sliced: 1 | failed: 0
TOTAL: 96.54 g, 31.85 m, time ~5h 57m
TOTAL cost: 212.39 RUB
Usage
filament-meter PATH [options]
PATH is the only positional argument. It may be:
- a file (
.3mf,.gcode,.stl,.obj,.gcode.3mf) — an explicitly named file is always processed, whatever its extension; - a directory — scanned recursively for the default model masks
*.3mf,*.stl,*.obj(disable the walk with--no-recursive, replace the masks with--glob); - a glob pattern such as
"D:/models/*.3mf".
Why
.gcodeis not a default directory mask. When a model is sliced the slicer drops aplate_N.gcode(or<model>.gcode) next to the.3mf. Scanning*.gcodetogether with*.3mfwould therefore report the same object twice. If the default scan of a directory finds nothing but*.gcodefiles are present (e.g. a folder of slices downloaded from Printables), the tool automatically retries with*.gcodeand notesfalling back to *.gcodeon stderr. Pass--glob "*.gcode"to opt into G-code explicitly.
For every file the tool decides between two paths:
- fast path — the
.3mfis already sliced (slice_info.confighas a real<plate>entry, or aMetadata/plate_N.gcodeexists): it is parsed locally and OrcaSlicer is never started; - slice path — otherwise (an unsliced project, or a bare
.stl/.obj) OrcaSlicer is located, downloaded if needed, and the model is sliced in a temporary directory.
Flags
| Flag | Description |
|---|---|
-o, --output FILE |
Write the report to a file. Format from the extension (.json / .csv / .md / .txt); unknown extensions fall back to txt. |
-f, --format {table,json,csv,md} |
Stdout format (default: table). |
--price FLOAT |
Filament price per kilogram, for cost calculation (persisted). |
--currency STR |
Currency code (persisted; default: RUB). |
--no-recursive |
Do not walk directories recursively. |
--glob PATTERN |
File mask(s) used when PATH is a directory (repeatable). Overrides the default masks (*.3mf, *.stl, *.obj). |
--orca PATH |
Explicit OrcaSlicer executable. |
--orca-version STR |
OrcaSlicer version to download (default: latest). |
--install-orca |
Only download OrcaSlicer and exit (does not need PATH). |
--no-auto-install |
Never download OrcaSlicer automatically. |
--no-slice |
Parse only; never slice. Unsliced files are reported as "no data", not as errors. |
--force-slice |
Slice again even if the file is already sliced. |
--machine / --process / --filament STR |
Profile file names (default: Bambu Lab A1 profiles). |
--use-project-settings |
Do not pass profiles; rely on settings embedded in the project. |
--jobs INT |
Number of files to slice in parallel (default: 1). |
--timeout INT |
Per-file slicing timeout in seconds (default: 3600). |
--cache-dir PATH |
Override the OrcaSlicer and result-cache directories. |
--no-cache |
Disable the persistent result cache (neither read nor write). |
--keep-sliced DIR |
Copy freshly-sliced .3mf files into DIR (default: temp dir, removed after parsing). |
-q, --quiet / -v, --verbose |
Adjust logging. |
--version |
Print the version and exit. |
--check |
Run environment diagnostics (OrcaSlicer, cache, write access) and exit. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Success — every file produced filament data. |
1 |
Partial success — some files could not be processed. |
2 |
Usage / environment error (bad path, OrcaSlicer missing with --no-auto-install). |
3 |
Total failure — not a single file yielded filament data. |
Output formats
| Format | Flag | Notes |
|---|---|---|
table |
-f table |
Human-readable ASCII, the default. |
json |
-f json |
Full RunReport, ensure_ascii=False, 2-space indent. |
csv |
-f csv |
;-separated, written with a UTF-8 BOM for Excel. |
md |
-f md |
Markdown table plus a totals block. |
The CSV columns match the legacy filament_report.csv exactly:
файл;нарезан;плашка;id филамента;тип;цвет;граммы;метры;время, с;цена, ₽
Gesha_Bambu_sliced.3mf;да;1;4;PLA;#8E9089;96.54;31.85;21439.0;212.39
Configuration
filament-meter is configured through flags and environment variables.
Besides the report you ask for and the OrcaSlicer cache, two small files are
written to disk:
- a result cache (
results.json) that remembers the filament figures of every model already analysed — keyed by its absolute path and file size — so an unchanged model is never sliced or re-parsed twice; - a settings file (
settings.json) that persists the last--currencyand--pricevalues so they become the defaults on later runs.
Both can be disabled / relocated with --no-cache and --cache-dir.
| Variable | Purpose | Default |
|---|---|---|
FILAMENT_METER_ORCA |
Path to an OrcaSlicer executable. | — |
FILAMENT_METER_CACHE_DIR |
Override the OrcaSlicer + result-cache directory. | platform cache dir |
FILAMENT_METER_CONFIG_DIR |
Override the settings-file directory. | platform config dir |
The cache location (unless overridden) is:
- Windows —
%LOCALAPPDATA%\filament-meter\Cache\orca\<version>\ - macOS —
~/Library/Caches/filament-meter/orca/<version>/ - Linux —
~/.cache/filament-meter/orca/<version>/
(platformdirs appends the Cache component on Windows, hence the
…\filament-meter\Cache\orca\… layout.)
OrcaSlicer discovery order
--orca PATHFILAMENT_METER_ORCAorca-slicer/OrcaSliceronPATH- Well-known install locations
(
%LOCALAPPDATA%\Programs\OrcaSlicer,/Applications/OrcaSlicer.app, …) - The download cache
If none is found and --no-auto-install is not set, a portable
OrcaSlicer build is downloaded automatically (no admin rights required).
Diagnostics
filament-meter --check prints the resolved environment and exits without
touching any model. A real example on Windows:
filament-meter 0.3.0
python: 3.12.14
platform: windows
cache dir: C:\Users\mikhe\AppData\Local\filament-meter\Cache\orca
cache writable: yes
OrcaSlicer: C:\Users\mikhe\AppData\Local\Programs\OrcaSlicer\orca-slicer.exe
OrcaSlicer version: unknown (system install; no VERSION marker)
A system OrcaSlicer install does not ship a VERSION marker file (and the
CLI does not answer --version), so the version line reads unknown — this
is expected, not an error. Builds downloaded by filament-meter itself write
a VERSION file next to the binary, so those do report a version.
How it works
┌────────────┐ sliced? ┌───────────────┐
PATH ─▶ │ discovery │ ───────────────▶ │ parser │ ─▶ FileReport
└────────────┘ yes ┌───────────│ (slice_info / │
│ │ │ gcode) │
│ no │ └───────────────┘
▼ │
┌────────────┐ │
│ orca │ find / download OrcaSlicer
└────────────┘ │
▼ │
┌────────────┐ │
│ slicer │ slice into a temp dir ──▶ sliced .3mf
└────────────┘ │
└────────────────┘
▼
┌────────────┐
│ report │ table / json / csv / md
└────────────┘
discovery— turns a path/glob into a de-duplicated, sorted file list.parser— readsMetadata/slice_info.configfirst, then theMetadata/plate_N.gcodefooter, and enriches plates with object / bed / nozzle data fromMetadata/plate_N.json. A rough length→mass estimate is used only when nothing better exists.slicer— builds the OrcaSlicer command line and drives the adaptive retry loop.orca— locates, downloads and provisions OrcaSlicer.report— renders the aggregateRunReport.
Project layout
.
├── pyproject.toml
├── README.md
└── src/filament_meter/
├── __init__.py # version + public API re-exports
├── __main__.py # python -m filament_meter
├── models.py # frozen dataclasses + SliceSource enum
├── errors.py # exception hierarchy
├── discovery.py # path (file/dir/glob) -> file list
├── cache.py # persistent result cache (path + size keyed)
├── settings.py # persisted currency / price settings
├── parser.py # sliced 3mf / gcode parsing
├── slicer.py # OrcaSlicer CLI invocation
├── orca.py # find / download / provision OrcaSlicer
├── report.py # table / json / csv / md renderers
└── cli/
├── __init__.py
├── app.py # argparse + main()
└── _common.py # exit codes, console setup, logging
Runtime artefacts:
<cache-dir>/orca/<version>/... # downloaded OrcaSlicer build (if needed)
<temp-dir>/filament-meter-*/ # per-file slicing workspace (auto-removed)
Troubleshooting
"OrcaSlicer was not found"
Either install OrcaSlicer, pass --orca <path>, set
FILAMENT_METER_ORCA, or drop --no-auto-install so the tool downloads
a portable build for you.
The download seems to hang
The first slice downloads a ~50 MB archive from GitHub into the cache.
Subsequent runs reuse it. Use --install-orca to fetch it up front, or
--check to see the cache location.
Numbers differ from Bambu Studio
For project .3mf files the settings embedded in the model always win
over --load-settings / --load-filaments; the flags only take effect
for bare .stl / .obj inputs. This is a slicer behaviour, not a bug —
and it means the reported numbers reflect the model author's intent.
--export-3mf swallowed my file name
Internally the export flag is always the last argument before the input
file. If you drive OrcaSlicer yourself, keep it last: it consumes the next
token as the output name.
"not in range" errors from OrcaSlicer
Models exported from a newer Bambu Studio can carry out-of-range values
(e.g. tree_support_wall_count: -1). filament-meter detects these in the
log and retries with --<key>=<min> automatically.
A .3mf reports "not sliced"
The file has only a slice_info.config header — it was never sliced, so it
physically contains no filament data. Drop --no-slice to let the tool
slice it first.
Roadmap
- Optional machine-readable
--profilepresets beyond the Bambu Lab A1 defaults. - A persistent usage registry (append-only CSV) with de-duplication, like
the legacy
filament-log.csv. - Multi-plate batch arrangement for large model libraries.
- Cost profiles per material (PLA / PETG / …).
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md for the local workflow. In short:
uv sync --all-extras
uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv run pytest -q
Keep changes minimal and respect the existing module boundaries
(discovery, parser, slicer, orca, report, cli).
Releasing
Releases are fully automated via GitHub Actions (.github/workflows/):
ci.yml— runs on every push tomainand on every pull request. Matrix-builds the sdist + wheel on Python 3.11 / 3.12 / 3.13, lints, type-checks, tests, and smoke-tests the built wheel.release.yml— runs on a published GitHub Release, or manually via the Run workflow button. Builds the wheel, smoke-tests it, and pushes it to PyPI (or TestPyPI) using PyPI Trusted Publishing (OIDC). No API tokens are stored in GitHub secrets.
One-time setup: register the trusted publisher on PyPI
The pypi and testpypi environments referenced by release.yml are
already configured in this repository.
- Add a pending publisher on PyPI at
https://pypi.org/manage/account/publishing/ with:
- PyPI project name:
filament-meter - Owner:
nimblemo - Repository:
filament-meter - Workflow filename:
release.yml - Environment name:
pypi
- PyPI project name:
- (Optional) Repeat on TestPyPI
(https://test.pypi.org/manage/account/publishing/) with environment
testpypiif you want a dry-run target.
A pending publisher creates the project on the first successful upload; it does not reserve the name in advance, so publish soon after registering it. No API tokens are stored in GitHub secrets.
Cutting a release
- Bump
versioninpyproject.tomlandsrc/filament_meter/__init__.py. - Add
.release-notes/vX.Y.Z.mdand updateCHANGELOG.md. - Commit, push, and publish a GitHub Release tagged
vX.Y.Zagainstmain.release.ymlbuilds the wheel, smoke-tests it and uploads it to PyPI.
For a manual upload instead of CI, use a PyPI API token:
uv build
UV_PUBLISH_TOKEN=<your-token> uv publish dist/*
License
Metadata
Release files for filament-meter 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| filament_meter-0.3.0.tar.gz | 104.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| filament_meter-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 149.9 kB
Release files / filament_meter-0.3.0.tar.gz
| Download URL | filament_meter-0.3.0.tar.gz |
|---|---|
| Size | 104.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c4aaea31e92af48312eb4e6f974a287cf2bd207ea209632a80c557aeced17ca2
|
|
BLAKE2b-256 checksum How to use checksums |
dce54e56d9bfbadeff8b68876bdf9477c63da36f8c8c272abe97b9a9b3795ce5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / filament_meter-0.3.0-py3-none-any.whl
| Download URL | filament_meter-0.3.0-py3-none-any.whl |
|---|---|
| Size | 45.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
492a216872b216093c9009ae05737d42317d37ef4b055c9185d1a0b8a1e4c667
|
|
BLAKE2b-256 checksum How to use checksums |
ef13d4df36a3ecdd29d69237ccf6af6df6d6c5ef0ac725ae533539e756867219
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|