From abae781b5249e39b35e3910fe9df4899de41722e Mon Sep 17 00:00:00 2001 From: timofey Date: Fri, 31 Mar 2023 11:30:24 +0000 Subject: [PATCH] loading cancellation --- requirements.txt | 2 +- v6d3music/commands.py | 18 ++++++++++- v6d3music/core/mainservice.py | 22 +++++++++++-- v6d3music/core/queueaudio.py | 2 +- v6d3music/core/ystate.py | 61 +++++++++++++++++++---------------- v6d3music/main.py | 2 +- 6 files changed, 73 insertions(+), 34 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0e7e626..784dbbc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ ptvp35 @ git+https://gitea.parrrate.ru/PTV/ptvp35.git@e760fca39e2070b9959aeb95b53e59e895f1ad57 v6d0auth @ git+https://gitea.parrrate.ru/PTV/v6d0auth.git@c718d4d1422945a756213d22d9e26aa24babe0f6 v6d1tokens @ git+https://gitea.parrrate.ru/PTV/v6d1tokens.git@9ada50f111bd6e9a49c9c6683fa7504fee030056 -v6d2ctx @ git+https://gitea.parrrate.ru/PTV/v6d2ctx.git@c9f3f5ac5c7feb2165fc4fae4eb998a0fe4f5f00 +v6d2ctx @ git+https://gitea.parrrate.ru/PTV/v6d2ctx.git@226bf1b6ada0c217408590abf69379d5f44a7972 rainbowadn @ git+https://gitea.parrrate.ru/PTV/rainbowadn.git@fc1d11f4b53ac4653ffac1bbcad130855e1b7f10 adaas @ git+https://gitea.parrrate.ru/PTV/adaas.git@8093665489901098f92d5a4001f1782dab6ddcf9 diff --git a/v6d3music/commands.py b/v6d3music/commands.py index ca95208..a51b1d5 100644 --- a/v6d3music/commands.py +++ b/v6d3music/commands.py @@ -3,7 +3,7 @@ from typing import Callable import discord -from v6d2ctx.at_of import * +from v6d2ctx.at_of import AtOf from v6d2ctx.context import * from v6d3music.core.default_effects import * from v6d3music.core.mainservice import * @@ -64,6 +64,10 @@ presets: {shlex.join(allowed_presets)} queue.append(audio) await ctx.reply("done") + @at("cancel") + async def cancel(ctx: Context, _args: list[str]) -> None: + mainservice.cancel_loading(ctx) + @at("skip") async def skip(ctx: Context, args: list[str]) -> None: await catch( @@ -275,6 +279,18 @@ presets: {shlex.join(allowed_presets)} or "no queue" ) + @at("np") + @at("cp") + @at("nowplaying") + @at("playing") + @at("//1") + @at("queue1") + async def queue1(ctx: Context, _args: list[str]) -> None: + await ctx.long( + (await (await mainservice.context(ctx, create=True, force_play=False).queue()).format(1)).strip() + or "no queue" + ) + @at("swap") async def swap(ctx: Context, args: list[str]) -> None: await catch( diff --git a/v6d3music/core/mainservice.py b/v6d3music/core/mainservice.py index 99a6d79..59e3021 100644 --- a/v6d3music/core/mainservice.py +++ b/v6d3music/core/mainservice.py @@ -1,7 +1,7 @@ import asyncio import traceback from contextlib import AsyncExitStack -from typing import AsyncIterable, TypeVar +from typing import AsyncIterable, Callable, TypeVar import discord @@ -22,6 +22,7 @@ from v6d3music.core.ystate import * from v6d3music.core.ytaservicing import * from v6d3music.core.ytaudio import * from v6d3music.processing.pool import * +from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.argctx import * __all__ = ('MainService', 'MainMode', 'MainContext', 'MainEvent') @@ -65,6 +66,7 @@ class MainService: self.restore_lock = asyncio.Lock() self.__events: SendableEvents[MainEvent] = events self.__pool_events: SendableEvents[PoolEvent] = _PMSendable(self.__events) + self.__ystates: dict[discord.Guild, YState] = {} def register_instrumentation(self): self.targets.register_type(v6d3music.processing.pool.UnitJob, 'run', Async) @@ -231,8 +233,22 @@ class MainService: 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 + ystate = YState(self.__servicing, self.__pool, ctx, argctx.sources) + self.__ystates[ctx.guild] = ystate + try: + async for audio in ystate.iterate(): + yield audio + finally: + del self.__ystates[ctx.guild] + + def cancel_loading(self, ctx: Context) -> None: + assert ctx.guild is not None + ystate = self.__ystates.get(ctx.guild) + if ystate is None: + return + if ystate.ctx.member != ctx.member: + assert_admin(ctx.member) + ystate.cancel() def pool_json(self) -> ResponseType: return self.__pool.json() diff --git a/v6d3music/core/queueaudio.py b/v6d3music/core/queueaudio.py index e812642..f098789 100644 --- a/v6d3music/core/queueaudio.py +++ b/v6d3music/core/queueaudio.py @@ -16,7 +16,7 @@ from v6d3music.utils.fill import * __all__ = ('QueueAudio',) -PRE_SET_LENGTH = 24 +PRE_SET_LENGTH = 6 class QueueAudio(discord.AudioSource): diff --git a/v6d3music/core/ystate.py b/v6d3music/core/ystate.py index 11a5d96..5c0e1b2 100644 --- a/v6d3music/core/ystate.py +++ b/v6d3music/core/ystate.py @@ -3,16 +3,15 @@ from collections import deque from contextlib import AsyncExitStack from typing import AsyncIterable, Iterable -from v6d2ctx.integration.responsetype import * - from v6d2ctx.context import * +from v6d2ctx.integration.responsetype import * from v6d3music.core.create_ytaudio import * from v6d3music.core.ytaservicing import * from v6d3music.core.ytaudio import * from v6d3music.processing.pool import * from v6d3music.utils.argctx import * -__all__ = ('YState',) +__all__ = ("YState",) class _Stop: @@ -48,7 +47,9 @@ class YState: try: return await future except OSError as e: - raise Explicit('extraction error\nunknown ytdl error (probably due to video being unavailable, e.g. because of regional restrictions)') from e + raise Explicit( + "extraction error\nunknown ytdl error (probably due to video being unavailable, e.g. because of regional restrictions)" + ) from e finally: self.results.task_done() @@ -82,12 +83,18 @@ class YState: else: return None + def cancel(self) -> None: + self.entries.clear() + self.playlists.clear() + self.sources.clear() + self.results.put_nowait(_Stop()) + class YStream(JobUnit): def __init__(self, state: YState) -> None: self.state = state self.__running = False - self.__details: dict[str, ResponseType] = {'status': 'stopped'} + self.__details: dict[str, ResponseType] = {"status": "stopped"} def _unpack_playlists(self) -> None: while self.state.playlists and self.state.playlists[0].done(): @@ -102,7 +109,7 @@ class YStream(JobUnit): async def _run(self, context: JobContext, /) -> JobUnit | None: if self.state.empty_processing(): - self.__details = {'status': 'stopping'} + self.__details = {"status": "stopping"} if self.state.results.empty(): self.state.results.put_nowait(_Stop()) return None @@ -110,20 +117,20 @@ class YStream(JobUnit): entry = self.state.entries.popleft() audiotask: asyncio.Future[YTAudio | None] if isinstance(entry, BaseException): - self._set_details(context, {'status': 'breaking downstream audio creation'}) + self._set_details(context, {"status": "breaking downstream audio creation"}) audiotask = asyncio.Future() audiotask.set_exception(entry) else: self._set_details( context, { - 'status': 'creating audio', - 'info': cast_to_response(entry.info), - 'effects': entry.effects, - 'already_read': entry.already_read, - 'tor': entry.tor, - 'ignore': entry.ignore, - } + "status": "creating audio", + "info": cast_to_response(entry.info), + "effects": entry.effects, + "already_read": entry.already_read, + "tor": entry.tor, + "ignore": entry.ignore, + }, ) audiotask = asyncio.create_task(self.state.result(entry)) self.state.results.put_nowait(audiotask) @@ -133,20 +140,20 @@ class YStream(JobUnit): self.state.entries.clear() self.state.playlists.clear() self.state.sources.clear() - self._set_details(context, {'status': 'rescheduling self from entries'}) + self._set_details(context, {"status": "rescheduling self from entries"}) return self elif self.state.sources: source = self.state.sources.popleft() self._set_details( context, { - 'status': 'parsing playlist', - 'url': source.url, - 'effects': source.effects, - 'already_read': source.already_read, - 'tor': source.tor, - 'ignore': source.ignore, - } + "status": "parsing playlist", + "url": source.url, + "effects": source.effects, + "already_read": source.already_read, + "tor": source.tor, + "ignore": source.ignore, + }, ) playlisttask = asyncio.create_task(self.state.playlist(source)) self.state.playlists.append(playlisttask) @@ -159,13 +166,13 @@ class YStream(JobUnit): self._unpack_playlists() rescheduled = self.state.descheduled self.state.descheduled = 0 - self._set_details(context, {'status': 'rescheduling others', 'rescheduling': rescheduled}) + self._set_details(context, {"status": "rescheduling others", "rescheduling": rescheduled}) for _ in range(rescheduled): await self.state.es.enter_async_context(YStream(self.state).at(self.state.pool)) - self._set_details(context, {'status': 'rescheduling self from sources'}) + self._set_details(context, {"status": "rescheduling self from sources"}) return self else: - self._set_details(context, {'status': 'descheduling'}) + self._set_details(context, {"status": "descheduling"}) self.state.descheduled += 1 return None @@ -179,7 +186,7 @@ class YStream(JobUnit): return await self._run(context) finally: self.__running = False - self.__details = {'status': 'stopped'} + self.__details = {"status": "stopped"} def json(self) -> ResponseType: - return {'type': 'ystream', 'details': self.__details, 'running': self.__running} + return {"type": "ystream", "details": self.__details, "running": self.__running} diff --git a/v6d3music/main.py b/v6d3music/main.py index 423a42d..d8fa08f 100644 --- a/v6d3music/main.py +++ b/v6d3music/main.py @@ -15,7 +15,7 @@ from v6d1tokens.client import * from v6d2ctx.handle_content import * from v6d2ctx.integration.event import * from v6d2ctx.integration.targets import * -from v6d2ctx.pain import ABlockMonitor +from v6d2ctx.pain import ABlockMonitor, ALog from v6d2ctx.serve import * from v6d3music.api import * from v6d3music.app import *