Heretic Webdl ((top)) May 2026
# 3️⃣ Install deps pip install -r requirements.txt
async with httpx.AsyncClient(follow_redirects=True, timeout=30) as client: try: # `stream=True` gives us an async iterator over the body async with client.stream("GET", url) as resp: # Basic sanity checks if resp.status_code != 200: raise HTTPException( status_code=status.HTTP_502_BAD_GATEWAY, detail=f"Remote server returned resp.status_code", ) heretic webdl
# --------------------------------------------------- # # Optional: expose OpenAPI UI (only in dev) # --------------------------------------------------- # if __name__ == "__main__": uvicorn.run("app.main:app", host="0.0.0.0", port=8000, log_level="info") 5.1 Pure‑Python (no Docker) # 1️⃣ Clone repo & cd into it git clone https://github.com/your‑user/heroku‑webdl.git cd heroku-webdl # 3️⃣ Install deps pip install -r requirements
class Settings: # ---- security / abuse limits ------------------------------------------------- MAX_CONTENT_LENGTH = int(os.getenv("MAX_CONTENT_LENGTH", "104857600")) # 100 MiB # Optional whitelist (comma‑separated). If empty → allow any domain. WHITELISTED_DOMAINS = set( filter(None, os.getenv("WHITELISTED_DOMAINS", "").split(",")) ) # Rate limiting – simple token bucket stored in memory (good enough for free tier) RATE_LIMIT = int(os.getenv("RATE_LIMIT", "10")) # requests per minute per IP # Using httpx HEAD request (fast, no body)
# We need to fetch the headers *once* to get the MIME type. # Using httpx HEAD request (fast, no body) – fallback to generic if unavailable. import httpx async with httpx.AsyncClient(follow_redirects=True, timeout=10) as client: try: head = await client.head(payload.url, follow_redirects=True) if head.status_code == 200: ct = head.headers.get("Content-Type") if ct: headers["Content-Type"] = ct except Exception: # ignore – we will stream anyway pass
import uvicorn from fastapi import FastAPI, HTTPException, Request, status from fastapi.responses import StreamingResponse, JSONResponse from pydantic import BaseModel, HttpUrl, validator
headers = "Content-Disposition": f'attachment; filename="filename"', # The downstream `StreamingResponse` will automatically forward # the `Content-Type` from the remote response if we set it later.