v6d3music/v6d3music/core/queueaudio.py
2022-06-20 00:35:12 +03:00

108 lines
3.3 KiB
Python

import asyncio
from collections import deque
from io import StringIO
import discord
from ptvp35 import Db, KVJson
from v6d3music.config import myroot
from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.fill import FILL
queue_db = Db(myroot / 'queue.db', kvrequest_type=KVJson)
class QueueAudio(discord.AudioSource):
def __init__(self, guild: discord.Guild, respawned: list[YTAudio]):
self.queue: deque[YTAudio] = deque()
self.queue.extend(respawned)
self.guild = guild
@staticmethod
async def respawned(guild: discord.Guild) -> list[YTAudio]:
respawned = []
try:
for audio_respawn in queue_db.get(guild.id, []):
try:
respawned.append(await YTAudio.respawn(guild, audio_respawn))
except Exception as e:
print('audio respawn failed', e)
raise
except Exception as e:
print('queue respawn failed', e)
return respawned
@classmethod
async def create(cls, guild: discord.Guild):
return cls(guild, await QueueAudio.respawned(guild))
async def save(self):
hybernated = []
for audio in list(self.queue):
await asyncio.sleep(0.01)
hybernated.append(audio.hybernate())
queue_db.set_nowait(self.guild.id, hybernated)
def append(self, audio: YTAudio):
self.queue.append(audio)
def read(self) -> bytes:
if not self.queue:
return FILL
audio = self.queue[0]
frame = audio.read()
if len(frame) != discord.opus.Encoder.FRAME_SIZE:
self.queue.popleft().cleanup()
frame = FILL
return frame
def skip_at(self, pos: int, member: discord.Member) -> bool:
if pos < len(self.queue):
audio = self.queue[pos]
if audio.can_be_skipped_by(member):
self.queue.remove(audio)
audio.cleanup()
return True
return False
def skip_audio(self, audio: YTAudio, member: discord.Member) -> bool:
if audio in self.queue:
if audio.can_be_skipped_by(member):
self.queue.remove(audio)
audio.cleanup()
return True
return False
def clear(self, member: discord.Member) -> None:
assert_admin(member)
self.cleanup()
def swap(self, member: discord.Member, a: int, b: int) -> None:
assert_admin(member)
if max(a, b) >= len(self.queue):
return
self.queue[a], self.queue[b] = self.queue[b], self.queue[a]
def move(self, member: discord.Member, a: int, b: int) -> None:
assert_admin(member)
if max(a, b) >= len(self.queue):
return
audio = self.queue[a]
self.queue.remove(audio)
self.queue.insert(b, audio)
async def format(self) -> str:
stream = StringIO()
for i, audio in enumerate(list(self.queue)):
stream.write(f'`[{i}]` `{audio.source_timecode()} / {audio.duration()}` {audio.description}\n')
return stream.getvalue()
def cleanup(self):
self.queue.clear()
for audio in self.queue:
try:
audio.cleanup()
except ValueError:
pass