servicing
This commit is contained in:
		
							parent
							
								
									b2dc8c5edb
								
							
						
					
					
						commit
						872271fc6f
					
				@ -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:
 | 
				
			||||||
 | 
				
			|||||||
@ -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]):
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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))
 | 
				
			||||||
 | 
				
			|||||||
@ -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:
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
					 | 
				
			||||||
							
								
								
									
										11
									
								
								v6d3music/core/ytaservicing.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								v6d3music/core/ytaservicing.py
									
									
									
									
									
										Normal 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
 | 
				
			||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								v6d3music/processing/abstractrunner.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								v6d3music/processing/abstractrunner.py
									
									
									
									
									
										Normal 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
 | 
				
			||||||
@ -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:
 | 
				
			||||||
 | 
				
			|||||||
@ -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')
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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:
 | 
				
			||||||
 | 
				
			|||||||
@ -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]
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user