bigbossmonster commited on
Commit
70dc04e
Β·
verified Β·
1 Parent(s): db1f3c7

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +108 -114
main.py CHANGED
@@ -29,12 +29,16 @@ async def log(message: str):
29
  if len(log_buffer) > 100:
30
  log_buffer.pop(0)
31
 
32
- def get_direct_link(file_id: str):
 
 
 
 
 
 
 
33
  """
34
- Robust link extractor:
35
- 1. Handles 'Wait' errors (403) from Step 1.
36
- 2. Handles Direct URL returned immediately in Step 1.
37
- 3. Handles Ticket -> Wait -> Step 2 (JSON) -> Final URL.
38
  """
39
  base_url = "https://api.streamtape.com/file/dlticket"
40
  params = {'file': file_id, 'login': API_LOGIN, 'key': API_KEY}
@@ -42,7 +46,7 @@ def get_direct_link(file_id: str):
42
  max_retries = 5
43
  for attempt in range(max_retries):
44
  try:
45
- print(f"Requesting ticket (Attempt {attempt+1})...")
46
  response = requests.get(base_url, params=params).json()
47
 
48
  status = response.get('status')
@@ -51,135 +55,120 @@ def get_direct_link(file_id: str):
51
 
52
  # --- CASE 1: SUCCESS (200 OK) ---
53
  if status == 200:
54
- # Sub-case A: Direct URL provided immediately in Step 1
55
- if result and 'url' in result and result['url']:
56
- print("βœ… Direct URL found immediately in Step 1.")
57
- return result['url']
 
58
 
59
- # Sub-case B: Ticket provided (Standard Flow)
60
- elif result and 'ticket' in result:
61
  ticket = result['ticket']
62
  wait_time = result.get('wait_time', 5)
63
- print(f"🎟️ Ticket received. Waiting {wait_time}s...")
64
-
65
  time.sleep(wait_time)
66
 
67
- # Step 2: Request the download link
68
- # We treat this as a JSON request now, based on your findings
69
  dl_url = "https://api.streamtape.com/file/dl"
70
  dl_params = {'file': file_id, 'ticket': ticket}
71
 
72
- print("πŸ”„ Requesting Step 2 (Final Link)...")
73
- dl_response = requests.get(dl_url, params=dl_params).json()
74
 
75
- if dl_response.get('status') == 200:
76
- final_url = dl_response['result']['url']
77
- print(f"βœ… Final Link Found: {final_url}")
78
- return final_url
79
  else:
80
- raise Exception(f"Step 2 Error: {dl_response.get('msg')}")
81
-
82
  else:
83
- raise Exception("API returned 200 but neither URL nor Ticket was found.")
84
 
85
  # --- CASE 2: WAIT (403) ---
86
  elif status == 403 and "wait" in msg.lower():
87
  wait_match = re.search(r'(\d+)', msg)
88
  wait_seconds = int(wait_match.group(1)) if wait_match else 5
89
  print(f"Rate Limit: Sleeping {wait_seconds}s...")
90
- time.sleep(wait_seconds + 2) # +2s buffer
91
  continue
92
-
93
  else:
94
  raise Exception(f"API Error: {msg}")
95
 
96
  except Exception as e:
97
- print(f"Error in attempt {attempt}: {e}")
98
- if attempt == max_retries - 1:
99
- raise e
100
  time.sleep(2)
101
 
102
  raise Exception("Max retries exceeded")
103
 
104
- async def process_video_task(file_id: str, filename: str, hf_token: str = None, hf_repo: str = None):
105
- local_path = os.path.join(DOWNLOAD_DIR, filename)
 
 
 
 
106
 
107
- # --- BROWSER HEADERS (Crucial for bypassing some blocks) ---
108
  headers = {
109
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
110
  "Referer": "https://streamtape.com/",
111
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
112
- "Accept-Language": "en-US,en;q=0.5",
113
  "Connection": "keep-alive"
114
  }
115
 
116
- try:
117
- await log(f"πŸš€ Starting task for: {filename}")
118
 
119
- # --- Step 1: Get Link ---
120
- await log(f"Requesting link for ID {file_id}...")
121
- try:
122
- # Pass headers to the link extractor too just in case
123
- direct_url = await asyncio.to_thread(get_direct_link, file_id)
124
- await log(f"πŸ”— Link acquired successfully.")
125
- except Exception as e:
126
- await log(f"❌ Error getting link: {str(e)}")
127
- return
128
-
129
- # --- Step 2: Download with Session & Headers ---
130
- await log(f"⬇️ Downloading from Streamtape...")
131
 
132
- def download_chunked():
133
- # Use a Session to manage connection pooling better
134
- with requests.Session() as s:
135
- s.headers.update(headers)
136
-
137
- # Timeout set to 15s to prevent hanging
138
- with s.get(direct_url, stream=True, timeout=15) as r:
139
- r.raise_for_status()
140
-
141
- total_size = int(r.headers.get('content-length', 0))
142
- downloaded = 0
143
-
144
- with open(local_path, 'wb') as f:
145
- for chunk in r.iter_content(chunk_size=8192):
146
- if chunk:
147
- f.write(chunk)
148
- downloaded += len(chunk)
149
- return downloaded
150
-
151
  try:
152
- await asyncio.to_thread(download_chunked)
153
- await log(f"βœ… Download complete: {filename}")
154
- except requests.exceptions.ConnectionError as e:
155
- if "101" in str(e) or "unreachable" in str(e):
156
- await log(f"β›” BLOCKED: Hugging Face Free Tier firewall blocked the storage server.")
157
- await log(f"πŸ’‘ Solution: This code works perfectly on 'Local PC' or 'Paid Spaces'.")
158
- return
159
- else:
160
- raise e
161
 
162
- # --- Step 3: Upload ---
163
- if hf_token and hf_repo:
164
- await log(f"⬆️ Uploading to HF Repo: {hf_repo}...")
165
 
166
- def upload_to_hf():
167
- api = HfApi(token=hf_token)
168
- api.upload_file(
169
- path_or_fileobj=local_path,
170
- path_in_repo=filename,
171
- repo_id=hf_repo,
172
- repo_type="dataset"
173
- )
174
 
175
- try:
176
- await asyncio.to_thread(upload_to_hf)
177
- await log(f"πŸŽ‰ Upload Success!")
178
- except Exception as e:
179
- await log(f"❌ Upload Failed: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- except Exception as e:
182
- await log(f"❌ Critical Error: {str(e)}")
183
 
184
  # --- API ENDPOINTS ---
185
 
@@ -201,25 +190,30 @@ async def read_root(request: Request):
201
  files = os.listdir(DOWNLOAD_DIR) if os.path.exists(DOWNLOAD_DIR) else []
202
  return templates.TemplateResponse("index.html", {"request": request, "files": files})
203
 
204
- @app.post("/download")
205
- async def start_download(
206
  request: Request,
207
  background_tasks: BackgroundTasks,
208
- url: str = Form(...),
209
- filename: str = Form(...),
210
  hf_token: str = Form(None),
211
- hf_repo: str = Form(None)
 
212
  ):
213
- try:
214
- # Robust ID Extraction
215
- if "/v/" in url: file_id = url.split("/v/")[1].split("/")[0]
216
- elif "/e/" in url: file_id = url.split("/e/")[1].split("/")[0]
217
- else: file_id = url.strip()
218
-
219
- background_tasks.add_task(process_video_task, file_id, filename, hf_token, hf_repo)
220
- msg = f"Task started for {filename}"
221
- except Exception as e:
222
- msg = f"Error: {str(e)}"
223
-
224
- files = os.listdir(DOWNLOAD_DIR) if os.path.exists(DOWNLOAD_DIR) else []
225
- return templates.TemplateResponse("index.html", {"request": request, "files": files, "message": msg})
 
 
 
 
 
 
29
  if len(log_buffer) > 100:
30
  log_buffer.pop(0)
31
 
32
+ def extract_file_id(url: str):
33
+ """Cleanly extracts ID from various URL formats."""
34
+ url = url.strip()
35
+ if "/v/" in url: return url.split("/v/")[1].split("/")[0]
36
+ if "/e/" in url: return url.split("/e/")[1].split("/")[0]
37
+ return url
38
+
39
+ def get_download_info(file_id: str):
40
  """
41
+ Returns a tuple: (direct_url, filename)
 
 
 
42
  """
43
  base_url = "https://api.streamtape.com/file/dlticket"
44
  params = {'file': file_id, 'login': API_LOGIN, 'key': API_KEY}
 
46
  max_retries = 5
47
  for attempt in range(max_retries):
48
  try:
49
+ print(f"Requesting ticket for {file_id} (Attempt {attempt+1})...")
50
  response = requests.get(base_url, params=params).json()
51
 
52
  status = response.get('status')
 
55
 
56
  # --- CASE 1: SUCCESS (200 OK) ---
57
  if status == 200:
58
+ # Sub-case A: Direct URL provided immediately
59
+ if result.get('url'):
60
+ # Name usually provided in result as well
61
+ name = result.get('name', f"{file_id}.mp4")
62
+ return result['url'], name
63
 
64
+ # Sub-case B: Ticket provided
65
+ elif result.get('ticket'):
66
  ticket = result['ticket']
67
  wait_time = result.get('wait_time', 5)
68
+ print(f"🎟️ Ticket found. Waiting {wait_time}s...")
 
69
  time.sleep(wait_time)
70
 
71
+ # Step 2: Get Final Link & Name
 
72
  dl_url = "https://api.streamtape.com/file/dl"
73
  dl_params = {'file': file_id, 'ticket': ticket}
74
 
75
+ dl_resp = requests.get(dl_url, params=dl_params).json()
 
76
 
77
+ if dl_resp.get('status') == 200:
78
+ final_url = dl_resp['result']['url']
79
+ final_name = dl_resp['result']['name']
80
+ return final_url, final_name
81
  else:
82
+ raise Exception(f"Step 2 Error: {dl_resp.get('msg')}")
 
83
  else:
84
+ raise Exception("API returned 200 but missing URL/Ticket.")
85
 
86
  # --- CASE 2: WAIT (403) ---
87
  elif status == 403 and "wait" in msg.lower():
88
  wait_match = re.search(r'(\d+)', msg)
89
  wait_seconds = int(wait_match.group(1)) if wait_match else 5
90
  print(f"Rate Limit: Sleeping {wait_seconds}s...")
91
+ time.sleep(wait_seconds + 2)
92
  continue
 
93
  else:
94
  raise Exception(f"API Error: {msg}")
95
 
96
  except Exception as e:
97
+ print(f"Error: {e}")
98
+ if attempt == max_retries - 1: raise e
 
99
  time.sleep(2)
100
 
101
  raise Exception("Max retries exceeded")
102
 
103
+ async def batch_processor(urls: list, hf_token: str, hf_repo: str, repo_type: str):
104
+ """
105
+ Processes a list of URLs sequentially:
106
+ Download -> Upload -> Delete -> Next
107
+ """
108
+ await log(f"πŸ“¦ Batch started with {len(urls)} links.")
109
 
 
110
  headers = {
111
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/115.0.0.0 Safari/537.36",
112
  "Referer": "https://streamtape.com/",
 
 
113
  "Connection": "keep-alive"
114
  }
115
 
116
+ for index, url in enumerate(urls):
117
+ if not url.strip(): continue
118
 
119
+ file_id = extract_file_id(url)
120
+ await log(f"▢️ Processing ({index+1}/{len(urls)}): ID {file_id}")
 
 
 
 
 
 
 
 
 
 
121
 
122
+ local_path = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  try:
124
+ # 1. Get Link & Name
125
+ direct_url, filename = await asyncio.to_thread(get_download_info, file_id)
126
+ await log(f" πŸ”Ή Name Detected: {filename}")
127
+
128
+ local_path = os.path.join(DOWNLOAD_DIR, filename)
 
 
 
 
129
 
130
+ # 2. Download
131
+ await log(f" ⬇️ Downloading...")
 
132
 
133
+ def download_file():
134
+ with requests.Session() as s:
135
+ s.headers.update(headers)
136
+ with s.get(direct_url, stream=True, timeout=45) as r:
137
+ r.raise_for_status()
138
+ with open(local_path, 'wb') as f:
139
+ shutil.copyfileobj(r.raw, f)
 
140
 
141
+ await asyncio.to_thread(download_file)
142
+ await log(f" βœ… Downloaded to storage.")
143
+
144
+ # 3. Upload to HF
145
+ if hf_token and hf_repo:
146
+ await log(f" ⬆️ Uploading to HF ({repo_type})...")
147
+
148
+ def upload_file():
149
+ api = HfApi(token=hf_token)
150
+ api.upload_file(
151
+ path_or_fileobj=local_path,
152
+ path_in_repo=filename,
153
+ repo_id=hf_repo,
154
+ repo_type=repo_type
155
+ )
156
+
157
+ await asyncio.to_thread(upload_file)
158
+ await log(f" πŸŽ‰ Upload Complete!")
159
+
160
+ # 4. DELETE LOCAL FILE (Cleanup)
161
+ if os.path.exists(local_path):
162
+ os.remove(local_path)
163
+ await log(f" πŸ—‘οΈ Local file deleted to free space.")
164
+
165
+ except Exception as e:
166
+ await log(f" ❌ Failed: {str(e)}")
167
+ # Even if failed, try to cleanup partial files
168
+ if local_path and os.path.exists(local_path):
169
+ os.remove(local_path)
170
 
171
+ await log("🏁 Batch Processing Completed.")
 
172
 
173
  # --- API ENDPOINTS ---
174
 
 
190
  files = os.listdir(DOWNLOAD_DIR) if os.path.exists(DOWNLOAD_DIR) else []
191
  return templates.TemplateResponse("index.html", {"request": request, "files": files})
192
 
193
+ @app.post("/start_batch")
194
+ async def start_batch(
195
  request: Request,
196
  background_tasks: BackgroundTasks,
197
+ urls: str = Form(...),
 
198
  hf_token: str = Form(None),
199
+ hf_repo: str = Form(None),
200
+ repo_type: str = Form("dataset")
201
  ):
202
+ # Split text area by newlines
203
+ url_list = [line.strip() for line in urls.splitlines() if line.strip()]
204
+
205
+ if not url_list:
206
+ return templates.TemplateResponse("index.html", {
207
+ "request": request,
208
+ "files": os.listdir(DOWNLOAD_DIR),
209
+ "message": "❌ No URLs provided."
210
+ })
211
+
212
+ # Start background process
213
+ background_tasks.add_task(batch_processor, url_list, hf_token, hf_repo, repo_type)
214
+
215
+ return templates.TemplateResponse("index.html", {
216
+ "request": request,
217
+ "files": os.listdir(DOWNLOAD_DIR),
218
+ "message": f"πŸš€ Batch started for {len(url_list)} files."
219
+ })