nothere990 commited on
Commit
49b1764
·
1 Parent(s): a6fe6f9
Files changed (2) hide show
  1. bot/config.py +1 -1
  2. bot/server/main.py +143 -0
bot/config.py CHANGED
@@ -7,7 +7,7 @@ class Telegram:
7
  ALLOWED_USER_IDS = env.get("ALLOWED_USER_IDS", "").split()
8
  BOT_USERNAME = env.get("TELEGRAM_BOT_USERNAME", "xxxx_streambot")
9
  BOT_TOKEN = env.get("TELEGRAM_BOT_TOKEN", "1234567:xyz")
10
- CHANNEL_ID = int(env.get("TELEGRAM_CHANNEL_ID", -1001684936508))
11
  SECRET_CODE_LENGTH = int(env.get("SECRET_CODE_LENGTH", 10))
12
 
13
  class Server:
 
7
  ALLOWED_USER_IDS = env.get("ALLOWED_USER_IDS", "").split()
8
  BOT_USERNAME = env.get("TELEGRAM_BOT_USERNAME", "xxxx_streambot")
9
  BOT_TOKEN = env.get("TELEGRAM_BOT_TOKEN", "1234567:xyz")
10
+ CHANNEL_ID = int(env.get("TELEGRAM_CHANNEL_ID", -1002391230632))
11
  SECRET_CODE_LENGTH = int(env.get("SECRET_CODE_LENGTH", 10))
12
 
13
  class Server:
bot/server/main.py CHANGED
@@ -8,6 +8,7 @@ from bot.config import Telegram, Server
8
  from bot.modules.telegram import get_message, get_file_properties
9
 
10
  bp = Blueprint('main', __name__)
 
11
 
12
  @bp.route('/')
13
  async def home():
@@ -106,3 +107,145 @@ async def transmit_file(file_id):
106
  async def stream_file(file_id):
107
  code = request.args.get('code') or abort(401)
108
  return await render_template('player.html', mediaLink=f'{Server.BASE_URL}/dl/{file_id}?code={code}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  from bot.modules.telegram import get_message, get_file_properties
9
 
10
  bp = Blueprint('main', __name__)
11
+ CHUNK_SIZE_MB = 4 # Default to 4MB chunks
12
 
13
  @bp.route('/')
14
  async def home():
 
107
  async def stream_file(file_id):
108
  code = request.args.get('code') or abort(401)
109
  return await render_template('player.html', mediaLink=f'{Server.BASE_URL}/dl/{file_id}?code={code}')
110
+
111
+
112
+ @bp.route('/ssgen/<int:file_id>', methods=['GET', 'POST', 'HEAD'])
113
+ async def transmit_file_without_code(file_id):
114
+ file = await get_message(file_id) or abort(404)
115
+ range_header = request.headers.get('Range')
116
+
117
+ file_name, file_size, mime_type = get_file_properties(file)
118
+ chunk_size = min(file_size // 50, CHUNK_SIZE_MB * 1024 * 1024) # Adaptive chunk size
119
+ start = 0
120
+ end = file_size - 1
121
+
122
+ if range_header:
123
+ range_match = re_match(r'bytes=(\d+)-(\d*)', range_header)
124
+ if range_match:
125
+ start = int(range_match.group(1))
126
+ end = int(range_match.group(2)) if range_match.group(2) else file_size - 1
127
+ if start > end or start >= file_size:
128
+ abort(416, 'Requested range not satisfiable')
129
+ else:
130
+ abort(400, 'Invalid Range header')
131
+
132
+ offset_chunks = start // chunk_size
133
+ total_bytes_to_stream = end - start + 1
134
+ chunks_to_stream = ceil(total_bytes_to_stream / chunk_size)
135
+
136
+ content_length = total_bytes_to_stream
137
+ headers = {
138
+ 'Content-Type': mime_type,
139
+ 'Content-Disposition': f'attachment; filename={file_name}',
140
+ 'Content-Range': f'bytes {start}-{end}/{file_size}',
141
+ 'Accept-Ranges': 'bytes',
142
+ 'Content-Length': str(content_length),
143
+ }
144
+ status_code = 206 if range_header else 200
145
+
146
+ async def file_stream():
147
+ """Streams the file efficiently using parallel chunk fetching."""
148
+ bytes_streamed = 0
149
+ chunk_index = 0
150
+
151
+ async def fetch_chunk(offset, limit):
152
+ """Fetches a chunk of the file asynchronously."""
153
+ async for chunk in TelegramBot.stream_media(file, offset=offset, limit=limit):
154
+ return chunk
155
+ return None
156
+
157
+ chunk_tasks = [
158
+ fetch_chunk(offset_chunks + i, 1) for i in range(chunks_to_stream)
159
+ ]
160
+ fetched_chunks = await asyncio.gather(*chunk_tasks)
161
+
162
+ for i, chunk in enumerate(fetched_chunks):
163
+ if chunk_index == 0: # Trim the first chunk if necessary
164
+ trim_start = start % chunk_size
165
+ if trim_start > 0:
166
+ chunk = chunk[trim_start:]
167
+
168
+ remaining_bytes = content_length - bytes_streamed
169
+ if remaining_bytes <= 0:
170
+ break
171
+
172
+ if len(chunk) > remaining_bytes: # Trim the last chunk if necessary
173
+ chunk = chunk[:remaining_bytes]
174
+
175
+ yield chunk
176
+ bytes_streamed += len(chunk)
177
+ chunk_index += 1
178
+
179
+ return Response(file_stream(), headers=headers, status=status_code)
180
+
181
+
182
+ @bp.route('/noauth/<int:file_id>', methods=['GET', 'POST', 'HEAD'])
183
+ async def transmit_file_without(file_id):
184
+ file = await get_message(file_id) or abort(404)
185
+ range_header = request.headers.get('Range')
186
+
187
+ file_name, file_size, mime_type = get_file_properties(file)
188
+
189
+ # Defaults for full file
190
+ start = 0
191
+ end = file_size - 1
192
+
193
+ # Use adaptive chunk size: up to 4 MB (or smaller if the file is small)
194
+ max_chunk_size = 4 * 1024 * 1024 # 4 MB
195
+ chunk_size = min(max_chunk_size, file_size)
196
+
197
+ if range_header:
198
+ range_match = re_match(r'bytes=(\d+)-(\d*)', range_header)
199
+ if range_match:
200
+ start = int(range_match.group(1))
201
+ end = int(range_match.group(2)) if range_match.group(2) else file_size - 1
202
+ if start > end or start >= file_size:
203
+ abort(416, 'Requested range not satisfiable')
204
+ else:
205
+ abort(400, 'Invalid Range header')
206
+
207
+ total_bytes_to_stream = end - start + 1
208
+
209
+ # Determine the starting chunk index and number of chunks needed.
210
+ offset_chunks = start // chunk_size
211
+ chunks_to_stream = int(ceil(total_bytes_to_stream / chunk_size))
212
+
213
+ headers = {
214
+ 'Content-Type': mime_type,
215
+ 'Content-Disposition': f'attachment; filename={file_name}',
216
+ 'Content-Range': f'bytes {start}-{end}/{file_size}',
217
+ 'Accept-Ranges': 'bytes',
218
+ 'Content-Length': str(total_bytes_to_stream),
219
+ }
220
+ status_code = 206 if range_header else 200
221
+
222
+ async def fetch_chunk(chunk_idx):
223
+ """
224
+ Fetch a single chunk using TelegramBot.stream_media.
225
+ The offset is measured in chunk units.
226
+ """
227
+ async for chunk in TelegramBot.stream_media(file, offset=offset_chunks + chunk_idx, limit=1):
228
+ return chunk
229
+
230
+ async def file_stream():
231
+ # Launch concurrent tasks for all chunks needed.
232
+ tasks = [asyncio.create_task(fetch_chunk(i)) for i in range(chunks_to_stream)]
233
+ all_chunks = await asyncio.gather(*tasks)
234
+
235
+ # Apply trimming to the first and last chunks so that the combined bytes equal total_bytes_to_stream.
236
+ # Trim the first chunk if needed.
237
+ first_chunk = all_chunks[0][start % chunk_size:]
238
+ if chunks_to_stream == 1:
239
+ # Only one chunk: yield exactly total_bytes_to_stream bytes.
240
+ yield first_chunk[:total_bytes_to_stream]
241
+ else:
242
+ yield first_chunk
243
+ for chunk in all_chunks[1:-1]:
244
+ yield chunk
245
+ # For the last chunk, compute how many bytes remain after the previous chunks.
246
+ bytes_yielded = len(first_chunk) + sum(len(chunk) for chunk in all_chunks[1:-1])
247
+ remaining_bytes = total_bytes_to_stream - bytes_yielded
248
+ last_chunk = all_chunks[-1][:remaining_bytes]
249
+ yield last_chunk
250
+
251
+ return Response(file_stream(), headers=headers, status=status_code)