diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 7206a74..0000000 --- a/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM python:3.10-alpine - -WORKDIR /app - -COPY ./main.py . -COPY ./common ./common -COPY ./modules ./modules -COPY ./requirements.txt . - -# 指定源, 如果后期源挂了, 更换个源就可以. -RUN pip install --no-cache -i https://pypi.mirrors.ustc.edu.cn/simple/ -r requirements.txt - -CMD [ "python", "main.py" ] diff --git a/build.py b/build.py index f6ee837..1275f60 100644 --- a/build.py +++ b/build.py @@ -105,7 +105,7 @@ def build_test(fileName): 'PyInstaller', '-F', '-i', - 'icon.ico', + 'res/icon.ico', '--name', fileName if fileName else f'lx-music-api-server_{sha}', 'main.py']) @@ -140,7 +140,7 @@ def build_release(fileName = ''): 'PyInstaller', '-F', '-i', - 'icon.ico', + 'res/icon.ico', '--name', fileName if fileName else f'lx-music-api-server_{vername}', 'main.py']) @@ -231,4 +231,4 @@ def main(): main() except KeyboardInterrupt: print('[INFO] Aborting...') - sys.exit(0) \ No newline at end of file + sys.exit(0) diff --git a/common/default_config.py b/common/default_config.py index caa410a..952d1cf 100644 --- a/common/default_config.py +++ b/common/default_config.py @@ -141,13 +141,16 @@ use_vkey_api: false vkey_api_url: "xxx" - wy: # 网易云音乐相关配置 + wy: # 网易云音乐相关配置, proto支持值: ['offcial', 'ncmapi'] enable: true # 是否开启本平台服务 + proto: offcial user: cookie: "" # 账号cookie数据,可以通过浏览器获取,需要vip账号来获取会员歌曲,如果没有请留为空值 refresh_login: enable: false interval: 86400 + ncmapi: + api_url: "" # NeteaseCloudMusicApi的URL, 自行参考https://gitlab.com/Binaryify/neteasecloudmusicapi搭建 mg: # 咪咕音乐相关配置 enable: true # 是否开启本平台服务 diff --git a/common/variable.py b/common/variable.py index f195c21..9b04d86 100644 --- a/common/variable.py +++ b/common/variable.py @@ -8,36 +8,34 @@ # This file is part of the "lx-music-api-server" project. import os as _os -import ujson as _json +import ruamel.yaml as _yaml + +yaml = _yaml.YAML() def _read_config_file(): try: - with open("./config/config.json", "r", encoding="utf-8") as f: - return _json.load(f) + with open(f"./config/config.yml", "r", encoding="utf-8") as f: + return yaml.load(f.read()) except: - return {} + return [] def _read_config(key): - try: - config = _read_config_file() - keys = key.split('.') - value = config - for k in keys: - if isinstance(value, dict): - if k not in value and keys.index(k) != len(keys) - 1: - value[k] = {} - elif k not in value and keys.index(k) == len(keys) - 1: - value = None - value = value[k] - else: + config = _read_config_file() + keys = key.split('.') + value = config + for k in keys: + if isinstance(value, dict): + if k not in value and keys.index(k) != len(keys) - 1: + value[k] = [] + elif k not in value and keys.index(k) == len(keys) - 1: value = None - break - - return value - except: - return None + value = value[k] + else: + value = None + break + return value _dm = _read_config("common.debug_mode") diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index aff40ff..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: "3.8" -services: - lx: - container_name: lx-server - build: . - ports: - - "9763:9763" - volumes: - - .:/app - environment: - TZ: 'Asia/Shanghai' - restart: always -networks: - default: - diff --git a/modules/wy/__init__.py b/modules/wy/__init__.py index 46e1fd9..e5977c1 100644 --- a/modules/wy/__init__.py +++ b/modules/wy/__init__.py @@ -15,6 +15,9 @@ import ujson as json from . import refresh_login +PROTO = config.read_config("module.wy.proto") +API_URL = config.read_config("module.wy.ncmapi.api_url") + tools = { 'qualityMap': { '128k': 'standard', @@ -45,32 +48,64 @@ }, } + async def url(songId, quality): - path = '/api/song/enhance/player/url/v1' - requestUrl = 'https://interface.music.163.com/eapi/song/enhance/player/url/v1' - requestBody = { - "ids": json.dumps([songId]), - "level": tools["qualityMap"][quality], - "encodeType": "flac", - } - if (quality == "sky"): - requestBody["immerseType"] = "c51" - req = await Httpx.AsyncRequest(requestUrl, { - 'method': 'POST', - 'headers': { - 'Cookie': config.read_config('module.wy.user.cookie') if (not variable.use_cookie_pool) else random.choice(config.read_config('module.cookiepool.wy'))['cookie'], - }, - 'form': eapiEncrypt(path, json.dumps(requestBody)) - }) - body = req.json() - if (not body.get("data") or (not body.get("data")) or (not body.get("data")[0].get("url"))): - raise FailedException("failed") + if PROTO == "offcial": + path = '/api/song/enhance/player/url/v1' + requestUrl = 'https://interface.music.163.com/eapi/song/enhance/player/url/v1' + requestBody = { + "ids": json.dumps([songId]), + "level": tools["qualityMap"][quality], + "encodeType": "flac", + } + if (quality == "sky"): + requestBody["immerseType"] = "c51" + req = await Httpx.AsyncRequest(requestUrl, { + 'method': 'POST', + 'headers': { + 'Cookie': config.read_config('module.wy.user.cookie') if (not variable.use_cookie_pool) else random.choice(config.read_config('module.cookiepool.wy'))['cookie'], + }, + 'form': eapiEncrypt(path, json.dumps(requestBody)) + }) + body = req.json() + if (not body.get("data") or (not body.get("data")) or (not body.get("data")[0].get("url"))): + raise FailedException("失败") + + data = body["data"][0] + if (data['level'] != tools['qualityMap'][quality]): + raise FailedException("reject unmatched quality") - data = body["data"][0] - if (data['level'] != tools['qualityMap'][quality]): - raise FailedException("reject unmatched quality") + return { + 'url': data["url"].split("?")[0], + 'quality': tools['qualityMapReverse'][data['level']] + } + elif (PROTO == "ncmapi") and (API_URL): + requestUrl = f"{API_URL}/song/url/v1" + requestBody = { + "ids": songId, + "level": tools["qualityMap"][quality], + "cookie": config.read_config('module.wy.user.cookie') if (not variable.use_cookie_pool) else random.choice(config.read_config('module.cookiepool.wy'))['cookie'] + } + req = await Httpx.AsyncRequest(requestUrl, { + "method": "GET", + "params": requestBody + }) + body = req.json() + if (body["code"] != 200) or (not body.get(data, "")): + raise FailedException("失败") + data = body["data"][0] - return { - 'url': data["url"].split("?")[0], - 'quality': tools['qualityMapReverse'][data['level']] - } + # 修正:映射服务器返回的 level 为标准化值 + data_level = data['level'] + expected_level = tools["qualityMap"][quality] + + # 检查客户端请求的 quality 与服务器返回的 level 是否匹配 + if data_level != expected_level: + raise FailedException( + f"reject unmatched quality: expected={expected_level}, got={data_level}" + ) + + return { + 'url': data["url"].split("?")[0], + 'quality': quality + } diff --git a/icon.ico b/res/icon.ico similarity index 100% rename from icon.ico rename to res/icon.ico diff --git a/icon.png b/res/icon.png similarity index 100% rename from icon.png rename to res/icon.png