Spaces:
Building
Building
import os | |
import time | |
import uuid | |
import asyncio | |
import libtorrent as lt | |
from fastapi import FastAPI, HTTPException | |
from fastapi.responses import StreamingResponse | |
import magic | |
from fastapi import Request | |
from fastapi.staticfiles import StaticFiles | |
from fastapi.templating import Jinja2Templates | |
from bs4 import BeautifulSoup | |
import requests | |
app = FastAPI() | |
# Add to top of app.py | |
app.mount("/static", StaticFiles(directory="static"), name="static") | |
templates = Jinja2Templates(directory=".") | |
active_sessions = {} | |
# Add search endpoint | |
async def search_torrents(q: str): | |
try: | |
url = f"https://torrentgalaxy.to/torrents.php?search={q}" | |
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} | |
response = requests.get(url, headers=headers) | |
soup = BeautifulSoup(response.text, 'html.parser') | |
torrents = [] | |
for row in soup.select('div.tgxtablerow'): | |
try: | |
title = row.select_one('div:nth-child(4)').get_text(strip=True) | |
magnet = row.select_one('div:nth-child(5) a[href^="magnet:"]')['href'] | |
size = row.select_one('div:nth-child(8)').get_text(strip=True) | |
seeds = row.select_one('div:nth-child(11) font').get_text(strip=True) | |
torrents.append({ | |
'title': title, | |
'magnet': magnet, | |
'size': size, | |
'seeds': seeds | |
}) | |
except: | |
continue | |
return torrents[:10] # Return top 10 results | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
# Add mime type endpoint | |
async def get_mime_type(torrent_id: str): | |
if torrent_id not in active_sessions: | |
raise HTTPException(status_code=404, detail="Torrent session not found") | |
file_path = active_sessions[torrent_id]["file_path"] | |
mime = magic.Magic(mime=True) | |
return {"mime_type": mime.from_file(file_path)} | |
def get_largest_file(torrent_info): | |
files = torrent_info.files() | |
file_sizes = [(i, files.file_size(i)) for i in range(files.num_files())] | |
if not file_sizes: | |
return None | |
return max(file_sizes, key=lambda x: x[1]) | |
async def add_torrent(magnet_link: str, save_path: str): | |
ses = lt.session() | |
params = { | |
'save_path': save_path, | |
'storage_mode': lt.storage_mode_t(2), | |
} | |
handle = lt.add_magnet_uri(ses, magnet_link, params) | |
# Wait for metadata | |
while not handle.has_metadata(): | |
await asyncio.sleep(1) | |
torrent_info = handle.get_torrent_info() | |
file_info = get_largest_file(torrent_info) | |
if not file_info: | |
raise Exception("No files found in torrent") | |
file_index, _ = file_info | |
file_path = os.path.join(save_path, torrent_info.files().file_path(file_index)) | |
# Prioritize largest file | |
for i in range(torrent_info.num_files()): | |
handle.file_priority(i, 7 if i == file_index else 0) | |
handle.set_sequential_download(True) | |
ses.resume() | |
return { | |
"session": ses, | |
"handle": handle, | |
"file_path": file_path, | |
"downloading": True | |
} | |
async def start_stream(magnet_link: str): | |
torrent_id = str(uuid.uuid4()) | |
save_path = f"./downloads/{torrent_id}" | |
os.makedirs(save_path, exist_ok=True) | |
try: | |
session_info = await add_torrent(magnet_link, save_path) | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
active_sessions[torrent_id] = session_info | |
return {"stream_url": f"/stream/{torrent_id}"} | |
async def stream_torrent(torrent_id: str): | |
if torrent_id not in active_sessions: | |
raise HTTPException(status_code=404, detail="Torrent session not found") | |
session_info = active_sessions[torrent_id] | |
file_path = session_info["file_path"] | |
if not os.path.exists(file_path): | |
raise HTTPException(status_code=404, detail="File not found") | |
mime = magic.Magic(mime=True) | |
mime_type = mime.from_file(file_path) | |
def file_generator(): | |
last_position = 0 | |
while True: | |
if not os.path.exists(file_path): | |
time.sleep(1) | |
continue | |
with open(file_path, "rb") as f: | |
f.seek(last_position) | |
data = f.read(1024 * 1024) # 1MB chunks | |
if not data: | |
if session_info["handle"].is_seed(): | |
break | |
time.sleep(1) | |
continue | |
last_position = f.tell() | |
yield data | |
return StreamingResponse(file_generator(), media_type=mime_type) | |
if __name__ == "__main__": | |
import uvicorn | |
uvicorn.run(app, host="0.0.0.0", port=8000) |