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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -65
app.py CHANGED
@@ -4,6 +4,7 @@ import time
4
  import datetime
5
  import traceback
6
  import fractions
 
7
 
8
  from fastapi import FastAPI, Request
9
  import av
@@ -13,13 +14,10 @@ app = FastAPI()
13
  # -------------------------------------------------------------------
14
  # Configuration & Global Variables
15
  # -------------------------------------------------------------------
16
- # (No token or outgoing HTTP calls are used in this version.)
17
- # Conversation state
18
  user_inputs = {}
19
- # The conversation fields will depend on the mode.
20
- # Simple mode (default): Only "input_url" and "output_url" are required.
21
- # Advanced mode (if user sends /setting): Additional fields are required.
22
- conversation_fields = []
23
  current_step = None
24
  advanced_mode = False
25
 
@@ -33,7 +31,7 @@ default_settings = {
33
 
34
  # Streaming state & statistics
35
  streaming_state = "idle" # "idle", "streaming", "paused", "stopped"
36
- stream_chat_id = None # Chat ID for periodic updates
37
  stream_start_time = None
38
  frames_encoded = 0
39
  bytes_sent = 0
@@ -48,9 +46,9 @@ stream_thread = None
48
  live_log_thread = None
49
 
50
  # Live logging globals
51
- live_log_lines = [] # Rolling list (max 50 log lines)
52
- live_log_display = "" # Global variable updated every second by live_log_updater
53
- error_notification = "" # Global variable to hold error details if any
54
 
55
  # -------------------------------------------------------------------
56
  # Enhanced Logging Setup
@@ -61,31 +59,32 @@ logging.basicConfig(
61
  datefmt="%Y-%m-%d %H:%M:%S",
62
  )
63
  logger = logging.getLogger()
 
64
 
65
  def append_live_log(line: str):
66
- global live_log_lines
67
  live_log_lines.append(line)
68
- if len(live_log_lines) > 50:
69
- live_log_lines.pop(0)
70
 
71
  class ListHandler(logging.Handler):
 
72
  def emit(self, record):
73
  log_entry = self.format(record)
74
  append_live_log(log_entry)
75
 
76
  list_handler = ListHandler()
77
- list_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s", "%Y-%m-%d %H:%M:%S"))
 
78
  logger.addHandler(list_handler)
79
 
80
  # -------------------------------------------------------------------
81
  # Utility Functions & UI Helpers
82
  # -------------------------------------------------------------------
83
  def create_html_message(text: str):
84
- # Wrap text in <pre> tags for monospaced output using HTML parse mode
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,7 +100,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 conversation is complete.
105
  keyboard = {
106
  "inline_keyboard": [
107
  [
@@ -112,12 +111,13 @@ def get_inline_keyboard_for_start():
112
  return keyboard
113
 
114
  def help_text():
 
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 log history (live log display)\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 +127,7 @@ def help_text():
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",
@@ -137,19 +137,21 @@ def send_guide_message(chat_id, message):
137
  }
138
 
139
  def reset_statistics():
 
140
  global stream_start_time, frames_encoded, bytes_sent
141
  stream_start_time = datetime.datetime.now()
142
  frames_encoded = 0
143
  bytes_sent = 0
144
 
145
  def get_uptime():
 
146
  if stream_start_time:
147
  uptime = datetime.datetime.now() - stream_start_time
148
  return str(uptime).split('.')[0]
149
  return "0"
150
 
151
  def validate_inputs():
152
- # Ensure all fields in conversation_fields have been provided
153
  missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
154
  if missing:
155
  return False, f"Missing fields: {', '.join(missing)}"
@@ -159,33 +161,32 @@ def validate_inputs():
159
  # Error Notification Helper
160
  # -------------------------------------------------------------------
161
  def notify_error(chat_id, error_message):
 
162
  global error_notification
163
  error_notification = error_message
164
- logging.error(f"Error for chat {chat_id}: {error_message}")
165
 
166
  # -------------------------------------------------------------------
167
- # Live Log Updater (Background Thread)
168
  # -------------------------------------------------------------------
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
 
179
  # -------------------------------------------------------------------
180
  # Logs History Handler (/logs)
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
- # Remove any opening <pre> tag from log_text and prepend error message
189
  if log_text.startswith("<pre>"):
190
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
191
  else:
@@ -202,7 +203,6 @@ def logs_history(chat_id):
202
  # -------------------------------------------------------------------
203
  def handle_start(chat_id):
204
  global current_step, user_inputs, conversation_fields, advanced_mode
205
- # By default, use simple mode unless advanced_mode is set via /setting
206
  user_inputs = {}
207
  if not advanced_mode:
208
  conversation_fields = ["input_url", "output_url"]
@@ -213,7 +213,7 @@ def handle_start(chat_id):
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
- logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
217
  return {
218
  "method": "sendMessage",
219
  "chat_id": chat_id,
@@ -229,7 +229,7 @@ def handle_setting(chat_id):
229
  current_step = conversation_fields[0]
230
  text = ("⚙️ *Advanced Mode Activated!*\n\n"
231
  "Please enter the *input url*:")
232
- logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
233
  return {
234
  "method": "sendMessage",
235
  "chat_id": chat_id,
@@ -238,7 +238,7 @@ def handle_setting(chat_id):
238
  }
239
 
240
  def handle_help(chat_id):
241
- logging.info(f"/help command from chat {chat_id}")
242
  return {
243
  "method": "sendMessage",
244
  "chat_id": chat_id,
@@ -249,13 +249,12 @@ def handle_help(chat_id):
249
  def handle_conversation(chat_id, text):
250
  global current_step, user_inputs, conversation_fields
251
  if current_step:
252
- # If the response is empty and a default exists, use the default.
253
  if text.strip() == "" and current_step in default_settings:
254
  user_inputs[current_step] = default_settings[current_step]
255
- logging.info(f"Using default for {current_step}: {default_settings[current_step]}")
256
  else:
257
  user_inputs[current_step] = text.strip()
258
- logging.info(f"Received {current_step}: {text.strip()}")
259
 
260
  idx = conversation_fields.index(current_step)
261
  if idx < len(conversation_fields) - 1:
@@ -265,17 +264,14 @@ def handle_conversation(chat_id, text):
265
  prompt += f" _(default: {default_settings[current_step]})_"
266
  return send_guide_message(chat_id, prompt)
267
  else:
268
- # All inputs have been collected.
269
  current_step = None
270
  valid, msg = validate_inputs()
271
  if not valid:
272
  return send_guide_message(chat_id, f"Validation error: {msg}")
273
- # In simple mode, fill in advanced fields with defaults.
274
  if not advanced_mode:
275
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
276
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
277
  user_inputs.setdefault("audio_codec", default_settings["audio_codec"])
278
- # Instead of asking the user to type "start", send an inline button.
279
  return {
280
  "method": "sendMessage",
281
  "chat_id": chat_id,
@@ -291,7 +287,7 @@ def handle_conversation(chat_id, text):
291
  # -------------------------------------------------------------------
292
  def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, output_url, chat_id):
293
  global video_stream, audio_stream_in, output_stream, streaming_state, frames_encoded, bytes_sent
294
- logging.info("Initiating streaming to YouTube")
295
  try:
296
  streaming_state = "streaming"
297
  reset_statistics()
@@ -323,17 +319,15 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
323
 
324
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
325
 
326
- logging.info("Streaming started successfully.")
327
 
328
- # Start the live log updater in a background thread if not already running.
329
  global live_log_thread
330
  if live_log_thread is None or not live_log_thread.is_alive():
331
- live_log_thread = threading.Thread(target=live_log_updater)
332
- live_log_thread.daemon = True
333
  live_log_thread.start()
334
- logging.info("Live log updater thread started.")
335
 
336
- # Stream loop: process packets until state changes
337
  while streaming_state in ["streaming", "paused"]:
338
  for packet in input_stream.demux():
339
  if streaming_state == "stopped":
@@ -358,7 +352,6 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
358
  if hasattr(out_packet, "size"):
359
  bytes_sent += out_packet.size
360
 
361
- # Flush remaining packets
362
  for out_packet in video_stream.encode():
363
  output_stream.mux(out_packet)
364
  for out_packet in out_audio_stream.encode():
@@ -367,22 +360,20 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
367
  if streaming_state == "paused":
368
  time.sleep(1)
369
 
370
- # Clean up resources
371
  try:
372
  video_stream.close()
373
  out_audio_stream.close()
374
  output_stream.close()
375
  input_stream.close()
376
  except Exception as cleanup_error:
377
- logging.error(f"Error during cleanup: {cleanup_error}")
378
 
379
- logging.info("Streaming complete, resources cleaned up.")
380
  streaming_state = "idle"
381
  except Exception as e:
382
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
383
- logging.error(error_message)
384
  streaming_state = "idle"
385
- # Notify the user about the error by storing it in a global variable
386
  notify_error(chat_id, error_message)
387
 
388
  def start_streaming(chat_id):
@@ -393,7 +384,6 @@ def start_streaming(chat_id):
393
 
394
  stream_chat_id = chat_id
395
  try:
396
- # Start the background streaming thread
397
  stream_thread = threading.Thread(
398
  target=stream_to_youtube,
399
  args=(
@@ -403,14 +393,11 @@ def start_streaming(chat_id):
403
  user_inputs["audio_codec"],
404
  user_inputs["output_url"],
405
  chat_id,
406
- )
 
407
  )
408
- stream_thread.daemon = True
409
  stream_thread.start()
410
- logging.info("Streaming thread started.")
411
-
412
- # Immediately inform the user that streaming has started;
413
- # live log updates are available via the global variable and the /logs command.
414
  return {
415
  "method": "sendMessage",
416
  "chat_id": chat_id,
@@ -420,7 +407,7 @@ def start_streaming(chat_id):
420
  }
421
  except Exception as e:
422
  error_message = f"Failed to start streaming: {str(e)}"
423
- logging.error(error_message)
424
  notify_error(chat_id, error_message)
425
  return send_guide_message(chat_id, error_message)
426
 
@@ -431,7 +418,7 @@ def pause_stream(chat_id):
431
  global streaming_state
432
  if streaming_state == "streaming":
433
  streaming_state = "paused"
434
- logging.info("Streaming paused.")
435
  return {
436
  "method": "sendMessage",
437
  "chat_id": chat_id,
@@ -444,7 +431,7 @@ def resume_stream(chat_id):
444
  global streaming_state
445
  if streaming_state == "paused":
446
  streaming_state = "streaming"
447
- logging.info("Streaming resumed.")
448
  return {
449
  "method": "sendMessage",
450
  "chat_id": chat_id,
@@ -457,7 +444,7 @@ def abort_stream(chat_id):
457
  global streaming_state
458
  if streaming_state in ["streaming", "paused"]:
459
  streaming_state = "stopped"
460
- logging.info("Streaming aborted by user.")
461
  return {
462
  "method": "sendMessage",
463
  "chat_id": chat_id,
@@ -487,7 +474,7 @@ def stream_status(chat_id):
487
  @app.post("/webhook")
488
  async def telegram_webhook(request: Request):
489
  update = await request.json()
490
- logging.debug(f"Received update: {update}")
491
 
492
  # Process messages from users
493
  if "message" in update:
@@ -511,7 +498,6 @@ async def telegram_webhook(request: Request):
511
  elif text.startswith("/status"):
512
  return stream_status(chat_id)
513
  else:
514
- # Process conversation setup inputs
515
  return handle_conversation(chat_id, text)
516
 
517
  # Process inline keyboard callback queries
@@ -533,7 +519,6 @@ async def telegram_webhook(request: Request):
533
  else:
534
  response = send_guide_message(chat_id, "❓ Unknown callback command.")
535
 
536
- # Edit the original message with updated information
537
  response["method"] = "editMessageText"
538
  response["message_id"] = message_id
539
  return response
 
4
  import datetime
5
  import traceback
6
  import fractions
7
+ import collections
8
 
9
  from fastapi import FastAPI, Request
10
  import av
 
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
 
 
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
 
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
 
59
  datefmt="%Y-%m-%d %H:%M:%S",
60
  )
61
  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
  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
  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
  )
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
  }
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)}"
 
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:
 
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"]
 
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,
 
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
  }
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,
 
249
  def handle_conversation(chat_id, text):
250
  global current_step, user_inputs, conversation_fields
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:
 
264
  prompt += f" _(default: {default_settings[current_step]})_"
265
  return send_guide_message(chat_id, prompt)
266
  else:
 
267
  current_step = None
268
  valid, msg = validate_inputs()
269
  if not valid:
270
  return send_guide_message(chat_id, f"Validation error: {msg}")
 
271
  if not advanced_mode:
272
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
273
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
274
  user_inputs.setdefault("audio_codec", default_settings["audio_codec"])
 
275
  return {
276
  "method": "sendMessage",
277
  "chat_id": chat_id,
 
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
 
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
  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
  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
 
379
  def start_streaming(chat_id):
 
384
 
385
  stream_chat_id = chat_id
386
  try:
 
387
  stream_thread = threading.Thread(
388
  target=stream_to_youtube,
389
  args=(
 
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
  }
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
 
 
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
  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
  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,
 
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:
 
498
  elif text.startswith("/status"):
499
  return stream_status(chat_id)
500
  else:
 
501
  return handle_conversation(chat_id, text)
502
 
503
  # Process inline keyboard callback queries
 
519
  else:
520
  response = send_guide_message(chat_id, "❓ Unknown callback command.")
521
 
 
522
  response["method"] = "editMessageText"
523
  response["message_id"] = message_id
524
  return response