diff --git a/.gitignore b/.gitignore index f1cd347..238004a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ dist-ssr *.njsproj *.sln *.sw? +*.code-workspace # Virtual Environments .venv diff --git a/backend/app.py b/backend/app.py index 0ed3ae4..2ff39a1 100644 --- a/backend/app.py +++ b/backend/app.py @@ -468,7 +468,7 @@ def import_model_by_id(payload: dict): # we only save stl for now ext = typeName if typeName is not None else ".stl" - filename = f"{mid}{ext}" + filename = f"{mid}.{ext}" path = os.path.join(UPLOAD_DIR, filename) # Check if url is not None before calling importer diff --git a/backend/importers/printables.py b/backend/importers/printables.py index 4d76916..21cfc5e 100644 --- a/backend/importers/printables.py +++ b/backend/importers/printables.py @@ -4,7 +4,6 @@ import base64 - MODELQUERY = """ query ModelFiles($id: ID!) { model: print(id: $id) { @@ -137,13 +136,14 @@ } """ -class PrintablesImporter(): - '''Handles the import from printables site - ''' + +class PrintablesImporter: + """Handles the import from printables site""" + def __init__(self): self.session: requests.Session self.graphurl = "https://api.printables.com/graphql/" - self.clientId= "" + self.clientId = "" self.fileResult: bool self.fileDownloadLink = "" @@ -160,15 +160,15 @@ def _set_client_data(self, url): if response.status_code != 200: return response.status_code - self.clientId = re.search("data-client-uid=\"(([a-z0-9-])+)", response.text)[1] + self.clientId = re.search('data-client-uid="(([a-z0-9-])+)', response.text)[1] return True - + def _get_model_info(self, modelId): header = { "accept": "application/graphql-response+json, application/graphql+json, application/json, text/event-stream, multipart/mixed", "accept-language": "en", - "client-uid" : self.clientId, + "client-uid": self.clientId, "cache-control": "no-cache", "content-type": "application/json", "graphql-client-version": "v3.0.11", @@ -178,34 +178,39 @@ def _get_model_info(self, modelId): } variables = {"id": modelId} - response = self.session.post(self.graphurl, json={'query': MODELQUERY , 'variables': variables}, headers=header) + response = self.session.post( + self.graphurl, + json={"query": MODELQUERY, "variables": variables}, + headers=header, + ) if response.status_code != 200: return response.status_code - + modelData = response.json() modelCollection = [] try: for model in modelData["data"]["model"]["stls"]: modelCollection.append( - { - "parentId": modelId, - "id": model["id"], - "name": model["name"], - "folder": model["folder"], - "previewPath": "https://files.printables.com/" + model["filePreviewPath"], - "typeName": model["name"].split(".")[-1], - } + { + "parentId": modelId, + "id": model["id"], + "name": model["name"], + "folder": model["folder"], + "previewPath": "https://files.printables.com/" + + model["filePreviewPath"], + "typeName": model["name"].split(".")[-1], + } ) return modelCollection except Exception as e: raise e - + def _get_file(self, modelId, parentId): header = { "accept": "application/graphql-response+json, application/graphql+json, application/json, text/event-stream, multipart/mixed", "accept-language": "en", - "client-uid" : self.clientId, + "client-uid": self.clientId, "cache-control": "no-cache", "content-type": "application/json", "graphql-client-version": "v3.0.11", @@ -213,15 +218,26 @@ def _get_file(self, modelId, parentId): "priority": "u=1, i", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", } - variables = {"fileType":"stl","id":modelId,"modelId":parentId,"source":"model_detail"} + variables = { + "fileType": "stl", + "id": modelId, + "modelId": parentId, + "source": "model_detail", + } - response = self.session.post(self.graphurl, json={'query': FILEQUERY , 'variables': variables}, headers=header) + response = self.session.post( + self.graphurl, + json={"query": FILEQUERY, "variables": variables}, + headers=header, + ) if response.status_code != 200: return None fileData = response.json() try: self.fileResult = fileData["data"]["getDownloadLink"]["ok"] - self.fileDownloadLink = fileData["data"]["getDownloadLink"]["output"]["link"] + self.fileDownloadLink = fileData["data"]["getDownloadLink"]["output"][ + "link" + ] except Exception as e: raise e @@ -229,32 +245,34 @@ def _get_file(self, modelId, parentId): fileheader = { "accept": "application/graphql-response+json, application/graphql+json, application/json, text/event-stream, multipart/mixed", "accept-language": "en", - "client-uid" : self.clientId, + "client-uid": self.clientId, "cache-control": "no-cache", "pragma": "no-cache", "priority": "u=1, i", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", } - file = self.session.get(self.fileDownloadLink, allow_redirects=True, headers=fileheader) + file = self.session.get( + self.fileDownloadLink, allow_redirects=True, headers=fileheader + ) return file - + def _make_thumbnail(self, url): if len(url) > 5: fileheader = { - "accept": "image/*, application/json, text/event-stream, multipart/mixed", - "accept-language": "en", - "client-uid" : self.clientId, - "cache-control": "no-cache", - "pragma": "no-cache", - "priority": "u=1, i", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", + "accept": "image/*, application/json, text/event-stream, multipart/mixed", + "accept-language": "en", + "client-uid": self.clientId, + "cache-control": "no-cache", + "pragma": "no-cache", + "priority": "u=1, i", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", } file = self.session.get(url, allow_redirects=True, headers=fileheader) encoded_string = base64.b64encode(file.content) return "data:image/png;base64," + encoded_string.decode() - + return "" - + def importfromId(self, modelId, parentId, previewPath): self.session = requests.Session() try: @@ -282,4 +300,4 @@ def getModelOptions(self, url): except Exception as e: raise e finally: - self.session.close() \ No newline at end of file + self.session.close() diff --git a/frontend/App.tsx b/frontend/App.tsx index 85474b2..ac251fe 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -340,6 +340,26 @@ const App = () => { setSelectedOptions(newSet); }; + const handleUpdateSTEPThumbnail = async (newModel: STLModel) => { + let tbuff = await fetch(port + newModel.url).then((response) => { + return response; + }); + let thumbnailBuffer = await tbuff.bytes().then((bytes) => { + return bytes; + }); + try { + let thumbnail = await generateThumbnail( + new File([thumbnailBuffer], newModel.name), + ); + let newerModel = await api.updateModel(newModel.id, { + thumbnail: thumbnail, + }); + setModels((prev) => [newerModel, ...prev]); + } catch (e) { + console.warn("Thumbnail generation failed, uploading without thumbnail"); + } + }; + const handleImportChoice = async () => { if (!importUrl || !importFolderId) return; @@ -357,8 +377,8 @@ const App = () => { importFolderId, model.typeName, ); + await handleUpdateSTEPThumbnail(newModel); setUploadQueue((prev) => prev - 1); - setModels((prev) => [newModel, ...prev]); } } } catch (error) {