pushing new changes.
Some checks failed
Build-nightly / docker (push) Has been cancelled

This commit is contained in:
Umiko 2025-08-24 22:06:13 +07:00
parent 5d2065ae85
commit 2d279b8eae
4 changed files with 138 additions and 15 deletions

View File

@ -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(
"""\

View File

@ -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,

View File

@ -455,21 +455,26 @@ 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:
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:
self.player.set_speed(float(arg))
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
def help(self) -> str:
@ -700,3 +705,55 @@ class QueueCommand(Command):
response += self.translator.translate("\n...and {count} more.").format(count=remaining)
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)

View File

@ -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<c0+c1*sin(2*PI*t*0.2)|c1<c1-c0*sin(2*PI*t*0.2)')
# 3. Cek efek terkait kecepatan (misalnya, 'scaletempo' dari set_speed)
# Ini memungkinkan kita menambahkan filter sementara
if add_filters:
for f in add_filters:
# Hindari duplikasi
if not any(f.split('=')[0] in af for af in active_filters):
active_filters.append(f)
# Gabungkan semua filter aktif dengan koma
final_filter_string = ",".join(filter for filter in active_filters if filter)
# Terapkan ke mpv
self._player.af = final_filter_string
def seek_back(self, step: Optional[float] = None) -> None:
step = step if step else self.config.seek_step