Tolimo commited on
Commit
d0ae45d
·
verified ·
1 Parent(s): 181910d

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +95 -74
main.py CHANGED
@@ -3,111 +3,132 @@ from fastapi import FastAPI, HTTPException, Query, BackgroundTasks, Body
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import FileResponse
5
  from starlette.background import BackgroundTask
6
- import subprocess, os, uuid, json, requests, time, re, urllib.parse
 
 
 
 
 
 
 
 
7
 
8
  app = FastAPI()
9
- app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
 
 
 
 
 
 
 
10
 
11
  TEMP_DIR = "/tmp/fb_cache"
12
- if not os.path.exists(TEMP_DIR): os.makedirs(TEMP_DIR)
 
 
13
  MAX_FILE_AGE = 1800
14
 
15
  def cleanup(file_path: str):
16
- if os.path.exists(file_path): os.remove(file_path)
 
17
 
18
  def clean_fb_url(url: str):
19
  if not url: return None
20
  url = url.replace('\\/', '/')
21
- try: url = json.loads(f'"{url}"')
 
22
  except: pass
23
  return urllib.parse.unquote(url).replace('&', '&')
24
 
25
- def download_temp_file(url: str, suffix: str):
26
- if not url: return None
27
- path = os.path.join(TEMP_DIR, f"{uuid.uuid4()}{suffix}")
28
  try:
29
- headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'}
30
- r = requests.get(url, stream=True, timeout=45, headers=headers)
31
- if r.status_code == 200:
32
- with open(path, 'wb') as f:
33
- for chunk in r.iter_content(chunk_size=1024*1024):
34
- f.write(chunk)
35
- if os.path.getsize(path) > 1024: return path
36
- except: pass
37
- if os.path.exists(path): os.remove(path)
38
- return None
39
 
40
  @app.post("/download-private")
41
  async def download_private_fb(payload: dict = Body(...)):
42
  html_source = payload.get("html", "")
43
- if not html_source: return {"success": False, "error": "HTML Empty"}
44
-
45
- found_streams = []
46
 
47
- # ၁။ HD/SD သာမန်လင့်ခ်များ (ရုပ်ရောအသံရော ပါပြီးသား)
48
- hd = re.search(r'"browser_native_hd_url":"([^"]+)"', html_source) or re.search(r'"playable_url_quality_hd":"([^"]+)"', html_source)
49
- sd = re.search(r'"browser_native_sd_url":"([^"]+)"', html_source) or re.search(r'"playable_url":"([^"]+)"', html_source)
50
-
51
- if hd: found_streams.append({"res": "HD Quality", "url": clean_fb_url(hd.group(1)), "type": "instant"})
52
- if sd: found_streams.append({"res": "SD Quality", "url": clean_fb_url(sd.group(1)), "type": "instant"})
53
 
54
- # ၂။ DASH Audio ရှာဖွေခြင်း (ဗီဒီယိုနဲ့ ပေါင်းဖို့ အသံလင့်ခ် ရှာတာပါ)
 
55
  audio_url = None
56
- a_match = re.search(r'"mime_type":"audio/mp4".*?"base_url":"([^"]+)"', html_source)
57
- if a_match: audio_url = clean_fb_url(a_match.group(1))
58
-
59
- # ၃။ DASH Video Streams (Resolution ပေါင်းစုံ ရှာဖွေခြင်း)
60
- # Regex ကို ပိုပြီး Flexible ဖြစ်အောင် ပြင်ထားပါတယ်
61
- video_matches = re.findall(r'"mime_type":"video/mp4".*?"height":(\d+).*?"base_url":"([^"]+)"', html_source)
62
- for h, u in video_matches:
63
- res_label = f"{h}p"
64
- url = clean_fb_url(u)
65
- if not any(s['res'] == res_label for s in found_streams):
66
- found_streams.append({
67
- "res": res_label,
68
- "url": url,
69
- "audio_url": audio_url, # အသံလင့်ခ်ကိုပါ တွဲပေးလိုက်မယ်
70
- "type": "merge_needed"
71
- })
72
-
73
- if not found_streams:
74
- return {"success": False, "error": "No streams found. Make sure you copied the FULL source code."}
75
-
76
- return {"success": True, "streams": found_streams, "title": "Facebook Private Video"}
77
-
78
- @app.get("/process-video")
79
- async def process_video(url: str, res: str, audio_url: str = None):
80
- # အသုံးပြုသူက ခလုတ်နှိပ်လိုက်မှ Re-encoding အလုပ်လုပ်မယ့်နေရာ
81
- file_id = str(uuid.uuid4())
82
- out_file = os.path.join(TEMP_DIR, f"{file_id}.mp4")
83
 
84
- v_tmp = download_temp_file(url, ".mp4")
85
- a_tmp = download_temp_file(audio_url, ".mp3") if audio_url else None
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  try:
88
- if v_tmp:
89
- if a_tmp:
90
- # Merge Video + Audio + Force H.264 (The Fix)
91
- cmd = ['ffmpeg', '-y', '-i', v_tmp, '-i', a_tmp, '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '23', '-c:a', 'aac', '-b:a', '128k', '-map', '0:v:0', '-map', '1:a:0', '-movflags', '+faststart', out_file]
92
- else:
93
- cmd = ['ffmpeg', '-y', '-i', v_tmp, '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '23', '-c:a', 'aac', '-movflags', '+faststart', out_file]
94
-
95
- subprocess.run(cmd, check=True, capture_output=True)
96
- if v_tmp: os.remove(v_tmp)
97
- if a_tmp: os.remove(a_tmp)
 
 
 
 
 
 
 
98
 
99
- space_id = os.getenv('SPACE_ID', '').replace("/", "-")
100
- return {"success": True, "download_url": f"https://{space_id}.hf.space/get_file?id={file_id}.mp4"}
101
- else:
102
- raise Exception("Video download failed.")
 
 
 
 
 
 
 
 
 
103
  except Exception as e:
104
- if v_tmp and os.path.exists(v_tmp): os.remove(v_tmp)
105
- if a_tmp and os.path.exists(a_tmp): os.remove(a_tmp)
106
  return {"success": False, "error": str(e)}
107
 
108
  @app.get("/get_file")
109
  async def get_file(id: str):
110
  file_path = os.path.join(TEMP_DIR, id)
111
  if os.path.exists(file_path):
112
- return FileResponse(file_path, media_type="video/mp4", background=BackgroundTask(cleanup, file_path))
113
  raise HTTPException(status_code=404)
 
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import FileResponse
5
  from starlette.background import BackgroundTask
6
+ import subprocess
7
+ import os
8
+ import uuid
9
+ import json
10
+ import requests
11
+ import time
12
+ import hashlib
13
+ import re
14
+ import urllib.parse
15
 
16
  app = FastAPI()
17
+
18
+ app.add_middleware(
19
+ CORSMiddleware,
20
+ allow_origins=["*"],
21
+ allow_credentials=True,
22
+ allow_methods=["*"],
23
+ allow_headers=["*"],
24
+ )
25
 
26
  TEMP_DIR = "/tmp/fb_cache"
27
+ if not os.path.exists(TEMP_DIR):
28
+ os.makedirs(TEMP_DIR)
29
+
30
  MAX_FILE_AGE = 1800
31
 
32
  def cleanup(file_path: str):
33
+ if os.path.exists(file_path):
34
+ os.remove(file_path)
35
 
36
  def clean_fb_url(url: str):
37
  if not url: return None
38
  url = url.replace('\\/', '/')
39
+ try:
40
+ url = json.loads(f'"{url}"')
41
  except: pass
42
  return urllib.parse.unquote(url).replace('&', '&')
43
 
44
+ def get_ffprobe_info(file_path):
 
 
45
  try:
46
+ cmd = ['ffprobe', '-v', 'error', '-show_entries', 'stream=codec_type', '-of', 'json', file_path]
47
+ result = subprocess.run(cmd, capture_output=True, text=True)
48
+ data = json.loads(result.stdout)
49
+ return [s['codec_type'] for s in data.get('streams', [])]
50
+ except: return []
51
+
52
+ @app.get("/")
53
+ def root():
54
+ return {"message": "Bumiz Facebook Pro API Active"}
 
55
 
56
  @app.post("/download-private")
57
  async def download_private_fb(payload: dict = Body(...)):
58
  html_source = payload.get("html", "")
59
+ target_quality = payload.get("quality", "original") # original, 720, 480, 240
 
 
60
 
61
+ if not html_source:
62
+ return {"success": False, "error": "HTML Source is empty."}
 
 
 
 
63
 
64
+ # Video & Audio Extraction Logic
65
+ video_url = None
66
  audio_url = None
67
+
68
+ # HD သို့မဟုတ် Progressive Link ကို အရင်ရှာခြင်း
69
+ hd_match = re.search(r'"browser_native_hd_url":"([^"]+)"', html_source)
70
+ sd_match = re.search(r'"browser_native_sd_url":"([^"]+)"', html_source)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ if hd_match: video_url = clean_fb_url(hd_match.group(1))
73
+ elif sd_match: video_url = clean_fb_url(sd_match.group(1))
74
 
75
+ # Separate Streams ရှာဖွေခြင်း
76
+ v_matches = re.findall(r'"mime_type":"video[^"]+".*?"base_url":"([^"]+)"', html_source)
77
+ a_matches = re.findall(r'"mime_type":"audio[^"]+".*?"base_url":"([^"]+)"', html_source)
78
+
79
+ if not video_url and v_matches: video_url = clean_fb_url(v_matches[0])
80
+ if a_matches: audio_url = clean_fb_url(a_matches[0])
81
+
82
+ if not video_url:
83
+ return {"success": False, "error": "Video link not found in source."}
84
+
85
+ file_id = hashlib.md5(f"{video_url}_{target_quality}".encode()).hexdigest()
86
+ final_video = os.path.join(TEMP_DIR, f"{file_id}.mp4")
87
+
88
+ # Cache Check
89
+ if os.path.exists(final_video):
90
+ os.utime(final_video, None)
91
+ return {"success": True, "method": "Cache", "video_url": f"get_file?id={file_id}.mp4", "size": os.path.getsize(final_video)}
92
+
93
+ # Processing
94
  try:
95
+ v_tmp = os.path.join(TEMP_DIR, f"{uuid.uuid4()}_v.mp4")
96
+ a_tmp = os.path.join(TEMP_DIR, f"{uuid.uuid4()}_a.mp3")
97
+
98
+ # Download streams
99
+ headers = {'User-Agent': 'Mozilla/5.0'}
100
+ with open(v_tmp, 'wb') as f: f.write(requests.get(video_url, headers=headers).content)
101
+ if audio_url:
102
+ with open(a_tmp, 'wb') as f: f.write(requests.get(audio_url, headers=headers).content)
103
+
104
+ # Build FFmpeg Command (Force H.264 & AAC)
105
+ cmd = ['ffmpeg', '-y', '-i', v_tmp]
106
+ if os.path.exists(a_tmp) and os.path.getsize(a_tmp) > 1000:
107
+ cmd += ['-i', a_tmp, '-map', '0:v:0', '-map', '1:a:0']
108
+
109
+ # Quality Scaling if needed
110
+ if target_quality != "original":
111
+ cmd += ['-vf', f"scale=w='trunc(oh*a/2)*2':h={target_quality}"]
112
 
113
+ cmd += ['-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '23', '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart', final_video]
114
+
115
+ subprocess.run(cmd, check=True, capture_output=True)
116
+
117
+ if os.path.exists(v_tmp): os.remove(v_tmp)
118
+ if os.path.exists(a_tmp): os.remove(a_tmp)
119
+
120
+ return {
121
+ "success": True,
122
+ "method": "Bumiz Engine",
123
+ "video_url": f"get_file?id={file_id}.mp4",
124
+ "size": os.path.getsize(final_video)
125
+ }
126
  except Exception as e:
 
 
127
  return {"success": False, "error": str(e)}
128
 
129
  @app.get("/get_file")
130
  async def get_file(id: str):
131
  file_path = os.path.join(TEMP_DIR, id)
132
  if os.path.exists(file_path):
133
+ return FileResponse(file_path, media_type="video/mp4", filename=f"fb_bumiz_{id}", background=BackgroundTask(cleanup, file_path))
134
  raise HTTPException(status_code=404)