esuls 0.1.2__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esuls
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Utility library for async database operations, HTTP requests, and parallel execution
5
5
  Author-email: IperGiove <ipergiove@gmail.com>
6
6
  License: MIT
@@ -17,6 +17,8 @@ Requires-Dist: curl-cffi>=0.13.0
17
17
  Requires-Dist: fake-useragent>=2.2.0
18
18
  Requires-Dist: httpx>=0.28.1
19
19
  Requires-Dist: loguru>=0.7.3
20
+ Requires-Dist: pillow>=12.0.0
21
+ Requires-Dist: python-magic>=0.4.27
20
22
  Dynamic: license-file
21
23
 
22
24
  # esuls
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "esuls"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "Utility library for async database operations, HTTP requests, and parallel execution"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"
@@ -25,6 +25,8 @@ dependencies = [
25
25
  "fake-useragent>=2.2.0",
26
26
  "httpx>=0.28.1",
27
27
  "loguru>=0.7.3",
28
+ "pillow>=12.0.0",
29
+ "python-magic>=0.4.27",
28
30
  ]
29
31
 
30
32
  [project.urls]
@@ -0,0 +1,123 @@
1
+ from urllib.parse import urlparse, unquote
2
+ from typing import TypedDict, Optional, Dict, Any
3
+ import asyncio
4
+ import io
5
+ import magic
6
+ from PIL import Image
7
+ from .request_cli import make_request
8
+
9
+ # Type definition
10
+ class IconData(TypedDict):
11
+ data: bytes
12
+ size: int
13
+ mimetype: str
14
+ name: str
15
+
16
+ # MIME types mapping
17
+ MIME_TO_EXT: Dict[str, str] = {
18
+ 'image/jpeg': '.jpg',
19
+ 'image/png': '.png',
20
+ 'image/gif': '.gif',
21
+ 'image/webp': '.webp',
22
+ 'image/svg+xml': '.svg',
23
+ 'image/x-icon': '.ico',
24
+ 'image/vnd.microsoft.icon': '.ico',
25
+ }
26
+
27
+ async def download_icon(url: str, filename: Optional[str] = None) -> Optional[IconData]:
28
+ """
29
+ Download and validate an image from a URL.
30
+
31
+ Args:
32
+ url: Target image URL
33
+ filename: Optional custom filename
34
+
35
+ Returns:
36
+ IconData object or None if download/validation fails
37
+ """
38
+ if not url:
39
+ return None
40
+
41
+ # Process filename
42
+ if not filename:
43
+ filename = _extract_filename(url)
44
+
45
+ if any(term in filename.lower() for term in ['unknown', 'missing']):
46
+ return None
47
+
48
+ # Fetch image data
49
+ response = await make_request(url, max_attempt=3, add_user_agent=True)
50
+ if not response:
51
+ return None
52
+
53
+ file_buffer = response.content
54
+ mime_type = _detect_mime_type(file_buffer)
55
+
56
+ if not mime_type:
57
+ return None
58
+
59
+ if not verify_image(file_buffer, mime_type):
60
+ return None
61
+
62
+ # Generate filename with correct extension
63
+ base_name = filename.rsplit('.', 1)[0]
64
+ extension = MIME_TO_EXT.get(mime_type, '')
65
+ final_filename = f"{base_name}{extension}"
66
+
67
+ return {
68
+ "data": file_buffer,
69
+ "size": len(file_buffer),
70
+ "mimetype": mime_type,
71
+ "name": final_filename,
72
+ }
73
+
74
+ def _extract_filename(url: str) -> str:
75
+ """Extract filename from URL path."""
76
+ parsed_url = urlparse(url)
77
+ path_components = parsed_url.path.split('/')
78
+ filename = next((comp for comp in reversed(path_components) if comp))
79
+ return unquote(filename)
80
+
81
+ def _detect_mime_type(data: bytes) -> Optional[str]:
82
+ """Detect MIME type from file content."""
83
+ mime = magic.Magic(mime=True)
84
+ return mime.from_buffer(data)
85
+
86
+ def verify_image(data: bytes, mime_type: str) -> bool:
87
+ """Verify image data integrity."""
88
+ if not mime_type.startswith('image/'):
89
+ return False
90
+
91
+ try:
92
+ # SVG validation (basic XML check)
93
+ if mime_type == 'image/svg+xml':
94
+ return b'<svg' in data and b'</svg>' in data
95
+
96
+ # Standard image validation through PIL
97
+ with Image.open(io.BytesIO(data)) as img:
98
+ img.verify()
99
+ return True
100
+ except Exception:
101
+ return False
102
+
103
+
104
+
105
+ if __name__ == "__main__":
106
+ url = "https://pbs.twimg.com/profile_images/1899026397915488256/mc-jPC-w.jpg"
107
+ icon_data = asyncio.run(download_icon(url))
108
+ if icon_data:
109
+ print(f"Downloaded: {icon_data['name']}")
110
+ print(f"MIME type: {icon_data['mimetype']}")
111
+ print(f"Size: {icon_data['size']} bytes")
112
+
113
+ # Display image if it's a standard format (not SVG)
114
+ if icon_data['mimetype'] != 'image/svg+xml':
115
+ try:
116
+ img = Image.open(io.BytesIO(icon_data['data']))
117
+ print(f"Image dimensions: {img.size}")
118
+ img.show()
119
+ print("Image displayed!")
120
+ except Exception as e:
121
+ print(f"Could not display image: {e}")
122
+ else:
123
+ print("SVG image downloaded (cannot display with PIL)")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esuls
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Utility library for async database operations, HTTP requests, and parallel execution
5
5
  Author-email: IperGiove <ipergiove@gmail.com>
6
6
  License: MIT
@@ -17,6 +17,8 @@ Requires-Dist: curl-cffi>=0.13.0
17
17
  Requires-Dist: fake-useragent>=2.2.0
18
18
  Requires-Dist: httpx>=0.28.1
19
19
  Requires-Dist: loguru>=0.7.3
20
+ Requires-Dist: pillow>=12.0.0
21
+ Requires-Dist: python-magic>=0.4.27
20
22
  Dynamic: license-file
21
23
 
22
24
  # esuls
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  src/esuls/__init__.py
5
5
  src/esuls/db_cli.py
6
+ src/esuls/download_icon.py
6
7
  src/esuls/request_cli.py
7
8
  src/esuls/utils.py
8
9
  src/esuls.egg-info/PKG-INFO
@@ -3,3 +3,5 @@ curl-cffi>=0.13.0
3
3
  fake-useragent>=2.2.0
4
4
  httpx>=0.28.1
5
5
  loguru>=0.7.3
6
+ pillow>=12.0.0
7
+ python-magic>=0.4.27
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes