Spaces:
Running
Running
import requests | |
from typing import Optional, Union | |
import base64 | |
from pathlib import Path | |
import os | |
from pydantic import BaseModel | |
class ImageInfo(BaseModel): | |
filename: str | |
name: str | |
mime: str | |
extension: str | |
url: str | |
class ImgBBData(BaseModel): | |
id: str | |
title: str | |
url_viewer: str | |
url: str | |
display_url: str | |
width: int | |
height: int | |
size: int | |
time: int | |
expiration: int | |
image: ImageInfo | |
thumb: ImageInfo | |
medium: ImageInfo | |
delete_url: str | |
class ImgBBResponse(BaseModel): | |
data: ImgBBData | |
success: bool | |
status: int | |
class ImageUploader: | |
"""A class to handle image uploads to ImgBB service.""" | |
def __init__(self, api_key: str): | |
""" | |
Initialize the ImageUploader with an API key. | |
Args: | |
api_key (str): The ImgBB API key | |
""" | |
self.api_key = api_key | |
self.base_url = "https://api.imgbb.com/1/upload" | |
def upload( | |
self, | |
image: Union[str, bytes, Path], | |
name: Optional[str] = None, | |
expiration: Optional[int] = None, | |
) -> ImgBBResponse: | |
""" | |
Upload an image to ImgBB. | |
Args: | |
image: Can be: | |
- A file path (str or Path) | |
- Base64 encoded string | |
- Base64 data URI (e.g., data:image/jpeg;base64,...) | |
- URL to an image | |
- Bytes of an image | |
name: Optional name for the uploaded file | |
expiration: Optional expiration time in seconds (60-15552000) | |
Returns: | |
ImgBBResponse containing the parsed upload response from ImgBB | |
Raises: | |
ValueError: If the image format is invalid or upload fails | |
requests.RequestException: If the API request fails | |
""" | |
# Prepare the parameters | |
params = {"key": self.api_key} | |
if expiration: | |
if not 60 <= expiration <= 15552000: | |
raise ValueError("Expiration must be between 60 and 15552000 seconds") | |
params["expiration"] = expiration | |
# Handle different image input types | |
if isinstance(image, (str, Path)): | |
image_str = str(image) | |
files = {} | |
if os.path.isfile(image_str): | |
# It's a file path | |
with open(image_str, "rb") as file: | |
files["image"] = file | |
elif image_str.startswith(("http://", "https://")): | |
# It's a URL | |
files["image"] = (None, image_str) | |
elif image_str.startswith("data:image/"): | |
# It's a data URI | |
# Extract the base64 part after the comma | |
base64_data = image_str.split(",", 1)[1] | |
files["image"] = (None, base64_data) | |
else: | |
# Assume it's base64 data | |
files["image"] = (None, image_str) | |
if name: | |
files["name"] = (None, name) | |
response = requests.post(self.base_url, params=params, files=files) | |
elif isinstance(image, bytes): | |
# Convert bytes to base64 | |
base64_image = base64.b64encode(image).decode("utf-8") | |
files = {"image": (None, base64_image)} | |
if name: | |
files["name"] = (None, name) | |
response = requests.post(self.base_url, params=params, files=files) | |
else: | |
raise ValueError( | |
"Invalid image format. Must be file path, URL, base64 string, or bytes" | |
) | |
# Check the response | |
if response.status_code != 200: | |
raise ValueError( | |
f"Upload failed with status {response.status_code}: {response.text}" | |
) | |
# Parse the response using Pydantic model | |
response_json = response.json() | |
return ImgBBResponse.parse_obj(response_json) | |
def upload_file( | |
self, | |
file_path: Union[str, Path], | |
name: Optional[str] = None, | |
expiration: Optional[int] = None, | |
) -> ImgBBResponse: | |
""" | |
Convenience method to upload an image file. | |
Args: | |
file_path: Path to the image file | |
name: Optional name for the uploaded file | |
expiration: Optional expiration time in seconds (60-15552000) | |
Returns: | |
ImgBBResponse containing the parsed upload response from ImgBB | |
""" | |
return self.upload(file_path, name=name, expiration=expiration) | |
def upload_url( | |
self, | |
image_url: str, | |
name: Optional[str] = None, | |
expiration: Optional[int] = None, | |
) -> ImgBBResponse: | |
""" | |
Convenience method to upload an image from a URL. | |
Args: | |
image_url: URL of the image to upload | |
name: Optional name for the uploaded file | |
expiration: Optional expiration time in seconds (60-15552000) | |
Returns: | |
ImgBBResponse containing the parsed upload response from ImgBB | |
""" | |
return self.upload(image_url, name=name, expiration=expiration) | |