diff --git a/config/gunicorn.conf.py b/config/gunicorn.conf.py index 7afe08f..9d80f0d 100644 --- a/config/gunicorn.conf.py +++ b/config/gunicorn.conf.py @@ -1,5 +1,7 @@ import multiprocessing import os +import src.banner as banner + # Server socket bind = "127.0.0.1:7433" # nginx will proxy to this @@ -82,7 +84,8 @@ def on_reload(server): def when_ready(server): - """Called just after the server is started.""" + print(banner.generate("studspider v0.1")) + print("Built for PLAY-WELL TEKNOLOGIES by contact@studspider.sh\n\n") print(f"STUDSPIDER HOOK v0.1 RUNNING ON {bind}") print(f"WORKERS: {workers}") print(f"WORKING CLASS: {worker_class}\n") diff --git a/src/abricksetapi.py b/src/abricksetapi.py index 19889a1..482ab19 100644 --- a/src/abricksetapi.py +++ b/src/abricksetapi.py @@ -125,7 +125,7 @@ class AsyncBricksetAPI: # Set PW inventory name with theme our_set["pw_name"] = ( - f"{our_set['set_num']} - {our_set['set_theme'].upper()} {our_set['set_name']}" + f"{our_set['set_num']} - {our_set['set_theme'].upper()} - {our_set['set_name']}" ) # Flag for manual price review if needed diff --git a/src/asquare.py b/src/asquare.py index f035d6e..4f5b893 100644 --- a/src/asquare.py +++ b/src/asquare.py @@ -5,6 +5,8 @@ import asyncio import uuid import re from time import time +import aiohttp +from io import BytesIO from square.core.api_error import ApiError @@ -196,6 +198,72 @@ async def get_item_by_variation_id(variation_id: str) -> dict: raise +async def upload_item_image(item_id: str, image_url: str) -> bool: + """ + Download image from URL and upload to Square catalog item. + + Args: + item_id: Square catalog item ID + image_url: URL to download image from (e.g., Brickset thumbnail) + + Returns: + bool: True if upload successful, False otherwise + """ + try: + print(f"[@DEBUG] Downloading image from {image_url}") + + # Download image from URL + async with aiohttp.ClientSession() as session: + async with session.get(image_url) as resp: + if resp.status != 200: + print(f"[@ERROR] Failed to download image: HTTP {resp.status}") + return False + + image_data = await resp.read() + + # Determine content type from URL extension + content_type = "image/jpeg" + if image_url.lower().endswith(".png"): + content_type = "image/png" + elif image_url.lower().endswith(".gif"): + content_type = "image/gif" + + print( + f"[@DEBUG] Uploading {len(image_data)} bytes as {content_type} to item {item_id}" + ) + + # Upload to Square + result = await client.catalog.images.create( + image_file=image_data, + request={ + "idempotency_key": str(uuid.uuid4()), + "image": { + "type": "IMAGE", + "id": f"#IMAGE_{item_id}", + "image_data": { + "name": f"LEGO Set Image", + }, + }, + "object_id": item_id, + }, + image_file_name=f"lego_{item_id}.jpg", + content_type=content_type, + ) + + result_json = json.loads(result.model_dump_json()) + + if result_json.get("image"): + print(f"[@DEBUG] ✓ Successfully uploaded image for item {item_id}") + return True + else: + print(f"[@ERROR] Image upload failed: {result_json}") + return False + + except Exception as e: + print(f"[@ERROR] Failed to upload image for item {item_id}: {e}") + return False + + async def update_itm(item_id: str, lego_set: dict, item_data: dict = None): """ Asynchronously updates an item in the Square catalog based on the provided LEGO set information. @@ -390,8 +458,11 @@ async def update_itm(item_id: str, lego_set: dict, item_data: dict = None): ) print(f"[@DEBUG] Square returned category_id: {returned_category}") - # TODO: Handle image upload separately (Square Images API) - # This requires multipart/form-data upload + # Upload image if available (DISABLED - concerns about storage costs) + # if set_image: + # print(f"[@DEBUG] Uploading image from {set_image}") + # await upload_item_image(item_id, set_image) + return True else: print(