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
This commit is contained in:
maziggy
2026-01-14 07:57:49 +01:00
parent 372df9f06a
commit 3dbbd7ea19
+43 -23
View File
@@ -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"
# 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")
# Try to download the 3MF file from printer
temp_path = settings.archive_dir / "temp" / f"cover_{printer_id}_{filename}"
# 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}",
]
)
# 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)
remote_paths = [
f"/{filename}", # Root directory (most common)
f"/cache/{filename}",
f"/model/{filename}",
f"/data/{filename}",
]
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(