Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
anasty17
GitHub Repository: anasty17/mirror-leech-telegram-bot
Path: blob/master/web/wserver.py
1618 views
1
from uvloop import install
2
3
install()
4
from contextlib import asynccontextmanager
5
from fastapi import FastAPI, Request
6
from fastapi.responses import HTMLResponse, JSONResponse
7
from fastapi.templating import Jinja2Templates
8
from logging import getLogger, FileHandler, StreamHandler, INFO, basicConfig, WARNING
9
from asyncio import sleep
10
from sabnzbdapi import SabnzbdClient
11
from aioaria2 import Aria2HttpClient
12
from aioqbt.client import create_client
13
from aiohttp.client_exceptions import ClientError
14
from aioqbt.exc import AQError
15
16
from web.nodes import extract_file_ids, make_tree
17
18
getLogger("httpx").setLevel(WARNING)
19
getLogger("aiohttp").setLevel(WARNING)
20
21
aria2 = None
22
qbittorrent = None
23
sabnzbd_client = SabnzbdClient(
24
host="http://localhost",
25
api_key="mltb",
26
port="8070",
27
)
28
29
30
@asynccontextmanager
31
async def lifespan(app: FastAPI):
32
global aria2, qbittorrent
33
aria2 = Aria2HttpClient("http://localhost:6800/jsonrpc")
34
qbittorrent = await create_client("http://localhost:8090/api/v2/")
35
yield
36
await aria2.close()
37
await qbittorrent.close()
38
39
40
app = FastAPI(lifespan=lifespan)
41
42
43
templates = Jinja2Templates(directory="web/templates/")
44
45
basicConfig(
46
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
47
handlers=[FileHandler("log.txt"), StreamHandler()],
48
level=INFO,
49
)
50
51
LOGGER = getLogger(__name__)
52
53
54
async def re_verify(paused, resumed, hash_id):
55
k = 0
56
while True:
57
res = await qbittorrent.torrents.files(hash_id)
58
verify = True
59
for i in res:
60
if i.index in paused and i.priority != 0:
61
verify = False
62
break
63
if i.index in resumed and i.priority == 0:
64
verify = False
65
break
66
if verify:
67
break
68
LOGGER.info("Reverification Failed! Correcting stuff...")
69
await sleep(0.5)
70
if paused:
71
try:
72
await qbittorrent.torrents.file_prio(
73
hash=hash_id, id=paused, priority=0
74
)
75
except (ClientError, TimeoutError, Exception, AQError) as e:
76
LOGGER.error(f"{e} Errored in reverification paused!")
77
if resumed:
78
try:
79
await qbittorrent.torrents.file_prio(
80
hash=hash_id, id=resumed, priority=1
81
)
82
except (ClientError, TimeoutError, Exception, AQError) as e:
83
LOGGER.error(f"{e} Errored in reverification resumed!")
84
k += 1
85
if k > 5:
86
return False
87
LOGGER.info(f"Verified! Hash: {hash_id}")
88
return True
89
90
91
@app.get("/app/files", response_class=HTMLResponse)
92
async def files(request: Request):
93
return templates.TemplateResponse("page.html", {"request": request})
94
95
96
@app.api_route(
97
"/app/files/torrent", methods=["GET", "POST"], response_class=HTMLResponse
98
)
99
async def handle_torrent(request: Request):
100
params = request.query_params
101
102
if not (gid := params.get("gid")):
103
return JSONResponse(
104
{
105
"files": [],
106
"engine": "",
107
"error": "GID is missing",
108
"message": "GID not specified",
109
}
110
)
111
112
if not (pin := params.get("pin")):
113
return JSONResponse(
114
{
115
"files": [],
116
"engine": "",
117
"error": "Pin is missing",
118
"message": "PIN not specified",
119
}
120
)
121
122
code = "".join([nbr for nbr in gid if nbr.isdigit()][:4])
123
if code != pin:
124
return JSONResponse(
125
{
126
"files": [],
127
"engine": "",
128
"error": "Invalid pin",
129
"message": "The PIN you entered is incorrect",
130
}
131
)
132
133
if request.method == "POST":
134
if not (mode := params.get("mode")):
135
return JSONResponse(
136
{
137
"files": [],
138
"engine": "",
139
"error": "Mode is not specified",
140
"message": "Mode is not specified",
141
}
142
)
143
data = await request.json()
144
if mode == "rename":
145
if len(gid) > 20:
146
await handle_rename(gid, data)
147
content = {
148
"files": [],
149
"engine": "",
150
"error": "",
151
"message": "Rename successfully.",
152
}
153
else:
154
content = {
155
"files": [],
156
"engine": "",
157
"error": "Rename failed.",
158
"message": "Cannot rename aria2c torrent file",
159
}
160
else:
161
selected_files, unselected_files = extract_file_ids(data)
162
if gid.startswith("SABnzbd_nzo"):
163
await set_sabnzbd(gid, unselected_files)
164
elif len(gid) > 20:
165
await set_qbittorrent(gid, selected_files, unselected_files)
166
else:
167
selected_files = ",".join(selected_files)
168
await set_aria2(gid, selected_files)
169
content = {
170
"files": [],
171
"engine": "",
172
"error": "",
173
"message": "Your selection has been submitted successfully.",
174
}
175
else:
176
try:
177
if gid.startswith("SABnzbd_nzo"):
178
res = await sabnzbd_client.get_files(gid)
179
content = make_tree(res, "sabnzbd")
180
elif len(gid) > 20:
181
res = await qbittorrent.torrents.files(gid)
182
content = make_tree(res, "qbittorrent")
183
else:
184
res = await aria2.getFiles(gid)
185
op = await aria2.getOption(gid)
186
fpath = f"{op['dir']}/"
187
content = make_tree(res, "aria2", fpath)
188
except (ClientError, TimeoutError, Exception, AQError) as e:
189
LOGGER.error(str(e))
190
content = {
191
"files": [],
192
"engine": "",
193
"error": "Error getting files",
194
"message": str(e),
195
}
196
return JSONResponse(content)
197
198
199
async def handle_rename(gid, data):
200
try:
201
_type = data["type"]
202
del data["type"]
203
if _type == "file":
204
await qbittorrent.torrents.rename_file(hash=gid, **data)
205
else:
206
await qbittorrent.torrents.rename_folder(hash=gid, **data)
207
except (ClientError, TimeoutError, Exception, AQError) as e:
208
LOGGER.error(f"{e} Errored in renaming")
209
210
211
async def set_sabnzbd(gid, unselected_files):
212
await sabnzbd_client.remove_file(gid, unselected_files)
213
LOGGER.info(f"Verified! nzo_id: {gid}")
214
215
216
async def set_qbittorrent(gid, selected_files, unselected_files):
217
if unselected_files:
218
try:
219
await qbittorrent.torrents.file_prio(
220
hash=gid, id=unselected_files, priority=0
221
)
222
except (ClientError, TimeoutError, Exception, AQError) as e:
223
LOGGER.error(f"{e} Errored in paused")
224
if selected_files:
225
try:
226
await qbittorrent.torrents.file_prio(
227
hash=gid, id=selected_files, priority=1
228
)
229
except (ClientError, TimeoutError, Exception, AQError) as e:
230
LOGGER.error(f"{e} Errored in resumed")
231
await sleep(0.5)
232
if not await re_verify(unselected_files, selected_files, gid):
233
LOGGER.error(f"Verification Failed! Hash: {gid}")
234
235
236
async def set_aria2(gid, selected_files):
237
res = await aria2.changeOption(gid, {"select-file": selected_files})
238
if res == "OK":
239
LOGGER.info(f"Verified! Gid: {gid}")
240
else:
241
LOGGER.info(f"Verification Failed! Report! Gid: {gid}")
242
243
244
@app.get("/", response_class=HTMLResponse)
245
async def homepage():
246
return (
247
"<h1>See mirror-leech-telegram-bot "
248
"<a href='https://www.github.com/anasty17/mirror-leech-telegram-bot'>@GitHub</a> "
249
"By <a href='https://github.com/anasty17'>Anas</a></h1>"
250
)
251
252
253
@app.exception_handler(Exception)
254
async def page_not_found(_, exc):
255
return HTMLResponse(
256
f"<h1>404: Task not found! Mostly wrong input. <br><br>Error: {exc}</h1>",
257
status_code=404,
258
)
259
260