| 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 |
|
|
| |
| 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) |
|
|
| |
| 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 = "BQIlGM4AazlRr5P1dhaKtIXeQTq0O5LnNaFB9uBcdjcjZQ95iBGIm3L_NTaA0ncK91ZKIr_4loGx8gw84Z_RL9gQDSZ80QOIq5RrwtlKvsfF-NrJElCGJ5NAL8GMtDceRIQpQO78DfI4PM75h7zylKi3jnDQik2bAUzJ7ylG1VI_hgfKxYZlkK2FXkGaACNgJSzUzc-4_THAlSDLNhpQ6Dv_WlFKQpGR47KdZTMxR81ExV5WjW4-AG1A0tr_B0PUvRVyZrpMLb5AzPMqFmByxeQS3QicQzXGKYlJj9Pm5uYRCcFS5WCqhC2lwISMqklhjIivc7LHko5EjyWELHhAhz2RfmPDQAAAAAHK_kURAA" |
| 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 |
| PASSWORD_USED_BY = {} |
| GLOBAL_LIMIT = 1 |
| user_data = {} |
| admin_state = None |
|
|
| |
| sent_message_map = {} |
| recent_admin_messages =[] |
| GLOBAL_SCHEDULE_TIMES = [] |
|
|
| |
| 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")] |
| ]) |
|
|
| |
| 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: |
| 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.**") |
|
|
| |
| 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: |
| |
| 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_", "") |
| |
| |
| 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" |
| |
| |
| 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 = 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') |
|
|
| |
| 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 |
|
|
| |
| 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: |
| |
| 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 |
|
|
| |
| 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:**") |
|
|
| |
| 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: |
| |
| 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 |
| |
| 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: |
| |
| 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() |
| |
| |
| |
| 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 |
| |
| 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 |
|
|
| 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 |
| |
| vid_duration = get_video_duration(file_path) if file_path else 3600 |
| |
| |
| |
| |
| if loop_hours > 0: |
| base_sec = loop_hours * 3600 |
| |
| 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) |
|
|
| |
| command = ["ffmpeg", "-re"] |
| |
| if is_image_only: |
| |
| 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: |
| |
| command.extend(["-f", "lavfi", "-i", "anullsrc=channel_layout=stereo:sample_rate=44100"]) |
| command.extend(["-map", "0:v", "-map", "1:a"]) |
|
|
| |
| |
| |
| if orientation == 'portrait': |
| |
| vf_filter = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black" |
| else: |
| |
| vf_filter = "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=black" |
| |
| |
| wm_text = config.get('watermark_text') |
| if wm_text: |
| safe_text = wm_text.replace("'", "\\'").replace(":", "\\:") |
| |
| 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]) |
| |
|
|
| |
| command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"]) |
| |
| command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"]) |
| |
| command.extend(["-b:v", "4000k", "-maxrate", "4000k", "-minrate", "4000k", "-bufsize", "8000k", "-nal-hrd", "cbr"]) |
| command.extend(["-c:a", "aac", "-b:a", "128k"]) |
| |
| else: |
| |
| 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"]) |
| |
| |
| 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: |
| |
| wm_text = config.get('watermark_text') |
| |
| if wm_text: |
| safe_text = wm_text.replace("'", "\\'").replace(":", "\\:") |
| |
| 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"]) |
| |
| |
| 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) |
| |
| |
| 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 |
|
|
| |
| 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() |