108 lines
3.3 KiB
Python
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
|