-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupload_provider.py
More file actions
186 lines (153 loc) · 5.51 KB
/
upload_provider.py
File metadata and controls
186 lines (153 loc) · 5.51 KB
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env python3
"""
Upload Provider Interface
This module defines the abstract base class for upload providers,
allowing the application to support multiple cloud services
(CloudFront/S3, Cloudinary, etc.) through a unified interface.
"""
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional, Tuple
class UploadProvider(ABC):
"""Abstract base class for upload providers"""
@abstractmethod
def test_connection(self) -> bool:
"""Test the provider's API connection"""
pass
@abstractmethod
def upload_image(
self,
file_path: str,
file_name: str,
max_width: Optional[int] = None,
quality: int = 82,
smart_format: bool = True,
add_timestamp: bool = True,
**kwargs,
) -> Tuple[bool, Optional[str], Optional[Dict[str, Any]]]:
"""
Upload an image file with optimization
Args:
file_path: Path to the local image file
file_name: Original filename
max_width: Maximum width for resizing
quality: Image quality (1-100)
smart_format: Enable automatic format selection
add_timestamp: Add timestamp to filename
**kwargs: Provider-specific options
Returns:
Tuple of (success, public_url, metadata)
"""
pass
@abstractmethod
def upload_from_url(
self,
source_url: str,
max_width: Optional[int] = None,
quality: int = 82,
smart_format: bool = True,
add_timestamp: bool = True,
**kwargs,
) -> Tuple[bool, Optional[str], Optional[Dict[str, Any]]]:
"""
Upload an image directly from URL
Args:
source_url: URL of the image to upload
max_width: Maximum width for resizing
quality: Image quality (1-100)
smart_format: Enable automatic format selection
add_timestamp: Add timestamp to filename
**kwargs: Provider-specific options
Returns:
Tuple of (success, public_url, metadata)
"""
pass
@abstractmethod
def get_provider_name(self) -> str:
"""Get the name of this provider"""
pass
def get_upload_stats(self) -> Dict[str, Any]:
"""Get provider usage statistics (optional)"""
return {}
def delete_image(self, identifier: str, **kwargs) -> bool:
"""Delete an image (optional)"""
return False
def generate_responsive_url(
self, identifier: str, transformations: Optional[Dict[str, Any]] = None
) -> str:
"""Generate a responsive URL with transformations (optional)"""
return ""
class ProviderFactory:
"""Factory class for creating upload providers"""
@staticmethod
def create_provider(provider_type: str) -> UploadProvider:
"""
Create an upload provider based on type
Args:
provider_type: Type of provider ('cloudfront' or 'cloudinary')
Returns:
UploadProvider instance
"""
provider_type = provider_type.lower().strip()
if provider_type == "cloudfront":
from cloudfront_provider import CloudFrontProvider
return CloudFrontProvider()
elif provider_type == "cloudinary":
# Wrap CloudinaryProvider to match the interface
return CloudinaryProviderAdapter()
else:
raise ValueError(f"Unknown provider type: {provider_type}")
class CloudinaryProviderAdapter(UploadProvider):
"""Adapter to make CloudinaryProvider conform to UploadProvider interface"""
def __init__(self):
# Import here to avoid circular imports and unused import warnings
from cloudinary_provider import CloudinaryProvider
self.provider = CloudinaryProvider()
def test_connection(self) -> bool:
return self.provider.test_connection()
def upload_image(
self,
file_path: str,
file_name: str,
max_width: Optional[int] = None,
quality: int = 82,
smart_format: bool = True,
add_timestamp: bool = True,
**kwargs,
) -> Tuple[bool, Optional[str], Optional[Dict[str, Any]]]:
folder = kwargs.get("folder", "images")
return self.provider.upload_image(
file_path,
file_name,
max_width,
quality,
smart_format,
add_timestamp,
folder,
)
def upload_from_url(
self,
source_url: str,
max_width: Optional[int] = None,
quality: int = 82,
smart_format: bool = True,
add_timestamp: bool = True,
**kwargs,
) -> Tuple[bool, Optional[str], Optional[Dict[str, Any]]]:
folder = kwargs.get("folder", "images")
return self.provider.upload_from_url(
source_url, max_width, quality, smart_format, add_timestamp, folder
)
def get_provider_name(self) -> str:
return "cloudinary"
def get_upload_stats(self) -> Dict[str, Any]:
return self.provider.get_upload_stats()
def delete_image(self, identifier: str, **kwargs) -> bool:
folder = kwargs.get("folder", "images")
return self.provider.delete_image(identifier, folder)
def generate_responsive_url(
self, identifier: str, transformations: Optional[Dict[str, Any]] = None
) -> str:
folder = "images" # Default folder
return self.provider.generate_responsive_url(
identifier, folder, transformations
)