|
|
|
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() |
|
|
|
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
templates = Jinja2Templates(directory=".") |
|
|
|
active_sessions = {} |
|
|
|
|
|
@app.get("/search") |
|
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] |
|
|
|
except Exception as e: |
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
@app.get("/stream/{torrent_id}/mime") |
|
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) |
|
|
|
|
|
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)) |
|
|
|
|
|
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 |
|
} |
|
|
|
@app.post("/start_stream") |
|
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}"} |
|
|
|
@app.get("/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) |
|
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) |