支持flac格式的本地文件

This commit is contained in:
涵曦 2024-01-27 20:56:07 +08:00
parent 63eb0c22cb
commit 8e8a605816
2 changed files with 68 additions and 39 deletions

View File

@ -37,12 +37,18 @@ KEY_WORD_DICT = {
"播放歌曲": "play", "播放歌曲": "play",
"放歌曲": "play", "放歌曲": "play",
"下一首": "play_next", "下一首": "play_next",
"单曲循环":"set_play_type_one", "单曲循环": "set_play_type_one",
"全部循环":"set_play_type_all", "全部循环": "set_play_type_all",
"关机":"stop", "关机": "stop",
"停止播放":"stop", "停止播放": "stop",
} }
SUPPORT_MUSIC_TYPE = [
"mp3",
"flac",
]
@dataclass @dataclass
class Config: class Config:
hardware: str = os.getenv("MI_HARDWARE", "L07A") hardware: str = os.getenv("MI_HARDWARE", "L07A")

View File

@ -29,6 +29,7 @@ from xiaomusic.config import (
COOKIE_TEMPLATE, COOKIE_TEMPLATE,
LATEST_ASK_API, LATEST_ASK_API,
KEY_WORD_DICT, KEY_WORD_DICT,
SUPPORT_MUSIC_TYPE,
Config, Config,
) )
from xiaomusic.utils import ( from xiaomusic.utils import (
@ -41,6 +42,7 @@ EOF = object()
PLAY_TYPE_ONE = 0 # 单曲循环 PLAY_TYPE_ONE = 0 # 单曲循环
PLAY_TYPE_ALL = 1 # 全部循环 PLAY_TYPE_ALL = 1 # 全部循环
class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass pass
@ -61,6 +63,7 @@ class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
# ignore this or TODO find out why the error later # ignore this or TODO find out why the error later
pass pass
class XiaoMusic: class XiaoMusic:
def __init__(self, config: Config): def __init__(self, config: Config):
self.config = config self.config = config
@ -286,8 +289,7 @@ class XiaoMusic:
def is_downloading(self): def is_downloading(self):
if not self.download_proc: if not self.download_proc:
return False return False
if self.download_proc.returncode != None \ if self.download_proc.returncode != None and self.download_proc.returncode < 0:
and self.download_proc.returncode < 0:
return False return False
return True return True
@ -300,11 +302,18 @@ class XiaoMusic:
pass pass
sbp_args = ( sbp_args = (
"yt-dlp", f"ytsearch:{name}", "yt-dlp",
"-x", "--audio-format", "mp3", f"ytsearch:{name}",
"--paths", self.music_path, "-x",
"-o", f"{name}.mp3", "--audio-format",
"--ffmpeg-location", "./ffmpeg/bin") "mp3",
"--paths",
self.music_path,
"-o",
f"{name}.mp3",
"--ffmpeg-location",
"./ffmpeg/bin",
)
if self.proxy: if self.proxy:
sbp_args += ("--proxy", f"{self.proxy}") sbp_args += ("--proxy", f"{self.proxy}")
@ -313,43 +322,53 @@ class XiaoMusic:
await self.do_tts(f"正在下载歌曲{name}") await self.do_tts(f"正在下载歌曲{name}")
def get_filename(self, name): def get_filename(self, name):
filename = os.path.join(self.music_path, f"{name}.mp3") filename = os.path.join(self.music_path, name)
return filename return filename
# 本地是否存在歌曲 # 本地是否存在歌曲
def local_exist(self, name): def local_exist(self, name):
filename = self.get_filename(name) for tp in SUPPORT_MUSIC_TYPE:
self.log.debug("local_exist. filename:%s", filename) filename = self.get_filename(f"{name}.{tp}")
return os.path.exists(filename) self.log.debug("try local_exist. filename:%s", filename)
if os.path.exists(filename):
return filename
return ""
# 获取歌曲播放地址 # 获取歌曲播放地址
def get_file_url(self, name): def get_file_url(self, filename):
encoded_name = urllib.parse.quote(os.path.basename(name)) encoded_name = urllib.parse.quote(os.path.basename(filename))
return f"http://{self.hostname}:{self.port}/{encoded_name}.mp3" return f"http://{self.hostname}:{self.port}/{encoded_name}"
# 随机获取一首音乐 # 随机获取一首音乐
def random_music(self): def random_music(self):
files = os.listdir(self.music_path) files = os.listdir(self.music_path)
# 过滤 mp3 文件 # 过滤音乐文件
mp3_files = [file for file in files if file.endswith(".mp3")] music_files = []
if len(mp3_files) == 0: for file in files:
for tp in SUPPORT_MUSIC_TYPE:
if file.endswith(f".{tp}"):
music_files.append(file)
if len(music_files) == 0:
self.log.warning(f"没有随机到歌曲") self.log.warning(f"没有随机到歌曲")
return "" return ""
# 随机选择一个文件 # 随机选择一个文件
mp3_file = random.choice(mp3_files) music_file = random.choice(music_files)
name = mp3_file[:-4] (filename, extension) = os.path.splitext(music_file)
self.log.info(f"随机到歌曲{name}") self.log.info(f"随机到歌曲{filename}.{extension}")
return name return filename
# 获取mp3文件播放时长 # 获取文件播放时长
def get_mp3_duration(self, name): def get_file_duration(self, filename):
filename = self.get_filename(name) # 获取音频文件对象
audio = mutagen.mp3.MP3(filename) audio = mutagen.File(filename)
return audio.info.length # 获取播放时长
duration = audio.info.length
return duration
# 设置下一首歌曲的播放定时器 # 设置下一首歌曲的播放定时器
def set_next_music_timeout(self): def set_next_music_timeout(self):
sec = int(self.get_mp3_duration(self.cur_music)) sec = int(self.get_file_duration(self.cur_music))
self.log.info(f"歌曲{self.cur_music}的时长{sec}") self.log.info(f"歌曲{self.cur_music}的时长{sec}")
if self._next_timer: if self._next_timer:
self._next_timer.cancel() self._next_timer.cancel()
@ -372,7 +391,9 @@ class XiaoMusic:
await self.init_all_data(session) await self.init_all_data(session)
task = asyncio.create_task(self.poll_latest_ask()) task = asyncio.create_task(self.poll_latest_ask())
assert task is not None # to keep the reference to task, do not remove this assert task is not None # to keep the reference to task, do not remove this
self.log.info(f"Running xiaomusic now, 用`{'/'.join(KEY_WORD_DICT.keys())}`开头来控制") self.log.info(
f"Running xiaomusic now, 用`{'/'.join(KEY_WORD_DICT.keys())}`开头来控制"
)
while True: while True:
self.polling_event.set() self.polling_event.set()
await self.new_record_event.wait() await self.new_record_event.wait()
@ -396,12 +417,12 @@ class XiaoMusic:
opkey = match.groups()[0] opkey = match.groups()[0]
opvalue = KEY_WORD_DICT[opkey] opvalue = KEY_WORD_DICT[opkey]
oparg = query[len(opkey):] oparg = query[len(opkey) :]
self.log.info("收到指令:%s %s", opkey, oparg) self.log.info("收到指令:%s %s", opkey, oparg)
try: try:
func = getattr(self, opvalue) func = getattr(self, opvalue)
await func(name = oparg) await func(name=oparg)
except Exception as e: except Exception as e:
self.log.warning(f"执行出错 {str(e)}\n{traceback.format_exc()}") self.log.warning(f"执行出错 {str(e)}\n{traceback.format_exc()}")
@ -413,13 +434,15 @@ class XiaoMusic:
return return
await self.do_tts(f"即将播放{name}") await self.do_tts(f"即将播放{name}")
if not self.local_exist(name): filename = self.local_exist(name)
if len(filename) <= 0:
await self.download(name) await self.download(name)
self.log.info("正在下载中 %s", name) self.log.info("正在下载中 %s", name)
filename = self.get_filename(f"{name}.mp3")
await self.download_proc.wait() await self.download_proc.wait()
self.cur_music = name self.cur_music = filename
url = self.get_file_url(name) url = self.get_file_url(filename)
self.log.info("播放 %s", url) self.log.info("播放 %s", url)
await self.stop_if_xiaoai_is_playing() await self.stop_if_xiaoai_is_playing()
await self.mina_service.play_by_url(self.device_id, url) await self.mina_service.play_by_url(self.device_id, url)