├── 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 | [](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 |
--------------------------------------------------------------------------------