DraconicDragon commited on
Commit
c2421e6
·
verified ·
1 Parent(s): c90f341

Update fetch_url_util.py

Browse files
Files changed (1) hide show
  1. fetch_url_util.py +45 -26
fetch_url_util.py CHANGED
@@ -1,54 +1,73 @@
1
  import io
2
  import urllib.parse
 
3
  from PIL import Image
4
  from curl_cffi import requests
5
 
 
 
 
 
6
  def fetch_image_from_url(url):
7
- """
8
- Robustly fetches an image from a URL using TLS impersonation and
9
- dynamic Referer headers. Automatically handles missing http/https prefixes.
10
- """
11
  if not url:
12
  return None
13
 
14
- # 1. Handle missing protocol (e.g., pixiv.net -> https://pixiv.net)
15
  url = url.strip()
16
  if not url.startswith(("http://", "https://")):
17
  url = "https://" + url
18
 
19
- # 2. Extract base domain for the Referer header
20
- # This is crucial for Pixiv and similar sites to prevent 403 Forbidden
21
  parsed_url = urllib.parse.urlparse(url)
22
  base_domain = f"{parsed_url.scheme}://{parsed_url.netloc}/"
23
 
24
  headers = {
25
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
26
- "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
27
  "Accept-Language": "en-US,en;q=0.9",
28
  "Referer": base_domain,
29
  "Cache-Control": "no-cache",
30
  }
31
 
32
  try:
33
- # Changed 'follow_redirects' to 'allow_redirects' for curl_cffi compatibility
34
- response = requests.get(
35
  url,
36
  headers=headers,
37
- impersonate="chrome120",
38
  timeout=15,
39
- allow_redirects=True
40
- )
41
-
42
- response.raise_for_status()
43
-
44
- image_bytes = io.BytesIO(response.content)
45
- img = Image.open(image_bytes)
46
- img.load()
47
-
48
- return img.convert("RGB")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  except Exception as e:
51
- print(f"Error fetching {url}: {str(e)}")
52
- import gradio as gr
53
- # This will show a red toast notification in the UI
54
- raise gr.Error(f"Failed to fetch image: {str(e)}")
 
 
 
 
 
 
1
  import io
2
  import urllib.parse
3
+ import pillow_jxl
4
  from PIL import Image
5
  from curl_cffi import requests
6
 
7
+ # Max file size: 150MB
8
+ MAX_SIZE = 150 * 1024 * 1024
9
+ MAX_SIZE_MB = MAX_SIZE / (1024 * 1024)
10
+
11
  def fetch_image_from_url(url):
 
 
 
 
12
  if not url:
13
  return None
14
 
 
15
  url = url.strip()
16
  if not url.startswith(("http://", "https://")):
17
  url = "https://" + url
18
 
 
 
19
  parsed_url = urllib.parse.urlparse(url)
20
  base_domain = f"{parsed_url.scheme}://{parsed_url.netloc}/"
21
 
22
  headers = {
23
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.7559.59 Safari/537.36",
24
+ "Accept": "image/avif,image/webp,image/jxl,image/apng,image/png,image/jpeg,image/gif,image/svg+xml,image/*,*/*;q=0.8",
25
  "Accept-Language": "en-US,en;q=0.9",
26
  "Referer": base_domain,
27
  "Cache-Control": "no-cache",
28
  }
29
 
30
  try:
31
+ # We use stream=True to check file size BEFORE downloading everything
32
+ with requests.get(
33
  url,
34
  headers=headers,
35
+ impersonate="chrome120", # Handshake logic (latest stable in curl_cffi)
36
  timeout=15,
37
+ allow_redirects=True,
38
+ stream=True
39
+ ) as response:
40
+
41
+ response.raise_for_status()
42
+
43
+ # 1. Check Content-Length header if available
44
+ cl = response.headers.get("Content-Length")
45
+ if cl and int(cl) > MAX_SIZE:
46
+ raise RuntimeError(f"File too large (limit: {MAX_SIZE_MB:.0f}MB)")
47
+
48
+ # 2. Download in chunks to verify actual size (for servers with no header)
49
+ downloaded_data = io.BytesIO()
50
+ size = 0
51
+ for chunk in response.iter_content(chunk_size=8192):
52
+ size += len(chunk)
53
+ if size > MAX_SIZE:
54
+ raise RuntimeError(f"File exceeds {MAX_SIZE_MB:.0f}MB limit.")
55
+ downloaded_data.write(chunk)
56
+
57
+ # Open and verify
58
+ downloaded_data.seek(0)
59
+ img = Image.open(downloaded_data)
60
+ img.load()
61
+
62
+ return img.convert("RGB")
63
 
64
  except Exception as e:
65
+ error_msg = f"Fetch Error: {str(e)}"
66
+ print(error_msg)
67
+
68
+ # Try to raise a pretty Gradio error, otherwise raise standard RuntimeError
69
+ try:
70
+ import gradio as gr
71
+ raise gr.Error(error_msg)
72
+ except ImportError:
73
+ raise RuntimeError(error_msg)