From b3c264e910fa48d4310a7e126b283b9f2c44e0bf Mon Sep 17 00:00:00 2001 From: Shuming Liu Date: Wed, 12 Aug 2026 13:16:40 +0800 Subject: [PATCH] continue fixing.... --- README.md | 2 +- media_player.py | 94 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cb5f647..c836dd2 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ pip install -r requirements.txt | `←` / `→` | 后退 / 前进 10 秒 | | `Shift+←` / `Shift+→` | 后退 / 前进 60 秒 | | `↑` / `↓` | 音量 +5 / -5 | -| `Ctrl+↑` / `Ctrl+↓` | 快进步长 +5 / -5 秒(macOS 亦支持 `Command+↑ / ↓`) | +| `Ctrl+↑` / `Ctrl+↓` | 快进步长 +1 / -1 秒(macOS 亦支持 `Command+↑ / ↓`) | | `M` | 静音 / 恢复 | | `Ctrl+←` / `Ctrl+→` | 上一首 / 下一首(macOS 亦支持 `Command+← / →`) | | `N` / `P` | 下一首 / 上一首 | diff --git a/media_player.py b/media_player.py index 843ab36..4e1b914 100644 --- a/media_player.py +++ b/media_player.py @@ -12,6 +12,7 @@ ← / → 后退 / 前进 10 秒 Shift+← / → 后退 / 前进 60 秒 ↑ / ↓ 音量 +5 / -5 + Ctrl+↑ / ↓ 快进步长 +1 / -1 秒(macOS 亦支持 Command+↑ / ↓) M 静音 / 恢复 Ctrl+← / → 上一首 / 下一首(macOS 也支持 Command+← / →) N / P 下一首 / 上一首 @@ -830,6 +831,7 @@ class MediaPlayerApp(tk.Tk): self.current_index = -1 self.volume = 80 self.rate = 1.0 + self.seek_step = self._load_settings() self.loop_mode = "list" self._seeking = False self._switching_until = 0.0 @@ -963,10 +965,27 @@ class MediaPlayerApp(tk.Tk): self.seekbar = SeekBar(bottom, command=self._on_seek_command) self.seekbar.pack(fill="x") + controls = ttk.Frame(bottom) + controls.pack(fill="x", pady=(4, 0)) self.time_var = tk.StringVar(value="00:00 / --:--") self.status_var = tk.StringVar(value="就绪 — 打开文件或按 Ctrl+O") - ttk.Label(bottom, textvariable=self.time_var).pack(side="left", anchor="w") - ttk.Label(bottom, textvariable=self.status_var, anchor="e").pack(side="right", fill="x", expand=True) + self.seek_step_var = tk.StringVar(value=str(self.seek_step)) + ttk.Label(controls, textvariable=self.time_var).pack(side="left", anchor="w") + ttk.Label(controls, text="快进步长:").pack(side="left", padx=(16, 4)) + self.seek_step_spin = ttk.Spinbox( + controls, + from_=1, + to=600, + increment=1, + width=5, + textvariable=self.seek_step_var, + command=self._commit_seek_step, + ) + self.seek_step_spin.pack(side="left") + ttk.Label(controls, text="秒").pack(side="left", padx=(4, 0)) + self.seek_step_spin.bind("", self._on_seek_step_return) + self.seek_step_spin.bind("", lambda _e: self._commit_seek_step()) + ttk.Label(controls, textvariable=self.status_var, anchor="e").pack(side="right", fill="x", expand=True) def _bind_shortcuts(self): shortcuts = [ @@ -981,6 +1000,10 @@ class MediaPlayerApp(tk.Tk): ("", lambda _e: self.next_track()), ("", self._on_up), ("", self._on_down), + ("", lambda _e: self.adjust_seek_step(1)), + ("", lambda _e: self.adjust_seek_step(-1)), + ("", lambda _e: self.adjust_seek_step(1)), + ("", lambda _e: self.adjust_seek_step(-1)), ("", lambda _e: self.toggle_mute()), ("", lambda _e: self.toggle_mute()), ("", lambda _e: self.open_files()), @@ -1006,20 +1029,29 @@ class MediaPlayerApp(tk.Tk): ("", lambda _e: self.play_selected()), ] for sequence, handler in shortcuts: - self.bind(sequence, handler) - self.listbox.bind(sequence, handler) + wrapped = self._break_handler(handler) + self.bind(sequence, wrapped) + self.listbox.bind(sequence, wrapped) self.listbox.bind("", lambda _e: self.play_selected()) + @staticmethod + def _break_handler(handler): + """包装快捷键回调:执行后返回 "break",阻止事件继续冒泡造成重复触发。""" + def run(event): + handler(event) + return "break" + return run + def _on_space(self, _event): self.toggle_play() return "break" def _on_left(self, _event): - self.seek_relative(-SEEK_STEP) + self.seek_relative(-self.seek_step) return "break" def _on_right(self, _event): - self.seek_relative(SEEK_STEP) + self.seek_relative(self.seek_step) return "break" def _on_up(self, _event): @@ -1118,6 +1150,26 @@ class MediaPlayerApp(tk.Tk): def _playlist_path(self): return Path.home() / ".mediaplayer_playlist.json" + def _settings_path(self): + return Path.home() / ".mediaplayer_settings.json" + + def _load_settings(self): + try: + data = json.loads(self._settings_path().read_text(encoding="utf-8")) + value = int(data.get("seek_step", SEEK_STEP)) + except Exception: + value = SEEK_STEP + return value if 1 <= value <= 600 else SEEK_STEP + + def _save_settings(self): + try: + self._settings_path().write_text( + json.dumps({"seek_step": self.seek_step}, ensure_ascii=False, indent=2), + encoding="utf-8", + ) + except Exception as exc: + print("保存设置失败:", exc) + def _save_playlist(self): try: data = {"playlist": list(self.playlist), "current_index": self.current_index} @@ -1228,6 +1280,36 @@ class MediaPlayerApp(tk.Tk): return self.seek_to(pos + seconds * 1000) + def _commit_seek_step(self): + raw = self.seek_step_var.get().strip() + try: + value = int(float(raw)) + except ValueError: + value = 0 + if not 1 <= value <= 600: + self.seek_step_var.set(str(self.seek_step)) + self.status_var.set("快进步长需为 1-600 之间的整数") + return + if value != self.seek_step: + self.seek_step = value + self._save_settings() + self.status_var.set(f"快进步长已设为 {value} 秒(已保存)") + + def adjust_seek_step(self, delta): + value = max(1, min(600, self.seek_step + delta)) + if value == self.seek_step: + self.status_var.set(f"快进步长已到{'上限' if delta > 0 else '下限'}({value} 秒)") + return + self.seek_step = value + self.seek_step_var.set(str(value)) + self._save_settings() + self.status_var.set(f"快进步长已设为 {value} 秒(已保存)") + + def _on_seek_step_return(self, _event): + self._commit_seek_step() + self.focus_set() + return "break" + def seek_to(self, ms): ms = max(0, int(ms)) if not self.engine.seek(ms):