├── start.sh ├── heroku.yml ├── repo-mediaarea_1.0-20_all.deb ├── TokenGeneration ├── requirements.txt ├── forPyrogram.py ├── readme.txt └── TokenGen.py ├── Dockerfile ├── services ├── humanFunctions.py ├── ddl.py ├── sox.py ├── gdtot.py ├── appleMusic.py ├── appDrive.py ├── tgFile.py ├── gDrive.py └── mega.py ├── README.md ├── app.json ├── .gitignore └── bot.py /start.sh: -------------------------------------------------------------------------------- 1 | python3 bot.py -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | run: 5 | worker: ./start.sh -------------------------------------------------------------------------------- /repo-mediaarea_1.0-20_all.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bunnykek/MediaInfoBot/HEAD/repo-mediaarea_1.0-20_all.deb -------------------------------------------------------------------------------- /TokenGeneration/requirements.txt: -------------------------------------------------------------------------------- 1 | google-api-python-client 2 | google-auth-httplib2 3 | google-auth-oauthlib 4 | tgcrypto 5 | pyrogram -------------------------------------------------------------------------------- /TokenGeneration/forPyrogram.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pyrogram import Client 3 | 4 | 5 | #get your api_id and api_hash from https://my.telegram.org/apps 6 | api_id = 12345 # your api_id 7 | api_hash = "0123456789abcdef0123456789abcdef" # your api_hash 8 | 9 | 10 | async def main(): 11 | async with Client("my_account", api_id, api_hash) as app: 12 | await app.send_message("me", "Greetings from **Pyrogram**!") 13 | 14 | 15 | asyncio.run(main()) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | COPY . . 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update -y 5 | RUN apt-get upgrade -y 6 | RUN apt-get -y install wget ffmpeg 7 | RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-20_all.deb 8 | RUN dpkg -i repo-mediaarea_1.0-20_all.deb 9 | RUN apt-get update -y 10 | RUN apt-get -y install mediainfo megatools python3-pip sox 11 | RUN pip install --upgrade bs4 lxml google-api-python-client google-auth-httplib2 google-auth-oauthlib pycryptodomex pillow pyrogram tgcrypto pycryptodomex python-dotenv m3u8 12 | RUN chmod +x start.sh -------------------------------------------------------------------------------- /TokenGeneration/readme.txt: -------------------------------------------------------------------------------- 1 | step 1. Put the credentials.json file in this folder. 2 | step 2. pip install -r requirements.txt (run in cmd/terminal) 3 | step 3. python TokenGen.py (run in cmd/terminal follow the prompted procedure) 4 | step 4. You will see that a "token.json" file has been created. We need its contents. 5 | step 5. open forPyrogram.py file using notepad/IDE and replace the api_id and api_hash with yours and save it. (get it from https://my.telegram.org/apps) 6 | step 6. python forPyrogram.py (run in cmd/terminal) 7 | step 7. Follow the procedure shown by pyrogram, thats it, No need of it further. 8 | 9 | use 'pip3' if 'pip' doesnt work for you 10 | use 'py' or 'python3' if "python" doesn't work for you. 11 | -------------------------------------------------------------------------------- /TokenGeneration/TokenGen.py: -------------------------------------------------------------------------------- 1 | #pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib 2 | 3 | import os.path 4 | from google_auth_oauthlib.flow import InstalledAppFlow 5 | 6 | # If modifying these scopes, delete the file token.json. 7 | SCOPES = ['https://www.googleapis.com/auth/drive'] 8 | 9 | 10 | def main(): 11 | creds = None 12 | if not os.path.exists('token.json'): 13 | flow = InstalledAppFlow.from_client_secrets_file( 14 | 'credentials.json', SCOPES) 15 | creds = flow.run_local_server(port=0) 16 | # Save the credentials for the next run 17 | with open('token.json', 'w') as token: 18 | token.write(creds.to_json()) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /services/humanFunctions.py: -------------------------------------------------------------------------------- 1 | def humanBitrate(bitrate_kbps): 2 | if bitrate_kbps > 10000: 3 | bitrate = str(round(bitrate_kbps/1000, 2)) + ' ' + 'Mb/s' 4 | else: 5 | bitrate = str(round(bitrate_kbps, 2)) + ' ' + 'kb/s' 6 | return bitrate 7 | 8 | 9 | def humanSize(num): 10 | for x in ['bytes', 'KB', 'MB', 'GB', 'TB']: 11 | if num < 1024.0: 12 | return "%3.1f %s" % (num, x) 13 | num /= 1024.0 14 | return "%3.1f %s" % (num, 'TB') 15 | 16 | def remove_N(seq): 17 | i = 1 18 | while i < len(seq): 19 | if seq[i] == seq[i-1]: 20 | del seq[i] 21 | i -= 1 22 | else: 23 | i += 1 24 | 25 | 26 | print("Human functions loaded", flush=True) 27 | -------------------------------------------------------------------------------- /services/ddl.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import subprocess 4 | 5 | from pyrogram.types import Message 6 | 7 | 8 | URLRx = re.compile(r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])") 9 | nameRx = re.compile(r".+/(.+)") 10 | def ddlinfo(msg: Message): 11 | print("Got the DDL request!", flush=True) 12 | try: 13 | ddl = URLRx.search(msg.text).group(0) 14 | name = nameRx.search(ddl).group(1) 15 | gen_ddl_mediainfo(msg, ddl, name) 16 | 17 | except: 18 | print("Enter a valid ddl", flush=True) 19 | raise Exception("`Something went wrong.\nPlease make sure you used a valid URL.`") 20 | 21 | 22 | 23 | def gen_ddl_mediainfo(msg: Message, ddl:str, name:str): 24 | mediainfo = subprocess.check_output(['mediainfo', ddl]).decode("utf-8") 25 | lines = mediainfo.splitlines() 26 | for i in range(len(lines)): 27 | if 'Complete name' in lines[i]: 28 | lines[i] = re.sub(r": .+", ': '+name, lines[i]) 29 | break 30 | with open(f'{name}.txt', 'w') as f: 31 | f.write('\n'.join(lines)) 32 | 33 | #send the file 34 | msg.reply_document(f"{name}.txt") 35 | print("DDL file Mediainfo sent", flush=True) 36 | os.remove(f"{name}.txt") 37 | 38 | print("ddl.py loaded", flush=True) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MediaInfoBot 2 | Multi-Utility bot for telegram! 3 | 4 | ## Features 5 | - Generates MediaInfo of video/audio files from the following services: 6 | [G-Drive, Mega.nz, AppDrive, GDTOT, DDL, Telegram files] 7 | - Can generate audio spectrogram of a telegram audio file. 8 | - Can show all the available codecs of an AppleMusic album. 9 | 10 | #### Setup procedure: 11 | 12 | - Follow [this guide](https://www.iperiusbackup.net/en/how-to-enable-google-drive-api-and-get-client-credentials/), but at the last step instead of selecting application as `web application` use `desktop app` 13 | 14 | 15 | 16 | - Download the json and save it as `credentials.json`. 17 | - `git clone https://github.com/bunnykek/MediaInfoBot` 18 | - Navigate into the `TokenGeneration` directory and follow the [readme.txt](https://github.com/bunnykek/MediaInfoBot/blob/main/TokenGeneration/readme.txt) for further procedure. 19 | - Deploy to heroku 20 | #### Heroku Deploy 21 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/bunnykek/MediaInfoBot) 22 | #### Bot commands: 23 | - `/help` - Helps 24 | - `/info` - For generating MediaInfo or AppleMusic album codecs info. 25 | - `/spek` - Generates audio spectrogram. 26 | 27 | ##### Make sure to star/fork my projects if you enjoy using it. Thanks 28 | 29 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MediaInfoBot", 3 | "description": "Multiutility telegram bot by bunny", 4 | "repository": "https://github.com/bunnykek/MediaInfoBot", 5 | "logo": "https://imgur.com/UsUog2Z.png", 6 | "keywords": [ 7 | "telegram", 8 | "telegram-bot", 9 | "mediainfo", 10 | "spectrogram-bot" 11 | ], 12 | "env": { 13 | "api_id": { 14 | "description": "Get your api_id from https://my.telegram.org/apps", 15 | "value": "12345" 16 | }, 17 | "api_hash": { 18 | "description": "Get your api_hash from https://my.telegram.org/apps", 19 | "value": "0123456789abcdef0123456789abcdef" 20 | }, 21 | "bot_token": { 22 | "description": "Get your bot's token from https://telegram.me/BotFather ", 23 | "value": "XXXXXXXXXX:YYYYYYYYYYYYYYYYYYYYYYYYY" 24 | }, 25 | "gdtot_php": { 26 | "description": "Get it from GDTOT cookies", 27 | "value": "" 28 | }, 29 | "gdtot_crypt": { 30 | "description": "Get it from GDTOT cookies", 31 | "value": "" 32 | }, 33 | "appdrive_php": { 34 | "description": "Get it from AppDrive cookies", 35 | "value": "" 36 | }, 37 | "appdrive_md": { 38 | "description": "Get it from AppDrive cookies", 39 | "value": "" 40 | }, 41 | "token_json": { 42 | "description": "token.json contents", 43 | "value": "{...}" 44 | }, 45 | "owner": { 46 | "description": "Bot owner telegram ID", 47 | "value": "" 48 | } 49 | }, 50 | "stack": "container" 51 | } -------------------------------------------------------------------------------- /services/sox.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | from pyrogram.types import Message 5 | 6 | 7 | def generateSpek(msg: Message): 8 | print("Got the audio spek request.") 9 | attatchment = msg.reply_to_message 10 | mediaType = attatchment.media.value 11 | print("mediatype:", mediaType) 12 | 13 | if 'audio' in mediaType: 14 | media = attatchment.audio 15 | elif 'document' in mediaType and 'audio' in attatchment.document.mime_type: 16 | media = attatchment.document 17 | else: 18 | print("Non audio file") 19 | raise Exception("Non-audio file!") 20 | 21 | fileName = media.file_name 22 | mime = media.mime_type 23 | 24 | attatchment.download(os.path.join(os.getcwd(), fileName)) 25 | 26 | if 'm4a' in mime.lower() or 'audio/mp4' in mime.lower(): #for apple ALAC and AAC in m4a container 27 | subprocess.Popen(["ffmpeg", "-i", fileName, "-f", 28 | "flac", fileName+'.flac']).wait() 29 | subprocess.Popen(["sox", fileName+'.flac', "-n", "remix", "1", "spectrogram", "-x", "1000", 30 | "-y", "513", "-z", "120", "-w", "Kaiser", "-o", f"{fileName}.png"]).wait() 31 | os.remove(fileName+'.flac') 32 | else: 33 | subprocess.Popen(["sox", fileName, "-n", "remix", "1", "spectrogram", "-x", "1000", 34 | "-y", "513", "-z", "120", "-w", "Kaiser", "-o", f"{fileName}.png"]).wait() 35 | os.remove(fileName) 36 | if not os.path.exists(f"{fileName}.png"): 37 | raise Exception(f"[{mime}] Spek cannot be generated for this file!") 38 | msg.reply_document(f"{fileName}.png", caption=f"`{fileName}`") 39 | os.remove(f"{fileName}.png") 40 | -------------------------------------------------------------------------------- /services/gdtot.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import os 3 | import re 4 | 5 | import requests 6 | from dotenv import load_dotenv 7 | from pyrogram import Client 8 | from pyrogram.types import Message 9 | 10 | from services.ddl import URLRx 11 | from services.gDrive import downloadandsendGdrivefile 12 | 13 | load_dotenv() 14 | 15 | gtidRx = re.compile(r'file/(\d+)?') 16 | gdRx = re.compile(r'200&gd=(.+)&') 17 | gdriveRx = re.compile(r'https://drive\.google\.com/open\?id=(.+?)\"') 18 | gdtotTitleRx = re.compile(r'GDToT \| (.+)') 19 | 20 | Cookies = { 21 | 'PHPSESSID': "", 22 | 'crypt': f"{os.getenv('gdtot_crypt')}" 23 | } 24 | 25 | 26 | def updateCookie(): 27 | response = requests.get('https://new.gdtot.xyz/', cookies=Cookies) 28 | Cookies['PHPSESSID'] = response.cookies.get_dict()['PHPSESSID'] 29 | 30 | 31 | def getGd(gtid): 32 | 33 | params = { 34 | 'id': f'{gtid}' 35 | } 36 | 37 | for _ in range(3): 38 | response = requests.get( 39 | 'https://new.gdtot.xyz/dld', params=params, cookies=Cookies) 40 | if 'you must login' in response.text: 41 | print("Cookie error, retrying...", flush=True) 42 | updateCookie() 43 | continue 44 | else: 45 | break 46 | 47 | print(response.text, flush=True) 48 | gd = re.search(gdRx, response.text).group(1) 49 | return gd 50 | 51 | 52 | def gdtotInfo(msg: Message, client: Client): 53 | print("Got the gdtot request.", flush=True) 54 | url = URLRx.search(msg.text).group(0) 55 | print(url, flush=True) 56 | title = getTitle(url) 57 | print(title, flush=True) 58 | 59 | gtid = re.search(gtidRx, msg.text).group(1) 60 | driveid = base64.b64decode(getGd(gtid)).decode("utf-8") 61 | downloadandsendGdrivefile(msg, driveid, client) 62 | 63 | 64 | def getTitle(url): 65 | response = requests.get(url) 66 | title = re.search(gdtotTitleRx, response.text).group(1) 67 | return title 68 | 69 | 70 | print("gdtot.py loaded", flush=True) 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /services/appleMusic.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import m3u8 4 | import requests 5 | from pyrogram.types import Message 6 | 7 | apple_rx = re.compile(r"apple\.com\/(\w\w)\/album\/.+\/(\d+|pl\..+)") 8 | 9 | 10 | headers = { 11 | 'origin': 'https://music.apple.com', 12 | 'Authorization': 'Bearer pppppppppppppppppp', 13 | } 14 | 15 | params = { 16 | 'extend': 'extendedAssetUrls', 17 | } 18 | 19 | def updateToken(): 20 | response = requests.get("https://music.apple.com/us/album/positions-deluxe-edition/1553944254") 21 | headers['Authorization'] = f'Bearer {re.search(r"(eyJhbGc.+?)%22%7D", response.text).group(1)}' 22 | 23 | 24 | def amInfo(message: Message): 25 | # result = apple_rx.search(message) 26 | result = apple_rx.search(message.text) 27 | if not result: 28 | message.reply("`Improper Apple Music album URL!`") 29 | return 30 | region, id_ = result.groups() 31 | response = requests.get(f'https://amp-api.music.apple.com/v1/catalog/{region}/albums/{id_}/', params=params, headers=headers) 32 | if response.status_code == 401: 33 | print("Updating token!") 34 | updateToken() 35 | response = requests.get(f'https://amp-api.music.apple.com/v1/catalog/{region}/albums/{id_}/', params=params, headers=headers) 36 | info = response.json()['data'][0] 37 | release_date = info['attributes']['releaseDate'] 38 | adm = 'True' if info['attributes']['isMasteredForItunes'] else 'False' 39 | url = info['attributes']['url'] 40 | name = info['attributes']['name'] 41 | print(name) 42 | artist = info['attributes']['artistName'] 43 | traits = info['attributes']['audioTraits'] 44 | artwork = info['attributes']['artwork']['url'] 45 | w = str(info['attributes']['artwork']['width']) 46 | h = str(info['attributes']['artwork']['height']) 47 | 48 | print(traits) 49 | hls = info['relationships']['tracks']['data'][0]['attributes']['extendedAssetUrls']['enhancedHls'] 50 | #print(hls) 51 | playlist = m3u8.parse(m3u8.load(hls).dumps()) 52 | alacs = [] 53 | for stream in playlist['playlists']: 54 | if stream['stream_info']['codecs'] == 'alac': 55 | temp = stream['stream_info']['audio'].split('-') 56 | sr = int(temp[-2])/1000 57 | depth = int(temp[-1]) 58 | alacs.append((sr, depth)) 59 | alacs.sort() 60 | #print(alacs) 61 | codecs = ["Lossy AAC"] 62 | if 'atmos' in traits: 63 | codecs.append("Dolby Atmos") 64 | if 'lossless' in traits: 65 | for i,j in alacs: 66 | codecs.append(f"ALAC {i}-{j}") 67 | 68 | text = f"""Album : **[{name}]({url}) | [{w}x{h}]({artwork.format(w=w,h=h)})** 69 | Artist : **{artist}** 70 | Date : **{release_date}** 71 | Codecs: **{' | '.join(codecs)}** 72 | Apple Digital Masters: **{adm}**""" 73 | message.reply_photo(photo=artwork.format(w=w,h=h), caption=text) 74 | print("AppleMusic album info done.") 75 | 76 | print("appleMusic loaded", flush=True) -------------------------------------------------------------------------------- /services/appDrive.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | import requests 5 | from dotenv import load_dotenv 6 | from pyrogram import Client 7 | from pyrogram.types import Message 8 | 9 | from services.ddl import URLRx 10 | from services.gDrive import downloadandsendGdrivefile, gdriveRe, get_gdrive_metadata 11 | 12 | load_dotenv() 13 | 14 | keyRx = re.compile(r'formData\.append\( \"key\", \"(.+)\" \)') 15 | titleRx = re.compile(r"(.+)") 16 | cookies = { 17 | 'PHPSESSID': os.getenv('appdrive_php'), 18 | 'MD': os.getenv('appdrive_md') 19 | } 20 | 21 | text = """AppDrive: 22 | Filename: {} 23 | Gdrive FileID: {}""" 24 | 25 | 26 | def getKeyTitle(url): 27 | response = requests.get(url, cookies=cookies) 28 | key = re.search(keyRx, response.text).group(1) 29 | title = re.search(titleRx, response.text).group(1) 30 | print(key, flush=True) 31 | return (key, title) 32 | 33 | 34 | def getDriveUrl(url, key): 35 | headers = { 36 | 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryExPHAhKfkkEG7paq', 37 | } 38 | 39 | data = f'------WebKitFormBoundaryExPHAhKfkkEG7paq\r\nContent-Disposition: form-data; name="action"\r\n\r\noriginal\r\n------WebKitFormBoundaryExPHAhKfkkEG7paq\r\nContent-Disposition: form-data; name="type"\r\n\r\n1\r\n------WebKitFormBoundaryExPHAhKfkkEG7paq\r\nContent-Disposition: form-data; name="key"\r\n\r\n{key}\r\n------WebKitFormBoundaryExPHAhKfkkEG7paq--\r\n' 40 | 41 | response_json = {'error': True} 42 | appdrive_file_id = url.split('/')[-1] 43 | for i in range(5): 44 | print(i, "time", flush=True) 45 | if response_json['error']: 46 | response = requests.post( 47 | f'https://appdrive.info/file/{appdrive_file_id}', cookies=cookies, headers=headers, data=data) 48 | #print(response.text, flush=True) 49 | try: 50 | response_json = response.json() 51 | if not response_json['error']: 52 | break 53 | except: 54 | continue 55 | try: 56 | print(response.json()['url'], flush=True) 57 | return response.json()['url'] 58 | except: 59 | return None 60 | 61 | 62 | def appdriveInfo(msg: Message, client: Client): 63 | print("Got the appdrive request!", flush=True) 64 | url = URLRx.search(msg.text).group(0) 65 | key, title = getKeyTitle(url) 66 | 67 | driveurl = getDriveUrl(url, key) 68 | if driveurl == None: 69 | raise Exception("Something went wrong with appdrive.") 70 | 71 | fileid = gdriveRe.search(driveurl).group(1) 72 | print(text.format(title, fileid), flush=True) 73 | print("Fetching metadata using Gdrive API.", flush=True) 74 | metadata = get_gdrive_metadata(fileid) 75 | print(metadata, flush=True) 76 | print("Forwarding further processing of ID to Gdrive module") 77 | downloadandsendGdrivefile(msg, fileid, client) 78 | print("Appdrive MediaInfo done.", flush=True) 79 | 80 | print("AppDrive loaded", flush=True) 81 | -------------------------------------------------------------------------------- /services/tgFile.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | import subprocess 5 | 6 | from pyrogram import Client 7 | from pyrogram.types import Message 8 | 9 | from services.humanFunctions import humanBitrate, humanSize, remove_N 10 | 11 | def tgInfo(client: Client, msg: Message): 12 | print("processing TG", flush=True) 13 | message = msg.reply_to_message 14 | # print(message) 15 | mediaType = message.media.value 16 | if mediaType == 'video': 17 | media = message.video 18 | elif mediaType == 'audio': 19 | media = message.audio 20 | elif mediaType == 'document': 21 | media = message.document 22 | else: 23 | print("This media type is not supported", flush=True) 24 | raise Exception("`This media type is not supported`") 25 | 26 | mime = media.mime_type 27 | fileName = media.file_name 28 | size = media.file_size 29 | 30 | print(fileName, size, flush=True) 31 | 32 | if mediaType == 'document': 33 | if 'video' not in mime and 'audio' not in mime and 'image' not in mime: 34 | print("Makes no sense", flush=True) 35 | raise Exception("`This file makes no sense to me.`") 36 | 37 | if int(size) <= 50000000: 38 | message.download(os.path.join(os.getcwd(), fileName)) 39 | 40 | else: 41 | for chunk in client.stream_media(message, limit=5): 42 | # save these chunks to a file 43 | with open(fileName, 'ab') as f: 44 | f.write(chunk) 45 | 46 | mediainfo = subprocess.check_output( 47 | ['mediainfo', fileName]).decode("utf-8") 48 | 49 | mediainfo_json = json.loads(subprocess.check_output( 50 | ['mediainfo', fileName, '--Output=JSON']).decode("utf-8")) 51 | 52 | # write a function to convert bytes to readable format 53 | readable_size = humanSize(size) 54 | 55 | try: 56 | lines = mediainfo.splitlines() 57 | if 'image' not in mime: 58 | duration = float(mediainfo_json['media']['track'][0]['Duration']) 59 | bitrate_kbps = (size*8)/(duration*1000) 60 | bitrate = humanBitrate(bitrate_kbps) 61 | for i in range(len(lines)): 62 | if 'File size' in lines[i]: 63 | lines[i] = re.sub(r": .+", ': '+readable_size, lines[i]) 64 | elif 'Overall bit rate' in lines[i] and 'Overall bit rate mode' not in lines[i]: 65 | lines[i] = re.sub(r": .+", ': '+bitrate, lines[i]) 66 | elif 'IsTruncated' in lines[i] or 'FileExtension_Invalid' in lines[i]: 67 | lines[i] = '' 68 | 69 | remove_N(lines) 70 | 71 | with open(f'{fileName}.txt', 'w') as f: 72 | f.write('\n'.join(lines)) 73 | 74 | msg.reply_document(document=f'{fileName}.txt', caption=f'`{fileName}`') 75 | 76 | print("TG file Mediainfo sent", flush=True) 77 | os.remove(f'{fileName}.txt') 78 | except: 79 | message.reply_text( 80 | "Something bad occurred particularly with this file.") 81 | print("Something bad occurred for tg file", flush=True) 82 | finally: 83 | os.remove(fileName) 84 | 85 | 86 | print("TG file module loaded", flush=True) 87 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from dotenv import load_dotenv 5 | from pyrogram import Client, filters 6 | from pyrogram.types import Message 7 | 8 | from services.appDrive import appdriveInfo 9 | from services.appleMusic import amInfo 10 | from services.ddl import ddlinfo 11 | from services.gDrive import gdriveInfo 12 | from services.gdtot import gdtotInfo 13 | from services.mega import megaInfo 14 | from services.sox import generateSpek 15 | from services.tgFile import tgInfo 16 | 17 | load_dotenv() 18 | 19 | #sys.path.append(os.path.join(os.getcwd(), 'services')) 20 | 21 | 22 | helpMessage = """MediaInfo support the following services: 23 | `• G-DRIVE 24 | • MEGA.nz 25 | • AppDrive 26 | • GDTOT 27 | • Direct download links 28 | • Telegram files` 29 | 30 | **Example:** 31 | For MediaInfo: 32 | `/info url` 33 | `reply /info to file` 34 | 35 | For audio Spek: 36 | `reply /spek to audio` 37 | 38 | For AppleMusic album info: 39 | `/info album_url` 40 | 41 | Made by @pseudoboi🧪""" 42 | 43 | 44 | owner = str(os.getenv('owner')) 45 | 46 | app = Client('botsession', api_id=os.getenv('api_id'), 47 | api_hash=os.getenv('api_hash'), 48 | bot_token=os.getenv('bot_token')) 49 | 50 | print("MediaInfo bot started!", flush=True) 51 | 52 | 53 | @app.on_message(filters.text & filters.private) 54 | def hello(client: Client, message: Message): 55 | if message.from_user.id == owner: 56 | message.reply("Unauthorized!!!") 57 | return 58 | 59 | if '/start' in message.text: 60 | message.reply("Send /help for more info.") 61 | return 62 | 63 | if '/help' in message.text: 64 | message.reply(helpMessage) 65 | return 66 | 67 | try: 68 | if "/spek" in message.text: 69 | message.reply("Processing your spectrogram request...") 70 | generateSpek(message) 71 | return 72 | 73 | elif "/info" in message.text: 74 | if 'mega.nz' in message.text.lower(): 75 | message.reply("Processing your MEGA.nz request...") 76 | megaInfo(message, app) 77 | elif 'drive.google' in message.text.lower(): 78 | message.reply("Processing your G-DRIVE request...") 79 | gdriveInfo(message, app) 80 | elif 'appdrive' in message.text.lower(): 81 | message.reply("Processing your AppDrive request...") 82 | appdriveInfo(message, app) 83 | elif 'gdtot' in message.text.lower(): 84 | message.reply("Processing your GDTOT request...") 85 | gdtotInfo(message, app) 86 | elif 'music.apple' in message.text.lower(): 87 | amInfo(message) 88 | elif message.reply_to_message: 89 | message.reply("Processing your Telegram file request...") 90 | tgInfo(client, message) 91 | elif len(message.text) > 10: 92 | message.reply("Processing your DDL request...") 93 | ddlinfo(message) 94 | except Exception as e: 95 | message.reply(f"`An error occured!`\n{e}") 96 | print(e, flush=True) 97 | return 98 | 99 | 100 | app.run() 101 | -------------------------------------------------------------------------------- /services/gDrive.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | import subprocess 5 | 6 | from google.oauth2.credentials import Credentials 7 | from googleapiclient.discovery import build 8 | from googleapiclient.http import MediaIoBaseDownload 9 | from pyrogram import Client 10 | from pyrogram.types import Message 11 | 12 | from services.humanFunctions import humanBitrate, humanSize, remove_N 13 | 14 | tokenJson = json.loads(os.getenv('token_json')) 15 | 16 | gdriveRe = re.compile('([-\w]{25,})') 17 | 18 | SCOPES = ['https://www.googleapis.com/auth/drive'] 19 | 20 | creds = Credentials.from_authorized_user_info(tokenJson, SCOPES) 21 | service = build('drive', 'v3', credentials=creds) 22 | 23 | 24 | def gdriveInfo(msg: Message, client: Client): 25 | print("Got the Gdrive request.", flush=True) 26 | match = re.search(gdriveRe, msg.text) 27 | if not match: 28 | print("Send a proper GDRIVE public file link!", flush=True) 29 | raise Exception("Send a proper/accessible GDRIVE file URL.") 30 | fileid = match.group(1) 31 | downloadandsendGdrivefile(msg, fileid, client) 32 | 33 | 34 | def get_gdrive_metadata(fileid): 35 | metadata = service.files().get(fileId=fileid, fields='name, size, mimeType', 36 | supportsAllDrives=True).execute() 37 | return metadata 38 | 39 | 40 | def downloadandsendGdrivefile(msg: Message, fileid, client: Client): 41 | request = service.files().get_media(fileId=fileid) 42 | metadata = get_gdrive_metadata(fileid) 43 | print(metadata, flush=True) 44 | 45 | fname = metadata['name'] 46 | 47 | if not 'video' in metadata['mimeType'] and not 'audio' in metadata['mimeType']: 48 | print("File made no sense", flush=True) 49 | raise Exception("`This file makes no sense to me.`") 50 | 51 | f = open(f"{fileid}", "wb") 52 | downloader = MediaIoBaseDownload(f, request) 53 | print("way to loop", flush=True) 54 | try: 55 | for i in range(2): 56 | downloader.next_chunk() 57 | f.close() 58 | except ValueError as v: 59 | print(v, flush=True) 60 | print("Downloaded the first 2 chunk.", flush=True) 61 | 62 | mediainfo = subprocess.check_output( 63 | ['mediainfo', fileid]).decode("utf-8") 64 | mediainfo_json = json.loads(subprocess.check_output( 65 | ['mediainfo', fileid, '--Output=JSON']).decode("utf-8")) 66 | duration = float(mediainfo_json['media']['track'][0]['Duration']) 67 | bitrate_kbps = float(metadata['size'])*8/(duration * 1000) 68 | bitrate = humanBitrate(bitrate_kbps) 69 | 70 | size_readable = humanSize(float(metadata['size'])) 71 | print("Got the file size", flush=True) 72 | 73 | # print(bitrate) 74 | lines = mediainfo.splitlines() 75 | for i in range(len(lines)): 76 | if 'Complete name' in lines[i]: 77 | lines[i] = re.sub(r": .+", ': '+metadata['name'], lines[i]) 78 | elif 'File size' in lines[i]: 79 | lines[i] = re.sub(r": .+", ': '+size_readable, lines[i]) 80 | elif 'Overall bit rate' in lines[i] and 'Overall bit rate mode' not in lines[i]: 81 | lines[i] = re.sub(r": .+", ': '+bitrate, lines[i]) 82 | elif 'IsTruncated' in lines[i] or 'FileExtension_Invalid' in lines[i]: 83 | lines[i] = '' 84 | remove_N(lines) 85 | 86 | # save the list to a file 87 | with open(f"{fname}.txt", 'w') as f: 88 | f.write('\n'.join(lines)) 89 | 90 | msg.reply_document(document=f'{fname}.txt', caption=f'`{fname}`') 91 | 92 | os.remove(f"{metadata['name']}.txt") 93 | os.remove(fileid) 94 | print("G-Drive file mediainfo sent", flush=True) 95 | 96 | 97 | print("Gdrive loaded", flush=True) 98 | -------------------------------------------------------------------------------- /services/mega.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | import subprocess 5 | import time 6 | 7 | import requests 8 | from bs4 import BeautifulSoup 9 | from dotenv import load_dotenv 10 | from pyrogram import Client 11 | from pyrogram.types import Message 12 | 13 | from services.humanFunctions import humanBitrate, remove_N 14 | 15 | load_dotenv() 16 | 17 | 18 | megaRx = re.compile(r'mega.nz/file/(.+?)#(.+?)$') 19 | fileNameRx = re.compile(r"(.+):") 20 | sizeRx = re.compile(r"(.+) (GB|MB)") 21 | 22 | 23 | def megaInfo(msg: Message, client: Client): 24 | print("Got the mega request.", flush=True) 25 | matches = re.search(megaRx, msg.text) 26 | if not matches: 27 | print("Invalid mega file link format", flush=True) 28 | raise Exception( 29 | "`Send mega.nz public file link in proper format.\nhttps://mega.nz/file/XXXXXXXX#YYYYYYYYYYYY`") 30 | 31 | fileId = matches.group(1) 32 | key = matches.group(2) 33 | 34 | print("Getting the file size", flush=True) 35 | size_kb, size_readable = getSize(f"https://mega.nz/file/{fileId}#{key}") 36 | if not size_kb or not size_readable: 37 | raise Exception("Unable to fetch the file size") 38 | 39 | print("megadl subprocess started", flush=True) 40 | pro = subprocess.Popen(['megadl', '--print-names', 41 | '--path', f'.megatmp.{fileId}', f'https://mega.nz/#!{fileId}!{key}'], stdout=subprocess.PIPE) 42 | time.sleep(3) 43 | pro.terminate() 44 | 45 | try: 46 | fileName = pro.communicate()[0].decode("utf-8").partition("\n")[0] 47 | fileName = fileNameRx.search(fileName).group(1) 48 | print(fileName, flush=True) 49 | 50 | mediainfo = subprocess.check_output( 51 | ['mediainfo', f'.megatmp.{fileId}']).decode("utf-8") 52 | mediainfo_json = json.loads(subprocess.check_output( 53 | ['mediainfo', f'.megatmp.{fileId}', '--Output=JSON']).decode("utf-8")) 54 | duration = float(mediainfo_json['media']['track'][0]['Duration']) 55 | 56 | bitrate_kbps = size_kb/duration 57 | bitrate = humanBitrate(bitrate_kbps) 58 | 59 | # print(bitrate) 60 | 61 | lines = mediainfo.splitlines() 62 | for i in range(len(lines)): 63 | if 'Complete name' in lines[i]: 64 | lines[i] = re.sub(r": \..+", ': '+fileName, lines[i]) 65 | elif 'File size' in lines[i]: 66 | lines[i] = re.sub(r": .+", ': '+size_readable, lines[i]) 67 | elif 'Overall bit rate' in lines[i] and 'Overall bit rate mode' not in lines[i]: 68 | lines[i] = re.sub(r": .+", ': '+bitrate, lines[i]) 69 | elif 'IsTruncated' in lines[i] or 'FileExtension_Invalid' in lines[i]: 70 | lines[i] = '' 71 | remove_N(lines) 72 | 73 | # save the list to a file 74 | with open(f'{fileName}.txt', 'w') as f: 75 | f.write('\n'.join(lines)) 76 | 77 | msg.send_document(document=f'{fileName}.txt', caption=f'`{fileName}`') 78 | 79 | print("mega File mediainfo sent", flush=True) 80 | os.remove(f'{fileName}.txt') 81 | except: 82 | msg.reply( 83 | "Either you have sent a non-video/non-audio URL,\nor the link does not exist.") 84 | print("Either you have sent a non-video/non-audio URL,or the link does not exist", flush=True) 85 | finally: 86 | os.remove(f'.megatmp.{fileId}') 87 | 88 | 89 | def getSize(link): 90 | response = requests.get(link) 91 | soup = BeautifulSoup(response.text, 'lxml') 92 | temp = soup.find_all("meta")[0]["content"] 93 | search = sizeRx.search(temp) 94 | if not search: 95 | return None, None 96 | size, unit = search.groups() 97 | print(size, unit, flush=True) 98 | if unit == 'GB': 99 | return (float(size) * 8 * 1000000, size+' '+unit) 100 | elif unit == 'MB': 101 | return (float(size) * 8 * 1000, size+' '+unit) 102 | else: 103 | return (None, None) 104 | 105 | 106 | print("mega.py loaded", flush=True) 107 | --------------------------------------------------------------------------------