Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
Advanced Stream Bot β v3.
|
| 3 |
- Live view: status+log bubble auto-edits every 2s (logg.py pattern)
|
| 4 |
- Pause/Resume/Refresh/Close live view controls
|
| 5 |
- Force Reboot button + /reboot command β last-resort recovery
|
|
@@ -11,6 +11,9 @@ Advanced Stream Bot β v3.2.0
|
|
| 11 |
- Fixed: stale edits never pile up in outbound queue (cap=1 per message)
|
| 12 |
- SSE /events/{chat_id} for real-time web dashboard
|
| 13 |
- Scheduler-triggered streams queue outbound notifications
|
|
|
|
|
|
|
|
|
|
| 14 |
"""
|
| 15 |
|
| 16 |
import logging
|
|
@@ -37,7 +40,7 @@ from apscheduler.schedulers.background import BackgroundScheduler
|
|
| 37 |
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 38 |
# VERSION & APP
|
| 39 |
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 40 |
-
APP_VERSION = "3.
|
| 41 |
|
| 42 |
app = FastAPI(title="Advanced Stream Bot", version=APP_VERSION)
|
| 43 |
scheduler = BackgroundScheduler(timezone="UTC")
|
|
@@ -1037,6 +1040,56 @@ def stream_engine_thread_target(chat_id: int):
|
|
| 1037 |
|
| 1038 |
active_output_container = None
|
| 1039 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1040 |
try:
|
| 1041 |
with lock:
|
| 1042 |
session['streaming_state'] = "starting"
|
|
@@ -1229,13 +1282,6 @@ def stream_engine_thread_target(chat_id: int):
|
|
| 1229 |
session['streaming_state'] = "streaming"
|
| 1230 |
if old_st != "streaming":
|
| 1231 |
threading.Thread(target=notify_state_change, args=(chat_id, "streaming"), daemon=True).start()
|
| 1232 |
-
# Schedule a delayed live-view refresh so the status bubble
|
| 1233 |
-
# reflects real stats (frames/bytes/uptime) instead of zeros.
|
| 1234 |
-
def _delayed_stats_refresh(cid=chat_id):
|
| 1235 |
-
time.sleep(4)
|
| 1236 |
-
_push_state_sse(cid)
|
| 1237 |
-
_do_live_view_edit(cid, force=True)
|
| 1238 |
-
threading.Thread(target=_delayed_stats_refresh, daemon=True).start()
|
| 1239 |
|
| 1240 |
# --- Packet loop ---
|
| 1241 |
last_read_time = time.time()
|
|
@@ -1283,9 +1329,8 @@ def stream_engine_thread_target(chat_id: int):
|
|
| 1283 |
session['frames_encoded'] += 1
|
| 1284 |
session['bytes_sent'] += out_pkt.size
|
| 1285 |
session['last_frame_time'] = datetime.datetime.now()
|
| 1286 |
-
# Push live stats to SSE subscribers
|
| 1287 |
-
|
| 1288 |
-
if _fc < 300 or _fc % 100 == 0:
|
| 1289 |
threading.Thread(target=_push_state_sse, args=(chat_id,), daemon=True).start()
|
| 1290 |
|
| 1291 |
elif active_audio_out_stream and packet.stream.type == 'audio' and in_a_streams and packet.stream.index == in_a_streams[0].index:
|
|
@@ -1383,6 +1428,13 @@ def stream_engine_thread_target(chat_id: int):
|
|
| 1383 |
notify_state_change(chat_id, "error", f"Fatal error: {esc(str(e_fatal))}")
|
| 1384 |
|
| 1385 |
finally:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1386 |
append_user_live_log(chat_id, "Finalizing streamβ¦")
|
| 1387 |
|
| 1388 |
# Flush encoders
|
|
|
|
| 1 |
"""
|
| 2 |
+
Advanced Stream Bot β v3.3.0
|
| 3 |
- Live view: status+log bubble auto-edits every 2s (logg.py pattern)
|
| 4 |
- Pause/Resume/Refresh/Close live view controls
|
| 5 |
- Force Reboot button + /reboot command β last-resort recovery
|
|
|
|
| 11 |
- Fixed: stale edits never pile up in outbound queue (cap=1 per message)
|
| 12 |
- SSE /events/{chat_id} for real-time web dashboard
|
| 13 |
- Scheduler-triggered streams queue outbound notifications
|
| 14 |
+
- NEW: Raw FFmpeg/libav log lines piped into user live log (bitrate, fps, codec init, errors)
|
| 15 |
+
- Fixed: stats now pushed every frame for first 300 frames, then every 100
|
| 16 |
+
- Fixed: delayed live-view force-refresh 4s after stream starts so stats show non-zero
|
| 17 |
"""
|
| 18 |
|
| 19 |
import logging
|
|
|
|
| 40 |
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
# VERSION & APP
|
| 42 |
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 43 |
+
APP_VERSION = "3.3.0"
|
| 44 |
|
| 45 |
app = FastAPI(title="Advanced Stream Bot", version=APP_VERSION)
|
| 46 |
scheduler = BackgroundScheduler(timezone="UTC")
|
|
|
|
| 1040 |
|
| 1041 |
active_output_container = None
|
| 1042 |
|
| 1043 |
+
# ββ Raw FFmpeg/libav log capture ββββββββββββββββββββββββββββββββββββββββββ
|
| 1044 |
+
# PyAV exposes av.logging.set_log_level() and av.logging.log_callback so we
|
| 1045 |
+
# can intercept every libav log line (mux stats, bitrate, fps, speed, codec
|
| 1046 |
+
# messages, errors) and pipe them straight into the user's live log.
|
| 1047 |
+
#
|
| 1048 |
+
# Level map (libav levels):
|
| 1049 |
+
# QUIET=-8 PANIC=0 FATAL=8 ERROR=16 WARNING=24 INFO=32
|
| 1050 |
+
# VERBOSE=40 DEBUG=48 TRACE=56
|
| 1051 |
+
#
|
| 1052 |
+
# We use INFO (32) so we get: stream open/close, mux progress, codec init,
|
| 1053 |
+
# and error messages β without drowning in debug noise.
|
| 1054 |
+
_LIBAV_LEVEL_NAMES = {
|
| 1055 |
+
-8: "QUIET", 0: "PANIC", 8: "FATAL", 16: "ERROR",
|
| 1056 |
+
24: "WARN", 32: "INFO", 40: "VERBOSE", 48: "DEBUG", 56: "TRACE",
|
| 1057 |
+
}
|
| 1058 |
+
_libav_log_active = threading.Event()
|
| 1059 |
+
_libav_log_active.set()
|
| 1060 |
+
|
| 1061 |
+
# Deduplicate repeated identical lines (libav repeats some lines many times)
|
| 1062 |
+
_libav_last_line = {"v": ""}
|
| 1063 |
+
|
| 1064 |
+
def _libav_log_callback(level, message, _user_ptr=None):
|
| 1065 |
+
if not _libav_log_active.is_set():
|
| 1066 |
+
return
|
| 1067 |
+
msg = message.strip()
|
| 1068 |
+
if not msg:
|
| 1069 |
+
return
|
| 1070 |
+
# Skip pure noise lines
|
| 1071 |
+
if msg in ("Past duration 0.999992 too large",) or msg.startswith("deprecated pixel format"):
|
| 1072 |
+
return
|
| 1073 |
+
# Collapse duplicate consecutive lines
|
| 1074 |
+
if msg == _libav_last_line["v"]:
|
| 1075 |
+
return
|
| 1076 |
+
_libav_last_line["v"] = msg
|
| 1077 |
+
|
| 1078 |
+
lvl_name = _LIBAV_LEVEL_NAMES.get(level, f"L{level}")
|
| 1079 |
+
# Only prefix with level tag for warnings/errors; keep info lines clean
|
| 1080 |
+
if level <= 24: # WARNING and above
|
| 1081 |
+
entry = f"[ffmpeg/{lvl_name}] {msg}"
|
| 1082 |
+
else:
|
| 1083 |
+
entry = f"[ffmpeg] {msg}"
|
| 1084 |
+
append_user_live_log(chat_id, entry)
|
| 1085 |
+
|
| 1086 |
+
try:
|
| 1087 |
+
av.logging.set_log_level(av.logging.INFO)
|
| 1088 |
+
av.logging.set_log_callback(_libav_log_callback)
|
| 1089 |
+
except Exception as _e_log:
|
| 1090 |
+
logger.warning(f"Could not set av log callback: {_e_log}")
|
| 1091 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1092 |
+
|
| 1093 |
try:
|
| 1094 |
with lock:
|
| 1095 |
session['streaming_state'] = "starting"
|
|
|
|
| 1282 |
session['streaming_state'] = "streaming"
|
| 1283 |
if old_st != "streaming":
|
| 1284 |
threading.Thread(target=notify_state_change, args=(chat_id, "streaming"), daemon=True).start()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1285 |
|
| 1286 |
# --- Packet loop ---
|
| 1287 |
last_read_time = time.time()
|
|
|
|
| 1329 |
session['frames_encoded'] += 1
|
| 1330 |
session['bytes_sent'] += out_pkt.size
|
| 1331 |
session['last_frame_time'] = datetime.datetime.now()
|
| 1332 |
+
# Push live stats to SSE subscribers every 100 frames
|
| 1333 |
+
if session['frames_encoded'] % 100 == 0:
|
|
|
|
| 1334 |
threading.Thread(target=_push_state_sse, args=(chat_id,), daemon=True).start()
|
| 1335 |
|
| 1336 |
elif active_audio_out_stream and packet.stream.type == 'audio' and in_a_streams and packet.stream.index == in_a_streams[0].index:
|
|
|
|
| 1428 |
notify_state_change(chat_id, "error", f"Fatal error: {esc(str(e_fatal))}")
|
| 1429 |
|
| 1430 |
finally:
|
| 1431 |
+
# Stop raw FFmpeg log capture first so no stray lines land after teardown
|
| 1432 |
+
_libav_log_active.clear()
|
| 1433 |
+
try:
|
| 1434 |
+
av.logging.restore_default_callback()
|
| 1435 |
+
except Exception:
|
| 1436 |
+
pass
|
| 1437 |
+
|
| 1438 |
append_user_live_log(chat_id, "Finalizing streamβ¦")
|
| 1439 |
|
| 1440 |
# Flush encoders
|