A plugin-based CLI tool for syncing 3D model designs across different platforms. Currently supports Thingiverse and local directory structures.
- 🔄 Bidirectional sync between Thingiverse and local directories
- 🔌 Plugin architecture for easy extension to other platforms
- 📁 Local directory support with metadata preservation
- 🔐 Flexible configuration system with multiple sources
- 📦 Unified model representation for cross-platform compatibility
git clone https://github.com/nomike/modpub.git
cd modpub
pip install -e .pip install -e '.[dev]'You'll need a Thingiverse API token from https://www.thingiverse.com/apps
There are three ways to provide the token:
export THINGIVERSE_TOKEN=your_token_here# Create config in home directory (global)
modpub config init --location home
# Or create config in current directory (project-specific)
modpub config init --location local
# Edit the file and add your tokenTHINGIVERSE_TOKEN=your_token modpub sync --from thingiverse:123456 --to localdir:/pathmodpub sync --from thingiverse:6961850 --to localdir:./my-design# Create new thing
modpub sync --from localdir:./my-design --to thingiverse:new
# Update existing thing
modpub sync --from localdir:./my-design --to thingiverse:6961850modpub sync --from localdir:./design-v1 --to localdir:./design-v2The configuration system supports multiple sources with the following precedence (highest to lowest):
- Environment variables -
THINGIVERSE_TOKEN - Current directory -
.modpub.conf - Home directory -
~/.modpub.conf
Configuration files use INI format:
[thingiverse]
token = your_token_here
[localdir]
# Future: default author info
author_name = Your Name
author_url = https://example.com
[defaults]
# Future: default license for new designs
license = CC-BY-4.0# Show sample configuration
modpub config init
# Create config file in home directory
modpub config init --location home
# Create config file in current directory
modpub config init --location local
# Show current configuration (tokens are masked)
modpub config showYou can also run modpub as a Python module:
python -m modpub sync --from thingiverse:123456 --to localdir:./outputThe localdir plugin uses the following structure:
my-design/
├── README.md # Human-readable description and instructions
├── metadata.json # Complete design metadata
├── files/ # STL, SCAD, 3MF, STEP files
│ ├── model.stl
│ └── source.scad
└── images/ # Photos and renders
├── photo1.jpg
└── render.png
{
"title": "My Awesome Design",
"description_md": "A detailed description...",
"instructions_md": "Print with 0.2mm layers...",
"tags": ["3d-printing", "useful"],
"license": {
"key": "CC-BY-4.0",
"name": "Creative Commons - Attribution 4.0"
},
"author": {
"name": "Designer Name",
"url": "https://example.com"
}
}The localdir plugin creates human-readable README.md files with:
- Main description from design metadata
- Embedded HTML markers for round-trip preservation
- Print instructions in a dedicated section
- Automatic fallback to heading-based parsing when markers are absent
modpub uses a plugin-based architecture where each platform adapter implements the RepositoryPlugin interface:
- Core Model (
modpub.core.model): UnifiedDesignclass for cross-platform compatibility - Plugin Registry (
modpub.plugins): Dynamic plugin loading via Python entry points - Plugins: Platform-specific adapters (Thingiverse, LocalDir, etc.)
- thingiverse: Full read/write support for Thingiverse via their API
- localdir: Local directory structure with metadata preservation
Plugins are loaded via Python entry points. To create a new plugin:
- Implement the
RepositoryPluginbase class - Register in
pyproject.toml:[project.entry-points."modpub.plugins"] myplugin = "mypackage.plugin:MyPlugin"
python -m pytest tests/python -m pytest tests/test_model.py::test_roundtrip_jsonmodpub/
├── modpub/
│ ├── __init__.py
│ ├── __main__.py # Module entry point
│ ├── cli.py # CLI interface
│ ├── config.py # Configuration management
│ ├── core/
│ │ ├── model.py # Unified Design model
│ │ ├── storage.py # File handling utilities
│ │ └── licenses.py # License mapping
│ └── plugins/
│ ├── registry.py # Plugin loader
│ ├── repository.py # Base plugin interface
│ ├── thingiverse/ # Thingiverse plugin
│ └── localdir/ # Local directory plugin
└── tests/
└── ...
Licenses are normalized to canonical keys (e.g., CC-BY-4.0) and mapped to platform-specific identifiers when publishing. Unknown mappings are omitted from the outbound payload.
If you get "THINGIVERSE_TOKEN not set" errors:
- Check your token is correctly set:
modpub config show - Verify the token at https://www.thingiverse.com/apps
- Ensure config file has correct format (INI style)
If you get "Unknown plugin" errors after installation:
- Reinstall in development mode:
pip install -e . - Check entry points are registered:
pip show modpub
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
Nomike Postmann
- Built to solve the problem of keeping 3D designs synchronized across platforms
- Inspired by the need for better tooling in the 3D printing community