pmrony commited on
Commit
0b905fb
·
verified ·
1 Parent(s): 371c6fd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -38
app.py CHANGED
@@ -5,7 +5,7 @@ import requests
5
  import asyncio
6
  import re
7
  import urllib3
8
- from flask import Flask, jsonify, make_response, request
9
  from supabase import create_client
10
  from pyrogram import Client, filters, enums, idle
11
  from pyrogram.errors import SessionPasswordNeeded, PhoneCodeInvalid, PhoneCodeExpired, UserDeactivated, SessionRevoked, AuthKeyUnregistered, FloodWait
@@ -19,9 +19,12 @@ API_ID = int(os.environ.get("API_ID", 0))
19
  API_HASH = os.environ.get("API_HASH")
20
  SUPABASE_URL = os.environ.get("SUPABASE_URL")
21
  SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
22
- BYSE_API_KEY = os.environ.get("BYSE_API_KEY")
23
 
24
  PREMIUM_CHANNEL_ID = -1002825744390
 
 
 
 
25
 
26
  # WebApp URL (index.html)
27
  WEB_APP_URL = "https://rony90790.github.io/Forward-bot/index.html"
@@ -47,6 +50,84 @@ bot = Client("file_unlocker_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BO
47
  async def db_query(func):
48
  return await asyncio.to_thread(func)
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  # ================= FLASK API ROUTES =================
51
  @app.route('/')
52
  def index():
@@ -312,12 +393,10 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
312
  else:
313
  await status_msg.edit_text(f"⏳ <b>Cloning started!</b>\nFetching video list from <code>{source_id}</code>...\n<i>This might take a few minutes if the group has many videos.</i>", parse_mode=enums.ParseMode.HTML)
314
 
315
- # === Auto Reconnect & Retry loop for Video Scanning ===
316
  video_ids = []
317
  retries = 5
318
  while retries > 0:
319
  try:
320
- # যদি বট ক্লায়েন্ট কোনো কারণে ডিসকানেক্ট হয়, রিকানেক্ট করবে
321
  if not client.is_connected:
322
  try: await client.connect()
323
  except: pass
@@ -328,18 +407,16 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
328
  video_ids.append(msg.id)
329
  if len(video_ids) % 200 == 0:
330
  await asyncio.sleep(0.1)
331
- break # সফল হলে লুপ থেকে বের হবে
332
  except Exception as e:
333
  err_msg = str(e).lower()
334
- # যদি ডিসকানেক্ট বা কানেকশন জনিত সমস্যা হয়
335
  if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "reset" in err_msg:
336
  retries -= 1
337
- video_ids = [] # লিস্ট রিসেট করা হলো যাতে কোনো ভিডিও মিস না যায়
338
  await status_msg.edit_text(f"⚠️ <b>Network issue detected!</b>\nRetrying in 10 seconds... (Attempts left: {retries})\nError: <code>{e}</code>", parse_mode=enums.ParseMode.HTML)
339
  await asyncio.sleep(10)
340
  else:
341
- raise e # অন্য কোনো পারমিশন বা বড় এরর হলে কাজ থামিয়ে দেবে
342
- # =======================================================
343
 
344
  if not video_ids:
345
  if last_copied_id:
@@ -347,7 +424,7 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
347
  else:
348
  return await status_msg.edit_text("❌ <b>No videos found in the source group!</b>\n(Make sure the bot is an admin with read history permission in that group).", parse_mode=enums.ParseMode.HTML)
349
 
350
- video_ids.reverse() # Oldest to newest
351
  total = len(video_ids)
352
 
353
  if last_copied_id:
@@ -362,7 +439,6 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
362
  copy_success = False
363
  copy_retries = 3
364
 
365
- # === Video Sending Auto Reconnect & Retry ===
366
  while copy_retries > 0:
367
  try:
368
  if not client.is_connected:
@@ -373,21 +449,19 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
373
  success += 1
374
  await save_progress(source_id, dest_id, msg_id)
375
  copy_success = True
376
- break # কপি সফল হলে রিট্রাই লুপ থেকে বের হবে
377
  except FloodWait as e:
378
- # লিমিট এররে রিট্রাই কমানো হবে না, শুধু ওয়েট করবে
379
  await asyncio.sleep(e.value + 2)
380
  except Exception as e:
381
  err_msg = str(e).lower()
382
  if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "reset" in err_msg:
383
  copy_retries -= 1
384
- await asyncio.sleep(5) # ৫ সেকেন্ড পর আবার চেষ্টা করবে
385
  else:
386
- break # অন্য কোনো এরর (যেমন: ভিডিও ডিলিট করা) হলে লুপ স্কিপ করবে
387
 
388
  if not copy_success:
389
  failed += 1
390
- # ============================================
391
 
392
  if index % 20 == 0 or index == total:
393
  try:
@@ -546,7 +620,7 @@ async def handle_media_upload(client, message):
546
  original_file = await message.download(progress=download_progress)
547
  clean_upload_file = original_file
548
 
549
- # ১. ওয়াটারমার্ক (Watermark) করা - Balanced (Superfast + CRF 23)
550
  if media_type == "video" and not is_large_video:
551
  await status_msg.edit_text("⏳ Watermarking video... (HD + Superfast Processing)")
552
  watermarked_file = f"{original_file}_wm.mp4"
@@ -561,35 +635,31 @@ async def handle_media_upload(client, message):
561
  if process.returncode == 0 and os.path.exists(watermarked_file):
562
  clean_upload_file = watermarked_file
563
 
564
- # ২. byse.sx ার্ারে HD / Clean ভিডিও আপলোড করা
565
  if media_type == "video":
566
- await status_msg.edit_text("⏳ Uploading Clean HD video to byse.sx server...")
567
- api_endpoint = "https://api.byse.sx/upload/server"
568
- loop = asyncio.get_event_loop()
569
- response = await loop.run_in_executor(None, lambda: requests.get(api_endpoint, params={'key': BYSE_API_KEY}, timeout=30))
570
- result = response.json()
571
 
572
- if result.get('status') == 200:
573
- upload_res = await loop.run_in_executor(None, upload_file_sync, result.get('result'), clean_upload_file, BYSE_API_KEY)
574
- if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
575
- file_status = upload_res['files'][0].get('status', '')
576
- if "not allowed" in str(file_status).lower():
577
- await status_msg.edit_text(f"❌ byse.sx rejected the file: <code>{file_status}</code>", parse_mode=enums.ParseMode.HTML)
578
- return
579
- file_code = upload_res['files'][0].get('filecode')
580
- if file_code: embed_link = f"https://bysesayeveum.com/e/{file_code}"
581
 
582
- if not embed_link:
583
- await status_msg.edit_text("❌ Uploaded to byse.sx but Embed Link not found.")
584
- return
 
585
 
586
  if is_large_video:
587
- admin_cap = f"✅ <b>Success! (Large Video)</b>\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>\n\n📌 <i>Broadcast skipped due to large file size.</i>"
 
588
  await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
589
  await status_msg.delete()
590
  return
591
 
592
- # ৩. টেলিগ্রামে পাঠানোর জন্য ভিডিও ব্লার করা
593
  telegram_file = clean_upload_file
594
  if is_blur and not is_large_video:
595
  await status_msg.edit_text(f"⏳ Applying {blur_percent}% blur for Telegram broadcast...")
@@ -624,7 +694,13 @@ async def handle_media_upload(client, message):
624
  caption_text = f"{clean_caption}\n\n👇 <i>Click the button below to open Bot!</i>" if clean_caption else f"🔥 <b>New Premium Viral Content!</b> 🔞\n\n🎬 <b>Watch HD Video Here:</b>\n👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n👇 <i>Click the button below to open Bot!</i>"
625
 
626
  group_markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]])
627
- admin_cap = f"✅ <b>Success!</b> Media is broadcasting...\n\n🔗 <b>Embed Link (Clean HD):</b>\n<code>{embed_link or 'N/A'}</code>"
 
 
 
 
 
 
628
 
629
  thumb_path = None
630
  if media_type == "video":
 
5
  import asyncio
6
  import re
7
  import urllib3
8
+ from flask import Flask, jsonify, make_response, request, Response
9
  from supabase import create_client
10
  from pyrogram import Client, filters, enums, idle
11
  from pyrogram.errors import SessionPasswordNeeded, PhoneCodeInvalid, PhoneCodeExpired, UserDeactivated, SessionRevoked, AuthKeyUnregistered, FloodWait
 
19
  API_HASH = os.environ.get("API_HASH")
20
  SUPABASE_URL = os.environ.get("SUPABASE_URL")
21
  SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
 
22
 
23
  PREMIUM_CHANNEL_ID = -1002825744390
24
+ STORAGE_CHANNEL_ID = -1002825744390 # যে চ্যানেলে ভিডিও স্টোর হবে
25
+
26
+ # আপনার Hugging Face স্পেসের ডিরেক্ট ইউআরএল (টোকেন জেনারেট করার জন্য)
27
+ BACKEND_URL = os.environ.get("BACKEND_URL", "https://pmrony-forwardbot.hf.space")
28
 
29
  # WebApp URL (index.html)
30
  WEB_APP_URL = "https://rony90790.github.io/Forward-bot/index.html"
 
50
  async def db_query(func):
51
  return await asyncio.to_thread(func)
52
 
53
+ # ==================== CUSTOM VIDEO STREAMING ENGINE ====================
54
+ def get_file_stream(message_id):
55
+ """টেলিগ্রামের স্টোরেজ চ্যানেল থেকে রিয়েল-টাইমে ডাটা স্ট্রিম করার জেনারেটর"""
56
+ async def stream_generator():
57
+ try:
58
+ msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
59
+ if not msg or not msg.video:
60
+ return
61
+
62
+ # chunk_size অনুযায়ী ডাটা রিয়েল-টাইমে রিড করে পাঠানো হবে
63
+ async for chunk in bot.stream_media(msg.video):
64
+ yield chunk
65
+ except Exception as e:
66
+ print(f"Error in stream_generator: {e}")
67
+
68
+ # Async জেনারেটরকে Flask-এর সাথে সিঙ্ক করার জন্য ইভেন্ট লুপ ফিক্স
69
+ loop = asyncio.new_event_loop()
70
+ def run():
71
+ asyncio.set_event_loop(loop)
72
+ gen = stream_generator()
73
+ while True:
74
+ try:
75
+ chunk = loop.run_until_complete(gen.__anext__())
76
+ yield chunk
77
+ except StopAsyncIteration:
78
+ break
79
+ except Exception as e:
80
+ print(f"Sync wrapper error: {e}")
81
+ break
82
+ loop.close()
83
+
84
+ return run()
85
+
86
+ @app.route('/stream/<int:message_id>')
87
+ def stream_video(message_id):
88
+ """অনলাইনে প্লেয়ারে ভিডিও দেখার লিংক"""
89
+ try:
90
+ async def get_media_info():
91
+ msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
92
+ if msg and msg.video:
93
+ return msg.video.file_size, msg.video.file_name, msg.video.mime_type
94
+ return None, None, None
95
+
96
+ file_size, file_name, mime_type = run_async(get_media_info())
97
+ if not file_size:
98
+ return "File not found or invalid message", 404
99
+
100
+ response = make_response(Response(get_file_stream(message_id), mimetype=mime_type or "video/mp4"))
101
+ response.headers['Content-Length'] = file_size
102
+ response.headers['Content-Type'] = mime_type or "video/mp4"
103
+ response.headers['Accept-Ranges'] = 'bytes'
104
+ response.headers['Content-Disposition'] = f'inline; filename="{file_name or "video.mp4"}"'
105
+ return response
106
+ except Exception as e:
107
+ return f"Error: {e}", 500
108
+
109
+ @app.route('/download/<int:message_id>')
110
+ def download_video(message_id):
111
+ """সরাসরি ওয়ান-ক্লিকে ডাউনলোড করার লিংক"""
112
+ try:
113
+ async def get_media_info():
114
+ msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
115
+ if msg and msg.video:
116
+ return msg.video.file_size, msg.video.file_name, msg.video.mime_type
117
+ return None, None, None
118
+
119
+ file_size, file_name, mime_type = run_async(get_media_info())
120
+ if not file_size:
121
+ return "File not found or invalid message", 404
122
+
123
+ response = make_response(Response(get_file_stream(message_id), mimetype=mime_type or "application/octet-stream"))
124
+ response.headers['Content-Length'] = file_size
125
+ response.headers['Content-Disposition'] = f'attachment; filename="{file_name or "video.mp4"}"'
126
+ return response
127
+ except Exception as e:
128
+ return f"Error: {e}", 500
129
+ # =======================================================================
130
+
131
  # ================= FLASK API ROUTES =================
132
  @app.route('/')
133
  def index():
 
393
  else:
394
  await status_msg.edit_text(f"⏳ <b>Cloning started!</b>\nFetching video list from <code>{source_id}</code>...\n<i>This might take a few minutes if the group has many videos.</i>", parse_mode=enums.ParseMode.HTML)
395
 
 
396
  video_ids = []
397
  retries = 5
398
  while retries > 0:
399
  try:
 
400
  if not client.is_connected:
401
  try: await client.connect()
402
  except: pass
 
407
  video_ids.append(msg.id)
408
  if len(video_ids) % 200 == 0:
409
  await asyncio.sleep(0.1)
410
+ break
411
  except Exception as e:
412
  err_msg = str(e).lower()
 
413
  if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "reset" in err_msg:
414
  retries -= 1
415
+ video_ids = []
416
  await status_msg.edit_text(f"⚠️ <b>Network issue detected!</b>\nRetrying in 10 seconds... (Attempts left: {retries})\nError: <code>{e}</code>", parse_mode=enums.ParseMode.HTML)
417
  await asyncio.sleep(10)
418
  else:
419
+ raise e
 
420
 
421
  if not video_ids:
422
  if last_copied_id:
 
424
  else:
425
  return await status_msg.edit_text("❌ <b>No videos found in the source group!</b>\n(Make sure the bot is an admin with read history permission in that group).", parse_mode=enums.ParseMode.HTML)
426
 
427
+ video_ids.reverse()
428
  total = len(video_ids)
429
 
430
  if last_copied_id:
 
439
  copy_success = False
440
  copy_retries = 3
441
 
 
442
  while copy_retries > 0:
443
  try:
444
  if not client.is_connected:
 
449
  success += 1
450
  await save_progress(source_id, dest_id, msg_id)
451
  copy_success = True
452
+ break
453
  except FloodWait as e:
 
454
  await asyncio.sleep(e.value + 2)
455
  except Exception as e:
456
  err_msg = str(e).lower()
457
  if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "reset" in err_msg:
458
  copy_retries -= 1
459
+ await asyncio.sleep(5)
460
  else:
461
+ break
462
 
463
  if not copy_success:
464
  failed += 1
 
465
 
466
  if index % 20 == 0 or index == total:
467
  try:
 
620
  original_file = await message.download(progress=download_progress)
621
  clean_upload_file = original_file
622
 
623
+ # ১. ওয়াটারমার্ক (Watermark) করা - HD
624
  if media_type == "video" and not is_large_video:
625
  await status_msg.edit_text("⏳ Watermarking video... (HD + Superfast Processing)")
626
  watermarked_file = f"{original_file}_wm.mp4"
 
635
  if process.returncode == 0 and os.path.exists(watermarked_file):
636
  clean_upload_file = watermarked_file
637
 
638
+ # ২. byse.sx এর বদলে আপনার নিজের টেলিগম স্টোরে চ্যানেলে HD ভিডিও আপলোড
639
  if media_type == "video":
640
+ await status_msg.edit_text("⏳ Uploading Clean HD video to your storage channel...")
 
 
 
 
641
 
642
+ # আপনার নিজের চ্যানেলে ব্যাকআপ হিসেবে HD ওয়াটারমার্কড ভিডিওটি পাঠানো হচ্ছে
643
+ sent_to_channel = await client.send_video(
644
+ chat_id=STORAGE_CHANNEL_ID,
645
+ video=clean_upload_file,
646
+ caption=f"Backup of video uploaded by Admin. File: {os.path.basename(clean_upload_file)}"
647
+ )
648
+ storage_msg_id = sent_to_channel.id
 
 
649
 
650
+ # আপনার নিজস্ব ডোমেইন বা সার্ভারের স্ট্রিমিং ও ডিরেক্ট ডাউনলোড লিংক জেনারেট
651
+ stream_link = f"{BACKEND_URL}/stream/{storage_msg_id}"
652
+ download_link = f"{BACKEND_URL}/download/{storage_msg_id}"
653
+ embed_link = stream_link # এমবেড লিংক হিসেবে এখন নিজস্ব স্ট্রিমিং লিংক ব্যবহার হবে
654
 
655
  if is_large_video:
656
+ # যদি লার্জ ভিডিও হয়, তবে সেটিরও স্ট্রিমিং লিংক জেনারেট হয়ে অ্যাডমিন চ্যাটে শো করবে
657
+ admin_cap = f"✅ <b>Success! (Large Video)</b>\n\n🔗 <b>Embed Link (Clean HD):</b>\n<code>{embed_link or 'N/A'}</code>\n\n📌 <i>Broadcast skipped due to large file size.</i>"
658
  await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
659
  await status_msg.delete()
660
  return
661
 
662
+ # ৩. টেলিগ্রামের জন্য ভিডিও ব্লার করা
663
  telegram_file = clean_upload_file
664
  if is_blur and not is_large_video:
665
  await status_msg.edit_text(f"⏳ Applying {blur_percent}% blur for Telegram broadcast...")
 
694
  caption_text = f"{clean_caption}\n\n👇 <i>Click the button below to open Bot!</i>" if clean_caption else f"🔥 <b>New Premium Viral Content!</b> 🔞\n\n🎬 <b>Watch HD Video Here:</b>\n👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n👇 <i>Click the button below to open Bot!</i>"
695
 
696
  group_markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]])
697
+
698
+ # অ্যাডমিন চ্যাটে সাকস���স মেসেজ ও লিংক পাঠানো হচ্ছে
699
+ admin_cap = (
700
+ f"✅ <b>Upload and Processing Complete!</b>\n\n"
701
+ f"🎬 <b>Stream/Watch Online Link:</b>\n<code>{stream_link}</code>\n\n"
702
+ f"📥 <b>Direct Download Link:</b>\n<code>{download_link}</code>"
703
+ )
704
 
705
  thumb_path = None
706
  if media_type == "video":