mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-04 13:11:35 +02:00
Adds an optional slicer-api/ Compose stack and wires Bambuddy's File
Manager, Archives, and MakerWorld pages to a new server-side Slice flow.
Slicing runs as an in-memory background job (POST returns 202 + job_id,
polled via GET /api/v1/slice-jobs/{id}) so a multi-minute slice no
longer pins the modal; result lands as a new .gcode.3mf in the same
folder (or new archive for archive sources) with the embedded
thumbnail extracted.
Backend
- New services: slice_dispatch (in-memory dispatcher, 30min retention
sweep) and slicer_api (HTTP bridge with 4xx/5xx/connection error
split that drives the 3MF embedded-settings fallback retry path).
- New schemas: SliceRequest, SliceResponse, SliceArchiveResponse,
SliceJobEnqueueResponse.
- New routes: POST /library/files/{id}/slice,
POST /archives/{id}/slice, GET /api/v1/slice-jobs/{id} (gated on
LIBRARY_READ since job IDs are sequential and the body leaks source
filenames and result IDs).
- AppSettings + env defaults: use_slicer_api, orcaslicer_api_url,
bambu_studio_api_url. DB-stored values override env defaults.
Frontend
- New SliceModal handles preset gating; enqueues then closes
immediately.
- New SliceJobTrackerProvider polls active jobs at app level, surfaces
a single toast per job (queued -> running -> completed / failed)
and invalidates library/archives queries on terminal status.
- Settings -> Workflow -> Slicer card: preferred slicer dropdown,
Use Slicer API toggle, contextual sidecar URL field.
- File Manager / Archives / MakerWorld get a Slice button gated on
the Use Slicer API setting.
- gcode-viewer adapter learns ?library_file=<id> so sliced library
files preview inline.
i18n
- New slice.* and settings.{useSlicerApi,slicerCard,orcaslicerApiUrl,
bambuStudioApiUrl,slicerApiUrlDescription,useSlicerApiDescription}
+ fileManager.noPermissionSlice keys across all 8 locales (en, de,
fr, it, ja, pt-BR, zh-CN, zh-TW). English fully translated, German
fully translated, the other six seeded with English fallbacks
pending native translation.
Tests
- 10 backend integration tests in test_library_slice_api.py covering
validation (404/400), happy-path enqueue, sidecar-down, 3MF
embedded-settings fallback, STL no-fallback, and preset-error ->
failed job paths.
- New unit tests in test_slicer_api.py for the HTTP bridge.
- 5 new SliceModal frontend tests covering preset gating, library +
archive enqueue paths, error surface, and preset-load failure.
- Existing SettingsPage tests adjusted: slicer dropdown asserts now
switch to the Workflow tab first; added a beforeEach URL reset so
one test's tab click doesn't bleed into sibling tests.
Sidecar
- New slicer-api/ folder is self-contained and optional. Two services
(orca-slicer-api on 3003, bambu-studio-api on 3001 behind --profile
bambu) build via Docker git-build-context from
maziggy/orca-slicer-api@bambuddy/profile-resolver. The fork patches
the OrcaSlicer CLI's profile compatibility quirks (inherits-chain
resolver, from:User -> system rewrite, '# ' clone-prefix strip,
sentinel-value strip) empirically required to slice real GUI
exports without segfaulting the CLI.
Docs
- CHANGELOG entry under [0.2.4b1] - Unreleased Added.
- README File Manager bullet for the new server-side Slice button.
- bambuddy-website features.html: new card under "Configurable Slicer".
- bambuddy-wiki: new page features/slicer-api.md + nav entry +
features index card.
Notes
- Opt-in: with Use Slicer API off, the existing "open in desktop
slicer via URI" flow is the default and unchanged.
- 3MF inputs that segfault the CLI on --load-settings transparently
retry with embedded settings; the resulting job carries
used_embedded_settings: true.
- Sliced files always export as .gcode.3mf so File Manager picks up
the embedded thumbnail; file_type is set to "gcode" (blue badge).
98 lines
3.7 KiB
Python
98 lines
3.7 KiB
Python
import logging
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from pydantic_settings import BaseSettings
|
|
|
|
# Application version - single source of truth
|
|
APP_VERSION = "0.2.4b1"
|
|
GITHUB_REPO = "maziggy/bambuddy"
|
|
BUG_REPORT_RELAY_URL = os.environ.get("BUG_REPORT_RELAY_URL", "https://bambuddy.cool/api/bug-report")
|
|
|
|
# App directory - where the application is installed (for static files)
|
|
_app_dir = Path(__file__).resolve().parent.parent.parent.parent
|
|
|
|
# Data directory - for persistent data (database, archives)
|
|
# Use DATA_DIR env var if set (Docker), otherwise use project root (local dev)
|
|
_data_dir_env = os.environ.get("DATA_DIR")
|
|
_data_dir = Path(_data_dir_env) if _data_dir_env else _app_dir
|
|
|
|
# Plate calibration directory - special handling to maintain backwards compatibility
|
|
# Docker: DATA_DIR/plate_calibration (e.g., /data/plate_calibration)
|
|
# Local dev: project_root/data/plate_calibration (original location)
|
|
_plate_cal_dir = Path(_data_dir_env) / "plate_calibration" if _data_dir_env else _app_dir / "data" / "plate_calibration"
|
|
|
|
# Log directory - use LOG_DIR env var if set, otherwise use app_dir/logs
|
|
_log_dir_env = os.environ.get("LOG_DIR")
|
|
_log_dir = Path(_log_dir_env) if _log_dir_env else _app_dir / "logs"
|
|
|
|
|
|
def _migrate_database() -> Path:
|
|
"""Migrate database from old name to new name if needed."""
|
|
old_db = _data_dir / "bambutrack.db"
|
|
new_db = _data_dir / "bambuddy.db"
|
|
|
|
# If old database exists and new one doesn't, rename it
|
|
if old_db.exists() and not new_db.exists():
|
|
try:
|
|
old_db.rename(new_db)
|
|
logging.info("Migrated database: %s -> %s", old_db, new_db)
|
|
except Exception as e:
|
|
logging.warning("Could not migrate database: %s. Using old location.", e)
|
|
return old_db
|
|
|
|
# If old database exists (and new one now exists too), it was migrated
|
|
# If only new exists, use new
|
|
# If neither exists, use new (will be created)
|
|
return new_db if new_db.exists() or not old_db.exists() else old_db
|
|
|
|
|
|
# External DATABASE_URL takes priority (PostgreSQL support)
|
|
_external_db_url = os.environ.get("DATABASE_URL")
|
|
|
|
# Determine database path (handles migration) — only used for SQLite
|
|
_db_path = _migrate_database() if not _external_db_url else None
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
app_name: str = "Bambuddy"
|
|
debug: bool = False # Default to production mode
|
|
|
|
# Paths
|
|
base_dir: Path = _data_dir # For backwards compatibility
|
|
archive_dir: Path = _data_dir / "archive"
|
|
plate_calibration_dir: Path = _plate_cal_dir # Plate detection references
|
|
static_dir: Path = _app_dir / "static" # Static files are part of app, not data
|
|
log_dir: Path = _log_dir
|
|
database_url: str = _external_db_url or f"sqlite+aiosqlite:///{_db_path}"
|
|
|
|
# Logging
|
|
log_level: str = "INFO" # Override with LOG_LEVEL env var or DEBUG=true
|
|
log_to_file: bool = True # Set to false to disable file logging
|
|
|
|
# API
|
|
api_prefix: str = "/api/v1"
|
|
|
|
# Slicer API sidecars. Defaults match the docker-compose.yml ports in the
|
|
# orca-slicer-api fork (https://github.com/maziggy/orca-slicer-api):
|
|
# OrcaSlicer → port 3003 (default profile)
|
|
# BambuStudio → port 3001 (built locally via Dockerfile.bambu-studio)
|
|
# The slice route picks which one based on the user's preferred_slicer
|
|
# setting.
|
|
slicer_api_url: str = "http://localhost:3003"
|
|
bambu_studio_api_url: str = "http://localhost:3001"
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
env_file_encoding = "utf-8"
|
|
|
|
|
|
settings = Settings()
|
|
|
|
# Ensure directories exist
|
|
settings.archive_dir.mkdir(parents=True, exist_ok=True)
|
|
settings.plate_calibration_dir.mkdir(parents=True, exist_ok=True)
|
|
settings.static_dir.mkdir(exist_ok=True)
|
|
if settings.log_to_file:
|
|
settings.log_dir.mkdir(exist_ok=True)
|