import re import requests import os import zipfile import io import uuid from typing import List, Dict # Standard headers to avoid bot detection HEADERS = { "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", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", } class LinkImporter: @staticmethod def extract_id(url: str): # Thingiverse: thingiverse.com/thing:12345 or thingiverse.com/thing:12345/files thing_match = re.search(r"thingiverse\.com/thing:(\d+)", url) if thing_match: return "thingiverse", thing_match.group(1) # Printables: printables.com/model/12345-name or printables.com/en/model/12345 printables_match = re.search(r"printables\.com/(?:[a-z]{2}/)?model/(\d+)", url) if printables_match: return "printables", printables_match.group(1) return None, None @staticmethod def download_and_extract(url: str, upload_dir: str, background_tasks=None) -> List[Dict]: service, item_id = LinkImporter.extract_id(url) if not service: raise ValueError("Unsupported URL format. Please use Thingiverse or Printables link.") download_url = "" if service == "thingiverse": download_url = f"https://www.thingiverse.com/thing:{item_id}/zip" elif service == "printables": download_url = f"https://www.printables.com/model/{item_id}/files/zip" print(f"DEBUG: Importing from {service} ID {item_id}. URL: {download_url}") try: response = requests.get(download_url, headers=HEADERS, stream=True, timeout=60) response.raise_for_status() except Exception as e: raise Exception(f"Failed to download from {service}: {e}") import db import hashlib from services import order_processing # Process the ZIP in memory imported_files = [] with zipfile.ZipFile(io.BytesIO(response.content)) as z: for file_info in z.infolist(): filename = os.path.basename(file_info.filename) if not filename or filename.startswith('.') or filename.startswith('__MACOSX'): continue ext = os.path.splitext(filename)[1].lower() if ext in ['.stl', '.obj', '.step', '.stp', '.3mf']: unique_name = f"{uuid.uuid4().hex}{ext}" save_path = os.path.join(upload_dir, unique_name) db_file_path = f"uploads/{unique_name}" # Read content to memory to hash it content = z.read(file_info.filename) with open(save_path, "wb") as target: target.write(content) file_hash = hashlib.sha256(content).hexdigest() file_size = len(content) # Check cache for filament/time filament_g = None print_time = None cached = db.execute_query( "SELECT filament_g, print_time FROM order_files WHERE file_hash = %s AND print_time IS NOT NULL LIMIT 1", (file_hash,) ) if cached: filament_g = cached[0]['filament_g'] print_time = cached[0]['print_time'] # Insert into DB 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)" f_id = db.execute_commit(query, (filename, db_file_path, file_size, file_hash, print_time, filament_g)) # Trigger background processing (previews, etc.) if background_tasks: background_tasks.add_task(order_processing.process_single_file_async, f_id) imported_files.append({ "id": f_id, "filename": filename, "size": file_size, "print_time": print_time, "filament_g": filament_g, "status": "success" }) if not imported_files: raise Exception("No valid 3D models found in the downloaded archive.") return imported_files