192 lines
6.7 KiB
Python
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}
|