File size: 5,109 Bytes
9e822e4
 
 
 
 
 
 
fcb8f25
9e822e4
 
 
 
 
 
 
fcb8f25
9e822e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fcb8f25
9e822e4
 
 
 
 
fcb8f25
9e822e4
 
fcb8f25
9e822e4
 
 
fcb8f25
9e822e4
 
 
 
 
fcb8f25
9e822e4
 
 
 
fcb8f25
9e822e4
 
 
fcb8f25
9e822e4
 
 
 
 
 
 
 
 
fcb8f25
9e822e4
 
fcb8f25
9e822e4
 
 
 
 
fcb8f25
9e822e4
 
 
fcb8f25
9e822e4
 
 
 
 
 
 
fcb8f25
 
 
9e822e4
fcb8f25
 
9e822e4
 
fcb8f25
 
9e822e4
 
fcb8f25
9e822e4
 
fcb8f25
9e822e4
 
 
fcb8f25
 
9e822e4
fcb8f25
9e822e4
 
fcb8f25
 
 
9e822e4
 
 
fcb8f25
 
 
9e822e4
 
 
 
 
 
 
 
 
fcb8f25
9e822e4
 
 
fcb8f25
9e822e4
 
 
 
fcb8f25
9e822e4
 
 
 
 
 
 
 
 
fcb8f25
9e822e4
 
 
fcb8f25
9e822e4
 
 
 
fcb8f25
9e822e4
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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)