# -*- coding: utf-8 -*- """修「手表推了 mp3 过去,暂停不了」。 根因:play_audio 用 Windows MCI 直接播放文件,而 MCI **不是系统媒体会话**, 不响应全局多媒体按键(VK_MEDIA_PLAY_PAUSE)。手表发的「播放/暂停」正是 模拟媒体键,所以对推进去的音频完全无效 —— 只能靠「停止」(它会 close)。 修法:只要电脑正在播手表推的音频,媒体命令就直接作用于 MCI; 音量类仍然走系统媒体键(音量是全局的)。 另外补三条精确命令:audio_pause / audio_resume / audio_status。 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\windows" r"\lanremote") ACTIONS = ROOT / "actions.py" SERVER = ROOT / "server.py" PROTOCOL = ROOT / "protocol.py" # ---------------- protocol ---------------- PROTO_OLD = 'CMD_FEATURE_RUN = "feature_run" # 执行某个功能\n' PROTO_NEW = ('CMD_FEATURE_RUN = "feature_run" # 执行某个功能\n' 'CMD_AUDIO_PAUSE = "audio_pause" # 暂停电脑正在播的音频\n' 'CMD_AUDIO_RESUME = "audio_resume" # 继续播放\n' 'CMD_AUDIO_STATUS = "audio_status" # 当前播放状态\n') # ---------------- actions:状态字段 ---------------- INIT_OLD = ''' self._last_cpu_total = 0 self._last_cpu_idle = 0''' INIT_NEW = ''' self._last_cpu_total = 0 self._last_cpu_idle = 0 # 电脑正在播的音频是否被暂停(MCI 不是系统媒体会话,得自己记) self._audio_paused = False self._audio_name = ""''' # ---------------- actions:play_audio 记名字、复位暂停 ---------------- PLAY_OLD = ''' self._audio_alias = alias self._mci(f"play {alias}") return f"正在播放 {p.name}"''' PLAY_NEW = ''' self._audio_alias = alias self._audio_paused = False self._audio_name = p.name self._mci(f"play {alias}") return f"正在播放 {p.name}"''' STOP_OLD = ''' def stop_audio(self) -> str: with self._lock: self._mci_close_locked() return "已停止播放"''' STOP_NEW = ''' def stop_audio(self) -> str: with self._lock: self._mci_close_locked() self._audio_paused = False self._audio_name = "" return "已停止播放" def _mci_mode(self, alias: str) -> str: """问 MCI 现在是什么状态:playing / paused / stopped。""" buffer = ctypes.create_unicode_buffer(64) try: ctypes.windll.winmm.mciSendStringW( ctypes.c_wchar_p(f"status {alias} mode"), buffer, 64, None) return (buffer.value or "").strip().lower() except Exception: # noqa: BLE001 return "" def audio_pause(self) -> str: with self._lock: alias = self._audio_alias if not alias: raise ActionError("电脑当前没有播放手表推过去的音频") if self._audio_paused: return "已经暂停了" if not self._mci(f"pause {alias}"): raise ActionError("暂停失败") self._audio_paused = True return f"已暂停 {self._audio_name or '音频'}" def audio_resume(self) -> str: with self._lock: alias = self._audio_alias if not alias: raise ActionError("电脑当前没有播放手表推过去的音频") if not self._audio_paused: return "正在播放中" if not self._mci(f"resume {alias}"): raise ActionError("继续播放失败") self._audio_paused = False return f"继续播放 {self._audio_name or '音频'}" def audio_status(self) -> str: with self._lock: alias = self._audio_alias if not alias: return "电脑当前没有播放音频" mode = self._mci_mode(alias) if mode == "playing": return f"正在播放 {self._audio_name}" if mode == "paused": return f"已暂停 {self._audio_name}" # MCI 自己放完了 self._mci_close_locked() self._audio_paused = False name = self._audio_name self._audio_name = "" return f"{name} 已播放完毕" if name else "播放已结束"''' # ---------------- actions:media_control 优先控制 MCI ---------------- MEDIA_OLD = ''' def media_control(self, action: str) -> str: vk = VK_MEDIA.get(action) if vk is None: raise ActionError(f"不支持的媒体动作: {action}") if not IS_WINDOWS: raise ActionError("媒体控制目前仅支持 Windows")''' MEDIA_NEW = ''' def media_control(self, action: str) -> str: if not IS_WINDOWS: raise ActionError("媒体控制目前仅支持 Windows") if action not in VK_MEDIA: raise ActionError(f"不支持的媒体动作: {action}") # 电脑正在播手表推过去的音频时,直接控制它。 # MCI 不是系统媒体会话,全局媒体键对它无效 —— # 这就是"推了 mp3 过去却暂停不了"的原因。 if action in ("playpause", "stop"): handled = self._control_pushed_audio(action) if handled: return handled vk = VK_MEDIA.get(action)''' CONTROL_MCI = ''' def _control_pushed_audio(self, action: str) -> str | None: """如果正在播推过去的音频,就控制它;否则返回 None 走系统媒体键。""" with self._lock: alias = self._audio_alias if not alias: return None mode = self._mci_mode(alias) if mode not in ("playing", "paused"): # MCI 已经放完了 self._mci_close_locked() self._audio_paused = False self._audio_name = "" return None if action == "stop": self._mci_close_locked() self._audio_paused = False self._audio_name = "" return "已停止播放" # playpause if mode == "paused" or self._audio_paused: if self._mci(f"resume {alias}"): self._audio_paused = False return f"继续播放 {self._audio_name}" return None if self._mci(f"pause {alias}"): self._audio_paused = True return f"已暂停 {self._audio_name}" return None ''' # ---------------- server:三条新命令 ---------------- SRV_OLD = ' if cmd == P.CMD_FEATURE_LIST:' SRV_NEW = ''' if cmd == P.CMD_AUDIO_PAUSE: note = self.actions.audio_pause() self._log(f"[音频] {note}") return {"ok": True, "note": note} if cmd == P.CMD_AUDIO_RESUME: note = self.actions.audio_resume() self._log(f"[音频] {note}") return {"ok": True, "note": note} if cmd == P.CMD_AUDIO_STATUS: return {"ok": True, "note": self.actions.audio_status()} if cmd == P.CMD_FEATURE_LIST:''' FIXES = [ (PROTOCOL, PROTO_OLD, PROTO_NEW, "protocol: 三条音频命令"), (ACTIONS, INIT_OLD, INIT_NEW, "actions: 音频状态字段"), (ACTIONS, PLAY_OLD, PLAY_NEW, "actions: play_audio 记状态"), (ACTIONS, STOP_OLD, STOP_NEW, "actions: 暂停/继续/状态实现"), (ACTIONS, MEDIA_OLD, MEDIA_NEW, "actions: media_control 优先控 MCI"), (ACTIONS, " # ------------------------------------------------------------------\n # 语音播报(系统自带 TTS)", CONTROL_MCI + "\n # ------------------------------------------------------------------\n # 语音播报(系统自带 TTS)", "actions: _control_pushed_audio"), (SERVER, SRV_OLD, SRV_NEW, "server: 三条音频命令"), ] def main() -> int: applied, skipped = 0, [] for path, old, new, tag in FIXES: text = path.read_text(encoding="utf-8") if old not in text: skipped.append(tag) print(f" [跳过] {tag} —— 锚点没找到") continue path.write_text(text.replace(old, new, 1), encoding="utf-8") applied += 1 print(f" {tag}: OK") print(f"\n 应用 {applied} / {len(FIXES)}") if skipped: print(f" 跳过: {', '.join(skipped)}") import ast ok = True for label, path in (("protocol.py", PROTOCOL), ("actions.py", ACTIONS), ("server.py", SERVER), ("headless.py", ROOT / "headless.py")): try: ast.parse(path.read_text(encoding="utf-8")) print(f" {label}: 语法 OK") except SyntaxError as exc: print(f" {label}: 语法错误 {exc}") ok = False return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())