commit
cf5cffe60e
26
ncm/api.py
26
ncm/api.py
|
@ -4,7 +4,7 @@ import requests
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ncm.encrypt import encrypted_request
|
from ncm.encrypt import encrypted_request
|
||||||
from ncm.constants import headers
|
from ncm.constants import headers, get_program_url, program_download_url
|
||||||
from ncm.constants import song_download_url
|
from ncm.constants import song_download_url
|
||||||
from ncm.constants import get_song_url
|
from ncm.constants import get_song_url
|
||||||
from ncm.constants import get_album_url
|
from ncm.constants import get_album_url
|
||||||
|
@ -52,9 +52,31 @@ class CloudApi(object):
|
||||||
"""
|
"""
|
||||||
url = get_song_url(song_id)
|
url = get_song_url(song_id)
|
||||||
result = self.get_request(url)
|
result = self.get_request(url)
|
||||||
|
|
||||||
return result['songs'][0]
|
return result['songs'][0]
|
||||||
|
|
||||||
|
def get_program(self, program_id):
|
||||||
|
"""
|
||||||
|
Get program info by its id
|
||||||
|
:param bit_rate:
|
||||||
|
:param program_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = get_program_url(program_id)
|
||||||
|
csrf = ''
|
||||||
|
result = self.post_request(url, {'id': program_id, 'csrf_token': csrf})
|
||||||
|
return result['program']
|
||||||
|
|
||||||
|
def get_program_url(self, program, encode_type="aac", level="standard"):
|
||||||
|
"""
|
||||||
|
Get the download url of the program
|
||||||
|
:param program
|
||||||
|
:param encode_type:
|
||||||
|
:param level:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
id = program['mainSong']['id']
|
||||||
|
return self.get_song_url(id)
|
||||||
|
|
||||||
def get_album_songs(self, album_id):
|
def get_album_songs(self, album_id):
|
||||||
"""
|
"""
|
||||||
Get all album songs info by album id
|
Get all album songs info by album id
|
||||||
|
|
|
@ -7,22 +7,26 @@ modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3
|
||||||
nonce = '0CoJUm6Qyw8W8jud'
|
nonce = '0CoJUm6Qyw8W8jud'
|
||||||
pub_key = '010001'
|
pub_key = '010001'
|
||||||
|
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'Accept': '*/*',
|
'Accept': '*/*',
|
||||||
'Host': 'music.163.com',
|
'Host': 'music.163.com',
|
||||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
|
||||||
'Referer': 'http://music.163.com',
|
'Referer': 'http://music.163.com',
|
||||||
'Cookie': 'appver=2.0.2; _ntes_nuid={}; NMTID={}'.format(''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)),
|
'Cookie': 'appver=2.0.2; _ntes_nuid={}; NMTID={}'.format(
|
||||||
''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)))
|
''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)),
|
||||||
|
''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)))
|
||||||
}
|
}
|
||||||
song_download_url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='
|
song_download_url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='
|
||||||
|
program_download_url = 'http://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='
|
||||||
|
|
||||||
def get_song_url(song_id):
|
def get_song_url(song_id):
|
||||||
return 'http://music.163.com/api/song/detail/?ids=[{}]'.format(song_id)
|
return 'http://music.163.com/api/song/detail/?ids=[{}]'.format(song_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_program_url(program_id):
|
||||||
|
return 'http://music.163.com/weapi/dj/program/detail?csrf_token='
|
||||||
|
|
||||||
|
|
||||||
def get_album_url(album_id):
|
def get_album_url(album_id):
|
||||||
return 'http://music.163.com/api/album/{}/'.format(album_id)
|
return 'http://music.163.com/api/album/{}/'.format(album_id)
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,17 @@ def download_song_by_id(song_id, download_folder, sub_folder=True):
|
||||||
download_song_by_song(song, download_folder, sub_folder)
|
download_song_by_song(song, download_folder, sub_folder)
|
||||||
|
|
||||||
|
|
||||||
def download_song_by_song(song, download_folder, sub_folder=True):
|
def download_song_by_song(song, download_folder, sub_folder=True, program=False):
|
||||||
# get song info
|
# get song info
|
||||||
api = CloudApi()
|
api = CloudApi()
|
||||||
song_id = song['id']
|
song_id = song['id']
|
||||||
song_name = format_string(song['name'])
|
song_name = format_string(song['name'])
|
||||||
artist_name = format_string(song['artists'][0]['name'])
|
if program:
|
||||||
album_name = format_string(song['album']['name'])
|
artist_name = format_string(song['dj']['nickname'])
|
||||||
|
album_name = format_string(song['dj']['brand'])
|
||||||
|
else:
|
||||||
|
artist_name = format_string(song['artists'][0]['name'])
|
||||||
|
album_name = format_string(song['album']['name'])
|
||||||
|
|
||||||
# update song file name by config
|
# update song file name by config
|
||||||
song_file_name = '{}.mp3'.format(song_name)
|
song_file_name = '{}.mp3'.format(song_name)
|
||||||
|
@ -51,7 +55,11 @@ def download_song_by_song(song, download_folder, sub_folder=True):
|
||||||
song_download_folder = download_folder
|
song_download_folder = download_folder
|
||||||
|
|
||||||
# download song
|
# download song
|
||||||
song_url = api.get_song_url(song_id)
|
if program:
|
||||||
|
song_url = api.get_program_url(song, level="standard")
|
||||||
|
else:
|
||||||
|
song_url = api.get_song_url(song_id)
|
||||||
|
|
||||||
if song_url is None:
|
if song_url is None:
|
||||||
print('Song <<{}>> is not available due to copyright issue!'.format(song_name))
|
print('Song <<{}>> is not available due to copyright issue!'.format(song_name))
|
||||||
return
|
return
|
||||||
|
@ -61,9 +69,16 @@ def download_song_by_song(song, download_folder, sub_folder=True):
|
||||||
return
|
return
|
||||||
|
|
||||||
# download cover
|
# download cover
|
||||||
cover_url = song['album']['blurPicUrl']
|
if program:
|
||||||
|
cover_url = song['coverUrl']
|
||||||
|
else:
|
||||||
|
cover_url = song['album']['coverUrl']
|
||||||
|
|
||||||
if cover_url is None:
|
if cover_url is None:
|
||||||
cover_url = song['album']['picUrl']
|
if program:
|
||||||
|
cover_url = song['mainSong']['album']['picUrl']
|
||||||
|
else:
|
||||||
|
cover_url = song['album']['picUrl']
|
||||||
cover_file_name = 'cover_{}.jpg'.format(song_id)
|
cover_file_name = 'cover_{}.jpg'.format(song_id)
|
||||||
download_file(cover_url, cover_file_name, song_download_folder)
|
download_file(cover_url, cover_file_name, song_download_folder)
|
||||||
|
|
||||||
|
@ -73,14 +88,13 @@ def download_song_by_song(song, download_folder, sub_folder=True):
|
||||||
# add metadata for song
|
# add metadata for song
|
||||||
song_file_path = os.path.join(song_download_folder, song_file_name)
|
song_file_path = os.path.join(song_download_folder, song_file_name)
|
||||||
cover_file_path = os.path.join(song_download_folder, cover_file_name)
|
cover_file_path = os.path.join(song_download_folder, cover_file_name)
|
||||||
add_metadata_to_song(song_file_path, cover_file_path, song)
|
add_metadata_to_song(song_file_path, cover_file_path, song, program)
|
||||||
|
|
||||||
# delete cover file
|
# delete cover file
|
||||||
os.remove(cover_file_path)
|
os.remove(cover_file_path)
|
||||||
|
|
||||||
|
|
||||||
def download_file(file_url, file_name, folder):
|
def download_file(file_url, file_name, folder):
|
||||||
|
|
||||||
if not os.path.exists(folder):
|
if not os.path.exists(folder):
|
||||||
os.makedirs(folder)
|
os.makedirs(folder)
|
||||||
file_path = os.path.join(folder, file_name)
|
file_path = os.path.join(folder, file_name)
|
||||||
|
@ -113,8 +127,8 @@ class ProgressBar(object):
|
||||||
self.end_str = '\r'
|
self.end_str = '\r'
|
||||||
|
|
||||||
def __get_info(self):
|
def __get_info(self):
|
||||||
return 'Progress: {:6.2f}%, {:8.2f}KB, [{:.30}]'\
|
return 'Progress: {:6.2f}%, {:8.2f}KB, [{:.30}]' \
|
||||||
.format(self.count/self.total*100, self.total/1024, self.file_name)
|
.format(self.count / self.total * 100, self.total / 1024, self.file_name)
|
||||||
|
|
||||||
def refresh(self, count):
|
def refresh(self, count):
|
||||||
self.count += count
|
self.count += count
|
||||||
|
|
|
@ -18,7 +18,7 @@ def resize_img(file_path, max_size=(640, 640), quality=90):
|
||||||
img.save(file_path, quality=quality)
|
img.save(file_path, quality=quality)
|
||||||
|
|
||||||
|
|
||||||
def add_metadata_to_song(file_path, cover_path, song):
|
def add_metadata_to_song(file_path, cover_path, song, is_program = False):
|
||||||
# If no ID3 tags in mp3 file
|
# If no ID3 tags in mp3 file
|
||||||
try:
|
try:
|
||||||
audio = MP3(file_path, ID3=ID3)
|
audio = MP3(file_path, ID3=ID3)
|
||||||
|
@ -52,12 +52,21 @@ def add_metadata_to_song(file_path, cover_path, song):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# add artist name
|
# add artist name
|
||||||
id3.add(
|
if is_program:
|
||||||
TPE1(
|
id3.add(
|
||||||
encoding=3,
|
TPE1(
|
||||||
text=song['artists'][0]['name']
|
encoding=3,
|
||||||
|
text=song['dj']['nickname']
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
id3.add(
|
||||||
|
TPE1(
|
||||||
|
encoding=3,
|
||||||
|
text=song['artists'][0]['name']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# add song name
|
# add song name
|
||||||
id3.add(
|
id3.add(
|
||||||
TIT2(
|
TIT2(
|
||||||
|
@ -66,17 +75,27 @@ def add_metadata_to_song(file_path, cover_path, song):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# add album name
|
# add album name
|
||||||
id3.add(
|
if is_program:
|
||||||
TALB(
|
id3.add(
|
||||||
encoding=3,
|
TALB(
|
||||||
text=song['album']['name']
|
encoding=3,
|
||||||
|
text=song['dj']['brand']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
id3.add(
|
||||||
|
TALB(
|
||||||
|
encoding=3,
|
||||||
|
text=song['album']['name']
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
#add track no
|
#add track no
|
||||||
id3.add(
|
if not is_program:
|
||||||
TRCK(
|
id3.add(
|
||||||
encoding=3,
|
TRCK(
|
||||||
text="%s/%s" %(song['no'],song['album']['size'])
|
encoding=3,
|
||||||
|
text="%s/%s" %(song['no'],song['album']['size'])
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
# programs doesn't have a valid album info.
|
||||||
id3.save(v2_version=3)
|
id3.save(v2_version=3)
|
||||||
|
|
11
ncm/start.py
11
ncm/start.py
|
@ -34,6 +34,13 @@ def download_album_songs(album_id):
|
||||||
download_song_by_song(song, folder_path, False)
|
download_song_by_song(song, folder_path, False)
|
||||||
|
|
||||||
|
|
||||||
|
def download_program(program_id):
|
||||||
|
program = api.get_program(program_id)
|
||||||
|
folder_name = format_string(program['dj']['brand']) + ' - album'
|
||||||
|
folder_path = os.path.join(config.DOWNLOAD_DIR, folder_name)
|
||||||
|
download_song_by_song(program, folder_path, False, True)
|
||||||
|
|
||||||
|
|
||||||
def download_playlist_songs(playlist_id):
|
def download_playlist_songs(playlist_id):
|
||||||
songs, playlist_name = api.get_playlist_songs(playlist_id)
|
songs, playlist_name = api.get_playlist_songs(playlist_id)
|
||||||
folder_name = format_string(playlist_name) + ' - playlist'
|
folder_name = format_string(playlist_name) + ' - playlist'
|
||||||
|
@ -62,6 +69,8 @@ def main():
|
||||||
help='Download an artist hot 50 songs by artist_id')
|
help='Download an artist hot 50 songs by artist_id')
|
||||||
parser.add_argument('-a', metavar='album_id', dest='album_id',
|
parser.add_argument('-a', metavar='album_id', dest='album_id',
|
||||||
help='Download an album all songs by album_id')
|
help='Download an album all songs by album_id')
|
||||||
|
parser.add_argument('-dj', metavar='program_id', dest='program_id',
|
||||||
|
help='Download a program by program_id')
|
||||||
parser.add_argument('-p', metavar='playlist_id', dest='playlist_id',
|
parser.add_argument('-p', metavar='playlist_id', dest='playlist_id',
|
||||||
help='Download a playlist all songs by playlist_id')
|
help='Download a playlist all songs by playlist_id')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
@ -76,6 +85,8 @@ def main():
|
||||||
download_album_songs(get_parse_id(args.album_id))
|
download_album_songs(get_parse_id(args.album_id))
|
||||||
elif args.playlist_id:
|
elif args.playlist_id:
|
||||||
download_playlist_songs(get_parse_id(args.playlist_id))
|
download_playlist_songs(get_parse_id(args.playlist_id))
|
||||||
|
elif args.program_id:
|
||||||
|
download_program(get_parse_id(args.program_id))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in New Issue