#!/usr/bin/env python3 import jsbeautifier from bs4 import BeautifulSoup as bs4 import sys, os, subprocess, random, time, requests, re, json, logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) import os import random import time import asyncio from subprocess import getoutput as getoutput calidades={'0':'SD', '1':'HD', '2':'FHD'} def extract_arg(arg): return arg.split()[1:] if bool(os.environ.get("WEBHOOK", False)): from sample_config import Config else: from config import Config from translation import Translation import pyrogram logging.getLogger("pyrogram").setLevel(logging.WARNING) from helper_funcs.display_progress import progress_for_pyrogram from helper_funcs.help_Nekmo_ffmpeg import take_screen_shot from hachoir.metadata import extractMetadata from hachoir.parser import createParser from PIL import Image def gethtml(url): return bs4(requests.get(url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36','Origin':url,'Referer':url}).text,'html.parser') def buscar_video(formats): try: with open("calidad.cfg", "r", encoding="utf-8") as f: calidad = int(f.read().strip()) except (FileNotFoundError, ValueError): calidad = 0 for format in formats[calidad:]: if not format['vcodec'].lower()=='none': return format['format_id'] def buscar_audio(formats): for format in formats: if format['resolution'].lower()=='audio only' and format['format_note'].lower()=='español': return format['format_id'] for format in formats: if format['resolution'].lower()=='audio only': return format['format_id'] def seleccionar_video(formats): try: with open("calidad.cfg", "r", encoding="utf-8") as f: calidad = int(f.read().strip()) except (FileNotFoundError, ValueError): calidad = 0 encontrado=False for format in formats[calidad:]: if not format['vcodec'].lower()=='none' and not format['resolution'].lower()=='audio only' and not format['acodec'].lower()=='none': encontrado=True return(format['format_id']) if encontrado==False: video=buscar_video(formats) audio=buscar_audio(formats) return(f'{video}+{audio}') async def convert(video_file): os.rename(video_file,f'{video_file}.original') out_put_file_name = '.'.join(video_file.split('.')[:-1])+'.mp4' video_file=f'{video_file}.original' file_generator_command = [ "ffmpeg", "-y", "-i", video_file, "-c", "copy", out_put_file_name ] process = await asyncio.create_subprocess_exec( *file_generator_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await process.communicate() e_response = stderr.decode().strip() t_response = stdout.decode().strip() if os.path.lexists(out_put_file_name): os.remove(video_file) return out_put_file_name else: return None @pyrogram.Client.on_message(pyrogram.filters.command(["upload"])) async def enviar_video(bot, update): if update.from_user.id in Translation.ALLOWED: if update.from_user.id in Config.BANNED_USERS: await bot.send_message( chat_id=update.chat.id, text=Translation.BANNED_USER_TEXT, reply_to_message_id=update.id ) return if update.reply_to_message is not None: if update.reply_to_message.caption is not None: filename=update.reply_to_message.caption.split('\n')[0].strip() try: ext=update.reply_to_message.video.file_name.split('.')[-1] except: try: ext=update.reply_to_message.document.file_name.split('.')[-1] except: ext='mp4' download_location = Config.DOWNLOAD_LOCATION + "/"+filename+'.'+ext else: download_location = Config.DOWNLOAD_LOCATION + "/" a = await bot.send_message( chat_id=update.chat.id, text=Translation.DOWNLOAD_START, reply_to_message_id=update.id ) c_time = time.time() the_real_download_location = await bot.download_media( message=update.reply_to_message, file_name=download_location, progress=progress_for_pyrogram, progress_args=( Translation.DOWNLOAD_START, a, c_time ) ) if the_real_download_location is not None: await bot.edit_message_text( text='Subiendo video, por favor espera...', chat_id=update.chat.id, message_id=a.id ) logger.info(the_real_download_location) video_file=the_real_download_location salida=getoutput(f'upload uq "{video_file}"') if os.path.lexists(video_file): os.remove(video_file) await bot.edit_message_text( text=salida, chat_id=update.chat.id, message_id=a.id, disable_web_page_preview=True ) else: await bot.send_message( chat_id=update.chat.id, text=Translation.REPLY_TO_DOC_FOR_C2V, reply_to_message_id=update.id ) else: await bot.send_message( chat_id=update.chat.id, text=Translation.REPLY_TO_UNALLOWED, reply_to_message_id=update.id ) @pyrogram.Client.on_message(pyrogram.filters.command(["subir"])) async def subir(bot, update): if len(update.command)==1: await bot.send_message( chat_id=update.chat.id, text='Debes escribir /subir seguido de un enlace directo de un video para subirlo', reply_to_message_id=update.id ) else: link = update.command[1].strip() salida=getoutput(f'python3 subir.py "{link}"') if len(update.command)>2: nombre=' '.join(update.command[2:]) salida_t=salida.split('\n') salida=[] for linea in salida_t: salida.append(f'{linea} | {nombre}') salida='\n'.join(salida) await bot.send_message( chat_id=update.chat.id, text=salida, reply_to_message_id=update.id ) @pyrogram.Client.on_message(pyrogram.filters.command(["convert"])) async def convert_to_video(bot, update): if update.from_user.id in Translation.ALLOWED: if update.from_user.id in Config.BANNED_USERS: await bot.send_message( chat_id=update.chat.id, text=Translation.BANNED_USER_TEXT, reply_to_message_id=update.id ) return if update.reply_to_message is not None: if update.reply_to_message.caption is not None: filename=update.reply_to_message.caption.split('\n')[0].strip() try: ext=update.reply_to_message.video.file_name.split('.')[-1] except: try: ext=update.reply_to_message.document.file_name.split('.')[-1] except: ext='mp4' download_location = Config.DOWNLOAD_LOCATION + "/"+filename+'.'+ext else: download_location = Config.DOWNLOAD_LOCATION + "/" description = Translation.CUSTOM_CAPTION_UL_FILE a = await bot.send_message( chat_id=update.chat.id, text=Translation.DOWNLOAD_START, reply_to_message_id=update.id ) c_time = time.time() the_real_download_location = await bot.download_media( message=update.reply_to_message, file_name=download_location, progress=progress_for_pyrogram, progress_args=( Translation.DOWNLOAD_START, a, c_time ) ) if the_real_download_location is not None: await bot.edit_message_text( text='Convirtiendo a video, por favor espera...', chat_id=update.chat.id, message_id=a.id ) logger.info(the_real_download_location) width = 0 height = 0 duration = 0 the_old_download_location=the_real_download_location description='.'.join(the_real_download_location.split('.')[:-1]).split('/')[-1] the_real_download_location=await convert(the_real_download_location) with createParser(the_real_download_location) as parser: metadata = extractMetadata(parser) if metadata.has("duration"): duration = metadata.get('duration').seconds thumb_image_path = Config.DOWNLOAD_LOCATION + "/" + str(update.from_user.id) + ".jpg" thumb=True if not os.path.exists(thumb_image_path): thumb_image_path = await take_screen_shot( the_real_download_location, os.path.dirname(the_real_download_location), random.randint( 0, duration - 1 ) ) else: thumb=False logger.info(thumb_image_path) with createParser(thumb_image_path) as parser: metadata = extractMetadata(parser) if metadata.has("width"): width = metadata.get("width") if metadata.has("height"): height = metadata.get("height") with Image.open(thumb_image_path) as img: img.convert("RGB") img.save(thumb_image_path) with Image.open(thumb_image_path) as img: img.resize((90, height)) img.save(thumb_image_path, "JPEG") c_time = time.time() await bot.send_video( chat_id=update.chat.id, video=the_real_download_location, caption=description, duration=duration, width=width, height=height, supports_streaming=True, thumb=thumb_image_path, reply_to_message_id=update.reply_to_message.id, progress=progress_for_pyrogram, progress_args=( Translation.UPLOAD_START, a, c_time ) ) try: os.remove(the_real_download_location) if thumb: os.remove(thumb_image_path) except: pass await bot.delete_messages( chat_id=update.chat.id, message_ids=a.id, ) else: await bot.send_message( chat_id=update.chat.id, text=Translation.REPLY_TO_DOC_FOR_C2V, reply_to_message_id=update.id ) else: await bot.send_message( chat_id=update.chat.id, text=Translation.REPLY_TO_UNALLOWED, reply_to_message_id=update.id ) ############ from pyrogram import enums from pyrogram.types import Message # Necesaria si no está importada globalmente # --- FUNCIÓN /LOG FINAL (Asumiendo que 'enums' y 'pyrogram' están importados) --- @pyrogram.Client.on_message(pyrogram.filters.command(["log"])) async def send_log_command(bot, update): """ Maneja el comando /log, utilizando enums.ParseMode.MARKDOWN correctamente. """ LOG_FILE = 'bot.log' MAX_MESSAGE_LENGTH = 4096 NUM_LINES_TO_SHOW = 10 # 1. Intentar leer el archivo de log try: with open(LOG_FILE, 'r', encoding='utf-8', errors='ignore') as f: lines = f.readlines() except FileNotFoundError: # Uso correcto de enums await update.reply_text(f"El archivo de log `{LOG_FILE}` no fue encontrado.", parse_mode=enums.ParseMode.MARKDOWN) return except Exception as e: # Uso correcto de enums await update.reply_text(f"Error al leer el log: `{e}`", parse_mode=enums.ParseMode.MARKDOWN) return # 2. Log vacío if not lines: # Uso correcto de enums await update.reply_text("Log vacío", parse_mode=enums.ParseMode.MARKDOWN) return # 3. Determinar el contenido a enviar en el mensaje de texto (extracto) if len(lines) <= (NUM_LINES_TO_SHOW * 2): contenido = "".join(lines) else: head = "".join(lines[:NUM_LINES_TO_SHOW]) tail = "".join(lines[-NUM_LINES_TO_SHOW:]) contenido = f"{head}\n... [Salto de líneas] ...\n{tail}" # 4. Asegurar que el contenido para el mensaje de texto no exceda el límite if len(contenido) > MAX_MESSAGE_LENGTH: contenido = contenido[-MAX_MESSAGE_LENGTH:] # 5. Enviar el extracto del log como mensaje de texto # Uso correcto de enums await update.reply_text(f"```\n{contenido}\n```", parse_mode=enums.ParseMode.MARKDOWN) # 6. Enviar el archivo de log completo como documento try: await bot.send_document( chat_id=update.chat.id, document=LOG_FILE, caption=LOG_FILE ) except Exception as e: # Uso correcto de enums await update.reply_text(f"Error al enviar el archivo de log completo: `{e}`", parse_mode=enums.ParseMode.MARKDOWN) @pyrogram.Client.on_message(pyrogram.filters.command(["calidad"])) async def subir(bot, update): try: with open("calidad.cfg", "r", encoding="utf-8") as f: calidad = f.read().strip() except (FileNotFoundError, ValueError): calidad = '0' # Si solo escriben /calidad sin número if len(update.command) == 1: mensaje = f"Envía el comando seguido del nivel: 0 ({calidades['0']}), 1 ({calidades['1']}), 2 ({calidades['2']})\nLa calidad está configurada en {calidad} ({calidades[calidad]})" await bot.send_message( chat_id=update.chat.id, text=mensaje, reply_to_message_id=update.id ) return # Tomamos el valor (el primer argumento después del comando) nivel_calidad = update.command[1].strip() if nivel_calidad not in ['0', '1', '2']: mensaje='Valor no válido. Solo se permite 0, 1 o 2.' await bot.send_message( chat_id=update.chat.id, text=mensaje, reply_to_message_id=update.id ) return try: with open("calidad.cfg", "w", encoding="utf-8") as f: f.write(nivel_calidad) mensaje = f"Calidad configurada en {nivel_calidad} ({calidades[nivel_calidad]})" except Exception as e: mensaje = f"❌ Error al guardar: {e}" await bot.send_message( chat_id=update.chat.id, text=mensaje, reply_to_message_id=update.id ) return # --- FIN DE FUNCIÓN /LOG ADAPTADA --- ############ @pyrogram.Client.on_message(pyrogram.filters.regex(r'^https?\:\/\/.*')) async def upload_video(bot, update): for url in update.text.splitlines(): try: if 'name' in locals(): del(name) url=url.strip() if '|' in url: lista=url.split('|') url=lista[0].strip() name='|'.join(lista[1:]).strip() elif '\t' in url: [url,name]=url.split('\t') url=url.strip() name=name.strip() enlace=url if update.from_user.id in Translation.ALLOWED: if update.from_user.id in Config.BANNED_USERS: await bot.send_message( chat_id=update.chat.id, text=Translation.BANNED_USER_TEXT, reply_to_message_id=update.id ) return if update.reply_to_message is None: description = Translation.CUSTOM_CAPTION_UL_FILE download_location = Config.DOWNLOAD_LOCATION + "/" a = await bot.send_message( chat_id=update.chat.id, text=f'Intentando descargar {enlace}', reply_to_message_id=update.id ) c_time = time.time() if 'uqload' in url.lower() or 'vudeo' in url.lower(): if not 'embed' in url.lower(): parts=url.split('/') url='/'.join(parts[0:-1]+['embed-'+parts[-1]]) if not url.endswith('.html'): url+='.html' html=requests.get(url).text url=re.search(r'https://[^"]+?/v\.mp4',html).group() if not 'name' in locals(): try: name= re.search(r'chromecast: { media: {title: "(.*)"}',str(html)).group(1) except: name=datetime.now().strftime("%Y_%m_%d-%I_%M_%S") if 'enpantallas' in url.lower(): elhtml=gethtml(url) script='\n'+elhtml.find_all('script',{'type':'text/javascript'})[-2].text arreglo=jsbeautifier.beautify(script) refile=re.compile(r'src: "(.+?.{2,}?)",\s+type: "video\/mp4",') url=refile.findall(arreglo)[0] if not 'name' in locals(): try: name= elhtml.title.text except: name=datetime.now().strftime("%Y_%m_%d-%I_%M_%S") wish=False lulu=False if 'lulu' in url.lower(): lulu=True if 'wish' in url.lower() or 'iplayerhls' in url.lower() or 'hlsflast' in url.lower() or 'dhcplay' in url.lower(): wish=True id=url.split('/')[-1] if not 'name' in locals(): # fileurl=f'https://playerwish.com/{id}' fileurl=f'https://hlswish.com/{id}' elhtml=gethtml(fileurl) try: wishname=html.h4.text.strip().split()[-1].split('.')[0] except: wishname='video' # url=f'https://playerwish.com/e/{id}' url=f'https://hlswish.com/e/{id}' elhtml=gethtml(url) script='\n'+elhtml.find_all('script',{'type':'text/javascript'})[-1].text arreglo=jsbeautifier.beautify(script) refile=re.compile(r'"hls2": "(.+?.{2,}?)"') final=refile.findall(arreglo)[0] myjson=json.loads(subprocess.check_output(['yt-dlp', '--no-check-certificate', '-J', final])) formats=myjson['formats'] if 'vidhidepr' in url.lower() or 'smoothpre' in url.lower(): wish=True id=url.split('/')[-1] if not 'name' in locals(): fileurl=f'https://smoothpre.com/f/{id}' elhtml=gethtml(fileurl) try: wishname=html.h4.text.strip().split()[-1].split('.')[0] except: wishname='video' url=f'https://smoothpre.com/embed/{id}' elhtml=gethtml(url) script='\n'+elhtml.find_all('script',{'type':'text/javascript'})[-1].text arreglo=jsbeautifier.beautify(script) refile=re.compile(r'"hls2": "(.+?.{2,}?)"') final=refile.findall(arreglo)[0] myjson=json.loads(subprocess.check_output(['yt-dlp', '--no-check-certificate', '-J', final])) formats=myjson['formats'] if not 'name' in locals(): name_command = ["yt-dlp", '--no-check-certificate', '--user-agent', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",'--referer',url, url, "--no-check-certificate", "--get-filename", "-c", "-o", "%(title)s"] name = subprocess.check_output(name_command).decode("utf-8")[:-1] if not wish else wishname try: # ext_command = ["yt-dlp", '--user-agent', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",'--referer',url, url, "--no-check-certificate", "--get-filename", "-c", "-o", "%(ext)s", '--extractor-args', "generic:impersonate"] ext_command = ["yt-dlp", '--no-check-certificate', '--user-agent', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",'--referer',url, url, "--no-check-certificate", "--get-filename", "-c", "-o", "%(ext)s"] ext = subprocess.check_output(ext_command).decode("utf-8")[:-1] if not wish else 'mp4' except: ext='mp4' name=f'{name}.{ext}' video_file=f'tmp/{name}' the_real_download_location=video_file if the_real_download_location is not None: await bot.edit_message_text( text=f'Descargando video desde {enlace}, por favor espera...', chat_id=update.chat.id, message_id=a.id ) logger.info(the_real_download_location) if wish: #file_command = ["yt-dlp", '--user-agent', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", '--referer', final, final, "--no-check-certificate", "-i", "-f", seleccionar_video(formats), "--merge-output-format", "mp4", "-o", video_file] file_command = [ "yt-dlp", '--user-agent', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", '--referer', final, final, "--no-check-certificate", "-i", "-f", seleccionar_video(formats), "--merge-output-format", "mp4", "-o", video_file, "--hls-prefer-native", # Usar el descargador nativo para HLS "--buffer-size", "16K", # Buffer pequeño para no saturar "--concurrent-fragments", "20", # El doble de lo que tienes, pero sin morir "--socket-timeout", "45", # Mucha más paciencia para evitar los cortes "--retries", "20" ] else: file_command = ["yt-dlp", '--user-agent', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", '--referer',url, url, "--no-check-certificate", "-i", "-f", "18/22/hls-719/mpd-2/mpd-3/mp4-360p/360p/hls-360p/480/sd/best[height<=360]/best[height<=480]/bestvideo[height<=360]+audio0-Español/bestvideo[height<=480]+audio0-Español/bestvideo[height<=360]+bestaudio[language=es]/bestvideo[height<=480]+bestaudio[language=es]/bestvideo[height<=360]+bestaudio/bestvideo[height<=480]+bestaudio/b", "--merge-output-format", "mp4", "-o", video_file] file_command += ["--external-downloader", "./aria2c", "--external-downloader-args", "aria2c: --file-allocation=none -x16 -c -q --summary-interval=0 --console-log-level=error"] # file_command += ['--extractor-args', "generic:impersonate", "--external-downloader", "aria2c", "--external-downloader-args", "aria2c: --file-allocation=none -x16 -c -q --summary-interval=0 --console-log-level=error"] # file_command += ["--external-downloader", "./aria2c", "--external-downloader-args", "aria2c: --file-allocation=none -x16 -c -q --summary-interval=0 --console-log-level=error"] subprocess.run(file_command) width = 0 height = 0 duration = 0 the_old_download_location=the_real_download_location description='.'.join(the_real_download_location.split('.')[:-1]).split('/')[-1] if ext.lower()!='mp4': the_real_download_location=await convert(the_real_download_location) with createParser(the_real_download_location) as parser: metadata = extractMetadata(parser) if metadata.has("duration"): duration = metadata.get('duration').seconds thumb_image_path = Config.DOWNLOAD_LOCATION + "/" + str(update.from_user.id) + ".jpg" thumb=True if not os.path.exists(thumb_image_path): thumb_image_path = await take_screen_shot( the_real_download_location, os.path.dirname(the_real_download_location), random.randint( 0, duration - 1 ) ) else: thumb=False logger.info(thumb_image_path) with createParser(thumb_image_path) as parser: metadata = extractMetadata(parser) if metadata.has("width"): width = metadata.get("width") if metadata.has("height"): height = metadata.get("height") with Image.open(thumb_image_path) as img: img.convert("RGB") img.save(thumb_image_path) with Image.open(thumb_image_path) as img: img.resize((90, height)) img.save(thumb_image_path, "JPEG") c_time = time.time() await bot.send_video( chat_id=update.chat.id, video=the_real_download_location, caption=description, duration=duration, width=width, height=height, supports_streaming=True, # reply_markup=reply_markup, thumb=thumb_image_path, #reply_to_message_id=update.reply_to_message.id, progress=progress_for_pyrogram, progress_args=( Translation.UPLOAD_START, a, c_time ) ) for file in [the_real_download_location, f'{the_old_download_location}.original']: try: os.remove(file) except: pass if thumb: os.remove(thumb_image_path) await bot.delete_messages( chat_id=update.chat.id, message_ids=a.id, ) else: await bot.send_message( chat_id=update.chat.id, text=Translation.REPLY_TO_DOC_FOR_C2V, reply_to_message_id=update.id ) else: await bot.send_message( chat_id=update.chat.id, text=Translation.REPLY_TO_UNALLOWED, reply_to_message_id=update.id ) except Exception as e: #await bot.send_message( #chat_id=update.chat.id, #text=e, #reply_to_message_id=update.id #) #print(e) await bot.delete_messages( chat_id=update.chat.id, message_ids=a.id )