diff --git a/xiaomusic/command_handler.py b/xiaomusic/command_handler.py index 47a260a..cabdd01 100644 --- a/xiaomusic/command_handler.py +++ b/xiaomusic/command_handler.py @@ -88,6 +88,14 @@ class CommandHandler: Returns: tuple: (命令值, 命令参数),未匹配返回 (None, None) """ + # 检查是否有待选择的歌曲 + if device._pending_selection: + patternarg = r"^第?([零一二三四五六七八九十百千万亿]+)[个首条集]$" + matcharg = re.match(patternarg, query) + if matcharg: + self.log.info(f"匹配到选择指令. query:{query}") + return "select_index", query + # 优先处理完全匹配 opvalue = self.check_full_match_cmd(device, query, ctrl_panel) if opvalue: diff --git a/xiaomusic/config.py b/xiaomusic/config.py index 00b1fd1..46910e6 100644 --- a/xiaomusic/config.py +++ b/xiaomusic/config.py @@ -142,6 +142,8 @@ class Config: enable_fuzzy_match: bool = ( os.getenv("XIAOMUSIC_ENABLE_FUZZY_MATCH", "true").lower() == "true" ) + # 模糊搜索返回的最大结果数量(用于多结果选择功能) + fuzzy_match_max_results: int = int(os.getenv("XIAOMUSIC_FUZZY_MATCH_MAX_RESULTS", "100")) stop_tts_msg: str = os.getenv("XIAOMUSIC_STOP_TTS_MSG", "收到,再见") enable_config_example: bool = False diff --git a/xiaomusic/device_player.py b/xiaomusic/device_player.py index e6bad56..f08943b 100644 --- a/xiaomusic/device_player.py +++ b/xiaomusic/device_player.py @@ -77,6 +77,8 @@ class XiaoMusicDevice: # 关机定时器 self._stop_timer = None self._last_cmd = None + self._pending_selection = None + self._pending_selection_count = 0 self.update_playlist() # 添加歌曲定时器 @@ -237,6 +239,12 @@ class XiaoMusicDevice: search_key: 搜索关键词 allow_download: 是否允许下载(True: _play行为,False: playlocal行为) """ + # 清除旧的待选择状态 + if self._pending_selection: + self.log.info(f"清除旧的待选择状态,重新搜索: {name}") + self._pending_selection = None + self._pending_selection_count = 0 + # 初始检查逻辑 if not search_key and not name: if self.check_play_next(): @@ -253,9 +261,21 @@ class XiaoMusicDevice: self.log.info(f"没有歌曲播放了 name:{name} search_key:{search_key}") return - # 模糊搜索 - names = self.xiaomusic.music_library.find_real_music_name(name, n=1) - self.log.info(f"play_internal. names:{names} {len(names)}") + # 模糊搜索(支持多结果选择) + max_results = self.config.fuzzy_match_max_results + names = self.xiaomusic.music_library.find_real_music_name(name, n=max_results) + self.log.info(f"play_internal. 搜索关键词:{name} 匹配数量:{len(names)}") + if len(names) > 1: + for idx, music_name in enumerate(names, 1): + self.log.info(f" 第{idx}个: {music_name}") + + if len(names) > 1: + self._pending_selection = names + self._pending_selection_count = len(names) + selection_text = f"共找到{len(names)}条匹配记录,请重新呼叫小爱同学并告诉她第几个" + self.log.info(selection_text) + await self.xiaomusic.do_tts(self.did, selection_text) + return if not names: # 检查本地是否存在歌曲,不存在则根据参数决定是否下载 @@ -1041,3 +1061,18 @@ class XiaoMusicDevice: if name in music_list.get("所有电台", []): return "所有电台" return "全部" + + async def handle_selection(self, index): + """处理用户选择第几个歌曲 + + Args: + index: 用户选择的序号(从1开始) + """ + if not self._pending_selection or index < 1 or index > len(self._pending_selection): + await self.xiaomusic.do_tts(self.did, "选择无效") + return + + selected_name = self._pending_selection[index - 1] + self.log.info(f"用户选择了第{index}个: {selected_name}") + # 保持待选择状态不变,支持用户继续选择其他歌曲 + await self._playmusic(selected_name) diff --git a/xiaomusic/music_library.py b/xiaomusic/music_library.py index 2b8b6d9..f575a66 100644 --- a/xiaomusic/music_library.py +++ b/xiaomusic/music_library.py @@ -596,7 +596,7 @@ class MusicLibrary: if name in real_names: return [name] - # 音乐不在查找结果同时n大于1, 模糊匹配模式,扩大范围再找,最后保留随机 n 个 + # 音乐不在查找结果同时n大于1, 模糊匹配模式,扩大范围再找,最后按文件名自然排序 if n > 1: real_names = find_best_match( name, @@ -605,7 +605,7 @@ class MusicLibrary: n=n * 2, extra_search_index=self._extra_index_search, ) - random.shuffle(real_names) + real_names.sort(key=custom_sort_key) self.log.info(f"没找到歌曲【{name}】") return real_names[:n] diff --git a/xiaomusic/xiaomusic.py b/xiaomusic/xiaomusic.py index 7b55a22..e67f64d 100644 --- a/xiaomusic/xiaomusic.py +++ b/xiaomusic/xiaomusic.py @@ -507,6 +507,23 @@ class XiaoMusic: return await self.do_tts(did, f"播放列表{list_name}中找不到第${index}个") + # 口令:选择第几个 + async def select_index(self, did="", arg1="", **kwargs): + patternarg = r"^第?([零一二三四五六七八九十百千万亿]+)[个首条集]$" + matcharg = re.match(patternarg, arg1) + if not matcharg: + return + + chinese_index = matcharg.groups()[0] + index = chinese_to_number(chinese_index) + + device = self.device_manager.devices.get(did) + if not device: + self.log.warning(f"设备 did:{did} 不存在") + return + + await device.handle_selection(index) + # 口令:播放歌曲 async def play(self, did="", arg1="", **kwargs): parts = arg1.split("|")