Files
zyrarbot/zyrarbot/ui/api/routers/playback_router.py
T

192 lines
6.7 KiB
Python

from __future__ import annotations
import logging
from typing import Any
import discord
from fastapi import APIRouter, Depends, HTTPException
from zyrarbot.audio.track_factory import TrackFactory
from zyrarbot.music.loop_mode import LoopMode
from zyrarbot.music.music_player import MusicPlayer
from zyrarbot.storage.uploads_repository import UploadsRepository
from zyrarbot.ui.api.dependencies import (
get_active_guild,
get_broadcaster,
get_player,
get_tracks,
get_uploads,
)
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_request import PlayRequest
from zyrarbot.ui.api.requests.volume_request import VolumeRequest
from zyrarbot.ui.api.status_broadcaster import StatusBroadcaster
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api", tags=["playback"])
def _require_voice(player: MusicPlayer, guild: discord.Guild) -> None:
if player.voice_client(guild.id) is None:
raise HTTPException(
status_code=400, detail="Bot is not connected to a voice channel on this server"
)
@router.post("/play")
async def play(
request: PlayRequest,
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
tracks: TrackFactory = Depends(get_tracks),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, Any]:
_require_voice(player, guild)
try:
track = await tracks.resolve(request.query)
result = await player.enqueue(guild.id, track, from_web=True)
except Exception as error:
logger.error("Nie udało się dodać utworu z panelu: %s", error)
raise HTTPException(status_code=500, detail=str(error)) from error
await broadcaster.broadcast()
return result
@router.post("/play_file")
async def play_file(
request: PlayFileRequest,
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
tracks: TrackFactory = Depends(get_tracks),
uploads: UploadsRepository = Depends(get_uploads),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, Any]:
_require_voice(player, guild)
if not uploads.exists(request.filename, guild.id):
raise HTTPException(status_code=404, detail="Plik nie istnieje na serwerze")
try:
# Odświeżenie mtime utrzymuje plik na szczycie listy ostatnio używanych.
uploads.touch(request.filename, guild.id)
track = await tracks.from_file(uploads.resolve(request.filename, guild.id))
result = await player.enqueue(guild.id, track, from_web=True)
except Exception as error:
logger.error("Nie udało się odtworzyć pliku z panelu: %s", error)
raise HTTPException(status_code=500, detail=str(error)) from error
await broadcaster.broadcast()
return result
@router.post("/pause")
async def pause(
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, str]:
voice_client = player.voice_client(guild.id)
if not voice_client or not voice_client.is_playing():
raise HTTPException(status_code=400, detail="Bot is not playing on this server")
voice_client.pause()
await broadcaster.broadcast()
return {"status": "paused"}
@router.post("/resume")
async def resume(
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, str]:
voice_client = player.voice_client(guild.id)
if not voice_client or not voice_client.is_paused():
raise HTTPException(status_code=400, detail="Bot is not paused on this server")
voice_client.resume()
await broadcaster.broadcast()
return {"status": "resumed"}
@router.post("/skip")
async def skip(
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, str]:
state = player.state(guild.id)
if state.loop_mode.loops_track:
state.loop_mode = LoopMode.OFF
if not player.is_active(guild.id):
raise HTTPException(status_code=400, detail="Bot is not playing anything on this server")
player.voice_client(guild.id).stop()
await broadcaster.broadcast()
return {"status": "skipped"}
@router.post("/stop")
async def stop(
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, str]:
player.finish_current(guild.id)
player.save_queue(guild.id)
if player.is_active(guild.id):
player.voice_client(guild.id).stop()
player.clear(guild.id)
await broadcaster.broadcast()
return {"status": "stopped"}
@router.post("/volume")
async def set_volume(
request: VolumeRequest,
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, Any]:
if not 0 <= request.volume <= 100:
raise HTTPException(status_code=400, detail="Volume must be between 0 and 100")
player.set_volume(guild.id, request.volume / 100)
await broadcaster.broadcast()
return {"status": "success", "volume": request.volume}
@router.post("/loop")
async def set_loop(
request: LoopRequest,
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, Any]:
player.set_loop_mode(guild.id, LoopMode.TRACK if request.loop else LoopMode.OFF)
await broadcaster.broadcast()
return {"status": "success", "loop": request.loop}
@router.post("/loop_playlist")
async def toggle_loop_playlist(
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, Any]:
enabled = not player.state(guild.id).loop_mode.loops_queue
player.set_loop_mode(guild.id, LoopMode.QUEUE if enabled else LoopMode.OFF)
await broadcaster.broadcast()
return {"status": "success", "loop_playlist": enabled}
@router.post("/loop_cycle")
async def cycle_loop(
guild: discord.Guild = Depends(get_active_guild),
player: MusicPlayer = Depends(get_player),
broadcaster: StatusBroadcaster = Depends(get_broadcaster),
) -> dict[str, Any]:
mode = player.cycle_loop_mode(guild.id)
await broadcaster.broadcast()
return {"status": "success", "mode": mode.value}