Tst / app.py
Mroo9's picture
Update app.py
90256d0 verified
Raw History Blame Contribute Delete
13 kB
import os
import time
import subprocess
import threading
import json
import pickle
import psutil
from datetime import datetime, timezone
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from flask import Flask
# ------------------------------------------------------------
# الإعدادات الأساسية
# ------------------------------------------------------------
TIKTOK_USERNAME = "@a3dam_111"
CUSTOM_STREAM_KEY = os.getenv("CUSTOM_STREAM_KEY", "p3dq-5cex-bc1k-vet7-bsyx")
FFMPEG_LOG = "/tmp/ffmpeg.log"
MONITOR_LOG = "/tmp/monitor.log"
LOCK_FILE = "/tmp/app_monitor_core.lock"
TOKEN_FILE = "/tmp/yt_token.pkl"
BROADCAST_FILE = "/tmp/current_broadcast.json"
TIKTOK_COOKIES = os.getenv("TIKTOK_COOKIES", "")
TIKTOK_COOKIES_ADULT = os.getenv("TIKTOK_COOKIES_ADULT", "")
def log(msg):
ts = time.strftime("%Y-%m-%d %H:%M:%S")
line = f"[{ts}] {msg}"
print(line, flush=True)
with open(MONITOR_LOG, "a", encoding="utf-8") as f:
f.write(line + "\n")
# ------------------------------------------------------------
# حفظ الكوكيز
# ------------------------------------------------------------
def save_cookies():
if TIKTOK_COOKIES:
with open("/tmp/tiktok_cookies.txt", "w") as f:
f.write(TIKTOK_COOKIES)
if TIKTOK_COOKIES_ADULT:
with open("/tmp/tiktok_cookies_adult.txt", "w") as f:
f.write(TIKTOK_COOKIES_ADULT)
save_cookies()
# ------------------------------------------------------------
# أدوات مساعدة
# ------------------------------------------------------------
def kill_process_tree(pid):
try:
parent = psutil.Process(pid)
for child in parent.children(recursive=True):
child.kill()
parent.kill()
except:
pass
def get_tiktok_stream_url():
try:
url = f"https://www.tiktok.com/{TIKTOK_USERNAME}/live"
cmd = ["streamlink", "--stream-url", url, "best", "--retry-streams", "5", "--retry-open", "3"]
if os.path.exists("/tmp/tiktok_cookies_adult.txt"):
cmd.extend(["--cookies", "/tmp/tiktok_cookies_adult.txt"])
elif os.path.exists("/tmp/tiktok_cookies.txt"):
cmd.extend(["--cookies", "/tmp/tiktok_cookies.txt"])
result = subprocess.run(cmd, capture_output=True, text=True, timeout=25)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip().split("\n")[0]
except: pass
try:
url = f"https://www.tiktok.com/{TIKTOK_USERNAME}/live"
cmd = ["yt-dlp", "--no-warnings", "--no-check-certificates", "--no-live-from-start", "--no-cache-dir", "--rm-cache-dir", "-f", "best", "--get-url", url]
if os.path.exists("/tmp/tiktok_cookies_adult.txt"):
cmd.extend(["--cookies", "/tmp/tiktok_cookies_adult.txt"])
elif os.path.exists("/tmp/tiktok_cookies.txt"):
cmd.extend(["--cookies", "/tmp/tiktok_cookies.txt"])
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip().split("\n")[0]
except: pass
return None
def start_stream(stream_key, stream_url):
if not stream_key or not stream_url: return None
log("🎬 بدء البث (تحويل الفيديو H.264 + فلاتر صوتية + تخطي الحظر)...")
rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{stream_key}"
try: subprocess.run(["killall", "-9", "ffmpeg", "yt-dlp", "streamlink"], stderr=subprocess.DEVNULL)
except: pass
cmd = [
"nice", "-n", "19", "ffmpeg", "-hide_banner", "-loglevel", "warning",
"-thread_queue_size", "2048",
"-user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"-headers", "Referer: https://www.tiktok.com/\r\n",
"-fflags", "+genpts+discardcorrupt+igndts", # إصلاح التزامن
"-flags", "+global_header",
"-reconnect", "1",
"-reconnect_at_eof", "1",
"-reconnect_delay_max", "60",
"-i", stream_url,
"-c:v", "libx264", "-preset", "ultrafast", "-b:v", "2500k",
"-maxrate", "2500k", "-bufsize", "5000k",
"-pix_fmt", "yuv420p",
"-g", "60",
"-c:a", "aac", "-b:a", "192k", "-ar", "44100",
"-profile:a", "aac_low",
"-af", "aresample=async=1000:min_hard_comp=0.100000:first_pts=0,highpass=f=50,lowpass=f=15000,dynaudnorm=f=150:g=15:m=25:p=0.95",
"-max_muxing_queue_size", "2048",
"-f", "flv", rtmp_url
]
with open(FFMPEG_LOG, "w") as stderr_file:
proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=stderr_file, preexec_fn=os.setsid)
log(f"✅ ffmpeg PID: {proc.pid}")
return proc
# ------------------------------------------------------------
# YouTube API
# ------------------------------------------------------------
def build_youtube_client():
try:
if os.path.exists(TOKEN_FILE):
with open(TOKEN_FILE, "rb") as f: creds = pickle.load(f)
if creds and creds.valid: return build('youtube', 'v3', credentials=creds)
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
with open(TOKEN_FILE, "wb") as f: pickle.dump(creds, f)
return build('youtube', 'v3', credentials=creds)
creds_json = os.getenv("YT_CREDENTIALS_JSON")
if not creds_json: return None
data = json.loads(creds_json)
creds = Credentials(token=data.get("token"), refresh_token=data.get("refresh_token"), token_uri=data.get("token_uri"), client_id=data.get("client_id"), client_secret=data.get("client_secret"), scopes=data.get("scopes", ["https://www.googleapis.com/auth/youtube.force-ssl"]))
if creds.expired and creds.refresh_token: creds.refresh(Request())
with open(TOKEN_FILE, "wb") as f: pickle.dump(creds, f)
return build('youtube', 'v3', credentials=creds)
except Exception as e:
log(f"❌ فشل بناء عميل YouTube: {e}")
return None
def create_new_broadcast():
try:
yt = build_youtube_client()
if not yt: return None, None
now = datetime.now(timezone.utc)
date_str = now.strftime('%d-%m-%Y %H:%M')
title = f"🔥 بث تيك توك أعدام المباشر | {date_str} 🚀"
description = (
"أهلاً بكم في البث المباشر المخصص لـ أعدام.\n"
"استمتعوا معنا بأجواء البث وتفاعلوا في التعليقات!\n\n"
"🔔 لا تنسوا الاشتراك في القناة وتفعيل زر التنبيهات ليصلكم كل جديد فور انطلاق البث.\n"
"شكراً لدعمكم المتواصل!\n\n"
"#أعدام #بث_مباشر #تيك_توك #TikTokLive #الاردن #بث #مباشر #a3dam_111"
)
insert_body = {
"snippet": {
"title": title,
"description": description,
"scheduledStartTime": now.isoformat()
},
"status": {"privacyStatus": "public"},
"contentDetails": {"enableAutoStart": True, "enableAutoStop": False, "latencyPreference": "normal"}
}
new_broadcast = yt.liveBroadcasts().insert(part="snippet,status,contentDetails", body=insert_body).execute()
bid = new_broadcast["id"]
log(f"✅ بث جديد: {bid} | {title}")
stream_insert = yt.liveStreams().insert(
part="snippet,cdn,contentDetails",
body={"snippet": {"title": "TikTok Stream Key"}, "cdn": {"ingestionType": "rtmp", "resolution": "1080p", "frameRate": "30fps"}, "contentDetails": {"isReusable": True}}
).execute()
stream_id = stream_insert["id"]
stream_key = stream_insert["cdn"]["ingestionInfo"]["streamName"]
yt.liveBroadcasts().bind(id=bid, part="id", streamId=stream_id).execute()
with open(BROADCAST_FILE, "w") as f:
json.dump({"broadcast_id": bid, "stream_key": stream_key}, f)
return stream_key, bid
except Exception as e:
log(f"❌ فشل إنشاء بث جديد: {e}")
return None, None
# ✅ دالة جديدة: تنظيف البث السابق لنفس المساحة فقط
def cleanup_own_previous_broadcast():
"""تنهي بثاً سابقاً لهذه المساحة فقط (إن وُجد وما زال مفتوحاً)"""
if not os.path.exists(BROADCAST_FILE):
return
try:
with open(BROADCAST_FILE, "r") as f:
data = json.load(f)
bid = data.get("broadcast_id")
if not bid:
return
yt = build_youtube_client()
if not yt:
return
# جلب حالة البث
resp = yt.liveBroadcasts().list(id=bid, part="status").execute()
items = resp.get("items", [])
if not items:
os.remove(BROADCAST_FILE)
return
status = items[0]["status"]["lifeCycleStatus"]
if status in ["live", "ready", "created", "testing"]:
log(f"🧹 إنهاء بث سابق لهذه المساحة: {bid}")
yt.liveBroadcasts().transition(broadcastStatus="complete", id=bid, part="status").execute()
# نحذف الملف على أية حال لأننا سننشئ بثاً جديداً
os.remove(BROADCAST_FILE)
except Exception as e:
log(f"⚠️ تعذر تنظيف البث السابق: {e}")
if os.path.exists(BROADCAST_FILE):
os.remove(BROADCAST_FILE)
def finalize_current_broadcast(broadcast_id):
if not broadcast_id: return
try:
yt = build_youtube_client()
if not yt: return
yt.liveBroadcasts().transition(broadcastStatus="complete", id=broadcast_id, part="status").execute()
log(f"🛑 تم إنهاء البث: {broadcast_id}")
except Exception as e:
log(f"⚠️ فشل إنهاء البث: {e}")
if os.path.exists(BROADCAST_FILE):
os.remove(BROADCAST_FILE)
# ------------------------------------------------------------
# المحرك الرئيسي
# ------------------------------------------------------------
def monitor_loop():
try: subprocess.run(["killall", "-9", "ffmpeg", "yt-dlp", "streamlink"], stderr=subprocess.DEVNULL)
except: pass
time.sleep(2)
if os.path.exists(LOCK_FILE): return
with open(LOCK_FILE, "w") as f: f.write(str(os.getpid()))
# ✅ تنظيف بث هذه المساحة فقط (وليس كل البثوث)
cleanup_own_previous_broadcast()
ffmpeg_proc = None
was_streaming = False
consecutive_fails = 0
current_stream_key = None
current_broadcast_id = None
while True:
try:
stream_url = get_tiktok_stream_url()
if stream_url:
consecutive_fails = 0
if was_streaming and ffmpeg_proc and ffmpeg_proc.poll() is None:
pass
elif was_streaming and ffmpeg_proc and ffmpeg_proc.poll() is not None:
kill_process_tree(ffmpeg_proc.pid)
ffmpeg_proc = start_stream(current_stream_key, stream_url)
else:
if not current_broadcast_id:
# لا ننهي كل البثوث، فقط ننشئ بثًا جديدًا (المساحة الأخرى مستقلة)
stream_key, bid = create_new_broadcast()
if not stream_key:
stream_key = CUSTOM_STREAM_KEY
current_broadcast_id = bid
current_stream_key = stream_key
ffmpeg_proc = start_stream(current_stream_key, stream_url)
was_streaming = True
else:
if was_streaming:
consecutive_fails += 1
if consecutive_fails >= 6:
if ffmpeg_proc: kill_process_tree(ffmpeg_proc.pid)
ffmpeg_proc = None
was_streaming = False
if current_broadcast_id:
finalize_current_broadcast(current_broadcast_id)
current_broadcast_id = None
current_stream_key = None
time.sleep(30)
except Exception as e:
log(f"❌ خطأ في حلقة المراقبة: {e}")
time.sleep(30)
app = Flask(__name__)
@app.route("/")
def index(): return "OK"
if __name__ == "__main__":
t = threading.Thread(target=monitor_loop, daemon=True)
t.start()
app.run(host="0.0.0.0", port=7860, debug=False)