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" #
|
36 |
stream_chat_id = None # Chat ID for periodic updates
|
37 |
stream_start_time = None
|
38 |
frames_encoded = 0
|
@@ -48,8 +48,9 @@ stream_thread = None
|
|
48 |
live_log_thread = None
|
49 |
|
50 |
# Live logging globals
|
51 |
-
live_log_lines = [] #
|
52 |
-
|
|
|
53 |
|
54 |
# -------------------------------------------------------------------
|
55 |
# Enhanced Logging Setup
|
@@ -80,11 +81,11 @@ logger.addHandler(list_handler)
|
|
80 |
# Utility Functions & UI Helpers
|
81 |
# -------------------------------------------------------------------
|
82 |
def create_html_message(text: str):
|
83 |
-
#
|
84 |
return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
|
85 |
|
86 |
def get_inline_keyboard_for_stream():
|
87 |
-
# Inline keyboard for
|
88 |
keyboard = {
|
89 |
"inline_keyboard": [
|
90 |
[
|
@@ -100,7 +101,7 @@ def get_inline_keyboard_for_stream():
|
|
100 |
return keyboard
|
101 |
|
102 |
def get_inline_keyboard_for_start():
|
103 |
-
# Inline keyboard with a
|
104 |
keyboard = {
|
105 |
"inline_keyboard": [
|
106 |
[
|
@@ -126,7 +127,7 @@ def help_text():
|
|
126 |
)
|
127 |
|
128 |
def send_guide_message(chat_id, message):
|
129 |
-
#
|
130 |
logging.info(f"Sending message to chat {chat_id}: {message}")
|
131 |
return {
|
132 |
"method": "sendMessage",
|
@@ -136,7 +137,7 @@ def send_guide_message(chat_id, message):
|
|
136 |
}
|
137 |
|
138 |
def send_guide_message_html(chat_id, message):
|
139 |
-
#
|
140 |
logging.info(f"Sending HTML message to chat {chat_id}: {message}")
|
141 |
return {
|
142 |
"method": "sendMessage",
|
@@ -158,14 +159,15 @@ def get_uptime():
|
|
158 |
return "0"
|
159 |
|
160 |
def validate_inputs():
|
161 |
-
#
|
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
|
|
|
169 |
status = (
|
170 |
f"<b>Stream Status:</b>\n"
|
171 |
f"State: {streaming_state}\n"
|
@@ -173,14 +175,7 @@ def get_streaming_display_message(prefix=""):
|
|
173 |
f"Frames Encoded: {frames_encoded}\n"
|
174 |
f"Bytes Sent: {bytes_sent}\n"
|
175 |
)
|
176 |
-
if
|
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,12 +190,14 @@ def notify_error(chat_id, error_message):
|
|
195 |
# Live Log Updater (Background Thread)
|
196 |
# -------------------------------------------------------------------
|
197 |
def live_log_updater():
|
198 |
-
|
199 |
try:
|
200 |
while True:
|
201 |
-
|
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}")
|
206 |
|
@@ -208,13 +205,10 @@ def live_log_updater():
|
|
208 |
# Logs History Handler (/logs)
|
209 |
# -------------------------------------------------------------------
|
210 |
def logs_history(chat_id):
|
211 |
-
|
212 |
-
if
|
213 |
-
|
214 |
-
|
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,7 +226,7 @@ def logs_history(chat_id):
|
|
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
|
236 |
user_inputs = {}
|
237 |
if not advanced_mode:
|
238 |
conversation_fields = ["input_url", "output_url"]
|
@@ -345,14 +339,14 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
|
|
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 |
-
#
|
|
|
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":
|
|
|
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
|
|
|
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 |
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
|
|
|
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 that remain visible in the message
|
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 setup is complete.
|
105 |
keyboard = {
|
106 |
"inline_keyboard": [
|
107 |
[
|
|
|
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",
|
|
|
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",
|
|
|
159 |
return "0"
|
160 |
|
161 |
def validate_inputs():
|
162 |
+
# Ensure all fields in conversation_fields have been provided
|
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 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"
|
|
|
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 |
# -------------------------------------------------------------------
|
|
|
190 |
# Live Log Updater (Background Thread)
|
191 |
# -------------------------------------------------------------------
|
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 |
|
|
|
205 |
# Logs History Handler (/logs)
|
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:]
|
|
|
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"]
|
|
|
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)
|
347 |
live_log_thread.daemon = True
|
348 |
live_log_thread.start()
|
349 |
logging.info("Live log updater thread started.")
|
|
|
350 |
while streaming_state in ["streaming", "paused"]:
|
351 |
for packet in input_stream.demux():
|
352 |
if streaming_state == "stopped":
|