Browse Source

Fix FTP download failing for .3mf files without .gcode extension

The cover and objects endpoints only tried downloading files with the
.gcode.3mf extension pattern, but files uploaded directly (not sliced
via Bambu Studio) are stored as plain .3mf files.

This caused FTP error 550 (file not found) when trying to display cover
images for prints started from BamBuddy archive.

Now tries both naming patterns:
- {name}.gcode.3mf (sliced via Bambu Studio)
- {name}.3mf (uploaded directly)

Fixes #79
maziggy 4 months ago
parent
commit
3dbbd7ea19
1 changed files with 44 additions and 24 deletions
  1. 44 24
      backend/app/api/routes/printers.py

+ 44 - 24
backend/app/api/routes/printers.py

@@ -488,24 +488,37 @@ async def get_printer_cover(
         if cache_key in _cover_cache[printer_id]:
             return Response(content=_cover_cache[printer_id][cache_key], media_type="image/png")
 
-    # Build 3MF filename from subtask_name
-    # Bambu printers store files as "name.gcode.3mf"
-    filename = subtask_name
-    if not filename.endswith(".3mf"):
-        filename = filename + ".gcode.3mf"
-
-    # Try to download the 3MF file from printer
-    temp_path = settings.archive_dir / "temp" / f"cover_{printer_id}_{filename}"
-    temp_path.parent.mkdir(parents=True, exist_ok=True)
+    # Build possible 3MF filenames from subtask_name
+    # Bambu printers may store files as "name.gcode.3mf" (sliced via Bambu Studio)
+    # or just "name.3mf" (uploaded directly)
+    possible_filenames = []
+    if subtask_name.endswith(".3mf"):
+        possible_filenames.append(subtask_name)
+    else:
+        # Try both naming patterns
+        possible_filenames.append(f"{subtask_name}.gcode.3mf")
+        possible_filenames.append(f"{subtask_name}.3mf")
+
+    # Build list of all remote paths to try
+    remote_paths = []
+    for filename in possible_filenames:
+        remote_paths.extend(
+            [
+                f"/{filename}",  # Root directory (most common)
+                f"/cache/{filename}",
+                f"/model/{filename}",
+                f"/data/{filename}",
+            ]
+        )
 
-    remote_paths = [
-        f"/{filename}",  # Root directory (most common)
-        f"/cache/{filename}",
-        f"/model/{filename}",
-        f"/data/{filename}",
-    ]
+    # Use first filename for temp path (will be reused)
+    temp_filename = possible_filenames[0]
+    temp_path = settings.archive_dir / "temp" / f"cover_{printer_id}_{temp_filename}"
+    temp_path.parent.mkdir(parents=True, exist_ok=True)
 
-    logger.info(f"Trying to download cover for '{filename}' from {printer.ip_address}")
+    logger.info(
+        f"Trying to download cover for '{subtask_name}' from {printer.ip_address} (trying {len(remote_paths)} paths)"
+    )
 
     # Retry logic for transient FTP failures
     max_retries = 2
@@ -535,7 +548,8 @@ async def get_printer_cover(
 
     if not downloaded:
         raise HTTPException(
-            404, f"Could not download 3MF file '{filename}' from printer {printer.ip_address}. Tried: {remote_paths}"
+            404,
+            f"Could not download 3MF file for '{subtask_name}' from printer {printer.ip_address}. Tried: {possible_filenames}",
         )
 
     # Verify file actually exists and has content
@@ -547,7 +561,7 @@ async def get_printer_cover(
 
     if file_size == 0:
         temp_path.unlink()
-        raise HTTPException(500, f"Downloaded file is empty: {filename}")
+        raise HTTPException(500, f"Downloaded file is empty for '{subtask_name}'")
 
     try:
         # Extract thumbnail from 3MF (which is a ZIP file)
@@ -1207,16 +1221,22 @@ async def get_printable_objects(
             from backend.app.services.archive import extract_printable_objects_from_3mf
             from backend.app.services.bambu_ftp import download_file_try_paths_async
 
-            # Build 3MF filename
-            filename = subtask_name
-            if not filename.endswith(".3mf"):
-                filename = filename + ".gcode.3mf"
+            # Build possible 3MF filenames (try both .gcode.3mf and .3mf)
+            possible_filenames = []
+            if subtask_name.endswith(".3mf"):
+                possible_filenames.append(subtask_name)
+            else:
+                possible_filenames.append(f"{subtask_name}.gcode.3mf")
+                possible_filenames.append(f"{subtask_name}.3mf")
 
             # Download 3MF from printer
-            temp_path = settings.archive_dir / "temp" / f"objects_{printer_id}_{filename}"
+            temp_path = settings.archive_dir / "temp" / f"objects_{printer_id}_{possible_filenames[0]}"
             temp_path.parent.mkdir(parents=True, exist_ok=True)
 
-            remote_paths = [f"/{filename}", f"/cache/{filename}", f"/model/{filename}"]
+            # Build list of all remote paths to try
+            remote_paths = []
+            for filename in possible_filenames:
+                remote_paths.extend([f"/{filename}", f"/cache/{filename}", f"/model/{filename}"])
 
             try:
                 downloaded = await download_file_try_paths_async(