THED2 commited on
Commit
45d6603
·
verified ·
1 Parent(s): e95661d

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -473
app.py DELETED
@@ -1,473 +0,0 @@
1
- import asyncio
2
- import os
3
- import re
4
- import time
5
- import uuid
6
- import shutil
7
- import aiohttp
8
- import gc # Memory management ke liye garbage collector
9
- import zipfile # ZIP file handle karne ke liye in-built library
10
- import urllib3 # Zero-buffer strict low RAM downloding ke liye
11
- from fastapi import FastAPI, BackgroundTasks, Request, File, UploadFile
12
- from fastapi.middleware.cors import CORSMiddleware
13
- from fastapi.responses import FileResponse, StreamingResponse
14
- from pydantic import BaseModel
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
- tasks = {}
27
- DATA_DIR = "/data"
28
- os.makedirs(DATA_DIR, exist_ok=True)
29
-
30
- class DownloadRequest(BaseModel):
31
- url: str
32
-
33
- def clean_filename(url: str) -> str:
34
- """URL se sahi extension aur filename nikalne ka jugaad"""
35
- path = url.split("?")[0]
36
- filename = path.split("/")[-1]
37
- if not filename or "." not in filename:
38
- return "downloaded_file.bin"
39
- return filename
40
-
41
- async def cleanup_old_files():
42
- while True:
43
- try:
44
- now = time.time()
45
- for filename in os.listdir(DATA_DIR):
46
- filepath = os.path.join(DATA_DIR, filename)
47
- if os.path.isfile(filepath):
48
- if now - os.path.getmtime(filepath) > 86400:
49
- os.remove(filepath)
50
- for tid, tinfo in list(tasks.items()):
51
- if tinfo.get("file_path") == filepath:
52
- del tasks[tid]
53
- except Exception as e:
54
- print(f"Cleanup error: {e}")
55
- await asyncio.sleep(3600)
56
-
57
- @app.on_event("startup")
58
- async def startup_event():
59
- asyncio.create_task(cleanup_old_files())
60
-
61
- def sync_download_worker(task_id: str, url: str):
62
- """Urllib3 pool se strict raw streaming taaki RAM me cache accumulate na ho"""
63
- file_path = tasks[task_id]["file_path"]
64
- try:
65
- http = urllib3.PoolManager(block=True, maxsize=1)
66
- response = http.request('GET', url, preload_content=False, timeout=None)
67
-
68
- total_size = int(response.headers.get('Content-Length', 0))
69
- tasks[task_id]["total_size"] = total_size
70
-
71
- # Proper filename aur extension extraction (.apk wagera)
72
- cd = response.headers.get('Content-Disposition')
73
- if cd and 'filename=' in cd:
74
- fname = re.findall('filename="([^"]+)"', cd)
75
- if not fname:
76
- fname = re.findall('filename=([^;]+)', cd)
77
- if fname:
78
- new_filename = fname[0]
79
- file_path = os.path.join(DATA_DIR, f"{task_id}_{new_filename}")
80
- tasks[task_id]["file_path"] = file_path
81
- tasks[task_id]["original_filename"] = new_filename
82
-
83
- downloaded = 0
84
- start_time = time.time()
85
- last_time = start_time
86
- last_downloaded = 0
87
- chunk_counter = 0
88
-
89
- with open(file_path, 'wb') as f:
90
- # 64KB strict buffer chunking, RAM me data holds hi nahi hoga
91
- for chunk in response.stream(64 * 1024):
92
- if not chunk:
93
- break
94
- f.write(chunk)
95
- downloaded += len(chunk)
96
- tasks[task_id]["downloaded"] = downloaded
97
-
98
- current_time = time.time()
99
- if current_time - last_time >= 1.0:
100
- speed = (downloaded - last_downloaded) / (current_time - last_time)
101
- tasks[task_id]["speed"] = speed
102
- last_time = current_time
103
- last_downloaded = downloaded
104
-
105
- f.flush()
106
- chunk_counter += 1
107
- if chunk_counter % 10 == 0:
108
- os.fsync(f.fileno())
109
- del chunk
110
- gc.collect() # Strict cleanup inside raw sync thread
111
-
112
- response.release_conn()
113
- tasks[task_id]["status"] = "completed"
114
- tasks[task_id]["speed"] = 0.0
115
- except Exception as e:
116
- tasks[task_id]["status"] = "error"
117
- tasks[task_id]["error"] = str(e)
118
- finally:
119
- gc.collect()
120
-
121
- async def download_file(task_id: str, url: str):
122
- # Loop block bypass karne ke liye execution ko framework thread pool me run karenge
123
- loop = asyncio.get_event_loop()
124
- await loop.run_in_executor(None, sync_download_worker, task_id, url)
125
-
126
- @app.post("/start_download")
127
- async def start_download(req: DownloadRequest, background_tasks: BackgroundTasks):
128
- task_id = str(uuid.uuid4())
129
- filename = clean_filename(req.url)
130
-
131
- tasks[task_id] = {
132
- "task_id": task_id,
133
- "url": req.url,
134
- "status": "downloading",
135
- "total_size": 0,
136
- "downloaded": 0,
137
- "speed": 0.0,
138
- "file_path": os.path.join(DATA_DIR, f"{task_id}_{filename}"),
139
- "original_filename": filename,
140
- "timestamp": time.time()
141
- }
142
- background_tasks.add_task(download_file, task_id, req.url)
143
- return {"task_id": task_id}
144
-
145
- @app.get("/status/{task_id}")
146
- async def get_status(task_id: str):
147
- if task_id not in tasks:
148
- return {"error": "Task not found"}
149
- return tasks[task_id]
150
-
151
- @app.get("/download/{task_id}")
152
- async def download(task_id: str):
153
- if task_id not in tasks or tasks[task_id]["status"] != "completed":
154
- return {"error": "File not ready"}
155
-
156
- # Hugging Face reverse proxy buffer layer bypass headers
157
- headers = {
158
- "X-Accel-Buffering": "no",
159
- "Cache-Control": "no-cache, no-store, must-revalidate",
160
- "Pragma": "no-cache"
161
- }
162
- return FileResponse(
163
- tasks[task_id]["file_path"],
164
- filename=tasks[task_id].get("original_filename"),
165
- headers=headers
166
- )
167
-
168
- @app.get("/stream/{task_id}")
169
- async def stream(task_id: str, request: Request):
170
- if task_id not in tasks or tasks[task_id]["status"] != "completed":
171
- return {"error": "File not ready"}
172
-
173
- file_path = tasks[task_id]["file_path"]
174
- file_size = os.path.getsize(file_path)
175
- range_header = request.headers.get("Range")
176
-
177
- # OUTPUT DATA PIPELINE UNLOCK: User download chunk size badhakar 2MB kar diya hai taaki throughput fast ho sake
178
- STREAM_CHUNK_SIZE = 2 * 1024 * 1024
179
-
180
- # Proxy bypass headers to stop network-side buffering during transmission
181
- base_headers = {
182
- "X-Accel-Buffering": "no",
183
- "Cache-Control": "no-cache, no-store, must-revalidate",
184
- "Pragma": "no-cache",
185
- "Accept-Ranges": "bytes"
186
- }
187
-
188
- if range_header:
189
- byte1, byte2 = 0, None
190
- match = range_header.replace("bytes=", "").split("-")
191
- if match[0]:
192
- byte1 = int(match[0])
193
- if len(match) > 1 and match[1]:
194
- byte2 = int(match[1])
195
-
196
- length = file_size - byte1
197
- if byte2 is not None:
198
- length = byte2 + 1 - byte1
199
-
200
- def file_iterator(start, length_left):
201
- with open(file_path, "rb") as f:
202
- f.seek(start)
203
- while length_left > 0:
204
- read_size = min(STREAM_CHUNK_SIZE, length_left)
205
- data = f.read(read_size)
206
- if not data:
207
- break
208
- yield data
209
- length_left -= len(data)
210
- del data
211
-
212
- base_headers.update({
213
- "Content-Range": f"bytes {byte1}-{byte1+length-1}/{file_size}",
214
- "Content-Length": str(length),
215
- })
216
- return StreamingResponse(file_iterator(byte1, length), status_code=206, headers=base_headers)
217
- else:
218
- def full_file_iterator():
219
- with open(file_path, "rb") as f:
220
- while True:
221
- data = f.read(STREAM_CHUNK_SIZE)
222
- if not data:
223
- break
224
- yield data
225
- del data
226
-
227
- base_headers.update({
228
- "Content-Length": str(file_size),
229
- "Content-Disposition": f'attachment; filename="{tasks[task_id].get("original_filename")}"'
230
- })
231
- return StreamingResponse(full_file_iterator(), media_type="application/octet-stream", headers=base_headers)
232
-
233
- @app.get("/storage")
234
- async def get_storage():
235
- total, used, free = shutil.disk_usage(DATA_DIR)
236
- return {"total": total, "used": used, "free": free}
237
-
238
- @app.post("/delete_all")
239
- async def delete_all():
240
- for filename in os.listdir(DATA_DIR):
241
- filepath = os.path.join(DATA_DIR, filename)
242
- try:
243
- if os.path.isfile(filepath):
244
- os.remove(filepath)
245
- elif os.path.isdir(filepath):
246
- shutil.rmtree(filepath)
247
- except Exception as e:
248
- print(f"Failed to delete {filepath}: {e}")
249
- tasks.clear()
250
- gc.collect()
251
- return {"status": "success"}
252
-
253
- @app.get("/history")
254
- async def get_history():
255
- return {"history": list(tasks.values())}
256
-
257
- @app.post("/upload")
258
- async def upload_file(file: UploadFile = File(...)):
259
- task_id = str(uuid.uuid4())
260
- original_filename = file.filename if file.filename else "uploaded_file.bin"
261
- file_path = os.path.join(DATA_DIR, f"{task_id}_{original_filename}")
262
-
263
- with open(file_path, "wb") as buffer:
264
- while True:
265
- chunk = await file.read(64 * 1024)
266
- if not chunk:
267
- break
268
- buffer.write(chunk)
269
- buffer.flush()
270
- os.fsync(buffer.fileno())
271
- del chunk
272
- gc.collect()
273
-
274
- await file.close()
275
- file_size = os.path.getsize(file_path)
276
-
277
- tasks[task_id] = {
278
- "task_id": task_id,
279
- "url": "local_upload",
280
- "status": "completed",
281
- "total_size": file_size,
282
- "downloaded": file_size,
283
- "speed": 0.0,
284
- "file_path": file_path,
285
- "original_filename": original_filename,
286
- "timestamp": time.time()
287
- }
288
- gc.collect()
289
- return {"task_id": task_id}
290
-
291
- @app.post("/start_gofile_transfer")
292
- async def start_gofile_transfer(req: DownloadRequest, background_tasks: BackgroundTasks):
293
- task_id = str(uuid.uuid4())
294
- filename = clean_filename(req.url)
295
-
296
- tasks[task_id] = {
297
- "task_id": task_id,
298
- "url": req.url,
299
- "status": "downloading",
300
- "total_size": 0,
301
- "downloaded": 0,
302
- "speed": 0.0,
303
- "file_path": os.path.join(DATA_DIR, f"{task_id}_{filename}"),
304
- "original_filename": filename,
305
- "gofile_url": None,
306
- "timestamp": time.time()
307
- }
308
- background_tasks.add_task(process_gofile_transfer, task_id, req.url)
309
- return {"task_id": task_id}
310
-
311
- async def file_sender(file_path, chunk_size=64 * 1024):
312
- with open(file_path, 'rb') as f:
313
- while True:
314
- chunk = f.read(chunk_size)
315
- if not chunk:
316
- break
317
- yield chunk
318
- del chunk
319
- gc.collect()
320
-
321
- def sync_gofile_download_part(task_id: str, url: str):
322
- """GoFile processing ke liye bhi same raw low RAM wrapper"""
323
- file_path = tasks[task_id]["file_path"]
324
- http = urllib3.PoolManager(block=True, maxsize=1)
325
- response = http.request('GET', url, preload_content=False, timeout=None)
326
-
327
- total_size = int(response.headers.get('Content-Length', 0))
328
- tasks[task_id]["total_size"] = total_size
329
-
330
- cd = response.headers.get('Content-Disposition')
331
- if cd and 'filename=' in cd:
332
- fname = re.findall('filename="([^"]+)"', cd)
333
- if not fname:
334
- fname = re.findall('filename=([^;]+)', cd)
335
- if fname:
336
- new_filename = fname[0]
337
- file_path = os.path.join(DATA_DIR, f"{task_id}_{new_filename}")
338
- tasks[task_id]["file_path"] = file_path
339
- tasks[task_id]["original_filename"] = new_filename
340
-
341
- downloaded = 0
342
- start_time = time.time()
343
- last_time = start_time
344
- last_downloaded = 0
345
- chunk_counter = 0
346
-
347
- with open(file_path, 'wb') as f:
348
- for chunk in response.stream(64 * 1024):
349
- if not chunk:
350
- break
351
- f.write(chunk)
352
- downloaded += len(chunk)
353
- tasks[task_id]["downloaded"] = downloaded
354
-
355
- current_time = time.time()
356
- if current_time - last_time >= 1.0:
357
- tasks[task_id]["speed"] = (downloaded - last_downloaded) / (current_time - last_time)
358
- last_time = current_time
359
- last_downloaded = downloaded
360
-
361
- f.flush()
362
- chunk_counter += 1
363
- if chunk_counter % 10 == 0:
364
- os.fsync(f.fileno())
365
- del chunk
366
- gc.collect()
367
- response.release_conn()
368
-
369
- async def process_gofile_transfer(task_id: str, url: str):
370
- try:
371
- loop = asyncio.get_event_loop()
372
- await loop.run_in_executor(None, sync_gofile_download_part, task_id, url)
373
-
374
- file_path = tasks[task_id]["file_path"]
375
- tasks[task_id]["status"] = "uploading_to_gofile"
376
- tasks[task_id]["speed"] = 0.0
377
-
378
- async with aiohttp.ClientSession() as session:
379
- async with session.get("https://api.gofile.io/servers") as resp:
380
- servers_data = await resp.json()
381
- server = servers_data["data"]["servers"][0]["name"]
382
-
383
- upload_url = f"https://{server}.gofile.io/uploadFile"
384
-
385
- data = aiohttp.FormData()
386
- data.add_field('file', file_sender(file_path), filename=tasks[task_id]["original_filename"])
387
-
388
- async with session.post(upload_url, data=data) as upload_resp:
389
- upload_result = await upload_resp.json()
390
- if upload_result["status"] == "ok":
391
- tasks[task_id]["gofile_url"] = upload_result["data"]["downloadPage"]
392
- tasks[task_id]["status"] = "completed"
393
- else:
394
- raise Exception("GoFile upload failed")
395
-
396
- try:
397
- os.remove(file_path)
398
- except:
399
- pass
400
-
401
- except Exception as e:
402
- tasks[task_id]["status"] = "error"
403
- tasks[task_id]["error"] = str(e)
404
- finally:
405
- gc.collect()
406
-
407
- # ==================== ZIP MANAGEMENT FEATURES ====================
408
-
409
- @app.get("/list_zip/{task_id}")
410
- async def list_zip_contents(task_id: str):
411
- if task_id not in tasks:
412
- return {"error": "Task not found"}
413
-
414
- file_path = tasks[task_id]["file_path"]
415
- if not os.path.exists(file_path):
416
- return {"error": "File does not exist on server"}
417
-
418
- if not zipfile.is_zipfile(file_path):
419
- return {"error": "This file is not a valid ZIP archive"}
420
-
421
- try:
422
- with zipfile.ZipFile(file_path, 'r') as z:
423
- file_list = []
424
- for info in z.infolist():
425
- file_list.append({
426
- "filename": info.filename,
427
- "file_size_mb": round(info.file_size / (1024 * 1024), 2),
428
- "is_dir": info.is_dir()
429
- })
430
- return {"task_id": task_id, "total_files": len(file_list), "files": file_list}
431
- except Exception as e:
432
- return {"error": f"Failed to read ZIP: {str(e)}"}
433
-
434
- @app.post("/extract_zip/{task_id}")
435
- async def extract_zip_file(task_id: str, background_tasks: BackgroundTasks):
436
- if task_id not in tasks:
437
- return {"error": "Task not found"}
438
-
439
- file_path = tasks[task_id]["file_path"]
440
- if not os.path.exists(file_path):
441
- return {"error": "File does not exist on server"}
442
-
443
- if not zipfile.is_zipfile(file_path):
444
- return {"error": "This file is not a valid ZIP archive"}
445
-
446
- extract_folder = os.path.join(DATA_DIR, f"extracted_{task_id}")
447
- os.makedirs(extract_folder, exist_ok=True)
448
-
449
- tasks[task_id]["status"] = "extracting"
450
- tasks[task_id]["extract_path"] = extract_folder
451
-
452
- background_tasks.add_task(process_zip_extraction, task_id, file_path, extract_folder)
453
- return {"status": "extraction_started", "task_id": task_id, "target_folder": extract_folder}
454
-
455
- def process_zip_extraction(task_id: str, zip_path: str, target_dir: str):
456
- try:
457
- with zipfile.ZipFile(zip_path, 'r') as z:
458
- file_counter = 0
459
- for member in z.namelist():
460
- z.extract(member, path=target_dir)
461
- file_counter += 1
462
- if file_counter % 10 == 0:
463
- gc.collect()
464
- tasks[task_id]["status"] = "extracted"
465
- except Exception as e:
466
- tasks[task_id]["status"] = "extraction_error"
467
- tasks[task_id]["error"] = str(e)
468
- finally:
469
- gc.collect()
470
-
471
- if __name__ == "__main__":
472
- import uvicorn
473
- uvicorn.run(app, host="0.0.0.0", port=7860)