├── .gitignore ├── Dockerfile ├── Procfile ├── README.md ├── app.json ├── config.py ├── main.py ├── requirements.txt ├── runtime.txt ├── shamil ├── callback.py ├── commands.py ├── player.py ├── radio.py ├── song.py └── stickers.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | #---------------------------------# 2 | # Shamil X MwK Musics - @MwKLinks # 3 | #---------------------------------# 4 | 5 | /PATCH_*.sh 6 | 7 | /app/etc/local.xml 8 | 9 | /media/* 10 | !/media/.htaccess 11 | 12 | !/media/customer 13 | /media/customer/* 14 | !/media/customer/.htaccess 15 | 16 | !/media/dhl 17 | /media/dhl/* 18 | !/media/dhl/logo.jpg 19 | 20 | !/media/downloadable 21 | /media/downloadable/* 22 | !/media/downloadable/.htaccess 23 | 24 | !/media/xmlconnect 25 | /media/xmlconnect/* 26 | 27 | !/media/xmlconnect/custom 28 | /media/xmlconnect/custom/* 29 | !/media/xmlconnect/custom/ok.gif 30 | 31 | !/media/xmlconnect/original 32 | /media/xmlconnect/original/* 33 | !/media/xmlconnect/original/ok.gif 34 | 35 | !/media/xmlconnect/system 36 | /media/xmlconnect/system/* 37 | !/media/xmlconnect/system/ok.gif 38 | 39 | /var/* 40 | !/var/.htaccess 41 | 42 | !/var/package 43 | /var/package/* 44 | !/var/package/*.xml 45 | 46 | *.pyc 47 | *.raw 48 | *.log 49 | *.session 50 | unknown_errors.txt 51 | *.session-journal 52 | .env 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster 2 | WORKDIR /app 3 | COPY requirements.txt requirements.txt 4 | RUN pip3 install -r requirements.txt 5 | RUN apt update && apt upgrade -y 6 | RUN apt install ffmpeg -y 7 | COPY . . 8 | 9 | CMD python3 main.py 10 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 main.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bot To Stream Musics on PyTGcalls with Channel Support. 2 | 3 | A Telegram Bot to Play Audio in Voice Chats With Supports Live streaming from youtube and Mega Radio Fm Streamings 4 | 5 | 6 | ## Deploy to Heroku 7 | 8 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/shamilhabeebnelli/MwK-Musics/tree/main) 9 | 10 | ## Deploy On Railway 11 | 12 | [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https://github.com/shamilhabeebnelli/mwk-musics/tree/main&envs=API_ID,API_HASH,BOT_TOKEN,SESSION_STRING,CHAT,ADMINS,STREAM_URL,ADMIN_ONLY,DUR,LOG_GROUP&optionalEnvs=LOG_GROUP,ADMIN_ONLY,DUR&API_IDDesc=Your+App+id+and+hash+:get+it+from+my.telegram.org/apps&API_HASHDesc=Your+App+id+and+hash+:get+it+from+my.telegram.org/apps&BOT_TOKENDesc=Your+Bot+Api+Token+Get+it+from+@botfather&SESSION_STRINGDesc=your+pyrogram+session+string+:get+it+from+@gensessionbot&ADMINSDesc=your+telegram+ID+And+ids+of+whom+you+wish+to+controll+this&ADMIN_ONLYDesc=Optional+set+this+Y+or+N&LOG_GROUPDesc=Optional:+ID+of+your+bot+commands+log+group;+only+needed+if+you+are+playing+on+channel&CHATDesc=ID+of+channel/group+were+you+want+the+bot+user+to+play&STREAM_URLDesc=Stream+URL+of+radio+station+or+a+youtube+live+video+to+stream+when+the+bot+starts+or+with+/r+command&ADMIN_ONLYDefault=Y&STREAM_URLDefault=https://bit.ly/MwKradio&ADMINSDefault=749673781&DURDefault=300&CHATDefault=-100) 13 | 14 | # Vars: 15 | 1. `API_ID` : Get From my.telegram.org 16 | 2. `API_HASH` : Get from my.telegram.org 17 | 3. `BOT_TOKEN` : @Botfather 18 | 4. `SESSION_STRING` : Generate From [here](https://t.me/genstr_bot) 19 | 5. `CHAT` : ID of Channel/Group where the bot plays Music. 20 | 6. `LOG_GROUP` : Group to send Playlist, if CHAT is a Group 21 | 7. `ADMINS` : ID of users who can use admin commands. 22 | 8. `STREAM_URL` : Stream URL of radio station or a youtube live video to stream when the bot starts or with /r command. 23 | 24 | #### Disclaimer n Credits 25 | ###### This is Old Version of [subinps/musicplayer](https://github.com/subinps/MusicPlayer) with some Allara Chillara Maattam 26 | ###### Fork [Main Project](https://github.com/subinps/MusicPlayer) for Latest Update 27 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MwK-Musics PyTGcalls", 3 | "description": "Telegram Bot to Play Audio And Live Streams in PyTGcalls", 4 | "logo": "https://telegra.ph/file/a3937c3ddc19bb3300d89.jpg", 5 | "repository": "https://github.com/shamilhabeebnelli/MwK-Musics", 6 | "keywords": [ 7 | "telegram", 8 | "bot", 9 | "voicechat", 10 | "music", 11 | "python", 12 | "pyrogram", 13 | "pytgcalls", 14 | "tgcalls", 15 | "voip" 16 | ], 17 | "env": { 18 | "API_ID": { 19 | "description": "api_id part of your Telegram API Key from my.telegram.org/apps", 20 | "required": true 21 | }, 22 | "API_HASH": { 23 | "description": "api_hash part of your Telegram API Key from my.telegram.org/apps", 24 | "required": true 25 | }, 26 | "BOT_TOKEN": { 27 | "description": "Bot token of Bot, get from @Botfather", 28 | "required": true 29 | }, 30 | "ARQ_API": { 31 | "description": "get it for free from @ARQRobot", 32 | "required": true 33 | }, 34 | "SESSION_STRING": { 35 | "description": "Session string, use @genstr_bot", 36 | "required": true 37 | }, 38 | "CHAT": { 39 | "description": "ID of Channel or Group where the Bot plays Music", 40 | "value": "-100", 41 | "required": true 42 | }, 43 | "LOG_GROUP": { 44 | "description": "ID of the group to send playlist If CHAT is a Group, if channel thenleave blank", 45 | "required": false 46 | }, 47 | "ADMINS": { 48 | "description": "ID of Users who can use Admin commands seperated by space)", 49 | "value": "749673781", 50 | "required": true 51 | }, 52 | "STREAM_URL": { 53 | "description": "URL of Radio station or Youtube live video url to stream with /r command", 54 | "value": "https://bit.ly/MwKradio", 55 | "required": true 56 | }, 57 | "ADMIN_ONLY": { 58 | "description": "Make All Commands Admin Only Usable Y/N", 59 | "value": "Y", 60 | "required": false 61 | }, 62 | "DUR": { 63 | "description": "Keep This Blank if you dont now what it is, Maximum duration of song to play.(Optional)", 64 | "value": "300", 65 | "required": false 66 | } 67 | }, 68 | "formation": { 69 | "worker": { 70 | "quantity": 1, 71 | "size": "free" 72 | } 73 | }, 74 | "buildpacks": [ 75 | { 76 | "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest" 77 | }, 78 | { 79 | "url": "heroku/python" 80 | } 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import re 4 | from youtube_dl import YoutubeDL 5 | ydl_opts = { 6 | "geo-bypass": True, 7 | "nocheckcertificate": True 8 | } 9 | ydl = YoutubeDL(ydl_opts) 10 | links=[] 11 | finalurl="" 12 | STREAM=os.environ.get("STREAM_URL", "http://node-25.zeno.fm/kezsc0y2wwzuv?listening-from-radio-garden=1622271954020&rj-ttl=5&rj-tok=AAABec5bAE4Aj31dmRAEFgcbvw") 13 | regex = r"^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+" 14 | match = re.match(regex,STREAM) 15 | if match: 16 | meta = ydl.extract_info(STREAM, download=False) 17 | formats = meta.get('formats', [meta]) 18 | for f in formats: 19 | links.append(f['url']) 20 | finalurl=links[0] 21 | else: 22 | finalurl=STREAM 23 | 24 | class Config: 25 | ADMIN = os.environ.get("ADMINS", '') 26 | ADMINS = [int(admin) if re.search('^\d+$', admin) else admin for admin in (ADMIN).split()] 27 | API_ID = int(os.environ.get("API_ID", '')) 28 | CHAT = int(os.environ.get("CHAT", "")) 29 | LOG_GROUP=os.environ.get("LOG_GROUP", "") 30 | if LOG_GROUP: 31 | LOG_GROUP=int(LOG_GROUP) 32 | else: 33 | LOG_GROUP=None 34 | STREAM_URL=finalurl 35 | ADMIN_ONLY=os.environ.get("ADMIN_ONLY", "Y") 36 | DURATION_LIMIT=int(os.environ.get("DUR", 15)) 37 | API_HASH = os.environ.get("API_HASH", "") 38 | BOT_TOKEN = os.environ.get("BOT_TOKEN", "") 39 | SESSION = os.environ.get("SESSION_STRING", "SESSION") 40 | DOWNLOAD_LOCATION = os.environ.get("DOWNLOAD_LOCATION", "./DOWNLOADS/") 41 | playlist=[] 42 | msg = {} 43 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | 2 | from pyrogram import Client, idle, filters 3 | import os 4 | from threading import Thread 5 | import sys 6 | from config import Config 7 | from utils import mp 8 | import asyncio 9 | from pyrogram.raw import functions, types 10 | 11 | 12 | CHAT=Config.CHAT 13 | bot = Client( 14 | "MwkVC", 15 | Config.API_ID, 16 | Config.API_HASH, 17 | bot_token=Config.BOT_TOKEN, 18 | plugins=dict(root="shamil") 19 | ) 20 | if not os.path.isdir("./downloads"): 21 | os.makedirs("./downloads") 22 | async def main(): 23 | async with bot: 24 | await mp.start_radio() 25 | 26 | bot.run(main()) 27 | bot.start() 28 | @bot.on_message(filters.command("update") & filters.user(Config.ADMINS)) 29 | def restart(client, message): 30 | message.reply_text("Updating Bot...") 31 | Thread( 32 | target=stop_and_restart 33 | ).start() 34 | 35 | idle() 36 | bot.stop() 37 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pyrogram==1.2.9 2 | TgCrypto==1.2.2 3 | ffmpeg-python 4 | psutil 5 | pytgcalls 6 | wheel 7 | python-arq 8 | aiohttp==3.6.2 9 | youtube_dl 10 | youtube_search_python 11 | youtube_search 12 | wget 13 | requests 14 | python-decouple 15 | aiofiles 16 | python-dotenv 17 | asyncio 18 | search_engine_parser 19 | Pillow 20 | future 21 | tswift 22 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.2 2 | -------------------------------------------------------------------------------- /shamil/callback.py: -------------------------------------------------------------------------------- 1 | # A Subinps Project 2 | # Pyrogram - Telegram MTProto API Client Library for Python 3 | # Copyright (C) 2017-2020 Dan 4 | # 5 | # This file is part of Pyrogram. 6 | # 7 | # Pyrogram is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Lesser General Public License as published 9 | # by the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Pyrogram is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Pyrogram. If not, see . 19 | 20 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 21 | from pyrogram import Client, emoji 22 | from utils import mp 23 | from config import Config 24 | playlist=Config.playlist 25 | 26 | HELP = """ 27 | 28 | 🎧 I Can Play Music On VoiceChats 🤪 29 | 30 | 🎶 **Common Commands**: 31 | • `/current` __Show current playing song__ 32 | • `/help` __Show help for commands__ 33 | • `/mwk` __Shows the playlist__ 34 | • `/stickerid` __To Get Id Of Replied Sticker__ 35 | 36 | 🎶 **Admin Commands**: 37 | • `/play` __Reply to an audio file or YouTube link to play it or use /p __ 38 | • `/dplay` __Play music from Deezer, Use /d __ 39 | • `/skip [n]` __...Skip current or n where n >= 2__ 40 | • `/join` __Join voice chat__ 41 | • `/leave` __Leave current voice chat__ 42 | • `/mwk` __Check which VC is joined__ 43 | • `/stop` __Stop playing__ 44 | • `/radio` __Start Radio__ 45 | • `/stopradio` __Stops Radio Stream__ 46 | • `/replay` __Play from the beginning__ 47 | • `/clear` __Remove unused RAW PCM files__ 48 | • `/pause` __Pause playing__ 49 | • `/resume` __Resume playing__ 50 | • `/mute` __Mute in VC__ 51 | • `/unmute` __Unmute in VC__ 52 | • `/update` __Update Current Settings n Restarts the Bot__ 53 | 54 | © Powered By 55 | [ __@mwkBoTs | @subin_works__ ] 56 | """ 57 | 58 | 59 | @Client.on_callback_query() 60 | async def cb_handler(client: Client, query: CallbackQuery): 61 | if query.data == "rp": 62 | group_call = mp.group_call 63 | if not playlist: 64 | return 65 | group_call.restart_playout() 66 | if not playlist: 67 | pl = f"😖 Nothing On Que Ser" 68 | else: 69 | pl = f"🎧 **Playlist**:\n" + "\n".join([ 70 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 71 | for i, x in enumerate(playlist) 72 | ]) 73 | await query.edit_message_text( 74 | f"{pl}", 75 | parse_mode="Markdown", 76 | reply_markup=InlineKeyboardMarkup( 77 | [ 78 | [ 79 | InlineKeyboardButton("Replay", callback_data="rp"), 80 | InlineKeyboardButton("Pause", callback_data="ps") 81 | ],[ 82 | InlineKeyboardButton("Skip", callback_data="sk"), 83 | InlineKeyboardButton("Report Bug", url="subinps_bot") 84 | ] 85 | ] 86 | ) 87 | ) 88 | 89 | elif query.data == "ps": 90 | if not playlist: 91 | return 92 | else: 93 | mp.group_call.pause_playout() 94 | pl = f"🎧 **Playlist**:\n" + "\n".join([ 95 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 96 | for i, x in enumerate(playlist) 97 | ]) 98 | await query.edit_message_text(f"{emoji.PLAY_OR_PAUSE_BUTTON} Paused\n\n{pl}", 99 | reply_markup=InlineKeyboardMarkup( 100 | [ 101 | [ 102 | InlineKeyboardButton("Replay", callback_data="replace"), 103 | InlineKeyboardButton("Resume", callback_data="resume") 104 | ],[ 105 | InlineKeyboardButton("Skip", callback_data="skip"), 106 | InlineKeyboardButton("Updates", url='t.me/mwkBoTs') 107 | ], 108 | ] 109 | ) 110 | ) 111 | 112 | 113 | elif query.data == "rs": 114 | if not playlist: 115 | return 116 | else: 117 | mp.group_call.resume_playout() 118 | pl = f"🎧 **Playlist**:\n" + "\n".join([ 119 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 120 | for i, x in enumerate(playlist) 121 | ]) 122 | await query.edit_message_text(f"{emoji.PLAY_OR_PAUSE_BUTTON} Resumed\n\n{pl}", 123 | reply_markup=InlineKeyboardMarkup( 124 | [ 125 | [ 126 | InlineKeyboardButton("Replay", callback_data="rp"), 127 | InlineKeyboardButton("Pause", callback_data="ps") 128 | ],[ 129 | InlineKeyboardButton("Skip", callback_data="sk"), 130 | InlineKeyboardButton("Support", url="https://t.me/redbullfed") 131 | ], 132 | ] 133 | ) 134 | ) 135 | 136 | elif query.data=="sk": 137 | if not playlist: 138 | return 139 | else: 140 | await mp.skip_current_playing() 141 | pl = f"🎧 **Playlist**:\n" + "\n".join([ 142 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 143 | for i, x in enumerate(playlist) 144 | ]) 145 | try: 146 | await query.edit_message_text(f"{emoji.PLAY_OR_PAUSE_BUTTON} Skipped\n\n{pl}", 147 | reply_markup=InlineKeyboardMarkup( 148 | [ 149 | [ 150 | InlineKeyboardButton("Replay", callback_data="rp"), 151 | InlineKeyboardButton("Pause", callback_data="ps") 152 | ],[ 153 | InlineKeyboardButton("Skip", callback_data="sk"), 154 | InlineKeyboardButton("Updates", url="t.me/subin_works") 155 | 156 | ], 157 | ] 158 | ) 159 | ) 160 | except: 161 | pass 162 | elif query.data=="help": 163 | buttons = [ 164 | [ 165 | InlineKeyboardButton('📢 Updates', url='https://t.me/mwklinks'), 166 | InlineKeyboardButton('💬 Support', url='https://t.me/redbullfed') 167 | ],[ 168 | InlineKeyboardButton('🤖 Developer', url='t.me/subinps'), 169 | InlineKeyboardButton('Bugs', url='t.me/subin_works') 170 | ],[ 171 | InlineKeyboardButton('📜 Source Code 📜', url='https://github.com/shamilhabeebnelli/mwk-musics'), 172 | ] 173 | ] 174 | reply_markup = InlineKeyboardMarkup(buttons) 175 | await query.edit_message_text( 176 | HELP, 177 | reply_markup=reply_markup 178 | 179 | ) 180 | -------------------------------------------------------------------------------- /shamil/commands.py: -------------------------------------------------------------------------------- 1 | # A Subinps Project 2 | # Pyrogram - Telegram MTProto API Client Library for Python 3 | # Copyright (C) 2017-2020 Dan 4 | # 5 | # This file is part of Pyrogram. 6 | # 7 | # Pyrogram is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Lesser General Public License as published 9 | # by the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Pyrogram is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Pyrogram. If not, see . 19 | 20 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 21 | from pyrogram import Client, filters 22 | import signal 23 | from utils import USERNAME, FFMPEG_PROCESSES 24 | from config import Config 25 | import os 26 | import sys 27 | U=USERNAME 28 | CHAT=Config.CHAT 29 | 30 | 31 | HOME_TEXT = "Helo, [{}](tg://user?id={})\n\n• Iam A Bot Project by MwK MusicS\n• I Can Manage Group VC's\n\n• Hit /help to know about available commands." 32 | HELP = """ 33 | 🎧 I Can Play Music On VoiceChats 🤪 34 | 35 | 🎶 **Common Commands**: 36 | • `/current` __Show current playing song__ 37 | • `/help` __Show help for commands__ 38 | • `/mwk` __Shows the playlist__ 39 | • `/stickerid` __To Get Id Of Replied Sticker__ 40 | 41 | 🎶 **Admin Commands**: 42 | • `/play` __Reply to an audio file or YouTube link to play it or use /p __ 43 | • `/dplay` __Play music from Deezer, Use /d __ 44 | • `/skip [n]` __...Skip current or n where n >= 2__ 45 | • `/join` __Join voice chat__ 46 | • `/leave` __Leave current voice chat__ 47 | • `/mwk` __Check which VC is joined__ 48 | • `/stop` __Stop playing__ 49 | • `/radio` __Start Radio__ 50 | • `/stopradio` __Stops Radio Stream__ 51 | • `/replay` __Play from the beginning__ 52 | • `/clear` __Remove unused RAW PCM files__ 53 | • `/pause` __Pause playing__ 54 | • `/resume` __Resume playing__ 55 | • `/mute` __Mute in VC__ 56 | • `/unmute` __Unmute in VC__ 57 | • `/update` __Update Current Settings n Restarts the Bot__ 58 | 59 | © Powered By 60 | [ __@mwkBoTs | @subin_works__ ] 61 | """ 62 | 63 | 64 | 65 | @Client.on_message(filters.command('start')) 66 | async def start(client, message): 67 | buttons = [ 68 | [ 69 | InlineKeyboardButton("❔ How To Use Me ❔", callback_data="help"), 70 | ],[ 71 | InlineKeyboardButton('📢 Updates', url='https://t.me/mwklinks'), 72 | InlineKeyboardButton('💬 Support', url='https://t.me/redbullfed') 73 | ],[ 74 | InlineKeyboardButton('🤖 Developer', url='t.me/subinps'), 75 | InlineKeyboardButton('Bugs', url='t.me/subin_works') 76 | ],[ 77 | InlineKeyboardButton('📜 Source Code 📜', url='https://github.com/shamilhabeebnelli/mwk-musics'), 78 | ]] 79 | reply_markup = InlineKeyboardMarkup(buttons) 80 | await message.reply_photo(photo="https://telegra.ph/file/a3937c3ddc19bb3300d89.jpg", caption=HOME_TEXT.format(message.from_user.first_name, message.from_user.id), reply_markup=reply_markup) 81 | await message.delete() 82 | 83 | 84 | @Client.on_message(filters.command("help")) 85 | async def show_help(client, message): 86 | buttons = [ 87 | [ 88 | InlineKeyboardButton('📢 Updates', url='https://t.me/mwklinks'), 89 | InlineKeyboardButton('💬 Support', url='https://t.me/redbullfed') 90 | ],[ 91 | InlineKeyboardButton('🤖 Developer', url='t.me/subinps'), 92 | InlineKeyboardButton('Bugs', url='t.me/subin_works') 93 | ],[ 94 | InlineKeyboardButton('📜 Source Code 📜', url='https://github.com/shamilhabeebnelli/mwk-musics'), 95 | ] 96 | ] 97 | reply_markup = InlineKeyboardMarkup(buttons) 98 | await message.reply_photo(photo="https://telegra.ph/file/a3937c3ddc19bb3300d89.jpg", caption=HELP, reply_markup=reply_markup) 99 | await message.delete() 100 | -------------------------------------------------------------------------------- /shamil/player.py: -------------------------------------------------------------------------------- 1 | # A Subinps Project 2 | # Pyrogram - Telegram MTProto API Client Library for Python 3 | # Copyright (C) 2017-2020 Dan 4 | # 5 | # This file is part of Pyrogram. 6 | # 7 | # Pyrogram is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Lesser General Public License as published 9 | # by the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Pyrogram is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Pyrogram. If not, see . 19 | 20 | import os 21 | from youtube_dl import YoutubeDL 22 | from config import Config 23 | from pyrogram import Client, filters, emoji 24 | from pyrogram.methods.messages.download_media import DEFAULT_DOWNLOAD_DIR 25 | from pyrogram.types import Message 26 | from utils import mp, RADIO, USERNAME, FFMPEG_PROCESSES 27 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 28 | from youtube_search import YoutubeSearch 29 | from pyrogram import Client 30 | from aiohttp import ClientSession 31 | import signal 32 | import re 33 | U=USERNAME 34 | LOG_GROUP=Config.LOG_GROUP 35 | ADMIN_ONLY=Config.ADMIN_ONLY 36 | DURATION_LIMIT = Config.DURATION_LIMIT 37 | session = ClientSession() 38 | playlist=Config.playlist 39 | 40 | ADMINS=Config.ADMINS 41 | CHAT=Config.CHAT 42 | 43 | @Client.on_message(filters.command(["play", f"play@{U}"]) | filters.audio & filters.private) 44 | async def yplay(_, message: Message): 45 | if ADMIN_ONLY == "Y": 46 | admins=[626664225] 47 | grpadmins=await _.get_chat_members(chat_id=CHAT, filter="administrators") 48 | for administrator in grpadmins: 49 | admins.append(administrator.user.id) 50 | if message.from_user.id not in admins: 51 | await message.reply_sticker("CAACAgUAAxkBAAIJM2DTpi52NSM-O-KnYcC1IzbJos8HAAK6AQACsm0wVffnRbQlKgeTHwQ") 52 | await message.delete() 53 | return 54 | type="" 55 | yturl="" 56 | ysearch="" 57 | if message.audio: 58 | type="audio" 59 | m_audio = message 60 | elif message.reply_to_message and message.reply_to_message.audio: 61 | type="audio" 62 | m_audio = message.reply_to_message 63 | else: 64 | if message.reply_to_message: 65 | link=message.reply_to_message.text 66 | regex = r"^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+" 67 | match = re.match(regex,link) 68 | if match: 69 | type="youtube" 70 | yturl=link 71 | elif " " in message.text: 72 | text = message.text.split(" ", 1) 73 | query = text[1] 74 | regex = r"^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+" 75 | match = re.match(regex,query) 76 | if match: 77 | type="youtube" 78 | yturl=query 79 | else: 80 | type="query" 81 | ysearch=query 82 | else: 83 | await message.reply_text("lmao 😇, Lemma Gib Me something to play or reply /p to any audio file.") 84 | return 85 | user=f"[{message.from_user.first_name}](tg://user?id={message.from_user.id})" 86 | group_call = mp.group_call 87 | if type=="audio": 88 | if round(m_audio.audio.duration / 60) > DURATION_LIMIT: 89 | await message.reply_text(f"Oops Its Seems too Lengthy is about {round(m_audio.audio.duration/60)} minute(s) The Limit you can use is {DURATION_LIMIT} minute(s)") 90 | return 91 | if playlist and playlist[-1][2] \ 92 | == m_audio.audio.file_id: 93 | await message.reply_text(f"📜 Already Qued Bruh") 94 | return 95 | data={1:m_audio.audio.title, 2:m_audio.audio.file_id, 3:"telegram", 4:user} 96 | playlist.append(data) 97 | if len(playlist) == 1: 98 | m_status = await message.reply_text( 99 | f"fetching data from m.youtube.com" 100 | ) 101 | await mp.download_audio(playlist[0]) 102 | if 1 in RADIO: 103 | if group_call: 104 | group_call.input_filename = '' 105 | RADIO.remove(1) 106 | RADIO.add(0) 107 | process = FFMPEG_PROCESSES.get(CHAT) 108 | if process: 109 | process.send_signal(signal.SIGTERM) 110 | if not group_call.is_connected: 111 | await mp.start_call() 112 | file=playlist[0][1] 113 | group_call.input_filename = os.path.join( 114 | _.workdir, 115 | DEFAULT_DOWNLOAD_DIR, 116 | f"{file}.raw" 117 | ) 118 | 119 | await m_status.delete() 120 | print(f"- START PLAYING: {playlist[0][1]}") 121 | if not playlist: 122 | pl = f"📻 Nothing Is On Que" 123 | else: 124 | pl = f"🎧 **Que**:\n" + "\n".join([ 125 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 126 | for i, x in enumerate(playlist) 127 | ]) 128 | if LOG_GROUP and message.chat.id != LOG_GROUP: 129 | await message.reply_text(pl) 130 | for track in playlist[:2]: 131 | await mp.download_audio(track) 132 | if LOG_GROUP: 133 | await mp.send_playlist() 134 | else: 135 | await message.reply_text(pl) 136 | if type=="youtube" or type=="query": 137 | if type=="youtube": 138 | msg = await message.reply_text("**Fetching Song Data From YouTube...**") 139 | url=yturl 140 | elif type=="query": 141 | try: 142 | msg = await message.reply_text("**Seems Like Something Found...**") 143 | ytquery=ysearch 144 | results = YoutubeSearch(ytquery, max_results=1).to_dict() 145 | url = f"https://youtube.com{results[0]['url_suffix']}" 146 | title = results[0]["title"][:40] 147 | except Exception as e: 148 | await msg.edit( 149 | "Sed Nothing Is Found For Your Query, Ensure spelling or Try inline" 150 | ) 151 | print(str(e)) 152 | return 153 | else: 154 | return 155 | ydl_opts = { 156 | "geo-bypass": True, 157 | "nocheckcertificate": True 158 | } 159 | ydl = YoutubeDL(ydl_opts) 160 | info = ydl.extract_info(url, False) 161 | duration = round(info["duration"] / 60) 162 | print(info) 163 | title= info["title"] 164 | if int(duration) > DURATION_LIMIT: 165 | await message.reply_text(f"Oops Its Seems too Lengthy is about {round(m_audio.audio.duration/60)} minute(s) The Limit you can use is {DURATION_LIMIT} minute(s)") 166 | return 167 | 168 | data={1:title, 2:url, 3:"youtube", 4:user} 169 | playlist.append(data) 170 | group_call = mp.group_call 171 | client = group_call.client 172 | if len(playlist) == 1: 173 | m_status = await msg.edit( 174 | f"Im on it bruh..." 175 | ) 176 | await mp.download_audio(playlist[0]) 177 | if 1 in RADIO: 178 | if group_call: 179 | group_call.input_filename = '' 180 | RADIO.remove(1) 181 | RADIO.add(0) 182 | process = FFMPEG_PROCESSES.get(CHAT) 183 | if process: 184 | process.send_signal(signal.SIGTERM) 185 | if not group_call.is_connected: 186 | await mp.start_call() 187 | file=playlist[0][1] 188 | group_call.input_filename = os.path.join( 189 | client.workdir, 190 | DEFAULT_DOWNLOAD_DIR, 191 | f"{file}.raw" 192 | ) 193 | 194 | await m_status.delete() 195 | print(f"- START PLAYING: {playlist[0][1]}") 196 | if not playlist: 197 | pl = f"📻 Nothing Is On Que" 198 | else: 199 | pl = f"🎧 **Que**:\n" + "\n".join([ 200 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 201 | for i, x in enumerate(playlist) 202 | ]) 203 | if LOG_GROUP and message.chat.id != LOG_GROUP: 204 | await message.reply_text(pl) 205 | for track in playlist[:2]: 206 | await mp.download_audio(track) 207 | if LOG_GROUP: 208 | await mp.send_playlist() 209 | else: 210 | await message.reply_text(pl) 211 | await message.delete() 212 | 213 | @Client.on_message(filters.command(["current", f"current@{U}"])) 214 | async def player(_, m: Message): 215 | if not playlist: 216 | await m.reply_text(f"{emoji.NO_ENTRY} No songs are playing") 217 | return 218 | else: 219 | pl = f"{emoji.PLAY_BUTTON} **Playlist**:\n" + "\n".join([ 220 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 221 | for i, x in enumerate(playlist) 222 | ]) 223 | await m.reply_text( 224 | pl, 225 | parse_mode="Markdown", 226 | reply_markup=InlineKeyboardMarkup( 227 | [ 228 | [ 229 | InlineKeyboardButton("Replay", callback_data="rp"), 230 | InlineKeyboardButton("Pause", callback_data="ps"), 231 | InlineKeyboardButton("Skip", callback_data="sk") 232 | 233 | ], 234 | 235 | ] 236 | ) 237 | ) 238 | await m.delete() 239 | 240 | @Client.on_message(filters.command(["skip", f"skip@{U}"]) & filters.user(ADMINS)) 241 | async def skip_track(_, m: Message): 242 | group_call = mp.group_call 243 | if not group_call.is_connected: 244 | await m.reply("Playing, Playlist, and Que is utterly empty like your brain 🤪") 245 | return 246 | if len(m.command) == 1: 247 | await mp.skip_current_playing() 248 | if not playlist: 249 | pl = f"📻 Empty Que, Like Your Brain" 250 | else: 251 | pl = f"{emoji.PLAY_BUTTON} **Playlist**:\n" + "\n".join([ 252 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 253 | for i, x in enumerate(playlist) 254 | ]) 255 | if LOG_GROUP and m.chat.id != LOG_GROUP: 256 | await m.reply_text(pl) 257 | if LOG_GROUP: 258 | await mp.send_playlist() 259 | else: 260 | await m.reply_text(pl) 261 | else: 262 | try: 263 | items = list(dict.fromkeys(m.command[1:])) 264 | items = [int(x) for x in items if x.isdigit()] 265 | items.sort(reverse=True) 266 | text = [] 267 | for i in items: 268 | if 2 <= i <= (len(playlist) - 1): 269 | audio = f"{playlist[i].audio.title}" 270 | playlist.pop(i) 271 | text.append(f"{emoji.WASTEBASKET} {i}. **{audio}**") 272 | else: 273 | text.append(f"{emoji.CROSS_MARK} {i}") 274 | await m.reply_text("\n".join(text)) 275 | if not playlist: 276 | pl = f"📻 Empty Que, Like your Brain" 277 | else: 278 | pl = f"{emoji.PLAY_BUTTON} **Playlist**:\n" + "\n".join([ 279 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 280 | for i, x in enumerate(playlist) 281 | ]) 282 | if LOG_GROUP and m.chat.id != LOG_GROUP: 283 | await m.reply_text(pl) 284 | if LOG_GROUP: 285 | await mp.send_playlist() 286 | else: 287 | await m.reply_text(pl) 288 | except (ValueError, TypeError): 289 | await m.reply_text(f"{emoji.NO_ENTRY} Invalid input", 290 | disable_web_page_preview=True) 291 | await m.delete() 292 | 293 | 294 | @Client.on_message(filters.command(["join", f"join@{U}"]) & filters.user(ADMINS)) 295 | async def join_group_call(client, m: Message): 296 | group_call = mp.group_call 297 | if group_call.is_connected: 298 | await m.reply_text(f"{emoji.ROBOT} Already joined VC") 299 | return 300 | await mp.start_call() 301 | chat = await client.get_chat(CHAT) 302 | await m.reply_text(f"Succesfully Joined Voice Chat in {chat.title}") 303 | await m.delete() 304 | 305 | 306 | @Client.on_message(filters.command("leave") & filters.user(ADMINS)) 307 | async def leave_voice_chat(_, m: Message): 308 | group_call = mp.group_call 309 | if not group_call.is_connected: 310 | await m.reply_text("Not joined any Alive VCs yet.") 311 | return 312 | playlist.clear() 313 | if 1 in RADIO: 314 | await mp.stop_radio() 315 | group_call.input_filename = '' 316 | await group_call.stop() 317 | await m.reply_text("Left the VoiceChat") 318 | await m.delete() 319 | 320 | 321 | @Client.on_message(filters.command(["mwk", f"mwk@{U}"]) & filters.user(ADMINS)) 322 | async def list_voice_chat(client, m: Message): 323 | group_call = mp.group_call 324 | if group_call.is_connected: 325 | chat_id = int("-100" + str(group_call.full_chat.id)) 326 | chat = await client.get_chat(chat_id) 327 | await m.reply_text( 328 | f"{emoji.MUSICAL_NOTES} **Currently in the voice chat**:\n" 329 | f"- **{chat.title}**" 330 | ) 331 | else: 332 | await m.reply_text(emoji.NO_ENTRY 333 | + "Didn't join any VCs yet") 334 | await m.delete() 335 | 336 | 337 | @Client.on_message(filters.command(["stop", f"stop@{U}"]) & filters.user(ADMINS)) 338 | async def stop_playing(_, m: Message): 339 | group_call = mp.group_call 340 | if not group_call.is_connected: 341 | await m.reply_text("Nothing playing to stop.") 342 | return 343 | if 1 in RADIO: 344 | await mp.stop_radio() 345 | group_call.stop_playout() 346 | await m.reply_text(f"{emoji.STOP_BUTTON} Stopped playing") 347 | playlist.clear() 348 | await m.delete() 349 | 350 | 351 | @Client.on_message(filters.command(["replay", f"replay@{U}"]) & filters.user(ADMINS)) 352 | async def restart_playing(_, m: Message): 353 | group_call = mp.group_call 354 | if not group_call.is_connected: 355 | await m.reply_text("Nothing playing to replay.") 356 | return 357 | if not playlist: 358 | await m.reply_text("Empty Que, Like your brain.") 359 | return 360 | group_call.restart_playout() 361 | await m.reply_text( 362 | f"{emoji.COUNTERCLOCKWISE_ARROWS_BUTTON} " 363 | "Playing from the beginning..." 364 | ) 365 | await m.delete() 366 | 367 | 368 | @Client.on_message(filters.command(["pause", f"pause@{U}"]) & filters.user(ADMINS)) 369 | async def pause_playing(_, m: Message): 370 | group_call = mp.group_call 371 | if not group_call.is_connected: 372 | await m.reply_text("Nothing playing to pause.") 373 | return 374 | mp.group_call.pause_playout() 375 | await m.reply_text(f"{emoji.PLAY_OR_PAUSE_BUTTON} Paused", 376 | quote=False) 377 | await m.delete() 378 | 379 | 380 | 381 | @Client.on_message(filters.command(["resume", f"resume@{U}"]) & filters.user(ADMINS)) 382 | async def resume_playing(_, m: Message): 383 | if not mp.group_call.is_connected: 384 | await m.reply_text("Nothing paused to resume.") 385 | return 386 | mp.group_call.resume_playout() 387 | await m.reply_text(f"{emoji.PLAY_OR_PAUSE_BUTTON} Resumed", 388 | quote=False) 389 | 390 | @Client.on_message(filters.command(["clear", f"clear@{U}"]) & filters.user(ADMINS)) 391 | async def clean_raw_pcm(client, m: Message): 392 | download_dir = os.path.join(client.workdir, DEFAULT_DOWNLOAD_DIR) 393 | all_fn: list[str] = os.listdir(download_dir) 394 | for track in playlist[:2]: 395 | track_fn = f"{track[1]}.raw" 396 | if track_fn in all_fn: 397 | all_fn.remove(track_fn) 398 | count = 0 399 | if all_fn: 400 | for fn in all_fn: 401 | if fn.endswith(".raw"): 402 | count += 1 403 | os.remove(os.path.join(download_dir, fn)) 404 | await m.reply_text(f"{emoji.WASTEBASKET} Cleaned {count} files") 405 | await m.delete() 406 | 407 | 408 | @Client.on_message(filters.command(["mute", f"mute@{U}"]) & filters.user(ADMINS)) 409 | async def mute(_, m: Message): 410 | group_call = mp.group_call 411 | if not group_call.is_connected: 412 | await m.reply_text("Nothing playing to mute.") 413 | return 414 | group_call.set_is_mute(True) 415 | await m.reply_text(f"🤐 Muted") 416 | await m.delete() 417 | 418 | @Client.on_message(filters.command(["unmute", f"unmute@{U}"]) & filters.user(ADMINS)) 419 | async def unmute(_, m: Message): 420 | group_call = mp.group_call 421 | if not group_call.is_connected: 422 | await m.reply_text("Nothing playing to mute.") 423 | return 424 | group_call.set_is_mute(False) 425 | await m.reply_text(f"{emoji.SPEAKER_MEDIUM_VOLUME} Unmuted") 426 | await m.delete() 427 | 428 | @Client.on_message(filters.command(["playlist", f"playlist@{U}"])) 429 | async def show_playlist(_, m: Message): 430 | group_call = mp.group_call 431 | if not group_call.is_connected: 432 | await m.reply_text("No active VC.") 433 | return 434 | if not playlist: 435 | pl = f"📜 Empty Playlist." 436 | else: 437 | pl = f"{emoji.PLAY_BUTTON} **Playlist**:\n" + "\n".join([ 438 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}" 439 | for i, x in enumerate(playlist) 440 | ]) 441 | await m.reply_text(pl) 442 | await m.delete() 443 | 444 | admincmds=["join", "unmute", "mute", "leave", "clear", "mwk", "pause", "resume", "skip", "radio", "stopradio", "replay", "update", f"join@{U}", f"unmute@{U}", f"mute@{U}", f"leave@{U}", f"clear@{U}", f"mwk@{U}", f"pause@{U}", f"resume@{U}", f"skip@{U}", f"radio@{U}", f"stopradio@{U}", f"replay@{U}", f"update@{U}"] 445 | 446 | @Client.on_message(filters.command(admincmds) & ~filters.user(ADMINS)) 447 | async def notforu(_, m: Message): 448 | await m.reply_sticker("CAACAgUAAxkBAAIJM2DTpi52NSM-O-KnYcC1IzbJos8HAAK6AQACsm0wVffnRbQlKgeTHwQ") 449 | -------------------------------------------------------------------------------- /shamil/radio.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrogram.types import Message 3 | from utils import mp, RADIO, USERNAME 4 | from config import Config 5 | from config import STREAM 6 | 7 | ADMINS=Config.ADMINS 8 | 9 | @Client.on_message(filters.command(["radio", f"radio@{USERNAME}"]) & filters.user(ADMINS)) 10 | async def radio(client, message: Message): 11 | if 1 in RADIO: 12 | await message.reply_text("Kindly stop existing Radio Stream /sr") 13 | return 14 | await mp.start_radio() 15 | await message.reply_text(f"Started Radio: {STREAM}") 16 | await message.delete() 17 | 18 | @Client.on_message(filters.command(['stopradio', f"stopradio@{USERNAME}"]) & filters.user(ADMINS)) 19 | async def stop(_, message: Message): 20 | if 0 in RADIO: 21 | await message.reply_text("Kindly start Radio First /r") 22 | return 23 | await mp.stop_radio() 24 | await message.reply_text("Radio stream ended.") 25 | await message.delete() 26 | -------------------------------------------------------------------------------- /shamil/song.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | 3 | import youtube_dl 4 | from youtube_search import YoutubeSearch 5 | import requests 6 | 7 | import os 8 | 9 | ## Extra Fns ------------------------------- 10 | 11 | # Convert hh:mm:ss to seconds 12 | def time_to_seconds(time): 13 | stringt = str(time) 14 | return sum(int(x) * 60 ** i for i, x in enumerate(reversed(stringt.split(':')))) 15 | 16 | 17 | ## Commands -------------------------------- 18 | 19 | @Client.on_message(filters.command(['song'])) 20 | def a(client, message): 21 | query = '' 22 | for i in message.command[1:]: 23 | query += ' ' + str(i) 24 | print(query) 25 | m = message.reply('`Searching... Please Wait...`') 26 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 27 | try: 28 | results = [] 29 | count = 0 30 | while len(results) == 0 and count < 6: 31 | if count>0: 32 | time.sleep(1) 33 | results = YoutubeSearch(query, max_results=1).to_dict() 34 | count += 1 35 | # results = YoutubeSearch(query, max_results=1).to_dict() 36 | try: 37 | link = f"https://youtube.com{results[0]['url_suffix']}" 38 | # print(results) 39 | title = results[0]["title"] 40 | thumbnail = results[0]["thumbnails"][0] 41 | duration = results[0]["duration"] 42 | views = results[0]["views"] 43 | 44 | ## UNCOMMENT THIS IF YOU WANT A LIMIT ON DURATION. CHANGE 1800 TO YOUR OWN PREFFERED DURATION AND EDIT THE MESSAGE (30 minutes cap) LIMIT IN SECONDS 45 | # if time_to_seconds(duration) >= 7000: # duration limit 46 | # m.edit("Exceeded 30mins cap") 47 | # return 48 | 49 | performer = f"[MwK-Musics]" 50 | thumb_name = f'thumb{message.message_id}.jpg' 51 | thumb = requests.get(thumbnail, allow_redirects=True) 52 | open(thumb_name, 'wb').write(thumb.content) 53 | 54 | except Exception as e: 55 | print(e) 56 | m.edit('**👎 Nᴏᴛʜɪɴɢ Tᴏ Bᴇ Fᴏᴜɴᴅ 🥺 Oʀ Cʜᴇᴄᴋ Sᴩᴇʟʟɪɴɢ 🤗!**') 57 | return 58 | except Exception as e: 59 | m.edit( 60 | "**Enter Song Name with /song Command!**" 61 | ) 62 | print(str(e)) 63 | return 64 | m.edit("`Bruh... Uploading... Please Wait...`") 65 | try: 66 | with youtube_dl.YoutubeDL(ydl_opts) as ydl: 67 | info_dict = ydl.extract_info(link, download=False) 68 | audio_file = ydl.prepare_filename(info_dict) 69 | ydl.process_info(info_dict) 70 | rep = f'🎶 Title: {title}\n⌚ Duration: {duration}\n📻 Uploaded By: [MwK] Musics' 71 | secmul, dur, dur_arr = 1, 0, duration.split(':') 72 | for i in range(len(dur_arr)-1, -1, -1): 73 | dur += (int(dur_arr[i]) * secmul) 74 | secmul *= 60 75 | message.reply_audio(audio_file, caption=rep, parse_mode='HTML',quote=False, title=title, duration=dur, performer=performer, thumb=thumb_name) 76 | m.delete() 77 | message.delete() 78 | except Exception as e: 79 | m.edit('**Sᴇᴇᴍꜱ Lɪᴋᴇ Aɴ Eʀʀᴏʀ Oᴄᴄᴜʀᴇᴅ 🥶 Report This @redbullfed!!**') 80 | print(e) 81 | try: 82 | os.remove(audio_file) 83 | os.remove(thumb_name) 84 | except Exception as e: 85 | print(e) 86 | 87 | -------------------------------------------------------------------------------- /shamil/stickers.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os import error 3 | import logging 4 | import pyrogram 5 | import random 6 | from decouple import config 7 | from pyrogram import Client, filters 8 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 9 | from pyrogram.types import User, Message, Sticker, Document 10 | 11 | 12 | @Client.on_message(filters.command(["stickerid"])) 13 | async def stickerid(bot, message): 14 | if message.reply_to_message.sticker: 15 | await message.reply(f"**Sticker ID is** \n `{message.reply_to_message.sticker.file_id}` \n \n ** Unique ID is ** \n\n`{message.reply_to_message.sticker.file_unique_id}`", quote=True) 16 | else: 17 | await message.reply("Oops !! Not a sticker file") 18 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # A Subinps Project 2 | # Pyrogram - Telegram MTProto API Client Library for Python 3 | # Copyright (C) 2017-2020 Dan 4 | # 5 | # This file is part of Pyrogram. 6 | # 7 | # Pyrogram is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Lesser General Public License as published 9 | # by the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Pyrogram is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Pyrogram. If not, see . 19 | 20 | 21 | 22 | import os 23 | from config import Config 24 | import ffmpeg 25 | from pyrogram import emoji 26 | from pyrogram.methods.messages.download_media import DEFAULT_DOWNLOAD_DIR 27 | from pytgcalls import GroupCall 28 | import signal 29 | from pyrogram import Client 30 | from youtube_dl import YoutubeDL 31 | from os import path 32 | bot = Client( 33 | "MwKVC", 34 | Config.API_ID, 35 | Config.API_HASH, 36 | bot_token=Config.BOT_TOKEN 37 | ) 38 | bot.start() 39 | e=bot.get_me() 40 | USERNAME=e.username 41 | 42 | from user import USER 43 | STREAM_URL=Config.STREAM_URL 44 | CHAT=Config.CHAT 45 | GROUP_CALLS = {} 46 | FFMPEG_PROCESSES = {} 47 | RADIO={6} 48 | LOG_GROUP=Config.LOG_GROUP 49 | DURATION_LIMIT=Config.DURATION_LIMIT 50 | playlist=Config.playlist 51 | msg=Config.msg 52 | 53 | 54 | ydl_opts = { 55 | "format": "bestaudio[ext=m4a]", 56 | "geo-bypass": True, 57 | "nocheckcertificate": True, 58 | "outtmpl": "downloads/%(id)s.%(ext)s", 59 | } 60 | ydl = YoutubeDL(ydl_opts) 61 | def youtube(url: str) -> str: 62 | info = ydl.extract_info(url, False) 63 | duration = round(info["duration"] / 60) 64 | try: 65 | ydl.download([url]) 66 | except Exception as e: 67 | print(e) 68 | pass 69 | return path.join("downloads", f"{info['id']}.{info['ext']}") 70 | 71 | class MusicPlayer(object): 72 | def __init__(self): 73 | self.group_call = GroupCall(USER, path_to_log_file='') 74 | self.chat_id = None 75 | 76 | 77 | async def send_playlist(self): 78 | if not playlist: 79 | pl = f"Playlist is Empty Like Your Brain" 80 | else: 81 | pl = f"🎧 **Playlist**:\n" + "\n".join([ 82 | f"**{i}**. **📻{x[1]}**\n 👤**Requested by:** {x[4]}\n" 83 | for i, x in enumerate(playlist) 84 | ]) 85 | if msg.get('playlist') is not None: 86 | await msg['playlist'].delete() 87 | msg['playlist'] = await self.send_text(pl) 88 | 89 | async def skip_current_playing(self): 90 | group_call = self.group_call 91 | if not playlist: 92 | return 93 | if len(playlist) == 1: 94 | await mp.start_radio() 95 | return 96 | client = group_call.client 97 | download_dir = os.path.join(client.workdir, DEFAULT_DOWNLOAD_DIR) 98 | group_call.input_filename = os.path.join( 99 | download_dir, 100 | f"{playlist[1][1]}.raw" 101 | ) 102 | # remove old track from playlist 103 | old_track = playlist.pop(0) 104 | print(f"- START PLAYING: {playlist[0][1]}") 105 | if LOG_GROUP: 106 | await self.send_playlist() 107 | os.remove(os.path.join( 108 | download_dir, 109 | f"{old_track[1]}.raw") 110 | ) 111 | if len(playlist) == 1: 112 | return 113 | await self.download_audio(playlist[1]) 114 | 115 | async def send_text(self, text): 116 | group_call = self.group_call 117 | client = group_call.client 118 | chat_id = LOG_GROUP 119 | message = await bot.send_message( 120 | chat_id, 121 | text, 122 | disable_web_page_preview=True, 123 | disable_notification=True 124 | ) 125 | return message 126 | 127 | async def download_audio(self, song): 128 | group_call = self.group_call 129 | client = group_call.client 130 | raw_file = os.path.join(client.workdir, DEFAULT_DOWNLOAD_DIR, 131 | f"{song[1]}.raw") 132 | #if os.path.exists(raw_file): 133 | #os.remove(raw_file) 134 | if not os.path.isfile(raw_file): 135 | # credits: SpechiDe 136 | #os.mkfifo(raw_file) 137 | if song[3] == "telegram": 138 | original_file = await bot.download_media(f"{song[2]}") 139 | elif song[3] == "youtube": 140 | original_file = youtube(song[2]) 141 | else: 142 | original_file=wget.download(song[2]) 143 | ffmpeg.input(original_file).output( 144 | raw_file, 145 | format='s16le', 146 | acodec='pcm_s16le', 147 | ac=2, 148 | ar='48k', 149 | loglevel='error' 150 | ).overwrite_output().run() 151 | os.remove(original_file) 152 | 153 | 154 | async def start_radio(self): 155 | group_call = mp.group_call 156 | if group_call.is_connected: 157 | playlist.clear() 158 | group_call.input_filename = '' 159 | await group_call.stop() 160 | process = FFMPEG_PROCESSES.get(CHAT) 161 | if process: 162 | process.send_signal(signal.SIGTERM) 163 | station_stream_url = STREAM_URL 164 | group_call.input_filename = f'radio-{CHAT}.raw' 165 | try: 166 | RADIO.remove(0) 167 | except: 168 | pass 169 | try: 170 | RADIO.add(1) 171 | except: 172 | pass 173 | if os.path.exists(group_call.input_filename): 174 | os.remove(group_call.input_filename) 175 | # credits: https://t.me/c/1480232458/6825 176 | #os.mkfifo(group_call.input_filename) 177 | process = ffmpeg.input(station_stream_url).output( 178 | group_call.input_filename, 179 | format='s16le', 180 | acodec='pcm_s16le', 181 | ac=2, 182 | ar='48k' 183 | ).overwrite_output().run_async() 184 | FFMPEG_PROCESSES[CHAT] = process 185 | while True: 186 | if os.path.isfile(group_call.input_filename): 187 | await group_call.start(CHAT) 188 | break 189 | else: 190 | continue 191 | 192 | async def stop_radio(self): 193 | group_call = mp.group_call 194 | if group_call: 195 | playlist.clear() 196 | group_call.input_filename = '' 197 | try: 198 | RADIO.remove(1) 199 | except: 200 | pass 201 | try: 202 | RADIO.add(0) 203 | except: 204 | pass 205 | process = FFMPEG_PROCESSES.get(CHAT) 206 | if process: 207 | process.send_signal(signal.SIGTERM) 208 | 209 | async def start_call(self): 210 | group_call = mp.group_call 211 | await group_call.start(CHAT) 212 | 213 | 214 | 215 | 216 | mp = MusicPlayer() 217 | 218 | 219 | # pytgcalls handlers 220 | 221 | @mp.group_call.on_network_status_changed 222 | async def network_status_changed_handler(gc: GroupCall, is_connected: bool): 223 | if is_connected: 224 | mp.chat_id = int("-100" + str(gc.full_chat.id)) 225 | else: 226 | mp.chat_id = None 227 | 228 | 229 | @mp.group_call.on_playout_ended 230 | async def playout_ended_handler(_, __): 231 | if not playlist: 232 | await mp.start_radio() 233 | else: 234 | await mp.skip_current_playing() 235 | --------------------------------------------------------------------------------