MyanmarSwe commited on
Commit
3e8e235
·
verified ·
1 Parent(s): a3f8c77

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +67 -52
main.py CHANGED
@@ -9,7 +9,7 @@ import urllib.parse
9
  import mimetypes
10
  from bs4 import BeautifulSoup
11
  from fastapi import FastAPI, HTTPException, Request
12
- from fastapi.responses import StreamingResponse, RedirectResponse
13
  from fake_useragent import UserAgent
14
  import uvicorn
15
 
@@ -26,12 +26,12 @@ ua = UserAgent(fallback='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/5
26
 
27
  # MediaFire Cache
28
  MEDIAFIRE_CACHE = {}
29
- CACHE_TTL = 1800
30
 
31
  client = httpx.AsyncClient(
32
  timeout=httpx.Timeout(60.0, read=None),
33
  follow_redirects=True,
34
- limits=httpx.Limits(max_connections=100, max_keepalive_connections=20)
35
  )
36
 
37
  @app.get("/")
@@ -63,7 +63,7 @@ def get_clean_filename(url):
63
  decoded_url = urllib.parse.unquote(url)
64
  name = decoded_url.split('/')[-1].split('?')[0]
65
  if not name or '.' not in name:
66
- name = "video.mp4"
67
  return name
68
 
69
  @app.get("/download")
@@ -78,45 +78,39 @@ async def download_proxy(request: Request, url: str, key: str = None):
78
 
79
  # --- MediaFire Section ---
80
  if "mediafire.com" in clean_url:
81
- cached_data = MEDIAFIRE_CACHE.get(clean_url)
82
-
83
  target_link = None
84
- if cached_data and (current_time - cached_data['time']) < CACHE_TTL:
85
- target_link = cached_data['link']
86
- else:
 
 
 
 
 
87
  try:
88
- req_ua = ua.random
89
- async with httpx.AsyncClient(headers={'User-Agent': req_ua}, follow_redirects=True) as temp_client:
90
- page_res = await temp_client.get(clean_url)
91
- if page_res.status_code != 200:
92
- raise HTTPException(status_code=page_res.status_code, detail="MediaFire access failed")
93
-
94
- html_content = page_res.text
95
- match = re.search(r"https?://download[0-9]+\.mediafire\.com/[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+/[^\s'\"]+", html_content)
96
- if match:
97
- target_link = match.group(0).replace('"', '').replace("'", "")
98
-
99
- if not target_link:
100
- soup = BeautifulSoup(html_content, 'html.parser')
101
- download_btn = soup.find('a', {'id': 'downloadButton'}) or soup.find('a', {'class': 'input_btn_p'})
102
- if download_btn: target_link = download_btn.get('href')
103
-
104
- if target_link:
105
- if target_link.startswith("//"): target_link = f"https:{target_link}"
106
- elif target_link.startswith("/"): target_link = f"https://www.mediafire.com{target_link}"
107
- MEDIAFIRE_CACHE[clean_url] = {'link': target_link, 'time': current_time}
108
  except Exception as e:
109
- print(f"MediaFire Error: {e}")
110
- raise HTTPException(status_code=500, detail=str(e))
111
 
112
  if target_link:
113
- # FIX: Player က Video မှန်းသိအောင် Link အဆုံးမှာ /filename.mkv ကို Hint အနေနဲ့ ထည့်ပေးလိုက်ခြင်း
114
- # ဒါဟာ MediaFire ဆီ Request ရောက်တဲ့အခါ query parameter အနေနဲ့ပဲ မြင်သွားမှာမို့ link မပျက်ပါဘူး
115
- separator = "&" if "?" in target_link else "?"
116
- hinted_link = f"{target_link}{separator}ext={filename}"
117
- return RedirectResponse(url=hinted_link)
118
  else:
119
- raise HTTPException(status_code=404, detail="Direct link extraction failed")
120
 
121
  # --- Google Drive Section ---
122
  elif "drive.google.com" in clean_url:
@@ -133,13 +127,14 @@ async def download_proxy(request: Request, url: str, key: str = None):
133
  headers = {"Authorization": f"Bearer {token}"}
134
  if range_header: headers['Range'] = range_header
135
  try:
136
- req = client.build_request("GET", api_link, headers=headers)
137
- r = await client.send(req, stream=True)
138
  if r.status_code in [200, 206]:
139
  return await process_response(r, filename)
140
  await r.aclose()
141
  except: continue
142
-
 
143
  public_url = f"https://drive.google.com/uc?export=download&id={file_id}"
144
  return await stream_file(public_url, range_header, filename)
145
 
@@ -149,34 +144,54 @@ async def download_proxy(request: Request, url: str, key: str = None):
149
  async def stream_file(target_url, range_header, filename):
150
  headers = {'User-Agent': ua.random}
151
  if range_header: headers['Range'] = range_header
 
152
  try:
153
- req = client.build_request("GET", target_url, headers=headers)
154
- r = await client.send(req, stream=True)
 
 
 
 
 
 
155
  return await process_response(r, filename)
 
 
156
  except Exception as e:
 
157
  raise HTTPException(status_code=500, detail=str(e))
158
 
159
  async def process_response(r, filename):
160
  mime_type, _ = mimetypes.guess_type(filename)
161
  if not mime_type or 'application' in mime_type:
162
- mime_type = 'video/x-matroska' if filename.endswith('.mkv') else 'video/mp4'
163
-
164
- safe_headers = ['content-length', 'content-range', 'accept-ranges', 'cache-control']
165
- res_headers = {n: v for n, v in r.headers.items() if n.lower() in safe_headers}
166
- res_headers['Content-Type'] = mime_type
167
- res_headers['Accept-Ranges'] = 'bytes'
 
 
 
168
 
169
- quoted_name = urllib.parse.quote(filename)
170
- res_headers['Content-Disposition'] = f'inline; filename="{quoted_name}"'
 
 
171
 
172
  async def stream_generator():
173
  try:
174
- async for chunk in r.aiter_bytes(chunk_size=262144):
175
  yield chunk
176
  finally:
177
  await r.aclose()
178
 
179
- return StreamingResponse(stream_generator(), status_code=r.status_code, headers=res_headers, media_type=mime_type)
 
 
 
 
 
180
 
181
  if __name__ == "__main__":
182
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
9
  import mimetypes
10
  from bs4 import BeautifulSoup
11
  from fastapi import FastAPI, HTTPException, Request
12
+ from fastapi.responses import StreamingResponse
13
  from fake_useragent import UserAgent
14
  import uvicorn
15
 
 
26
 
27
  # MediaFire Cache
28
  MEDIAFIRE_CACHE = {}
29
+ CACHE_TTL = 3600
30
 
31
  client = httpx.AsyncClient(
32
  timeout=httpx.Timeout(60.0, read=None),
33
  follow_redirects=True,
34
+ limits=httpx.Limits(max_connections=200, max_keepalive_connections=50)
35
  )
36
 
37
  @app.get("/")
 
63
  decoded_url = urllib.parse.unquote(url)
64
  name = decoded_url.split('/')[-1].split('?')[0]
65
  if not name or '.' not in name:
66
+ return "video.mp4"
67
  return name
68
 
69
  @app.get("/download")
 
78
 
79
  # --- MediaFire Section ---
80
  if "mediafire.com" in clean_url:
 
 
81
  target_link = None
82
+
83
+ # 1. Check Cache
84
+ cached = MEDIAFIRE_CACHE.get(clean_url)
85
+ if cached and (current_time - cached['time']) < CACHE_TTL:
86
+ target_link = cached['link']
87
+
88
+ # 2. Fetch New Link if not cached
89
+ if not target_link:
90
  try:
91
+ headers = {'User-Agent': ua.random}
92
+ async with httpx.AsyncClient(headers=headers, follow_redirects=True, timeout=30.0) as temp_client:
93
+ r = await temp_client.get(clean_url)
94
+ if r.status_code == 200:
95
+ # Regex for direct link
96
+ match = re.search(r"https?://download[0-9]+\.mediafire\.com/[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+/[^\s'\"]+", r.text)
97
+ if match:
98
+ target_link = match.group(0).replace('"', '').replace("'", "")
99
+ else:
100
+ soup = BeautifulSoup(r.text, 'html.parser')
101
+ btn = soup.find('a', {'id': 'downloadButton'}) or soup.find('a', {'class': 'input_btn_p'})
102
+ if btn: target_link = btn.get('href')
103
+
104
+ if target_link:
105
+ if target_link.startswith("//"): target_link = f"https:{target_link}"
106
+ MEDIAFIRE_CACHE[clean_url] = {'link': target_link, 'time': current_time}
 
 
 
 
107
  except Exception as e:
108
+ print(f"Scraper Error: {e}")
 
109
 
110
  if target_link:
111
+ return await stream_file(target_link, range_header, filename)
 
 
 
 
112
  else:
113
+ raise HTTPException(status_code=404, detail="MediaFire link could not be resolved.")
114
 
115
  # --- Google Drive Section ---
116
  elif "drive.google.com" in clean_url:
 
127
  headers = {"Authorization": f"Bearer {token}"}
128
  if range_header: headers['Range'] = range_header
129
  try:
130
+ # Use stream=True to handle large video files
131
+ r = await client.get(api_link, headers=headers, stream=True)
132
  if r.status_code in [200, 206]:
133
  return await process_response(r, filename)
134
  await r.aclose()
135
  except: continue
136
+
137
+ # Public Fallback
138
  public_url = f"https://drive.google.com/uc?export=download&id={file_id}"
139
  return await stream_file(public_url, range_header, filename)
140
 
 
144
  async def stream_file(target_url, range_header, filename):
145
  headers = {'User-Agent': ua.random}
146
  if range_header: headers['Range'] = range_header
147
+
148
  try:
149
+ # Request with stream=True
150
+ r = await client.get(target_url, headers=headers, stream=True)
151
+
152
+ # If MediaFire returns HTML (Error), raise exception to retry or show error
153
+ if "text/html" in r.headers.get("Content-Type", "").lower():
154
+ await r.aclose()
155
+ raise HTTPException(status_code=415, detail="MediaFire blocked the request. Try again.")
156
+
157
  return await process_response(r, filename)
158
+ except HTTPException:
159
+ raise
160
  except Exception as e:
161
+ print(f"Stream Error: {e}")
162
  raise HTTPException(status_code=500, detail=str(e))
163
 
164
  async def process_response(r, filename):
165
  mime_type, _ = mimetypes.guess_type(filename)
166
  if not mime_type or 'application' in mime_type:
167
+ mime_type = 'video/mp4' if filename.lower().endswith('.mp4') else 'video/x-matroska'
168
+
169
+ # Essential Headers for Resume and Seeking
170
+ res_headers = {
171
+ 'Content-Type': mime_type,
172
+ 'Accept-Ranges': 'bytes',
173
+ 'Content-Disposition': f'inline; filename="{urllib.parse.quote(filename)}"',
174
+ 'Cache-Control': 'no-cache'
175
+ }
176
 
177
+ if 'content-length' in r.headers:
178
+ res_headers['Content-Length'] = r.headers['content-length']
179
+ if 'content-range' in r.headers:
180
+ res_headers['Content-Range'] = r.headers['content-range']
181
 
182
  async def stream_generator():
183
  try:
184
+ async for chunk in r.aiter_bytes(chunk_size=131072): # 128KB chunks
185
  yield chunk
186
  finally:
187
  await r.aclose()
188
 
189
+ return StreamingResponse(
190
+ stream_generator(),
191
+ status_code=r.status_code,
192
+ headers=res_headers,
193
+ media_type=mime_type
194
+ )
195
 
196
  if __name__ == "__main__":
197
  uvicorn.run(app, host="0.0.0.0", port=7860)