1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 19:44:20 +00:00

MOAR MOAR MOAR

This commit is contained in:
Steffo 2019-11-22 21:09:49 +01:00
parent bf70ceafe7
commit 2b24d8cd09
8 changed files with 97 additions and 10 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
.idea/misc.xml .idea/misc.xml
.idea/royalnet.iml .idea/royalnet.iml
dist/ dist/
**/__pycache__/

View file

@ -3,6 +3,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/royalnet.iml" filepath="$PROJECT_DIR$/.idea/royalnet.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/royalnet.iml" filepath="$PROJECT_DIR$/.idea/royalnet.iml" />
<module fileurl="file://$PROJECT_DIR$/../royalpack/.idea/royalpack.iml" filepath="$PROJECT_DIR$/../royalpack/.idea/royalpack.iml" />
</modules> </modules>
</component> </component>
</project> </project>

View file

@ -3,11 +3,13 @@
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/royalnet" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/royalnet" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/royalnet.egg-info" /> <excludeFolder url="file://$MODULE_DIR$/royalnet.egg-info" />
<excludeFolder url="file://$MODULE_DIR$/venv" /> <excludeFolder url="file://$MODULE_DIR$/venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.8 (royalnet-1MWM6-kd-py3.8)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.8 (royalnet-1MWM6-kd-py3.8)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="royalpack" />
</component> </component>
<component name="TestRunnerService"> <component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" /> <option name="PROJECT_TEST_RUNNER" value="Unittests" />

View file

@ -2,5 +2,7 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/docs_source/royalpack" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../royalpack" vcs="Git" />
</component> </component>
</project> </project>

View file

@ -1,7 +1,7 @@
from .ytdlinfo import YtdlInfo from .ytdlinfo import YtdlInfo
from .ytdlfile import YtdlFile from .ytdlfile import YtdlFile
from .ytdlmp3 import YtdlMp3 from .ytdlmp3 import YtdlMp3
from .errors import * from .ytdldiscord import YtdlDiscord
try: try:
from .fileaudiosource import FileAudioSource from .fileaudiosource import FileAudioSource
@ -13,10 +13,6 @@ __all__ = [
"YtdlInfo", "YtdlInfo",
"YtdlFile", "YtdlFile",
"YtdlMp3", "YtdlMp3",
"BardError", "YtdlDiscord",
"YtdlError",
"NotFoundError",
"MultipleFilesError",
"FileAudioSource", "FileAudioSource",
"UnsupportedError",
] ]

View file

@ -0,0 +1,38 @@
from typing import Optional, AsyncGenerator, Tuple, Any, Dict
try:
import discord
except ImportError:
discord = None
class Playable:
"""An abstract class representing something that can be played back in a :class:`VoicePlayer`."""
def __init__(self):
self.generator: \
Optional[AsyncGenerator[Optional["discord.AudioSource"], Tuple[Tuple[Any, ...], Dict[str, Any]]]] = None
async def next(self, *args, **kwargs) -> Optional["discord.AudioSource"]:
"""Get the next :class:`discord.AudioSource` that should be played.
Called when the :class:`Playable` is first attached to a :class:`VoicePlayer` and when a
:class:`discord.AudioSource` stops playing.
Args and kwargs can be used to pass data to the generator.
Returns:
:const:`None` if there is nothing available to play, otherwise the :class:`discord.AudioSource` that should
be played.
"""
return await self.generator.asend((args, kwargs,))
async def _generator(self) \
-> AsyncGenerator[Optional["discord.AudioSource"], Tuple[Tuple[Any, ...], Dict[str, Any]]]:
"""Create an async generator that returns the next source to be played;
it can take a args+kwargs tuple in input to optionally select a different source.
Note:
For `weird Python reasons
<https://www.python.org/dev/peps/pep-0525/#support-for-asynchronous-iteration-protocol>`, the generator
should ``yield`` once before doing anything else."""
yield
raise NotImplementedError()

View file

@ -0,0 +1,37 @@
from typing import Optional, List, AsyncGenerator, Tuple, Any, Dict
from royalnet.bard import YtdlDiscord
from .playable import Playable
try:
import discord
except ImportError:
discord = None
class PlayableYTDQueue(Playable):
"""A queue of :class:`YtdlDiscord` to be played in sequence."""
def __init__(self, start_with: Optional[List[YtdlDiscord]] = None):
super().__init__()
self.contents: List[YtdlDiscord] = []
if start_with is not None:
self.contents = [*self.contents, *start_with]
async def _generator(self) \
-> AsyncGenerator[Optional["discord.AudioSource"], Tuple[Tuple[Any, ...], Dict[str, Any]]]:
yield
while True:
try:
# Try to get the first YtdlDiscord of the queue
ytd: YtdlDiscord = self.contents.pop(0)
except IndexError:
# If there isn't anything, yield None
yield None
continue
try:
# Create a FileAudioSource from the YtdlDiscord
# If the file hasn't been fetched / downloaded / converted yet, it will do so before yielding
async with ytd.spawn_audiosource() as fas:
# Yield the resulting AudioSource
yield fas
finally:
# Delete the YtdlDiscord file
await ytd.delete_asap()

View file

@ -1,6 +1,7 @@
import asyncio import asyncio
from typing import Optional from typing import Optional
from .errors import * from .errors import *
from .playable import Playable
try: try:
import discord import discord
except ImportError: except ImportError:
@ -10,7 +11,7 @@ except ImportError:
class VoicePlayer: class VoicePlayer:
def __init__(self): def __init__(self):
self.voice_client: Optional["discord.VoiceClient"] = None self.voice_client: Optional["discord.VoiceClient"] = None
... self.playing: Optional[Playable] = None
async def connect(self, channel: "discord.VoiceChannel") -> "discord.VoiceClient": async def connect(self, channel: "discord.VoiceChannel") -> "discord.VoiceClient":
"""Connect the :class:`VoicePlayer` to a :class:`discord.VoiceChannel`, creating a :class:`discord.VoiceClient` """Connect the :class:`VoicePlayer` to a :class:`discord.VoiceChannel`, creating a :class:`discord.VoiceClient`
@ -29,7 +30,7 @@ class VoicePlayer:
GuildAlreadyConnectedError: GuildAlreadyConnectedError:
OpusNotLoadedError: OpusNotLoadedError:
""" """
if self.voice_client is not None: if self.voice_client is not None and self.voice_client.is_connected():
raise PlayerAlreadyConnectedError() raise PlayerAlreadyConnectedError()
try: try:
self.voice_client = await channel.connect() self.voice_client = await channel.connect()
@ -48,7 +49,7 @@ class VoicePlayer:
Raises: Raises:
PlayerNotConnectedError: PlayerNotConnectedError:
""" """
if self.voice_client is None: if self.voice_client is None or not self.voice_client.is_connected():
raise PlayerNotConnectedError() raise PlayerNotConnectedError()
await self.voice_client.disconnect(force=True) await self.voice_client.disconnect(force=True)
self.voice_client = None self.voice_client = None
@ -58,5 +59,14 @@ class VoicePlayer:
This requires the :class:`VoicePlayer` to already be connected, and for the passed :class:`discord.VoiceChannel` This requires the :class:`VoicePlayer` to already be connected, and for the passed :class:`discord.VoiceChannel`
to be in the same :class:`discord.Guild` as """ to be in the same :class:`discord.Guild` as """
if self.voice_client is None or not self.voice_client.is_connected():
raise PlayerNotConnectedError()
await self.voice_client.move_to(channel)
async def start(self):
"""Start playing music on the :class:`discord.VoiceClient`."""
if self.voice_client is None or not self.voice_client.is_connected():
raise PlayerNotConnectedError()
def _playback_ended(self, error=None):
... ...