link_importer.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import re
  2. import requests
  3. import os
  4. import zipfile
  5. import io
  6. import uuid
  7. from typing import List, Dict
  8. # Standard headers to avoid bot detection
  9. HEADERS = {
  10. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
  11. "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
  12. }
  13. class LinkImporter:
  14. @staticmethod
  15. def extract_id(url: str):
  16. # Thingiverse: thingiverse.com/thing:12345 or thingiverse.com/thing:12345/files
  17. thing_match = re.search(r"thingiverse\.com/thing:(\d+)", url)
  18. if thing_match:
  19. return "thingiverse", thing_match.group(1)
  20. # Printables: printables.com/model/12345-name or printables.com/en/model/12345
  21. printables_match = re.search(r"printables\.com/(?:[a-z]{2}/)?model/(\d+)", url)
  22. if printables_match:
  23. return "printables", printables_match.group(1)
  24. return None, None
  25. @staticmethod
  26. def download_and_extract(url: str, upload_dir: str, background_tasks=None) -> List[Dict]:
  27. service, item_id = LinkImporter.extract_id(url)
  28. if not service:
  29. raise ValueError("Unsupported URL format. Please use Thingiverse or Printables link.")
  30. download_url = ""
  31. if service == "thingiverse":
  32. download_url = f"https://www.thingiverse.com/thing:{item_id}/zip"
  33. elif service == "printables":
  34. download_url = f"https://www.printables.com/model/{item_id}/files/zip"
  35. print(f"DEBUG: Importing from {service} ID {item_id}. URL: {download_url}")
  36. try:
  37. response = requests.get(download_url, headers=HEADERS, stream=True, timeout=60)
  38. response.raise_for_status()
  39. except Exception as e:
  40. raise Exception(f"Failed to download from {service}: {e}")
  41. import db
  42. import hashlib
  43. from services import order_processing
  44. # Process the ZIP in memory
  45. imported_files = []
  46. with zipfile.ZipFile(io.BytesIO(response.content)) as z:
  47. for file_info in z.infolist():
  48. filename = os.path.basename(file_info.filename)
  49. if not filename or filename.startswith('.') or filename.startswith('__MACOSX'):
  50. continue
  51. ext = os.path.splitext(filename)[1].lower()
  52. if ext in ['.stl', '.obj', '.step', '.stp', '.3mf']:
  53. unique_name = f"{uuid.uuid4().hex}{ext}"
  54. save_path = os.path.join(upload_dir, unique_name)
  55. db_file_path = f"uploads/{unique_name}"
  56. # Read content to memory to hash it
  57. content = z.read(file_info.filename)
  58. with open(save_path, "wb") as target:
  59. target.write(content)
  60. file_hash = hashlib.sha256(content).hexdigest()
  61. file_size = len(content)
  62. # Check cache for filament/time
  63. filament_g = None
  64. print_time = None
  65. cached = db.execute_query(
  66. "SELECT filament_g, print_time FROM order_files WHERE file_hash = %s AND print_time IS NOT NULL LIMIT 1",
  67. (file_hash,)
  68. )
  69. if cached:
  70. filament_g = cached[0]['filament_g']
  71. print_time = cached[0]['print_time']
  72. # Insert into DB
  73. query = "INSERT INTO order_files (order_id, filename, file_path, file_size, quantity, file_hash, print_time, filament_g, preview_path) VALUES (NULL, %s, %s, %s, 1, %s, %s, %s, NULL)"
  74. f_id = db.execute_commit(query, (filename, db_file_path, file_size, file_hash, print_time, filament_g))
  75. # Trigger background processing (previews, etc.)
  76. if background_tasks:
  77. background_tasks.add_task(order_processing.process_single_file_async, f_id)
  78. imported_files.append({
  79. "id": f_id,
  80. "filename": filename,
  81. "size": file_size,
  82. "print_time": print_time,
  83. "filament_g": filament_g,
  84. "status": "success"
  85. })
  86. if not imported_files:
  87. raise Exception("No valid 3D models found in the downloaded archive.")
  88. return imported_files