feat: musicinfo接口新增musictag参数,用于返回歌曲额外信息
This commit is contained in:
parent
10693e103e
commit
425214d453
46
test/test_music_tags.py
Normal file
46
test/test_music_tags.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import traceback
|
||||||
|
|
||||||
|
from xiaomusic.const import (
|
||||||
|
SUPPORT_MUSIC_TYPE,
|
||||||
|
)
|
||||||
|
from xiaomusic.utils import (
|
||||||
|
get_audio_metadata,
|
||||||
|
traverse_music_directory,
|
||||||
|
)
|
||||||
|
|
||||||
|
# title 标题
|
||||||
|
# artist 艺术家
|
||||||
|
# album 影集
|
||||||
|
# year 年
|
||||||
|
# genre 性
|
||||||
|
# picture 图片
|
||||||
|
# lyrics 歌词
|
||||||
|
|
||||||
|
|
||||||
|
async def test_one_music(filename):
|
||||||
|
# 获取播放时长
|
||||||
|
try:
|
||||||
|
metadata = get_audio_metadata(filename)
|
||||||
|
if metadata:
|
||||||
|
lyrics = metadata.get("lyrics")
|
||||||
|
if lyrics:
|
||||||
|
print(f"歌曲 : {filename} 的 {lyrics}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"歌曲 : {filename} no tag {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
async def main(directory):
|
||||||
|
# 获取所有歌曲文件
|
||||||
|
local_musics = traverse_music_directory(directory, 10, [], SUPPORT_MUSIC_TYPE)
|
||||||
|
print(local_musics)
|
||||||
|
for _, files in local_musics.items():
|
||||||
|
for file in files:
|
||||||
|
await test_one_music(file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
directory = "./music" # 替换为你的音乐目录路径
|
||||||
|
asyncio.run(main(directory))
|
@ -227,13 +227,18 @@ async def musiclist(Verifcation=Depends(verification)):
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/musicinfo")
|
@app.get("/musicinfo")
|
||||||
async def musicinfo(name: str, Verifcation=Depends(verification)):
|
async def musicinfo(
|
||||||
|
name: str, musictag: bool = False, Verifcation=Depends(verification)
|
||||||
|
):
|
||||||
url = xiaomusic.get_music_url(name)
|
url = xiaomusic.get_music_url(name)
|
||||||
return {
|
info = {
|
||||||
"ret": "OK",
|
"ret": "OK",
|
||||||
"name": name,
|
"name": name,
|
||||||
"url": url,
|
"url": url,
|
||||||
}
|
}
|
||||||
|
if musictag:
|
||||||
|
info["tags"] = xiaomusic.get_music_tags(name)
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
@app.get("/curplaylist")
|
@app.get("/curplaylist")
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import base64
|
||||||
import copy
|
import copy
|
||||||
import difflib
|
import difflib
|
||||||
import json
|
import json
|
||||||
@ -20,8 +21,13 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import mutagen
|
import mutagen
|
||||||
from mutagen.id3 import ID3
|
from mutagen.flac import FLAC
|
||||||
|
from mutagen.id3 import APIC, ID3
|
||||||
|
from mutagen.monkeysaudio import MonkeysAudio
|
||||||
from mutagen.mp3 import MP3
|
from mutagen.mp3 import MP3
|
||||||
|
from mutagen.mp4 import MP4
|
||||||
|
from mutagen.oggvorbis import OggVorbis
|
||||||
|
from mutagen.wave import WAVE
|
||||||
from requests.utils import cookiejar_from_dict
|
from requests.utils import cookiejar_from_dict
|
||||||
|
|
||||||
from xiaomusic.const import SUPPORT_MUSIC_TYPE
|
from xiaomusic.const import SUPPORT_MUSIC_TYPE
|
||||||
@ -441,3 +447,131 @@ def chinese_to_number(chinese):
|
|||||||
result += num
|
result += num
|
||||||
num = 0
|
num = 0
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_audio_metadata(file_path):
|
||||||
|
if file_path.endswith(".mp3"):
|
||||||
|
return get_mp3_metadata(file_path)
|
||||||
|
elif file_path.endswith(".flac"):
|
||||||
|
return get_flac_metadata(file_path)
|
||||||
|
elif file_path.endswith(".wav"):
|
||||||
|
return get_wav_metadata(file_path)
|
||||||
|
elif file_path.endswith(".ape"):
|
||||||
|
return get_ape_metadata(file_path)
|
||||||
|
elif file_path.endswith(".ogg"):
|
||||||
|
return get_ogg_metadata(file_path)
|
||||||
|
elif file_path.endswith(".m4a"):
|
||||||
|
return get_m4a_metadata(file_path)
|
||||||
|
else:
|
||||||
|
raise ValueError("Unsupported file type")
|
||||||
|
|
||||||
|
|
||||||
|
def get_mp3_metadata(file_path):
|
||||||
|
audio = MP3(file_path, ID3=ID3)
|
||||||
|
tags = audio.tags
|
||||||
|
if tags is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
"title": tags.get("TIT2", [""])[0] if "TIT2" in tags else "",
|
||||||
|
"artist": tags.get("TPE1", [""])[0] if "TPE1" in tags else "",
|
||||||
|
"album": tags.get("TALB", [""])[0] if "TALB" in tags else "",
|
||||||
|
"year": tags.get("TDRC", [""])[0] if "TDRC" in tags else "",
|
||||||
|
"genre": tags.get("TCON", [""])[0] if "TCON" in tags else "",
|
||||||
|
"picture": "",
|
||||||
|
"lyrics": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
for tag in tags.values():
|
||||||
|
if isinstance(tag, APIC):
|
||||||
|
metadata["picture"] = base64.b64encode(tag.data).decode("utf-8")
|
||||||
|
break
|
||||||
|
|
||||||
|
lyrics = tags.getall("USLT")
|
||||||
|
if lyrics:
|
||||||
|
metadata["lyrics"] = lyrics[0]
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def get_flac_metadata(file_path):
|
||||||
|
audio = FLAC(file_path)
|
||||||
|
metadata = {
|
||||||
|
"title": audio.get("title", [""])[0],
|
||||||
|
"artist": audio.get("artist", [""])[0],
|
||||||
|
"album": audio.get("album", [""])[0],
|
||||||
|
"year": audio.get("date", [""])[0],
|
||||||
|
"genre": audio.get("genre", [""])[0],
|
||||||
|
"picture": "",
|
||||||
|
"lyrics": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
if audio.pictures:
|
||||||
|
picture = audio.pictures[0]
|
||||||
|
metadata["picture"] = base64.b64encode(picture.data).decode("utf-8")
|
||||||
|
|
||||||
|
if "lyrics" in audio:
|
||||||
|
metadata["lyrics"] = audio["lyrics"][0]
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def get_wav_metadata(file_path):
|
||||||
|
audio = WAVE(file_path)
|
||||||
|
metadata = {
|
||||||
|
"title": audio.get("TIT2", [""])[0],
|
||||||
|
"artist": audio.get("TPE1", [""])[0],
|
||||||
|
"album": audio.get("TALB", [""])[0],
|
||||||
|
"year": audio.get("TDRC", [""])[0],
|
||||||
|
"genre": audio.get("TCON", [""])[0],
|
||||||
|
"picture": "",
|
||||||
|
"lyrics": "",
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def get_ape_metadata(file_path):
|
||||||
|
audio = MonkeysAudio(file_path)
|
||||||
|
metadata = {
|
||||||
|
"title": audio.get("TIT2", [""])[0],
|
||||||
|
"artist": audio.get("TPE1", [""])[0],
|
||||||
|
"album": audio.get("TALB", [""])[0],
|
||||||
|
"year": audio.get("TDRC", [""])[0],
|
||||||
|
"genre": audio.get("TCON", [""])[0],
|
||||||
|
"picture": "",
|
||||||
|
"lyrics": "",
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def get_ogg_metadata(file_path):
|
||||||
|
audio = OggVorbis(file_path)
|
||||||
|
metadata = {
|
||||||
|
"title": audio.get("title", [""])[0],
|
||||||
|
"artist": audio.get("artist", [""])[0],
|
||||||
|
"album": audio.get("album", [""])[0],
|
||||||
|
"year": audio.get("date", [""])[0],
|
||||||
|
"genre": audio.get("genre", [""])[0],
|
||||||
|
"picture": "",
|
||||||
|
"lyrics": "",
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def get_m4a_metadata(file_path):
|
||||||
|
audio = MP4(file_path)
|
||||||
|
metadata = {
|
||||||
|
"title": audio.tags.get("\xa9nam", [""])[0],
|
||||||
|
"artist": audio.tags.get("\xa9ART", [""])[0],
|
||||||
|
"album": audio.tags.get("\xa9alb", [""])[0],
|
||||||
|
"year": audio.tags.get("\xa9day", [""])[0],
|
||||||
|
"genre": audio.tags.get("\xa9gen", [""])[0],
|
||||||
|
"picture": "",
|
||||||
|
"lyrics": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
if "covr" in audio.tags:
|
||||||
|
cover = audio.tags["covr"][0]
|
||||||
|
metadata["picture"] = base64.b64encode(cover).decode("utf-8")
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
@ -41,6 +41,7 @@ from xiaomusic.utils import (
|
|||||||
deepcopy_data_no_sensitive_info,
|
deepcopy_data_no_sensitive_info,
|
||||||
find_best_match,
|
find_best_match,
|
||||||
fuzzyfinder,
|
fuzzyfinder,
|
||||||
|
get_audio_metadata,
|
||||||
get_local_music_duration,
|
get_local_music_duration,
|
||||||
get_web_music_duration,
|
get_web_music_duration,
|
||||||
is_mp3,
|
is_mp3,
|
||||||
@ -68,6 +69,7 @@ class XiaoMusic:
|
|||||||
self.music_list = {} # 播放列表 key 为目录名, value 为 play_list
|
self.music_list = {} # 播放列表 key 为目录名, value 为 play_list
|
||||||
self.devices = {} # key 为 did
|
self.devices = {} # key 为 did
|
||||||
self.running_task = []
|
self.running_task = []
|
||||||
|
self.all_music_tags = {} # 歌曲额外信息
|
||||||
|
|
||||||
# 初始化配置
|
# 初始化配置
|
||||||
self.init_config()
|
self.init_config()
|
||||||
@ -389,6 +391,9 @@ class XiaoMusic:
|
|||||||
self.log.warning(f"获取歌曲时长失败 {name} {url}")
|
self.log.warning(f"获取歌曲时长失败 {name} {url}")
|
||||||
return sec, url
|
return sec, url
|
||||||
|
|
||||||
|
def get_music_tags(self, name):
|
||||||
|
return self.all_music_tags.get(name, {})
|
||||||
|
|
||||||
def get_music_url(self, name):
|
def get_music_url(self, name):
|
||||||
if self.is_web_music(name):
|
if self.is_web_music(name):
|
||||||
url = self.all_music[name]
|
url = self.all_music[name]
|
||||||
@ -432,6 +437,7 @@ class XiaoMusic:
|
|||||||
# 获取目录下所有歌曲,生成随机播放列表
|
# 获取目录下所有歌曲,生成随机播放列表
|
||||||
def _gen_all_music_list(self):
|
def _gen_all_music_list(self):
|
||||||
self.all_music = {}
|
self.all_music = {}
|
||||||
|
self.all_music_tags = {}
|
||||||
all_music_by_dir = {}
|
all_music_by_dir = {}
|
||||||
local_musics = traverse_music_directory(
|
local_musics = traverse_music_directory(
|
||||||
self.music_path,
|
self.music_path,
|
||||||
@ -455,6 +461,7 @@ class XiaoMusic:
|
|||||||
filename = os.path.basename(file)
|
filename = os.path.basename(file)
|
||||||
(name, _) = os.path.splitext(filename)
|
(name, _) = os.path.splitext(filename)
|
||||||
self.all_music[name] = file
|
self.all_music[name] = file
|
||||||
|
self.all_music_tags[name] = get_audio_metadata(file)
|
||||||
all_music_by_dir[dir_name][name] = True
|
all_music_by_dir[dir_name][name] = True
|
||||||
self.log.debug(f"_gen_all_music_list {name}:{dir_name}:{file}")
|
self.log.debug(f"_gen_all_music_list {name}:{dir_name}:{file}")
|
||||||
|
|
||||||
@ -516,6 +523,8 @@ class XiaoMusic:
|
|||||||
if (not name) or (not url):
|
if (not name) or (not url):
|
||||||
continue
|
continue
|
||||||
self.all_music[name] = url
|
self.all_music[name] = url
|
||||||
|
# TODO: 网络歌曲获取歌曲额外信息
|
||||||
|
# self.all_music_tags[name] = get_audio_metadata(url)
|
||||||
one_music_list.append(name)
|
one_music_list.append(name)
|
||||||
|
|
||||||
# 处理电台列表
|
# 处理电台列表
|
||||||
|
Loading…
Reference in New Issue
Block a user