import json import os import sys from pathlib import Path from urllib.request import Request, HTTPRedirectHandler, build_opener from urllib.parse import urljoin, urlsplit class NoRedirect(HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None origin = os.environ.get("STASHBAY_ORIGIN", "https://stashbay.net").rstrip("/") key = os.environ["STASHBAY_API_KEY"] path = Path(sys.argv[1]) client = build_opener(NoRedirect) def api(route, body): request = Request(origin + "/api/v1" + route, method="POST", data=json.dumps(body).encode(), headers={"Authorization": "Bearer " + key, "Content-Type": "application/json"}) with client.open(request, timeout=300) as response: return json.load(response) size = path.stat().st_size with path.open("rb") as file: plan = api("/uploads/init", {"filename": path.name, "sizeBytes": size}) print("Upload ID:", plan["fileId"], file=sys.stderr) if not 1 <= plan["partSize"] <= 64 * 1024 * 1024: raise ValueError("Unexpected part size") parts = [] for number in range(1, plan["partCount"] + 1): length = min(plan["partSize"], size - (number - 1) * plan["partSize"]) data = file.read(length) if len(data) != length: raise ValueError("File changed during upload") address = plan["uploadUrl"] if plan["single"] else api( "/uploads/parts", {"fileId": plan["fileId"], "from": number, "count": 1} )["parts"][0]["url"] url = urljoin(origin, address) target, base = urlsplit(url), urlsplit(origin) headers = {"Content-Type": "application/octet-stream"} if plan["strategy"] == "proxy" and (target.scheme, target.netloc) == (base.scheme, base.netloc): headers["Authorization"] = "Bearer " + key elif plan["strategy"] != "presigned" or target.scheme != "https": raise ValueError("Unexpected upload URL or strategy") if plan["single"] and plan["strategy"] == "presigned": headers["If-None-Match"] = "*" with client.open(Request(url, data=data, headers=headers, method="PUT"), timeout=300) as response: if not plan["single"]: etag = json.load(response)["etag"] if plan["strategy"] == "proxy" else response.headers.get("ETag") if not etag: raise ValueError("Missing part ETag") parts.append({"partNumber": number, "etag": etag.strip('"')}) result = api("/uploads/complete", {"fileId": plan["fileId"], "parts": parts}) print(result.get("shareUrl") or json.dumps(result))