From 4f611b2d501419c3d67356cbf67b0b3316bf5424 Mon Sep 17 00:00:00 2001 From: aniss Date: Sat, 12 Oct 2024 15:15:12 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=9C=A8=20.gitignore=20=E4=B8=AD,=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20.vscode/*?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 51bd8ef..28e839d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ share/python-wheels/ *.egg MANIFEST +.vscode/* + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. From 56656079e840298939f10f525fe8455e9e732d40 Mon Sep 17 00:00:00 2001 From: aniss Date: Sat, 12 Oct 2024 20:03:32 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(search):=20=E6=B7=BB=E5=8A=A0=E7=BD=91?= =?UTF-8?q?=E6=98=93=E4=BA=91=E9=9F=B3=E4=B9=90=E6=90=9C=E7=B4=A2=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/cover.py | 3 +- mod/searchx/__init__.py | 4 +- mod/searchx/netease.py | 145 ++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 +- 4 files changed, 151 insertions(+), 4 deletions(-) diff --git a/api/cover.py b/api/cover.py index 860aff0..e53eb80 100644 --- a/api/cover.py +++ b/api/cover.py @@ -8,6 +8,7 @@ from mod import searchx +headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"} # 跟踪重定向 def follow_redirects(url, max_redirects=10): @@ -27,7 +28,7 @@ def local_cover_search(title: str, artist: str, album: str): for item in result: if cover_url := item.get('cover'): res = requests.get(cover_url, - headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"}) + headers=headers) if res.status_code == 200: return res.content, 200, {"Content-Type": res.headers['Content-Type']} diff --git a/mod/searchx/__init__.py b/mod/searchx/__init__.py index a4933ea..5fc826f 100644 --- a/mod/searchx/__init__.py +++ b/mod/searchx/__init__.py @@ -1,10 +1,10 @@ from concurrent import futures -from mod.searchx import api, kugou +from mod.searchx import api, kugou, netease def search_all(title, artist, album, timeout=30): - funcs = [api, kugou] + funcs = [api, kugou, netease] results = [] def request(task): diff --git a/mod/searchx/netease.py b/mod/searchx/netease.py index e69de29..ae213fb 100644 --- a/mod/searchx/netease.py +++ b/mod/searchx/netease.py @@ -0,0 +1,145 @@ +import json +import aiohttp +import asyncio +import logging + +from functools import lru_cache +from Levenshtein import distance +from mod import tools +from mygo.devtools import no_error + +headers = { + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0', + 'origin': 'https://music.163.com', + 'referer': 'https://music.163.com', +} + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +# type: 1-songs, 10-albums, 100-artists, 1000-playlists +COMMON_SEARCH_URL_WANGYI = 'https://music.163.com/api/search/get/web?csrf_token=hlpretag=&hlposttag=&s={}&type={}&offset={}&total=true&limit={}' +ALBUM_SEARCH_URL_WANGYI = 'https://music.163.com/api/album/{}?ext=true' +LYRIC_URL_WANGYI = 'https://music.163.com/api/song/lyric?id={}&lv=1&tv=1' + + +def levenshtein_similarity(a, b): + return 1 - (distance(a, b) / max(len(a), len(b))) + + +async def get_cover_url(session: aiohttp.ClientSession, album_id: int): + url = ALBUM_SEARCH_URL_WANGYI.format(album_id) + json_data_r = await session.get(url, headers=headers) + json_data = json.loads(await json_data_r.text()) + if json_data.get('album', False) and json_data.get('album').get('picUrl', False): + return json_data['album']['picUrl'] + return None + + +async def get_lyrics(session: aiohttp.ClientSession, track_id: int): + url = LYRIC_URL_WANGYI.format(track_id) + json_data_r = await session.get(url, headers=headers) + json_data = json.loads(await json_data_r.text()) + if json_data.get('lrc', False) and json_data.get('lrc').get('lyric', False): + return json_data['lrc']['lyric'] + return None + + +async def a_search(title='', artist='', album=''): + """ + 网易上有太多的自制音乐, 搜起来一大堆乱七八糟的. + 就是说, 只有歌名的情况下, 搜出来的结果十有八九是错的. + 而有 artist + album + title 组合搜索, 准确性可以提高很多很多 + """ + if not any((title, artist, album)): + return None + result_list = [] + limit = 3 + async with aiohttp.ClientSession() as session: + search_str = ' '.join( + [item for item in [title, artist, album] if item]) + async with session.get(COMMON_SEARCH_URL_WANGYI.format(search_str, 1, 0, 100), headers=headers) as response: + if response.status == 200: + song_info_t: str = await response.text() + song_info: dict = json.loads(song_info_t) + song_info: list[dict] = song_info["result"]["songs"] + if len(song_info) < 1: + return None + candidate_songs = [] + for song_item in song_info: + song_name = song_item["name"] + artists = song_item.get("artists", None) + singer_name = " ".join( + [x['name'] for x in artists]) if artists is not None else "" + album_ = song_item.get("album", None) + album_name = album_[ + 'name'] if album is not None else '' + + title_conform_ratio = levenshtein_similarity( + title, song_name) if len(title) * len(song_name) > 0 else 0.0 + artist_conform_ratio = levenshtein_similarity( + artist, singer_name) if len(artist) * len(singer_name) > 0 else 0.0 + album_conform_ratio = levenshtein_similarity( + album, album_name) if len(album) * len(album_name) > 0 else 0.0 + + ratio: float = (title_conform_ratio + + album_conform_ratio + + artist_conform_ratio) / 3.0 + + if ratio >= 0.5: + song_id = song_item['id'] + album_id = album_[ + 'id'] if album is not None else None + singer_id = [x['id'] for x in artists][0] + candidate_songs.append( + {'ratio': ratio, "item": { + "artist": singer_name, + "album": album_name, + "title": song_name, + "artist_id": singer_id, + "album_id": album_id, + "trace_id": song_id + }}) + + candidate_songs.sort( + key=lambda x: x['ratio'], reverse=True) + if len(candidate_songs) < 1: + return None + + candidate_songs = candidate_songs[:min( + len(candidate_songs), limit)] + + for candidate in candidate_songs: + track = candidate['item'] + ratio = candidate['ratio'] + + cover_url = await get_cover_url(session, track['album_id']) + lyrics = await get_lyrics(session, track['trace_id']) + + # 结构化JSON数据 + music_json_data: dict = { + "title": track['title'], + "album": track['album'], + "artists": track['artist'], + "lyrics": lyrics, + "cover": cover_url, + "hash": tools.calculate_md5( + f"title:{track['title']};artists:{track['artist']};album:{track['album']}") + } + + result_list.append(music_json_data) + else: + return None + return result_list + + +@lru_cache(maxsize=64) +@no_error(throw=logger.info, + exceptions=(aiohttp.ClientError, asyncio.TimeoutError, KeyError, IndexError, AttributeError)) +def search(title='', artist='', album=''): + return asyncio.run(a_search(title=title, artist=artist, album=album)) + + +if __name__ == "__main__": + print(search(album="十年")) diff --git a/requirements.txt b/requirements.txt index 426c105..153ef74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ aiohttp~=3.9.1 Pillow~=10.1.0 pyaes~=1.6.1 Werkzeug~=3.0.1 -mygo~=1.0.0 \ No newline at end of file +mygo~=1.0.0 +Levenshtein==0.26.0 \ No newline at end of file From 2b56a983a5321600adf13d45b0c49d5fb2713cd4 Mon Sep 17 00:00:00 2001 From: aniss Date: Sat, 12 Oct 2024 21:02:12 +0800 Subject: [PATCH 3/6] =?UTF-8?q?cover=20=E6=9F=A5=E8=AF=A2,=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20title=20=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/cover.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/cover.py b/api/cover.py index e53eb80..0632262 100644 --- a/api/cover.py +++ b/api/cover.py @@ -32,12 +32,14 @@ def local_cover_search(title: str, artist: str, album: str): if res.status_code == 200: return res.content, 200, {"Content-Type": res.headers['Content-Type']} - @app.route('/cover', methods=['GET']) @cache.cached(timeout=86400, key_prefix=make_cache_key) @no_error(exceptions=AttributeError) def cover_api(): - title = unquote_plus(request.args.get('title')) + title = request.args.get('title') + if not title: + abort(400, '歌曲名称不能为空') + title = unquote_plus(title) artist = unquote_plus(request.args.get('artist', '')) album = unquote_plus(request.args.get('album', '')) req_args = {key: request.args.get(key) for key in request.args} From d8b434510c2c96869edb0a9b741aed15e3726275 Mon Sep 17 00:00:00 2001 From: aniss Date: Thu, 17 Oct 2024 13:10:52 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E7=BD=91=E6=98=93=E4=BA=91=E6=9F=A5=E8=AF=A2=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=92=8C=E6=AD=8C=E8=AF=8D=E7=9A=84=E9=80=BB=E8=BE=91?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 可根据是否传入艺术家/专辑/歌曲, 查询不同的封面. --- api/cover.py | 7 +- api/lyrics.py | 9 +- mod/searchx/netease.py | 291 +++++++++++++++++++++++++++++------------ 3 files changed, 216 insertions(+), 91 deletions(-) diff --git a/api/cover.py b/api/cover.py index f362a68..1761a10 100644 --- a/api/cover.py +++ b/api/cover.py @@ -8,9 +8,11 @@ from mod import searchx -headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"} +headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"} # 跟踪重定向 + + def follow_redirects(url, max_redirects=10): for _ in range(max_redirects): response = requests.head(url, allow_redirects=False) @@ -27,8 +29,7 @@ def local_cover_search(title: str, artist: str, album: str): result: list = searchx.search_all(title=title, artist=artist, album=album, timeout=30) for item in result: if cover_url := item.get('cover'): - res = requests.get(cover_url, - headers=headers) + res = requests.get(cover_url, headers=headers) if res.status_code == 200: return res.content, 200, {"Content-Type": res.headers['Content-Type']} diff --git a/api/lyrics.py b/api/lyrics.py index 23d1031..7b6d20d 100644 --- a/api/lyrics.py +++ b/api/lyrics.py @@ -13,7 +13,7 @@ from mod.auth.authentication import require_auth -def read_file_with_encoding(file_path:str, encodings:list[str]): +def read_file_with_encoding(file_path: str, encodings: list[str]): for encoding in encodings: try: with open(file_path, 'r', encoding=encoding) as f: @@ -40,7 +40,7 @@ def lyrics(): if path: lrc_path = os.path.splitext(path)[0] + '.lrc' if os.path.isfile(lrc_path): - file_content: str|None = read_file_with_encoding(lrc_path, ['utf-8', 'gbk']) + file_content: str | None = read_file_with_encoding(lrc_path, ['utf-8', 'gbk']) if file_content is not None: return lrc.standard(file_content) try: @@ -96,8 +96,9 @@ def lrc_json(): for i in lyrics_list: if not i: continue - i['lyrics'] = lrc.standard(i['lyrics']) - response.append(i) + if lyric := i.get('lyrics'): + i['lyrics'] = lrc.standard(lyric) + response.append(i) _response = jsonify(response) _response.headers['Content-Type'] = 'application/json; charset=utf-8' return jsonify(response) diff --git a/mod/searchx/netease.py b/mod/searchx/netease.py index ae213fb..e731794 100644 --- a/mod/searchx/netease.py +++ b/mod/searchx/netease.py @@ -4,10 +4,14 @@ import logging from functools import lru_cache -from Levenshtein import distance -from mod import tools + +import urllib + +from mod import textcompare, tools from mygo.devtools import no_error +from mod.ttscn import t2s + headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0', 'origin': 'https://music.163.com', @@ -22,10 +26,95 @@ COMMON_SEARCH_URL_WANGYI = 'https://music.163.com/api/search/get/web?csrf_token=hlpretag=&hlposttag=&s={}&type={}&offset={}&total=true&limit={}' ALBUM_SEARCH_URL_WANGYI = 'https://music.163.com/api/album/{}?ext=true' LYRIC_URL_WANGYI = 'https://music.163.com/api/song/lyric?id={}&lv=1&tv=1' +ARTIST_SEARCH_URL = 'http://music.163.com/api/v1/artist/{}' +ALBUMS_SEARCH_URL = "http://music.163.com/api/artist/albums/{}?offset=0&total=true&limit=300" +ALBUM_INFO_URL = "http://music.163.com/api/album/{}?ext=true" + + +def listify(obj): + if isinstance(obj, list): + return obj + else: + return [obj] + + +async def search_artist_blur(session: aiohttp.ClientSession, artist_blur, limit=1): + """ 由于没有选择交互的过程, 因此 artist_blur 如果输入的不准确, 可能会查询到错误的歌手 """ + # logging.info('开始搜索: ' + artist_blur) + + num = 0 + if not artist_blur: + logging.info('Missing artist. Skipping match') + return None + + url = COMMON_SEARCH_URL_WANGYI.format( + urllib.parse.quote(artist_blur.lower()), 100, 0, limit) + artists = [] + try: + json_data_r = await session.get(url, headers=headers) + response = json.loads(await json_data_r.text()) + + artist_results = response['result'] + num = int(artist_results['artistCount']) + lim = min(limit, num) + # logging.info('搜索到的歌手数量:' + str(lim)) + for i in range(lim): + try: + artists = listify(artist_results['artists']) + except: + logging.error('Error retrieving artist search results.') + except: + logging.error('Error retrieving artist search results.') + if len(artists) > 0: + return artists[0] + return None + +async def search_albums(session: aiohttp.ClientSession, artist_id): + url = ALBUMS_SEARCH_URL.format(artist_id) + json_data_r = await session.get(url, headers=headers) + response = json.loads(await json_data_r.text()) + if response['code'] == 200: + return response['hotAlbums'] + return None + + +def filter_and_get_album_id(album_list, album): + most_similar = None + highest_similarity = 0 -def levenshtein_similarity(a, b): - return 1 - (distance(a, b) / max(len(a), len(b))) + for candidate_album in album_list: + if album == candidate_album['name']: + return candidate_album['id'] + similarity = textcompare.association(album, candidate_album['name']) + if similarity > highest_similarity: + highest_similarity = similarity + most_similar = candidate_album + return most_similar['id'] if most_similar is not None else None + + +async def get_album_info_by_id(session: aiohttp.ClientSession, album_id): + url = ALBUM_INFO_URL.format(album_id) + json_data_r = await session.get(url, headers=headers) + response = json.loads(await json_data_r.text()) + if response['code'] == 200: + return response['album'] + return None + + +async def get_album_info(session, artist, album): + artist = t2s(artist) + album = t2s(album) + # 1. 根据 artist, 获取 artist_id + if blur_result := await search_artist_blur(session, artist_blur=artist): + artist_id = blur_result['id'] + # 2. 根据 artist_id 查询所有专辑 + if album_list := await search_albums(session, artist_id): + # 3. 根据 album, 过滤, 并获取到 album_id + if album_id := filter_and_get_album_id(album_list, album): + # 4. 根据 album_id, 查询 album_info + return await get_album_info_by_id(session, album_id) + return None async def get_cover_url(session: aiohttp.ClientSession, album_id: int): @@ -48,90 +137,124 @@ async def get_lyrics(session: aiohttp.ClientSession, track_id: int): async def a_search(title='', artist='', album=''): """ - 网易上有太多的自制音乐, 搜起来一大堆乱七八糟的. - 就是说, 只有歌名的情况下, 搜出来的结果十有八九是错的. - 而有 artist + album + title 组合搜索, 准确性可以提高很多很多 + 查询封面: + 三者都传:获取歌曲封面 + 不传歌曲标题:获取专辑封面 --- 传歌手/歌曲 + 只传歌手名:获取歌手图片 + 查询歌词: + title 不能为空 + album, artist 这两个可以为空 """ if not any((title, artist, album)): return None - result_list = [] - limit = 3 + async with aiohttp.ClientSession() as session: - search_str = ' '.join( - [item for item in [title, artist, album] if item]) - async with session.get(COMMON_SEARCH_URL_WANGYI.format(search_str, 1, 0, 100), headers=headers) as response: - if response.status == 200: - song_info_t: str = await response.text() - song_info: dict = json.loads(song_info_t) - song_info: list[dict] = song_info["result"]["songs"] - if len(song_info) < 1: - return None - candidate_songs = [] - for song_item in song_info: - song_name = song_item["name"] - artists = song_item.get("artists", None) - singer_name = " ".join( - [x['name'] for x in artists]) if artists is not None else "" - album_ = song_item.get("album", None) - album_name = album_[ - 'name'] if album is not None else '' - - title_conform_ratio = levenshtein_similarity( - title, song_name) if len(title) * len(song_name) > 0 else 0.0 - artist_conform_ratio = levenshtein_similarity( - artist, singer_name) if len(artist) * len(singer_name) > 0 else 0.0 - album_conform_ratio = levenshtein_similarity( - album, album_name) if len(album) * len(album_name) > 0 else 0.0 - - ratio: float = (title_conform_ratio + - album_conform_ratio + - artist_conform_ratio) / 3.0 - - if ratio >= 0.5: - song_id = song_item['id'] - album_id = album_[ - 'id'] if album is not None else None - singer_id = [x['id'] for x in artists][0] - candidate_songs.append( - {'ratio': ratio, "item": { - "artist": singer_name, - "album": album_name, - "title": song_name, - "artist_id": singer_id, - "album_id": album_id, - "trace_id": song_id - }}) - - candidate_songs.sort( - key=lambda x: x['ratio'], reverse=True) - if len(candidate_songs) < 1: - return None - - candidate_songs = candidate_songs[:min( - len(candidate_songs), limit)] - - for candidate in candidate_songs: - track = candidate['item'] - ratio = candidate['ratio'] - - cover_url = await get_cover_url(session, track['album_id']) - lyrics = await get_lyrics(session, track['trace_id']) - - # 结构化JSON数据 - music_json_data: dict = { - "title": track['title'], - "album": track['album'], - "artists": track['artist'], - "lyrics": lyrics, - "cover": cover_url, - "hash": tools.calculate_md5( - f"title:{track['title']};artists:{track['artist']};album:{track['album']}") - } - - result_list.append(music_json_data) - else: - return None - return result_list + # 查询歌曲, 包括封面和歌词 + if title: + return await search_track(session, title=title, artist=artist, album=album) + elif artist and album: + # 只查询专辑封面 + return await search_album(session, artist, album) + elif artist: + # 查询艺术家封面 + return await search_artist(session, artist) + return None + + +async def search_artist(session, artist): + # 1. 根据 artist, 获取 artist_id + if blur_result := await search_artist_blur(session, artist_blur=artist): + music_json_data: dict = { + "cover": blur_result['img1v1Url'] + } + return listify(music_json_data) + return None + + +async def search_album(session, artist, album): + if album_info := await get_album_info(session, artist, album): + music_json_data: dict = { + "cover": album_info['picUrl'] + } + return listify(music_json_data) + return None + + +async def search_track(session, title, artist, album): + result_list = [] + limit = 10 + search_str = ' '.join([item for item in [title, artist, album] if item]) + url = COMMON_SEARCH_URL_WANGYI.format(search_str, 1, 0, 100) + + response = await session.get(url, headers=headers) + + if response.status != 200: + return None + + song_info_t: str = await response.text() + song_info: dict = json.loads(song_info_t) + song_info: list[dict] = song_info["result"]["songs"] + if len(song_info) < 1: + return None + candidate_songs = [] + for song_item in song_info: + # 有些歌, 查询的 title 可能在别名里, 例如周杰伦的 八度空间-"分裂/离开", 有两个名字. + song_names: list = song_item['alias'] + song_names.append(song_item['name']) + artists = song_item.get("artists", None) + singer_name = " ".join([x['name'] for x in artists]) if artists is not None else "" + album_ = song_item.get("album", None) + album_name = album_['name'] if album is not None else '' + # 取所有名字中最高的相似度 + title_conform_ratio = max([textcompare.association(title, name) for name in song_names]) + + artist_conform_ratio = textcompare.assoc_artists(artist, singer_name) + album_conform_ratio = textcompare.association(album, album_name) + + ratio: float = (title_conform_ratio * (artist_conform_ratio + album_conform_ratio) / 2.0) ** 0.5 + + if ratio >= 0.2: + song_id = song_item['id'] + album_id = album_[ + 'id'] if album is not None else None + singer_id = [x['id'] for x in artists][0] + candidate_songs.append( + {'ratio': ratio, "item": { + "artist": singer_name, + "album": album_name, + "title": title, + "artist_id": singer_id, + "album_id": album_id, + "trace_id": song_id + }}) + + candidate_songs.sort( + key=lambda x: x['ratio'], reverse=True) + if len(candidate_songs) < 1: + return None + + candidate_songs = candidate_songs[:min(len(candidate_songs), limit)] + + for candidate in candidate_songs: + track = candidate['item'] + ratio = candidate['ratio'] + + cover_url = await get_cover_url(session, track['album_id']) + lyrics = await get_lyrics(session, track['trace_id']) + + # 结构化JSON数据 + music_json_data: dict = { + "title": track['title'], + "album": track['album'], + "artists": track['artist'], + "lyrics": lyrics, + "cover": cover_url, + "hash": tools.calculate_md5( + f"title:{track['title']};artists:{track['artist']};album:{track['album']}") + } + + result_list.append(music_json_data) + return result_list @lru_cache(maxsize=64) From b7e7510b096e7ffa94723a58dcdd0435931dae85 Mon Sep 17 00:00:00 2001 From: aniss Date: Thu, 17 Oct 2024 13:34:34 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E5=88=AA=E9=99=A4=20Levenshtein?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 153ef74..426c105 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,4 @@ aiohttp~=3.9.1 Pillow~=10.1.0 pyaes~=1.6.1 Werkzeug~=3.0.1 -mygo~=1.0.0 -Levenshtein==0.26.0 \ No newline at end of file +mygo~=1.0.0 \ No newline at end of file From ca617c84b4a114433e6b23997a94ee4ff575b352 Mon Sep 17 00:00:00 2001 From: aniss Date: Thu, 17 Oct 2024 14:38:39 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix=20bugs=201.=20tag=20=E6=8E=A5=E5=8F=A3,?= =?UTF-8?q?=20=E6=9C=80=E6=96=B0=E7=89=88=E6=9C=AC=E9=9F=B3=E6=B5=81?= =?UTF-8?q?=E5=8F=91=E9=80=81=E8=AF=B7=E6=B1=82=E4=B8=AD=20content=5Ftype?= =?UTF-8?q?=20=E4=B8=BA=E7=A9=BA,=20=E4=BC=9A=E6=8A=A5:=20UnsupportedMedia?= =?UTF-8?q?Type:=20415=20Unsupported=20Media=20Type=202.=20=E9=89=B4?= =?UTF-8?q?=E6=9D=83=E9=83=A8=E5=88=86,=20=E5=81=B6=E5=8F=91=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=99=BB=E5=BD=95=E7=9A=84=20cookies=20=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E8=A7=A3=E6=9E=90,=20=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF.=20=E5=8E=9F=E5=9B=A0=E4=B8=8D=E6=98=8E.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tag.py | 7 ++++++- mod/auth/crypto.py | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/api/tag.py b/api/tag.py index f196d90..407fd23 100644 --- a/api/tag.py +++ b/api/tag.py @@ -1,3 +1,4 @@ +import json import os from . import * @@ -22,7 +23,11 @@ def set_tag(): "具体请查看以启用API鉴权功能。") return render_template_string(webui.error()), 421 - music_data = request.json + try: + # v1.3.2 版本的音流, 传入的 content_type 为空, 会报: UnsupportedMediaType: 415 Unsupported Media Type + music_data = request.json + except: + music_data = json.loads(request.data) audio_path = music_data.get("path") if not audio_path: return "Missing 'path' key in JSON.", 422 diff --git a/mod/auth/crypto.py b/mod/auth/crypto.py index 931f17a..5fa928d 100644 --- a/mod/auth/crypto.py +++ b/mod/auth/crypto.py @@ -31,8 +31,11 @@ def decrypt(self, data: str) -> str: :param data: encrypted data :return: json string """ - aes = pyaes.AESModeOfOperationCTR(self.key.encode(encoding='utf-8')) - return aes.decrypt(bytes.fromhex(data)).decode(encoding='utf-8') + try: + aes = pyaes.AESModeOfOperationCTR(self.key.encode(encoding='utf-8')) + return aes.decrypt(bytes.fromhex(data)).decode(encoding='utf-8') + except: + return "" def change_key(self): self.key = self.gen_key()