mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-20 17:50:12 +02:00
● feat(slicer): server-side slicing via OrcaSlicer / Bambu Studio sidecar
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).
This commit is contained in:
@@ -926,6 +926,11 @@ export interface AppSettings {
|
||||
camera_view_mode: 'window' | 'embedded';
|
||||
// Preferred slicer
|
||||
preferred_slicer: 'bambu_studio' | 'orcaslicer';
|
||||
// Use the slicer-API sidecar for slicing (in-app modal) vs desktop URI scheme
|
||||
use_slicer_api: boolean;
|
||||
// Per-install sidecar URLs. Empty string falls back to the env defaults.
|
||||
orcaslicer_api_url: string;
|
||||
bambu_studio_api_url: string;
|
||||
// Prometheus metrics
|
||||
prometheus_enabled: boolean;
|
||||
prometheus_token: string;
|
||||
@@ -1105,6 +1110,57 @@ export interface BuiltinFilament {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Slice request/response — POST /library/files/{id}/slice and /archives/{id}/slice
|
||||
export interface SliceRequest {
|
||||
printer_preset_id: number;
|
||||
process_preset_id: number;
|
||||
filament_preset_id: number;
|
||||
plate?: number;
|
||||
export_3mf?: boolean;
|
||||
}
|
||||
|
||||
export interface SliceResponse {
|
||||
library_file_id: number;
|
||||
name: string;
|
||||
print_time_seconds: number;
|
||||
filament_used_g: number;
|
||||
filament_used_mm: number;
|
||||
used_embedded_settings: boolean;
|
||||
}
|
||||
|
||||
export interface SliceArchiveResponse {
|
||||
archive_id: number;
|
||||
name: string;
|
||||
print_time_seconds: number;
|
||||
filament_used_g: number;
|
||||
filament_used_mm: number;
|
||||
used_embedded_settings: boolean;
|
||||
}
|
||||
|
||||
// Background slice-job lifecycle. POST /slice returns 202 + this shape;
|
||||
// the frontend polls /slice-jobs/{id} until status is terminal.
|
||||
export type SliceJobStatus = 'pending' | 'running' | 'completed' | 'failed';
|
||||
|
||||
export interface SliceJobEnqueueResponse {
|
||||
job_id: number;
|
||||
status: SliceJobStatus;
|
||||
status_url: string;
|
||||
}
|
||||
|
||||
export interface SliceJobState {
|
||||
job_id: number;
|
||||
status: SliceJobStatus;
|
||||
kind: 'library_file' | 'archive';
|
||||
source_id: number;
|
||||
source_name: string;
|
||||
created_at: string;
|
||||
started_at: string | null;
|
||||
completed_at: string | null;
|
||||
result?: SliceResponse | SliceArchiveResponse;
|
||||
error_status?: number;
|
||||
error_detail?: string;
|
||||
}
|
||||
|
||||
// Local preset types (OrcaSlicer imports)
|
||||
export interface LocalPreset {
|
||||
id: number;
|
||||
@@ -4905,6 +4961,21 @@ export const api = {
|
||||
body: JSON.stringify({ url }),
|
||||
}),
|
||||
|
||||
// Slicer API — slice in the background. Both endpoints return 202 + a
|
||||
// job_id; poll /slice-jobs/{id} until status is `completed` or `failed`.
|
||||
sliceLibraryFile: (fileId: number, body: SliceRequest) =>
|
||||
request<SliceJobEnqueueResponse>(`/library/files/${fileId}/slice`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
sliceArchive: (archiveId: number, body: SliceRequest) =>
|
||||
request<SliceJobEnqueueResponse>(`/archives/${archiveId}/slice`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
getSliceJob: (jobId: number) =>
|
||||
request<SliceJobState>(`/slice-jobs/${jobId}`),
|
||||
|
||||
// Local Presets (OrcaSlicer imports)
|
||||
getLocalPresets: () =>
|
||||
request<LocalPresetsResponse>('/local-presets/'),
|
||||
|
||||
Reference in New Issue
Block a user