refactor: remove legacy volume_boost references in favor of generalized player effects API
This commit is contained in:
12 files changed
+27
-112
No files matched your search
@@ -55,8 +55,6 @@ export const api = {
|
|||||||
skip: () => post('/api/skip'),
|
skip: () => post('/api/skip'),
|
||||||
stop: () => post('/api/stop'),
|
stop: () => post('/api/stop'),
|
||||||
setVolume: (volume: number) => post('/api/volume', { volume }),
|
setVolume: (volume: number) => post('/api/volume', { volume }),
|
||||||
setVolumeBoost: (enabled?: boolean) =>
|
|
||||||
post<{ status: string; volume_boost: boolean }>('/api/volume_boost', { enabled }),
|
|
||||||
toggleEffect: (effectId: string, enabled?: boolean) =>
|
toggleEffect: (effectId: string, enabled?: boolean) =>
|
||||||
post<{ status: string; effect_id: string; active: boolean; active_effects: string[] }>(
|
post<{ status: string; effect_id: string; active: boolean; active_effects: string[] }>(
|
||||||
'/api/effects/toggle',
|
'/api/effects/toggle',
|
||||||
|
|||||||
@@ -65,8 +65,6 @@ export interface BotStatus {
|
|||||||
loop_single: boolean
|
loop_single: boolean
|
||||||
loop_playlist: boolean
|
loop_playlist: boolean
|
||||||
volume: number
|
volume: number
|
||||||
volume_boost?: boolean
|
|
||||||
volume_boost_multiplier?: number
|
|
||||||
effects?: PlayerEffect[]
|
effects?: PlayerEffect[]
|
||||||
active_effects?: string[]
|
active_effects?: string[]
|
||||||
current: CurrentTrack | null
|
current: CurrentTrack | null
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ EXPECTED_ENDPOINTS = {
|
|||||||
("post", "/api/skip"),
|
("post", "/api/skip"),
|
||||||
("post", "/api/stop"),
|
("post", "/api/stop"),
|
||||||
("post", "/api/volume"),
|
("post", "/api/volume"),
|
||||||
("post", "/api/volume_boost"),
|
|
||||||
("post", "/api/effects/toggle"),
|
("post", "/api/effects/toggle"),
|
||||||
("post", "/api/loop"),
|
("post", "/api/loop"),
|
||||||
("post", "/api/loop_playlist"),
|
("post", "/api/loop_playlist"),
|
||||||
|
|||||||
+17
-27
@@ -7,7 +7,6 @@ import tempfile
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from tests.fakes import FakeTrack, build_player
|
from tests.fakes import FakeTrack, build_player
|
||||||
from zyrarbot.music.guild_music_state import VOLUME_BOOST_MULTIPLIER
|
|
||||||
from zyrarbot.music.loop_mode import LoopMode
|
from zyrarbot.music.loop_mode import LoopMode
|
||||||
from zyrarbot.storage.queue_repository import QueueRepository
|
from zyrarbot.storage.queue_repository import QueueRepository
|
||||||
|
|
||||||
@@ -236,36 +235,27 @@ class MusicPlayerQueueTest(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
self.assertEqual(track.volume, 0.25)
|
self.assertEqual(track.volume, 0.25)
|
||||||
|
|
||||||
async def test_volume_boost_multiplies_effective_volume_by_multiplier(self):
|
async def test_effects_management_in_player(self):
|
||||||
player, _ = build_player()
|
player, _ = build_player()
|
||||||
player.set_volume(GUILD, 0.2)
|
self.assertFalse(player.is_effect_active(GUILD, "distortion"))
|
||||||
player.set_volume_boost(GUILD, True)
|
|
||||||
|
|
||||||
self.assertEqual(player.state(GUILD).volume, 0.2)
|
# Enable effect
|
||||||
self.assertAlmostEqual(
|
res = player.set_effect(GUILD, "distortion", True)
|
||||||
player.state(GUILD).effective_volume, 0.2 * VOLUME_BOOST_MULTIPLIER
|
|
||||||
)
|
|
||||||
|
|
||||||
track = FakeTrack("boosted")
|
|
||||||
await player.enqueue(GUILD, track)
|
|
||||||
self.assertAlmostEqual(track.volume, 0.2 * VOLUME_BOOST_MULTIPLIER)
|
|
||||||
|
|
||||||
player.set_volume(GUILD, 1.0)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
player.state(GUILD).effective_volume, 1.0 * VOLUME_BOOST_MULTIPLIER
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_toggle_volume_boost(self):
|
|
||||||
player, _ = build_player()
|
|
||||||
self.assertFalse(player.state(GUILD).volume_boost)
|
|
||||||
|
|
||||||
res = player.toggle_volume_boost(GUILD)
|
|
||||||
self.assertTrue(res)
|
self.assertTrue(res)
|
||||||
self.assertTrue(player.state(GUILD).volume_boost)
|
self.assertTrue(player.is_effect_active(GUILD, "distortion"))
|
||||||
|
self.assertIn("distortion", player.state(GUILD).active_effects)
|
||||||
|
|
||||||
|
# Toggle effect off
|
||||||
|
toggled = player.toggle_effect(GUILD, "distortion")
|
||||||
|
self.assertFalse(toggled)
|
||||||
|
self.assertFalse(player.is_effect_active(GUILD, "distortion"))
|
||||||
|
self.assertNotIn("distortion", player.state(GUILD).active_effects)
|
||||||
|
|
||||||
|
# Multiple simultaneous effects
|
||||||
|
player.set_effect(GUILD, "distortion", True)
|
||||||
|
player.set_effect(GUILD, "fourth_density", True)
|
||||||
|
self.assertEqual(player.state(GUILD).active_effects, {"distortion", "fourth_density"})
|
||||||
|
|
||||||
res2 = player.toggle_volume_boost(GUILD)
|
|
||||||
self.assertFalse(res2)
|
|
||||||
self.assertFalse(player.state(GUILD).volume_boost)
|
|
||||||
|
|
||||||
async def test_default_plays_are_applied_per_guild(self):
|
async def test_default_plays_are_applied_per_guild(self):
|
||||||
player, _ = build_player()
|
player, _ = build_player()
|
||||||
|
|||||||
@@ -40,11 +40,7 @@ class TestYTDLSource(unittest.TestCase):
|
|||||||
self.assertEqual(empty, b"")
|
self.assertEqual(empty, b"")
|
||||||
self.assertAlmostEqual(ytdl.elapsed, FRAME_LENGTH_SECONDS)
|
self.assertAlmostEqual(ytdl.elapsed, FRAME_LENGTH_SECONDS)
|
||||||
|
|
||||||
def test_read_with_volume_boost_over_2_does_not_clamp_to_2(self):
|
def test_read_with_high_volume_does_not_clamp_to_2(self):
|
||||||
# Sample value 5000:
|
|
||||||
# At volume 2.0 (discord limit): 5000 * 2 = 10000
|
|
||||||
# At volume 5.0: 5000 * 5 = 25000
|
|
||||||
# At volume 10.0: 5000 * 10 = 50000 -> clipped to 32767
|
|
||||||
pcm = struct.pack("<hh", 5000, -5000)
|
pcm = struct.pack("<hh", 5000, -5000)
|
||||||
source = DummyPCM(pcm)
|
source = DummyPCM(pcm)
|
||||||
ytdl = YTDLSource(source, data={"title": "distortion test"}, volume=5.0)
|
ytdl = YTDLSource(source, data={"title": "distortion test"}, volume=5.0)
|
||||||
@@ -54,11 +50,11 @@ class TestYTDLSource(unittest.TestCase):
|
|||||||
self.assertEqual(left, 25000)
|
self.assertEqual(left, 25000)
|
||||||
self.assertEqual(right, -25000)
|
self.assertEqual(right, -25000)
|
||||||
|
|
||||||
def test_read_distortion_clips_at_pcm_limits(self):
|
def test_read_distortion_effect_clips_at_pcm_limits(self):
|
||||||
# Overdriving large amplitude samples causes clipping / przester at [-32768, 32767]
|
|
||||||
pcm = struct.pack("<hh", 10000, -10000)
|
pcm = struct.pack("<hh", 10000, -10000)
|
||||||
source = DummyPCM(pcm)
|
source = DummyPCM(pcm)
|
||||||
ytdl = YTDLSource(source, data={"title": "przester"}, volume=10.0)
|
ytdl = YTDLSource(source, data={"title": "przester"}, volume=1.0)
|
||||||
|
ytdl.set_active_effects({"distortion"})
|
||||||
|
|
||||||
data = ytdl.read()
|
data = ytdl.read()
|
||||||
left, right = struct.unpack("<hh", data)
|
left, right = struct.unpack("<hh", data)
|
||||||
@@ -66,5 +62,6 @@ class TestYTDLSource(unittest.TestCase):
|
|||||||
self.assertEqual(right, -32768)
|
self.assertEqual(right, -32768)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -7,7 +7,6 @@ from zyrarbot.audio.effects.distortion import DistortionEffect
|
|||||||
from zyrarbot.audio.effects.fourth_density import FourthDensityEffect
|
from zyrarbot.audio.effects.fourth_density import FourthDensityEffect
|
||||||
from zyrarbot.audio.effects.muffled import MuffledEffect
|
from zyrarbot.audio.effects.muffled import MuffledEffect
|
||||||
from zyrarbot.audio.effects.tremolo import TremoloEffect
|
from zyrarbot.audio.effects.tremolo import TremoloEffect
|
||||||
from zyrarbot.music.guild_music_state import VOLUME_BOOST_MULTIPLIER
|
|
||||||
|
|
||||||
|
|
||||||
def create_default_effects() -> list[AudioEffect]:
|
def create_default_effects() -> list[AudioEffect]:
|
||||||
@@ -15,7 +14,7 @@ def create_default_effects() -> list[AudioEffect]:
|
|||||||
return [
|
return [
|
||||||
MuffledEffect(),
|
MuffledEffect(),
|
||||||
FourthDensityEffect(),
|
FourthDensityEffect(),
|
||||||
DistortionEffect(multiplier=VOLUME_BOOST_MULTIPLIER),
|
DistortionEffect(),
|
||||||
TremoloEffect(),
|
TremoloEffect(),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ from zyrarbot.music.loop_mode import LoopMode
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from zyrarbot.audio.ytdl_source import YTDLSource
|
from zyrarbot.audio.ytdl_source import YTDLSource
|
||||||
|
|
||||||
VOLUME_BOOST_MULTIPLIER: float = 10.0
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class GuildMusicState:
|
class GuildMusicState:
|
||||||
"""Stan odtwarzania jednego serwera Discord.
|
"""Stan odtwarzania jednego serwera Discord.
|
||||||
@@ -31,28 +28,12 @@ class GuildMusicState:
|
|||||||
jump_pending: bool = False
|
jump_pending: bool = False
|
||||||
notification_channel: discord.abc.Messageable | None = None
|
notification_channel: discord.abc.Messageable | None = None
|
||||||
|
|
||||||
@property
|
|
||||||
def volume_boost(self) -> bool:
|
|
||||||
return "distortion" in self.active_effects
|
|
||||||
|
|
||||||
@volume_boost.setter
|
|
||||||
def volume_boost(self, value: bool) -> None:
|
|
||||||
if value:
|
|
||||||
self.active_effects.add("distortion")
|
|
||||||
else:
|
|
||||||
self.active_effects.discard("distortion")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def effective_volume(self) -> float:
|
|
||||||
"""Zwraca głośność z uwzględnieniem trybu podbicia (mnożnik VOLUME_BOOST_MULTIPLIER)."""
|
|
||||||
return self.volume * VOLUME_BOOST_MULTIPLIER if self.volume_boost else self.volume
|
|
||||||
|
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.queue.clear()
|
self.queue.clear()
|
||||||
self.current = None
|
self.current = None
|
||||||
self.index = -1
|
self.index = -1
|
||||||
|
|
||||||
|
|
||||||
def find(self, track_id: str) -> YTDLSource | None:
|
def find(self, track_id: str) -> YTDLSource | None:
|
||||||
return next((track for track in self.queue if track.id == track_id), None)
|
return next((track for track in self.queue if track.id == track_id), None)
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class MusicPlayer:
|
|||||||
state = self.state(guild_id)
|
state = self.state(guild_id)
|
||||||
voice_client = self.voice_client(guild_id)
|
voice_client = self.voice_client(guild_id)
|
||||||
if voice_client and voice_client.source:
|
if voice_client and voice_client.source:
|
||||||
voice_client.source.volume = state.effective_volume
|
voice_client.source.volume = state.volume
|
||||||
if hasattr(voice_client.source, "set_active_effects"):
|
if hasattr(voice_client.source, "set_active_effects"):
|
||||||
voice_client.source.set_active_effects(state.active_effects)
|
voice_client.source.set_active_effects(state.active_effects)
|
||||||
|
|
||||||
@@ -236,11 +236,6 @@ class MusicPlayer:
|
|||||||
def is_effect_active(self, guild_id: int, effect_id: str) -> bool:
|
def is_effect_active(self, guild_id: int, effect_id: str) -> bool:
|
||||||
return effect_id in self.state(guild_id).active_effects
|
return effect_id in self.state(guild_id).active_effects
|
||||||
|
|
||||||
def set_volume_boost(self, guild_id: int, boost: bool) -> bool:
|
|
||||||
return self.set_effect(guild_id, "distortion", boost)
|
|
||||||
|
|
||||||
def toggle_volume_boost(self, guild_id: int) -> bool:
|
|
||||||
return self.toggle_effect(guild_id, "distortion")
|
|
||||||
|
|
||||||
|
|
||||||
def set_loop_mode(self, guild_id: int, mode: LoopMode) -> None:
|
def set_loop_mode(self, guild_id: int, mode: LoopMode) -> None:
|
||||||
@@ -350,7 +345,7 @@ class MusicPlayer:
|
|||||||
|
|
||||||
track = await self._tracks.recreate(current, loop=self._bot.loop, start_at=position)
|
track = await self._tracks.recreate(current, loop=self._bot.loop, start_at=position)
|
||||||
track.played = True
|
track.played = True
|
||||||
track.volume = state.effective_volume
|
track.volume = state.volume
|
||||||
if hasattr(track, "set_active_effects"):
|
if hasattr(track, "set_active_effects"):
|
||||||
track.set_active_effects(state.active_effects)
|
track.set_active_effects(state.active_effects)
|
||||||
track.plays_left = self.plays_left(current, guild_id)
|
track.plays_left = self.plays_left(current, guild_id)
|
||||||
@@ -458,7 +453,7 @@ class MusicPlayer:
|
|||||||
|
|
||||||
track.played = True
|
track.played = True
|
||||||
track.plays_left = self.plays_left(track, guild_id)
|
track.plays_left = self.plays_left(track, guild_id)
|
||||||
track.volume = state.effective_volume
|
track.volume = state.volume
|
||||||
if hasattr(track, "set_active_effects"):
|
if hasattr(track, "set_active_effects"):
|
||||||
track.set_active_effects(state.active_effects)
|
track.set_active_effects(state.active_effects)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeBoostRequest(BaseModel):
|
|
||||||
"""Włączenie, wyłączenie lub przełączenie trybu 500% głośności."""
|
|
||||||
|
|
||||||
enabled: bool | None = None
|
|
||||||
@@ -22,7 +22,6 @@ from zyrarbot.ui.api.requests.loop_request import LoopRequest
|
|||||||
from zyrarbot.ui.api.requests.play_file_request import PlayFileRequest
|
from zyrarbot.ui.api.requests.play_file_request import PlayFileRequest
|
||||||
from zyrarbot.ui.api.requests.play_request import PlayRequest
|
from zyrarbot.ui.api.requests.play_request import PlayRequest
|
||||||
from zyrarbot.ui.api.requests.seek_request import SeekRequest
|
from zyrarbot.ui.api.requests.seek_request import SeekRequest
|
||||||
from zyrarbot.ui.api.requests.volume_boost_request import VolumeBoostRequest
|
|
||||||
from zyrarbot.ui.api.requests.volume_request import VolumeRequest
|
from zyrarbot.ui.api.requests.volume_request import VolumeRequest
|
||||||
from zyrarbot.ui.api.status_broadcaster import StatusBroadcaster
|
from zyrarbot.ui.api.status_broadcaster import StatusBroadcaster
|
||||||
|
|
||||||
@@ -175,21 +174,6 @@ async def set_volume(
|
|||||||
return {"status": "success", "volume": request.volume}
|
return {"status": "success", "volume": request.volume}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/volume_boost")
|
|
||||||
async def set_volume_boost(
|
|
||||||
request: VolumeBoostRequest | None = None,
|
|
||||||
guild: discord.Guild = Depends(get_active_guild),
|
|
||||||
player: MusicPlayer = Depends(get_player),
|
|
||||||
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
|
|
||||||
) -> dict[str, Any]:
|
|
||||||
if request is not None and request.enabled is not None:
|
|
||||||
boost = player.set_volume_boost(guild.id, request.enabled)
|
|
||||||
else:
|
|
||||||
boost = player.toggle_volume_boost(guild.id)
|
|
||||||
await broadcaster.broadcast()
|
|
||||||
return {"status": "success", "volume_boost": boost}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/effects/toggle")
|
@router.post("/effects/toggle")
|
||||||
async def toggle_effect(
|
async def toggle_effect(
|
||||||
request: EffectToggleRequest,
|
request: EffectToggleRequest,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import discord
|
|||||||
|
|
||||||
from zyrarbot.audio.effects import get_available_effects_metadata
|
from zyrarbot.audio.effects import get_available_effects_metadata
|
||||||
from zyrarbot.audio.ytdl_source import YTDLSource
|
from zyrarbot.audio.ytdl_source import YTDLSource
|
||||||
from zyrarbot.music.guild_music_state import VOLUME_BOOST_MULTIPLIER
|
|
||||||
from zyrarbot.music.music_player import MusicPlayer
|
from zyrarbot.music.music_player import MusicPlayer
|
||||||
from zyrarbot.music.notification_channel_resolver import NotificationChannelResolver
|
from zyrarbot.music.notification_channel_resolver import NotificationChannelResolver
|
||||||
from zyrarbot.ui.bot.guild_resolver import GuildResolver
|
from zyrarbot.ui.bot.guild_resolver import GuildResolver
|
||||||
@@ -51,8 +50,6 @@ class StatusService:
|
|||||||
"loop_single": False,
|
"loop_single": False,
|
||||||
"loop_playlist": False,
|
"loop_playlist": False,
|
||||||
"volume": 100,
|
"volume": 100,
|
||||||
"volume_boost": False,
|
|
||||||
"volume_boost_multiplier": VOLUME_BOOST_MULTIPLIER,
|
|
||||||
"effects": [{**meta, "active": False} for meta in get_available_effects_metadata()],
|
"effects": [{**meta, "active": False} for meta in get_available_effects_metadata()],
|
||||||
"active_effects": [],
|
"active_effects": [],
|
||||||
"current": None,
|
"current": None,
|
||||||
@@ -76,8 +73,6 @@ class StatusService:
|
|||||||
"loop_single": state.loop_mode.loops_track,
|
"loop_single": state.loop_mode.loops_track,
|
||||||
"loop_playlist": state.loop_mode.loops_queue,
|
"loop_playlist": state.loop_mode.loops_queue,
|
||||||
"volume": int(state.volume * 100),
|
"volume": int(state.volume * 100),
|
||||||
"volume_boost": state.volume_boost,
|
|
||||||
"volume_boost_multiplier": VOLUME_BOOST_MULTIPLIER,
|
|
||||||
"effects": [
|
"effects": [
|
||||||
{**meta, "active": meta["id"] in state.active_effects}
|
{**meta, "active": meta["id"] in state.active_effects}
|
||||||
for meta in get_available_effects_metadata()
|
for meta in get_available_effects_metadata()
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
|
|
||||||
from zyrarbot.music.guild_music_state import VOLUME_BOOST_MULTIPLIER
|
|
||||||
from zyrarbot.music.loop_mode import LoopMode
|
from zyrarbot.music.loop_mode import LoopMode
|
||||||
from zyrarbot.ui.bot.cogs.common import MusicCogBase
|
from zyrarbot.ui.bot.cogs.common import MusicCogBase
|
||||||
from zyrarbot.ui.bot.discord_theme import message_embed
|
from zyrarbot.ui.bot.discord_theme import message_embed
|
||||||
@@ -112,19 +111,6 @@ class ControlCog(MusicCogBase, name="Control"):
|
|||||||
embed=message_embed("Głośność", f"Ustawiono głośność na {volume}%.")
|
embed=message_embed("Głośność", f"Ustawiono głośność na {volume}%.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@app_commands.command(name="volumeboost", description="Przełącz tryb podbicia głośności")
|
|
||||||
async def volume_boost(self, interaction: discord.Interaction) -> None:
|
|
||||||
boost = self.player.toggle_volume_boost(interaction.guild.id)
|
|
||||||
await self.player.trigger_state_change()
|
|
||||||
boost_percent = int(VOLUME_BOOST_MULTIPLIER * 100)
|
|
||||||
status_text = f"włączony ({boost_percent}%)" if boost else "wyłączony (100%)"
|
|
||||||
await interaction.response.send_message(
|
|
||||||
embed=message_embed(
|
|
||||||
"Podbicie głośności",
|
|
||||||
f"⚡ Tryb podbicia głośności ({boost_percent}%) jest teraz **{status_text}**.",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@app_commands.command(name="efekt", description="Włącz lub wyłącz efekt dźwiękowy odtwarzacza")
|
@app_commands.command(name="efekt", description="Włącz lub wyłącz efekt dźwiękowy odtwarzacza")
|
||||||
@app_commands.describe(nazwa="Wybierz efekt audio do przełączenia")
|
@app_commands.describe(nazwa="Wybierz efekt audio do przełączenia")
|
||||||
@app_commands.choices(
|
@app_commands.choices(
|
||||||
|
|||||||
Reference in new issue
Block a user