Update app.py
Browse files
app.py
CHANGED
@@ -16,9 +16,9 @@ app = FastAPI()
|
|
16 |
# (No token or outgoing HTTP calls are used in this version.)
|
17 |
# Conversation state
|
18 |
user_inputs = {}
|
19 |
-
#
|
20 |
-
# Simple mode (default):
|
21 |
-
# Advanced mode (if user sends /setting):
|
22 |
conversation_fields = []
|
23 |
current_step = None
|
24 |
advanced_mode = False
|
@@ -32,7 +32,7 @@ default_settings = {
|
|
32 |
}
|
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
|
@@ -48,9 +48,8 @@ stream_thread = None
|
|
48 |
live_log_thread = None
|
49 |
|
50 |
# Live logging globals
|
51 |
-
live_log_lines = [] #
|
52 |
-
|
53 |
-
error_notification = "" # Global variable to hold error details if any
|
54 |
|
55 |
# -------------------------------------------------------------------
|
56 |
# Enhanced Logging Setup
|
@@ -81,11 +80,11 @@ logger.addHandler(list_handler)
|
|
81 |
# Utility Functions & UI Helpers
|
82 |
# -------------------------------------------------------------------
|
83 |
def create_html_message(text: str):
|
84 |
-
#
|
85 |
return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
|
86 |
|
87 |
def get_inline_keyboard_for_stream():
|
88 |
-
# Inline keyboard for
|
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
|
105 |
keyboard = {
|
106 |
"inline_keyboard": [
|
107 |
[
|
@@ -127,7 +126,7 @@ def help_text():
|
|
127 |
)
|
128 |
|
129 |
def send_guide_message(chat_id, message):
|
130 |
-
#
|
131 |
logging.info(f"Sending message to chat {chat_id}: {message}")
|
132 |
return {
|
133 |
"method": "sendMessage",
|
@@ -137,7 +136,7 @@ def send_guide_message(chat_id, message):
|
|
137 |
}
|
138 |
|
139 |
def send_guide_message_html(chat_id, message):
|
140 |
-
#
|
141 |
logging.info(f"Sending HTML message to chat {chat_id}: {message}")
|
142 |
return {
|
143 |
"method": "sendMessage",
|
@@ -159,15 +158,14 @@ def get_uptime():
|
|
159 |
return "0"
|
160 |
|
161 |
def validate_inputs():
|
162 |
-
#
|
163 |
missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
|
164 |
if missing:
|
165 |
return False, f"Missing fields: {', '.join(missing)}"
|
166 |
return True, ""
|
167 |
|
168 |
def get_streaming_display_message(prefix=""):
|
169 |
-
# Build an HTML message
|
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"
|
@@ -175,7 +173,14 @@ def get_streaming_display_message(prefix=""):
|
|
175 |
f"Frames Encoded: {frames_encoded}\n"
|
176 |
f"Bytes Sent: {bytes_sent}\n"
|
177 |
)
|
178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
return f"{prefix}\n{status}\n\nLive Logs:\n{logs}"
|
180 |
|
181 |
# -------------------------------------------------------------------
|
@@ -190,11 +195,11 @@ def notify_error(chat_id, error_message):
|
|
190 |
# Live Log Updater (Background Thread)
|
191 |
# -------------------------------------------------------------------
|
192 |
def live_log_updater():
|
193 |
-
global
|
194 |
try:
|
195 |
-
while
|
196 |
-
#
|
197 |
-
|
198 |
time.sleep(1)
|
199 |
except Exception as e:
|
200 |
logging.error(f"Error in live log updater: {e}")
|
@@ -203,10 +208,13 @@ def live_log_updater():
|
|
203 |
# Logs History Handler (/logs)
|
204 |
# -------------------------------------------------------------------
|
205 |
def logs_history(chat_id):
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
|
|
|
|
|
|
210 |
if error_notification:
|
211 |
if log_text.startswith("<pre>"):
|
212 |
log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
|
@@ -224,7 +232,7 @@ def logs_history(chat_id):
|
|
224 |
# -------------------------------------------------------------------
|
225 |
def handle_start(chat_id):
|
226 |
global current_step, user_inputs, conversation_fields, advanced_mode
|
227 |
-
# Use simple mode by default (unless advanced_mode
|
228 |
user_inputs = {}
|
229 |
if not advanced_mode:
|
230 |
conversation_fields = ["input_url", "output_url"]
|
@@ -337,14 +345,14 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
|
|
337 |
out_audio_stream.layout = "stereo"
|
338 |
video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
|
339 |
logging.info("Streaming started successfully.")
|
340 |
-
# Start live log updater if not already running
|
341 |
global live_log_thread
|
342 |
if live_log_thread is None or not live_log_thread.is_alive():
|
343 |
live_log_thread = threading.Thread(target=live_log_updater)
|
344 |
live_log_thread.daemon = True
|
345 |
live_log_thread.start()
|
346 |
logging.info("Live log updater thread started.")
|
347 |
-
#
|
348 |
while streaming_state in ["streaming", "paused"]:
|
349 |
for packet in input_stream.demux():
|
350 |
if streaming_state == "stopped":
|
@@ -484,8 +492,6 @@ def stream_status(chat_id):
|
|
484 |
async def telegram_webhook(request: Request):
|
485 |
update = await request.json()
|
486 |
logging.debug(f"Received update: {update}")
|
487 |
-
|
488 |
-
# Process messages from users
|
489 |
if "message" in update:
|
490 |
chat_id = update["message"]["chat"]["id"]
|
491 |
text = update["message"].get("text", "").strip()
|
@@ -507,13 +513,10 @@ async def telegram_webhook(request: Request):
|
|
507 |
return stream_status(chat_id)
|
508 |
else:
|
509 |
return handle_conversation(chat_id, text)
|
510 |
-
|
511 |
-
# Process inline keyboard callback queries
|
512 |
elif "callback_query" in update:
|
513 |
callback_data = update["callback_query"]["data"]
|
514 |
chat_id = update["callback_query"]["message"]["chat"]["id"]
|
515 |
message_id = update["callback_query"]["message"]["message_id"]
|
516 |
-
|
517 |
if callback_data == "pause":
|
518 |
response = pause_stream(chat_id)
|
519 |
elif callback_data == "resume":
|
@@ -526,10 +529,8 @@ async def telegram_webhook(request: Request):
|
|
526 |
response = start_streaming(chat_id)
|
527 |
else:
|
528 |
response = send_guide_message_html(chat_id, "❓ Unknown callback command.")
|
529 |
-
# Update the original message while preserving inline keyboard
|
530 |
response["method"] = "editMessageText"
|
531 |
response["message_id"] = message_id
|
532 |
response["reply_markup"] = get_inline_keyboard_for_stream()
|
533 |
return response
|
534 |
-
|
535 |
return {"status": "ok"}
|
|
|
16 |
# (No token or outgoing HTTP calls are used in this version.)
|
17 |
# Conversation state
|
18 |
user_inputs = {}
|
19 |
+
# Conversation fields 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
|
|
|
32 |
}
|
33 |
|
34 |
# Streaming state & statistics
|
35 |
+
streaming_state = "idle" # Can be "idle", "streaming", "paused", or "stopped"
|
36 |
stream_chat_id = None # Chat ID for periodic updates
|
37 |
stream_start_time = None
|
38 |
frames_encoded = 0
|
|
|
48 |
live_log_thread = None
|
49 |
|
50 |
# Live logging globals
|
51 |
+
live_log_lines = [] # A rolling list of log lines (max 50 lines)
|
52 |
+
error_notification = "" # Holds error details (if any)
|
|
|
53 |
|
54 |
# -------------------------------------------------------------------
|
55 |
# Enhanced Logging Setup
|
|
|
80 |
# Utility Functions & UI Helpers
|
81 |
# -------------------------------------------------------------------
|
82 |
def create_html_message(text: str):
|
83 |
+
# Return an HTML-formatted message wrapped in <pre> tags for monospaced output.
|
84 |
return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
|
85 |
|
86 |
def get_inline_keyboard_for_stream():
|
87 |
+
# Inline keyboard for stream controls; this keyboard is always returned along with status messages.
|
88 |
keyboard = {
|
89 |
"inline_keyboard": [
|
90 |
[
|
|
|
100 |
return keyboard
|
101 |
|
102 |
def get_inline_keyboard_for_start():
|
103 |
+
# Inline keyboard with a "Start Streaming" button.
|
104 |
keyboard = {
|
105 |
"inline_keyboard": [
|
106 |
[
|
|
|
126 |
)
|
127 |
|
128 |
def send_guide_message(chat_id, message):
|
129 |
+
# Return a response dictionary (Markdown format) for webhook replies.
|
130 |
logging.info(f"Sending message to chat {chat_id}: {message}")
|
131 |
return {
|
132 |
"method": "sendMessage",
|
|
|
136 |
}
|
137 |
|
138 |
def send_guide_message_html(chat_id, message):
|
139 |
+
# Return a response dictionary (HTML format) for webhook replies.
|
140 |
logging.info(f"Sending HTML message to chat {chat_id}: {message}")
|
141 |
return {
|
142 |
"method": "sendMessage",
|
|
|
158 |
return "0"
|
159 |
|
160 |
def validate_inputs():
|
161 |
+
# Check that all required fields have been provided.
|
162 |
missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
|
163 |
if missing:
|
164 |
return False, f"Missing fields: {', '.join(missing)}"
|
165 |
return True, ""
|
166 |
|
167 |
def get_streaming_display_message(prefix=""):
|
168 |
+
# Build an HTML message that combines an optional prefix, current streaming status, and the last 15 log lines.
|
|
|
169 |
status = (
|
170 |
f"<b>Stream Status:</b>\n"
|
171 |
f"State: {streaming_state}\n"
|
|
|
173 |
f"Frames Encoded: {frames_encoded}\n"
|
174 |
f"Bytes Sent: {bytes_sent}\n"
|
175 |
)
|
176 |
+
if live_log_lines:
|
177 |
+
logs = "<pre>" + "\n".join(live_log_lines[-15:]) + "</pre>"
|
178 |
+
else:
|
179 |
+
logs = "<pre>No logs available yet.</pre>"
|
180 |
+
# Prepend any error notification if it exists.
|
181 |
+
global error_notification
|
182 |
+
if error_notification:
|
183 |
+
logs = f"<pre>ERROR: {error_notification}\n\n" + logs[5:]
|
184 |
return f"{prefix}\n{status}\n\nLive Logs:\n{logs}"
|
185 |
|
186 |
# -------------------------------------------------------------------
|
|
|
195 |
# Live Log Updater (Background Thread)
|
196 |
# -------------------------------------------------------------------
|
197 |
def live_log_updater():
|
198 |
+
# This thread continuously (every 1 second) updates the global log display.
|
199 |
try:
|
200 |
+
while True:
|
201 |
+
# (We no longer store the result in a separate variable;
|
202 |
+
# get_streaming_display_message computes the logs directly from live_log_lines.)
|
203 |
time.sleep(1)
|
204 |
except Exception as e:
|
205 |
logging.error(f"Error in live log updater: {e}")
|
|
|
208 |
# Logs History Handler (/logs)
|
209 |
# -------------------------------------------------------------------
|
210 |
def logs_history(chat_id):
|
211 |
+
# Return a response containing the last 50 log lines (if any) in HTML format.
|
212 |
+
if live_log_lines:
|
213 |
+
log_text = "<pre>" + "\n".join(live_log_lines[-50:]) + "</pre>"
|
214 |
+
else:
|
215 |
+
log_text = "<pre>No logs available yet.</pre>"
|
216 |
+
# Prepend error notification if present.
|
217 |
+
global error_notification
|
218 |
if error_notification:
|
219 |
if log_text.startswith("<pre>"):
|
220 |
log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
|
|
|
232 |
# -------------------------------------------------------------------
|
233 |
def handle_start(chat_id):
|
234 |
global current_step, user_inputs, conversation_fields, advanced_mode
|
235 |
+
# Use simple mode by default (unless advanced_mode has been enabled)
|
236 |
user_inputs = {}
|
237 |
if not advanced_mode:
|
238 |
conversation_fields = ["input_url", "output_url"]
|
|
|
345 |
out_audio_stream.layout = "stereo"
|
346 |
video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
|
347 |
logging.info("Streaming started successfully.")
|
348 |
+
# Start live log updater (if not already running)
|
349 |
global live_log_thread
|
350 |
if live_log_thread is None or not live_log_thread.is_alive():
|
351 |
live_log_thread = threading.Thread(target=live_log_updater)
|
352 |
live_log_thread.daemon = True
|
353 |
live_log_thread.start()
|
354 |
logging.info("Live log updater thread started.")
|
355 |
+
# Streaming loop
|
356 |
while streaming_state in ["streaming", "paused"]:
|
357 |
for packet in input_stream.demux():
|
358 |
if streaming_state == "stopped":
|
|
|
492 |
async def telegram_webhook(request: Request):
|
493 |
update = await request.json()
|
494 |
logging.debug(f"Received update: {update}")
|
|
|
|
|
495 |
if "message" in update:
|
496 |
chat_id = update["message"]["chat"]["id"]
|
497 |
text = update["message"].get("text", "").strip()
|
|
|
513 |
return stream_status(chat_id)
|
514 |
else:
|
515 |
return handle_conversation(chat_id, text)
|
|
|
|
|
516 |
elif "callback_query" in update:
|
517 |
callback_data = update["callback_query"]["data"]
|
518 |
chat_id = update["callback_query"]["message"]["chat"]["id"]
|
519 |
message_id = update["callback_query"]["message"]["message_id"]
|
|
|
520 |
if callback_data == "pause":
|
521 |
response = pause_stream(chat_id)
|
522 |
elif callback_data == "resume":
|
|
|
529 |
response = start_streaming(chat_id)
|
530 |
else:
|
531 |
response = send_guide_message_html(chat_id, "❓ Unknown callback command.")
|
|
|
532 |
response["method"] = "editMessageText"
|
533 |
response["message_id"] = message_id
|
534 |
response["reply_markup"] = get_inline_keyboard_for_stream()
|
535 |
return response
|
|
|
536 |
return {"status": "ok"}
|