mirror of
https://github.com/Suxiaoqinx/Netease_url.git
synced 2025-09-14 11:36:45 +08:00
Compare commits
9 Commits
acae07ffbe
...
2d619125f0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2d619125f0 | ||
![]() |
d62f2f04a6 | ||
![]() |
d1e1327909 | ||
![]() |
149b002859 | ||
![]() |
4c36462499 | ||
![]() |
ced1295054 | ||
![]() |
ce9c060704 | ||
![]() |
38520b4388 | ||
![]() |
8a012f3e40 |
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
14
README.md
14
README.md
@ -9,13 +9,17 @@ pip install -r requirements.txt
|
|||||||
# 环境要求
|
# 环境要求
|
||||||
Python >= 3
|
Python >= 3
|
||||||
|
|
||||||
# 请求示例
|
## GUI模式参数
|
||||||
|
python main.py
|
||||||
|
| 参数列表 | 参数说明 |
|
||||||
|
| ---- | ---- |
|
||||||
|
| --mode | api 或 gui|
|
||||||
|
| --level | 音质参数(请看下方音质说明) |
|
||||||
|
| --url | 解析获取到的网易云音乐地址 |
|
||||||
|
|
||||||
如图箭头显示
|
完整请求 python main.py --mode gui --url 音乐地址 --level 音质
|
||||||
|
|
||||||

|
## API模式参数列表
|
||||||
|
|
||||||
## 参数列表
|
|
||||||
|
|
||||||
请求链接选择 http://ip:port/Song_V1
|
请求链接选择 http://ip:port/Song_V1
|
||||||
|
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
# 网易云无损解析使用方法
|
|
||||||
先安装 文件所需要的依赖模块
|
|
||||||
pip install -r requirements.txt
|
|
||||||
再运行maingui.py文件即可
|
|
||||||
|
|
||||||
# 环境要求
|
|
||||||
Python >= 3
|
|
||||||
|
|
||||||
# 请求示例
|
|
||||||
|
|
||||||
如图箭头显示
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 参数列表
|
|
||||||
|
|
||||||
待补充
|
|
||||||
|
|
||||||
# 音质说明
|
|
||||||
|
|
||||||
待补充
|
|
||||||
|
|
||||||
# 注意事项
|
|
||||||
请先在cookie.txt文件内填入黑胶会员账号的cookie 才可以解析!
|
|
||||||
Cookie格式为↓
|
|
||||||
MUSIC_U=你获取到的MUSIC_U值;appver=8.9.70; 完整填入cookie.txt即可!
|
|
||||||
具体值在cookie.txt里面就有 替换一下就行了
|
|
||||||
|
|
||||||
# 感谢
|
|
||||||
[Ravizhan](https://github.com/ravizhan)
|
|
||||||
|
|
||||||
# 反馈方法
|
|
||||||
请在Github的lssues反馈 或者到我[博客](https://www.toubiec.cn)反馈
|
|
101
main.py
101
main.py
@ -1,4 +1,5 @@
|
|||||||
from flask import Flask, request, jsonify ,redirect ,Response
|
import argparse
|
||||||
|
from flask import Flask, request, render_template, redirect, jsonify
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
@ -45,7 +46,6 @@ def post(url, params, cookie):
|
|||||||
response = requests.post(url, headers=headers, cookies=cookies, data={"params": params})
|
response = requests.post(url, headers=headers, cookies=cookies, data={"params": params})
|
||||||
return response.text
|
return response.text
|
||||||
|
|
||||||
# 输入id选项
|
|
||||||
def ids(ids):
|
def ids(ids):
|
||||||
if '163cn.tv' in ids:
|
if '163cn.tv' in ids:
|
||||||
response = requests.get(ids, allow_redirects=False)
|
response = requests.get(ids, allow_redirects=False)
|
||||||
@ -55,7 +55,6 @@ def ids(ids):
|
|||||||
ids = ids[index:].split('&')[0]
|
ids = ids[index:].split('&')[0]
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
#转换文件大小
|
|
||||||
def size(value):
|
def size(value):
|
||||||
units = ["B", "KB", "MB", "GB", "TB", "PB"]
|
units = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||||
size = 1024.0
|
size = 1024.0
|
||||||
@ -65,24 +64,17 @@ def size(value):
|
|||||||
value = value / size
|
value = value / size
|
||||||
return value
|
return value
|
||||||
|
|
||||||
#转换音质
|
|
||||||
def music_level1(value):
|
def music_level1(value):
|
||||||
if value == 'standard':
|
levels = {
|
||||||
return "标准音质"
|
'standard': "标准音质",
|
||||||
elif value == 'exhigh':
|
'exhigh': "极高音质",
|
||||||
return "极高音质"
|
'lossless': "无损音质",
|
||||||
elif value == 'lossless':
|
'hires': "Hires音质",
|
||||||
return "无损音质"
|
'sky': "沉浸环绕声",
|
||||||
elif value == 'hires':
|
'jyeffect': "高清环绕声",
|
||||||
return "Hires音质"
|
'jymaster': "超清母带"
|
||||||
elif value == 'sky':
|
}
|
||||||
return "沉浸环绕声"
|
return levels.get(value, "未知音质")
|
||||||
elif value == 'jyeffect':
|
|
||||||
return "高清环绕声"
|
|
||||||
elif value == 'jymaster':
|
|
||||||
return "超清母带"
|
|
||||||
else:
|
|
||||||
return "未知音质"
|
|
||||||
|
|
||||||
def url_v1(id, level, cookies):
|
def url_v1(id, level, cookies):
|
||||||
url = "https://interface3.music.163.com/eapi/song/enhance/player/url/v1"
|
url = "https://interface3.music.163.com/eapi/song/enhance/player/url/v1"
|
||||||
@ -118,24 +110,23 @@ def url_v1(id, level, cookies):
|
|||||||
return json.loads(response)
|
return json.loads(response)
|
||||||
|
|
||||||
def name_v1(id):
|
def name_v1(id):
|
||||||
#歌曲信息接口
|
|
||||||
urls = "https://interface3.music.163.com/api/v3/song/detail"
|
urls = "https://interface3.music.163.com/api/v3/song/detail"
|
||||||
data = {'c': json.dumps([{"id":id,"v":0}])}
|
data = {'c': json.dumps([{"id":id,"v":0}])}
|
||||||
response = requests.post(url=urls, data=data)
|
response = requests.post(url=urls, data=data)
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
def lyric_v1(id,cookies):
|
def lyric_v1(id, cookies):
|
||||||
#歌词接口
|
|
||||||
url = "https://interface3.music.163.com/api/song/lyric"
|
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'}
|
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)
|
response = requests.post(url=url, data=data, cookies=cookies)
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
|
# Flask 应用部分
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
def hello_world():
|
def index():
|
||||||
return '你好,世界!'
|
return render_template('index.html')
|
||||||
|
|
||||||
@app.route('/Song_V1', methods=['GET', 'POST'])
|
@app.route('/Song_V1', methods=['GET', 'POST'])
|
||||||
def Song_v1():
|
def Song_v1():
|
||||||
@ -190,13 +181,61 @@ def Song_v1():
|
|||||||
"level":music_level1(urlv1['data'][0]['level']),
|
"level":music_level1(urlv1['data'][0]['level']),
|
||||||
"size": size(urlv1['data'][0]['size']),
|
"size": size(urlv1['data'][0]['size']),
|
||||||
"url": song_url.replace("http://", "https://", 1),
|
"url": song_url.replace("http://", "https://", 1),
|
||||||
"lyric": lyricv1.get('lrc', {}).get('lyric', '无歌词'),
|
"lyric": lyricv1['lrc']['lyric'],
|
||||||
"tlyric": lyricv1.get('tlyric', {}).get('lyric', '无翻译歌词')
|
"tlyric": lyricv1.get('tlyric', {}).get('lyric', None)
|
||||||
}
|
}
|
||||||
data = Response(json.dumps(data), content_type='application/json')
|
data = jsonify(data)
|
||||||
else:
|
else:
|
||||||
data = jsonify({"status": 400,'msg': '解析失败!请检查参数是否完整!'}), 400
|
data = jsonify({"status": 400,'msg': '解析失败!请检查参数是否完整!'}), 400
|
||||||
return data
|
return data
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def start_gui(url=None, level='lossless'):
|
||||||
|
if url:
|
||||||
|
print(f"正在处理 URL: {url},音质:{level}")
|
||||||
|
song_ids = ids(url)
|
||||||
|
cookies = parse_cookie(read_cookie())
|
||||||
|
urlv1 = url_v1(song_ids, level, cookies)
|
||||||
|
namev1 = name_v1(urlv1['data'][0]['id'])
|
||||||
|
lyricv1 = lyric_v1(urlv1['data'][0]['id'], cookies)
|
||||||
|
|
||||||
|
song_name = namev1['songs'][0]['name']
|
||||||
|
song_pic = namev1['songs'][0]['al']['picUrl']
|
||||||
|
artist_names = ', '.join(artist['name'] for artist in namev1['songs'][0]['ar'])
|
||||||
|
album_name = namev1['songs'][0]['al']['name']
|
||||||
|
music_quality = music_level1(urlv1['data'][0]['level'])
|
||||||
|
file_size = size(urlv1['data'][0]['size'])
|
||||||
|
music_url = urlv1['data'][0]['url']
|
||||||
|
lyrics = lyricv1['lrc']['lyric']
|
||||||
|
translated_lyrics = lyricv1.get('tlyric', {}).get('lyric', None)
|
||||||
|
|
||||||
|
output_text = f"""
|
||||||
|
歌曲名称: {song_name}
|
||||||
|
歌曲图片: {song_pic}
|
||||||
|
歌手: {artist_names}
|
||||||
|
专辑名称: {album_name}
|
||||||
|
音质: {music_quality}
|
||||||
|
大小: {file_size}
|
||||||
|
音乐链接: {music_url}
|
||||||
|
歌词: {lyrics}
|
||||||
|
翻译歌词: {translated_lyrics if translated_lyrics else '没有翻译歌词'}
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(output_text)
|
||||||
|
else:
|
||||||
|
print("没有提供 URL 参数")
|
||||||
|
|
||||||
|
def start_api():
|
||||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||||
|
|
||||||
|
# 启动模式解析
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description="启动 API 或 GUI")
|
||||||
|
parser.add_argument('--mode', choices=['api', 'gui'], help="选择启动模式:api 或 gui")
|
||||||
|
parser.add_argument('--url', help="提供 URL 参数供 GUI 模式使用")
|
||||||
|
parser.add_argument('--level', default='lossless', choices=['standard', 'exhigh', 'lossless', 'hires', 'sky', 'jyeffect', 'jymaster'], help="选择音质等级,默认是 lossless")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.mode == 'api':
|
||||||
|
start_api()
|
||||||
|
elif args.mode == 'gui':
|
||||||
|
start_gui(args.url, args.level)
|
||||||
|
191
maingui.py
191
maingui.py
@ -1,191 +0,0 @@
|
|||||||
from flask import Flask, request, render_template, jsonify, Response
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import urllib.parse
|
|
||||||
from hashlib import md5
|
|
||||||
from random import randrange
|
|
||||||
import requests
|
|
||||||
from cryptography.hazmat.primitives import padding
|
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms import StringField, SelectField, SubmitField
|
|
||||||
from wtforms.validators import DataRequired
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config['SECRET_KEY'] = 'your_secret_key'
|
|
||||||
|
|
||||||
# 定义表单类
|
|
||||||
class SongForm(FlaskForm):
|
|
||||||
song_ids = StringField('Song ID or URL', validators=[DataRequired()])
|
|
||||||
level = SelectField('Quality Level', choices=[
|
|
||||||
('standard', '标准音质'),
|
|
||||||
('exhigh', '极高音质'),
|
|
||||||
('lossless', '无损音质'),
|
|
||||||
('hires', 'Hires音质'),
|
|
||||||
('sky', '沉浸环绕声'),
|
|
||||||
('jyeffect', '高清环绕声'),
|
|
||||||
('jymaster', '超清母带'),
|
|
||||||
], validators=[DataRequired()])
|
|
||||||
submit = SubmitField('Submit')
|
|
||||||
|
|
||||||
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 parse_cookie(text: str):
|
|
||||||
cookie_ = [item.strip().split('=', 1) for item in text.strip().split(';') if item]
|
|
||||||
cookie_ = {k.strip(): v.strip() for k, v in cookie_}
|
|
||||||
return cookie_
|
|
||||||
|
|
||||||
def ids(ids):
|
|
||||||
if '163cn.tv' in ids:
|
|
||||||
response = requests.get(ids, allow_redirects=False)
|
|
||||||
ids = response.headers.get('Location')
|
|
||||||
if 'music.163.com' in ids:
|
|
||||||
index = ids.find('id=') + 3
|
|
||||||
ids = ids[index:].split('&')[0]
|
|
||||||
return ids
|
|
||||||
|
|
||||||
def read_cookie():
|
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
cookie_file = os.path.join(script_dir, 'cookie.txt')
|
|
||||||
with open(cookie_file, 'r') as f:
|
|
||||||
cookie_contents = f.read()
|
|
||||||
return cookie_contents
|
|
||||||
|
|
||||||
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 size(value):
|
|
||||||
units = ["B", "KB", "MB", "GB", "TB", "PB"]
|
|
||||||
size = 1024.0
|
|
||||||
for i in range(len(units)):
|
|
||||||
if (value / size) < 1:
|
|
||||||
return "%.2f%s" % (value, units[i])
|
|
||||||
value = value / size
|
|
||||||
return value
|
|
||||||
|
|
||||||
def music_level1(value):
|
|
||||||
if value == 'standard':
|
|
||||||
return "标准音质"
|
|
||||||
elif value == 'exhigh':
|
|
||||||
return "极高音质"
|
|
||||||
elif value == 'lossless':
|
|
||||||
return "无损音质"
|
|
||||||
elif value == 'hires':
|
|
||||||
return "Hires音质"
|
|
||||||
elif value == 'sky':
|
|
||||||
return "沉浸环绕声"
|
|
||||||
elif value == 'jyeffect':
|
|
||||||
return "高清环绕声"
|
|
||||||
elif value == 'jymaster':
|
|
||||||
return "超清母带"
|
|
||||||
else:
|
|
||||||
return "未知音质"
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
|
||||||
def index():
|
|
||||||
form = SongForm()
|
|
||||||
return render_template('index.html', form=form)
|
|
||||||
|
|
||||||
@app.route('/fetch_data', methods=['POST'])
|
|
||||||
def fetch_data():
|
|
||||||
song_ids = request.form.get('song_ids')
|
|
||||||
level = request.form.get('level')
|
|
||||||
|
|
||||||
if not song_ids or not level:
|
|
||||||
return jsonify({"status": 400, "msg": "缺少参数!"})
|
|
||||||
|
|
||||||
cookies = parse_cookie(read_cookie())
|
|
||||||
song_id = ids(song_ids)
|
|
||||||
urlv1 = url_v1(song_id, level, cookies)
|
|
||||||
namev1 = name_v1(urlv1['data'][0]['id'])
|
|
||||||
lyricv1 = lyric_v1(urlv1['data'][0]['id'], cookies)
|
|
||||||
|
|
||||||
if urlv1['data'][0]['url'] is not None:
|
|
||||||
song_url = urlv1['data'][0]['url']
|
|
||||||
song_name = namev1['songs'][0]['name']
|
|
||||||
song_picUrl = namev1['songs'][0]['al']['picUrl']
|
|
||||||
song_alname = namev1['songs'][0]['al']['name']
|
|
||||||
artist_names = '/'.join(ar['name'] for ar in namev1['songs'][0]['ar'])
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"status": 200,
|
|
||||||
"name": song_name,
|
|
||||||
"pic": song_picUrl,
|
|
||||||
"ar_name": artist_names,
|
|
||||||
"al_name": song_alname,
|
|
||||||
"level": music_level1(urlv1['data'][0]['level']),
|
|
||||||
"size": size(urlv1['data'][0]['size']),
|
|
||||||
"url": song_url.replace("http://", "https://", 1),
|
|
||||||
"lyric": lyricv1.get('lrc', {}).get('lyric', '无歌词'),
|
|
||||||
"tlyric": lyricv1.get('tlyric', {}).get('lyric', '无翻译歌词')
|
|
||||||
}
|
|
||||||
return jsonify(data)
|
|
||||||
return jsonify({"status": 400, "msg": "信息获取不完整!"})
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
|
@ -1,18 +1,5 @@
|
|||||||
blinker==1.8.2
|
|
||||||
certifi==2024.7.4
|
|
||||||
cffi==1.17.0
|
|
||||||
charset-normalizer==3.3.2
|
|
||||||
click==8.1.7
|
|
||||||
colorama==0.4.6
|
|
||||||
cryptography==40.0.2
|
cryptography==40.0.2
|
||||||
Flask==3.0.3
|
Flask==3.0.3
|
||||||
Flask-WTF==1.2.1
|
|
||||||
idna==3.8
|
|
||||||
itsdangerous==2.2.0
|
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.4
|
||||||
MarkupSafe==2.1.5
|
|
||||||
pycparser==2.22
|
|
||||||
requests==2.28.2
|
requests==2.28.2
|
||||||
urllib3==1.26.15
|
urllib3==1.26.15
|
||||||
Werkzeug==3.0.4
|
|
||||||
WTForms==3.1.2
|
|
||||||
|
@ -184,7 +184,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.post('/fetch_data', { song_ids: validId, level: level }, function(data) {
|
$.post('/Song_V1', { url: validId, level: level, type:'json' }, function(data) {
|
||||||
if (data.status === 200) {
|
if (data.status === 200) {
|
||||||
$('#song_name').text(data.name);
|
$('#song_name').text(data.name);
|
||||||
$('#song_picUrl').attr('href', data.pic).text('点击查看');
|
$('#song_picUrl').attr('href', data.pic).text('点击查看');
|
||||||
|
Loading…
Reference in New Issue
Block a user