mirror of
https://github.com/Suxiaoqinx/Netease_url.git
synced 2025-07-14 05:12:21 +08:00
214 lines
7.6 KiB
Python
214 lines
7.6 KiB
Python
import json
|
||
import urllib.parse
|
||
from random import randrange
|
||
import requests
|
||
from hashlib import md5
|
||
from cryptography.hazmat.primitives import padding
|
||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||
|
||
def HexDigest(data):
|
||
return "".join([hex(d)[2:].zfill(2) for d in data])
|
||
|
||
def HashDigest(text):
|
||
HASH = md5(text.encode("utf-8"))
|
||
return HASH.digest()
|
||
|
||
def HashHexDigest(text):
|
||
return HexDigest(HashDigest(text))
|
||
|
||
def post(url, params, cookie):
|
||
headers = {
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36 Chrome/91.0.4472.164 NeteaseMusicDesktop/2.10.2.200154',
|
||
'Referer': '',
|
||
}
|
||
cookies = {
|
||
"os": "pc",
|
||
"appver": "",
|
||
"osver": "",
|
||
"deviceId": "pyncm!"
|
||
}
|
||
cookies.update(cookie)
|
||
response = requests.post(url, headers=headers, cookies=cookies, data={"params": params})
|
||
return response.text
|
||
|
||
def url_v1(id, level, cookies):
|
||
url = "https://interface3.music.163.com/eapi/song/enhance/player/url/v1"
|
||
AES_KEY = b"e82ckenh8dichen8"
|
||
config = {
|
||
"os": "pc",
|
||
"appver": "",
|
||
"osver": "",
|
||
"deviceId": "pyncm!",
|
||
"requestId": str(randrange(20000000, 30000000))
|
||
}
|
||
|
||
payload = {
|
||
'ids': [id],
|
||
'level': level,
|
||
'encodeType': 'flac',
|
||
'header': json.dumps(config),
|
||
}
|
||
|
||
if level == 'sky':
|
||
payload['immerseType'] = 'c51'
|
||
|
||
url2 = urllib.parse.urlparse(url).path.replace("/eapi/", "/api/")
|
||
digest = HashHexDigest(f"nobody{url2}use{json.dumps(payload)}md5forencrypt")
|
||
params = f"{url2}-36cd479b6b5-{json.dumps(payload)}-36cd479b6b5-{digest}"
|
||
padder = padding.PKCS7(algorithms.AES(AES_KEY).block_size).padder()
|
||
padded_data = padder.update(params.encode()) + padder.finalize()
|
||
cipher = Cipher(algorithms.AES(AES_KEY), modes.ECB())
|
||
encryptor = cipher.encryptor()
|
||
enc = encryptor.update(padded_data) + encryptor.finalize()
|
||
params = HexDigest(enc)
|
||
response = post(url, params, cookies)
|
||
return json.loads(response)
|
||
|
||
def name_v1(id):
|
||
urls = "https://interface3.music.163.com/api/v3/song/detail"
|
||
data = {'c': json.dumps([{"id":id,"v":0}])}
|
||
response = requests.post(url=urls, data=data)
|
||
return response.json()
|
||
|
||
def lyric_v1(id, cookies):
|
||
url = "https://interface3.music.163.com/api/song/lyric"
|
||
data = {'id': id, 'cp': 'false', 'tv': '0', 'lv': '0', 'rv': '0', 'kv': '0', 'yv': '0', 'ytv': '0', 'yrv': '0'}
|
||
response = requests.post(url=url, data=data, cookies=cookies)
|
||
return response.json()
|
||
|
||
def search_music(keywords, cookies, limit=10):
|
||
"""
|
||
网易云音乐搜索接口,返回歌曲信息列表
|
||
:param keywords: 搜索关键词
|
||
:param cookies: 登录 cookies
|
||
:param limit: 返回数量
|
||
:return: 歌曲信息列表
|
||
"""
|
||
url = 'https://music.163.com/api/cloudsearch/pc'
|
||
data = {'s': keywords, 'type': 1, 'limit': limit}
|
||
headers = {
|
||
'User-Agent': 'Mozilla/5.0',
|
||
'Referer': 'https://music.163.com/'
|
||
}
|
||
response = requests.post(url, data=data, headers=headers, cookies=cookies)
|
||
result = response.json()
|
||
songs = []
|
||
for item in result.get('result', {}).get('songs', []):
|
||
song_info = {
|
||
'id': item['id'],
|
||
'name': item['name'],
|
||
'artists': '/'.join(artist['name'] for artist in item['ar']),
|
||
'album': item['al']['name'],
|
||
'picUrl': item['al']['picUrl']
|
||
}
|
||
songs.append(song_info)
|
||
return songs
|
||
|
||
def playlist_detail(playlist_id, cookies):
|
||
"""
|
||
获取网易云歌单详情及全部歌曲列表
|
||
:param playlist_id: 歌单ID
|
||
:param cookies: 登录 cookies
|
||
:return: 歌单基本信息和全部歌曲列表
|
||
"""
|
||
url = f'https://music.163.com/api/v6/playlist/detail'
|
||
data = {'id': playlist_id}
|
||
headers = {
|
||
'User-Agent': 'Mozilla/5.0',
|
||
'Referer': 'https://music.163.com/'
|
||
}
|
||
response = requests.post(url, data=data, headers=headers, cookies=cookies)
|
||
result = response.json()
|
||
playlist = result.get('playlist', {})
|
||
info = {
|
||
'id': playlist.get('id'),
|
||
'name': playlist.get('name'),
|
||
'coverImgUrl': playlist.get('coverImgUrl'),
|
||
'creator': playlist.get('creator', {}).get('nickname', ''),
|
||
'trackCount': playlist.get('trackCount'),
|
||
'description': playlist.get('description', ''),
|
||
'tracks': []
|
||
}
|
||
# 获取所有trackIds
|
||
track_ids = [str(t['id']) for t in playlist.get('trackIds', [])]
|
||
# 分批获取详细信息(每批最多100首)
|
||
for i in range(0, len(track_ids), 100):
|
||
batch_ids = track_ids[i:i+100]
|
||
song_detail_url = 'https://interface3.music.163.com/api/v3/song/detail'
|
||
song_data = {'c': json.dumps([{ 'id': int(sid), 'v': 0 } for sid in batch_ids])}
|
||
song_resp = requests.post(url=song_detail_url, data=song_data, headers=headers, cookies=cookies)
|
||
song_result = song_resp.json()
|
||
for song in song_result.get('songs', []):
|
||
info['tracks'].append({
|
||
'id': song['id'],
|
||
'name': song['name'],
|
||
'artists': '/'.join(artist['name'] for artist in song['ar']),
|
||
'album': song['al']['name'],
|
||
'picUrl': song['al']['picUrl']
|
||
})
|
||
return info
|
||
|
||
def album_detail(album_id, cookies):
|
||
"""
|
||
获取网易云专辑详情及全部歌曲列表
|
||
:param album_id: 专辑ID
|
||
:param cookies: 登录 cookies
|
||
:return: 专辑基本信息和全部歌曲列表
|
||
"""
|
||
url = f'https://music.163.com/api/v1/album/{album_id}'
|
||
headers = {
|
||
'User-Agent': 'Mozilla/5.0',
|
||
'Referer': 'https://music.163.com/'
|
||
}
|
||
response = requests.get(url, headers=headers, cookies=cookies)
|
||
result = response.json()
|
||
album = result.get('album', {})
|
||
info = {
|
||
'id': album.get('id'),
|
||
'name': album.get('name'),
|
||
'coverImgUrl': get_pic_url(album.get('pic')),
|
||
#'coverImgEncryptId': netease_encryptId(str(album.get('pic'))),
|
||
'artist': album.get('artist', {}).get('name', ''),
|
||
'publishTime': album.get('publishTime'),
|
||
'description': album.get('description', ''),
|
||
'songs': []
|
||
}
|
||
for song in result.get('songs', []):
|
||
info['songs'].append({
|
||
'id': song['id'],
|
||
'name': song['name'],
|
||
'artists': '/'.join(artist['name'] for artist in song['ar']),
|
||
'album': song['al']['name'],
|
||
'picUrl': get_pic_url(song['al'].get('pic'))
|
||
})
|
||
return info
|
||
|
||
def netease_encryptId(id_str):
|
||
"""
|
||
网易云加密图片ID算法(PHP移植版)
|
||
:param id_str: 歌曲/专辑/图片ID(字符串)
|
||
:return: 加密后的字符串
|
||
"""
|
||
import base64
|
||
magic = list('3go8&$8*3*3h0k(2)2')
|
||
song_id = list(id_str)
|
||
for i in range(len(song_id)):
|
||
song_id[i] = chr(ord(song_id[i]) ^ ord(magic[i % len(magic)]))
|
||
m = ''.join(song_id)
|
||
import hashlib
|
||
md5_bytes = hashlib.md5(m.encode('utf-8')).digest()
|
||
result = base64.b64encode(md5_bytes).decode('utf-8')
|
||
result = result.replace('/', '_').replace('+', '-')
|
||
return result
|
||
|
||
def get_pic_url(pic_id, size=300):
|
||
"""
|
||
获取网易云加密歌曲/专辑封面直链
|
||
:param pic_id: 封面ID(数字或字符串)
|
||
:param size: 图片尺寸,默认300
|
||
:return: {'url': url}
|
||
"""
|
||
enc_id = netease_encryptId(str(pic_id))
|
||
url = f'https://p3.music.126.net/{enc_id}/{pic_id}.jpg?param={size}y{size}'
|
||
return url
|