import os import time import math import json import asyncio import subprocess import uuid import yt_dlp from datetime import datetime, timedelta from threading import Thread from flask import Flask from pyrogram import Client, filters from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery # --- Hugging Face को जगाए रखने के लिए Flask Server --- app = Flask(__name__) @app.route('/') def health_check(): return "Bot is running perfectly!" def run_flask(): app.run(host="0.0.0.0", port=7860) # --- Telegram Bot की डिटेल्स --- BOT_TOKEN = "8728797060:AAH3L0eqApxEKuVvYjKT0JGpgQ3BPlrjbgI" API_ID = 35985614 API_HASH = "6a0df17414daf6935f1f0a71b8af1ee9" bot = Client("LiveBot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN) # --- USER SESSION (Private Channel Download के लिए) --- USER_SESSION = "BQIlGM4AazlRr5P1dhaKtIXeQTq0O5LnNaFB9uBcdjcjZQ95iBGIm3L_NTaA0ncK91ZKIr_4loGx8gw84Z_RL9gQDSZ80QOIq5RrwtlKvsfF-NrJElCGJ5NAL8GMtDceRIQpQO78DfI4PM75h7zylKi3jnDQik2bAUzJ7ylG1VI_hgfKxYZlkK2FXkGaACNgJSzUzc-4_THAlSDLNhpQ6Dv_WlFKQpGR47KdZTMxR81ExV5WjW4-AG1A0tr_B0PUvRVyZrpMLb5AzPMqFmByxeQS3QicQzXGKYlJj9Pm5uYRCcFS5WCqhC2lwISMqklhjIivc7LHko5EjyWELHhAhz2RfmPDQAAAAAHK_kURAA" # 👉 यहाँ अपना Pyrogram String Session डालें user_app = Client("UserAcc", api_id=API_ID, api_hash=API_HASH, session_string=USER_SESSION) # --- ग्लोबल वेरिएबल्स --- ADMIN_ID = 7700628753 GLOBAL_PASSWORD = "jitu@123" PASSWORD_LIMIT = 999999 # डिफ़ॉल्ट लिमिट (Unlimted) PASSWORD_USED_BY = {} # जिन लोगों ने पासवर्ड इस्तेमाल किया है उनका डेटा GLOBAL_LIMIT = 1 user_data = {} admin_state = None # --- मैसेज डिलीट करने के लिए नया डेटाबेस (टेम्परेरी) --- sent_message_map = {} recent_admin_messages =[] GLOBAL_SCHEDULE_TIMES = [] # एडमिन के सेट किये गए प्री-डिफाइंड शेड्यूल्स # --- Data Persistence (बॉट रिस्टार्ट होने पर डेटा सेव रखने के लिए) --- DATA_FILE = "bot_config.json" def save_data(): try: # सिर्फ ऑथेंटिकेटेड यूज़र्स का डेटा सेव करेंगे ताकि फाइल भारी न हो auth_users = {str(uid): info['name'] for uid, info in user_data.items() if info.get('authenticated')} with open(DATA_FILE, "w") as f: json.dump({ "GLOBAL_PASSWORD": GLOBAL_PASSWORD, "PASSWORD_LIMIT": PASSWORD_LIMIT, "GLOBAL_LIMIT": GLOBAL_LIMIT, "PASSWORD_USED_BY": {str(k): v for k, v in PASSWORD_USED_BY.items()}, "authenticated_users": auth_users, "GLOBAL_SCHEDULE_TIMES": GLOBAL_SCHEDULE_TIMES }, f) except Exception as e: pass def load_data(): global GLOBAL_PASSWORD, PASSWORD_LIMIT, GLOBAL_LIMIT, PASSWORD_USED_BY, user_data if os.path.exists(DATA_FILE): try: with open(DATA_FILE, "r") as f: data = json.load(f) GLOBAL_PASSWORD = data.get("GLOBAL_PASSWORD", "jitu@123") PASSWORD_LIMIT = data.get("PASSWORD_LIMIT", 999999) GLOBAL_LIMIT = data.get("GLOBAL_LIMIT", 1) PASSWORD_USED_BY = {int(k): v for k, v in data.get("PASSWORD_USED_BY", {}).items()} GLOBAL_SCHEDULE_TIMES = data.get("GLOBAL_SCHEDULE_TIMES", []) saved_users = data.get("authenticated_users", {}) for uid, name in saved_users.items(): uid = int(uid) if uid not in user_data: user_data[uid] = { 'name': name, 'authenticated': True, 'step': 'PLATFORM', 'platform': None, 'rtmp_url': None, 'loop_hours': 0, 'orientation': 'landscape', 'custom_image_path': None, 'custom_audio_path': None, 'video_url': None, 'schedule_time': None, 'processes':[], 'stream_limit': GLOBAL_LIMIT, 'file_path': None, 'manual_stop': False, 'join_date': get_ist_time(), 'last_online': get_ist_time(), 'total_streams': 0, 'last_stream': "Never" } except Exception as e: pass def get_ist_time(): return datetime.utcnow() + timedelta(hours=5, minutes=30) def format_date(dt): if isinstance(dt, str): return dt return dt.strftime("%d-%b-%Y %I:%M %p") def get_video_duration(file_path): try: result = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", file_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True ) return float(result.stdout.strip()) except: return 0 def update_schedule_dates(): global GLOBAL_SCHEDULE_TIMES now_ist = get_ist_time().replace(tzinfo=None) updated = False new_times = [] for time_str in GLOBAL_SCHEDULE_TIMES: try: dt_obj = datetime.strptime(time_str, "%d/%m/%Y %I:%M %p") while dt_obj <= now_ist: dt_obj += timedelta(days=1) updated = True new_times.append(dt_obj.strftime("%d/%m/%Y %I:%M %p")) except: new_times.append(time_str) if updated: unique_times = list(dict.fromkeys(new_times)) GLOBAL_SCHEDULE_TIMES.clear() GLOBAL_SCHEDULE_TIMES.extend(unique_times) save_data() async def notify_admin(client, text): try: await client.send_message(ADMIN_ID, text) except: pass def init_user(user_id, message=None): if user_id not in user_data: name = message.from_user.first_name if message else "Unknown" user_data[user_id] = { 'name': name, 'authenticated': False if user_id != ADMIN_ID else True, 'step': 'PASSWORD' if user_id != ADMIN_ID else 'PLATFORM', 'platform': None, 'rtmp_url': None, 'loop_hours': 0, 'orientation': 'landscape', 'custom_image_path': None, # नया: कस्टम फोटो सेव करने के लिए 'custom_audio_path': None, # ऑडियो रिप्लेस 'watermark_text': None, # <--- नया: मूविंग वाटरमार्क के लिए 'video_url': None, # डायरेक्ट लिंक 'schedule_time': None, # शेड्यूलिंग 'processes':[], 'scheduled_tasks': {}, # <--- नया: शेड्यूल्ड टास्क को ट्रैक करने के लिए 'stream_limit': GLOBAL_LIMIT, 'file_path': None, 'manual_stop': False, 'join_date': get_ist_time(), 'last_online': get_ist_time(), 'total_streams': 0, 'last_stream': "Never" } if user_id != ADMIN_ID: asyncio.create_task(notify_admin(bot, f"🔔 **New User Joined!**\n👤 **Name:** {name}\n🆔 **ID:** `{user_id}`\n📅 **Date:** {format_date(get_ist_time())}")) else: user_data[user_id]['last_online'] = get_ist_time() if message: user_data[user_id]['name'] = message.from_user.first_name def platform_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("🔴 YouTube Live", callback_data="plat_youtube")],[InlineKeyboardButton("🟢 Rumble Live", callback_data="plat_rumble")],[InlineKeyboardButton("🔵 Custom/Other", callback_data="plat_custom")]]) def loop_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("▶️ 1 Time (No Loop)", callback_data="loop_0")],[InlineKeyboardButton("⏳ 30 Mins", callback_data="loop_0.5"), InlineKeyboardButton("⏳ 1 Hour", callback_data="loop_1")],[InlineKeyboardButton("🔁 3 Hours", callback_data="loop_3"), InlineKeyboardButton("🔁 6 Hours", callback_data="loop_6")],[InlineKeyboardButton("🔁 12 Hours", callback_data="loop_12"), InlineKeyboardButton("🔁 24 Hours", callback_data="loop_24")] ]) def orientation_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("🖥 Landscape (16:9)", callback_data="ori_landscape")],[InlineKeyboardButton("📱 Portrait / Shorts (9:16)", callback_data="ori_portrait")]]) def image_choice_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("✅ Yes (Add Custom Photo)", callback_data="img_yes")],[InlineKeyboardButton("❌ No (Normal Black Bg)", callback_data="img_no")]]) def audio_choice_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("🎵 Replace Audio (Send File)", callback_data="aud_yes")],[InlineKeyboardButton("🔊 Keep Original Audio", callback_data="aud_no")]]) def watermark_choice_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("✅ Yes (Moving Watermark)", callback_data="wm_yes")],[InlineKeyboardButton("❌ No Watermark", callback_data="wm_no")]]) def schedule_choice_keyboard(): return InlineKeyboardMarkup([[InlineKeyboardButton("▶️ Start Now", callback_data="sch_now")],[InlineKeyboardButton("⏳ Schedule Time", callback_data="sch_later")]]) def admin_keyboard(): return InlineKeyboardMarkup([ [InlineKeyboardButton("👥 Users List", callback_data="admin_users"), InlineKeyboardButton("🔴 Live Status", callback_data="admin_live")], [InlineKeyboardButton("🔑 Change Password", callback_data="admin_pass"), InlineKeyboardButton("⚙️ Set Limit", callback_data="admin_limit")], [InlineKeyboardButton("📅 Manage Schedule Times", callback_data="admin_sch_times")], [InlineKeyboardButton("📢 Broadcast / Send Msg", callback_data="admin_msg")], [InlineKeyboardButton("🗑 Delete Last 5 Broadcasts", callback_data="admin_del")] ]) # --- Live Progress Bar --- async def progress_bar(current, total, msg, start_time, last_update): now = time.time() if now - last_update[0] > 3 or current == total: last_update[0] = now diff = now - start_time percentage = current * 100 / total speed = current / diff if diff > 0 else 0 eta = round((total - current) / speed) if speed > 0 else 0 current_mb = round(current / (1024 * 1024), 2) total_mb = round(total / (1024 * 1024), 2) speed_mb = round(speed / (1024 * 1024), 2) progress_str = "[{0}{1}]".format(''.join(["█" for _ in range(math.floor(percentage / 5))]), ''.join(["░" for _ in range(20 - math.floor(percentage / 5))])) eta_str = f"{eta} sec" if eta < 60 else f"{eta//60} min {eta%60} sec" text = f"📥 **Downloading Video...**\n\n{progress_str} **{round(percentage, 2)}%**\n📦 **Size:** `{current_mb} MB / {total_mb} MB`\n🚀 **Speed:** `{speed_mb} MB/s`\n⏳ **ETA:** `{eta_str}`" try: await msg.edit_text(text) except: pass @bot.on_message(filters.command("admin")) async def admin_cmd(client, message): if message.chat.id == ADMIN_ID: global admin_state admin_state = None await message.reply("👑 **Welcome to Admin Panel**\nChoose an option below:", reply_markup=admin_keyboard()) @bot.on_message(filters.command("start")) async def start_cmd(client, message): user_id = message.chat.id init_user(user_id, message) if user_data[user_id]['authenticated']: user_data[user_id]['step'] = 'PLATFORM' await message.reply("**✅ Logged in!**\n👇 **Select platform:**", reply_markup=platform_keyboard()) else: user_data[user_id]['step'] = 'PASSWORD' await message.reply("👋 **Welcome!**\n🔐 **Enter Password to continue:**") @bot.on_message(filters.command("new")) async def new_cmd(client, message): user_id = message.chat.id init_user(user_id, message) if not user_data[user_id]['authenticated']: user_data[user_id]['step'] = 'PASSWORD' return await message.reply("🔐 **Session Expired! Please enter the new password:**") user_data[user_id]['step'] = 'PLATFORM' # नई स्ट्रीम शुरू करते समय पुराना डेटा मिटा दें user_data[user_id]['custom_image_path'] = None user_data[user_id]['custom_audio_path'] = None user_data[user_id]['watermark_text'] = None user_data[user_id]['video_url'] = None user_data[user_id]['file_path'] = None await message.reply("🔄 **New Stream Setup!**\n👇 **Select platform:**", reply_markup=platform_keyboard()) @bot.on_message(filters.command("stop")) async def stop_cmd(client, message): user_id = message.chat.id if user_id not in user_data: return active_procs = [] for p in user_data[user_id].get('processes',[]): proc_obj = p['process'] if isinstance(p, dict) else p if proc_obj.poll() is None: active_procs.append(proc_obj) if not active_procs: return await message.reply("⚠️ **No active stream found.**") user_data[user_id]['manual_stop'] = True for p in active_procs: p.terminate() user_data[user_id]['processes'] =[] await message.reply(f"🛑 **Stopping {len(active_procs)} Active Stream(s)... Generating Report!**") @bot.on_callback_query(filters.regex("^admin_")) async def admin_callbacks(client, callback_query: CallbackQuery): if callback_query.message.chat.id != ADMIN_ID: return data = callback_query.data global admin_state global recent_admin_messages if data == "admin_users": total_users = len(user_data) text = f"👥 **Total Users:** `{total_users}`\n\n" for uid, info in user_data.items(): if uid == ADMIN_ID: continue text += f"👤 **Name:** {info['name']}\n🆔 **ID:** `{uid}`\n📅 **Joined:** {format_date(info['join_date'])}\n🕒 **Last Online:** {format_date(info['last_online'])}\n🎬 **Streams Done:** {info['total_streams']}\n⏳ **Last Stream:** {format_date(info['last_stream'])}\n" text += "➖➖➖➖➖➖➖➖\n" if len(text) > 4000: for x in range(0, len(text), 4000): await client.send_message(ADMIN_ID, text[x:x+4000]) else: await callback_query.message.reply(text) elif data == "admin_live": live_text = "" total_live = 0 for uid, info in user_data.items(): active_streams =[] for p in info.get('processes', []): if isinstance(p, dict) and p['process'].poll() is None: active_streams.append(p) elif not isinstance(p, dict) and p.poll() is None: # Old format safety active_streams.append({'process': p, 'start_time': get_ist_time(), 'end_time': "Unknown", 'loop_hours': 0, 'is_image': False}) if active_streams: total_live += len(active_streams) live_text += f"👤 **Name:** {info['name']} (ID: `{uid}`)\n🌐 **Platform:** `{info.get('platform', 'Unknown')}`\n" for i, strm in enumerate(active_streams, 1): mode_str = f"{strm['loop_hours']}h Loop" if strm['loop_hours'] > 0 else "1 Time" if strm.get('is_image'): mode_str += " (🖼 Image)" else: mode_str += " (🎥 Video)" st_str = strm['start_time'].strftime("%I:%M %p") et_str = strm['end_time'].strftime("%I:%M %p") if isinstance(strm['end_time'], datetime) else str(strm['end_time']) live_text += f" ➤ **Stream {i}:** Mode: `{mode_str}`\n" live_text += f" 🟢 Start: `{st_str}` | 🔴 End: `{et_str}`\n" live_text += "➖➖➖➖➖➖\n" if not live_text: await callback_query.message.reply("❌ **No one is streaming right now.**") else: await callback_query.message.reply(f"🔴 **Live Streams ({total_live} total):**\n\n" + live_text) elif data == "admin_pass": markup = InlineKeyboardMarkup([ [InlineKeyboardButton("🔑 Change Password", callback_data="admin_pass_change")],[InlineKeyboardButton("📊 Pass Analysis", callback_data="admin_pass_analysis")] ]) await callback_query.message.edit_text("🔐 **Password Management**\nChoose an option below:", reply_markup=markup) elif data == "admin_pass_change": admin_state = "AWAIT_NEW_PASSWORD" await callback_query.message.reply("🔑 **Please send the NEW PASSWORD and LIMIT.**\n`उदाहरण: 5550 5` (इसका मतलब सिर्फ 5 लोग पासवर्ड यूज़ कर पाएंगे)") elif data == "admin_pass_analysis": limit_text = str(PASSWORD_LIMIT) if PASSWORD_LIMIT != 999999 else "Unlimited" if not PASSWORD_USED_BY: await callback_query.message.reply(f"📊 **Pass Analysis:**\n🔑 **Current Password:** `{GLOBAL_PASSWORD}`\n👥 **Limit:** `0 / {limit_text}`\n\nअभी तक किसी ने भी मौजूदा पासवर्ड का इस्तेमाल नहीं किया है।") else: text = f"📊 **Pass Analysis:**\n🔑 **Current Password:** `{GLOBAL_PASSWORD}`\n👥 **Limit:** `{len(PASSWORD_USED_BY)} / {limit_text}`\n\n" for uid, uname in PASSWORD_USED_BY.items(): text += f"👤 **Name:** {uname}\n🆔 **ID:** `{uid}`\n➖➖➖➖➖➖\n" await callback_query.message.reply(text) elif data == "admin_limit": admin_state = "AWAIT_LIMIT" await callback_query.message.reply("⚙️ **Set Stream Limit Mode:**\n\n👉 **सबका लिमिट बदलने के लिए:** सिर्फ नंबर भेजें (e.g. `2`)\n👉 **किसी एक यूज़र का लिमिट बदलने के लिए:** ID और नंबर भेजें\n`उदाहरण: 384884848 2`") elif data == "admin_msg": admin_state = "AWAIT_BROADCAST" await callback_query.message.reply("📢 **Broadcast / Personal Message Mode!**\n\n" "👉 **सबको भेजने के लिए:** कोई भी Text, Photo या Video सीधा भेजें।\n" "👉 **किसी एक को भेजने के लिए:** ID स्पेस मेसेज लिखें।\n" "`उदाहरण: 8383838487 Hello kese ho`") elif data == "admin_sch_times": update_schedule_dates() markup = [] for st in GLOBAL_SCHEDULE_TIMES: markup.append([InlineKeyboardButton(f"❌ Delete {st}", callback_data=f"admindelscht_{st}")]) markup.append([InlineKeyboardButton("➕ Add New Time", callback_data="admin_sch_add")]) markup.append([InlineKeyboardButton("🔙 Back", callback_data="admin_back")]) await callback_query.message.edit_text("📅 **Manage Pre-defined Schedule Times:**\nये समय यूज़र्स को बटन के रूप में दिखेंगे। (पुराने समय अगले दिन पर शिफ्ट हो जाएंगे 🔄)", reply_markup=InlineKeyboardMarkup(markup)) elif data == "admin_back": await callback_query.message.edit_text("👑 **Welcome to Admin Panel**\nChoose an option below:", reply_markup=admin_keyboard()) elif data == "admin_sch_add": admin_state = "AWAIT_SCH_TIME" await callback_query.message.reply("📅 **कृपया नया शेड्यूल्ड समय भेजें:**\n\n**Format:** `DD/MM/YYYY hh:mm AM/PM`\n**Example:** `24/06/2026 05:50 AM`") elif data.startswith("admindelscht_"): time_to_del = data.replace("admindelscht_", "") if time_to_del in GLOBAL_SCHEDULE_TIMES: GLOBAL_SCHEDULE_TIMES.remove(time_to_del) save_data() await admin_callbacks(client, callback_query._client.callback_query(id=callback_query.id, from_user=callback_query.from_user, message=callback_query.message, data="admin_sch_times")) elif data == "admin_del": if not recent_admin_messages: return await callback_query.message.reply("❌ **No recent messages/broadcasts found to delete.**") deleted_ops = 0 total_msgs = 0 for _ in range(5): if not recent_admin_messages: break targets = recent_admin_messages.pop() deleted_ops += 1 for uid, mid in targets: try: await client.delete_messages(uid, mid) total_msgs += 1 except: pass await callback_query.message.reply(f"✅ **Deleted last {deleted_ops} broadcast(s)/message(s) from {total_msgs} users' chats!**") @bot.on_callback_query(filters.regex("^plat_")) async def select_platform(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id init_user(user_id) if not user_data[user_id].get('authenticated'): return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**") active_procs = [p for p in user_data[user_id].get('processes', []) if (p['process'].poll() if isinstance(p, dict) else p.poll()) is None] user_data[user_id]['processes'] = active_procs limit = user_data[user_id].get('stream_limit', GLOBAL_LIMIT) if len(active_procs) >= limit: return await callback_query.message.edit_text(f"⚠️ **Limit Reached!**\nआप एक साथ सिर्फ {limit} स्ट्रीम चला सकते हैं। कृपया पहली स्ट्रीम खत्म होने का इंतज़ार करें।") data = callback_query.data if data == "plat_youtube": user_data[user_id]['platform'] = 'YouTube' text = "🔴 **YouTube Selected!**\n🔑 **Send YouTube Stream Key:**" elif data == "plat_rumble": user_data[user_id]['platform'] = 'Rumble' text = "🟢 **Rumble Selected!**\n🔑 **Send Rumble Stream Key (STATIC KEY):**" elif data == "plat_custom": user_data[user_id]['platform'] = 'Custom' text = "🔵 **Custom RTMP Selected!**\n🔗 **Send FULL RTMP URL + Key:**" user_data[user_id]['step'] = 'STREAM_KEY' if data != "plat_custom" else 'CUSTOM_URL' await callback_query.message.edit_text(text) @bot.on_callback_query(filters.regex("^loop_")) async def select_loop(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id init_user(user_id) if not user_data[user_id].get('authenticated'): return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**") # Changed int() to float() to support 0.5 hours (30 Mins) hours = float(callback_query.data.split("_")[1]) user_data[user_id]['loop_hours'] = hours user_data[user_id]['step'] = 'ORIENTATION' await callback_query.message.edit_text("📐 **Select Stream Orientation:**\nवीडियो कैसे चलाना है?", reply_markup=orientation_keyboard()) @bot.on_callback_query(filters.regex("^ori_")) async def select_orientation(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id init_user(user_id) if not user_data[user_id].get('authenticated'): return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**") data = callback_query.data if data == "ori_landscape": user_data[user_id]['orientation'] = 'landscape' user_data[user_id]['step'] = 'VIDEO_OR_URL' text = "🖥 **Landscape (16:9) Selected!**\n\n📥 **अब कोई Video File भेजें या डायरेक्ट Link (YouTube/Insta/FB) भेजें:**" await callback_query.message.edit_text(text) else: user_data[user_id]['orientation'] = 'portrait' user_data[user_id]['step'] = 'WAIT_IMAGE_CHOICE' text = "📱 **Portrait/Shorts (9:16) Selected!**\n\n🖼 **क्या आप वीडियो के ऊपर/नीचे खाली जगह पर कोई Custom Photo (Background) लगाना चाहते हैं?**" await callback_query.message.edit_text(text, reply_markup=image_choice_keyboard()) @bot.on_callback_query(filters.regex("^img_")) async def select_image_choice(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id init_user(user_id) if not user_data[user_id].get('authenticated'): return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**") data = callback_query.data if data == "img_yes": user_data[user_id]['step'] = 'AWAIT_IMAGE' text = "🖼 **Send your Custom Background Image (Photo)!**\n(यह फोटो आपके वीडियो के पीछे (Background) में दिखेगी)" else: user_data[user_id]['step'] = 'VIDEO_OR_URL' text = "⬛️ **No Image Selected (Black Background).**\n\n📥 **अब कोई Video File भेजें या डायरेक्ट Link (YouTube/Insta/FB) भेजें:**" await callback_query.message.edit_text(text) @bot.on_callback_query(filters.regex("^aud_")) async def select_audio_choice(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id data = callback_query.data if data == "aud_yes": user_data[user_id]['step'] = 'AWAIT_AUDIO' await callback_query.message.edit_text("🎵 **अपना Audio (MP3) या Video File भेजें जिससे आवाज़ निकालनी है!**") else: user_data[user_id]['step'] = 'SCHEDULE' await callback_query.message.edit_text("⏳ **स्ट्रीम अभी चालू करनी है या Schedule (शेड्यूल) करनी है?**", reply_markup=schedule_choice_keyboard()) @bot.on_callback_query(filters.regex("^wm_")) async def select_watermark_choice(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id if callback_query.data == "wm_yes": user_data[user_id]['step'] = 'AWAIT_WATERMARK_TEXT' await callback_query.message.edit_text("✍️ **कृपया Watermark का Text भेजें:**\n(जैसे: Class PDF Telegram Pr Milengi)") else: user_data[user_id]['step'] = 'AUDIO_CHOICE' await callback_query.message.edit_text("🎵 **YouTube के लिए ऑडियो जरूरी है।**\nक्या आप अपना खुद का Audio (MP3) लगाना चाहते हैं?", reply_markup=audio_choice_keyboard()) @bot.on_callback_query(filters.regex("^sch_")) async def select_schedule_choice(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id if callback_query.data == "sch_now": user_data[user_id]['step'] = 'READY' await callback_query.message.delete() await process_stream_start(client, callback_query.message) else: user_data[user_id]['step'] = 'AWAIT_DATETIME' update_schedule_dates() markup = [] for st in GLOBAL_SCHEDULE_TIMES: # Inline button format for specific times markup.append([InlineKeyboardButton(f"🗓 {st}", callback_data=f"set_schtime_{st}")]) # बटन के बिना मैन्युअल एंट्री का निर्देश text = "📅 **Schedule Time चुनें या लिखें!**\n\n" if GLOBAL_SCHEDULE_TIMES: text += "👇 **एडमिन द्वारा दिए गए समय में से चुनें:**\n" text += "\n✍️ **या अपना खुद का समय लिखकर भेजें:**\n**Format:** `DD/MM/YYYY hh:mm AM/PM`\n**Example:** `24/06/2026 05:50 AM`" await callback_query.message.edit_text(text, reply_markup=InlineKeyboardMarkup(markup) if markup else None) @bot.on_callback_query(filters.regex("^set_schtime_")) async def select_predefined_time(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id time_str = callback_query.data.replace("set_schtime_", "") # Create fake message to pass to AWAIT_DATETIME logic class FakeMessage: def __init__(self, text, chat_id): self.text = text self.chat = type('Chat', (), {'id': chat_id})() self.id = callback_query.message.id await callback_query.message.edit_text(f"⏳ Processing schedule for {time_str}...") await handle_all_messages(client, FakeMessage(time_str, user_id)) @bot.on_message(filters.command("edit")) async def edit_cmd(client, message): user_id = message.chat.id init_user(user_id, message) markup = [] text = "🛠 **Manage Your Streams:**\nयहाँ से आप अपनी ग़लत बनी हुई या चल रही स्ट्रीम्स को Delete/Stop कर सकते हैं।\n\n" # Running streams active_procs = [p for p in user_data[user_id].get('processes', []) if p['process'].poll() is None] if active_procs: text += "🔴 **Running Streams:**\n" for idx, p in enumerate(active_procs, 1): p_id = p.get('id', 'unknown') plat = p.get('platform', 'Unknown') text += f"{idx}. 🟢 {plat} - Started: {p['start_time'].strftime('%I:%M %p')}\n" markup.append([InlineKeyboardButton(f"🛑 Stop Running: {plat} Stream {idx}", callback_data=f"user_stop_{p_id}")]) # Scheduled streams scheduled = user_data[user_id].get('scheduled_tasks', {}) if scheduled: text += "\n⏳ **Scheduled Streams:**\n" idx = 1 for s_id, s_info in list(scheduled.items()): time_str = s_info['time_str'] plat = s_info['config'].get('platform', 'Unknown') text += f"{idx}. 🗓 {plat} - Scheduled for: {time_str}\n" markup.append([InlineKeyboardButton(f"❌ Cancel Scheduled: {plat} {time_str}", callback_data=f"user_cancelsch_{s_id}")]) idx += 1 if not active_procs and not scheduled: text = "⚠️ **आपकी कोई भी स्ट्रीम अभी चल नहीं रही है और ना ही शेड्यूल है।**" await message.reply(text, reply_markup=InlineKeyboardMarkup(markup) if markup else None) @bot.on_message(~filters.command(["start", "stop", "new", "admin", "edit"])) async def handle_all_messages(client, message): user_id = message.chat.id init_user(user_id, message) step = user_data[user_id].get('step') # === Admin Reply to Delete === if message.text == "/delete" and message.reply_to_message and user_id == ADMIN_ID: summary_id = message.reply_to_message.id if summary_id in sent_message_map: targets = sent_message_map[summary_id] deleted_count = 0 for uid, mid in targets: try: await client.delete_messages(uid, mid) deleted_count += 1 except: pass await message.reply(f"✅ **Message successfully deleted from {deleted_count} user(s) chat!**") del sent_message_map[summary_id] else: await message.reply("❌ **Could not find message data. Already deleted or too old.**") return # === Admin State Handling === if user_id == ADMIN_ID: global admin_state, GLOBAL_PASSWORD, GLOBAL_LIMIT, recent_admin_messages, PASSWORD_LIMIT, PASSWORD_USED_BY if admin_state == "AWAIT_NEW_PASSWORD" and message.text: parts = message.text.strip().split() GLOBAL_PASSWORD = parts[0] # अगर एडमिन ने स्पेस देकर नंबर लिखा है, तो उसे लिमिट सेट करें, नहीं तो अनलिमिटेड PASSWORD_LIMIT = int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else 999999 PASSWORD_USED_BY = {} # नया पासवर्ड आते ही पुरानी लिस्ट खाली कर दें admin_state = None limit_text = str(PASSWORD_LIMIT) if PASSWORD_LIMIT != 999999 else "Unlimited" await message.reply(f"✅ **Password Changed to:** `{GLOBAL_PASSWORD}`\n👥 **Max Users Allowed:** `{limit_text}`\n\nLogging everyone out...") for uid in user_data: if uid != ADMIN_ID: user_data[uid]['authenticated'] = False user_data[uid]['step'] = 'PASSWORD' try: await client.send_message(uid, "⚠️ **लॉगआउट अलर्ट! (Logout Alert)**\n\nएडमिन ने पासवर्ड बदल दिया है। कृपया `/start` दबाकर नया पासवर्ड डालें।") except: pass save_data() # डेटाबेस अपडेट करें return elif admin_state == "AWAIT_LIMIT" and message.text: text = message.text.strip() parts = text.split() if len(parts) == 1 and parts[0].isdigit(): GLOBAL_LIMIT = int(parts[0]) for uid in user_data: user_data[uid]['stream_limit'] = GLOBAL_LIMIT await message.reply(f"✅ **Global Stream Limit set to {GLOBAL_LIMIT} for everyone!**") elif len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit(): target_id = int(parts[0]) limit = int(parts[1]) if target_id in user_data: user_data[target_id]['stream_limit'] = limit await message.reply(f"✅ **User `{target_id}` can now run {limit} streams at once!**") try: await client.send_message(target_id, f"🎉 **Admin Alert:** Your concurrent stream limit has been increased to **{limit}**!") except: pass else: await message.reply("❌ **User not found in bot database!**") else: await message.reply("❌ **Invalid format!** Send something like `2` or `123456789 2`") admin_state = None save_data() # डेटाबेस अपडेट करें return elif admin_state == "AWAIT_BROADCAST": text_to_check = message.text or message.caption is_personal = False target_id = None content = None if text_to_check: parts = text_to_check.split(" ", 1) if len(parts) > 1 and parts[0].isdigit() and len(parts[0]) > 5: is_personal = True target_id = int(parts[0]) content = parts[1] targets =[] if is_personal: try: if message.text: msg = await client.send_message(target_id, content) else: msg = await message.copy(target_id, caption=content) targets.append((target_id, msg.id)) summary = await message.reply(f"✅ **Message sent privately to `{target_id}`!**\n_Reply to this message with `/delete` to remove it from their chat._") except Exception as e: await message.reply(f"❌ **Failed to send.** Target might have blocked the bot.") admin_state = None return else: success = 0 for uid in user_data: if uid != ADMIN_ID: try: msg = await message.copy(uid) targets.append((uid, msg.id)) success += 1 except: pass summary = await message.reply(f"✅ **Broadcast Sent successfully to {success} users!**\n_Reply to this message with `/delete` to remove it from all users._") if targets: sent_message_map[summary.id] = targets recent_admin_messages.append(targets) if len(recent_admin_messages) > 50: recent_admin_messages.pop(0) admin_state = None return elif admin_state == "AWAIT_SCH_TIME" and message.text: try: # Validation dt_obj = datetime.strptime(message.text.strip(), "%d/%m/%Y %I:%M %p") GLOBAL_SCHEDULE_TIMES.append(message.text.strip()) save_data() await message.reply(f"✅ **Time Added Successfully!**\n`{message.text.strip()}`\n\nअब यह समय सभी यूज़र्स को बटन में दिखाई देगा।") admin_state = None except Exception as e: await message.reply("❌ **Invalid Format!**\nकृपया इस फॉर्मेट में भेजें: `24/06/2026 05:50 AM`") return # === Normal User Handling === if not user_data[user_id]['authenticated'] and step != 'PASSWORD': user_data[user_id]['step'] = 'PASSWORD' return await message.reply("🔐 **Session Expired! Please enter password:**") # यहाँ हमने नए स्टेप्स (VIDEO_OR_URL और AWAIT_AUDIO) को Add कर दिया है if not message.text and step not in['VIDEO_OR_URL', 'AWAIT_IMAGE', 'AWAIT_AUDIO']: return if step == 'PASSWORD': if message.text == GLOBAL_PASSWORD: # चेक करें कि यूज़र पहले से लिस्ट में है या नहीं if user_id not in PASSWORD_USED_BY: if len(PASSWORD_USED_BY) >= PASSWORD_LIMIT: return await message.reply("🚫 **Password Limit Reached!**\nयह पासवर्ड अपनी लिमिट पूरी कर चुका है। कृपया एडमिन से नया पासवर्ड लें।") # अगर लिमिट बची है, तो यूज़र को एनालिसिस लिस्ट में जोड़ दें PASSWORD_USED_BY[user_id] = user_data[user_id]['name'] user_data[user_id]['authenticated'] = True user_data[user_id]['step'] = 'PLATFORM' save_data() # यूज़र लॉगिन सेव करें ताकि रीस्टार्ट होने पर लॉगिन न हटे await message.reply("**✅ Password Correct!**\n👇 **Select platform:**", reply_markup=platform_keyboard()) else: await message.reply("❌ **Wrong Password! Try again.**") elif step == 'STREAM_KEY': key = message.text platform = user_data[user_id]['platform'] if platform == 'YouTube': user_data[user_id]['rtmp_url'] = f"rtmp://a.rtmp.youtube.com/live2/{key}" elif platform == 'Rumble': user_data[user_id]['rtmp_url'] = f"rtmp://rtmp.rumble.com/live/{key}" user_data[user_id]['step'] = 'LOOP_CHOICE' await message.reply(f"✅ **Key Saved!**\n\n⏱ **How long do you want to stream?**", reply_markup=loop_keyboard()) elif step == 'CUSTOM_URL': user_data[user_id]['rtmp_url'] = message.text user_data[user_id]['step'] = 'LOOP_CHOICE' await message.reply("✅ **Custom URL Saved!**\n\n⏱ **How long do you want to stream?**", reply_markup=loop_keyboard()) elif step == 'AWAIT_IMAGE': if message.photo or message.document: msg = await message.reply("⏳ **Saving Image...**") image_path = await message.download() user_data[user_id]['custom_image_path'] = image_path user_data[user_id]['step'] = 'VIDEO_OR_URL' await msg.edit_text("✅ **Background Image Saved!**\n\n📥 **अब कोई Video File भेजें या डायरेक्ट Link (YouTube/Insta/FB) भेजें:**") else: await message.reply("⚠️ **Please send a valid Photo!**") elif step == 'VIDEO_OR_URL': user_data[user_id]['is_image_only'] = False if message.video or message.document: msg = await message.reply("⏳ **Downloading Video...**") start_time = time.time() last_update = [start_time] file_path = await message.download(progress=progress_bar, progress_args=(msg, start_time, last_update)) user_data[user_id]['file_path'] = file_path await msg.delete() elif message.photo: msg = await message.reply("⏳ **Downloading Image...**") file_path = await message.download() user_data[user_id]['file_path'] = file_path user_data[user_id]['is_image_only'] = True await msg.delete() elif message.text and "t.me/c/" in message.text: # Telegram Private Channel Link Logic if not USER_SESSION: return await message.reply("❌ **String Session सेट नहीं है! बॉट के कोड में USER_SESSION डालें।**") msg = await message.reply("🔍 **प्राइवेट चैनल में वीडियो ढूँढा जा रहा है... (आस-पास के मैसेज चेक कर रहा हूँ)**") try: link_parts = message.text.strip().split('/') chat_id = int("-100" + link_parts[4]) msg_id = int(link_parts[5]) if not user_app.is_connected: await user_app.start() target_msg = None # आगे-पीछे के 20 मैसेज चेक करेगा कि Video किसमें है messages = await user_app.get_messages(chat_id, range(max(1, msg_id - 10), msg_id + 11)) for m in messages: if m and not m.empty and (m.video or m.document): target_msg = m break if not target_msg: return await msg.edit_text("❌ **इस लिंक के आस-पास कोई Video नहीं मिला!**") await msg.edit_text("⏳ **Private Video Downloading...**") start_time = time.time() last_update = [start_time] file_path = await user_app.download_media(target_msg, progress=progress_bar, progress_args=(msg, start_time, last_update)) user_data[user_id]['file_path'] = file_path await msg.delete() except Exception as e: return await msg.edit_text(f"❌ **Private Video Error:** {str(e)}") elif message.text and ("http://" in message.text or "https://" in message.text): user_data[user_id]['video_url'] = message.text.strip() else: return await message.reply("⚠️ **कृपया सही Video, Photo या Link भेजें!**") user_data[user_id]['step'] = 'WATERMARK_CHOICE' if user_data[user_id]['is_image_only']: await message.reply("✨ **क्या आप फोटो पर चलता हुआ (Moving) नाम/वाटरमार्क लगाना चाहते हैं?**\n(यह YouTube को स्पैम से बचाएगा)", reply_markup=watermark_choice_keyboard()) else: await message.reply("✨ **क्या आप वीडियो पर Watermark (जैसे 'Class PDF Telegram...') लगाना चाहते हैं?**\n(यह हर 1 मिनट में दिखाई देगा)", reply_markup=watermark_choice_keyboard()) elif step == 'AWAIT_WATERMARK_TEXT': user_data[user_id]['watermark_text'] = message.text.strip() user_data[user_id]['step'] = 'AUDIO_CHOICE' await message.reply(f"✅ **वाटरमार्क '{message.text.strip()}' सेव हो गया!**\n\n🎵 **YouTube के लिए ऑडियो जरूरी है।**\nक्या आप अपना खुद का Audio (MP3) फाइल लगाना चाहते हैं?", reply_markup=audio_choice_keyboard()) elif step == 'AWAIT_AUDIO': if message.audio or message.video or message.document: msg = await message.reply("⏳ **Downloading Audio...**") audio_path = await message.download() user_data[user_id]['custom_audio_path'] = audio_path user_data[user_id]['step'] = 'SCHEDULE' await msg.edit_text("✅ **Audio Saved!**\n\n⏳ **स्ट्रीम अभी चालू करनी है या Schedule (शेड्यूल) करनी है?**", reply_markup=schedule_choice_keyboard()) else: await message.reply("⚠️ **कृपया सही Audio या Video File भेजें!**") elif step == 'AWAIT_DATETIME': try: time_str = message.text.strip() dt_obj = datetime.strptime(time_str, "%d/%m/%Y %I:%M %p") now_ist = get_ist_time().replace(tzinfo=None) if dt_obj <= now_ist: # If pre-defined time is passed, allow it logically but warn if past try: await client.send_message(user_id, "⚠️ **समय (Time) भविष्य का होना चाहिए!** कृपया आगे का समय भेजें।") except: await message.reply("⚠️ **समय (Time) भविष्य का होना चाहिए!** कृपया आगे का समय भेजें।") return user_data[user_id]['step'] = 'READY' delay = (dt_obj - now_ist).total_seconds() # --- Snapshot Configuration --- # (अगर यूज़र दूसरी स्ट्रीम बनायेगा तो पहली स्ट्रीम का डेटा डिलीट न हो) stream_config = { 'file_path': user_data[user_id].get('file_path'), 'video_url': user_data[user_id].get('video_url'), 'custom_audio_path': user_data[user_id].get('custom_audio_path'), 'platform': user_data[user_id].get('platform', 'Custom'), 'loop_hours': user_data[user_id].get('loop_hours', 0), 'rtmp_url': user_data[user_id].get('rtmp_url'), 'orientation': user_data[user_id].get('orientation', 'landscape'), 'custom_image_path': user_data[user_id].get('custom_image_path'), 'is_image_only': user_data[user_id].get('is_image_only', False), 'watermark_text': user_data[user_id].get('watermark_text') } s_id = str(uuid.uuid4())[:8] task = asyncio.create_task(scheduled_stream_starter(client, message, delay, stream_config, s_id)) user_data[user_id]['scheduled_tasks'][s_id] = { 'task': task, 'time_str': time_str, 'config': stream_config } try: await client.send_message(user_id, f"✅ **Stream Scheduled!**\nयह वीडियो आटोमेटिकली `{time_str}` पर चालू हो जाएगा।\nअगर कोई गलती हुई हो तो `/edit` टाइप करें।") except: await message.reply(f"✅ **Stream Scheduled!**\nयह वीडियो आटोमेटिकली `{time_str}` पर चालू हो जाएगा।\nअगर कोई गलती हुई हो तो `/edit` टाइप करें।") except Exception as e: try: await client.send_message(user_id, "❌ **Invalid Format!**\nकृपया इस फॉर्मेट में भेजें: `24/06/2026 05:50 AM`") except: await message.reply("❌ **Invalid Format!**\nकृपया इस फॉर्मेट में भेजें: `24/06/2026 05:50 AM`") @bot.on_callback_query(filters.regex("^user_cancelsch_")) async def cancel_sch_callback(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id s_id = callback_query.data.replace("user_cancelsch_", "") if s_id in user_data[user_id].get('scheduled_tasks', {}): user_data[user_id]['scheduled_tasks'][s_id]['task'].cancel() del user_data[user_id]['scheduled_tasks'][s_id] await callback_query.message.edit_text("✅ **Scheduled Stream Cancelled / Delete हो गई!**\nअब आप `/new` से नई बना सकते हैं।") else: await callback_query.message.edit_text("❌ **यह शेड्यूल पहले ही कैंसिल हो चुका है या चल चुका है।**") @bot.on_callback_query(filters.regex("^user_stop_")) async def stop_running_callback(client, callback_query: CallbackQuery): user_id = callback_query.message.chat.id p_id = callback_query.data.replace("user_stop_", "") stopped = False for p in user_data[user_id].get('processes', []): if p.get('id') == p_id and p['process'].poll() is None: user_data[user_id]['manual_stop'] = True p['process'].terminate() stopped = True break if stopped: await callback_query.message.edit_text("✅ **Running Stream को सफलतापूर्वक बंद कर दिया गया है!**") else: await callback_query.message.edit_text("❌ **यह स्ट्रीम पहले ही बंद हो चुकी है।**") async def scheduled_stream_starter(client, message, delay, stream_config, s_id): try: await asyncio.sleep(delay) user_id = message.chat.id # Remove from pending queue as it starts if s_id in user_data[user_id].get('scheduled_tasks', {}): del user_data[user_id]['scheduled_tasks'][s_id] await handle_video(client, message, stream_config) except asyncio.CancelledError: pass # Stream was cancelled by user async def process_stream_start(client, message): await handle_video(client, message) def get_direct_url(url): ydl_opts = {'format': 'best', 'quiet': True, 'noplaylist': True} try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) return info.get('url', url) except: return url async def handle_video(client, message, stream_config=None): user_id = message.chat.id # अगर कॉन्फ़िग स्नैपशॉट (शेड्यूल से) आया है, तो उसका इस्तेमाल करें। नहीं तो करंट डेटा का। config = stream_config if stream_config else user_data[user_id] user_data[user_id]['manual_stop'] = False try: msg = await client.send_message(user_id, "⏳ **Preparing Stream Environment...**") except: msg = await message.reply("⏳ **Preparing Stream Environment...**") file_path = config.get('file_path') video_url = config.get('video_url') custom_audio = config.get('custom_audio_path') input_source = file_path if video_url: await msg.edit_text("🔗 **Extracting direct stream link (YouTube/Facebook/Rumble etc.)...**") input_source = await asyncio.to_thread(get_direct_url, video_url) if not input_source: return await msg.edit_text("❌ **Failed to extract link!**") platform = config.get('platform', 'Custom') loop_hours = config.get('loop_hours', 0) rtmp_url = config.get('rtmp_url') orientation = config.get('orientation', 'landscape') image_path = config.get('custom_image_path') user_data[user_id]['total_streams'] += 1 user_data[user_id]['last_stream'] = get_ist_time() import random # Random time जनरेट करने के लिए vid_duration = get_video_duration(file_path) if file_path else 3600 # ----------------------------------------------------- # ANTI-BOT RANDOMIZER: हर बार समय को थोड़ा आगे-पीछे करना # ----------------------------------------------------- if loop_hours > 0: base_sec = loop_hours * 3600 # 10% समय रैंडमली कम या ज्यादा करें (जैसे 3 घंटे में +/- 18 मिनट, 30 मिनट में +/- 3 मिनट) random_offset = random.randint(int(-0.1 * base_sec), int(0.1 * base_sec)) actual_stream_seconds = base_sec + random_offset total_stream_time = actual_stream_seconds else: total_stream_time = vid_duration actual_stream_seconds = vid_duration is_image_only = config.get('is_image_only', False) # --- FFMPEG Command Setup --- command = ["ffmpeg", "-re"] if is_image_only: # Image Only Streaming Logic (YouTube Standard 30 FPS) command.extend(["-loop", "1", "-framerate", "30", "-i", input_source]) if custom_audio: command.extend(["-stream_loop", "-1", "-i", custom_audio]) command.extend(["-map", "0:v", "-map", "1:a:0?"]) else: # YouTube rejects streams without audio, so we add silent dummy audio command.extend(["-f", "lavfi", "-i", "anullsrc=channel_layout=stereo:sample_rate=44100"]) command.extend(["-map", "0:v", "-map", "1:a"]) # ----------------------------------------------------- # NEW: Aspect Ratio Fix for Image (Portrait vs Landscape) # ----------------------------------------------------- if orientation == 'portrait': # Makes 16:9 photo into 9:16 (Adds black bars on top/bottom) vf_filter = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black" else: # Ensures normal photos fit 16:9 screen vf_filter = "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=black" # --- NEW: SLOW SMOOTH MOVING WATERMARK --- wm_text = config.get('watermark_text') if wm_text: safe_text = wm_text.replace("'", "\\'").replace(":", "\\:") # sin(t/10) और cos(t/15) का मतलब है कि यह बहुत धीरे-धीरे (Slowly) पूरी स्क्रीन पर गोल घूमेगा drawtext = f",drawtext=text='{safe_text}':fontcolor=white@0.4:fontsize=50:x=(w-tw)/2+((w-tw)/2.5)*sin(t/10):y=(h-th)/2+((h-th)/2.5)*cos(t/15)" vf_filter += drawtext command.extend(["-vf", vf_filter]) # ----------------------------------------------------- # Encode with strict YouTube standards (30 FPS, 2-sec Keyframe, CBR) command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"]) # YouTube Keyframe Fix: 30fps के हिसाब से हर 2 सेकंड में कीफ्रेम (-g 60) command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"]) # YouTube Bitrate Fix: Strict CBR (Constant Bitrate) with Dummy Padding command.extend(["-b:v", "4000k", "-maxrate", "4000k", "-minrate", "4000k", "-bufsize", "8000k", "-nal-hrd", "cbr"]) command.extend(["-c:a", "aac", "-b:a", "128k"]) else: # Normal Video Logic if loop_hours > 0: command.extend(["-stream_loop", "-1"]) command.extend(["-i", input_source]) current_idx = 1; idx_aud = 0 if custom_audio: command.extend(["-stream_loop", "-1", "-i", custom_audio]) idx_aud = current_idx; current_idx += 1 idx_img = -1 if orientation == 'portrait' and image_path: command.extend(["-loop", "1", "-i", image_path]) idx_img = current_idx; current_idx += 1 if orientation == 'portrait': if image_path: vf_complex = f"[{idx_img}:v]scale=1080:1920[bg];[0:v]scale=1080:1920:force_original_aspect_ratio=decrease[vid];[bg][vid]overlay=(W-w)/2:(H-h)/2:shortest=1[outv]" command.extend(["-filter_complex", vf_complex, "-map", "[outv]"]) else: vf_filter = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black" command.extend(["-vf", vf_filter, "-map", "0:v"]) # YouTube Strict Bitrate & Keyframe (GOP) Fix for Portrait Video command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"]) command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"]) command.extend(["-b:v", "6000k", "-maxrate", "6000k", "-minrate", "6000k", "-bufsize", "12000k", "-nal-hrd", "cbr"]) else: # YouTube Strict Bitrate & Keyframe (GOP) Fix for Landscape Video wm_text = config.get('watermark_text') if wm_text: safe_text = wm_text.replace("'", "\\'").replace(":", "\\:") # Custom Timing Logic: 1st min, 3rd min, then every 5 mins (each for 15 sec) timing_logic = "between(t\\,60\\,75)+between(t\\,180\\,195)+(gte(t\\,480)*lt(mod(t-480\\,300)\\,15))" vf_filter = f"drawtext=text=' Telegram\\: {safe_text} ':fontcolor=white:fontsize=38:box=1:boxcolor=#2AABEE@0.9:boxborderw=15:x=40:y=h-th-40:enable='{timing_logic}'" command.extend(["-vf", vf_filter, "-map", "0:v"]) else: command.extend(["-map", "0:v"]) command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"]) command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"]) command.extend(["-b:v", "6000k", "-maxrate", "6000k", "-minrate", "6000k", "-bufsize", "12000k", "-nal-hrd", "cbr"]) if custom_audio: command.extend(["-map", f"{idx_aud}:a:0?", "-c:a", "aac", "-b:a", "128k", "-shortest"]) else: command.extend(["-map", "0:a?", "-c:a", "aac", "-b:a", "128k"]) # Apply time limit for BOTH Image and Video if loop_hours > 0: seconds_str = str(int(actual_stream_seconds)) # असली रैंडम सेकंड्स if not is_image_only: command.extend(["-fflags", "+genpts"]) command.extend(["-t", seconds_str]) command.extend(["-f", "flv", rtmp_url]) st_type = "🖼 Image" if is_image_only else "🎥 Video" live_msg = await msg.edit_text(f"🟢 **Live Started on {platform}!**\n📋 **Type:** `{st_type}`\n📐 **Mode:** `{orientation.capitalize()}`\n⏳ **Time Left:** `Calculating...`\n\n🔹 To stop, send /stop") process_obj = subprocess.Popen(command) # Store Advanced Details for Admin Panel (Now saves actual randomized end time) start_t = get_ist_time() end_t = start_t + timedelta(seconds=actual_stream_seconds) if loop_hours > 0 else "Auto (Depends on Video)" unique_p_id = str(uuid.uuid4())[:8] user_data[user_id]['processes'].append({ 'id': unique_p_id, 'process': process_obj, 'start_time': start_t, 'end_time': end_t, 'loop_hours': loop_hours, 'is_image': is_image_only, 'platform': platform }) asyncio.create_task(monitor_stream(client, message.chat.id, process_obj, file_path, live_msg.id, loop_hours, platform, total_stream_time, image_path, custom_audio)) async def monitor_stream(client, chat_id, process, file_path, msg_id, loop_hours, platform, total_time, image_path, custom_audio): start_time_ist = get_ist_time() last_ui_update = time.time() mode_text = f"🔁 {loop_hours} Hours Loop" if loop_hours > 0 else "▶️ 1-Time Play" while process.poll() is None: await asyncio.sleep(5) now_time = time.time() if total_time > 0 and (now_time - last_ui_update > 60): last_ui_update = now_time elapsed = (get_ist_time() - start_time_ist).total_seconds() rem_sec = total_time - elapsed if rem_sec > 0: h = int(rem_sec // 3600) m = int((rem_sec % 3600) // 60) time_left_str = f"{h}h {m}m" if h > 0 else f"{m}m" try: await client.edit_message_text( chat_id, msg_id, f"🟢 **Live is RUNNING on {platform}!**\n" f"⏱ **Loop:** `{mode_text}`\n" f"⏳ **Time Left:** `{time_left_str}`\n\n" f"🔹 Send /stop to end manually." ) except: pass end_time_ist = get_ist_time() duration = end_time_ist - start_time_ist try: await client.delete_messages(chat_id, msg_id) except: pass # Server Storage साफ़ करना (Delete file to save memory) try: if file_path and os.path.exists(file_path): os.remove(file_path) if image_path and os.path.exists(image_path): os.remove(image_path) if custom_audio and os.path.exists(custom_audio): os.remove(custom_audio) except: pass start_str = start_time_ist.strftime("%I:%M %p") end_str = end_time_ist.strftime("%I:%M %p") dur_str = str(duration).split('.')[0] is_manual = user_data.get(chat_id, {}).get('manual_stop', False) status_text = "🛑 **Live Stream Stopped Manually!**" if is_manual else "🔴 **Live Stream Ended Automatically!**" report_msg = ( f"{status_text}\n\n" f"📊 **STREAM SUMMARY:**\n" f"🔸 **Platform:** `{platform}`\n" f"🟢 **Started at:** `{start_str}`\n" f"🔴 **Ended at:** `{end_str}`\n" f"⏱ **Total Runtime:** `{dur_str}`\n\n" f"🗑 __Video, Audio & Image deleted safely.__" ) await client.send_message(chat_id, report_msg) if __name__ == "__main__": load_data() # बॉट स्टार्ट होते ही पुरानी सेटिंग्स और यूज़र्स वापस लोड करें Thread(target=run_flask).start() bot.run()