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:
parent
bf70ceafe7
commit
2b24d8cd09
8 changed files with 97 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
.idea/misc.xml
|
||||
.idea/royalnet.iml
|
||||
dist/
|
||||
**/__pycache__/
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<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>
|
||||
</component>
|
||||
</project>
|
|
@ -3,11 +3,13 @@
|
|||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<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$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.8 (royalnet-1MWM6-kd-py3.8)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="royalpack" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
|
||||
|
|
|
@ -2,5 +2,7 @@
|
|||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/docs_source/royalpack" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../royalpack" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,7 +1,7 @@
|
|||
from .ytdlinfo import YtdlInfo
|
||||
from .ytdlfile import YtdlFile
|
||||
from .ytdlmp3 import YtdlMp3
|
||||
from .errors import *
|
||||
from .ytdldiscord import YtdlDiscord
|
||||
|
||||
try:
|
||||
from .fileaudiosource import FileAudioSource
|
||||
|
@ -13,10 +13,6 @@ __all__ = [
|
|||
"YtdlInfo",
|
||||
"YtdlFile",
|
||||
"YtdlMp3",
|
||||
"BardError",
|
||||
"YtdlError",
|
||||
"NotFoundError",
|
||||
"MultipleFilesError",
|
||||
"YtdlDiscord",
|
||||
"FileAudioSource",
|
||||
"UnsupportedError",
|
||||
]
|
||||
|
|
38
royalnet/serf/discord/playable.py
Normal file
38
royalnet/serf/discord/playable.py
Normal 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()
|
37
royalnet/serf/discord/playableytdqueue.py
Normal file
37
royalnet/serf/discord/playableytdqueue.py
Normal 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()
|
|
@ -1,6 +1,7 @@
|
|||
import asyncio
|
||||
from typing import Optional
|
||||
from .errors import *
|
||||
from .playable import Playable
|
||||
try:
|
||||
import discord
|
||||
except ImportError:
|
||||
|
@ -10,7 +11,7 @@ except ImportError:
|
|||
class VoicePlayer:
|
||||
def __init__(self):
|
||||
self.voice_client: Optional["discord.VoiceClient"] = None
|
||||
...
|
||||
self.playing: Optional[Playable] = None
|
||||
|
||||
async def connect(self, channel: "discord.VoiceChannel") -> "discord.VoiceClient":
|
||||
"""Connect the :class:`VoicePlayer` to a :class:`discord.VoiceChannel`, creating a :class:`discord.VoiceClient`
|
||||
|
@ -29,7 +30,7 @@ class VoicePlayer:
|
|||
GuildAlreadyConnectedError:
|
||||
OpusNotLoadedError:
|
||||
"""
|
||||
if self.voice_client is not None:
|
||||
if self.voice_client is not None and self.voice_client.is_connected():
|
||||
raise PlayerAlreadyConnectedError()
|
||||
try:
|
||||
self.voice_client = await channel.connect()
|
||||
|
@ -48,7 +49,7 @@ class VoicePlayer:
|
|||
Raises:
|
||||
PlayerNotConnectedError:
|
||||
"""
|
||||
if self.voice_client is None:
|
||||
if self.voice_client is None or not self.voice_client.is_connected():
|
||||
raise PlayerNotConnectedError()
|
||||
await self.voice_client.disconnect(force=True)
|
||||
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`
|
||||
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):
|
||||
...
|
||||
|
|
Loading…
Reference in a new issue