AstraOS commited on
Commit
e03c954
·
verified ·
1 Parent(s): e571993

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -74
app.py CHANGED
@@ -85,7 +85,7 @@ def create_html_message(text: str):
85
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
86
 
87
  def get_inline_keyboard_for_stream():
88
- # Inline keyboard for streaming controls that remain visible in the message
89
  keyboard = {
90
  "inline_keyboard": [
91
  [
@@ -101,7 +101,7 @@ def get_inline_keyboard_for_stream():
101
  return keyboard
102
 
103
  def get_inline_keyboard_for_start():
104
- # Inline keyboard with a start button for when setup is complete.
105
  keyboard = {
106
  "inline_keyboard": [
107
  [
@@ -127,7 +127,7 @@ def help_text():
127
  )
128
 
129
  def send_guide_message(chat_id, message):
130
- # Returns a response dictionary (Markdown format) to be sent as the webhook reply
131
  logging.info(f"Sending message to chat {chat_id}: {message}")
132
  return {
133
  "method": "sendMessage",
@@ -136,16 +136,6 @@ def send_guide_message(chat_id, message):
136
  "parse_mode": "Markdown"
137
  }
138
 
139
- def send_guide_message_html(chat_id, message):
140
- # Returns a response dictionary (HTML format) to be sent as the webhook reply
141
- logging.info(f"Sending HTML message to chat {chat_id}: {message}")
142
- return {
143
- "method": "sendMessage",
144
- "chat_id": chat_id,
145
- "text": message,
146
- "parse_mode": "HTML"
147
- }
148
-
149
  def reset_statistics():
150
  global stream_start_time, frames_encoded, bytes_sent
151
  stream_start_time = datetime.datetime.now()
@@ -165,19 +155,6 @@ def validate_inputs():
165
  return False, f"Missing fields: {', '.join(missing)}"
166
  return True, ""
167
 
168
- def get_streaming_display_message(prefix=""):
169
- # Build an HTML message with a prefix, current streaming status, and live logs.
170
- global live_log_display, streaming_state, frames_encoded, bytes_sent
171
- status = (
172
- f"<b>Stream Status:</b>\n"
173
- f"State: {streaming_state}\n"
174
- f"Uptime: {get_uptime()}\n"
175
- f"Frames Encoded: {frames_encoded}\n"
176
- f"Bytes Sent: {bytes_sent}\n"
177
- )
178
- logs = live_log_display if live_log_display.strip() != "" else "<pre>No logs available yet.</pre>"
179
- return f"{prefix}\n{status}\n\nLive Logs:\n{logs}"
180
-
181
  # -------------------------------------------------------------------
182
  # Error Notification Helper
183
  # -------------------------------------------------------------------
@@ -192,12 +169,10 @@ def notify_error(chat_id, error_message):
192
  def live_log_updater():
193
  global live_log_display, streaming_state
194
  try:
195
- while True:
 
196
  live_log_display = "<pre>" + "\n".join(live_log_lines[-15:]) + "</pre>"
197
  time.sleep(1)
198
- # If streaming has ended, break out after updating one last time.
199
- if streaming_state == "idle":
200
- break
201
  except Exception as e:
202
  logging.error(f"Error in live log updater: {e}")
203
 
@@ -206,9 +181,9 @@ def live_log_updater():
206
  # -------------------------------------------------------------------
207
  def logs_history(chat_id):
208
  global live_log_display, error_notification
209
- # Use live_log_display if available; otherwise, show a default message.
210
- log_text = live_log_display if live_log_display.strip() != "" else "<pre>No logs available yet.</pre>"
211
- # Prepend any error notification if present.
212
  if error_notification:
213
  if log_text.startswith("<pre>"):
214
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
@@ -221,30 +196,39 @@ def logs_history(chat_id):
221
  "parse_mode": "HTML"
222
  }
223
 
 
 
 
 
 
 
 
 
 
 
 
224
  # -------------------------------------------------------------------
225
  # Conversation Handlers
226
  # -------------------------------------------------------------------
227
  def handle_start(chat_id):
228
  global current_step, user_inputs, conversation_fields, advanced_mode
229
- # Use simple mode by default (unless advanced_mode was set via /setting)
230
  user_inputs = {}
231
  if not advanced_mode:
232
  conversation_fields = ["input_url", "output_url"]
233
  else:
234
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
235
  current_step = conversation_fields[0]
236
- text = (
237
- "👋 <b>Welcome to the Stream Bot!</b>\n\n"
238
- "Let's set up your stream.\n"
239
- f"Please enter the <b>{current_step.replace('_', ' ')}</b>"
240
- f"{' (no default)' if current_step not in default_settings else f' (default: {default_settings[current_step]})'}:"
241
- )
242
  logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
243
  return {
244
  "method": "sendMessage",
245
  "chat_id": chat_id,
246
  "text": text,
247
- "parse_mode": "HTML"
248
  }
249
 
250
  def handle_setting(chat_id):
@@ -253,16 +237,14 @@ def handle_setting(chat_id):
253
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
254
  user_inputs = {}
255
  current_step = conversation_fields[0]
256
- text = (
257
- "⚙️ <b>Advanced Mode Activated!</b>\n\n"
258
- "Please enter the <b>input url</b>:"
259
- )
260
  logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
261
  return {
262
  "method": "sendMessage",
263
  "chat_id": chat_id,
264
  "text": text,
265
- "parse_mode": "HTML"
266
  }
267
 
268
  def handle_help(chat_id):
@@ -286,15 +268,15 @@ def handle_conversation(chat_id, text):
286
  idx = conversation_fields.index(current_step)
287
  if idx < len(conversation_fields) - 1:
288
  current_step = conversation_fields[idx + 1]
289
- prompt = f"Please enter the <b>{current_step.replace('_', ' ')}</b>"
290
  if current_step in default_settings:
291
- prompt += f" (default: {default_settings[current_step]})"
292
- return send_guide_message_html(chat_id, prompt)
293
  else:
294
  current_step = None
295
  valid, msg = validate_inputs()
296
  if not valid:
297
- return send_guide_message_html(chat_id, f"Validation error: {msg}")
298
  if not advanced_mode:
299
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
300
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
@@ -302,12 +284,12 @@ def handle_conversation(chat_id, text):
302
  return {
303
  "method": "sendMessage",
304
  "chat_id": chat_id,
305
- "text": "All inputs received. Press <b>🚀 Start Streaming</b> to begin.",
306
  "reply_markup": get_inline_keyboard_for_start(),
307
- "parse_mode": "HTML"
308
  }
309
  else:
310
- return send_guide_message_html(chat_id, "Unrecognized input. Type /help for available commands.")
311
 
312
  # -------------------------------------------------------------------
313
  # Background Streaming Functions
@@ -339,8 +321,7 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
339
  out_audio_stream.layout = "stereo"
340
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
341
  logging.info("Streaming started successfully.")
342
- # Add an initial log entry so live_log_display is not empty.
343
- logging.info("Live log updating has begun.")
344
  global live_log_thread
345
  if live_log_thread is None or not live_log_thread.is_alive():
346
  live_log_thread = threading.Thread(target=live_log_updater)
@@ -395,7 +376,7 @@ def start_streaming(chat_id):
395
  global stream_thread, stream_chat_id
396
  valid, msg = validate_inputs()
397
  if not valid:
398
- return send_guide_message_html(chat_id, f"Validation error: {msg}")
399
  stream_chat_id = chat_id
400
  try:
401
  stream_thread = threading.Thread(
@@ -412,10 +393,11 @@ def start_streaming(chat_id):
412
  stream_thread.daemon = True
413
  stream_thread.start()
414
  logging.info("Streaming thread started.")
 
415
  return {
416
  "method": "sendMessage",
417
  "chat_id": chat_id,
418
- "text": get_streaming_display_message("<b>Streaming initiated!</b>"),
419
  "reply_markup": get_inline_keyboard_for_stream(),
420
  "parse_mode": "HTML"
421
  }
@@ -423,7 +405,7 @@ def start_streaming(chat_id):
423
  error_message = f"Failed to start streaming: {str(e)}"
424
  logging.error(error_message)
425
  notify_error(chat_id, error_message)
426
- return send_guide_message_html(chat_id, error_message)
427
 
428
  # -------------------------------------------------------------------
429
  # Stream Control Handlers
@@ -436,11 +418,10 @@ def pause_stream(chat_id):
436
  return {
437
  "method": "sendMessage",
438
  "chat_id": chat_id,
439
- "text": get_streaming_display_message("<b>Streaming paused.</b>"),
440
- "reply_markup": get_inline_keyboard_for_stream(),
441
  "parse_mode": "HTML"
442
  }
443
- return send_guide_message_html(chat_id, "Streaming is not active.")
444
 
445
  def resume_stream(chat_id):
446
  global streaming_state
@@ -450,11 +431,10 @@ def resume_stream(chat_id):
450
  return {
451
  "method": "sendMessage",
452
  "chat_id": chat_id,
453
- "text": get_streaming_display_message("<b>Streaming resumed.</b>"),
454
- "reply_markup": get_inline_keyboard_for_stream(),
455
  "parse_mode": "HTML"
456
  }
457
- return send_guide_message_html(chat_id, "Streaming is not paused.")
458
 
459
  def abort_stream(chat_id):
460
  global streaming_state
@@ -464,19 +444,24 @@ def abort_stream(chat_id):
464
  return {
465
  "method": "sendMessage",
466
  "chat_id": chat_id,
467
- "text": get_streaming_display_message("<b>Streaming aborted.</b>"),
468
- "reply_markup": get_inline_keyboard_for_stream(),
469
  "parse_mode": "HTML"
470
  }
471
- return send_guide_message_html(chat_id, "No active streaming to abort.")
472
 
473
  def stream_status(chat_id):
 
 
 
 
 
 
 
474
  return {
475
  "method": "sendMessage",
476
  "chat_id": chat_id,
477
- "text": get_streaming_display_message("<b>Current streaming status:</b>"),
478
- "reply_markup": get_inline_keyboard_for_stream(),
479
- "parse_mode": "HTML"
480
  }
481
 
482
  # -------------------------------------------------------------------
@@ -486,6 +471,7 @@ def stream_status(chat_id):
486
  async def telegram_webhook(request: Request):
487
  update = await request.json()
488
  logging.debug(f"Received update: {update}")
 
489
  if "message" in update:
490
  chat_id = update["message"]["chat"]["id"]
491
  text = update["message"].get("text", "").strip()
@@ -507,6 +493,7 @@ async def telegram_webhook(request: Request):
507
  return stream_status(chat_id)
508
  else:
509
  return handle_conversation(chat_id, text)
 
510
  elif "callback_query" in update:
511
  callback_data = update["callback_query"]["data"]
512
  chat_id = update["callback_query"]["message"]["chat"]["id"]
@@ -522,9 +509,15 @@ async def telegram_webhook(request: Request):
522
  elif callback_data == "start_stream":
523
  response = start_streaming(chat_id)
524
  else:
525
- response = send_guide_message_html(chat_id, "❓ Unknown callback command.")
526
- response["method"] = "editMessageText"
527
- response["message_id"] = message_id
528
- response["reply_markup"] = get_inline_keyboard_for_stream()
 
 
 
 
 
 
529
  return response
530
  return {"status": "ok"}
 
85
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
86
 
87
  def get_inline_keyboard_for_stream():
88
+ # Inline keyboard for streaming controls after the stream has started
89
  keyboard = {
90
  "inline_keyboard": [
91
  [
 
101
  return keyboard
102
 
103
  def get_inline_keyboard_for_start():
104
+ # Inline keyboard with a start button for when conversation is complete.
105
  keyboard = {
106
  "inline_keyboard": [
107
  [
 
127
  )
128
 
129
  def send_guide_message(chat_id, message):
130
+ # Return a response dictionary to be sent as the webhook reply
131
  logging.info(f"Sending message to chat {chat_id}: {message}")
132
  return {
133
  "method": "sendMessage",
 
136
  "parse_mode": "Markdown"
137
  }
138
 
 
 
 
 
 
 
 
 
 
 
139
  def reset_statistics():
140
  global stream_start_time, frames_encoded, bytes_sent
141
  stream_start_time = datetime.datetime.now()
 
155
  return False, f"Missing fields: {', '.join(missing)}"
156
  return True, ""
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  # -------------------------------------------------------------------
159
  # Error Notification Helper
160
  # -------------------------------------------------------------------
 
169
  def live_log_updater():
170
  global live_log_display, streaming_state
171
  try:
172
+ while streaming_state in ["streaming", "paused"]:
173
+ # Update the global live_log_display with the last 15 log lines in HTML format
174
  live_log_display = "<pre>" + "\n".join(live_log_lines[-15:]) + "</pre>"
175
  time.sleep(1)
 
 
 
176
  except Exception as e:
177
  logging.error(f"Error in live log updater: {e}")
178
 
 
181
  # -------------------------------------------------------------------
182
  def logs_history(chat_id):
183
  global live_log_display, error_notification
184
+ # Use the global live_log_display if available; otherwise, show a default message.
185
+ log_text = live_log_display if live_log_display else "<pre>No logs available yet.</pre>"
186
+ # If an error occurred, prepend the error notification.
187
  if error_notification:
188
  if log_text.startswith("<pre>"):
189
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
 
196
  "parse_mode": "HTML"
197
  }
198
 
199
+ # -------------------------------------------------------------------
200
+ # Helper to Compose the Streaming Message
201
+ # -------------------------------------------------------------------
202
+ def compose_streaming_message():
203
+ global live_log_display, error_notification
204
+ msg = ""
205
+ if error_notification:
206
+ msg += "<b>ERROR:</b> " + error_notification + "\n\n"
207
+ msg += "🚀 <b>Streaming in progress!</b>\n\nLive Logs:\n" + live_log_display + "\n\nUse the inline keyboard to control the stream."
208
+ return msg
209
+
210
  # -------------------------------------------------------------------
211
  # Conversation Handlers
212
  # -------------------------------------------------------------------
213
  def handle_start(chat_id):
214
  global current_step, user_inputs, conversation_fields, advanced_mode
215
+ # By default, use simple mode unless advanced_mode is set via /setting
216
  user_inputs = {}
217
  if not advanced_mode:
218
  conversation_fields = ["input_url", "output_url"]
219
  else:
220
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
221
  current_step = conversation_fields[0]
222
+ text = ("👋 *Welcome to the Stream Bot!*\n\n"
223
+ "Let's set up your stream.\n"
224
+ f"Please enter the *{current_step.replace('_', ' ')}*"
225
+ f"{' (no default)' if current_step not in default_settings else f' _(default: {default_settings[current_step]})_'}:")
 
 
226
  logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
227
  return {
228
  "method": "sendMessage",
229
  "chat_id": chat_id,
230
  "text": text,
231
+ "parse_mode": "Markdown"
232
  }
233
 
234
  def handle_setting(chat_id):
 
237
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
238
  user_inputs = {}
239
  current_step = conversation_fields[0]
240
+ text = ("⚙️ *Advanced Mode Activated!*\n\n"
241
+ "Please enter the *input url*:")
 
 
242
  logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
243
  return {
244
  "method": "sendMessage",
245
  "chat_id": chat_id,
246
  "text": text,
247
+ "parse_mode": "Markdown"
248
  }
249
 
250
  def handle_help(chat_id):
 
268
  idx = conversation_fields.index(current_step)
269
  if idx < len(conversation_fields) - 1:
270
  current_step = conversation_fields[idx + 1]
271
+ prompt = f"Please enter the *{current_step.replace('_', ' ')}*"
272
  if current_step in default_settings:
273
+ prompt += f" _(default: {default_settings[current_step]})_"
274
+ return send_guide_message(chat_id, prompt)
275
  else:
276
  current_step = None
277
  valid, msg = validate_inputs()
278
  if not valid:
279
+ return send_guide_message(chat_id, f"Validation error: {msg}")
280
  if not advanced_mode:
281
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
282
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
 
284
  return {
285
  "method": "sendMessage",
286
  "chat_id": chat_id,
287
+ "text": "All inputs received. Press *🚀 Start Streaming* to begin.",
288
  "reply_markup": get_inline_keyboard_for_start(),
289
+ "parse_mode": "Markdown"
290
  }
291
  else:
292
+ return send_guide_message(chat_id, "Unrecognized input. Type /help for available commands.")
293
 
294
  # -------------------------------------------------------------------
295
  # Background Streaming Functions
 
321
  out_audio_stream.layout = "stereo"
322
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
323
  logging.info("Streaming started successfully.")
324
+ # Start live log updater thread if not already running
 
325
  global live_log_thread
326
  if live_log_thread is None or not live_log_thread.is_alive():
327
  live_log_thread = threading.Thread(target=live_log_updater)
 
376
  global stream_thread, stream_chat_id
377
  valid, msg = validate_inputs()
378
  if not valid:
379
+ return send_guide_message(chat_id, f"Validation error: {msg}")
380
  stream_chat_id = chat_id
381
  try:
382
  stream_thread = threading.Thread(
 
393
  stream_thread.daemon = True
394
  stream_thread.start()
395
  logging.info("Streaming thread started.")
396
+ # Return a message that includes live log display and inline keyboard.
397
  return {
398
  "method": "sendMessage",
399
  "chat_id": chat_id,
400
+ "text": "🚀 <b>Streaming initiated!</b>\n\n" + compose_streaming_message(),
401
  "reply_markup": get_inline_keyboard_for_stream(),
402
  "parse_mode": "HTML"
403
  }
 
405
  error_message = f"Failed to start streaming: {str(e)}"
406
  logging.error(error_message)
407
  notify_error(chat_id, error_message)
408
+ return send_guide_message(chat_id, error_message)
409
 
410
  # -------------------------------------------------------------------
411
  # Stream Control Handlers
 
418
  return {
419
  "method": "sendMessage",
420
  "chat_id": chat_id,
421
+ "text": "<b>Streaming paused.</b>\n\n" + compose_streaming_message(),
 
422
  "parse_mode": "HTML"
423
  }
424
+ return send_guide_message(chat_id, "Streaming is not active.")
425
 
426
  def resume_stream(chat_id):
427
  global streaming_state
 
431
  return {
432
  "method": "sendMessage",
433
  "chat_id": chat_id,
434
+ "text": "▶️ <b>Streaming resumed.</b>\n\n" + compose_streaming_message(),
 
435
  "parse_mode": "HTML"
436
  }
437
+ return send_guide_message(chat_id, "Streaming is not paused.")
438
 
439
  def abort_stream(chat_id):
440
  global streaming_state
 
444
  return {
445
  "method": "sendMessage",
446
  "chat_id": chat_id,
447
+ "text": "<b>Streaming aborted.</b>",
 
448
  "parse_mode": "HTML"
449
  }
450
+ return send_guide_message(chat_id, "No active streaming to abort.")
451
 
452
  def stream_status(chat_id):
453
+ stats = (
454
+ f"*Stream Status:*\n\n"
455
+ f"• **State:** {streaming_state}\n"
456
+ f"• **Uptime:** {get_uptime()}\n"
457
+ f"• **Frames Encoded:** {frames_encoded}\n"
458
+ f"• **Bytes Sent:** {bytes_sent}\n"
459
+ )
460
  return {
461
  "method": "sendMessage",
462
  "chat_id": chat_id,
463
+ "text": stats,
464
+ "parse_mode": "Markdown"
 
465
  }
466
 
467
  # -------------------------------------------------------------------
 
471
  async def telegram_webhook(request: Request):
472
  update = await request.json()
473
  logging.debug(f"Received update: {update}")
474
+ # Process messages from users
475
  if "message" in update:
476
  chat_id = update["message"]["chat"]["id"]
477
  text = update["message"].get("text", "").strip()
 
493
  return stream_status(chat_id)
494
  else:
495
  return handle_conversation(chat_id, text)
496
+ # Process inline keyboard callback queries
497
  elif "callback_query" in update:
498
  callback_data = update["callback_query"]["data"]
499
  chat_id = update["callback_query"]["message"]["chat"]["id"]
 
509
  elif callback_data == "start_stream":
510
  response = start_streaming(chat_id)
511
  else:
512
+ response = send_guide_message(chat_id, "❓ Unknown callback command.")
513
+ # For inline queries that control streaming, update the message text
514
+ if callback_data in ["pause", "resume", "abort", "status", "start_stream"]:
515
+ response["method"] = "editMessageText"
516
+ response["message_id"] = message_id
517
+ response["text"] = compose_streaming_message()
518
+ response["parse_mode"] = "HTML"
519
+ # Always include the inline keyboard if streaming is active
520
+ if streaming_state in ["streaming", "paused"]:
521
+ response["reply_markup"] = get_inline_keyboard_for_stream()
522
  return response
523
  return {"status": "ok"}