pmrony commited on
Commit
64d5acd
·
verified ·
1 Parent(s): 12535e9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -74
app.py CHANGED
@@ -33,6 +33,7 @@ API_ID = int(os.environ.get("API_ID", 0))
33
  API_HASH = os.environ.get("API_HASH")
34
  SUPABASE_URL = os.environ.get("SUPABASE_URL")
35
  SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
 
36
 
37
  PREMIUM_CHANNEL_ID = -1002825744390
38
  STORAGE_CHANNEL_ID = -1002825744390 # যে চ্যানেলে ভিডিও স্টোর হবে
@@ -49,6 +50,9 @@ supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
49
  admin_states = {}
50
  temp_clients = {}
51
 
 
 
 
52
  try:
53
  main_loop = asyncio.get_running_loop()
54
  except RuntimeError:
@@ -553,6 +557,25 @@ async def start_cloning(client, message):
553
 
554
  # =================================================================
555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
  @bot.on_message(filters.command("blur") & filters.private & filters.user(ADMIN_IDS))
557
  async def set_blur_state(client, message):
558
  try:
@@ -595,15 +618,26 @@ def upload_file_sync(upload_url, file_path, api_key):
595
  except Exception as e:
596
  return {}
597
 
598
- @bot.on_message((filters.video | filters.animation | filters.photo) & filters.private & filters.user(ADMIN_IDS))
 
599
  async def handle_media_upload(client, message):
 
600
  state = admin_states.get(message.chat.id, {})
601
  if state.get("step") == "broadcast":
602
  await process_broadcast(client, message)
603
  return
604
 
 
 
 
 
 
 
 
 
 
605
  # FORCE ANIMATIONS (GIFS) TO BE TREATED AS VIDEOS FOR BROADCAST STABILITY
606
- media_type = "video" if (message.video or message.animation) else "photo"
607
  has_blur_caption = message.caption and "/blur" in message.caption.lower()
608
  is_persistent_blur = bool(state.get("blur_percent"))
609
 
@@ -643,6 +677,7 @@ async def handle_media_upload(client, message):
643
 
644
  is_large_video = False
645
  if media_type == "video":
 
646
  media = get_media_obj(message)
647
  duration = media.duration if media and hasattr(media, 'duration') and media.duration else 0
648
  file_size = media.file_size if media and hasattr(media, 'file_size') and media.file_size else 0
@@ -679,11 +714,13 @@ async def handle_media_upload(client, message):
679
  clean_upload_file = original_file
680
 
681
  # ১. ওয়াটারমার্ক (Watermark) করা - Balanced (Superfast + CRF 23)
 
682
  if media_type == "video" and not is_large_video:
683
  await status_msg.edit_text("⏳ Watermarking video... (HD + Superfast Processing)")
684
  watermarked_file = f"{original_file}_wm.mp4"
685
 
686
- has_audio = not bool(message.animation)
 
687
  audio_opts = ["-an"] if not has_audio else ["-c:a", "copy"]
688
 
689
  cmd = [
@@ -696,19 +733,17 @@ async def handle_media_upload(client, message):
696
  process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
697
  await process.communicate()
698
 
 
699
  if process.returncode == 0 and os.path.exists(watermarked_file) and os.path.getsize(watermarked_file) > 0:
700
  clean_upload_file = watermarked_file
701
 
702
- # ২. আপনার নিজের টেলিগ্রাম স্টোরেজ চ্যনেলে ফাইল আপলোড (ভিডিও ব)
703
- # এখানে থাম্বনেইল, সঠিক ডিউরেশন এবং সাইজ জেনারেট করে পোস্ট করা হবে যাতে সাদা ০:০০ শো না করে
704
  storage_msg_id = None
705
- if media_type in ["video", "photo"]:
706
- await status_msg.edit_text(f"⏳ Uploading {media_type} to your storage channel...")
707
-
708
- if media_type == "photo":
709
- sent_to_channel = await client.send_photo(chat_id=STORAGE_CHANNEL_ID, photo=clean_upload_file, caption="Backup of photo uploaded by Admin.")
710
- else:
711
- # ভিডিওর অরিজিনাল মেটাডাটা ও থাম্বনেইল বের করা হচ্ছে
712
  thumb_path_storage = f"{original_file}_storage_thumb.jpg"
713
  proc = await asyncio.create_subprocess_exec("ffmpeg", "-y", "-i", clean_upload_file, "-vframes", "1", thumb_path_storage, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
714
  await proc.communicate()
@@ -729,13 +764,36 @@ async def handle_media_upload(client, message):
729
  height=vid_height,
730
  thumb=thumb_path_storage
731
  )
732
-
733
- storage_msg_id = sent_to_channel.id
734
-
735
- # আপনার নিজস্ব ডোমেইন বা সার্ভারের স্ট্রিমিং ও ডিরেক্ট ডাউনলোড লিংক জেনারেট
736
- stream_link = f"{BACKEND_URL}/stream/{storage_msg_id}"
737
- download_link = f"{BACKEND_URL}/download/{storage_msg_id}"
738
- embed_link = stream_link
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
 
740
  if is_large_video:
741
  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>"
@@ -759,16 +817,18 @@ async def handle_media_upload(client, message):
759
  else:
760
  ff_filter = ["-vf", f"boxblur={radius}:1"]
761
 
 
 
 
 
762
  if media_type == "photo":
763
  cmd_blur = ["ffmpeg", "-y", "-i", clean_upload_file] + ff_filter + [blurred_file]
764
- elif media_type == "animation":
765
- cmd_blur = ["ffmpeg", "-y", "-i", clean_upload_file] + ff_filter + ["-c:v", "libx264", "-preset", "superfast", "-pix_fmt", "yuv420p", blurred_file]
766
  else:
767
- cmd_blur = ["ffmpeg", "-y", "-i", clean_upload_file] + ff_filter + ["-c:v", "libx264", "-preset", "superfast", "-crf", "23", "-pix_fmt", "yuv420p", "-c:a", "copy", "-movflags", "+faststart", blurred_file]
768
 
769
  process_blur = await asyncio.create_subprocess_exec(*cmd_blur, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
770
  await process_blur.communicate()
771
- if process_blur.returncode == 0 and os.path.exists(blurred_file):
772
  telegram_file = blurred_file
773
 
774
  await status_msg.edit_text("⏳ Preparing to broadcast to groups...")
@@ -795,8 +855,6 @@ async def handle_media_upload(client, message):
795
 
796
  if media_type == "photo":
797
  sent_to_admin = await client.send_photo(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
798
- elif media_type == "animation":
799
- sent_to_admin = await client.send_animation(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
800
  else:
801
  media = get_media_obj(message)
802
  vid_duration = media.duration if media and hasattr(media, 'duration') and media.duration else 0
@@ -950,54 +1008,10 @@ async def manual_clean_channel(client, message):
950
  try: await temp_client.disconnect()
951
  except: pass
952
  await db_query(lambda: supabase.table('user_sessions').delete().eq('user_id', user_id).execute())
953
-
954
- if not is_valid:
955
- try:
956
- await bot.ban_chat_member(PREMIUM_CHANNEL_ID, user_id)
957
- await bot.unban_chat_member(PREMIUM_CHANNEL_ID, user_id)
958
- kicked += 1
959
- except Exception: pass
960
-
961
- await asyncio.sleep(1.5)
962
- except FloodWait as e:
963
- await asyncio.sleep(e.value + 1)
964
- except Exception:
965
- pass
966
-
967
- await message.reply(f"✅ <b>Cleanup Complete!</b>\n\n👥 Members checked: {checked}\n👢 Users Kicked: {kicked}")
968
- except Exception as e:
969
- await message.reply(f"❌ Error: {e}")
970
-
971
- async def auto_clean_channel_loop():
972
- await asyncio.sleep(60)
973
- while True:
974
- try:
975
- res_users = await db_query(lambda: supabase.table('referrals').select('user_id').execute())
976
- if res_users.data:
977
- user_ids = [u['user_id'] for u in res_users.data]
978
- for user_id in set(user_ids):
979
- try:
980
- chat_member = await bot.get_chat_member(PREMIUM_CHANNEL_ID, user_id)
981
- if chat_member.status in [enums.ChatMemberStatus.MEMBER, enums.ChatMemberStatus.RESTRICTED]:
982
- res_session = await db_query(lambda: supabase.table('user_sessions').select('session_string').eq('user_id', user_id).execute())
983
-
984
- is_valid = False
985
- if res_session.data:
986
- session_string = res_session.data[0]['session_string']
987
- temp_client = Client(f"bg_chk_{user_id}", session_string=session_string, api_id=API_ID, api_hash=API_HASH, in_memory=True)
988
- try:
989
- await temp_client.connect()
990
- await temp_client.get_me()
991
- await temp_client.disconnect()
992
- is_valid = True
993
- except (SessionRevoked, AuthKeyUnregistered, UserDeactivated):
994
- try: await temp_client.disconnect()
995
- except: pass
996
- await db_query(lambda: supabase.table('user_sessions').delete().eq('user_id', user_id).execute())
997
- except Exception:
998
- try: await temp_client.disconnect()
999
- except: pass
1000
- is_valid = True
1001
 
1002
  if not is_valid:
1003
  try:
@@ -1014,7 +1028,7 @@ async def auto_clean_channel_loop():
1014
 
1015
  await asyncio.sleep(4 * 3600)
1016
 
1017
- @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur", "clean", "sendto", "clone"]))
1018
  async def catch_admin_steps(client, message):
1019
  state = admin_states.get(message.chat.id, {})
1020
  if state.get("step") == 1:
 
33
  API_HASH = os.environ.get("API_HASH")
34
  SUPABASE_URL = os.environ.get("SUPABASE_URL")
35
  SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
36
+ BYSE_API_KEY = os.environ.get("BYSE_API_KEY", "133323knboif885fhgwxvf")
37
 
38
  PREMIUM_CHANNEL_ID = -1002825744390
39
  STORAGE_CHANNEL_ID = -1002825744390 # যে চ্যানেলে ভিডিও স্টোর হবে
 
50
  admin_states = {}
51
  temp_clients = {}
52
 
53
+ # ডিফল্ট আপলোড সার্ভার মোড (Telegram)
54
+ upload_mode = "telegram"
55
+
56
  try:
57
  main_loop = asyncio.get_running_loop()
58
  except RuntimeError:
 
557
 
558
  # =================================================================
559
 
560
+ # ================= NEW: SET UPLOAD SERVER MODE =================
561
+ @bot.on_message(filters.command("upload") & filters.private & filters.user(ADMIN_IDS))
562
+ async def set_upload_mode(client, message):
563
+ global upload_mode
564
+ args = message.command
565
+ if len(args) > 1:
566
+ mode = args[1].lower()
567
+ if mode in ["telegram", "tg", "local"]:
568
+ upload_mode = "telegram"
569
+ await message.reply("✅ <b>Upload server set to: Telegram</b>\nVideos will be uploaded to your own channel and streamed via Hugging Face.")
570
+ elif mode in ["byse", "byse.sx", "external"]:
571
+ upload_mode = "byse"
572
+ await message.reply("✅ <b>Upload server set to: Byse.sx</b>\nVideos will be uploaded to Byse.sx and streamed via their player.")
573
+ else:
574
+ await message.reply("❌ <b>Invalid server!</b> Use `/upload telegram` or `/upload byse`.")
575
+ else:
576
+ await message.reply(f"📌 <b>Current Upload Server:</b> <code>{upload_mode.upper()}</code>\n\nTo change, use:\n👉 `/upload telegram` (Storage Channel Stream)\n👉 `/upload byse` (Byse.sx third-party player)")
577
+ # ===============================================================
578
+
579
  @bot.on_message(filters.command("blur") & filters.private & filters.user(ADMIN_IDS))
580
  async def set_blur_state(client, message):
581
  try:
 
618
  except Exception as e:
619
  return {}
620
 
621
+ # filters.document যুক্ত করা হলো যাতে ফাইল/ডকুমেন্ট হিসেবে ভিডিও পাঠালেও বট প্রসেস করতে পারে
622
+ @bot.on_message((filters.video | filters.animation | filters.photo | filters.document) & filters.private & filters.user(ADMIN_IDS))
623
  async def handle_media_upload(client, message):
624
+ global upload_mode
625
  state = admin_states.get(message.chat.id, {})
626
  if state.get("step") == "broadcast":
627
  await process_broadcast(client, message)
628
  return
629
 
630
+ # ফাইলটি ভিডিও, জিআইএফ নাকি ফটো তা নিখুঁতভাবে চেক করা হচ্ছে
631
+ is_video = message.video or (message.document and message.document.mime_type and "video" in message.document.mime_type)
632
+ is_animation = message.animation or (message.document and message.document.mime_type and "gif" in message.document.mime_type)
633
+ is_photo = message.photo or (message.document and message.document.mime_type and "image" in message.document.mime_type)
634
+
635
+ # যদি ভিডিও, ফটো বা অ্যানিমেশন না হয় তবে কাস্টম ফাইল স্কিপ করবে
636
+ if not (is_video or is_animation or is_photo):
637
+ return
638
+
639
  # FORCE ANIMATIONS (GIFS) TO BE TREATED AS VIDEOS FOR BROADCAST STABILITY
640
+ media_type = "video" if (is_video or is_animation) else "photo"
641
  has_blur_caption = message.caption and "/blur" in message.caption.lower()
642
  is_persistent_blur = bool(state.get("blur_percent"))
643
 
 
677
 
678
  is_large_video = False
679
  if media_type == "video":
680
+ # Safe size checks for both Videos and Animations (GIFs/Documents)
681
  media = get_media_obj(message)
682
  duration = media.duration if media and hasattr(media, 'duration') and media.duration else 0
683
  file_size = media.file_size if media and hasattr(media, 'file_size') and media.file_size else 0
 
714
  clean_upload_file = original_file
715
 
716
  # ১. ওয়াটারমার্ক (Watermark) করা - Balanced (Superfast + CRF 23)
717
+ # জাইএফ (Animation) ফাইলের কোনো অডিও ট্র্যাক থাকে না, তাই এফএফএমপেগ ফেইল হওয়া ঠেকাতে অডিও বাইপাস করা হলো
718
  if media_type == "video" and not is_large_video:
719
  await status_msg.edit_text("⏳ Watermarking video... (HD + Superfast Processing)")
720
  watermarked_file = f"{original_file}_wm.mp4"
721
 
722
+ # জাইএফ ফাইলের ক্ষেত্রে অডিও ট্র্যাক বাদ দেওয়া হবে (-an) অন্যথায় কপি করা হবে
723
+ has_audio = not (message.animation or (message.document and message.document.mime_type and "gif" in message.document.mime_type))
724
  audio_opts = ["-an"] if not has_audio else ["-c:a", "copy"]
725
 
726
  cmd = [
 
733
  process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
734
  await process.communicate()
735
 
736
+ # বাগ সংশোধন: ফাইলের সাইজ ০-এর চেয়ে বেশি হলে তবেই আউটপুট ব্যবহার করা হবে, অন্যথায় অরিজিনাল ফাইল ব্যবহৃত হবে
737
  if process.returncode == 0 and os.path.exists(watermarked_file) and os.path.getsize(watermarked_file) > 0:
738
  clean_upload_file = watermarked_file
739
 
740
+ # ২. ভিডিও আপলোড (টেলিগ্রাম স্টোরেজ byse.sx - ইউজ সেিং অনুযায়ী)
 
741
  storage_msg_id = None
742
+ if media_type == "video":
743
+ if upload_mode == "telegram":
744
+ await status_msg.edit_text("⏳ Uploading Clean HD video to your storage channel...")
745
+
746
+ # স্টোরেজ চ্যানেলেও অরিজিনাল থাম্বনেইল, ডিউরেশন এবং সাইজ জেনারেট করে পাঠানো হচ্ছে যাতে সাদা ০:০০ শো না করে
 
 
747
  thumb_path_storage = f"{original_file}_storage_thumb.jpg"
748
  proc = await asyncio.create_subprocess_exec("ffmpeg", "-y", "-i", clean_upload_file, "-vframes", "1", thumb_path_storage, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
749
  await proc.communicate()
 
764
  height=vid_height,
765
  thumb=thumb_path_storage
766
  )
767
+ storage_msg_id = sent_to_channel.id
768
+
769
+ # আপনার নিজস্ব ডোমেইন বা সার্ভারের স্ট্রিমিং ও ডিরেক্ট ডাউনলোড লিংক জেনারেট
770
+ stream_link = f"{BACKEND_URL}/stream/{storage_msg_id}"
771
+ download_link = f"{BACKEND_URL}/download/{storage_msg_id}"
772
+ embed_link = stream_link
773
+ else:
774
+ # Byse.sx আপলোড মোড
775
+ await status_msg.edit_text("⏳ Uploading Clean HD video to byse.sx server...")
776
+ api_endpoint = "https://api.byse.sx/upload/server"
777
+ loop = asyncio.get_event_loop()
778
+ response = await loop.run_in_executor(None, lambda: requests.get(api_endpoint, params={'key': BYSE_API_KEY}, timeout=30))
779
+ result = response.json()
780
+
781
+ if result.get('status') == 200:
782
+ upload_res = await loop.run_in_executor(None, upload_file_sync, result.get('result'), clean_upload_file, BYSE_API_KEY)
783
+ if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
784
+ file_status = upload_res['files'][0].get('status', '')
785
+ if "not allowed" in str(file_status).lower():
786
+ await status_msg.edit_text(f"❌ byse.sx rejected the file: <code>{file_status}</code>", parse_mode=enums.ParseMode.HTML)
787
+ return
788
+ file_code = upload_res['files'][0].get('filecode')
789
+ if file_code: embed_link = f"https://bysesayeveum.com/e/{file_code}"
790
+
791
+ if not embed_link:
792
+ await status_msg.edit_text("❌ Uploaded to byse.sx but Embed Link not found.")
793
+ return
794
+
795
+ stream_link = embed_link
796
+ download_link = embed_link
797
 
798
  if is_large_video:
799
  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>"
 
817
  else:
818
  ff_filter = ["-vf", f"boxblur={radius}:1"]
819
 
820
+ # জাইএফ ফাইলের ক্ষেত্রে অডিও ট্র্যাক বাদ দেওয়া হবে (-an) অন্যথায় কপি করা হবে
821
+ has_audio = not (message.animation or (message.document and message.document.mime_type and "gif" in message.document.mime_type))
822
+ audio_opts_blur = ["-an"] if not has_audio else []
823
+
824
  if media_type == "photo":
825
  cmd_blur = ["ffmpeg", "-y", "-i", clean_upload_file] + ff_filter + [blurred_file]
 
 
826
  else:
827
+ cmd_blur = ["ffmpeg", "-y", "-i", clean_upload_file] + ff_filter + ["-c:v", "libx264", "-preset", "superfast", "-crf", "23", "-pix_fmt", "yuv420p"] + audio_opts_blur + ["-movflags", "+faststart", blurred_file]
828
 
829
  process_blur = await asyncio.create_subprocess_exec(*cmd_blur, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
830
  await process_blur.communicate()
831
+ if process_blur.returncode == 0 and os.path.exists(blurred_file) and os.path.getsize(blurred_file) > 0:
832
  telegram_file = blurred_file
833
 
834
  await status_msg.edit_text("⏳ Preparing to broadcast to groups...")
 
855
 
856
  if media_type == "photo":
857
  sent_to_admin = await client.send_photo(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
 
 
858
  else:
859
  media = get_media_obj(message)
860
  vid_duration = media.duration if media and hasattr(media, 'duration') and media.duration else 0
 
1008
  try: await temp_client.disconnect()
1009
  except: pass
1010
  await db_query(lambda: supabase.table('user_sessions').delete().eq('user_id', user_id).execute())
1011
+ except Exception:
1012
+ try: await temp_client.disconnect()
1013
+ except: pass
1014
+ is_valid = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
 
1016
  if not is_valid:
1017
  try:
 
1028
 
1029
  await asyncio.sleep(4 * 3600)
1030
 
1031
+ @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur", "clean", "sendto", "clone", "upload"]))
1032
  async def catch_admin_steps(client, message):
1033
  state = admin_states.get(message.chat.id, {})
1034
  if state.get("step") == 1: