From 2d279b8eae846b22ecf1e48a9d25c38d60acea28 Mon Sep 17 00:00:00 2001 From: Umiko Date: Sun, 24 Aug 2025 22:06:13 +0700 Subject: [PATCH] pushing new changes. --- bot/app_vars.py | 2 +- bot/commands/__init__.py | 2 + bot/commands/user_commands.py | 77 ++++++++++++++++++++++++++++++----- bot/player/__init__.py | 72 ++++++++++++++++++++++++++++++-- 4 files changed, 138 insertions(+), 15 deletions(-) diff --git a/bot/app_vars.py b/bot/app_vars.py index 3bb87df..d427794 100644 --- a/bot/app_vars.py +++ b/bot/app_vars.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: from bot.translator import Translator app_name = "WalkMan" -app_version = "2.3.5" +app_version = "2.5.0" client_name = app_name + "-V (Version)" + app_version about_text: Callable[[Translator], str] = lambda translator: translator.translate( """\ diff --git a/bot/commands/__init__.py b/bot/commands/__init__.py index 7567b7c..c6b868e 100644 --- a/bot/commands/__init__.py +++ b/bot/commands/__init__.py @@ -43,6 +43,8 @@ class CommandProcessor: "e": user_commands.EnqueueCommand, "q": user_commands.QueueCommand, "u": user_commands.PlayUrlCommand, + "eq": user_commands.EqualizerCommand, + "8d": user_commands.Audio8DCommand, "sv": user_commands.ServiceCommand, "s": user_commands.StopCommand, "b": user_commands.PreviousTrackCommand, diff --git a/bot/commands/user_commands.py b/bot/commands/user_commands.py index 88ffe68..22b91c9 100644 --- a/bot/commands/user_commands.py +++ b/bot/commands/user_commands.py @@ -455,20 +455,25 @@ class SpeedCommand(Command): @property def help(self) -> str: return self.translator.translate( - "SPEED Sets playback speed from 0.25 to 4. If no speed is given, shows current speed" + "LEVEL Sets playback speed/pitch level from -5 to 5. 0 is normal. If no level is given, shows current level." ) def __call__(self, arg: str, user: User) -> Optional[str]: if not arg: - return self.translator.translate("Current rate: {}").format( - str(self.player.get_speed()) - ) - else: - try: - self.player.set_speed(float(arg)) - except ValueError: - raise errors.InvalidArgumentError() + return self.translator.translate("Current speed level: {level}").format(level=self.player.speed_level) + if self.player.state == State.Stopped: + return self.translator.translate("Nothing is playing. This command only works during playback.") + + try: + level = int(arg) + self.player.set_speed(level) + if level == 0: + return self.translator.translate("Speed and pitch reset to normal.") + else: + return self.translator.translate("Speed level set to: {level}").format(level=self.player.speed_level) + except ValueError: + raise errors.InvalidArgumentError() class FavoritesCommand(Command): @property @@ -699,4 +704,56 @@ class QueueCommand(Command): remaining = len(queue_list) - 10 response += self.translator.translate("\n...and {count} more.").format(count=remaining) - return response \ No newline at end of file + return response + +class EqualizerCommand(Command): + @property + def help(self) -> str: + preset_list = ", ".join(self.player.eq_presets.keys()) + return self.translator.translate( + "PRESET Sets the audio equalizer. If no preset is given, shows current and available presets. Available presets: {presets}" + ).format(presets=preset_list) + + def __call__(self, arg: str, user: User) -> Optional[str]: + if not arg: + # Tampilkan status saat ini dan daftar preset yang tersedia + preset_list = ", ".join(self.player.eq_presets.keys()) + return self.translator.translate( + "Current equalizer: {current_eq}\nAvailable presets: {presets}" + ).format(current_eq=self.player.current_eq.capitalize(), presets=preset_list) + + if self.player.state == State.Stopped: + return self.translator.translate("Nothing is playing. This command only works during playback.") + + preset_to_set = arg.lower() + success = self.player.set_eq(preset_to_set) + + if success: + if preset_to_set == 'off': + return self.translator.translate("Equalizer has been turned off.") + else: + return self.translator.translate("Equalizer set to: {preset}").format(preset=preset_to_set.capitalize()) + else: + return self.translator.translate("'{preset}' is not a valid preset.").format(preset=arg) + +class Audio8DCommand(Command): + @property + def help(self) -> str: + return self.translator.translate("on/off Toggles the 8D audio effect.") + + def __call__(self, arg: str, user: User) -> Optional[str]: + arg = arg.lower() + + if self.player.state == State.Stopped: + return self.translator.translate("Nothing is playing. This command only works during playback.") + + if arg == "on": + self.player.set_8d(True) + return self.translator.translate("8D audio effect enabled.") + elif arg == "off": + self.player.set_8d(False) + return self.translator.translate("8D audio effect disabled.") + else: + # Tampilkan status saat ini jika argumen tidak valid + status = "enabled" if self.player.is_8d_enabled else "disabled" + return self.translator.translate("8D audio is currently {status}. Use '8d on' or '8d off'.").format(status=status) \ No newline at end of file diff --git a/bot/player/__init__.py b/bot/player/__init__.py index 20f74aa..e54a15f 100644 --- a/bot/player/__init__.py +++ b/bot/player/__init__.py @@ -44,6 +44,18 @@ class Player: self.mode = Mode.TrackList self.volume = self.config.default_volume self.manual_queue = False + self.eq_presets = { + 'off': '', # Untuk mematikan EQ + 'default': 'equalizer=gains=[0 0 0 0 0 0 0 0 0 0]', + 'rock': 'equalizer=gains=[4 2 1 -2 -3 -1 2 4 5 5]', + 'pop': 'equalizer=gains=[-1 1 3 4 3 1 -1 -1 -2 -2]', + 'jazz': 'equalizer=gains=[3 2 1 -1 -2 -2 1 3 4 4]', + 'classic': 'equalizer=gains=[-2 -2 -2 -1 1 3 4 4 5 5]', + 'bass': 'equalizer=gains=[6 5 4 2 1 0 0 0 0 0]' + } + self.current_eq = 'off' + self.speed_level = 0 # 0 is normal, positive is faster, negative is slower + self.is_8d_enabled = False def initialize(self) -> None: logging.debug("Initializing player") @@ -56,6 +68,21 @@ class Player: self._player.observe_property("media-title", self.on_metadata_update) logging.debug("Player callbacks registered") + def set_eq(self, preset_name: str) -> bool: + """Sets the audio equalizer preset.""" + preset_name = preset_name.lower() + if preset_name in self.eq_presets: + self.current_eq = preset_name + self._update_audio_filters() + return True + return False + + def set_8d(self, enable: bool) -> None: + """Enables or disables the 8D audio effect.""" + self.is_8d_enabled = enable + self._update_audio_filters() + + def enqueue(self, tracks: List[Track]) -> None: """Adds tracks to the queue, clearing the old playlist if needed.""" if self.state == State.Stopped or not self.track_list: @@ -193,10 +220,47 @@ class Player: def get_speed(self) -> float: return self._player.speed - def set_speed(self, arg: float) -> None: - if arg < 0.25 or arg > 4: - raise ValueError() - self._player.speed = arg + def set_speed(self, level: int) -> None: + """Sets the playback speed and pitch based on a level.""" + level = max(-5, min(level, 5)) + self.speed_level = level + + if level == 0: + self._player.speed = 1.0 + # Cukup panggil update, ia akan menghapus 'scaletempo' secara implisit + self._update_audio_filters() + else: + speed_multiplier = 1.0 + (level * 0.10) + self._player.speed = speed_multiplier + # Tambahkan filter 'scaletempo' untuk menonaktifkan koreksi pitch bawaan mpv + self._update_audio_filters(add_filters=['scaletempo']) + + def _update_audio_filters(self, add_filters: list = None, remove_filters: list = None): + """Builds and applies the audio filter string based on active effects.""" + active_filters = [] + + # 1. Cek Equalizer + if self.current_eq != 'off': + active_filters.append(self.eq_presets[self.current_eq]) + + # 2. Cek 8D Audio + if self.is_8d_enabled: + # Filter 8D. Kecepatan 0.2Hz adalah awal yang baik. + active_filters.append('pan=stereo|c0