Files changed (1) hide show
  1. bot.py +0 -985
bot.py DELETED
@@ -1,985 +0,0 @@
1
- import os
2
- import time
3
- import math
4
- import json
5
- import asyncio
6
- import subprocess
7
- import yt_dlp
8
- from datetime import datetime, timedelta
9
- from threading import Thread
10
- from flask import Flask
11
- from pyrogram import Client, filters
12
- from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
13
-
14
- # --- Hugging Face को जगाए रखने के लिए Flask Server ---
15
- app = Flask(__name__)
16
- @app.route('/')
17
- def health_check():
18
- return "Bot is running perfectly!"
19
-
20
- def run_flask():
21
- app.run(host="0.0.0.0", port=7860)
22
-
23
- # --- Telegram Bot की डिटेल्स ---
24
- BOT_TOKEN = "8728797060:AAH3L0eqApxEKuVvYjKT0JGpgQ3BPlrjbgI"
25
- API_ID = 35985614
26
- API_HASH = "6a0df17414daf6935f1f0a71b8af1ee9"
27
-
28
- bot = Client("LiveBot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
29
-
30
- # --- USER SESSION (Private Channel Download के लिए) ---
31
- USER_SESSION = "BQIlGM4AazlRr5P1dhaKtIXeQTq0O5LnNaFB9uBcdjcjZQ95iBGIm3L_NTaA0ncK91ZKIr_4loGx8gw84Z_RL9gQDSZ80QOIq5RrwtlKvsfF-NrJElCGJ5NAL8GMtDceRIQpQO78DfI4PM75h7zylKi3jnDQik2bAUzJ7ylG1VI_hgfKxYZlkK2FXkGaACNgJSzUzc-4_THAlSDLNhpQ6Dv_WlFKQpGR47KdZTMxR81ExV5WjW4-AG1A0tr_B0PUvRVyZrpMLb5AzPMqFmByxeQS3QicQzXGKYlJj9Pm5uYRCcFS5WCqhC2lwISMqklhjIivc7LHko5EjyWELHhAhz2RfmPDQAAAAAHK_kURAA" # 👉 यहाँ अपना Pyrogram String Session डालें
32
- user_app = Client("UserAcc", api_id=API_ID, api_hash=API_HASH, session_string=USER_SESSION)
33
-
34
- # --- ग्लोबल वेरिएबल्स ---
35
- ADMIN_ID = 7700628753
36
- GLOBAL_PASSWORD = "jitu@123"
37
- PASSWORD_LIMIT = 999999 # डिफ़ॉल्ट लिमिट (Unlimted)
38
- PASSWORD_USED_BY = {} # जिन लोगों ने पासवर्ड इस्तेमाल किया है उनका डेटा
39
- GLOBAL_LIMIT = 1
40
- user_data = {}
41
- admin_state = None
42
-
43
- # --- मैसेज डिलीट करने के लिए नया डेटाबेस (टेम्परेरी) ---
44
- sent_message_map = {}
45
- recent_admin_messages =[]
46
-
47
- # --- Data Persistence (बॉट रिस्टार्ट होने पर डेटा सेव रखने के लिए) ---
48
- DATA_FILE = "bot_config.json"
49
-
50
- def save_data():
51
- try:
52
- # सिर्फ ऑथेंटिकेटेड यूज़र्स का डेटा सेव करेंगे ताकि फाइल भारी न हो
53
- auth_users = {str(uid): info['name'] for uid, info in user_data.items() if info.get('authenticated')}
54
- with open(DATA_FILE, "w") as f:
55
- json.dump({
56
- "GLOBAL_PASSWORD": GLOBAL_PASSWORD,
57
- "PASSWORD_LIMIT": PASSWORD_LIMIT,
58
- "GLOBAL_LIMIT": GLOBAL_LIMIT,
59
- "PASSWORD_USED_BY": {str(k): v for k, v in PASSWORD_USED_BY.items()},
60
- "authenticated_users": auth_users
61
- }, f)
62
- except Exception as e: pass
63
-
64
- def load_data():
65
- global GLOBAL_PASSWORD, PASSWORD_LIMIT, GLOBAL_LIMIT, PASSWORD_USED_BY, user_data
66
- if os.path.exists(DATA_FILE):
67
- try:
68
- with open(DATA_FILE, "r") as f:
69
- data = json.load(f)
70
- GLOBAL_PASSWORD = data.get("GLOBAL_PASSWORD", "jitu@123")
71
- PASSWORD_LIMIT = data.get("PASSWORD_LIMIT", 999999)
72
- GLOBAL_LIMIT = data.get("GLOBAL_LIMIT", 1)
73
- PASSWORD_USED_BY = {int(k): v for k, v in data.get("PASSWORD_USED_BY", {}).items()}
74
-
75
- saved_users = data.get("authenticated_users", {})
76
- for uid, name in saved_users.items():
77
- uid = int(uid)
78
- if uid not in user_data:
79
- user_data[uid] = {
80
- 'name': name,
81
- 'authenticated': True,
82
- 'step': 'PLATFORM',
83
- 'platform': None,
84
- 'rtmp_url': None,
85
- 'loop_hours': 0,
86
- 'orientation': 'landscape',
87
- 'custom_image_path': None,
88
- 'custom_audio_path': None,
89
- 'video_url': None,
90
- 'schedule_time': None,
91
- 'processes':[],
92
- 'stream_limit': GLOBAL_LIMIT,
93
- 'file_path': None,
94
- 'manual_stop': False,
95
- 'join_date': get_ist_time(),
96
- 'last_online': get_ist_time(),
97
- 'total_streams': 0,
98
- 'last_stream': "Never"
99
- }
100
- except Exception as e: pass
101
-
102
- def get_ist_time():
103
- return datetime.utcnow() + timedelta(hours=5, minutes=30)
104
-
105
- def format_date(dt):
106
- if isinstance(dt, str): return dt
107
- return dt.strftime("%d-%b-%Y %I:%M %p")
108
-
109
- def get_video_duration(file_path):
110
- try:
111
- result = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", file_path],
112
- stdout=subprocess.PIPE,
113
- stderr=subprocess.STDOUT,
114
- text=True
115
- )
116
- return float(result.stdout.strip())
117
- except:
118
- return 0
119
-
120
- async def notify_admin(client, text):
121
- try:
122
- await client.send_message(ADMIN_ID, text)
123
- except:
124
- pass
125
-
126
- def init_user(user_id, message=None):
127
- if user_id not in user_data:
128
- name = message.from_user.first_name if message else "Unknown"
129
- user_data[user_id] = {
130
- 'name': name,
131
- 'authenticated': False if user_id != ADMIN_ID else True,
132
- 'step': 'PASSWORD' if user_id != ADMIN_ID else 'PLATFORM',
133
- 'platform': None,
134
- 'rtmp_url': None,
135
- 'loop_hours': 0,
136
- 'orientation': 'landscape',
137
- 'custom_image_path': None, # नया: कस्टम फोटो सेव करने के लिए
138
- 'custom_audio_path': None, # ऑडियो रिप्लेस
139
- 'watermark_text': None, # <--- नया: मूविंग वाटरमार्क के लिए
140
- 'video_url': None, # डायरेक्ट लिंक
141
- 'schedule_time': None, # शेड्यूलिंग
142
- 'processes':[],
143
- 'stream_limit': GLOBAL_LIMIT,
144
- 'file_path': None,
145
- 'manual_stop': False,
146
- 'join_date': get_ist_time(),
147
- 'last_online': get_ist_time(),
148
- 'total_streams': 0,
149
- 'last_stream': "Never"
150
- }
151
- if user_id != ADMIN_ID:
152
- asyncio.create_task(notify_admin(bot, f"🔔 **New User Joined!**\n👤 **Name:** {name}\n🆔 **ID:** `{user_id}`\n📅 **Date:** {format_date(get_ist_time())}"))
153
- else:
154
- user_data[user_id]['last_online'] = get_ist_time()
155
- if message:
156
- user_data[user_id]['name'] = message.from_user.first_name
157
-
158
- def platform_keyboard():
159
- return InlineKeyboardMarkup([[InlineKeyboardButton("🔴 YouTube Live", callback_data="plat_youtube")],[InlineKeyboardButton("🟢 Rumble Live", callback_data="plat_rumble")],[InlineKeyboardButton("🔵 Custom/Other", callback_data="plat_custom")]])
160
-
161
- def loop_keyboard():
162
- 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")]
163
- ])
164
-
165
- def orientation_keyboard():
166
- return InlineKeyboardMarkup([[InlineKeyboardButton("🖥 Landscape (16:9)", callback_data="ori_landscape")],[InlineKeyboardButton("📱 Portrait / Shorts (9:16)", callback_data="ori_portrait")]])
167
-
168
- def image_choice_keyboard():
169
- return InlineKeyboardMarkup([[InlineKeyboardButton("✅ Yes (Add Custom Photo)", callback_data="img_yes")],[InlineKeyboardButton("❌ No (Normal Black Bg)", callback_data="img_no")]])
170
-
171
- def audio_choice_keyboard():
172
- return InlineKeyboardMarkup([[InlineKeyboardButton("🎵 Replace Audio (Send File)", callback_data="aud_yes")],[InlineKeyboardButton("🔊 Keep Original Audio", callback_data="aud_no")]])
173
-
174
- def watermark_choice_keyboard():
175
- return InlineKeyboardMarkup([[InlineKeyboardButton("✅ Yes (Moving Watermark)", callback_data="wm_yes")],[InlineKeyboardButton("❌ No Watermark", callback_data="wm_no")]])
176
-
177
- def schedule_choice_keyboard():
178
- return InlineKeyboardMarkup([[InlineKeyboardButton("▶️ Start Now", callback_data="sch_now")],[InlineKeyboardButton("⏳ Schedule Time", callback_data="sch_later")]])
179
-
180
- def admin_keyboard():
181
- 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("📢 Broadcast / Send Msg", callback_data="admin_msg")],[InlineKeyboardButton("🗑 Delete Last 5 Broadcasts", callback_data="admin_del")]])
182
-
183
- # --- Live Progress Bar ---
184
- async def progress_bar(current, total, msg, start_time, last_update):
185
- now = time.time()
186
- if now - last_update[0] > 3 or current == total:
187
- last_update[0] = now
188
- diff = now - start_time
189
- percentage = current * 100 / total
190
- speed = current / diff if diff > 0 else 0
191
- eta = round((total - current) / speed) if speed > 0 else 0
192
-
193
- current_mb = round(current / (1024 * 1024), 2)
194
- total_mb = round(total / (1024 * 1024), 2)
195
- speed_mb = round(speed / (1024 * 1024), 2)
196
-
197
- progress_str = "[{0}{1}]".format(''.join(["█" for _ in range(math.floor(percentage / 5))]), ''.join(["░" for _ in range(20 - math.floor(percentage / 5))]))
198
- eta_str = f"{eta} sec" if eta < 60 else f"{eta//60} min {eta%60} sec"
199
-
200
- 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}`"
201
- try: await msg.edit_text(text)
202
- except: pass
203
-
204
- @bot.on_message(filters.command("admin"))
205
- async def admin_cmd(client, message):
206
- if message.chat.id == ADMIN_ID:
207
- global admin_state
208
- admin_state = None
209
- await message.reply("👑 **Welcome to Admin Panel**\nChoose an option below:", reply_markup=admin_keyboard())
210
-
211
- @bot.on_message(filters.command("start"))
212
- async def start_cmd(client, message):
213
- user_id = message.chat.id
214
- init_user(user_id, message)
215
- if user_data[user_id]['authenticated']:
216
- user_data[user_id]['step'] = 'PLATFORM'
217
- await message.reply("**✅ Logged in!**\n👇 **Select platform:**", reply_markup=platform_keyboard())
218
- else:
219
- user_data[user_id]['step'] = 'PASSWORD'
220
- await message.reply("👋 **Welcome!**\n🔐 **Enter Password to continue:**")
221
-
222
- @bot.on_message(filters.command("new"))
223
- async def new_cmd(client, message):
224
- user_id = message.chat.id
225
- init_user(user_id, message)
226
-
227
- if not user_data[user_id]['authenticated']:
228
- user_data[user_id]['step'] = 'PASSWORD'
229
- return await message.reply("🔐 **Session Expired! Please enter the new password:**")
230
-
231
- user_data[user_id]['step'] = 'PLATFORM'
232
- # नई स्ट्रीम शुरू करते समय पुराना डेटा मिटा दें
233
- user_data[user_id]['custom_image_path'] = None
234
- user_data[user_id]['custom_audio_path'] = None
235
- user_data[user_id]['watermark_text'] = None
236
- user_data[user_id]['video_url'] = None
237
- user_data[user_id]['file_path'] = None
238
- await message.reply("🔄 **New Stream Setup!**\n👇 **Select platform:**", reply_markup=platform_keyboard())
239
-
240
- @bot.on_message(filters.command("stop"))
241
- async def stop_cmd(client, message):
242
- user_id = message.chat.id
243
- if user_id not in user_data: return
244
-
245
- active_procs = []
246
- for p in user_data[user_id].get('processes',[]):
247
- proc_obj = p['process'] if isinstance(p, dict) else p
248
- if proc_obj.poll() is None:
249
- active_procs.append(proc_obj)
250
-
251
- if not active_procs:
252
- return await message.reply("⚠️ **No active stream found.**")
253
-
254
- user_data[user_id]['manual_stop'] = True
255
- for p in active_procs:
256
- p.terminate()
257
-
258
- user_data[user_id]['processes'] =[]
259
- await message.reply(f"🛑 **Stopping {len(active_procs)} Active Stream(s)... Generating Report!**")
260
-
261
- @bot.on_callback_query(filters.regex("^admin_"))
262
- async def admin_callbacks(client, callback_query: CallbackQuery):
263
- if callback_query.message.chat.id != ADMIN_ID:
264
- return
265
- data = callback_query.data
266
- global admin_state
267
- global recent_admin_messages
268
-
269
- if data == "admin_users":
270
- total_users = len(user_data)
271
- text = f"👥 **Total Users:** `{total_users}`\n\n"
272
- for uid, info in user_data.items():
273
- if uid == ADMIN_ID: continue
274
- 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"
275
- text += "➖➖➖➖➖➖➖➖\n"
276
- if len(text) > 4000:
277
- for x in range(0, len(text), 4000):
278
- await client.send_message(ADMIN_ID, text[x:x+4000])
279
- else:
280
- await callback_query.message.reply(text)
281
-
282
- elif data == "admin_live":
283
- live_text = ""
284
- total_live = 0
285
- for uid, info in user_data.items():
286
- active_streams =[]
287
- for p in info.get('processes', []):
288
- if isinstance(p, dict) and p['process'].poll() is None:
289
- active_streams.append(p)
290
- elif not isinstance(p, dict) and p.poll() is None: # Old format safety
291
- active_streams.append({'process': p, 'start_time': get_ist_time(), 'end_time': "Unknown", 'loop_hours': 0, 'is_image': False})
292
-
293
- if active_streams:
294
- total_live += len(active_streams)
295
- live_text += f"👤 **Name:** {info['name']} (ID: `{uid}`)\n🌐 **Platform:** `{info.get('platform', 'Unknown')}`\n"
296
-
297
- for i, strm in enumerate(active_streams, 1):
298
- mode_str = f"{strm['loop_hours']}h Loop" if strm['loop_hours'] > 0 else "1 Time"
299
- if strm.get('is_image'): mode_str += " (🖼 Image)"
300
- else: mode_str += " (🎥 Video)"
301
-
302
- st_str = strm['start_time'].strftime("%I:%M %p")
303
- et_str = strm['end_time'].strftime("%I:%M %p") if isinstance(strm['end_time'], datetime) else str(strm['end_time'])
304
-
305
- live_text += f" ➤ **Stream {i}:** Mode: `{mode_str}`\n"
306
- live_text += f" 🟢 Start: `{st_str}` | 🔴 End: `{et_str}`\n"
307
- live_text += "➖➖➖➖➖➖\n"
308
-
309
- if not live_text:
310
- await callback_query.message.reply("❌ **No one is streaming right now.**")
311
- else:
312
- await callback_query.message.reply(f"🔴 **Live Streams ({total_live} total):**\n\n" + live_text)
313
-
314
- elif data == "admin_pass":
315
- markup = InlineKeyboardMarkup([
316
- [InlineKeyboardButton("🔑 Change Password", callback_data="admin_pass_change")],[InlineKeyboardButton("📊 Pass Analysis", callback_data="admin_pass_analysis")]
317
- ])
318
- await callback_query.message.edit_text("🔐 **Password Management**\nChoose an option below:", reply_markup=markup)
319
-
320
- elif data == "admin_pass_change":
321
- admin_state = "AWAIT_NEW_PASSWORD"
322
- await callback_query.message.reply("🔑 **Please send the NEW PASSWORD and LIMIT.**\n`उदाहरण: 5550 5` (इसका मतलब सिर्फ 5 लोग पासवर्ड यूज़ कर पाएंगे)")
323
-
324
- elif data == "admin_pass_analysis":
325
- limit_text = str(PASSWORD_LIMIT) if PASSWORD_LIMIT != 999999 else "Unlimited"
326
- if not PASSWORD_USED_BY:
327
- await callback_query.message.reply(f"📊 **Pass Analysis:**\n🔑 **Current Password:** `{GLOBAL_PASSWORD}`\n👥 **Limit:** `0 / {limit_text}`\n\nअभी तक किसी ने भी मौजूदा पासवर्ड का इस्तेमाल नहीं किया है।")
328
- else:
329
- text = f"📊 **Pass Analysis:**\n🔑 **Current Password:** `{GLOBAL_PASSWORD}`\n👥 **Limit:** `{len(PASSWORD_USED_BY)} / {limit_text}`\n\n"
330
- for uid, uname in PASSWORD_USED_BY.items():
331
- text += f"👤 **Name:** {uname}\n🆔 **ID:** `{uid}`\n➖➖➖➖➖➖\n"
332
- await callback_query.message.reply(text)
333
-
334
- elif data == "admin_limit":
335
- admin_state = "AWAIT_LIMIT"
336
- await callback_query.message.reply("⚙️ **Set Stream Limit Mode:**\n\n👉 **सबका लिमिट बदलने के लिए:** सिर्फ नंबर भेजें (e.g. `2`)\n👉 **किसी एक यूज़र का लिमिट बदलने के लिए:** ID और नंबर भेजें\n`उदाहरण: 384884848 2`")
337
-
338
- elif data == "admin_msg":
339
- admin_state = "AWAIT_BROADCAST"
340
- await callback_query.message.reply("📢 **Broadcast / Personal Message Mode!**\n\n"
341
- "👉 **सबको भेजने के लिए:** कोई भी Text, Photo या Video सीधा भेजें।\n"
342
- "👉 **किसी एक को भेजने के लिए:** ID स्पेस मेसेज लिखें।\n"
343
- "`उदाहरण: 8383838487 Hello kese ho`")
344
-
345
- elif data == "admin_del":
346
- if not recent_admin_messages:
347
- return await callback_query.message.reply("❌ **No recent messages/broadcasts found to delete.**")
348
-
349
- deleted_ops = 0
350
- total_msgs = 0
351
-
352
- for _ in range(5):
353
- if not recent_admin_messages:
354
- break
355
- targets = recent_admin_messages.pop()
356
- deleted_ops += 1
357
- for uid, mid in targets:
358
- try:
359
- await client.delete_messages(uid, mid)
360
- total_msgs += 1
361
- except: pass
362
-
363
- await callback_query.message.reply(f"✅ **Deleted last {deleted_ops} broadcast(s)/message(s) from {total_msgs} users' chats!**")
364
-
365
-
366
- @bot.on_callback_query(filters.regex("^plat_"))
367
- async def select_platform(client, callback_query: CallbackQuery):
368
- user_id = callback_query.message.chat.id
369
- init_user(user_id)
370
-
371
- if not user_data[user_id].get('authenticated'):
372
- return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**")
373
-
374
- 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]
375
- user_data[user_id]['processes'] = active_procs
376
- limit = user_data[user_id].get('stream_limit', GLOBAL_LIMIT)
377
-
378
- if len(active_procs) >= limit:
379
- return await callback_query.message.edit_text(f"⚠️ **Limit Reached!**\nआप एक साथ सिर्फ {limit} स्ट्रीम चला सकते हैं। कृपया पहली स्ट्रीम खत्म होने का इंतज़ार करें।")
380
-
381
- data = callback_query.data
382
- if data == "plat_youtube":
383
- user_data[user_id]['platform'] = 'YouTube'
384
- text = "🔴 **YouTube Selected!**\n🔑 **Send YouTube Stream Key:**"
385
- elif data == "plat_rumble":
386
- user_data[user_id]['platform'] = 'Rumble'
387
- text = "🟢 **Rumble Selected!**\n🔑 **Send Rumble Stream Key (STATIC KEY):**"
388
- elif data == "plat_custom":
389
- user_data[user_id]['platform'] = 'Custom'
390
- text = "🔵 **Custom RTMP Selected!**\n🔗 **Send FULL RTMP URL + Key:**"
391
-
392
- user_data[user_id]['step'] = 'STREAM_KEY' if data != "plat_custom" else 'CUSTOM_URL'
393
- await callback_query.message.edit_text(text)
394
-
395
- @bot.on_callback_query(filters.regex("^loop_"))
396
- async def select_loop(client, callback_query: CallbackQuery):
397
- user_id = callback_query.message.chat.id
398
- init_user(user_id)
399
-
400
- if not user_data[user_id].get('authenticated'):
401
- return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**")
402
-
403
- # Changed int() to float() to support 0.5 hours (30 Mins)
404
- hours = float(callback_query.data.split("_")[1])
405
- user_data[user_id]['loop_hours'] = hours
406
- user_data[user_id]['step'] = 'ORIENTATION'
407
-
408
- await callback_query.message.edit_text("📐 **Select Stream Orientation:**\nवीडियो कैसे चलाना है?", reply_markup=orientation_keyboard())
409
-
410
- @bot.on_callback_query(filters.regex("^ori_"))
411
- async def select_orientation(client, callback_query: CallbackQuery):
412
- user_id = callback_query.message.chat.id
413
- init_user(user_id)
414
-
415
- if not user_data[user_id].get('authenticated'):
416
- return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**")
417
-
418
- data = callback_query.data
419
- if data == "ori_landscape":
420
- user_data[user_id]['orientation'] = 'landscape'
421
- user_data[user_id]['step'] = 'VIDEO_OR_URL'
422
- text = "🖥 **Landscape (16:9) Selected!**\n\n📥 **अब कोई Video File भेजें या डायरेक्ट Link (YouTube/Insta/FB) भेजें:**"
423
- await callback_query.message.edit_text(text)
424
- else:
425
- user_data[user_id]['orientation'] = 'portrait'
426
- user_data[user_id]['step'] = 'WAIT_IMAGE_CHOICE'
427
- text = "📱 **Portrait/Shorts (9:16) Selected!**\n\n🖼 **क्या आप वीडियो के ऊपर/नीचे खाली जगह पर कोई Custom Photo (Background) लगाना चाहते हैं?**"
428
- await callback_query.message.edit_text(text, reply_markup=image_choice_keyboard())
429
-
430
- @bot.on_callback_query(filters.regex("^img_"))
431
- async def select_image_choice(client, callback_query: CallbackQuery):
432
- user_id = callback_query.message.chat.id
433
- init_user(user_id)
434
-
435
- if not user_data[user_id].get('authenticated'):
436
- return await callback_query.message.edit_text("🔐 **Session Expired! Please send /start and enter password.**")
437
-
438
- data = callback_query.data
439
- if data == "img_yes":
440
- user_data[user_id]['step'] = 'AWAIT_IMAGE'
441
- text = "🖼 **Send your Custom Background Image (Photo)!**\n(यह फोटो आपके वीडियो के पीछे (Background) में दिखेगी)"
442
- else:
443
- user_data[user_id]['step'] = 'VIDEO_OR_URL'
444
- text = "⬛️ **No Image Selected (Black Background).**\n\n📥 **अब कोई Video File भेजें या डायरेक्ट Link (YouTube/Insta/FB) भेजें:**"
445
-
446
- await callback_query.message.edit_text(text)
447
-
448
- @bot.on_callback_query(filters.regex("^aud_"))
449
- async def select_audio_choice(client, callback_query: CallbackQuery):
450
- user_id = callback_query.message.chat.id
451
- data = callback_query.data
452
- if data == "aud_yes":
453
- user_data[user_id]['step'] = 'AWAIT_AUDIO'
454
- await callback_query.message.edit_text("🎵 **अपना Audio (MP3) या Video File भेजें जिससे आवाज़ निकालनी है!**")
455
- else:
456
- user_data[user_id]['step'] = 'SCHEDULE'
457
- await callback_query.message.edit_text("⏳ **स्ट्रीम अभी चालू करनी है या Schedule (शेड्यूल) करनी है?**", reply_markup=schedule_choice_keyboard())
458
-
459
- @bot.on_callback_query(filters.regex("^wm_"))
460
- async def select_watermark_choice(client, callback_query: CallbackQuery):
461
- user_id = callback_query.message.chat.id
462
- if callback_query.data == "wm_yes":
463
- user_data[user_id]['step'] = 'AWAIT_WATERMARK_TEXT'
464
- await callback_query.message.edit_text("✍️ **कृपया Watermark का Text भेजें:**\n(जैसे: Class PDF Telegram Pr Milengi)")
465
- else:
466
- user_data[user_id]['step'] = 'AUDIO_CHOICE'
467
- await callback_query.message.edit_text("🎵 **YouTube के लिए ऑडियो जरूरी है।**\nक्या आप अपना खुद का Audio (MP3) लगाना चाहते हैं?", reply_markup=audio_choice_keyboard())
468
-
469
- @bot.on_callback_query(filters.regex("^sch_"))
470
- async def select_schedule_choice(client, callback_query: CallbackQuery):
471
- user_id = callback_query.message.chat.id
472
- if callback_query.data == "sch_now":
473
- user_data[user_id]['step'] = 'READY'
474
- # 🚀 यह मैसेज डिलीट हो जाएगा ताकि स्ट्रीम खत्म होने के बाद स्क्रीनशॉट की तरह फालतू न दिखे
475
- await callback_query.message.delete()
476
- await process_stream_start(client, callback_query.message)
477
- else:
478
- user_data[user_id]['step'] = 'AWAIT_DATETIME'
479
- await callback_query.message.edit_text("📅 **Schedule Time भेजो!**\n\n**Format:** `DD/MM/YYYY hh:mm AM/PM`\n**Example:** `26/05/2026 12:40 PM`")
480
-
481
- @bot.on_message(~filters.command(["start", "stop", "new", "admin"]))
482
- async def handle_all_messages(client, message):
483
- user_id = message.chat.id
484
- init_user(user_id, message)
485
- step = user_data[user_id].get('step')
486
-
487
- # === Admin Reply to Delete ===
488
- if message.text == "/delete" and message.reply_to_message and user_id == ADMIN_ID:
489
- summary_id = message.reply_to_message.id
490
- if summary_id in sent_message_map:
491
- targets = sent_message_map[summary_id]
492
- deleted_count = 0
493
- for uid, mid in targets:
494
- try:
495
- await client.delete_messages(uid, mid)
496
- deleted_count += 1
497
- except: pass
498
- await message.reply(f"✅ **Message successfully deleted from {deleted_count} user(s) chat!**")
499
- del sent_message_map[summary_id]
500
- else:
501
- await message.reply("❌ **Could not find message data. Already deleted or too old.**")
502
- return
503
-
504
- # === Admin State Handling ===
505
- if user_id == ADMIN_ID:
506
- global admin_state, GLOBAL_PASSWORD, GLOBAL_LIMIT, recent_admin_messages, PASSWORD_LIMIT, PASSWORD_USED_BY
507
-
508
- if admin_state == "AWAIT_NEW_PASSWORD" and message.text:
509
- parts = message.text.strip().split()
510
- GLOBAL_PASSWORD = parts[0]
511
- # अगर एडमिन ने स्पेस देकर नंबर लिखा है, तो उसे लिमिट सेट करें, नहीं तो अनलिमिटेड
512
- PASSWORD_LIMIT = int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else 999999
513
- PASSWORD_USED_BY = {} # नया पासवर्ड आते ही पुरानी लिस्ट खाली कर दें
514
- admin_state = None
515
-
516
- limit_text = str(PASSWORD_LIMIT) if PASSWORD_LIMIT != 999999 else "Unlimited"
517
- await message.reply(f"✅ **Password Changed to:** `{GLOBAL_PASSWORD}`\n👥 **Max Users Allowed:** `{limit_text}`\n\nLogging everyone out...")
518
- for uid in user_data:
519
- if uid != ADMIN_ID:
520
- user_data[uid]['authenticated'] = False
521
- user_data[uid]['step'] = 'PASSWORD'
522
- try:
523
- await client.send_message(uid, "⚠️ **लॉगआउट अलर्ट! (Logout Alert)**\n\nएडमिन ने पासवर्ड बदल दिया है। कृपया `/start` दबाकर नया पासवर्ड डालें।")
524
- except: pass
525
- save_data() # डेटाबेस अपडेट करें
526
- return
527
-
528
- elif admin_state == "AWAIT_LIMIT" and message.text:
529
- text = message.text.strip()
530
- parts = text.split()
531
- if len(parts) == 1 and parts[0].isdigit():
532
- GLOBAL_LIMIT = int(parts[0])
533
- for uid in user_data:
534
- user_data[uid]['stream_limit'] = GLOBAL_LIMIT
535
- await message.reply(f"✅ **Global Stream Limit set to {GLOBAL_LIMIT} for everyone!**")
536
- elif len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit():
537
- target_id = int(parts[0])
538
- limit = int(parts[1])
539
- if target_id in user_data:
540
- user_data[target_id]['stream_limit'] = limit
541
- await message.reply(f"✅ **User `{target_id}` can now run {limit} streams at once!**")
542
- try: await client.send_message(target_id, f"🎉 **Admin Alert:** Your concurrent stream limit has been increased to **{limit}**!")
543
- except: pass
544
- else:
545
- await message.reply("❌ **User not found in bot database!**")
546
- else:
547
- await message.reply("❌ **Invalid format!** Send something like `2` or `123456789 2`")
548
- admin_state = None
549
- save_data() # डेटाबेस अपडेट करें
550
- return
551
-
552
- elif admin_state == "AWAIT_BROADCAST":
553
- text_to_check = message.text or message.caption
554
- is_personal = False
555
- target_id = None
556
- content = None
557
-
558
- if text_to_check:
559
- parts = text_to_check.split(" ", 1)
560
- if len(parts) > 1 and parts[0].isdigit() and len(parts[0]) > 5:
561
- is_personal = True
562
- target_id = int(parts[0])
563
- content = parts[1]
564
-
565
- targets =[]
566
-
567
- if is_personal:
568
- try:
569
- if message.text:
570
- msg = await client.send_message(target_id, content)
571
- else:
572
- msg = await message.copy(target_id, caption=content)
573
- targets.append((target_id, msg.id))
574
- summary = await message.reply(f"✅ **Message sent privately to `{target_id}`!**\n_Reply to this message with `/delete` to remove it from their chat._")
575
- except Exception as e:
576
- await message.reply(f"❌ **Failed to send.** Target might have blocked the bot.")
577
- admin_state = None
578
- return
579
- else:
580
- success = 0
581
- for uid in user_data:
582
- if uid != ADMIN_ID:
583
- try:
584
- msg = await message.copy(uid)
585
- targets.append((uid, msg.id))
586
- success += 1
587
- except: pass
588
- summary = await message.reply(f"✅ **Broadcast Sent successfully to {success} users!**\n_Reply to this message with `/delete` to remove it from all users._")
589
-
590
- if targets:
591
- sent_message_map[summary.id] = targets
592
- recent_admin_messages.append(targets)
593
- if len(recent_admin_messages) > 50:
594
- recent_admin_messages.pop(0)
595
- admin_state = None
596
- return
597
-
598
- # === Normal User Handling ===
599
- if not user_data[user_id]['authenticated'] and step != 'PASSWORD':
600
- user_data[user_id]['step'] = 'PASSWORD'
601
- return await message.reply("🔐 **Session Expired! Please enter password:**")
602
-
603
- # यहाँ हमने नए स्टेप्स (VIDEO_OR_URL और AWAIT_AUDIO) को Add कर दिया है
604
- if not message.text and step not in['VIDEO_OR_URL', 'AWAIT_IMAGE', 'AWAIT_AUDIO']:
605
- return
606
-
607
- if step == 'PASSWORD':
608
- if message.text == GLOBAL_PASSWORD:
609
- # चेक करें कि यूज़र पहले से लिस्ट में है या नहीं
610
- if user_id not in PASSWORD_USED_BY:
611
- if len(PASSWORD_USED_BY) >= PASSWORD_LIMIT:
612
- return await message.reply("🚫 **Password Limit Reached!**\nयह पासवर्ड अपनी लिमिट पूरी कर चुका है। कृपया एडमिन से नया पासवर्ड लें।")
613
- # अगर लिमिट बची है, तो यूज़र को एनालिसिस लिस्ट में जोड़ दें
614
- PASSWORD_USED_BY[user_id] = user_data[user_id]['name']
615
-
616
- user_data[user_id]['authenticated'] = True
617
- user_data[user_id]['step'] = 'PLATFORM'
618
- save_data() # यूज़र लॉगिन सेव करें ताकि रीस्टार्ट होने पर लॉगिन न हटे
619
- await message.reply("**✅ Password Correct!**\n👇 **Select platform:**", reply_markup=platform_keyboard())
620
- else:
621
- await message.reply("❌ **Wrong Password! Try again.**")
622
-
623
- elif step == 'STREAM_KEY':
624
- key = message.text
625
- platform = user_data[user_id]['platform']
626
- if platform == 'YouTube':
627
- user_data[user_id]['rtmp_url'] = f"rtmp://a.rtmp.youtube.com/live2/{key}"
628
- elif platform == 'Rumble':
629
- user_data[user_id]['rtmp_url'] = f"rtmp://rtmp.rumble.com/live/{key}"
630
- user_data[user_id]['step'] = 'LOOP_CHOICE'
631
- await message.reply(f"✅ **Key Saved!**\n\n⏱ **How long do you want to stream?**", reply_markup=loop_keyboard())
632
-
633
- elif step == 'CUSTOM_URL':
634
- user_data[user_id]['rtmp_url'] = message.text
635
- user_data[user_id]['step'] = 'LOOP_CHOICE'
636
- await message.reply("✅ **Custom URL Saved!**\n\n⏱ **How long do you want to stream?**", reply_markup=loop_keyboard())
637
-
638
- elif step == 'AWAIT_IMAGE':
639
- if message.photo or message.document:
640
- msg = await message.reply("⏳ **Saving Image...**")
641
- image_path = await message.download()
642
- user_data[user_id]['custom_image_path'] = image_path
643
- user_data[user_id]['step'] = 'VIDEO_OR_URL'
644
- await msg.edit_text("✅ **Background Image Saved!**\n\n📥 **अब कोई Video File भेजें या डायरेक्ट Link (YouTube/Insta/FB) भेजें:**")
645
- else:
646
- await message.reply("⚠️ **Please send a valid Photo!**")
647
-
648
- elif step == 'VIDEO_OR_URL':
649
- user_data[user_id]['is_image_only'] = False
650
- if message.video or message.document:
651
- msg = await message.reply("⏳ **Downloading Video...**")
652
- start_time = time.time()
653
- last_update = [start_time]
654
- file_path = await message.download(progress=progress_bar, progress_args=(msg, start_time, last_update))
655
- user_data[user_id]['file_path'] = file_path
656
- await msg.delete()
657
- elif message.photo:
658
- msg = await message.reply("⏳ **Downloading Image...**")
659
- file_path = await message.download()
660
- user_data[user_id]['file_path'] = file_path
661
- user_data[user_id]['is_image_only'] = True
662
- await msg.delete()
663
- elif message.text and "t.me/c/" in message.text:
664
- # Telegram Private Channel Link Logic
665
- if not USER_SESSION:
666
- return await message.reply("❌ **String Session सेट नहीं है! बॉट के कोड में USER_SESSION डालें।**")
667
-
668
- msg = await message.reply("🔍 **प्राइवेट चैनल में वीडियो ढूँढा जा रहा है... (आस-पास के मैसेज चेक कर रहा हूँ)**")
669
- try:
670
- link_parts = message.text.strip().split('/')
671
- chat_id = int("-100" + link_parts[4])
672
- msg_id = int(link_parts[5])
673
-
674
- if not user_app.is_connected:
675
- await user_app.start()
676
-
677
- target_msg = None
678
- # आगे-पीछे के 20 मैसेज चेक करेगा कि Video किसमें है
679
- messages = await user_app.get_messages(chat_id, range(max(1, msg_id - 10), msg_id + 11))
680
- for m in messages:
681
- if m and not m.empty and (m.video or m.document):
682
- target_msg = m
683
- break
684
-
685
- if not target_msg:
686
- return await msg.edit_text("❌ **इस लिंक के आस-पास कोई Video नहीं मिला!**")
687
-
688
- await msg.edit_text("⏳ **Private Video Downloading...**")
689
- start_time = time.time()
690
- last_update = [start_time]
691
- file_path = await user_app.download_media(target_msg, progress=progress_bar, progress_args=(msg, start_time, last_update))
692
-
693
- user_data[user_id]['file_path'] = file_path
694
- await msg.delete()
695
- except Exception as e:
696
- return await msg.edit_text(f"❌ **Private Video Error:** {str(e)}")
697
-
698
- elif message.text and ("http://" in message.text or "https://" in message.text):
699
- user_data[user_id]['video_url'] = message.text.strip()
700
- else:
701
- return await message.reply("⚠️ **कृपया सही Video, Photo या Link भेजें!**")
702
-
703
- user_data[user_id]['step'] = 'WATERMARK_CHOICE'
704
- if user_data[user_id]['is_image_only']:
705
- await message.reply("✨ **क्या आप फोटो पर चलता हुआ (Moving) नाम/वाटरमार्क लगाना चाहते हैं?**\n(यह YouTube को स्पैम से बचाएगा)", reply_markup=watermark_choice_keyboard())
706
- else:
707
- await message.reply("✨ **क्या आप वीडियो पर Watermark (जैसे 'Class PDF Telegram...') लगाना चाहते हैं?**\n(यह हर 1 मिनट में दिखाई देगा)", reply_markup=watermark_choice_keyboard())
708
-
709
- elif step == 'AWAIT_WATERMARK_TEXT':
710
- user_data[user_id]['watermark_text'] = message.text.strip()
711
- user_data[user_id]['step'] = 'AUDIO_CHOICE'
712
- await message.reply(f"✅ **वाटरमार्क '{message.text.strip()}' सेव हो गया!**\n\n🎵 **YouTube के लिए ऑडियो जरूरी है।**\nक्या आप अपना खुद का Audio (MP3) फाइल लगाना चाहते हैं?", reply_markup=audio_choice_keyboard())
713
-
714
- elif step == 'AWAIT_AUDIO':
715
- if message.audio or message.video or message.document:
716
- msg = await message.reply("⏳ **Downloading Audio...**")
717
- audio_path = await message.download()
718
- user_data[user_id]['custom_audio_path'] = audio_path
719
- user_data[user_id]['step'] = 'SCHEDULE'
720
- await msg.edit_text("✅ **Audio Saved!**\n\n⏳ **स्ट्रीम अभी चालू करनी है या Schedule (शेड्यूल) करनी है?**", reply_markup=schedule_choice_keyboard())
721
- else:
722
- await message.reply("⚠️ **कृपया सही Audio या Video File भेजें!**")
723
-
724
- elif step == 'AWAIT_DATETIME':
725
- try:
726
- dt_obj = datetime.strptime(message.text.strip(), "%d/%m/%Y %I:%M %p")
727
- now_ist = get_ist_time().replace(tzinfo=None)
728
- if dt_obj <= now_ist:
729
- return await message.reply("⚠️ **समय (Time) भविष्य का होना चाहिए!** कृपया फिर से भेजें।\nExample: `26/05/2026 12:40 PM`")
730
-
731
- user_data[user_id]['schedule_time'] = dt_obj
732
- user_data[user_id]['step'] = 'READY'
733
-
734
- delay = (dt_obj - now_ist).total_seconds()
735
- await message.reply(f"✅ **Stream Scheduled!**\nयह वीडियो आटोमेटिकली `{message.text.strip()}` पर चालू हो जाएगा।")
736
-
737
- asyncio.create_task(scheduled_stream_starter(client, message, delay))
738
- except Exception as e:
739
- await message.reply("❌ **Invalid Format!**\nकृपया इस फॉर्मेट में भेजें: `26/05/2026 12:40 PM`")
740
-
741
- async def scheduled_stream_starter(client, message, delay):
742
- await asyncio.sleep(delay)
743
- await process_stream_start(client, message)
744
-
745
- async def process_stream_start(client, message):
746
- await handle_video(client, message)
747
-
748
- def get_direct_url(url):
749
- ydl_opts = {'format': 'best', 'quiet': True, 'noplaylist': True}
750
- try:
751
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
752
- info = ydl.extract_info(url, download=False)
753
- return info.get('url', url)
754
- except:
755
- return url
756
-
757
- async def handle_video(client, message):
758
- user_id = message.chat.id
759
-
760
- user_data[user_id]['manual_stop'] = False
761
- msg = await client.send_message(user_id, "⏳ **Preparing Stream Environment...**")
762
-
763
- file_path = user_data[user_id].get('file_path')
764
- video_url = user_data[user_id].get('video_url')
765
- custom_audio = user_data[user_id].get('custom_audio_path')
766
-
767
- input_source = file_path
768
- if video_url:
769
- await msg.edit_text("🔗 **Extracting direct stream link (YouTube/Facebook/Rumble etc.)...**")
770
- input_source = await asyncio.to_thread(get_direct_url, video_url)
771
- if not input_source:
772
- return await msg.edit_text("❌ **Failed to extract link!**")
773
-
774
- platform = user_data[user_id].get('platform', 'Custom')
775
- loop_hours = user_data[user_id].get('loop_hours', 0)
776
- rtmp_url = user_data[user_id]['rtmp_url']
777
- orientation = user_data[user_id].get('orientation', 'landscape')
778
- image_path = user_data[user_id].get('custom_image_path')
779
-
780
- user_data[user_id]['total_streams'] += 1
781
- user_data[user_id]['last_stream'] = get_ist_time()
782
-
783
- import random # Random time जनरेट करने के लिए
784
-
785
- vid_duration = get_video_duration(file_path) if file_path else 3600
786
-
787
- # -----------------------------------------------------
788
- # ANTI-BOT RANDOMIZER: हर बार समय को थोड़ा आगे-पीछे करना
789
- # -----------------------------------------------------
790
- if loop_hours > 0:
791
- base_sec = loop_hours * 3600
792
- # 10% समय रैंडमली कम या ज्यादा करें (जैसे 3 घंटे में +/- 18 मिनट, 30 मिनट में +/- 3 मिनट)
793
- random_offset = random.randint(int(-0.1 * base_sec), int(0.1 * base_sec))
794
- actual_stream_seconds = base_sec + random_offset
795
- total_stream_time = actual_stream_seconds
796
- else:
797
- total_stream_time = vid_duration
798
- actual_stream_seconds = vid_duration
799
-
800
- is_image_only = user_data[user_id].get('is_image_only', False)
801
-
802
- # --- FFMPEG Command Setup ---
803
- command = ["ffmpeg", "-re"]
804
-
805
- if is_image_only:
806
- # Image Only Streaming Logic (YouTube Standard 30 FPS)
807
- command.extend(["-loop", "1", "-framerate", "30", "-i", input_source])
808
-
809
- if custom_audio:
810
- command.extend(["-stream_loop", "-1", "-i", custom_audio])
811
- command.extend(["-map", "0:v", "-map", "1:a:0?"])
812
- else:
813
- # YouTube rejects streams without audio, so we add silent dummy audio
814
- command.extend(["-f", "lavfi", "-i", "anullsrc=channel_layout=stereo:sample_rate=44100"])
815
- command.extend(["-map", "0:v", "-map", "1:a"])
816
-
817
- # -----------------------------------------------------
818
- # NEW: Aspect Ratio Fix for Image (Portrait vs Landscape)
819
- # -----------------------------------------------------
820
- if orientation == 'portrait':
821
- # Makes 16:9 photo into 9:16 (Adds black bars on top/bottom)
822
- vf_filter = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black"
823
- else:
824
- # Ensures normal photos fit 16:9 screen
825
- vf_filter = "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=black"
826
-
827
- # --- NEW: SLOW SMOOTH MOVING WATERMARK ---
828
- wm_text = user_data[user_id].get('watermark_text')
829
- if wm_text:
830
- safe_text = wm_text.replace("'", "\\'").replace(":", "\\:")
831
- # sin(t/10) और cos(t/15) का मतलब है कि यह बहुत धीरे-धीरे (Slowly) पूरी स्क्रीन पर गोल घूमेगा
832
- 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)"
833
- vf_filter += drawtext
834
-
835
- command.extend(["-vf", vf_filter])
836
- # -----------------------------------------------------
837
-
838
- # Encode with strict YouTube standards (30 FPS, 2-sec Keyframe, CBR)
839
- command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"])
840
- # YouTube Keyframe Fix: 30fps के हिसाब से हर 2 सेकंड में कीफ्रेम (-g 60)
841
- command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"])
842
- # YouTube Bitrate Fix: Strict CBR (Constant Bitrate) with Dummy Padding
843
- command.extend(["-b:v", "4000k", "-maxrate", "4000k", "-minrate", "4000k", "-bufsize", "8000k", "-nal-hrd", "cbr"])
844
- command.extend(["-c:a", "aac", "-b:a", "128k"])
845
-
846
- else:
847
- # Normal Video Logic
848
- if loop_hours > 0: command.extend(["-stream_loop", "-1"])
849
- command.extend(["-i", input_source])
850
-
851
- current_idx = 1; idx_aud = 0
852
- if custom_audio:
853
- command.extend(["-stream_loop", "-1", "-i", custom_audio])
854
- idx_aud = current_idx; current_idx += 1
855
-
856
- idx_img = -1
857
- if orientation == 'portrait' and image_path:
858
- command.extend(["-loop", "1", "-i", image_path])
859
- idx_img = current_idx; current_idx += 1
860
-
861
- if orientation == 'portrait':
862
- if image_path:
863
- 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]"
864
- command.extend(["-filter_complex", vf_complex, "-map", "[outv]"])
865
- else:
866
- vf_filter = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black"
867
- command.extend(["-vf", vf_filter, "-map", "0:v"])
868
-
869
- # YouTube Strict Bitrate & Keyframe (GOP) Fix for Portrait Video
870
- command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"])
871
- command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"])
872
- command.extend(["-b:v", "6000k", "-maxrate", "6000k", "-minrate", "6000k", "-bufsize", "12000k", "-nal-hrd", "cbr"])
873
- else:
874
- # YouTube Strict Bitrate & Keyframe (GOP) Fix for Landscape Video
875
- wm_text = user_data[user_id].get('watermark_text')
876
-
877
- if wm_text:
878
- safe_text = wm_text.replace("'", "\\'").replace(":", "\\:")
879
- # Custom Timing Logic: 1st min, 3rd min, then every 5 mins (each for 15 sec)
880
- timing_logic = "between(t\\,60\\,75)+between(t\\,180\\,195)+(gte(t\\,480)*lt(mod(t-480\\,300)\\,15))"
881
-
882
- 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}'"
883
- command.extend(["-vf", vf_filter, "-map", "0:v"])
884
- else:
885
- command.extend(["-map", "0:v"])
886
-
887
- command.extend(["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p"])
888
- command.extend(["-r", "30", "-g", "60", "-sc_threshold", "0"])
889
- command.extend(["-b:v", "6000k", "-maxrate", "6000k", "-minrate", "6000k", "-bufsize", "12000k", "-nal-hrd", "cbr"])
890
-
891
- if custom_audio:
892
- command.extend(["-map", f"{idx_aud}:a:0?", "-c:a", "aac", "-b:a", "128k", "-shortest"])
893
- else:
894
- command.extend(["-map", "0:a?", "-c:a", "aac", "-b:a", "128k"])
895
-
896
- # Apply time limit for BOTH Image and Video
897
- if loop_hours > 0:
898
- seconds_str = str(int(actual_stream_seconds)) # असली रैंडम सेकंड्स
899
- if not is_image_only: command.extend(["-fflags", "+genpts"])
900
- command.extend(["-t", seconds_str])
901
-
902
- command.extend(["-f", "flv", rtmp_url])
903
-
904
- st_type = "🖼 Image" if is_image_only else "🎥 Video"
905
- 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")
906
-
907
- process_obj = subprocess.Popen(command)
908
-
909
- # Store Advanced Details for Admin Panel (Now saves actual randomized end time)
910
- start_t = get_ist_time()
911
- end_t = start_t + timedelta(seconds=actual_stream_seconds) if loop_hours > 0 else "Auto (Depends on Video)"
912
-
913
- user_data[user_id]['processes'].append({
914
- 'process': process_obj,
915
- 'start_time': start_t,
916
- 'end_time': end_t,
917
- 'loop_hours': loop_hours,
918
- 'is_image': is_image_only
919
- })
920
-
921
- 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))
922
-
923
- async def monitor_stream(client, chat_id, process, file_path, msg_id, loop_hours, platform, total_time, image_path, custom_audio):
924
- start_time_ist = get_ist_time()
925
- last_ui_update = time.time()
926
- mode_text = f"🔁 {loop_hours} Hours Loop" if loop_hours > 0 else "▶️ 1-Time Play"
927
-
928
- while process.poll() is None:
929
- await asyncio.sleep(5)
930
- now_time = time.time()
931
- if total_time > 0 and (now_time - last_ui_update > 60):
932
- last_ui_update = now_time
933
- elapsed = (get_ist_time() - start_time_ist).total_seconds()
934
- rem_sec = total_time - elapsed
935
-
936
- if rem_sec > 0:
937
- h = int(rem_sec // 3600)
938
- m = int((rem_sec % 3600) // 60)
939
- time_left_str = f"{h}h {m}m" if h > 0 else f"{m}m"
940
- try:
941
- await client.edit_message_text(
942
- chat_id, msg_id,
943
- f"🟢 **Live is RUNNING on {platform}!**\n"
944
- f"⏱ **Loop:** `{mode_text}`\n"
945
- f"⏳ **Time Left:** `{time_left_str}`\n\n"
946
- f"🔹 Send /stop to end manually."
947
- )
948
- except: pass
949
-
950
- end_time_ist = get_ist_time()
951
- duration = end_time_ist - start_time_ist
952
-
953
- try: await client.delete_messages(chat_id, msg_id)
954
- except: pass
955
-
956
- # Server Storage साफ़ करना (Delete file to save memory)
957
- try:
958
- if file_path and os.path.exists(file_path): os.remove(file_path)
959
- if image_path and os.path.exists(image_path): os.remove(image_path)
960
- if custom_audio and os.path.exists(custom_audio): os.remove(custom_audio)
961
- except: pass
962
-
963
- start_str = start_time_ist.strftime("%I:%M %p")
964
- end_str = end_time_ist.strftime("%I:%M %p")
965
- dur_str = str(duration).split('.')[0]
966
-
967
- is_manual = user_data.get(chat_id, {}).get('manual_stop', False)
968
- status_text = "🛑 **Live Stream Stopped Manually!**" if is_manual else "🔴 **Live Stream Ended Automatically!**"
969
-
970
- report_msg = (
971
- f"{status_text}\n\n"
972
- f"📊 **STREAM SUMMARY:**\n"
973
- f"🔸 **Platform:** `{platform}`\n"
974
- f"🟢 **Started at:** `{start_str}`\n"
975
- f"🔴 **Ended at:** `{end_str}`\n"
976
- f"⏱ **Total Runtime:** `{dur_str}`\n\n"
977
- f"🗑 __Video, Audio & Image deleted safely.__"
978
- )
979
-
980
- await client.send_message(chat_id, report_msg)
981
-
982
- if __name__ == "__main__":
983
- load_data() # बॉट स्टार्ट होते ही पुरानी सेटिंग्स और यूज़र्स वापस लोड करें
984
- Thread(target=run_flask).start()
985
- bot.run()