AstraOS commited on
Commit
3756307
·
verified ·
1 Parent(s): dd259e0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -60
app.py CHANGED
@@ -12,12 +12,15 @@ import av
12
  app = FastAPI()
13
 
14
  # -------------------------------------------------------------------
15
- # Configuration & Global Variables
16
  # -------------------------------------------------------------------
17
- # (No outgoing HTTP calls or token usage in this version.)
18
- # Conversation state variables
19
  user_inputs = {}
20
- conversation_fields = [] # Depends on mode: simple vs. advanced
 
 
 
21
  current_step = None
22
  advanced_mode = False
23
 
@@ -30,8 +33,8 @@ default_settings = {
30
  }
31
 
32
  # Streaming state & statistics
33
- streaming_state = "idle" # "idle", "streaming", "paused", "stopped"
34
- stream_chat_id = None # Chat ID for updates
35
  stream_start_time = None
36
  frames_encoded = 0
37
  bytes_sent = 0
@@ -45,13 +48,13 @@ output_stream = None
45
  stream_thread = None
46
  live_log_thread = None
47
 
48
- # Live logging globals
49
- live_log_lines = collections.deque(maxlen=50) # Efficient log storage
50
- live_log_display = "" # Global variable updated every second by live_log_updater
51
- error_notification = None # Global variable to hold error details if any
52
 
53
  # -------------------------------------------------------------------
54
- # Enhanced Logging Setup
55
  # -------------------------------------------------------------------
56
  logging.basicConfig(
57
  level=logging.DEBUG,
@@ -62,29 +65,30 @@ logger = logging.getLogger()
62
  logger.setLevel(logging.DEBUG)
63
 
64
  def append_live_log(line: str):
65
- """Append a log line to the live log deque."""
66
  live_log_lines.append(line)
67
 
68
  class ListHandler(logging.Handler):
69
- """Custom logging handler to store logs for live display."""
70
  def emit(self, record):
71
  log_entry = self.format(record)
72
  append_live_log(log_entry)
73
 
 
74
  list_handler = ListHandler()
75
  list_handler.setLevel(logging.DEBUG)
76
  list_handler.setFormatter(logging.Formatter("%Y-%m-%d %H:%M:%S [%(levelname)s] %(message)s"))
77
  logger.addHandler(list_handler)
78
 
79
  # -------------------------------------------------------------------
80
- # Utility Functions & UI Helpers
81
  # -------------------------------------------------------------------
82
  def create_html_message(text: str):
83
- """Create an HTML-formatted message (using <pre> for monospaced text)."""
84
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
85
 
86
  def get_inline_keyboard_for_stream():
87
- """Return the inline keyboard for controlling the stream."""
88
  keyboard = {
89
  "inline_keyboard": [
90
  [
@@ -100,7 +104,7 @@ def get_inline_keyboard_for_stream():
100
  return keyboard
101
 
102
  def get_inline_keyboard_for_start():
103
- """Return the inline keyboard with a Start Streaming button."""
104
  keyboard = {
105
  "inline_keyboard": [
106
  [
@@ -111,13 +115,13 @@ def get_inline_keyboard_for_start():
111
  return keyboard
112
 
113
  def help_text():
114
- """Return help text for the bot."""
115
  return (
116
  "*Stream Bot Help*\n\n"
117
  "*/start* - Begin setup for streaming (simple mode: only Input & Output URL)\n"
118
  "*/setting* - Enter advanced settings (Input URL, Quality Settings, Video Codec, Audio Codec, Output URL)\n"
119
  "*/help* - Display this help text\n"
120
- "*/logs* - Show the live log history\n\n"
121
  "After inputs are collected, press the inline *Start Streaming* button.\n\n"
122
  "While streaming, you can use inline buttons or commands:\n"
123
  "*/pause* - Pause the stream\n"
@@ -127,7 +131,7 @@ def help_text():
127
  )
128
 
129
  def send_guide_message(chat_id, message):
130
- """Return a response dictionary to send a guide message."""
131
  logging.info(f"Sending message to chat {chat_id}: {message}")
132
  return {
133
  "method": "sendMessage",
@@ -137,56 +141,58 @@ def send_guide_message(chat_id, message):
137
  }
138
 
139
  def reset_statistics():
140
- """Reset the streaming statistics."""
141
  global stream_start_time, frames_encoded, bytes_sent
142
  stream_start_time = datetime.datetime.now()
143
  frames_encoded = 0
144
  bytes_sent = 0
145
 
146
  def get_uptime():
147
- """Return the current uptime as a string."""
148
  if stream_start_time:
149
  uptime = datetime.datetime.now() - stream_start_time
150
  return str(uptime).split('.')[0]
151
  return "0"
152
 
153
  def validate_inputs():
154
- """Ensure all required fields have been provided."""
155
  missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
156
  if missing:
157
  return False, f"Missing fields: {', '.join(missing)}"
158
  return True, ""
159
 
160
  # -------------------------------------------------------------------
161
- # Error Notification Helper
162
  # -------------------------------------------------------------------
163
  def notify_error(chat_id, error_message):
164
- """Store the error message globally and log it."""
165
  global error_notification
166
  error_notification = error_message
167
- logger.error(f"Error for chat {chat_id}: {error_message}")
168
 
169
  # -------------------------------------------------------------------
170
- # Live Log Updater Thread
171
  # -------------------------------------------------------------------
172
  def live_log_updater():
173
- """Continuously update the global live_log_display variable while streaming is active."""
174
  global live_log_display, streaming_state
175
  try:
176
  while streaming_state in ["streaming", "paused"]:
 
177
  live_log_display = "<pre>" + "\n".join(list(live_log_lines)) + "</pre>"
178
  time.sleep(1)
179
  except Exception as e:
180
- logger.exception("Error in live log updater")
181
 
182
  # -------------------------------------------------------------------
183
- # Logs History Handler (/logs)
184
  # -------------------------------------------------------------------
185
  def logs_history(chat_id):
186
- """Return the live log display (including error notification if any) as HTML."""
187
  global live_log_display, error_notification
188
  log_text = live_log_display if live_log_display else "<pre>No logs available yet.</pre>"
189
  if error_notification:
 
190
  if log_text.startswith("<pre>"):
191
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
192
  else:
@@ -199,21 +205,24 @@ def logs_history(chat_id):
199
  }
200
 
201
  # -------------------------------------------------------------------
202
- # Conversation Handlers
203
  # -------------------------------------------------------------------
204
  def handle_start(chat_id):
205
  global current_step, user_inputs, conversation_fields, advanced_mode
 
206
  user_inputs = {}
207
  if not advanced_mode:
208
  conversation_fields = ["input_url", "output_url"]
209
  else:
210
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
211
  current_step = conversation_fields[0]
212
- text = ("👋 *Welcome to the Stream Bot!*\n\n"
213
- "Let's set up your stream.\n"
214
- f"Please enter the *{current_step.replace('_', ' ')}*"
215
- f"{' (no default)' if current_step not in default_settings else f' _(default: {default_settings[current_step]})_'}:")
216
- logger.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
 
 
217
  return {
218
  "method": "sendMessage",
219
  "chat_id": chat_id,
@@ -227,9 +236,11 @@ def handle_setting(chat_id):
227
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
228
  user_inputs = {}
229
  current_step = conversation_fields[0]
230
- text = ("⚙️ *Advanced Mode Activated!*\n\n"
231
- "Please enter the *input url*:")
232
- logger.info(f"/setting command from chat {chat_id} - advanced mode enabled")
 
 
233
  return {
234
  "method": "sendMessage",
235
  "chat_id": chat_id,
@@ -238,7 +249,7 @@ def handle_setting(chat_id):
238
  }
239
 
240
  def handle_help(chat_id):
241
- logger.info(f"/help command from chat {chat_id}")
242
  return {
243
  "method": "sendMessage",
244
  "chat_id": chat_id,
@@ -251,10 +262,10 @@ def handle_conversation(chat_id, text):
251
  if current_step:
252
  if text.strip() == "" and current_step in default_settings:
253
  user_inputs[current_step] = default_settings[current_step]
254
- logger.info(f"Using default for {current_step}: {default_settings[current_step]}")
255
  else:
256
  user_inputs[current_step] = text.strip()
257
- logger.info(f"Received {current_step}: {text.strip()}")
258
 
259
  idx = conversation_fields.index(current_step)
260
  if idx < len(conversation_fields) - 1:
@@ -283,11 +294,11 @@ def handle_conversation(chat_id, text):
283
  return send_guide_message(chat_id, "Unrecognized input. Type /help for available commands.")
284
 
285
  # -------------------------------------------------------------------
286
- # Background Streaming Functions
287
  # -------------------------------------------------------------------
288
  def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, output_url, chat_id):
289
  global video_stream, audio_stream_in, output_stream, streaming_state, frames_encoded, bytes_sent
290
- logger.info("Initiating streaming to YouTube")
291
  try:
292
  streaming_state = "streaming"
293
  reset_statistics()
@@ -319,15 +330,16 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
319
 
320
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
321
 
322
- logger.info("Streaming started successfully.")
323
 
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, daemon=True)
328
  live_log_thread.start()
329
- logger.info("Live log updater thread started.")
330
 
 
331
  while streaming_state in ["streaming", "paused"]:
332
  for packet in input_stream.demux():
333
  if streaming_state == "stopped":
@@ -352,6 +364,7 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
352
  if hasattr(out_packet, "size"):
353
  bytes_sent += out_packet.size
354
 
 
355
  for out_packet in video_stream.encode():
356
  output_stream.mux(out_packet)
357
  for out_packet in out_audio_stream.encode():
@@ -360,19 +373,20 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
360
  if streaming_state == "paused":
361
  time.sleep(1)
362
 
 
363
  try:
364
  video_stream.close()
365
  out_audio_stream.close()
366
  output_stream.close()
367
  input_stream.close()
368
  except Exception as cleanup_error:
369
- logger.error(f"Error during cleanup: {cleanup_error}")
370
 
371
- logger.info("Streaming complete, resources cleaned up.")
372
  streaming_state = "idle"
373
  except Exception as e:
374
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
375
- logger.error(error_message)
376
  streaming_state = "idle"
377
  notify_error(chat_id, error_message)
378
 
@@ -393,11 +407,12 @@ def start_streaming(chat_id):
393
  user_inputs["audio_codec"],
394
  user_inputs["output_url"],
395
  chat_id,
396
- ),
397
- daemon=True
398
  )
 
399
  stream_thread.start()
400
- logger.info("Streaming thread started.")
 
401
  return {
402
  "method": "sendMessage",
403
  "chat_id": chat_id,
@@ -407,18 +422,18 @@ def start_streaming(chat_id):
407
  }
408
  except Exception as e:
409
  error_message = f"Failed to start streaming: {str(e)}"
410
- logger.error(error_message)
411
  notify_error(chat_id, error_message)
412
  return send_guide_message(chat_id, error_message)
413
 
414
  # -------------------------------------------------------------------
415
- # Stream Control Handlers
416
  # -------------------------------------------------------------------
417
  def pause_stream(chat_id):
418
  global streaming_state
419
  if streaming_state == "streaming":
420
  streaming_state = "paused"
421
- logger.info("Streaming paused.")
422
  return {
423
  "method": "sendMessage",
424
  "chat_id": chat_id,
@@ -431,7 +446,7 @@ def resume_stream(chat_id):
431
  global streaming_state
432
  if streaming_state == "paused":
433
  streaming_state = "streaming"
434
- logger.info("Streaming resumed.")
435
  return {
436
  "method": "sendMessage",
437
  "chat_id": chat_id,
@@ -444,7 +459,7 @@ def abort_stream(chat_id):
444
  global streaming_state
445
  if streaming_state in ["streaming", "paused"]:
446
  streaming_state = "stopped"
447
- logger.info("Streaming aborted by user.")
448
  return {
449
  "method": "sendMessage",
450
  "chat_id": chat_id,
@@ -469,12 +484,12 @@ def stream_status(chat_id):
469
  }
470
 
471
  # -------------------------------------------------------------------
472
- # FastAPI Webhook Endpoint for Telegram Updates
473
  # -------------------------------------------------------------------
474
  @app.post("/webhook")
475
  async def telegram_webhook(request: Request):
476
  update = await request.json()
477
- logger.debug(f"Received update: {update}")
478
 
479
  # Process messages from users
480
  if "message" in update:
 
12
  app = FastAPI()
13
 
14
  # -------------------------------------------------------------------
15
+ # CONFIGURATION & GLOBAL VARIABLES
16
  # -------------------------------------------------------------------
17
+ # (No token or outgoing HTTP calls are used in this version.)
18
+ # Conversation state
19
  user_inputs = {}
20
+ # The conversation fields will depend on the mode.
21
+ # Simple mode (default): Only "input_url" and "output_url" are required.
22
+ # Advanced mode (if user sends /setting): Additional fields are required.
23
+ conversation_fields = []
24
  current_step = None
25
  advanced_mode = False
26
 
 
33
  }
34
 
35
  # Streaming state & statistics
36
+ streaming_state = "idle" # Possible values: "idle", "streaming", "paused", "stopped"
37
+ stream_chat_id = None # Chat ID for periodic updates
38
  stream_start_time = None
39
  frames_encoded = 0
40
  bytes_sent = 0
 
48
  stream_thread = None
49
  live_log_thread = None
50
 
51
+ # Live logging globals using collections.deque for efficiency
52
+ live_log_lines = collections.deque(maxlen=50) # Efficient log storage (max 50 lines)
53
+ live_log_display = "" # Updated by the live log updater thread
54
+ error_notification = "" # Holds error details if any error occurs
55
 
56
  # -------------------------------------------------------------------
57
+ # ENHANCED LOGGING SETUP
58
  # -------------------------------------------------------------------
59
  logging.basicConfig(
60
  level=logging.DEBUG,
 
65
  logger.setLevel(logging.DEBUG)
66
 
67
  def append_live_log(line: str):
68
+ """ Append a log line to the global deque. """
69
  live_log_lines.append(line)
70
 
71
  class ListHandler(logging.Handler):
72
+ """ Custom logging handler to store logs in the deque for live display. """
73
  def emit(self, record):
74
  log_entry = self.format(record)
75
  append_live_log(log_entry)
76
 
77
+ # Attach custom logging handler
78
  list_handler = ListHandler()
79
  list_handler.setLevel(logging.DEBUG)
80
  list_handler.setFormatter(logging.Formatter("%Y-%m-%d %H:%M:%S [%(levelname)s] %(message)s"))
81
  logger.addHandler(list_handler)
82
 
83
  # -------------------------------------------------------------------
84
+ # UTILITY FUNCTIONS & UI HELPERS
85
  # -------------------------------------------------------------------
86
  def create_html_message(text: str):
87
+ """ Returns a message dictionary with HTML formatting. """
88
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
89
 
90
  def get_inline_keyboard_for_stream():
91
+ """ Returns the inline keyboard for stream controls. """
92
  keyboard = {
93
  "inline_keyboard": [
94
  [
 
104
  return keyboard
105
 
106
  def get_inline_keyboard_for_start():
107
+ """ Returns the inline keyboard with a Start Streaming button. """
108
  keyboard = {
109
  "inline_keyboard": [
110
  [
 
115
  return keyboard
116
 
117
  def help_text():
118
+ """ Returns the help text message. """
119
  return (
120
  "*Stream Bot Help*\n\n"
121
  "*/start* - Begin setup for streaming (simple mode: only Input & Output URL)\n"
122
  "*/setting* - Enter advanced settings (Input URL, Quality Settings, Video Codec, Audio Codec, Output URL)\n"
123
  "*/help* - Display this help text\n"
124
+ "*/logs* - Show the live log display\n\n"
125
  "After inputs are collected, press the inline *Start Streaming* button.\n\n"
126
  "While streaming, you can use inline buttons or commands:\n"
127
  "*/pause* - Pause the stream\n"
 
131
  )
132
 
133
  def send_guide_message(chat_id, message):
134
+ """ Returns a response dictionary for sending a guide message. """
135
  logging.info(f"Sending message to chat {chat_id}: {message}")
136
  return {
137
  "method": "sendMessage",
 
141
  }
142
 
143
  def reset_statistics():
144
+ """ Resets streaming statistics. """
145
  global stream_start_time, frames_encoded, bytes_sent
146
  stream_start_time = datetime.datetime.now()
147
  frames_encoded = 0
148
  bytes_sent = 0
149
 
150
  def get_uptime():
151
+ """ Returns the uptime as a formatted string. """
152
  if stream_start_time:
153
  uptime = datetime.datetime.now() - stream_start_time
154
  return str(uptime).split('.')[0]
155
  return "0"
156
 
157
  def validate_inputs():
158
+ """ Validates that all required conversation fields have been provided. """
159
  missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
160
  if missing:
161
  return False, f"Missing fields: {', '.join(missing)}"
162
  return True, ""
163
 
164
  # -------------------------------------------------------------------
165
+ # ERROR NOTIFICATION VIA GLOBAL VARIABLE
166
  # -------------------------------------------------------------------
167
  def notify_error(chat_id, error_message):
168
+ """ Stores the error message in a global variable and logs it. """
169
  global error_notification
170
  error_notification = error_message
171
+ logging.error(f"Error for chat {chat_id}: {error_message}")
172
 
173
  # -------------------------------------------------------------------
174
+ # LIVE LOG UPDATER THREAD
175
  # -------------------------------------------------------------------
176
  def live_log_updater():
177
+ """ Continuously updates the global live_log_display with the latest log lines. """
178
  global live_log_display, streaming_state
179
  try:
180
  while streaming_state in ["streaming", "paused"]:
181
+ # Update with all lines from the deque, joined by newlines
182
  live_log_display = "<pre>" + "\n".join(list(live_log_lines)) + "</pre>"
183
  time.sleep(1)
184
  except Exception as e:
185
+ logging.exception("Error in live log updater")
186
 
187
  # -------------------------------------------------------------------
188
+ # LOGS HISTORY HANDLER (/logs)
189
  # -------------------------------------------------------------------
190
  def logs_history(chat_id):
191
+ """ Returns the current live log display, prepending any error notification if present. """
192
  global live_log_display, error_notification
193
  log_text = live_log_display if live_log_display else "<pre>No logs available yet.</pre>"
194
  if error_notification:
195
+ # Prepend the error message to the log display
196
  if log_text.startswith("<pre>"):
197
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
198
  else:
 
205
  }
206
 
207
  # -------------------------------------------------------------------
208
+ # CONVERSATION HANDLERS
209
  # -------------------------------------------------------------------
210
  def handle_start(chat_id):
211
  global current_step, user_inputs, conversation_fields, advanced_mode
212
+ # Default to simple mode unless /setting has been issued
213
  user_inputs = {}
214
  if not advanced_mode:
215
  conversation_fields = ["input_url", "output_url"]
216
  else:
217
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
218
  current_step = conversation_fields[0]
219
+ text = (
220
+ "👋 *Welcome to the Stream Bot!*\n\n"
221
+ "Let's set up your stream.\n"
222
+ f"Please enter the *{current_step.replace('_', ' ')}*"
223
+ f"{' (no default)' if current_step not in default_settings else f' _(default: {default_settings[current_step]})_'}:"
224
+ )
225
+ logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
226
  return {
227
  "method": "sendMessage",
228
  "chat_id": chat_id,
 
236
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
237
  user_inputs = {}
238
  current_step = conversation_fields[0]
239
+ text = (
240
+ "⚙️ *Advanced Mode Activated!*\n\n"
241
+ "Please enter the *input url*:"
242
+ )
243
+ logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
244
  return {
245
  "method": "sendMessage",
246
  "chat_id": chat_id,
 
249
  }
250
 
251
  def handle_help(chat_id):
252
+ logging.info(f"/help command from chat {chat_id}")
253
  return {
254
  "method": "sendMessage",
255
  "chat_id": chat_id,
 
262
  if current_step:
263
  if text.strip() == "" and current_step in default_settings:
264
  user_inputs[current_step] = default_settings[current_step]
265
+ logging.info(f"Using default for {current_step}: {default_settings[current_step]}")
266
  else:
267
  user_inputs[current_step] = text.strip()
268
+ logging.info(f"Received {current_step}: {text.strip()}")
269
 
270
  idx = conversation_fields.index(current_step)
271
  if idx < len(conversation_fields) - 1:
 
294
  return send_guide_message(chat_id, "Unrecognized input. Type /help for available commands.")
295
 
296
  # -------------------------------------------------------------------
297
+ # BACKGROUND STREAMING FUNCTIONS
298
  # -------------------------------------------------------------------
299
  def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, output_url, chat_id):
300
  global video_stream, audio_stream_in, output_stream, streaming_state, frames_encoded, bytes_sent
301
+ logging.info("Initiating streaming to YouTube")
302
  try:
303
  streaming_state = "streaming"
304
  reset_statistics()
 
330
 
331
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
332
 
333
+ logging.info("Streaming started successfully.")
334
 
335
  # Start live log updater thread if not already running
336
  global live_log_thread
337
  if live_log_thread is None or not live_log_thread.is_alive():
338
  live_log_thread = threading.Thread(target=live_log_updater, daemon=True)
339
  live_log_thread.start()
340
+ logging.info("Live log updater thread started.")
341
 
342
+ # Main streaming loop
343
  while streaming_state in ["streaming", "paused"]:
344
  for packet in input_stream.demux():
345
  if streaming_state == "stopped":
 
364
  if hasattr(out_packet, "size"):
365
  bytes_sent += out_packet.size
366
 
367
+ # Flush any remaining packets
368
  for out_packet in video_stream.encode():
369
  output_stream.mux(out_packet)
370
  for out_packet in out_audio_stream.encode():
 
373
  if streaming_state == "paused":
374
  time.sleep(1)
375
 
376
+ # Clean up resources
377
  try:
378
  video_stream.close()
379
  out_audio_stream.close()
380
  output_stream.close()
381
  input_stream.close()
382
  except Exception as cleanup_error:
383
+ logging.error(f"Error during cleanup: {cleanup_error}")
384
 
385
+ logging.info("Streaming complete, resources cleaned up.")
386
  streaming_state = "idle"
387
  except Exception as e:
388
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
389
+ logging.error(error_message)
390
  streaming_state = "idle"
391
  notify_error(chat_id, error_message)
392
 
 
407
  user_inputs["audio_codec"],
408
  user_inputs["output_url"],
409
  chat_id,
410
+ )
 
411
  )
412
+ stream_thread.daemon = True
413
  stream_thread.start()
414
+ logging.info("Streaming thread started.")
415
+
416
  return {
417
  "method": "sendMessage",
418
  "chat_id": chat_id,
 
422
  }
423
  except Exception as e:
424
  error_message = f"Failed to start streaming: {str(e)}"
425
+ logging.error(error_message)
426
  notify_error(chat_id, error_message)
427
  return send_guide_message(chat_id, error_message)
428
 
429
  # -------------------------------------------------------------------
430
+ # STREAM CONTROL HANDLERS
431
  # -------------------------------------------------------------------
432
  def pause_stream(chat_id):
433
  global streaming_state
434
  if streaming_state == "streaming":
435
  streaming_state = "paused"
436
+ logging.info("Streaming paused.")
437
  return {
438
  "method": "sendMessage",
439
  "chat_id": chat_id,
 
446
  global streaming_state
447
  if streaming_state == "paused":
448
  streaming_state = "streaming"
449
+ logging.info("Streaming resumed.")
450
  return {
451
  "method": "sendMessage",
452
  "chat_id": chat_id,
 
459
  global streaming_state
460
  if streaming_state in ["streaming", "paused"]:
461
  streaming_state = "stopped"
462
+ logging.info("Streaming aborted by user.")
463
  return {
464
  "method": "sendMessage",
465
  "chat_id": chat_id,
 
484
  }
485
 
486
  # -------------------------------------------------------------------
487
+ # FASTAPI WEBHOOK ENDPOINT FOR TELEGRAM UPDATES
488
  # -------------------------------------------------------------------
489
  @app.post("/webhook")
490
  async def telegram_webhook(request: Request):
491
  update = await request.json()
492
+ logging.debug(f"Received update: {update}")
493
 
494
  # Process messages from users
495
  if "message" in update: