diff --git a/backend/app/api/routes/printers.py b/backend/app/api/routes/printers.py index 106a8a53c..f45fc69d3 100644 --- a/backend/app/api/routes/printers.py +++ b/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" + # 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(