Comnto1 / streamlit_app.py
Reoo88's picture
Upload streamlit_app.py
3e2f159 verified
Raw History Blame Contribute Delete
6.14 kB
import os, time, threading, subprocess, requests
import streamlit as st
# ---------- إعدادات ----------
COMMENTS_URL = "https://reoo88-signserver.hf.space/comments"
COMMENTS_FILE = "/tmp/comments.txt"
FONT_PATH = "/usr/share/fonts/opentype/fonts-hosny-amiri/Amiri-Regular.ttf"
MAX_LINES = 6
MAX_LINE_LEN = 50
RECONNECT_MAX = 5
# تهيئة ملف التعليقات
if not os.path.exists(COMMENTS_FILE):
with open(COMMENTS_FILE, "w", encoding="utf-8") as f:
f.write(" ")
text_lock = threading.Lock()
stop_event = threading.Event()
# ---------- تحديث التعليقات ----------
def fetch_comments_once():
try:
r = requests.get(COMMENTS_URL, timeout=5)
if r.status_code != 200:
return
lines = [ln.strip() for ln in r.text.strip().split("\n") if ln.strip()][-MAX_LINES:]
lines = [(ln[:MAX_LINE_LEN] + "...") if len(ln) > MAX_LINE_LEN else ln for ln in lines]
new_txt = "\n".join(lines) if lines else " "
tmp = COMMENTS_FILE + ".tmp"
with text_lock:
with open(tmp, "w", encoding="utf-8") as f:
f.write(new_txt)
os.replace(tmp, COMMENTS_FILE)
print(f"✅ تم تحديث التعليقات ({len(lines)} سطر)")
except Exception as e:
print(f"❌ خطأ بالتعليقات: {e}")
def comments_updater():
while not stop_event.is_set():
fetch_comments_once()
for _ in range(30):
if stop_event.is_set(): return
time.sleep(0.1)
# ---------- بناء أوامر ffmpeg/streamlink ----------
def build_ff_cmd(yt_key: str) -> list:
drawtext = (
f"drawtext="
f"fontfile='{FONT_PATH}':"
f"textfile='{COMMENTS_FILE}':reload=1:"
f"text_shaping=1:"
f"fontcolor=white:fontsize=28:line_spacing=10:"
f"x=15:y=h-th-50:"
f"box=1:boxcolor=black@0.55:boxborderw=12:"
f"shadowcolor=black:shadowx=2:shadowy=2"
)
return [
"ffmpeg", "-hide_banner", "-loglevel", "warning",
"-thread_queue_size", "8192", "-re",
"-i", "pipe:0",
"-vf", f"scale=720:1280,{drawtext}",
"-c:v", "libx264", "-preset", "veryfast", "-tune", "zerolatency",
"-profile:v", "main", "-pix_fmt", "yuv420p",
"-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k",
"-r", "30", "-g", "60", "-keyint_min", "60", "-sc_threshold", "0",
"-c:a", "aac", "-ar", "44100", "-b:a", "128k", "-ac", "2",
"-f", "flv",
"-flvflags", "no_duration_filesize+aac_seq_header_detect",
"-rtmp_live", "live",
f"rtmp://a.rtmp.youtube.com/live2/{yt_key}",
]
def build_sl_cmd(username: str) -> list:
return [
"streamlink", f"https://www.tiktok.com/@{username}/live", "best",
"--hls-live-edge", "3", "--retry-streams", "3", "--retry-max", "5", "--stdout",
]
# ---------- بدء البث ----------
def start_stream(yt_key, username):
stop_event.clear()
threading.Thread(target=comments_updater, daemon=True).start()
time.sleep(2)
for attempt in range(1, RECONNECT_MAX + 1):
if stop_event.is_set(): break
print(f"\n========== محاولة بث #{attempt}/{RECONNECT_MAX} ==========")
sl_process = ffmpeg_process = None
try:
sl_process = subprocess.Popen(
build_sl_cmd(username),
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
)
ffmpeg_process = subprocess.Popen(
build_ff_cmd(yt_key),
stdin=subprocess.PIPE, stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE, bufsize=10**7,
)
def log_ffmpeg():
for line in iter(ffmpeg_process.stderr.readline, b""):
txt = line.decode(errors="ignore").rstrip()
if txt: print(f"[ffmpeg] {txt}")
threading.Thread(target=log_ffmpeg, daemon=True).start()
bytes_streamed, start_ts = 0, time.time()
while not stop_event.is_set():
if ffmpeg_process.poll() is not None: break
data = sl_process.stdout.read(8192)
if not data: break
try:
ffmpeg_process.stdin.write(data)
bytes_streamed += len(data)
except BrokenPipeError:
break
duration = time.time() - start_ts
print(f"[STATS] بُثّ {bytes_streamed/(1024*1024):.1f} MB خلال {duration:.0f}s")
if duration < 5:
print("❌ يوتيوب أغلق الاتصال فوراً — تحقّق من Stream Key.")
break
except Exception as e:
print(f"[ERROR] {e}")
finally:
for p in (ffmpeg_process, sl_process):
if not p: continue
try:
if p.stdin:
try: p.stdin.close()
except Exception: pass
if p.poll() is None:
p.terminate()
try: p.wait(timeout=5)
except subprocess.TimeoutExpired: p.kill()
except Exception: pass
if not stop_event.is_set() and attempt < RECONNECT_MAX:
time.sleep(5)
print("[INFO] انتهت جلسة البث.")
# ---------- واجهة Streamlit ----------
st.set_page_config(page_title="بث تيك توك + تعليقات", page_icon="📹")
st.title("📹 بث تيك توك مع تعليقات عربية مباشرة")
with st.form("stream_form"):
user = st.text_input("معرف تيك توك (بدون @)", "majd.alban")
key = st.text_input("YouTube Stream Key", type="password")
if st.form_submit_button("🚀 ابدأ البث", type="primary"):
if user and key:
threading.Thread(target=start_stream, args=(key, user), daemon=True).start()
st.success("✅ بدأ البث!")
else:
st.error("الرجاء إدخال المعرف ومفتاح البث.")