Last active
February 27, 2025 17:17
-
-
Save 0x1d107/1eecf9c89a0999f18689c85a6e210f82 to your computer and use it in GitHub Desktop.
MTS music downloader. Hashing algorithm was taken from https://github.com/llistochek/yandex-music-downloader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/env python | |
| import lxml.html as lh | |
| import urllib.request as rq | |
| import json | |
| import hashlib | |
| from tqdm import tqdm | |
| import sys, os | |
| MTS_MUSIC = "https://music.mts.ru" | |
| """ | |
| How to get ya_token: | |
| 1. go to https://music.mts.ru | |
| 2. log in to your account with premium | |
| 3. open DevTools(ctrl+shift+I) | |
| 4. at Storage tab look for Local Storage data for https://music.mts.ru | |
| 5. copy string with key ya_token | |
| 6. paste it to ya_token.txt | |
| """ | |
| YA_TOKEN = None | |
| try: | |
| with open('ya_token.txt') as f: | |
| YA_TOKEN = f.readline().strip() | |
| except OSError: | |
| print("ya_token.txt not found. Defaulting to preview download") | |
| MD5_SALT = 'XGRlBW9FXlekgbPrRHuSiA' | |
| class TqdmUpTo(tqdm): | |
| """Alternative Class-based version of the above. | |
| Provides `update_to(n)` which uses `tqdm.update(delta_n)`. | |
| Inspired by [twine#242](https://github.com/pypa/twine/pull/242), | |
| [here](https://github.com/pypa/twine/commit/42e55e06). | |
| """ | |
| def update_to(self, b=1, bsize=1, tsize=None): | |
| """ | |
| b : int, optional | |
| Number of blocks transferred so far [default: 1]. | |
| bsize : int, optional | |
| Size of each block (in tqdm units) [default: 1]. | |
| tsize : int, optional | |
| Total size (in tqdm units). If [default: None] remains unchanged. | |
| """ | |
| if tsize is not None: | |
| self.total = tsize | |
| return self.update(b * bsize - self.n) # also sets self.n = b * bsize | |
| def download_file(url, filename): | |
| with TqdmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1, | |
| desc=filename) as t: | |
| rq.urlretrieve(url, filename, reporthook=t.update_to, data=None) | |
| t.total = t.n | |
| def get_album_info(album_id): | |
| tree = lh.parse(rq.urlopen(MTS_MUSIC+f'/album/{album_id}')) | |
| return json.loads(tree.xpath('//script/text()')[0])['props']['pageProps'][ | |
| 'albumItem'] | |
| def get_download_info(track_id, oauth_token): | |
| req = rq.Request(f"{MTS_MUSIC}/ya_api/tracks/{track_id}/download-info") | |
| if oauth_token: | |
| req.add_header("Authorization", f"OAuth {oauth_token}") | |
| dlinfo = json.load(rq.urlopen(req)) | |
| return dlinfo['result'] | |
| def get_file_download_Link(dlinfo): | |
| url_info = json.load(rq.urlopen(dlinfo+"&format=json")) | |
| url_info['hash'] = hashlib.md5((MD5_SALT+url_info['path'][1:]+url_info['s'] | |
| ).encode()).hexdigest() | |
| return "https://{host}/get-mp3/{hash}/{ts}{path}".format_map(url_info) | |
| album_id = sys.argv[1] if len(sys.argv) > 1 else "5899311" | |
| album_info = get_album_info(album_id) | |
| tracks = album_info['tracks'] | |
| print(album_info) | |
| album_dir = album_info['title'] | |
| os.makedirs(album_dir, exist_ok=True) | |
| coverfname = f"{album_dir}/cover.jpg" | |
| download_file(album_info['cover'], coverfname) | |
| coverdata = None | |
| with open(coverfname, 'rb') as f: | |
| coverdata = f.read() | |
| for i, trk in enumerate(tracks): | |
| trkid = trk['id'] | |
| trkname = ",".join(a['name'] for a in trk['artists'])+' - '+trk['title'] | |
| dlinfos = get_download_info(trkid, YA_TOKEN) | |
| best_info = max(dlinfos, | |
| key=lambda x: x['bitrateInKbps']) | |
| trkformat = best_info['codec'] | |
| best_link = best_info['downloadInfoUrl'] | |
| filename = f"{album_dir}/{i:02d}. {trkname}.{trkformat}" | |
| download_file(get_file_download_Link(best_link), filename) | |
| try: | |
| import eyed3 | |
| file = eyed3.load(filename) | |
| file.initTag() | |
| file.tag.album = album_info['title'] | |
| file.tag.artist = ",".join(a['name'] for a in trk['artists']) | |
| file.tag.album_artist = ",".join(a['name'] | |
| for a in album_info['artists']) | |
| file.tag.title = trk['title'] | |
| file.tag.track_num = i+1 | |
| file.tag.images.set(eyed3.id3.frames.ImageFrame.FRONT_COVER, coverdata, 'image/jpeg') | |
| file.tag.save() | |
| except ImportError: | |
| print("eyeD3 python module not found: ID3 tags are not set") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment