Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,14 +7,13 @@ 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, utils
|
| 11 |
from pyrogram.errors import SessionPasswordNeeded, PhoneCodeInvalid, PhoneCodeExpired, UserDeactivated, SessionRevoked, AuthKeyUnregistered, FloodWait
|
| 12 |
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
|
| 13 |
|
| 14 |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
| 15 |
|
| 16 |
# ==================== PYROGRAM NEW ID RANGE FIX (MONKEY PATCH) ====================
|
| 17 |
-
# টেলিগ্রামের নতুন আইডিগুলোর জন্য Pyrogram-এর Peer ID Invalid বাগ সমাধান করা হলো
|
| 18 |
def get_peer_type_new(peer_id: int) -> str:
|
| 19 |
peer_id_str = str(peer_id)
|
| 20 |
if not peer_id_str.startswith("-"):
|
|
@@ -37,7 +36,7 @@ SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
|
|
| 37 |
PREMIUM_CHANNEL_ID = -1002825744390
|
| 38 |
STORAGE_CHANNEL_ID = -1002825744390 # যে চ্যানেলে ভিডিও স্টোর হবে
|
| 39 |
|
| 40 |
-
# আপনার Hugging Face স্পেসের ডিরেক্ট ইউআরএল
|
| 41 |
BACKEND_URL = os.environ.get("BACKEND_URL", "https://pmrony-forwardbot.hf.space")
|
| 42 |
|
| 43 |
# WebApp URL (index.html)
|
|
@@ -64,17 +63,45 @@ bot = Client("file_unlocker_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BO
|
|
| 64 |
async def db_query(func):
|
| 65 |
return await asyncio.to_thread(func)
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
# ==================== CUSTOM VIDEO STREAMING ENGINE ====================
|
| 68 |
def get_file_stream(message_id):
|
| 69 |
"""টেলিগ্রামের স্টোরেজ চ্যানেল থেকে রিয়েল-টাইমে ডাটা স্ট্রিম করার জেনারেটর"""
|
| 70 |
async def stream_generator():
|
| 71 |
try:
|
| 72 |
msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
|
| 73 |
-
|
|
|
|
| 74 |
return
|
| 75 |
|
| 76 |
# chunk_size অনুযায়ী ডাটা রিয়েল-টাইমে রিড করে পাঠানো হবে
|
| 77 |
-
async for chunk in bot.stream_media(
|
| 78 |
yield chunk
|
| 79 |
except Exception as e:
|
| 80 |
print(f"Error in stream_generator: {e}")
|
|
@@ -99,21 +126,23 @@ def get_file_stream(message_id):
|
|
| 99 |
|
| 100 |
@app.route('/stream/<int:message_id>')
|
| 101 |
def stream_video(message_id):
|
| 102 |
-
"""অনলাইনে প্লেয়ারে ভিডিও দেখার লিংক"""
|
| 103 |
try:
|
| 104 |
async def get_media_info():
|
| 105 |
msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
| 108 |
return None, None, None
|
| 109 |
|
| 110 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 111 |
if not file_size:
|
| 112 |
return "File not found or invalid message", 404
|
| 113 |
|
| 114 |
-
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type
|
| 115 |
response.headers['Content-Length'] = file_size
|
| 116 |
-
response.headers['Content-Type'] = mime_type
|
| 117 |
response.headers['Accept-Ranges'] = 'bytes'
|
| 118 |
response.headers['Content-Disposition'] = f'inline; filename="{file_name or "video.mp4"}"'
|
| 119 |
return response
|
|
@@ -126,15 +155,17 @@ def download_video(message_id):
|
|
| 126 |
try:
|
| 127 |
async def get_media_info():
|
| 128 |
msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
| 131 |
return None, None, None
|
| 132 |
|
| 133 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 134 |
if not file_size:
|
| 135 |
return "File not found or invalid message", 404
|
| 136 |
|
| 137 |
-
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type
|
| 138 |
response.headers['Content-Length'] = file_size
|
| 139 |
response.headers['Content-Disposition'] = f'attachment; filename="{file_name or "video.mp4"}"'
|
| 140 |
return response
|
|
@@ -649,25 +680,26 @@ async def handle_media_upload(client, message):
|
|
| 649 |
if process.returncode == 0 and os.path.exists(watermarked_file):
|
| 650 |
clean_upload_file = watermarked_file
|
| 651 |
|
| 652 |
-
# ২.
|
| 653 |
-
|
| 654 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 655 |
|
| 656 |
-
# আপনার নিজের চ্যানেলে ব্যাকআপ হিসেবে HD ওয়াটারমার্কড ভিডিওটি পাঠানো হচ্ছে
|
| 657 |
-
sent_to_channel = await client.send_video(
|
| 658 |
-
chat_id=STORAGE_CHANNEL_ID,
|
| 659 |
-
video=clean_upload_file,
|
| 660 |
-
caption=f"Backup of video uploaded by Admin. File: {os.path.basename(clean_upload_file)}"
|
| 661 |
-
)
|
| 662 |
storage_msg_id = sent_to_channel.id
|
| 663 |
|
| 664 |
# আপনার নিজস্ব ডোমেইন বা সার্ভারের স্ট্রিমিং ও ডিরেক্ট ডাউনলোড লিংক জেনারেট
|
| 665 |
stream_link = f"{BACKEND_URL}/stream/{storage_msg_id}"
|
| 666 |
download_link = f"{BACKEND_URL}/download/{storage_msg_id}"
|
| 667 |
-
embed_link = stream_link
|
| 668 |
|
| 669 |
if is_large_video:
|
| 670 |
-
# যদি লার্জ ভিডিও হয়, তবে সেটিরও স্ট্রিমিং লিংক জেনারেট হয়ে অ্যাডমিন চ্যাটে শো করবে
|
| 671 |
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>"
|
| 672 |
await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
|
| 673 |
await status_msg.delete()
|
|
@@ -725,10 +757,8 @@ async def handle_media_upload(client, message):
|
|
| 725 |
|
| 726 |
if media_type == "photo":
|
| 727 |
sent_to_admin = await client.send_photo(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
|
| 728 |
-
tg_file_id = sent_to_admin.photo.file_id
|
| 729 |
elif media_type == "animation":
|
| 730 |
sent_to_admin = await client.send_animation(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
|
| 731 |
-
tg_file_id = sent_to_admin.animation.file_id
|
| 732 |
else:
|
| 733 |
vid_duration = message.video.duration if message.video and message.video.duration else 0
|
| 734 |
vid_width = message.video.width if message.video and message.video.width else 0
|
|
@@ -744,7 +774,11 @@ async def handle_media_upload(client, message):
|
|
| 744 |
height=vid_height,
|
| 745 |
thumb=thumb_path
|
| 746 |
)
|
| 747 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 748 |
|
| 749 |
await status_msg.delete()
|
| 750 |
|
|
|
|
| 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, utils
|
| 11 |
from pyrogram.errors import SessionPasswordNeeded, PhoneCodeInvalid, PhoneCodeExpired, UserDeactivated, SessionRevoked, AuthKeyUnregistered, FloodWait
|
| 12 |
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
|
| 13 |
|
| 14 |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
| 15 |
|
| 16 |
# ==================== PYROGRAM NEW ID RANGE FIX (MONKEY PATCH) ====================
|
|
|
|
| 17 |
def get_peer_type_new(peer_id: int) -> str:
|
| 18 |
peer_id_str = str(peer_id)
|
| 19 |
if not peer_id_str.startswith("-"):
|
|
|
|
| 36 |
PREMIUM_CHANNEL_ID = -1002825744390
|
| 37 |
STORAGE_CHANNEL_ID = -1002825744390 # যে চ্যানেলে ভিডিও স্টোর হবে
|
| 38 |
|
| 39 |
+
# আপনার Hugging Face স্পেসের ডিরেক্ট ইউআরএল
|
| 40 |
BACKEND_URL = os.environ.get("BACKEND_URL", "https://pmrony-forwardbot.hf.space")
|
| 41 |
|
| 42 |
# WebApp URL (index.html)
|
|
|
|
| 63 |
async def db_query(func):
|
| 64 |
return await asyncio.to_thread(func)
|
| 65 |
|
| 66 |
+
# ==================== UNIVERSAL MEDIA HELPER ====================
|
| 67 |
+
def get_media_obj(msg):
|
| 68 |
+
"""মেসেজ থেকে যেকোনো ধরনের মিডিয়া অবজেক্ট (ভিডিও, জিআইএফ, ডকুমেন্ট) খুঁজে বের করার ফাংশন"""
|
| 69 |
+
if not msg:
|
| 70 |
+
return None
|
| 71 |
+
if msg.video:
|
| 72 |
+
return msg.video
|
| 73 |
+
if msg.animation:
|
| 74 |
+
return msg.animation
|
| 75 |
+
if msg.document:
|
| 76 |
+
return msg.document
|
| 77 |
+
if msg.audio:
|
| 78 |
+
return msg.audio
|
| 79 |
+
return None
|
| 80 |
+
|
| 81 |
+
def get_msg_file_id(msg):
|
| 82 |
+
"""মেসেজ থেকে ক্র্যাশ-ফ্রি উপায়ে file_id বের করার ফাংশন"""
|
| 83 |
+
if not msg:
|
| 84 |
+
return None
|
| 85 |
+
if msg.photo:
|
| 86 |
+
return msg.photo.file_id
|
| 87 |
+
media = get_media_obj(msg)
|
| 88 |
+
if media:
|
| 89 |
+
return media.file_id
|
| 90 |
+
return None
|
| 91 |
+
# ================================================================
|
| 92 |
+
|
| 93 |
# ==================== CUSTOM VIDEO STREAMING ENGINE ====================
|
| 94 |
def get_file_stream(message_id):
|
| 95 |
"""টেলিগ্রামের স্টোরেজ চ্যানেল থেকে রিয়েল-টাইমে ডাটা স্ট্রিম করার জেনারেটর"""
|
| 96 |
async def stream_generator():
|
| 97 |
try:
|
| 98 |
msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
|
| 99 |
+
media = get_media_obj(msg)
|
| 100 |
+
if not media:
|
| 101 |
return
|
| 102 |
|
| 103 |
# chunk_size অনুযায়ী ডাটা রিয়েল-টাইমে রিড করে পাঠানো হবে
|
| 104 |
+
async for chunk in bot.stream_media(media):
|
| 105 |
yield chunk
|
| 106 |
except Exception as e:
|
| 107 |
print(f"Error in stream_generator: {e}")
|
|
|
|
| 126 |
|
| 127 |
@app.route('/stream/<int:message_id>')
|
| 128 |
def stream_video(message_id):
|
| 129 |
+
"""অনলাইনে প্লেয়ারে ভিডিও/জিআইএফ দেখার লিংক"""
|
| 130 |
try:
|
| 131 |
async def get_media_info():
|
| 132 |
msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
|
| 133 |
+
media = get_media_obj(msg)
|
| 134 |
+
if media:
|
| 135 |
+
mime = getattr(media, 'mime_type', 'video/mp4') or 'video/mp4'
|
| 136 |
+
return media.file_size, getattr(media, 'file_name', 'video.mp4'), mime
|
| 137 |
return None, None, None
|
| 138 |
|
| 139 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 140 |
if not file_size:
|
| 141 |
return "File not found or invalid message", 404
|
| 142 |
|
| 143 |
+
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type))
|
| 144 |
response.headers['Content-Length'] = file_size
|
| 145 |
+
response.headers['Content-Type'] = mime_type
|
| 146 |
response.headers['Accept-Ranges'] = 'bytes'
|
| 147 |
response.headers['Content-Disposition'] = f'inline; filename="{file_name or "video.mp4"}"'
|
| 148 |
return response
|
|
|
|
| 155 |
try:
|
| 156 |
async def get_media_info():
|
| 157 |
msg = await bot.get_messages(STORAGE_CHANNEL_ID, message_id)
|
| 158 |
+
media = get_media_obj(msg)
|
| 159 |
+
if media:
|
| 160 |
+
mime = getattr(media, 'mime_type', 'application/octet-stream') or 'application/octet-stream'
|
| 161 |
+
return media.file_size, getattr(media, 'file_name', 'video.mp4'), mime
|
| 162 |
return None, None, None
|
| 163 |
|
| 164 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 165 |
if not file_size:
|
| 166 |
return "File not found or invalid message", 404
|
| 167 |
|
| 168 |
+
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type))
|
| 169 |
response.headers['Content-Length'] = file_size
|
| 170 |
response.headers['Content-Disposition'] = f'attachment; filename="{file_name or "video.mp4"}"'
|
| 171 |
return response
|
|
|
|
| 680 |
if process.returncode == 0 and os.path.exists(watermarked_file):
|
| 681 |
clean_upload_file = watermarked_file
|
| 682 |
|
| 683 |
+
# ২. আপনার নিজের টেলিগ্রাম স্টোরেজ চ্যানেলে ফাইল আপলোড (ভিডিও, অ্যানিমেশন বা ফটো)
|
| 684 |
+
storage_msg_id = None
|
| 685 |
+
if media_type in ["video", "animation", "photo"]:
|
| 686 |
+
await status_msg.edit_text(f"⏳ Uploading {media_type} to your storage channel...")
|
| 687 |
+
|
| 688 |
+
if media_type == "photo":
|
| 689 |
+
sent_to_channel = await client.send_photo(chat_id=STORAGE_CHANNEL_ID, photo=clean_upload_file, caption="Backup of photo uploaded by Admin.")
|
| 690 |
+
elif media_type == "animation":
|
| 691 |
+
sent_to_channel = await client.send_animation(chat_id=STORAGE_CHANNEL_ID, animation=clean_upload_file, caption="Backup of GIF uploaded by Admin.")
|
| 692 |
+
else:
|
| 693 |
+
sent_to_channel = await client.send_video(chat_id=STORAGE_CHANNEL_ID, video=clean_upload_file, caption=f"Backup of video uploaded by Admin. File: {os.path.basename(clean_upload_file)}")
|
| 694 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
storage_msg_id = sent_to_channel.id
|
| 696 |
|
| 697 |
# আপনার নিজস্ব ডোমেইন বা সার্ভারের স্ট্রিমিং ও ডিরেক্ট ডাউনলোড লিংক জেনারেট
|
| 698 |
stream_link = f"{BACKEND_URL}/stream/{storage_msg_id}"
|
| 699 |
download_link = f"{BACKEND_URL}/download/{storage_msg_id}"
|
| 700 |
+
embed_link = stream_link
|
| 701 |
|
| 702 |
if is_large_video:
|
|
|
|
| 703 |
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>"
|
| 704 |
await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
|
| 705 |
await status_msg.delete()
|
|
|
|
| 757 |
|
| 758 |
if media_type == "photo":
|
| 759 |
sent_to_admin = await client.send_photo(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
|
|
|
|
| 760 |
elif media_type == "animation":
|
| 761 |
sent_to_admin = await client.send_animation(message.chat.id, telegram_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
|
|
|
|
| 762 |
else:
|
| 763 |
vid_duration = message.video.duration if message.video and message.video.duration else 0
|
| 764 |
vid_width = message.video.width if message.video and message.video.width else 0
|
|
|
|
| 774 |
height=vid_height,
|
| 775 |
thumb=thumb_path
|
| 776 |
)
|
| 777 |
+
|
| 778 |
+
# ক্র্যাশ-ফ্রি উপায়ে file_id রিড করার জন্য ইউনিভার্সাল এক্সট্রাক্টর
|
| 779 |
+
tg_file_id = get_msg_file_id(sent_to_admin)
|
| 780 |
+
if not tg_file_id:
|
| 781 |
+
tg_file_id = get_msg_file_id(message) # Fallback to original
|
| 782 |
|
| 783 |
await status_msg.delete()
|
| 784 |
|