Explorar o código

fix(queue): create archive for library-file prints so 3MF usage tracking works (#364)

When a print was queued from the file manager (library_file_id path),
the scheduler never created an archive or called register_expected_print().
This forced on_print_start to re-download the 3MF via FTP, and if that
failed, a fallback archive with no file was created — breaking 3MF-based
filament usage tracking entirely. The queue item's archive_id also stayed
NULL, so the usage tracker couldn't find the AMS slot mapping.

Now the scheduler creates an archive from the library file before uploading,
sets item.archive_id, and the existing register_expected_print() call fires
since archive is no longer None.
maziggy hai 3 meses
pai
achega
e13a72ef19
Modificáronse 2 ficheiros con 22 adicións e 2 borrados
  1. 1 0
      CHANGELOG.md
  2. 21 2
      backend/app/services/print_scheduler.py

+ 1 - 0
CHANGELOG.md

@@ -44,6 +44,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Spool Form Scrollbar Flicker in Edge** ([#364](https://github.com/maziggy/bambuddy/issues/364)) — The Add/Edit Spool modal's scrollable area used `overflow-y: auto`, which on Windows Edge (where scrollbars take layout space) caused the scrollbar to appear and disappear on hover — making the color picker unusable at certain zoom levels. Added `scrollbar-gutter: stable` to reserve scrollbar space and prevent layout thrashing.
 - **Archive Duplicate Badge Misses Name-Based Duplicates** ([#315](https://github.com/maziggy/bambuddy/issues/315)) — The duplicate badge on archive cards only matched by file content hash, so re-sliced prints of the same model (different GCODE, same print name) were not flagged as duplicates. Now also matches by print name (case-insensitive), consistent with the detail view's duplicate detection.
 - **Schedule Print Allows No Plate Selected for Multi-Plate Files** ([#394](https://github.com/maziggy/bambuddy/issues/394)) — When scheduling a multi-plate file from the file manager, the modal showed a "Selection required" warning but still allowed submission without selecting a plate. The job defaulted to plate 1, but the queue item didn't indicate which plate, and editing showed no plate selected. Now auto-selects the first plate by default when plates load, and the submit button validation applies to both archive and library files.
+- **3MF Usage Tracking Broken for Queue Prints from File Manager** ([#364](https://github.com/maziggy/bambuddy/issues/364)) — When a print was queued from the file manager (library file), the scheduler did not create an archive or register the expected print. The `on_print_start` callback had to re-download the 3MF from the printer via FTP, and if that failed, a fallback archive was created without the 3MF file — making 3MF-based filament usage tracking impossible. The queue item's `archive_id` also remained NULL, so the usage tracker could not find the queue's AMS slot mapping for correct spool resolution. The scheduler now creates an archive from the library file before uploading, links it to the queue item, and registers it as an expected print — matching the behavior of the direct library print route.
 
 ### Improved
 - **Skip Objects: Click-to-Enlarge Lightbox** ([#396](https://github.com/maziggy/bambuddy/issues/396)) — The skip objects modal's small 208px image panel made it difficult to distinguish object markers when parts are small or close together. Clicking the image now opens a fullscreen lightbox overlay with the same image and markers at a much larger size (up to 600px). The 24px marker circles are proportionally smaller relative to the enlarged image, solving the overlap problem. Close via X button, Escape key, or clicking the backdrop. Escape cascades correctly — closes lightbox first, then the modal.

+ 21 - 2
backend/app/services/print_scheduler.py

@@ -903,12 +903,31 @@ class PrintScheduler:
                 await self._power_off_if_needed(db, item)
                 return
             # Library files store absolute paths
-            from pathlib import Path
-
             lib_path = Path(library_file.file_path)
             file_path = lib_path if lib_path.is_absolute() else settings.base_dir / library_file.file_path
             filename = library_file.filename
 
+            # Create archive from library file so usage tracking has access to the 3MF
+            try:
+                from backend.app.services.archive import ArchiveService
+
+                archive_service = ArchiveService(db)
+                archive = await archive_service.archive_print(
+                    printer_id=item.printer_id,
+                    source_file=file_path,
+                )
+                if archive:
+                    item.archive_id = archive.id
+                    await db.flush()
+                    logger.info(
+                        "Queue item %s: Created archive %s from library file %s",
+                        item.id,
+                        archive.id,
+                        item.library_file_id,
+                    )
+            except Exception as e:
+                logger.warning("Queue item %s: Failed to create archive from library file: %s", item.id, e)
+
         else:
             # Neither archive nor library file specified
             item.status = "failed"