feat: 搜索多个结果,并更新“当前”播放列表 (#185)
* local search returns multiple match, and refill play list * bug fix * bug fix in play: sometimes we do not need to update play_list * bug fix in find_best_match; do not update play_list when only 1 match
This commit is contained in:
parent
425214d453
commit
c72a619df0
@ -94,13 +94,34 @@ def fuzzyfinder(user_input, collection):
|
|||||||
return [lower_collection[match] for match in matches]
|
return [lower_collection[match] for match in matches]
|
||||||
|
|
||||||
|
|
||||||
def find_best_match(user_input, collection, cutoff=0.6):
|
# 关键词检测
|
||||||
|
def keyword_detection(user_input, str_list, n):
|
||||||
|
# 过滤包含关键字的字符串
|
||||||
|
matched, remains = [], []
|
||||||
|
for item in str_list:
|
||||||
|
if user_input in item:
|
||||||
|
matched.append(item)
|
||||||
|
else:
|
||||||
|
remains.append(item)
|
||||||
|
|
||||||
|
# 如果 n 是 -1,如果 n 大于匹配的数量,返回所有匹配的结果
|
||||||
|
if n == -1 or n > len(matched):
|
||||||
|
return matched, remains
|
||||||
|
|
||||||
|
# 随机选择 n 个匹配的结果
|
||||||
|
return random.sample(matched, n), remains
|
||||||
|
|
||||||
|
|
||||||
|
def find_best_match(user_input, collection, cutoff=0.6, n=1):
|
||||||
lower_collection = {item.lower(): item for item in collection}
|
lower_collection = {item.lower(): item for item in collection}
|
||||||
user_input = user_input.lower()
|
user_input = user_input.lower()
|
||||||
matches = difflib.get_close_matches(
|
matches, remains = keyword_detection(user_input, lower_collection.keys(), n=n)
|
||||||
user_input, lower_collection.keys(), n=1, cutoff=cutoff
|
if len(matches) < n:
|
||||||
)
|
# 如果没有准确关键词匹配,开始模糊匹配
|
||||||
return lower_collection[matches[0]] if matches else None
|
matches += difflib.get_close_matches(
|
||||||
|
user_input, lower_collection.keys(), n=n, cutoff=cutoff
|
||||||
|
)
|
||||||
|
return [lower_collection[match] for match in matches[:n]]
|
||||||
|
|
||||||
|
|
||||||
# 歌曲排序
|
# 歌曲排序
|
||||||
|
@ -52,6 +52,13 @@ from xiaomusic.utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def list2str(li, verbose=False):
|
||||||
|
if len(li) > 5 and not verbose:
|
||||||
|
return f"{li[:2]} ... {li[-2:]} with len: {len(li)}"
|
||||||
|
else:
|
||||||
|
return f"{li}"
|
||||||
|
|
||||||
|
|
||||||
class XiaoMusic:
|
class XiaoMusic:
|
||||||
def __init__(self, config: Config):
|
def __init__(self, config: Config):
|
||||||
self.config = config
|
self.config = config
|
||||||
@ -667,20 +674,27 @@ class XiaoMusic:
|
|||||||
self.log.info(f"未匹配到指令 {query} {ctrl_panel}")
|
self.log.info(f"未匹配到指令 {query} {ctrl_panel}")
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
def find_real_music_name(self, name):
|
def find_real_music_name(self, name, n=100):
|
||||||
if not self.config.enable_fuzzy_match:
|
if not self.config.enable_fuzzy_match:
|
||||||
self.log.debug("没开启模糊匹配")
|
self.log.debug("没开启模糊匹配")
|
||||||
return name
|
return name
|
||||||
|
|
||||||
all_music_list = list(self.all_music.keys())
|
all_music_list = list(self.all_music.keys())
|
||||||
real_name = find_best_match(
|
real_names = find_best_match(
|
||||||
name, all_music_list, cutoff=self.config.fuzzy_match_cutoff
|
name, all_music_list, cutoff=self.config.fuzzy_match_cutoff, n=n,
|
||||||
)
|
)
|
||||||
if real_name:
|
if real_names:
|
||||||
self.log.info(f"根据【{name}】找到歌曲【{real_name}】")
|
if n > 1:
|
||||||
return real_name
|
# 扩大范围再找,最后保留随机 n 个
|
||||||
|
real_names = find_best_match(
|
||||||
|
name, all_music_list, cutoff=self.config.fuzzy_match_cutoff, n=n * 2,
|
||||||
|
)
|
||||||
|
random.shuffle(real_names)
|
||||||
|
real_names = real_names[:n]
|
||||||
|
self.log.info(f"根据【{name}】找到歌曲【{real_names}】")
|
||||||
|
return real_names
|
||||||
self.log.info(f"没找到歌曲【{name}】")
|
self.log.info(f"没找到歌曲【{name}】")
|
||||||
return name
|
return []
|
||||||
|
|
||||||
def did_exist(self, did):
|
def did_exist(self, did):
|
||||||
return did in self.devices
|
return did in self.devices
|
||||||
@ -728,7 +742,7 @@ class XiaoMusic:
|
|||||||
# 模糊搜一个播放列表
|
# 模糊搜一个播放列表
|
||||||
real_name = find_best_match(
|
real_name = find_best_match(
|
||||||
list_name, self.music_list, cutoff=self.config.fuzzy_match_cutoff
|
list_name, self.music_list, cutoff=self.config.fuzzy_match_cutoff
|
||||||
)
|
)[0]
|
||||||
if real_name:
|
if real_name:
|
||||||
self.log.info(f"根据【{list_name}】找到播放列表【{real_name}】")
|
self.log.info(f"根据【{list_name}】找到播放列表【{real_name}】")
|
||||||
list_name = real_name
|
list_name = real_name
|
||||||
@ -1006,6 +1020,8 @@ class XiaoMusicDevice:
|
|||||||
self._duration = 0
|
self._duration = 0
|
||||||
self._paused_time = 0
|
self._paused_time = 0
|
||||||
|
|
||||||
|
self._play_list = []
|
||||||
|
|
||||||
# 关机定时器
|
# 关机定时器
|
||||||
self._stop_timer = None
|
self._stop_timer = None
|
||||||
self._last_cmd = None
|
self._last_cmd = None
|
||||||
@ -1023,23 +1039,29 @@ class XiaoMusicDevice:
|
|||||||
|
|
||||||
# 初始化播放列表
|
# 初始化播放列表
|
||||||
def update_playlist(self):
|
def update_playlist(self):
|
||||||
if self.device.cur_playlist not in self.xiaomusic.music_list:
|
# 没有重置 list 且非初始化
|
||||||
self.device.cur_playlist = "全部"
|
if self.device.cur_playlist == "当前" and len(self._play_list) > 0:
|
||||||
|
list_name = "当前"
|
||||||
|
else: # 调用了播放列表功能,cur_playlist 为新列表名称
|
||||||
|
if self.device.cur_playlist not in self.xiaomusic.music_list:
|
||||||
|
self.device.cur_playlist = "全部"
|
||||||
|
|
||||||
|
list_name = self.device.cur_playlist
|
||||||
|
self._play_list = copy.copy(self.xiaomusic.music_list[list_name])
|
||||||
|
|
||||||
list_name = self.device.cur_playlist
|
|
||||||
self._play_list = copy.copy(self.xiaomusic.music_list[list_name])
|
|
||||||
if self.device.play_type == PLAY_TYPE_RND:
|
if self.device.play_type == PLAY_TYPE_RND:
|
||||||
random.shuffle(self._play_list)
|
random.shuffle(self._play_list)
|
||||||
self.log.info(f"随机打乱 {list_name} {self._play_list}")
|
self.log.info(f"随机打乱 {list_name} {list2str(self._play_list, self.config.verbose)}")
|
||||||
else:
|
else:
|
||||||
self.log.info(f"没打乱 {list_name} {self._play_list}")
|
self._play_list = sorted(self._play_list)
|
||||||
|
self.log.info(f"没打乱 {list_name} {list2str(self._play_list, self.config.verbose)}")
|
||||||
|
|
||||||
# 播放歌曲
|
# 播放歌曲
|
||||||
async def play(self, name="", search_key=""):
|
async def play(self, name="", search_key=""):
|
||||||
self._last_cmd = "play"
|
self._last_cmd = "play"
|
||||||
return await self._play(name=name, search_key=search_key)
|
return await self._play(name=name, search_key=search_key, update_cur=True)
|
||||||
|
|
||||||
async def _play(self, name="", search_key=""):
|
async def _play(self, name="", search_key="", exact=False, update_cur=False):
|
||||||
if search_key == "" and name == "":
|
if search_key == "" and name == "":
|
||||||
if self.check_play_next():
|
if self.check_play_next():
|
||||||
await self._play_next()
|
await self._play_next()
|
||||||
@ -1049,8 +1071,20 @@ class XiaoMusicDevice:
|
|||||||
self.log.info(f"play. search_key:{search_key} name:{name}")
|
self.log.info(f"play. search_key:{search_key} name:{name}")
|
||||||
|
|
||||||
# 本地歌曲不存在时下载
|
# 本地歌曲不存在时下载
|
||||||
name = self.xiaomusic.find_real_music_name(name)
|
if exact:
|
||||||
if not self.xiaomusic.is_music_exist(name):
|
names = self.xiaomusic.find_real_music_name(name, n=1)
|
||||||
|
else:
|
||||||
|
names = self.xiaomusic.find_real_music_name(name)
|
||||||
|
if len(names) > 0:
|
||||||
|
if update_cur and len(names) > 1: # 大于一首歌才更新
|
||||||
|
self._play_list = names
|
||||||
|
self.device.cur_playlist = "当前"
|
||||||
|
self.update_playlist()
|
||||||
|
elif update_cur: # 只有一首歌,append
|
||||||
|
self._play_list = self._play_list + names
|
||||||
|
name = names[0]
|
||||||
|
self.log.debug(f"当前播放列表为:{list2str(self._play_list, self.config.verbose)}")
|
||||||
|
elif not self.xiaomusic.is_music_exist(name):
|
||||||
if self.config.disable_download:
|
if self.config.disable_download:
|
||||||
await self.do_tts(f"本地不存在歌曲{name}")
|
await self.do_tts(f"本地不存在歌曲{name}")
|
||||||
return
|
return
|
||||||
@ -1081,7 +1115,7 @@ class XiaoMusicDevice:
|
|||||||
if name == "":
|
if name == "":
|
||||||
await self.do_tts("本地没有歌曲")
|
await self.do_tts("本地没有歌曲")
|
||||||
return
|
return
|
||||||
await self._play(name)
|
await self._play(name, exact=True)
|
||||||
|
|
||||||
# 上一首
|
# 上一首
|
||||||
async def play_prev(self):
|
async def play_prev(self):
|
||||||
@ -1101,7 +1135,7 @@ class XiaoMusicDevice:
|
|||||||
if name == "":
|
if name == "":
|
||||||
await self.do_tts("本地没有歌曲")
|
await self.do_tts("本地没有歌曲")
|
||||||
return
|
return
|
||||||
await self._play(name)
|
await self._play(name, exact=True)
|
||||||
|
|
||||||
# 播放本地歌曲
|
# 播放本地歌曲
|
||||||
async def playlocal(self, name):
|
async def playlocal(self, name):
|
||||||
@ -1116,8 +1150,17 @@ class XiaoMusicDevice:
|
|||||||
self.log.info(f"playlocal. name:{name}")
|
self.log.info(f"playlocal. name:{name}")
|
||||||
|
|
||||||
# 本地歌曲不存在时下载
|
# 本地歌曲不存在时下载
|
||||||
name = self.xiaomusic.find_real_music_name(name)
|
names = self.xiaomusic.find_real_music_name(name)
|
||||||
if not self.xiaomusic.is_music_exist(name):
|
if len(names) > 0:
|
||||||
|
if len(names) > 1: # 大于一首歌才更新
|
||||||
|
self._play_list = names
|
||||||
|
self.device.cur_playlist = "当前"
|
||||||
|
self.update_playlist()
|
||||||
|
else: # 只有一首歌,append
|
||||||
|
self._play_list = self._play_list + names
|
||||||
|
name = names[0]
|
||||||
|
self.log.debug(f"当前播放列表为:{list2str(self._play_list, self.config.verbose)}")
|
||||||
|
elif not self.xiaomusic.is_music_exist(name):
|
||||||
await self.do_tts(f"本地不存在歌曲{name}")
|
await self.do_tts(f"本地不存在歌曲{name}")
|
||||||
return
|
return
|
||||||
await self._playmusic(name)
|
await self._playmusic(name)
|
||||||
@ -1464,7 +1507,7 @@ class XiaoMusicDevice:
|
|||||||
self.device.cur_playlist = list_name
|
self.device.cur_playlist = list_name
|
||||||
self.update_playlist()
|
self.update_playlist()
|
||||||
self.log.info(f"开始播放列表{list_name}")
|
self.log.info(f"开始播放列表{list_name}")
|
||||||
await self._play(music_name)
|
await self._play(music_name, exact=True)
|
||||||
|
|
||||||
async def stop(self, arg1=""):
|
async def stop(self, arg1=""):
|
||||||
self._last_cmd = "stop"
|
self._last_cmd = "stop"
|
||||||
|
Loading…
Reference in New Issue
Block a user