| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- 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/dimensions
- filament_g = None
- print_time = None
- dimensions = None
- cached = db.execute_query(
- "SELECT filament_g, print_time, dimensions 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']
- dimensions = cached[0]['dimensions']
-
- # Insert into DB
- query = "INSERT INTO order_files (order_id, filename, file_path, file_size, quantity, file_hash, print_time, filament_g, preview_path, dimensions) VALUES (NULL, %s, %s, %s, 1, %s, %s, %s, NULL, %s)"
- f_id = db.execute_commit(query, (filename, db_file_path, file_size, file_hash, print_time, filament_g, dimensions))
-
- # 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,
- "dimensions": dimensions,
- "status": "success"
- })
-
- if not imported_files:
- raise Exception("No valid 3D models found in the downloaded archive.")
-
- return imported_files
|