Add multi-file selection and bulk download to printer file browser

- Add checkbox selection for individual files
- Add Select All / Deselect All buttons
- Download single file directly, multiple files as ZIP archive
- Add POST /printers/{id}/files/download-zip endpoint
- Clear selection after successful download
- Support bulk delete for multiple files
- Update docs (changelog, wiki, website)

Closes #144
This commit is contained in:
maziggy
2026-01-24 08:28:03 +01:00
parent 8ba04dfa89
commit e0e53cb680
6 changed files with 610 additions and 435 deletions
+44
View File
@@ -707,6 +707,50 @@ async def download_printer_file(
)
@router.post("/{printer_id}/files/download-zip")
async def download_printer_files_as_zip(
printer_id: int,
request: dict,
db: AsyncSession = Depends(get_db),
):
"""Download multiple files from the printer as a ZIP archive."""
import io
paths = request.get("paths", [])
if not paths:
raise HTTPException(400, "No files specified")
result = await db.execute(select(Printer).where(Printer.id == printer_id))
printer = result.scalar_one_or_none()
if not printer:
raise HTTPException(404, "Printer not found")
# Create ZIP in memory
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
for path in paths:
try:
data = await download_file_bytes_async(printer.ip_address, printer.access_code, path)
if data:
filename = path.split("/")[-1]
zf.writestr(filename, data)
except Exception as e:
logging.warning(f"Failed to add {path} to ZIP: {e}")
continue
zip_buffer.seek(0)
zip_data = zip_buffer.read()
if len(zip_data) == 0:
raise HTTPException(404, "No files could be downloaded")
return Response(
content=zip_data,
media_type="application/zip",
headers={"Content-Disposition": 'attachment; filename="printer-files.zip"'},
)
@router.delete("/{printer_id}/files")
async def delete_printer_file(
printer_id: int,