Browse Source

Fix print failing on files with spaces in filename (#824)

  Filenames with spaces (e.g. "Junktion Box PRO 90.3mf") broke the MQTT
  print command because the url field (ftp://file name.3mf) contained
  unencoded spaces the firmware can't parse. FTP upload succeeded but the
  printer silently ignored the command and stayed IDLE. Replace spaces
  with underscores in the remote filename before upload and print command.
maziggy 2 tháng trước cách đây
mục cha
commit
886fb90339

+ 1 - 0
CHANGELOG.md

@@ -30,6 +30,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **SpoolBuddy Boot Splash Polished** — New splash image displays only the SpoolBuddy logo (removed Bambuddy branding) with green glow bloom, radial gradient background, light rays, and vignette. A generator script (`generate_splash.py`) is included for easy customization. Also reduced redundant initramfs rebuilds during install by deferring the rebuild until after the Plymouth theme is configured.
 
 ### Fixed
+- **Print Fails on Files With Spaces in Name** ([#824](https://github.com/maziggy/bambuddy/issues/824)) — Printing files with spaces in their filename (e.g. "Junktion Box PRO 90.3mf") caused the printer to silently ignore the print command and remain IDLE. The FTP upload succeeded, but the MQTT print command's `url` field (`ftp://file name.3mf`) contained unencoded spaces that the firmware couldn't parse. Fixed by replacing spaces with underscores in the remote filename before upload. Reported by @benjamdev.
 - **SpoolBuddy Low Filament Warning Missing Slot Number** — The status bar low filament warning showed "AMS B" instead of the specific slot like "B2". Now uses `formatSlotLabel` to display the full slot label (e.g. "Low Filament: PLA (B2) - 4% remaining").
 - **SpoolBuddy Read Tag Diagnostic Fails on NTAG Tags** — The `read_tag.py` diagnostic script had five issues preventing NTAG reads: (1) SAK `0x04` (MIFARE Ultralight family) was rejected as "unsupported tag type" — now accepts both `0x00` and `0x04`. (2) `ntag_read_pages` had TX CRC off (should be on per NTAG spec), no Crypto1 clear, and no IDLE→TRANSCEIVE state reset. (3) The PN5180 enters an unrecoverable state after an NTAG READ command — added full GPIO hardware reset between each 4-page batch. (4) Reading past the end of smaller tags (MIFARE Ultralight has 16 pages vs NTAG's 44+) caused a hard failure — now returns partial data gracefully. (5) `ntag_write_page`/`ntag_write_pages` had the same stale CRC/state issues plus unreliable ACK checking and post-write verification — synced with daemon.
 - **Delete Tag Leaves Stale Tag Type** — The "Delete Tag" button in the spool edit modal only cleared `tag_uid` but left `tray_uuid`, `tag_type`, and `data_origin` intact. All tag-related fields are now cleared together.

+ 4 - 0
backend/app/services/background_dispatch.py

@@ -565,6 +565,8 @@ class BackgroundDispatchService:
             elif base_name.endswith(".3mf"):
                 base_name = base_name[:-4]
             remote_filename = f"{base_name}.3mf"
+            # Sanitize: firmware parses ftp://{filename} as a URL, spaces break it
+            remote_filename = remote_filename.replace(" ", "_")
             remote_path = f"/{remote_filename}"
 
             ftp_retry_enabled, ftp_retry_count, ftp_retry_delay, ftp_timeout = await get_ftp_retry_settings()
@@ -732,6 +734,8 @@ class BackgroundDispatchService:
             elif base_name.endswith(".3mf"):
                 base_name = base_name[:-4]
             remote_filename = f"{base_name}.3mf"
+            # Sanitize: firmware parses ftp://{filename} as a URL, spaces break it
+            remote_filename = remote_filename.replace(" ", "_")
             remote_path = f"/{remote_filename}"
 
             ftp_retry_enabled, ftp_retry_count, ftp_retry_delay, ftp_timeout = await get_ftp_retry_settings()

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

@@ -1571,6 +1571,8 @@ class PrintScheduler:
         elif base_name.endswith(".3mf"):
             base_name = base_name[:-4]  # Remove .3mf
         remote_filename = f"{base_name}.3mf"
+        # Sanitize: firmware parses ftp://{filename} as a URL, spaces break it
+        remote_filename = remote_filename.replace(" ", "_")
         # Upload to root directory (not /cache/) - the start_print command references
         # files by name only (ftp://{filename}), so they must be in the root
         remote_path = f"/{remote_filename}"