mirror of
https://github.com/2061360308/NeteaseCloudMusic_PythonSDK.git
synced 2024-11-21 22:48:03 +00:00
chore:update publish.yml
This commit is contained in:
parent
f34639a9ce
commit
fb1d5fa298
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.1.8
|
current_version = 0.1.7
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
|
|
||||||
|
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Build a binary wheel and a source tarball
|
- name: Build a binary wheel and a source tarball
|
||||||
run: |
|
run: |
|
||||||
cd package
|
cd package
|
||||||
python setup.py build
|
python setup.py upload
|
||||||
ls ./dist/
|
ls ./dist/
|
||||||
|
|
||||||
- name: Publish distribution 📦 to PyPI
|
- name: Publish distribution 📦 to PyPI
|
||||||
|
@ -17,6 +17,7 @@ Description-Content-Type: text/markdown
|
|||||||
License-File: LICENSE
|
License-File: LICENSE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# NeteaseCloudMusic_PythonSDK
|
# NeteaseCloudMusic_PythonSDK
|
||||||
> 基于 [ NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) 封装的 Python SDK。
|
> 基于 [ NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) 封装的 Python SDK。
|
||||||
> 网易云API Python版本。
|
> 网易云API Python版本。
|
||||||
@ -54,15 +55,48 @@ print(api_list())
|
|||||||
|
|
||||||
> 注意: request(self, name, query=None) 的第一个参数为API名称,第二个参数为API参数,具体API名称和参数请参考 [NeteaseCloudMusicApi文档](https://docs.neteasecloudmusicapi.binaryify.com),name支持`/song/url/v1`和`song_url_v1`两种写法。
|
> 注意: request(self, name, query=None) 的第一个参数为API名称,第二个参数为API参数,具体API名称和参数请参考 [NeteaseCloudMusicApi文档](https://docs.neteasecloudmusicapi.binaryify.com),name支持`/song/url/v1`和`song_url_v1`两种写法。
|
||||||
|
|
||||||
|
> api已加入自动缓存,在测试接口时,如果频繁调用获取结果在query里应该加上timestamp参数来区分,例如
|
||||||
|
> ```python
|
||||||
|
> response = netease_cloud_music_api.request("song_url_v1", {"id": 33894312, "level": "exhigh", "timestamp": time.time()})
|
||||||
|
> ```
|
||||||
|
|
||||||
### 开发
|
### 开发
|
||||||
- 克隆项目 `git clone git@github.com:2061360308/NeteaseCloudMusic_PythonSDK.git`
|
- 克隆项目 `git clone git@github.com:2061360308/NeteaseCloudMusic_PythonSDK.git`
|
||||||
- 安装依赖 `pip install -r requirements.txt`
|
- 安装依赖 `pip install -r requirements.txt`
|
||||||
- 目录/文件说明
|
- 目录/文件说明
|
||||||
|
```
|
||||||
├── package 项目包根目录
|
├── package 项目包根目录
|
||||||
├── test_gender 生成测试代码的脚本
|
├── test_gender 生成测试代码的脚本
|
||||||
├── test.py 手动测试/ 使用示例
|
├── test.py 手动测试/ 使用示例
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
项目使用towncrier自动生成更新日志
|
||||||
|
|
||||||
|
在 newsfragments 目录下,创建一个新的文本文件。这个文件的名字应该是一个唯一的编号,后缀是 .rst。
|
||||||
|
例如,如果你正在处理编号为 123 的问题,你可以创建一个名为 123.feature.rst 的文件。
|
||||||
|
|
||||||
|
注意在 towncrier 中,新闻片段的类型通常由文件名的后缀决定。以下是一些常见的新闻片段类型:
|
||||||
|
- .feature: 用于描述新的特性或者功能。
|
||||||
|
- .bugfix: 用于描述一个 bug 修复。
|
||||||
|
- .doc: 用于描述文档的更改。
|
||||||
|
- .removal: 用于描述移除的特性或者功能。
|
||||||
|
- .misc: 用于描述其他类型的更改。
|
||||||
|
-
|
||||||
|
在这个文件中,写下你的更改的描述。这个描述应该是简短的,通常只有一到两句话。
|
||||||
|
例如`Added support for the XYZ feature.`
|
||||||
|
|
||||||
|
### 发布新版本
|
||||||
|
使用bumpversion自动更新版本号,提交并发布标签
|
||||||
|
你需要安装bumpversion然后执行
|
||||||
|
```bash
|
||||||
|
bumpversion patch # for a patch level increase (e.g., 1.0.0 to 1.0.1)
|
||||||
|
bumpversion minor # for a minor level increase (e.g., 1.0.0 to 1.1.0)
|
||||||
|
bumpversion major # for a major level increase (e.g., 1.0.0 to 2.0.0)
|
||||||
|
```
|
||||||
|
|
||||||
|
接下来会自动更新版本号并提交到远程仓库,然后发布一个新的标签
|
||||||
|
workflow会依据标签自动发布相应资源并且发布到pypi
|
||||||
|
|
||||||
### 改进
|
### 改进
|
||||||
> 下列API未支持
|
> 下列API未支持
|
||||||
@ -107,3 +141,8 @@ print(api_list())
|
|||||||
- /fm_trash
|
- /fm_trash
|
||||||
|
|
||||||
### 欢迎提交PR
|
### 欢迎提交PR
|
||||||
|
|
||||||
|
更正request库返回多个cookie时自动用分号拼接导致后续cookie不好解析的问题
|
||||||
|
添加了对cookie中Max-Age为0的处理
|
||||||
|
改进判断cookie是否过期的逻辑
|
||||||
|
添加了自动处理cookie的更新操作
|
||||||
|
@ -7,6 +7,7 @@ NeteaseCloudMusic/__init__.py
|
|||||||
NeteaseCloudMusic/config.json
|
NeteaseCloudMusic/config.json
|
||||||
NeteaseCloudMusic/help.py
|
NeteaseCloudMusic/help.py
|
||||||
NeteaseCloudMusic/main.py
|
NeteaseCloudMusic/main.py
|
||||||
|
NeteaseCloudMusic/utils.py
|
||||||
NeteaseCloudMusic.egg-info/PKG-INFO
|
NeteaseCloudMusic.egg-info/PKG-INFO
|
||||||
NeteaseCloudMusic.egg-info/SOURCES.txt
|
NeteaseCloudMusic.egg-info/SOURCES.txt
|
||||||
NeteaseCloudMusic.egg-info/dependency_links.txt
|
NeteaseCloudMusic.egg-info/dependency_links.txt
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
py_mini_racer
|
py_mini_racer
|
||||||
requests
|
requests
|
||||||
|
diskcache
|
||||||
|
@ -2,4 +2,4 @@ from .main import NeteaseCloudMusicApi
|
|||||||
from .help import api_help, api_list
|
from .help import api_help, api_list
|
||||||
from .utils import format_cookie_str, prase_cookie_str
|
from .utils import format_cookie_str, prase_cookie_str
|
||||||
|
|
||||||
__version__ = '0.1.8'
|
__version__ = '0.1.7'
|
||||||
|
@ -35,15 +35,48 @@ print(api_list())
|
|||||||
|
|
||||||
> 注意: request(self, name, query=None) 的第一个参数为API名称,第二个参数为API参数,具体API名称和参数请参考 [NeteaseCloudMusicApi文档](https://docs.neteasecloudmusicapi.binaryify.com),name支持`/song/url/v1`和`song_url_v1`两种写法。
|
> 注意: request(self, name, query=None) 的第一个参数为API名称,第二个参数为API参数,具体API名称和参数请参考 [NeteaseCloudMusicApi文档](https://docs.neteasecloudmusicapi.binaryify.com),name支持`/song/url/v1`和`song_url_v1`两种写法。
|
||||||
|
|
||||||
|
> api已加入自动缓存,在测试接口时,如果频繁调用获取结果在query里应该加上timestamp参数来区分,例如
|
||||||
|
> ```python
|
||||||
|
> response = netease_cloud_music_api.request("song_url_v1", {"id": 33894312, "level": "exhigh", "timestamp": time.time()})
|
||||||
|
> ```
|
||||||
|
|
||||||
### 开发
|
### 开发
|
||||||
- 克隆项目 `git clone git@github.com:2061360308/NeteaseCloudMusic_PythonSDK.git`
|
- 克隆项目 `git clone git@github.com:2061360308/NeteaseCloudMusic_PythonSDK.git`
|
||||||
- 安装依赖 `pip install -r requirements.txt`
|
- 安装依赖 `pip install -r requirements.txt`
|
||||||
- 目录/文件说明
|
- 目录/文件说明
|
||||||
|
```
|
||||||
├── package 项目包根目录
|
├── package 项目包根目录
|
||||||
├── test_gender 生成测试代码的脚本
|
├── test_gender 生成测试代码的脚本
|
||||||
├── test.py 手动测试/ 使用示例
|
├── test.py 手动测试/ 使用示例
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
项目使用towncrier自动生成更新日志
|
||||||
|
|
||||||
|
在 newsfragments 目录下,创建一个新的文本文件。这个文件的名字应该是一个唯一的编号,后缀是 .rst。
|
||||||
|
例如,如果你正在处理编号为 123 的问题,你可以创建一个名为 123.feature.rst 的文件。
|
||||||
|
|
||||||
|
注意在 towncrier 中,新闻片段的类型通常由文件名的后缀决定。以下是一些常见的新闻片段类型:
|
||||||
|
- .feature: 用于描述新的特性或者功能。
|
||||||
|
- .bugfix: 用于描述一个 bug 修复。
|
||||||
|
- .doc: 用于描述文档的更改。
|
||||||
|
- .removal: 用于描述移除的特性或者功能。
|
||||||
|
- .misc: 用于描述其他类型的更改。
|
||||||
|
-
|
||||||
|
在这个文件中,写下你的更改的描述。这个描述应该是简短的,通常只有一到两句话。
|
||||||
|
例如`Added support for the XYZ feature.`
|
||||||
|
|
||||||
|
### 发布新版本
|
||||||
|
使用bumpversion自动更新版本号,提交并发布标签
|
||||||
|
你需要安装bumpversion然后执行
|
||||||
|
```bash
|
||||||
|
bumpversion patch # for a patch level increase (e.g., 1.0.0 to 1.0.1)
|
||||||
|
bumpversion minor # for a minor level increase (e.g., 1.0.0 to 1.1.0)
|
||||||
|
bumpversion major # for a major level increase (e.g., 1.0.0 to 2.0.0)
|
||||||
|
```
|
||||||
|
|
||||||
|
接下来会自动更新版本号并提交到远程仓库,然后发布一个新的标签
|
||||||
|
workflow会依据标签自动发布相应资源并且发布到pypi
|
||||||
|
|
||||||
### 改进
|
### 改进
|
||||||
> 下列API未支持
|
> 下列API未支持
|
||||||
@ -88,3 +121,8 @@ print(api_list())
|
|||||||
- /fm_trash
|
- /fm_trash
|
||||||
|
|
||||||
### 欢迎提交PR
|
### 欢迎提交PR
|
||||||
|
|
||||||
|
更正request库返回多个cookie时自动用分号拼接导致后续cookie不好解析的问题
|
||||||
|
添加了对cookie中Max-Age为0的处理
|
||||||
|
改进判断cookie是否过期的逻辑
|
||||||
|
添加了自动处理cookie的更新操作
|
||||||
|
@ -44,6 +44,7 @@ if (typeof window === "undefined") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function encodeURIComponent(str) {
|
function encodeURIComponent(str) {
|
||||||
|
str = String(str); // 将输入转换为字符串 以免True,False等非字符无法转换
|
||||||
var result = "";
|
var result = "";
|
||||||
|
|
||||||
if (str !== undefined && str !== null) {
|
if (str !== undefined && str !== null) {
|
||||||
@ -37928,7 +37929,7 @@ const hug_comment_resourceTypeMap = config_namespaceObject.A;
|
|||||||
});
|
});
|
||||||
|
|
||||||
;// CONCATENATED MODULE: ./package.json
|
;// CONCATENATED MODULE: ./package.json
|
||||||
const package_namespaceObject = {"i8":"0.1.1"};
|
const package_namespaceObject = {"i8":"0.1.2"};
|
||||||
;// CONCATENATED MODULE: ./module/inner_version.js
|
;// CONCATENATED MODULE: ./module/inner_version.js
|
||||||
|
|
||||||
/* harmony default export */ const inner_version = ((query, request) => {
|
/* harmony default export */ const inner_version = ((query, request) => {
|
||||||
@ -38338,30 +38339,19 @@ var crypto_js = __webpack_require__(1292);
|
|||||||
;// CONCATENATED MODULE: ./module/login_refresh.js
|
;// CONCATENATED MODULE: ./module/login_refresh.js
|
||||||
// 登录刷新
|
// 登录刷新
|
||||||
|
|
||||||
/* harmony default export */ const login_refresh = (async (query, request) => {
|
/* harmony default export */ const login_refresh = ((query, request) => {
|
||||||
let result = await request(
|
return request(
|
||||||
'POST',
|
"POST",
|
||||||
`https://music.163.com/weapi/login/token/refresh`,
|
`https://music.163.com/weapi/login/token/refresh`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
crypto: 'weapi',
|
crypto: "weapi",
|
||||||
ua: 'pc',
|
ua: "pc",
|
||||||
cookie: query.cookie,
|
cookie: query.cookie,
|
||||||
proxy: query.proxy,
|
proxy: query.proxy,
|
||||||
realIP: query.realIP,
|
realIP: query.realIP,
|
||||||
},
|
|
||||||
)
|
|
||||||
if (result.body.code === 200) {
|
|
||||||
result = {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
...result.body,
|
|
||||||
cookie: result.cookie.join(';'),
|
|
||||||
},
|
|
||||||
cookie: result.cookie,
|
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
return result
|
|
||||||
});
|
});
|
||||||
|
|
||||||
;// CONCATENATED MODULE: ./module/login_status.js
|
;// CONCATENATED MODULE: ./module/login_status.js
|
||||||
@ -42852,6 +42842,7 @@ const weapi = (object) => {
|
|||||||
for (let i = 0; i < 16; i++) {
|
for (let i = 0; i < 16; i++) {
|
||||||
secretKey += base62.charAt(Math.round(Math.random() * 61))
|
secretKey += base62.charAt(Math.round(Math.random() * 61))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
params: aesEncrypt(
|
params: aesEncrypt(
|
||||||
aesEncrypt(text, 'cbc', presetKey, iv),
|
aesEncrypt(text, 'cbc', presetKey, iv),
|
||||||
@ -43181,6 +43172,27 @@ function beforeRequest(name, query) {
|
|||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
;// CONCATENATED MODULE: ./afterRequest/login_refresh.js
|
||||||
|
/* harmony default export */ const afterRequest_login_refresh = ((response) => {
|
||||||
|
response = JSON.parse(response);
|
||||||
|
let cookie = response.cookie;
|
||||||
|
if (Array.isArray(cookie)) {
|
||||||
|
cookie = cookie.join(";");
|
||||||
|
}
|
||||||
|
if (response.body.code === 200) {
|
||||||
|
response = {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
...response.body,
|
||||||
|
cookie: cookie,
|
||||||
|
},
|
||||||
|
cookie: cookie,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
;// CONCATENATED MODULE: ./afterRequest/login_status.js
|
;// CONCATENATED MODULE: ./afterRequest/login_status.js
|
||||||
/* harmony default export */ const afterRequest_login_status = ((response) => {
|
/* harmony default export */ const afterRequest_login_status = ((response) => {
|
||||||
response = JSON.parse(response);
|
response = JSON.parse(response);
|
||||||
@ -43255,9 +43267,11 @@ function beforeRequest(name, query) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* harmony default export */ const afterRequestApi = ({
|
/* harmony default export */ const afterRequestApi = ({
|
||||||
'check_music':afterRequest_check_music,
|
'check_music':afterRequest_check_music,
|
||||||
'login_cellphone':afterRequest_login_cellphone,
|
'login_cellphone':afterRequest_login_cellphone,
|
||||||
|
'login_refresh':afterRequest_login_refresh,
|
||||||
'login_status':afterRequest_login_status,
|
'login_status':afterRequest_login_status,
|
||||||
'related_playlist':afterRequest_related_playlist,
|
'related_playlist':afterRequest_related_playlist,
|
||||||
'top_playlist':afterRequest_top_playlist,
|
'top_playlist':afterRequest_top_playlist,
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
from .main import NeteaseCloudMusicApi
|
from .main import NeteaseCloudMusicApi
|
||||||
from .help import api_help, api_list
|
from .help import api_help, api_list
|
||||||
|
from .utils import format_cookie_str, prase_cookie_str
|
||||||
|
|
||||||
|
__version__ = '0.1.7'
|
||||||
|
@ -1,26 +1,36 @@
|
|||||||
import json
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
import socket
|
import socket
|
||||||
|
import time
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import http.cookies
|
import http.cookies
|
||||||
import datetime
|
import datetime
|
||||||
|
from diskcache import Cache
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import requests
|
import requests
|
||||||
from py_mini_racer import py_mini_racer
|
from py_mini_racer import py_mini_racer
|
||||||
from .help import api_list
|
from .help import api_list
|
||||||
|
from .utils import format_cookie_str, prase_cookie_str
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
|
||||||
class NeteaseCloudMusicApi:
|
class NeteaseCloudMusicApi:
|
||||||
__cookie = None
|
__cookie = None
|
||||||
__ip = None
|
__ip = None
|
||||||
|
|
||||||
def __init__(self, debug=False):
|
cache = Cache('cache', timeout=120) # 设置缓存目录和过期时间
|
||||||
|
|
||||||
|
# cache = TTLCache(maxsize=100, ttl=120) # 设置缓存大小为100,缓存项的生存时间为120秒
|
||||||
|
|
||||||
|
def __init__(self, debug=False, cache=False):
|
||||||
self.DEBUG = debug # 是否开启调试模式
|
self.DEBUG = debug # 是否开启调试模式
|
||||||
|
self.CACHE = cache # 是否开启缓存
|
||||||
|
|
||||||
self.special_api = {"/playlist/track/all": self.playlist_track_all,
|
self.special_api = {"/playlist/track/all": self.playlist_track_all,
|
||||||
"/login/cellphone": self.login_cellphone,
|
"/login/cellphone": self.login_cellphone,
|
||||||
"/inner/version": self.inner_version}
|
"/inner/version": self.inner_version,
|
||||||
|
"/login/refresh": self.login_refresh}
|
||||||
|
|
||||||
# 载入js代码
|
# 载入js代码
|
||||||
resource_path = pkg_resources.resource_filename(__name__, 'NeteaseCloudMusicApi.js')
|
resource_path = pkg_resources.resource_filename(__name__, 'NeteaseCloudMusicApi.js')
|
||||||
@ -66,8 +76,21 @@ class NeteaseCloudMusicApi:
|
|||||||
if name not in yubei_special.values():
|
if name not in yubei_special.values():
|
||||||
raise Exception(f"apiName: {name} not found,please use ”api_list()“ to view the interface list")
|
raise Exception(f"apiName: {name} not found,please use ”api_list()“ to view the interface list")
|
||||||
|
|
||||||
|
# 生成一个唯一的键,用于在缓存中查找结果
|
||||||
|
cache_key = (name, frozenset(query.items()) if query else None)
|
||||||
|
|
||||||
|
if self.CACHE:
|
||||||
|
# 检查缓存中是否已经有了结果
|
||||||
|
if self.cache.get(cache_key):
|
||||||
|
return self.cache.get(cache_key)
|
||||||
|
|
||||||
if query is None:
|
if query is None:
|
||||||
query = {}
|
query = {}
|
||||||
|
else:
|
||||||
|
# 如果存在timestamp参数,那么删除它
|
||||||
|
if query.get("timestamp"):
|
||||||
|
del query["timestamp"]
|
||||||
|
|
||||||
if query.get("cookie") is None:
|
if query.get("cookie") is None:
|
||||||
query["cookie"] = self.cookie
|
query["cookie"] = self.cookie
|
||||||
|
|
||||||
@ -82,7 +105,9 @@ class NeteaseCloudMusicApi:
|
|||||||
else:
|
else:
|
||||||
result = self.call_api(name, query)
|
result = self.call_api(name, query)
|
||||||
|
|
||||||
#
|
if self.CACHE:
|
||||||
|
# 将结果存入缓存
|
||||||
|
self.cache.set(cache_key, result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -105,46 +130,47 @@ class NeteaseCloudMusicApi:
|
|||||||
if self.__cookie is None:
|
if self.__cookie is None:
|
||||||
if os.path.isfile("cookie_storage"):
|
if os.path.isfile("cookie_storage"):
|
||||||
with open("cookie_storage", "r", encoding='utf-8') as f:
|
with open("cookie_storage", "r", encoding='utf-8') as f:
|
||||||
self.cookie = f.read()
|
content = f.read()
|
||||||
|
try:
|
||||||
|
cookie_storage = json.loads(content)
|
||||||
|
# 验证cookie是否过期
|
||||||
|
create_time_stamp = cookie_storage['create_time_stamp']
|
||||||
|
|
||||||
|
if time.time() - create_time_stamp > 1296010:
|
||||||
|
# cookie过期了
|
||||||
|
self.__cookie = ""
|
||||||
|
else:
|
||||||
|
# 判断cookie生成时间是否超过1天
|
||||||
|
if time.time() - create_time_stamp > 86400:
|
||||||
|
# 更新cookie
|
||||||
|
# Todo login_refresh接口返回cookie好像少一些值
|
||||||
|
# self.request("/login/refresh", {"cookie": cookie_storage['cookie'], "timestamp": time.time()})
|
||||||
|
self.cookie = cookie_storage['cookie']
|
||||||
|
else:
|
||||||
|
self.__cookie = cookie_storage['cookie']
|
||||||
|
except json.JSONDecodeError and KeyError:
|
||||||
|
self.__cookie = ""
|
||||||
else:
|
else:
|
||||||
self.__cookie = "" # 如果没有cookie文件,就设置为空
|
self.__cookie = "" # 如果没有cookie文件,就设置为空
|
||||||
|
|
||||||
return self.__cookie
|
return self.__cookie
|
||||||
|
|
||||||
@cookie.setter
|
@cookie.setter
|
||||||
def cookie(self, cookie):
|
def cookie(self, value):
|
||||||
if cookie is None:
|
if value is None:
|
||||||
cookie = ""
|
self.__cookie = ""
|
||||||
|
return
|
||||||
|
|
||||||
_cookie = cookie
|
"判断cookie是否合法, 简单检查一下关键的键"
|
||||||
|
necessary_keys = ["__csrf", "MUSIC_A_T", "MUSIC_R_T"]
|
||||||
|
cookie_dict = prase_cookie_str(value)
|
||||||
|
for key in necessary_keys:
|
||||||
|
if cookie_dict.get(key) is None:
|
||||||
|
raise Exception(f"cookie is illegal, missing key: {key}.")
|
||||||
|
|
||||||
'''判断cookie是否过期'''
|
self.__cookie = value
|
||||||
|
|
||||||
# 创建一个Morsel对象,它可以解析cookie字符串
|
|
||||||
morsel = http.cookies.SimpleCookie(cookie)
|
|
||||||
# 获取当前时间
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
|
|
||||||
# 只判断 __csrf 是否过期
|
|
||||||
if not morsel.get('__csrf'):
|
|
||||||
# __csrf 不存在,不是有效cookie
|
|
||||||
_cookie = ""
|
|
||||||
else:
|
|
||||||
# 将过期时间字符串转换为datetime对象
|
|
||||||
expires = morsel.get('__csrf')['expires']
|
|
||||||
expires_datetime = datetime.datetime.strptime(expires, "%a, %d %b %Y %H:%M:%S GMT")
|
|
||||||
|
|
||||||
# 判断cookie是否过期
|
|
||||||
if now > expires_datetime:
|
|
||||||
# 过期了
|
|
||||||
_cookie = ""
|
|
||||||
else:
|
|
||||||
# 未过期
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.__cookie = _cookie
|
|
||||||
with open("cookie_storage", "w+", encoding='utf-8') as f:
|
with open("cookie_storage", "w+", encoding='utf-8') as f:
|
||||||
f.write(_cookie)
|
f.write(json.dumps({"cookie": value, "create_time_stamp": time.time()}, indent=2, ensure_ascii=False))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ip(self):
|
def ip(self):
|
||||||
@ -155,18 +181,17 @@ class NeteaseCloudMusicApi:
|
|||||||
def call_api(self, name, query):
|
def call_api(self, name, query):
|
||||||
request_param = self.ctx.call('NeteaseCloudMusicApi.beforeRequest', name, query) # 拿到请求头和请求参数
|
request_param = self.ctx.call('NeteaseCloudMusicApi.beforeRequest', name, query) # 拿到请求头和请求参数
|
||||||
|
|
||||||
|
# Todo 了解 py_mini_racer 返回没有自动编码 而 node可以
|
||||||
param_data = {}
|
param_data = {}
|
||||||
if request_param["data"] != "":
|
if request_param["data"] != "":
|
||||||
for item in request_param["data"].split("&"):
|
for item in request_param["data"].split("&"):
|
||||||
|
# param_data[item.split("=")[0]] = urllib.parse.quote(item.split("=")[1], safe='') # 不需要编码后反而出错
|
||||||
param_data[item.split("=")[0]] = item.split("=")[1]
|
param_data[item.split("=")[0]] = item.split("=")[1]
|
||||||
|
|
||||||
# print("url", request_param["url"], "data", param_data, "headers\n", json.dumps(request_param["headers"], indent=2, ensure_ascii=False))
|
|
||||||
|
|
||||||
if request_param.get("method") == "GET":
|
if request_param.get("method") == "GET":
|
||||||
response = requests.get(request_param["url"], params=param_data, headers=request_param["headers"])
|
response = requests.get(request_param["url"], params=param_data, headers=request_param["headers"])
|
||||||
else:
|
else:
|
||||||
response = requests.post(request_param["url"], data=param_data, headers=request_param["headers"])
|
response = requests.post(request_param["url"], data=param_data, headers=request_param["headers"])
|
||||||
# response = requests.post(request_param["url"], data=param_data, headers=request_param["headers"])
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(response.text)
|
data = json.loads(response.text)
|
||||||
@ -179,12 +204,6 @@ class NeteaseCloudMusicApi:
|
|||||||
"status": response.status_code,
|
"status": response.status_code,
|
||||||
}
|
}
|
||||||
|
|
||||||
# print("headers", response.headers)
|
|
||||||
# print("headers_dict", dict(response.headers))
|
|
||||||
|
|
||||||
# with open("response_result.json", "w+", encoding='utf-8') as f:
|
|
||||||
# f.write(json.dumps(response_result, indent=2, ensure_ascii=False))
|
|
||||||
|
|
||||||
result = self.ctx.call('NeteaseCloudMusicApi.afterRequest',
|
result = self.ctx.call('NeteaseCloudMusicApi.afterRequest',
|
||||||
json.dumps(response_result),
|
json.dumps(response_result),
|
||||||
request_param.get('crypto', None),
|
request_param.get('crypto', None),
|
||||||
@ -230,8 +249,29 @@ class NeteaseCloudMusicApi:
|
|||||||
"""
|
"""
|
||||||
result = self.call_api("/login/cellphone", query)
|
result = self.call_api("/login/cellphone", query)
|
||||||
|
|
||||||
# 自动 填充cookie
|
# 自动 更新cookie
|
||||||
if result["code"] == 200:
|
if result["code"] == 200:
|
||||||
if result.get("data").get("cookie"):
|
if result.get("data").get("cookie"):
|
||||||
self.cookie = result.get("data").get("cookie")
|
# cookie_str = format_cookie_str(result.get("data").get("cookie"))
|
||||||
|
cookie_str = result.get("data").get("cookie")
|
||||||
|
result["data"]["cookie"] = cookie_str
|
||||||
|
self.cookie = cookie_str
|
||||||
|
return result
|
||||||
|
|
||||||
|
def login_refresh(self, query):
|
||||||
|
"""
|
||||||
|
刷新登录状态
|
||||||
|
:param query:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
result = self.call_api("/login/refresh", query)
|
||||||
|
|
||||||
|
# 自动 更新cookie
|
||||||
|
# if result["code"] == 200:
|
||||||
|
# if result.get("data").get("cookie"):
|
||||||
|
# # cookie_str = format_cookie_str(result.get("data").get("cookie"))
|
||||||
|
# cookie_str = result.get("data").get("cookie")
|
||||||
|
# result["data"]["cookie"] = cookie_str
|
||||||
|
# pprint(cookie_str)
|
||||||
|
# self.cookie = cookie_str
|
||||||
return result
|
return result
|
||||||
|
124
package/build/lib/NeteaseCloudMusic/utils.py
Normal file
124
package/build/lib/NeteaseCloudMusic/utils.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
def format_cookie_str(cookie_str: str) -> str:
|
||||||
|
"""
|
||||||
|
格式化cookie字符串
|
||||||
|
功能
|
||||||
|
处理以逗号分割的cookie字符串
|
||||||
|
删除max-age为0的cookie
|
||||||
|
注意:
|
||||||
|
传入cookie字符串必须是以 逗号+空格 或 分号+空格 分割的规范cookie字符串
|
||||||
|
:param cookie_str:
|
||||||
|
:return: 格式化后的cookie str
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not cookie_str:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# 解析cookie字符串
|
||||||
|
cookie_dict = prase_cookie_str(cookie_str)
|
||||||
|
|
||||||
|
cookie_valid_dict = {}
|
||||||
|
# 删除max-age为0的cookie
|
||||||
|
for item in cookie_dict.keys():
|
||||||
|
max_age = cookie_dict[item].get("Max-Age")
|
||||||
|
if max_age:
|
||||||
|
max_age = int(max_age)
|
||||||
|
if max_age != 0:
|
||||||
|
cookie_valid_dict[item] = cookie_dict[item]
|
||||||
|
|
||||||
|
# 重新拼接cookie字符串
|
||||||
|
cookie = ""
|
||||||
|
for item in cookie_valid_dict.keys():
|
||||||
|
cookie += item + "=" + cookie_valid_dict[item]["value"] + "; "
|
||||||
|
for key in cookie_valid_dict[item].keys():
|
||||||
|
if key != "value":
|
||||||
|
cookie += key + "=" + cookie_valid_dict[item][key] + "; "
|
||||||
|
|
||||||
|
return cookie[: -2]
|
||||||
|
|
||||||
|
|
||||||
|
def prase_cookie_str(cookie_str: str) -> dict:
|
||||||
|
"""
|
||||||
|
解析cookie字符串
|
||||||
|
功能
|
||||||
|
处理以逗号分割的cookie字符串
|
||||||
|
注意:
|
||||||
|
传入cookie字符串必须是以 逗号+空格 或 分号+空格 分割的规范cookie字符串
|
||||||
|
:param cookie_str:
|
||||||
|
:return: cookie dict
|
||||||
|
"""
|
||||||
|
if not cookie_str:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# 解决用逗号分割的问题
|
||||||
|
cookie_part = []
|
||||||
|
for item in cookie_str.split(", "):
|
||||||
|
cookie_part.extend(item.split("; "))
|
||||||
|
|
||||||
|
cookies = []
|
||||||
|
for item in cookie_part:
|
||||||
|
if "=" not in item:
|
||||||
|
# 属于上一个cookie的一部分
|
||||||
|
cookies[-1] = cookies[-1] + item
|
||||||
|
else:
|
||||||
|
cookies.append(item)
|
||||||
|
|
||||||
|
cookie_dict = {}
|
||||||
|
|
||||||
|
current_name = ""
|
||||||
|
for cookie in cookies:
|
||||||
|
# print(cookie)
|
||||||
|
if cookie[-1] == "=":
|
||||||
|
name = cookie[:-1]
|
||||||
|
value = ""
|
||||||
|
else:
|
||||||
|
part = cookie.split("=")
|
||||||
|
name = part[0]
|
||||||
|
value = part[1]
|
||||||
|
|
||||||
|
if name in ["Max-Age", "Expires", "Path", "Domain"]:
|
||||||
|
cookie_dict[current_name][name] = value
|
||||||
|
else:
|
||||||
|
cookie_dict[name] = {"value": value}
|
||||||
|
current_name = name
|
||||||
|
|
||||||
|
return cookie_dict
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cookie = ("MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/openapi/clientlog; Domain=.music.163.com, MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, "
|
||||||
|
"02 Jan 2092 11:39:38 GMT; Path=/wapi/feedback; Domain=.music.163.com, __remember_me=true; "
|
||||||
|
"Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/; Domain=.music.163.com, "
|
||||||
|
"MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/api/feedback; "
|
||||||
|
"Domain=.music.163.com, MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/eapi/clientlog; Domain=.music.163.com, MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, "
|
||||||
|
"02 Jan 2092 11:39:38 GMT; Path=/api/clientlog; Domain=.music.163.com, MUSIC_R_T=1579622000495; "
|
||||||
|
"Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/wapi/feedback; Domain=.music.163.com, "
|
||||||
|
"MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/weapi/clientlog; "
|
||||||
|
"Domain=.music.163.com, MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/neapi/feedback; Domain=.music.163.com, MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, "
|
||||||
|
"02 Jan 2092 11:39:38 GMT; Path=/eapi/clientlog; Domain=.music.163.com, MUSIC_R_T=1579622000495; "
|
||||||
|
"Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/api/feedback; Domain=.music.163.com, "
|
||||||
|
"MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/weapi/feedback; "
|
||||||
|
"Domain=.music.163.com, MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/eapi/feedback; Domain=.music.163.com, MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, "
|
||||||
|
"02 Jan 2092 11:39:38 GMT; Path=/neapi/clientlog; Domain=.music.163.com, MUSIC_R_T=1579622000495; "
|
||||||
|
"Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/wapi/clientlog; Domain=.music.163.com, "
|
||||||
|
"MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/wapi/clientlog; "
|
||||||
|
"Domain=.music.163.com, MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/weapi/feedback; Domain=.music.163.com, NMTID=00OLqRn-wykrFFXdElLpZA3_-aaQtIAAAGMbJSp9A; "
|
||||||
|
"Max-Age=315360000; Expires=Mon, 12 Dec 2033 08:25:31 GMT; Path=/; Domain=music.163.com, "
|
||||||
|
"__csrf=4d5e515015723fd3d7edaa5b05f35b4d; Max-Age=1296010; Expires=Sat, 30 Dec 2023 08:25:41 GMT; Path=/; "
|
||||||
|
"Domain=.music.163.com, MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/neapi/clientlog; Domain=.music.163.com, MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, "
|
||||||
|
"02 Jan 2092 11:39:38 GMT; Path=/openapi/clientlog; Domain=.music.163.com, MUSIC_A_T=1579621885297; "
|
||||||
|
"Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/eapi/feedback; Domain=.music.163.com, "
|
||||||
|
"MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/weapi/clientlog; "
|
||||||
|
"Domain=.music.163.com, MUSIC_R_T=1579622000495; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; "
|
||||||
|
"Path=/api/clientlog; Domain=.music.163.com, MUSIC_SNS=; Max-Age=0; Expires=Fri, 15 Dec 2023 08:25:31 GMT; "
|
||||||
|
"Path=/, "
|
||||||
|
"MUSIC_U=00CCEEF2F7A9825637C9869A455B5CA8E009D905415134A7EDAC2E404A940B2D58BFF7129A413193E9A8FD6D1D497E1E8E9023611BA91D2CB28FA94B53DBE4C42699A30FF1448C8EFAF5B00E73ADD353C52F2E004C094AEAC4BA4D83C6FFBB4CD532DEEAEA8D5584E69AC78110BBA682829C263DA72F5AA290AD826A47F640CA3903AC652E4DDE946ACC4EB8A63E5853FA695B480A919CF84498B7084C5CA9A91297F2CF5AF45C2D545AC0D5C03A1641306BAC0FB6FFD57BC653A91F2483FB52BFE85DE39280B012BC036DF244883D9700480E5FDDCD5A417C60241AE08CD6AA84BFC1C8890AE4A08286144D9D1146E33C67CF7797CDB5A961C85DB5AC492B232601D80D4DB07E4F170F635ABC01E080B4D32408FA0698DAD95AD80CDB99F2F672638048F5116214319175B596BF68B2A7A0269746EABFD919D62C165353725E6B1253A7C5D6774D453DDDCC28524D3378; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/; Domain=.music.163.com, MUSIC_A_T=1579621885297; Max-Age=2147483647; Expires=Wed, 02 Jan 2092 11:39:38 GMT; Path=/neapi/feedback; Domain=.music.163.com")
|
||||||
|
pprint(prase_cookie_str(cookie))
|
Binary file not shown.
BIN
package/dist/NeteaseCloudMusic-0.1.7.tar.gz
vendored
BIN
package/dist/NeteaseCloudMusic-0.1.7.tar.gz
vendored
Binary file not shown.
@ -19,7 +19,7 @@ URL = 'https://github.com/2061360308/NeteaseCloudMusic_PythonSDK'
|
|||||||
EMAIL = '2061360308@qq.com'
|
EMAIL = '2061360308@qq.com'
|
||||||
AUTHOR = '盧瞳'
|
AUTHOR = '盧瞳'
|
||||||
REQUIRES_PYTHON = '>=3.6.0'
|
REQUIRES_PYTHON = '>=3.6.0'
|
||||||
VERSION = '0.1.8'
|
VERSION = '0.1.7'
|
||||||
UPDATA_INFO = ('修复了初次使用时没有cookie导致的一系列问题\n'
|
UPDATA_INFO = ('修复了初次使用时没有cookie导致的一系列问题\n'
|
||||||
'修复了NeteaseCloudMusicApi.js没有更新的问题\n'
|
'修复了NeteaseCloudMusicApi.js没有更新的问题\n'
|
||||||
'添加了对于cookie的判断,现在可以正常判断cookie是否过期了')
|
'添加了对于cookie的判断,现在可以正常判断cookie是否过期了')
|
||||||
@ -114,13 +114,6 @@ class UploadCommand(Command):
|
|||||||
self.status('Building Source and Wheel (universal) distribution…')
|
self.status('Building Source and Wheel (universal) distribution…')
|
||||||
os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))
|
os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))
|
||||||
|
|
||||||
self.status('Uploading the package to PyPI via Twine…')
|
|
||||||
os.system('twine upload dist/*')
|
|
||||||
|
|
||||||
self.status('Pushing git tags…')
|
|
||||||
os.system(f'git tag -a {about["__version__"]} -m {UPDATA_INFO}')
|
|
||||||
os.system('git push --tags')
|
|
||||||
|
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
@ -193,6 +186,5 @@ setup(
|
|||||||
# $ setup.py publish support.
|
# $ setup.py publish support.
|
||||||
cmdclass={
|
cmdclass={
|
||||||
'upload': UploadCommand,
|
'upload': UploadCommand,
|
||||||
'build': BuildCommand,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user