Spaces:
Runtime error
Runtime error
Mbonea-Mjema
commited on
Commit
•
00c68f3
1
Parent(s):
49a605b
ok
Browse files- Dockerfile +7 -0
- src/Classes/Download.py +115 -0
- src/Classes/Hander.py +55 -0
- src/Classes/Sanity.py +37 -0
- src/__main__.py +1 -0
- src/app.py +55 -0
Dockerfile
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:latest
|
2 |
+
WORKDIR /usr/src/nginx
|
3 |
+
COPY . .
|
4 |
+
|
5 |
+
RUN pip install -r requirements.txt
|
6 |
+
CMD export PYTHONPATH="${PYTHONPATH}:./src" && python3 ./src/app.py
|
7 |
+
EXPOSE 8080
|
src/Classes/Download.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math, asyncio, subprocess
|
2 |
+
from telethon import TelegramClient
|
3 |
+
from aiohttp import web
|
4 |
+
|
5 |
+
import logging
|
6 |
+
|
7 |
+
logging.getLogger(__name__)
|
8 |
+
logging.basicConfig(level=logging.INFO)
|
9 |
+
|
10 |
+
|
11 |
+
class Download:
|
12 |
+
client: TelegramClient
|
13 |
+
route: str
|
14 |
+
offset: int
|
15 |
+
handler: None
|
16 |
+
file: None
|
17 |
+
limit: int
|
18 |
+
file_size: float
|
19 |
+
|
20 |
+
def __init__(self, handler):
|
21 |
+
self.handler = handler
|
22 |
+
self.file = handler.message.media
|
23 |
+
self.file_size = handler.message.file.size
|
24 |
+
self.limit = handler.sanity.limit
|
25 |
+
self.offset = handler.sanity.offset
|
26 |
+
self.client = handler.client
|
27 |
+
self.mime_type = handler.message.file.mime_type
|
28 |
+
|
29 |
+
async def download(self):
|
30 |
+
part_size = int(512 * 1024) * 2
|
31 |
+
first_part_cut = self.offset % part_size
|
32 |
+
first_part = math.floor(self.offset / part_size)
|
33 |
+
last_part_cut = part_size - (self.limit % part_size)
|
34 |
+
last_part = math.ceil(self.limit / part_size)
|
35 |
+
part_count = math.ceil(self.file_size / part_size)
|
36 |
+
part = first_part
|
37 |
+
try:
|
38 |
+
async for chunk in self.client.iter_download(
|
39 |
+
self.file, offset=first_part * part_size, request_size=part_size
|
40 |
+
):
|
41 |
+
|
42 |
+
if part == first_part:
|
43 |
+
yield chunk[first_part_cut:]
|
44 |
+
elif part == last_part:
|
45 |
+
yield chunk[:last_part_cut]
|
46 |
+
else:
|
47 |
+
yield chunk
|
48 |
+
logging.debug(f"Part {part}/{last_part} (total {part_count}) served!")
|
49 |
+
part += 1
|
50 |
+
logging.debug("serving finished")
|
51 |
+
except (GeneratorExit, StopAsyncIteration, asyncio.CancelledError):
|
52 |
+
logging.debug("file serve interrupted")
|
53 |
+
|
54 |
+
raise
|
55 |
+
except Exception as e:
|
56 |
+
print(e)
|
57 |
+
logging.debug("file serve errored", exc_info=True)
|
58 |
+
|
59 |
+
async def handle_request(self):
|
60 |
+
headers = {
|
61 |
+
"content-type": self.mime_type,
|
62 |
+
"content-range": f"bytes {self.offset}-{self.limit-1}/{self.file_size}",
|
63 |
+
"content-length": str(self.limit - self.offset),
|
64 |
+
"accept-ranges": "bytes",
|
65 |
+
"content-transfer-encoding": "Binary",
|
66 |
+
"content-disposition": f'{self.handler.route}; filename="{self.handler.message.file.name}"',
|
67 |
+
}
|
68 |
+
logging.info(
|
69 |
+
f"Serving file in {self.handler.message.file.name}) ; Range: {self.offset} - {self.limit}"
|
70 |
+
)
|
71 |
+
if self.handler.head:
|
72 |
+
body = None
|
73 |
+
else:
|
74 |
+
|
75 |
+
body = self.download()
|
76 |
+
# if body:
|
77 |
+
|
78 |
+
# ffmpeg = "ffmpeg"
|
79 |
+
|
80 |
+
# cmd = [
|
81 |
+
# ffmpeg,
|
82 |
+
# "ffmpeg",
|
83 |
+
# "-i",
|
84 |
+
|
85 |
+
|
86 |
+
# "pipe:0",
|
87 |
+
# "-c",
|
88 |
+
# "copy",
|
89 |
+
# "-re",
|
90 |
+
|
91 |
+
# "pipe:1",
|
92 |
+
# ]
|
93 |
+
# ffmpeg_cmd = subprocess.Popen(
|
94 |
+
# cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False
|
95 |
+
# )
|
96 |
+
# body = await body.__anext__()
|
97 |
+
# ffmpeg_cmd.stdin.write(body.tobytes())
|
98 |
+
# ffmpeg_cmd.stdin.close()
|
99 |
+
# body = b""
|
100 |
+
# while True:
|
101 |
+
# print(body)
|
102 |
+
# output = ffmpeg_cmd.stdout.read()
|
103 |
+
|
104 |
+
# if len(output) > 0:
|
105 |
+
# body += output
|
106 |
+
# ffmpeg_cmd.stdout.close()
|
107 |
+
|
108 |
+
# else:
|
109 |
+
# error_msg = ffmpeg_cmd.poll()
|
110 |
+
# if error_msg is not None:
|
111 |
+
# break
|
112 |
+
|
113 |
+
return web.Response(
|
114 |
+
body=body, headers=headers, status=206 if self.offset else 200
|
115 |
+
)
|
src/Classes/Hander.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from src.Classes.Sanity import Sanity
|
2 |
+
from aiohttp.web import Request
|
3 |
+
from aiohttp import web
|
4 |
+
from telethon import TelegramClient
|
5 |
+
from telethon.tl.types import Message
|
6 |
+
from src.Classes.Download import Download
|
7 |
+
|
8 |
+
|
9 |
+
class Handler:
|
10 |
+
req: Request
|
11 |
+
client: TelegramClient
|
12 |
+
chat_id = -1001278111932
|
13 |
+
message: Message
|
14 |
+
route: str
|
15 |
+
head = False
|
16 |
+
sanity: Sanity
|
17 |
+
|
18 |
+
def __init__(self, req: Request, client, route=None, head=False):
|
19 |
+
self.head = head
|
20 |
+
self.req = req
|
21 |
+
self.client = client
|
22 |
+
self.sanity = Sanity()
|
23 |
+
self.sanity.client = self.client
|
24 |
+
self.sanity.chat_id = self.chat_id
|
25 |
+
self.sanity.req = self.req
|
26 |
+
self.sanity.file_id = int(self.req.match_info["id"])
|
27 |
+
|
28 |
+
async def sanity_checks(self):
|
29 |
+
self.message = await self.sanity.file_exists()
|
30 |
+
|
31 |
+
try:
|
32 |
+
if not self.message.media:
|
33 |
+
return web.json_response(
|
34 |
+
status=404,
|
35 |
+
data={"Error": "File Does not Exist", "route": self.route},
|
36 |
+
)
|
37 |
+
except:
|
38 |
+
return web.json_response(
|
39 |
+
status=404, data={"Error": "File Does not Exist", "route": self.route}
|
40 |
+
)
|
41 |
+
|
42 |
+
if self.sanity.check_ranges() == False:
|
43 |
+
return web.json_response(
|
44 |
+
status=416,
|
45 |
+
text="416: Range Not Satisfiable",
|
46 |
+
headers={"Content-Range": f"bytes */{self.message.file.size}"},
|
47 |
+
)
|
48 |
+
|
49 |
+
async def process_request(self):
|
50 |
+
response = await self.sanity_checks()
|
51 |
+
if type(response) is web.Response:
|
52 |
+
return response
|
53 |
+
|
54 |
+
# download/stream
|
55 |
+
return await Download(self).handle_request()
|
src/Classes/Sanity.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from telethon import TelegramClient
|
2 |
+
from aiohttp.web import Request
|
3 |
+
|
4 |
+
|
5 |
+
class Sanity:
|
6 |
+
|
7 |
+
client: TelegramClient
|
8 |
+
media = None
|
9 |
+
chat_id: int
|
10 |
+
file_id: int
|
11 |
+
req: Request
|
12 |
+
limit: int
|
13 |
+
offset: int
|
14 |
+
|
15 |
+
async def file_exists(self):
|
16 |
+
try:
|
17 |
+
self.media = await self.client.get_messages(
|
18 |
+
entity=self.chat_id, ids=self.file_id
|
19 |
+
)
|
20 |
+
return self.media
|
21 |
+
except Exception as e:
|
22 |
+
pass
|
23 |
+
|
24 |
+
def check_ranges(self):
|
25 |
+
|
26 |
+
offset = self.req.http_range.start or 0
|
27 |
+
|
28 |
+
limit = self.req.http_range.stop or self.media.file.size
|
29 |
+
self.offset = offset
|
30 |
+
self.limit = limit
|
31 |
+
|
32 |
+
if (limit > self.media.file.size) or (offset < 0) or (limit < offset):
|
33 |
+
|
34 |
+
return False
|
35 |
+
else:
|
36 |
+
|
37 |
+
return True
|
src/__main__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import app
|
src/app.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import asyncio
|
3 |
+
from aiohttp import web
|
4 |
+
import asyncio
|
5 |
+
from telethon import TelegramClient
|
6 |
+
from src.Classes.Hander import Handler
|
7 |
+
import os
|
8 |
+
|
9 |
+
routes = web.RouteTableDef()
|
10 |
+
client = TelegramClient("stream_bot", 870972, "ce2efaca02dfcd110941be6025e9ac0d")
|
11 |
+
|
12 |
+
|
13 |
+
@routes.get(r"/")
|
14 |
+
async def get_status(req: web.Request):
|
15 |
+
return web.json_response(data={"status": "working"}, status=200)
|
16 |
+
|
17 |
+
|
18 |
+
@routes.get(r"/stream/{id:\d+}")
|
19 |
+
async def handle_get_request(req: web.Request) -> web.Response:
|
20 |
+
hander = Handler(req=req, client=client)
|
21 |
+
hander.route = "inline"
|
22 |
+
return await hander.process_request()
|
23 |
+
|
24 |
+
|
25 |
+
@routes.head(r"/download/{id:\d+}")
|
26 |
+
async def handle_get_request(req: web.Request) -> web.Response:
|
27 |
+
hander = Handler(req=req, client=client)
|
28 |
+
hander.head = True
|
29 |
+
hander.route = "attachment"
|
30 |
+
return await hander.process_request()
|
31 |
+
|
32 |
+
|
33 |
+
@routes.get(r"/download/{id:\d+}")
|
34 |
+
async def handle_get_request(req: web.Request) -> web.Response:
|
35 |
+
hander = Handler(req=req, client=client)
|
36 |
+
hander.route = "attachment"
|
37 |
+
return await hander.process_request()
|
38 |
+
|
39 |
+
|
40 |
+
async def main():
|
41 |
+
port = int(os.environ.get("PORT", "8080"))
|
42 |
+
await client.start(bot_token="384248541:AAFRkVeRyCUvlO_JuIzsE5qbWGt7-Mi7WKk")
|
43 |
+
server = web.Application()
|
44 |
+
server.add_routes(routes)
|
45 |
+
runner = web.AppRunner(server)
|
46 |
+
await runner.setup()
|
47 |
+
|
48 |
+
await web.TCPSite(runner, port=port).start()
|
49 |
+
|
50 |
+
|
51 |
+
if __name__ == "__main__":
|
52 |
+
loop = asyncio.get_event_loop()
|
53 |
+
loop.run_until_complete(main())
|
54 |
+
loop.run_forever()
|
55 |
+
|