servicing

This commit is contained in:
AF 2022-12-25 02:55:21 +00:00
parent b2dc8c5edb
commit 872271fc6f
19 changed files with 201 additions and 99 deletions

View File

@ -280,6 +280,10 @@ class MusicAppFactory(AppFactory):
except Api.MisusedApi as e: except Api.MisusedApi as e:
return web.json_response(e.json(), status=404) return web.json_response(e.json(), status=404)
@routes.get('/whaturl/')
async def whaturl(request: web.Request) -> web.StreamResponse:
return web.json_response(str(request.url))
class AppContext: class AppContext:
def __init__(self, mainservice: MainService) -> None: def __init__(self, mainservice: MainService) -> None:

View File

@ -3,19 +3,20 @@ from typing import Callable
from v6d3music.core.default_effects import * from v6d3music.core.default_effects import *
from v6d3music.core.mainservice import MainService from v6d3music.core.mainservice import MainService
from v6d3music.core.yt_audios import yt_audios
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.catch import catch from v6d3music.utils.catch import *
from v6d3music.utils.effects_for_preset import effects_for_preset from v6d3music.utils.effects_for_preset import *
from v6d3music.utils.options_for_effects import options_for_effects from v6d3music.utils.options_for_effects import *
from v6d3music.utils.presets import allowed_presets from v6d3music.utils.presets import allowed_presets
from v6d2ctx.at_of import AtOf from v6d2ctx.at_of import AtOf
from v6d2ctx.context import Context, Explicit, command_type from v6d2ctx.context import Context, Explicit, command_type
from v6d2ctx.lock_for import lock_for from v6d2ctx.lock_for import lock_for
__all__ = ('get_of',)
def get_of(mainservice: MainService, defaulteffects: DefaultEffects) -> Callable[[str], command_type]:
def get_of(mainservice: MainService) -> Callable[[str], command_type]:
at_of: AtOf[str, command_type] = AtOf() at_of: AtOf[str, command_type] = AtOf()
at, of = at_of() at, of = at_of()
@ -47,7 +48,7 @@ def get_of(mainservice: MainService, defaulteffects: DefaultEffects) -> Callable
if len(ctx.message.attachments) > 1: if len(ctx.message.attachments) > 1:
raise Explicit('no more than one attachment') raise Explicit('no more than one attachment')
args = [ctx.message.attachments[0].url] + args args = [ctx.message.attachments[0].url] + args
async for audio in yt_audios(mainservice.caching, defaulteffects, ctx, args): async for audio in mainservice.yt_audios(ctx, args):
queue.append(audio) queue.append(audio)
await ctx.reply('done') await ctx.reply('done')
@ -136,12 +137,12 @@ def get_of(mainservice: MainService, defaulteffects: DefaultEffects) -> Callable
case ['none']: case ['none']:
effects = None effects = None
case []: case []:
await ctx.reply(f'current default effects: {defaulteffects.get(ctx.guild.id)}') await ctx.reply(f'current default effects: {mainservice.defaulteffects.get(ctx.guild.id)}')
return return
case _: case _:
raise Explicit('misformatted') raise Explicit('misformatted')
assert_admin(ctx.member) assert_admin(ctx.member)
await defaulteffects.set(ctx.guild.id, effects) await mainservice.defaulteffects.set(ctx.guild.id, effects)
await ctx.reply(f'effects set to `{effects}`') await ctx.reply(f'effects set to `{effects}`')
@at('repeat') @at('repeat')
@ -155,14 +156,13 @@ def get_of(mainservice: MainService, defaulteffects: DefaultEffects) -> Callable
raise Explicit('misformatted') raise Explicit('misformatted')
assert_admin(ctx.member) assert_admin(ctx.member)
queue = await mainservice.context(ctx, create=False, force_play=False).queue() queue = await mainservice.context(ctx, create=False, force_play=False).queue()
if not queue.queue: queue.repeat(n)
raise Explicit('empty queue')
if n > 99: @at('shuffle')
raise Explicit('too long') async def shuffle(ctx: Context, args: list[str]):
audio = queue.queue[0] assert_admin(ctx.member)
for _ in range(n): queue = await mainservice.context(ctx, create=False, force_play=False).queue()
queue.queue.insert(1, audio.copy()) queue.shuffle()
queue.update_sources()
@at('branch') @at('branch')
async def branch(ctx: Context, args: list[str]): async def branch(ctx: Context, args: list[str]):

View File

@ -1,19 +1,18 @@
import string import string
from typing import Any, Optional
from v6d2ctx.context import Context, Explicit, escape
from v6d3music.core.real_url import real_url from v6d3music.core.real_url import real_url
from v6d3music.core.caching import Caching from v6d3music.core.ytaservicing import *
from v6d3music.core.ytaudio import YTAudio from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.argctx import InfoCtx
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.options_for_effects import options_for_effects from v6d3music.utils.options_for_effects import options_for_effects
from v6d3music.utils.presets import allowed_effects from v6d3music.utils.presets import allowed_effects
from v6d3music.utils.argctx import InfoCtx
from v6d2ctx.context import Context, Explicit, escape
async def create_ytaudio( async def create_ytaudio(
caching: Caching, ctx: Context, it: InfoCtx servicing: YTAServicing, ctx: Context, it: InfoCtx
) -> YTAudio: ) -> YTAudio:
assert ctx.member is not None assert ctx.member is not None
if it.effects: if it.effects:
@ -25,8 +24,8 @@ async def create_ytaudio(
else: else:
options = None options = None
return YTAudio( return YTAudio(
caching, servicing,
await real_url(caching, it.info['url'], False, it.tor), await real_url(servicing.caching, it.info['url'], False, it.tor),
it.info['url'], it.info['url'],
f'{escape(it.info.get("title", "unknown"))} `Rby` {ctx.member}', f'{escape(it.info.get("title", "unknown"))} `Rby` {ctx.member}',
options, options,

View File

@ -1,11 +1,13 @@
import discord import discord
from v6d3music.core.caching import Caching from v6d3music.core.ytaservicing import *
from v6d3music.core.queueaudio import QueueAudio from v6d3music.core.queueaudio import *
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
from ptvp35 import * from ptvp35 import *
from v6d2ctx.context import Explicit from v6d2ctx.context import Explicit
__all__ = ('MainAudio',)
class MainAudio(discord.PCMVolumeTransformer): class MainAudio(discord.PCMVolumeTransformer):
def __init__(self, db: DbConnection, queue: QueueAudio, volume: float): def __init__(self, db: DbConnection, queue: QueueAudio, volume: float):
@ -23,5 +25,5 @@ class MainAudio(discord.PCMVolumeTransformer):
await self.db.set(member.guild.id, volume) await self.db.set(member.guild.id, volume)
@classmethod @classmethod
async def create(cls, caching: Caching, db: DbConnection, queues: DbConnection, guild: discord.Guild) -> 'MainAudio': async def create(cls, servicing: YTAServicing, db: DbConnection, queues: DbConnection, guild: discord.Guild) -> 'MainAudio':
return cls(db, await QueueAudio.create(caching, queues, guild), volume=db.get(guild.id, 0.2)) return cls(db, await QueueAudio.create(servicing, queues, guild), volume=db.get(guild.id, 0.2))

View File

@ -1,22 +1,33 @@
import asyncio import asyncio
import traceback import traceback
from contextlib import AsyncExitStack from contextlib import AsyncExitStack
from typing import AsyncIterable, TypeVar
import discord import discord
from v6d3music.config import myroot from v6d3music.config import myroot
from v6d3music.core.caching import Caching from v6d3music.core.caching import *
from v6d3music.core.mainaudio import MainAudio from v6d3music.core.default_effects import *
from v6d3music.core.queueaudio import QueueAudio from v6d3music.core.mainaudio import *
from v6d3music.core.queueaudio import *
from v6d3music.core.ystate import *
from v6d3music.core.ytaservicing import *
from v6d3music.core.ytaudio import *
from v6d3music.processing.pool import *
from v6d3music.utils.argctx import *
from ptvp35 import * from ptvp35 import *
from v6d2ctx.context import Context, Explicit from v6d2ctx.context import Context, Explicit
from v6d2ctx.lock_for import lock_for from v6d2ctx.lock_for import lock_for
T = TypeVar('T')
class MainService: class MainService:
def __init__(self, client: discord.Client) -> None: def __init__(self, defaulteffects: DefaultEffects, client: discord.Client) -> None:
self.defaulteffects = defaulteffects
self.client = client self.client = client
self.mains: dict[discord.Guild, MainAudio] = {} self.mains: dict[discord.Guild, MainAudio] = {}
self.restore_lock = asyncio.Lock()
@staticmethod @staticmethod
async def raw_vc_for_member(member: discord.Member) -> discord.VoiceClient: async def raw_vc_for_member(member: discord.Member) -> discord.VoiceClient:
@ -51,16 +62,18 @@ class MainService:
return self.descriptor(create=create, force_play=force_play).context(ctx) return self.descriptor(create=create, force_play=force_play).context(ctx)
async def create(self, guild: discord.Guild) -> MainAudio: async def create(self, guild: discord.Guild) -> MainAudio:
return await MainAudio.create(self.caching, self.__volumes, self.queues, guild) return await MainAudio.create(self.__servicing, self.__volumes, self.__queues, guild)
async def __aenter__(self) -> 'MainService': async def __aenter__(self) -> 'MainService':
async with AsyncExitStack() as es: async with AsyncExitStack() as es:
self.__volumes = await es.enter_async_context(DbFactory(myroot / 'volume.db', kvfactory=KVJson())) self.__volumes = await es.enter_async_context(DbFactory(myroot / 'volume.db', kvfactory=KVJson()))
self.queues = await es.enter_async_context(DbFactory(myroot / 'queue.db', kvfactory=KVJson())) self.__queues = await es.enter_async_context(DbFactory(myroot / 'queue.db', kvfactory=KVJson()))
self.caching = await es.enter_async_context(Caching()) self.__caching = await es.enter_async_context(Caching())
self.__pool = await es.enter_async_context(Pool(5))
self.__servicing = YTAServicing(self.__caching, self.__pool)
self.__vcs_restored: asyncio.Future[None] = asyncio.Future() self.__vcs_restored: asyncio.Future[None] = asyncio.Future()
self.__es = es.pop_all()
self.__save_task = asyncio.create_task(self.save_daemon()) self.__save_task = asyncio.create_task(self.save_daemon())
self.__es = es.pop_all()
return self return self
async def __aexit__(self, exc_type, exc_val, exc_tb): async def __aexit__(self, exc_type, exc_val, exc_tb):
@ -83,10 +96,10 @@ class MainService:
if vc.is_playing(): if vc.is_playing():
if vc.guild is not None and vc.channel is not None: if vc.guild is not None and vc.channel is not None:
vcs.append((vc.guild.id, vc.channel.id, vc.is_paused())) vcs.append((vc.guild.id, vc.channel.id, vc.is_paused()))
self.queues.set_nowait('vcs', vcs) self.__queues.set_nowait('vcs', vcs)
async def save_commit(self) -> None: async def save_commit(self) -> None:
await self.queues.commit() await self.__queues.commit()
async def _save_all(self, delay: bool, save_playing: bool) -> None: async def _save_all(self, delay: bool, save_playing: bool) -> None:
await self.save_queues(delay) await self.save_queues(delay)
@ -152,7 +165,7 @@ class MainService:
print(f'vc restored {vcgid} {vccid}') print(f'vc restored {vcgid} {vccid}')
async def restore_vcs(self) -> None: async def restore_vcs(self) -> None:
vcs: list[tuple[int, int, bool]] = self.queues.get('vcs', []) vcs: list[tuple[int, int, bool]] = self.__queues.get('vcs', [])
try: try:
tasks = [] tasks = []
for vcgid, vccid, vc_is_paused in vcs: for vcgid, vccid, vc_is_paused in vcs:
@ -163,10 +176,16 @@ class MainService:
self.__vcs_restored.set_result(None) self.__vcs_restored.set_result(None)
async def restore(self) -> None: async def restore(self) -> None:
async with lock_for('vcs_restored', '...'): async with self.restore_lock:
if not self.__vcs_restored.done(): if not self.__vcs_restored.done():
await self.restore_vcs() await self.restore_vcs()
async def yt_audios(self, ctx: Context, args: list[str]) -> AsyncIterable[YTAudio]:
assert ctx.guild is not None
argctx = ArgCtx(self.defaulteffects.get(ctx.guild.id), args)
async for audio in YState(self.__servicing, self.__pool, ctx, argctx.sources).iterate():
yield audio
class MainDescriptor: class MainDescriptor:
def __init__(self, service: MainService, *, create: bool, force_play: bool) -> None: def __init__(self, service: MainService, *, create: bool, force_play: bool) -> None:

View File

@ -1,15 +1,21 @@
import asyncio import asyncio
import random
from collections import deque from collections import deque
from io import StringIO from io import StringIO
from typing import MutableSequence
import discord import discord
from v6d3music.core.caching import Caching from v6d2ctx.context import Explicit
from v6d3music.core.ytaservicing import *
from v6d3music.core.ytaudio import YTAudio from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.fill import FILL from v6d3music.utils.fill import FILL
from ptvp35 import * from ptvp35 import *
__all__ = ('QueueAudio',)
PRE_SET_LENGTH = 24 PRE_SET_LENGTH = 24
@ -30,12 +36,12 @@ class QueueAudio(discord.AudioSource):
return return
@staticmethod @staticmethod
async def respawned(caching: Caching, db: DbConnection, guild: discord.Guild) -> list[YTAudio]: async def respawned(servicing: YTAServicing, db: DbConnection, guild: discord.Guild) -> list[YTAudio]:
respawned = [] respawned = []
try: try:
for audio_respawn in db.get(guild.id, []): for audio_respawn in db.get(guild.id, []):
try: try:
respawned.append(await YTAudio.respawn(caching, guild, audio_respawn)) respawned.append(await YTAudio.respawn(servicing, guild, audio_respawn))
except Exception as e: except Exception as e:
print('audio respawn failed', e) print('audio respawn failed', e)
raise raise
@ -44,8 +50,8 @@ class QueueAudio(discord.AudioSource):
return respawned return respawned
@classmethod @classmethod
async def create(cls, caching: Caching, db: DbConnection, guild: discord.Guild) -> 'QueueAudio': async def create(cls, servicing: YTAServicing, db: DbConnection, guild: discord.Guild) -> 'QueueAudio':
return cls(db, guild, await cls.respawned(caching, db, guild)) return cls(db, guild, await cls.respawned(servicing, db, guild))
async def save(self, delay: bool) -> None: async def save(self, delay: bool) -> None:
hybernated = [] hybernated = []
@ -143,3 +149,41 @@ class QueueAudio(discord.AudioSource):
import random import random
audios = list(self.queue) audios = list(self.queue)
return [await audio.pubjson(member) for audio, _ in zip(audios, range(limit))] return [await audio.pubjson(member) for audio, _ in zip(audios, range(limit))]
def repeat(self, n: int) -> None:
if not self.queue:
raise Explicit('empty queue')
if n > 99:
raise Explicit('too long')
audio = self.queue[0]
for _ in range(n):
self.queue.insert(1, audio.copy())
self.update_sources()
def shuffle(self) -> None:
try:
random.shuffle(ForwardView(self.queue))
except:
from traceback import print_exc
print_exc()
self.update_sources()
class ForwardView(MutableSequence[YTAudio]):
def __init__(self, sequence: MutableSequence[YTAudio]) -> None:
self.sequence = sequence
def __len__(self) -> int:
return max(0, self.sequence.__len__() - 1)
def __setitem__(self, index: int, value: YTAudio) -> None:
self.sequence.__setitem__(index + 1, value)
def __getitem__(self, index: int) -> YTAudio:
return self.sequence.__getitem__(index + 1)
def __delitem__(self, index: int | slice) -> None:
self.sequence.__delitem__(index)
def insert(self, index: int, value: YTAudio) -> None:
self.sequence.insert(index, value)

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
import os import os
from v6d3music.core.caching import Caching from v6d3music.core.caching import *
from v6d3music.utils.bytes_hash import bytes_hash from v6d3music.utils.bytes_hash import bytes_hash
from v6d3music.utils.tor_prefix import tor_prefix from v6d3music.utils.tor_prefix import tor_prefix

View File

@ -3,6 +3,7 @@ from collections import deque
from contextlib import AsyncExitStack from contextlib import AsyncExitStack
from typing import AsyncIterable, Iterable from typing import AsyncIterable, Iterable
from v6d3music.core.ytaservicing import *
from v6d3music.core.create_ytaudio import * from v6d3music.core.create_ytaudio import *
from v6d3music.core.ytaudio import * from v6d3music.core.ytaudio import *
from v6d3music.processing.pool import * from v6d3music.processing.pool import *
@ -14,8 +15,8 @@ __all__ = ('YState',)
class YState: class YState:
def __init__(self, caching: Caching, pool: Pool, ctx: Context, sources: Iterable[UrlCtx]) -> None: def __init__(self, servicing: YTAServicing, pool: Pool, ctx: Context, sources: Iterable[UrlCtx]) -> None:
self.caching = caching self.servicing = servicing
self.pool = pool self.pool = pool
self.ctx = ctx self.ctx = ctx
self.sources: deque[UrlCtx] = deque(sources) self.sources: deque[UrlCtx] = deque(sources)
@ -53,7 +54,7 @@ class YState:
async def result(self, entry: InfoCtx) -> YTAudio | None: async def result(self, entry: InfoCtx) -> YTAudio | None:
try: try:
return await create_ytaudio(self.caching, self.ctx, entry) return await create_ytaudio(self.servicing, self.ctx, entry)
except Exception: except Exception:
if not entry.ignore: if not entry.ignore:
raise raise

View File

@ -1,18 +0,0 @@
from typing import AsyncIterable
from v6d3music.core.default_effects import *
from v6d3music.core.ystate import *
from v6d3music.core.ytaudio import *
from v6d3music.core.caching import Caching
from v6d3music.processing.pool import *
from v6d3music.utils.argctx import *
from v6d2ctx.context import Context
async def yt_audios(caching: Caching, defaulteffects: DefaultEffects, ctx: Context, args: list[str]) -> AsyncIterable[YTAudio]:
assert ctx.guild is not None
argctx = ArgCtx(defaulteffects.get(ctx.guild.id), args)
async with Pool(5) as pool:
async for audio in YState(caching, pool, ctx, argctx.sources).iterate():
yield audio

View File

@ -0,0 +1,11 @@
from v6d3music.core.caching import *
from v6d3music.processing.abstractrunner import *
__all__ = ('YTAServicing',)
class YTAServicing:
def __init__(self, caching: Caching, runner: AbstractRunner) -> None:
self.caching = caching
self.runner = runner

View File

@ -5,25 +5,22 @@ from typing import Optional
import discord import discord
from v6d3music.core.ffmpegnormalaudio import FFmpegNormalAudio from v6d3music.core.ffmpegnormalaudio import FFmpegNormalAudio
from v6d3music.core.real_url import real_url from v6d3music.core.real_url import real_url
from v6d3music.core.caching import Caching
from v6d3music.utils.fill import FILL from v6d3music.utils.fill import FILL
from v6d3music.utils.sparq import sparq from v6d3music.utils.sparq import sparq
from v6d3music.utils.tor_prefix import tor_prefix from v6d3music.utils.tor_prefix import tor_prefix
from v6d3music.core.ytaservicing import *
from v6d2ctx.context import Explicit from v6d2ctx.context import Explicit
__all__ = ('YTAudio',) __all__ = ('YTAudio',)
semaphore = asyncio.BoundedSemaphore(5)
class YTAudio(discord.AudioSource): class YTAudio(discord.AudioSource):
source: FFmpegNormalAudio source: FFmpegNormalAudio
def __init__( def __init__(
self, self,
caching: Caching, servicing: YTAServicing,
url: str, url: str,
origin: str, origin: str,
description: str, description: str,
@ -35,7 +32,7 @@ class YTAudio(discord.AudioSource):
*, *,
stop_at: int | None = None stop_at: int | None = None
): ):
self.caching = caching self.servicing = servicing
self.url = url self.url = url
self.origin = origin self.origin = origin
self.description = description self.description = description
@ -112,8 +109,7 @@ class YTAudio(discord.AudioSource):
self._durations[url] = (await ap.stdout.read()).decode().strip().split('.')[0] self._durations[url] = (await ap.stdout.read()).decode().strip().split('.')[0]
async def update_duration(self): async def update_duration(self):
async with semaphore: await self.servicing.runner.run(self._update_duration())
await self._update_duration()
def duration(self) -> str: def duration(self) -> str:
duration = self._durations.get(self.url) duration = self._durations.get(self.url)
@ -184,7 +180,7 @@ class YTAudio(discord.AudioSource):
} }
@classmethod @classmethod
async def respawn(cls, caching: Caching, guild: discord.Guild, respawn: dict) -> 'YTAudio': async def respawn(cls, servicing: YTAServicing, guild: discord.Guild, respawn: dict) -> 'YTAudio':
member_id: int | None = respawn['rby'] member_id: int | None = respawn['rby']
if member_id is None: if member_id is None:
member = None member = None
@ -197,7 +193,7 @@ class YTAudio(discord.AudioSource):
except discord.NotFound: except discord.NotFound:
member = None member = None
audio = YTAudio( audio = YTAudio(
caching, servicing,
respawn['url'], respawn['url'],
respawn['origin'], respawn['origin'],
respawn['description'], respawn['description'],
@ -213,7 +209,7 @@ class YTAudio(discord.AudioSource):
async def regenerate(self): async def regenerate(self):
try: try:
print(f'regenerating {self.origin}') print(f'regenerating {self.origin}')
self.url = await real_url(self.caching, self.origin, True, self.tor) self.url = await real_url(self.servicing.caching, self.origin, True, self.tor)
if hasattr(self, 'source'): if hasattr(self, 'source'):
self.source.cleanup() self.source.cleanup()
self.set_source() self.set_source()
@ -232,7 +228,7 @@ class YTAudio(discord.AudioSource):
def copy(self) -> 'YTAudio': def copy(self) -> 'YTAudio':
return YTAudio( return YTAudio(
self.caching, self.servicing,
self.url, self.url,
self.origin, self.origin,
self.description, self.description,
@ -247,7 +243,7 @@ class YTAudio(discord.AudioSource):
raise Explicit('already branched') raise Explicit('already branched')
self.stop_at = stop_at = self.already_read + 50 self.stop_at = stop_at = self.already_read + 50
audio = YTAudio( audio = YTAudio(
self.caching, self.servicing,
self.url, self.url,
self.origin, self.origin,
self.description, self.description,

View File

@ -166,7 +166,7 @@ const aUpdateQueueSetup = async (el) => {
while (true) { while (true) {
await sleep(2); await sleep(2);
if (queue !== null && queue.queuejson.length > 100) { if (queue !== null && queue.queuejson.length > 100) {
await sleep(queue.queuejson.length / 100); await sleep((queue.queuejson.length - 100) / 200);
} }
const newQueue = await aQueue(); const newQueue = await aQueue();
await aUpdateQueueOnce(newQueue, el); await aUpdateQueueOnce(newQueue, el);

View File

@ -0,0 +1,13 @@
from typing import Any, Coroutine, TypeVar
from abc import ABC, abstractmethod
T = TypeVar('T')
__all__ = ('AbstractRunner',)
class AbstractRunner(ABC):
@abstractmethod
async def run(self, coro: Coroutine[Any, Any, T]) -> T:
raise NotImplementedError

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
from typing import Union from typing import Any, Coroutine, Generic, TypeVar, Union
from v6d3music.processing.yprocess import * from .abstractrunner import AbstractRunner
__all__ = ('Job', 'Pool', 'JobDescriptor',) __all__ = ('Job', 'Pool', 'JobDescriptor',)
@ -164,7 +164,23 @@ class Working:
return self.__worker.busy() return self.__worker.busy()
class Pool: T = TypeVar('T')
class CoroJD(JobDescriptor, Generic[T]):
def __init__(self, coro: Coroutine[Any, Any, T]) -> None:
self.future = asyncio.Future()
self.coro = coro
async def run(self) -> JobDescriptor | None:
try:
self.future.set_result(await self.coro)
except BaseException as e:
self.future.set_exception(e)
return None
class Pool(AbstractRunner):
def __init__(self, workers: int) -> None: def __init__(self, workers: int) -> None:
if workers < 1: if workers < 1:
raise ValueError('non-positive number of workers') raise ValueError('non-positive number of workers')
@ -203,6 +219,11 @@ class Pool:
def workers(self) -> int: def workers(self) -> int:
return self.__workers return self.__workers
async def run(self, coro: Coroutine[Any, Any, T]) -> T:
job = CoroJD(coro)
self.submit(job.wrap())
return await job.future
class JDC: class JDC:
def __init__(self, descriptor: JobDescriptor, pool: Pool) -> None: def __init__(self, descriptor: JobDescriptor, pool: Pool) -> None:

View File

@ -6,7 +6,7 @@ import time
import discord import discord
from v6d3music.app import AppContext from v6d3music.app import AppContext
from v6d3music.commands import get_of from v6d3music.commands import *
from v6d3music.config import prefix from v6d3music.config import prefix
from v6d3music.core.caching import * from v6d3music.core.caching import *
from v6d3music.core.default_effects import * from v6d3music.core.default_effects import *
@ -54,8 +54,8 @@ def message_allowed(message: discord.Message) -> bool:
return guild_allowed(message.guild) return guild_allowed(message.guild)
def register_handlers(mainservice: MainService, defaulteffects: DefaultEffects): def register_handlers(mainservice: MainService):
of = get_of(mainservice, defaulteffects) of = get_of(mainservice)
@client.event @client.event
async def on_message(message: discord.Message) -> None: async def on_message(message: discord.Message) -> None:
@ -138,11 +138,11 @@ def _db_ee() -> contextlib.ExitStack:
async def main(): async def main():
async with ( async with (
DefaultEffects() as defaulteffects, DefaultEffects() as defaulteffects,
MainService(client) as mainservice, MainService(defaulteffects, client) as mainservice,
AppContext(mainservice), AppContext(mainservice),
ABlockMonitor(delta=0.5) ABlockMonitor(delta=0.5)
): ):
register_handlers(mainservice, defaulteffects) register_handlers(mainservice)
if 'guerilla' in sys.argv: if 'guerilla' in sys.argv:
from pathlib import Path from pathlib import Path
tokenpath = Path('.token.txt') tokenpath = Path('.token.txt')

View File

@ -4,6 +4,7 @@ from v6d3music.utils.effects_for_preset import effects_for_preset
from v6d3music.utils.entries_for_url import entries_for_url from v6d3music.utils.entries_for_url import entries_for_url
from v6d3music.utils.options_for_effects import options_for_effects from v6d3music.utils.options_for_effects import options_for_effects
from v6d3music.utils.sparq import sparq from v6d3music.utils.sparq import sparq
from v6d2ctx.context import Explicit
__all__ = ('InfoCtx', 'UrlCtx', 'ArgCtx',) __all__ = ('InfoCtx', 'UrlCtx', 'ArgCtx',)
@ -61,14 +62,16 @@ class ArgCtx:
case [*args]: case [*args]:
pass pass
ctx.already_read = round(seconds / sparq(options_for_effects(effects))) ctx.already_read = round(seconds / sparq(options_for_effects(effects)))
while True:
match args: match args:
case ['tor', *args]: case ['tor', *args]:
if ctx.tor:
raise Explicit('duplicate tor')
ctx.tor = True ctx.tor = True
case [*args]:
pass
match args:
case ['ignore', *args]: case ['ignore', *args]:
if ctx.ignore:
raise Explicit('duplicate ignore')
ctx.ignore = True ctx.ignore = True
case [*args]: case [*args]:
pass break
self.sources.append(ctx) self.sources.append(ctx)

View File

@ -2,6 +2,8 @@ from typing import Iterable
from v6d2ctx.context import Context, Implicit from v6d2ctx.context import Context, Implicit
__all__ = ('catch',)
async def catch(ctx: Context, args: list[str], reply: str, *catched: (Iterable[str] | str), attachments_ok=True): async def catch(ctx: Context, args: list[str], reply: str, *catched: (Iterable[str] | str), attachments_ok=True):
if ctx.message.attachments and attachments_ok: if ctx.message.attachments and attachments_ok:

View File

@ -3,6 +3,9 @@ from v6d2ctx.context import Explicit
from v6d3music.utils.presets import presets from v6d3music.utils.presets import presets
__all__ = ('effects_for_preset',)
def effects_for_preset(preset: str) -> str: def effects_for_preset(preset: str) -> str:
if preset in presets: if preset in presets:
return presets[preset] return presets[preset]

View File

@ -1,6 +1,8 @@
import shlex import shlex
from typing import Optional from typing import Optional
__all__ = ('options_for_effects',)
def options_for_effects(effects: str | None) -> Optional[str]: def options_for_effects(effects: str | None) -> Optional[str]:
return f'-af {shlex.quote(effects)}' if effects else None return f'-af {shlex.quote(effects)}' if effects else None