mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
DOCS!
This commit is contained in:
parent
b6b8c63f88
commit
7583f1330f
11 changed files with 72 additions and 24 deletions
|
@ -27,7 +27,10 @@ author = 'Stefano Pigozzi'
|
|||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ["sphinx.ext.autodoc"]
|
||||
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx.ext.intersphinx"]
|
||||
|
||||
intersphinx_mapping = {'python': ('https://docs.python.org/3.7', None),
|
||||
'discord': ('https://discordpy.readthedocs.io/en/latest/', None)}
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
|
|
@ -7,3 +7,10 @@ royalnet.database
|
|||
|
||||
.. automodule:: royalnet.database
|
||||
:members:
|
||||
|
||||
|
||||
Tables
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: royalnet.database.tables
|
||||
:members:
|
||||
|
|
|
@ -5,43 +5,50 @@ from .royalpcmaudio import RoyalPCMAudio
|
|||
|
||||
|
||||
class PlayMode:
|
||||
"""The base class for a PlayMode, such as :py:class:`royalnet.audio.Playlist`. Inherit from this class if you want to create a custom PlayMode."""
|
||||
|
||||
def __init__(self):
|
||||
"""Create a new PlayMode and initialize the generator inside."""
|
||||
self.now_playing: typing.Optional[RoyalPCMAudio] = None
|
||||
self.generator: typing.AsyncGenerator = self._generator()
|
||||
self.generator: typing.AsyncGenerator = self.generate_generator()
|
||||
|
||||
async def next(self):
|
||||
return await self.generator.__anext__()
|
||||
|
||||
def videos_left(self):
|
||||
def videos_left(self) -> typing.Union[int, float]:
|
||||
"""Return the number of videos left in the PlayMode.
|
||||
|
||||
Usually returns an :py:class:`int`, but may return :py:obj:`math.inf` if the PlayMode is infinite."""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def _generator(self):
|
||||
"""Get the next RPA from the list and advance it."""
|
||||
async def generate_generator(self):
|
||||
"""Get the next :py:class:`royalnet.audio.RoyalPCMAudio` from the list and advance it."""
|
||||
raise NotImplementedError()
|
||||
# This is needed to make the coroutine an async generator
|
||||
# noinspection PyUnreachableCode
|
||||
yield NotImplemented
|
||||
|
||||
def add(self, item):
|
||||
"""Add a new RPA to the PlayMode."""
|
||||
"""Add a new :py:class:`royalnet.audio.RoyalPCMAudio` to the PlayMode."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete(self):
|
||||
"""Delete all RPAs contained inside this PlayMode."""
|
||||
"""Delete all :py:class:`royalnet.audio.RoyalPCMAudio` contained inside this PlayMode."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Playlist(PlayMode):
|
||||
"""A video list. RPAs played are removed from the list."""
|
||||
"""A video list. :py:class:`royalnet.audio.RoyalPCMAudio` played are removed from the list."""
|
||||
def __init__(self, starting_list: typing.List[RoyalPCMAudio] = None):
|
||||
super().__init__()
|
||||
if starting_list is None:
|
||||
starting_list = []
|
||||
self.list: typing.List[RoyalPCMAudio] = starting_list
|
||||
|
||||
def videos_left(self):
|
||||
def videos_left(self) -> typing.Union[int, float]:
|
||||
return len(self.list)
|
||||
|
||||
async def _generator(self):
|
||||
async def generate_generator(self):
|
||||
while True:
|
||||
try:
|
||||
next_video = self.list.pop(0)
|
||||
|
@ -63,7 +70,7 @@ class Playlist(PlayMode):
|
|||
|
||||
|
||||
class Pool(PlayMode):
|
||||
"""A RPA pool. RPAs played are played back in random order, and they are kept in the pool."""
|
||||
"""A :py:class:`royalnet.audio.RoyalPCMAudio` pool. :py:class:`royalnet.audio.RoyalPCMAudio` are selected in random order and are not repeated until every song has been played at least once."""
|
||||
def __init__(self, starting_pool: typing.List[RoyalPCMAudio] = None):
|
||||
super().__init__()
|
||||
if starting_pool is None:
|
||||
|
@ -71,10 +78,10 @@ class Pool(PlayMode):
|
|||
self.pool: typing.List[RoyalPCMAudio] = starting_pool
|
||||
self._pool_copy: typing.List[RoyalPCMAudio] = []
|
||||
|
||||
def videos_left(self):
|
||||
def videos_left(self) -> typing.Union[int, float]:
|
||||
return math.inf
|
||||
|
||||
async def _generator(self):
|
||||
async def generate_generator(self):
|
||||
while True:
|
||||
if not self.pool:
|
||||
self.now_playing = None
|
||||
|
|
|
@ -5,17 +5,29 @@ from .royalpcmfile import RoyalPCMFile
|
|||
|
||||
|
||||
class RoyalPCMAudio(AudioSource):
|
||||
"""A discord-compatible :py:class:`discord.AudioSource` that keeps data in a file instead of in memory."""
|
||||
|
||||
def __init__(self, rpf: "RoyalPCMFile"):
|
||||
"""Create a :py:class:`discord.audio.RoyalPCMAudio` from a :ref:`discord.audio.RoyalPCMFile`. Not recommended, use """
|
||||
self.rpf: "RoyalPCMFile" = rpf
|
||||
self._file = open(self.rpf.audio_filename, "rb")
|
||||
|
||||
@staticmethod
|
||||
def create_from_url(url: str) -> typing.List["RoyalPCMAudio"]:
|
||||
"""Download a file with youtube_dl and create a list of :py:class:`discord.audio.RoyalPCMAudio`.
|
||||
|
||||
Parameters:
|
||||
url: The url of the file to download."""
|
||||
rpf_list = RoyalPCMFile.create_from_url(url)
|
||||
return [RoyalPCMAudio(rpf) for rpf in rpf_list]
|
||||
|
||||
@staticmethod
|
||||
def create_from_ytsearch(search: str, amount: int = 1) -> typing.List["RoyalPCMAudio"]:
|
||||
"""Search a string on YouTube and download the first ``amount`` number of videos, then download those with youtube_dl and create a list of :py:class:`discord.audio.RoyalPCMAudio`.
|
||||
|
||||
Parameters:
|
||||
search: The string to search on YouTube.
|
||||
amount: The number of videos to download."""
|
||||
rpf_list = RoyalPCMFile.create_from_ytsearch(search, amount)
|
||||
return [RoyalPCMAudio(rpf) for rpf in rpf_list]
|
||||
|
||||
|
@ -36,6 +48,7 @@ class RoyalPCMAudio(AudioSource):
|
|||
return data
|
||||
|
||||
def delete(self):
|
||||
"""Permanently delete the downloaded file."""
|
||||
self._file.close()
|
||||
self.rpf.delete_audio_file()
|
||||
|
||||
|
|
|
@ -46,13 +46,17 @@ class RoyalPCMFile(YtdlFile):
|
|||
return f"<RoyalPCMFile {self.audio_filename}>"
|
||||
|
||||
@staticmethod
|
||||
def create_from_url(url, **ytdl_args) -> typing.List["RoyalPCMFile"]:
|
||||
def create_from_url(url: str, **ytdl_args) -> typing.List["RoyalPCMFile"]:
|
||||
"""Download a file with youtube_dl and create a list of :py:class:`discord.audio.RoyalPCMFile`.
|
||||
|
||||
Parameters:
|
||||
url: The url of the file to download."""
|
||||
info_list = YtdlInfo.create_from_url(url)
|
||||
return [RoyalPCMFile(info, **ytdl_args) for info in info_list]
|
||||
|
||||
@staticmethod
|
||||
def create_from_ytsearch(search: str, amount: int = 1, **ytdl_args) -> typing.List["RoyalPCMFile"]:
|
||||
"""Search a string on YouTube and download the first amount videos found."""
|
||||
"""Search a string on YouTube and download the first ``amount`` number of videos, then download those with youtube_dl and create a list of :py:class:`discord.audio.RoyalPCMFile`."""
|
||||
url = f"ytsearch{amount}:{search}"
|
||||
info_list = YtdlInfo.create_from_url(url)
|
||||
return [RoyalPCMFile(info, **ytdl_args) for info in info_list]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from .telegram import TelegramBot, TelegramConfig
|
||||
from .discord import DiscordBot, DiscordConfig
|
||||
from .generic import GenericBot
|
||||
|
||||
__all__ = ["TelegramBot", "TelegramConfig", "DiscordBot", "DiscordConfig"]
|
||||
__all__ = ["TelegramBot", "TelegramConfig", "DiscordBot", "DiscordConfig", "GenericBot"]
|
||||
|
|
|
@ -19,11 +19,14 @@ if not discord.opus.is_loaded():
|
|||
|
||||
|
||||
class DiscordConfig:
|
||||
"""The specific configuration to be used for :ref:`royalnet.database.DiscordBot`."""
|
||||
def __init__(self, token: str):
|
||||
self.token = token
|
||||
|
||||
|
||||
class DiscordBot(GenericBot):
|
||||
"""A bot that connects to `Discord <https://discordapp.com/>`_."""
|
||||
|
||||
interface_name = "discord"
|
||||
|
||||
def _init_voice(self):
|
||||
|
|
|
@ -12,7 +12,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class GenericBot:
|
||||
"""A generic bot class, to be used as base for the other more specific classes, such as TelegramBot and DiscordBot."""
|
||||
"""A generic bot class, to be used as base for the other more specific classes, such as :ref:`royalnet.bots.TelegramBot` and :ref:`royalnet.bots.DiscordBot`."""
|
||||
interface_name = NotImplemented
|
||||
|
||||
def _init_commands(self,
|
||||
|
|
|
@ -13,16 +13,14 @@ loop = asyncio.get_event_loop()
|
|||
log = _logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def todo(message: Message):
|
||||
log.warning(f"Skipped {message} because handling isn't supported yet.")
|
||||
|
||||
|
||||
class TelegramConfig:
|
||||
"""The specific configuration to be used for :ref:`royalnet.database.TelegramBot`."""
|
||||
def __init__(self, token: str):
|
||||
self.token: str = token
|
||||
|
||||
|
||||
class TelegramBot(GenericBot):
|
||||
"""A bot that connects to `Telegram <https://telegram.org/>`_."""
|
||||
interface_name = "telegram"
|
||||
|
||||
def _init_client(self):
|
||||
|
|
|
@ -12,7 +12,15 @@ loop = asyncio.get_event_loop()
|
|||
|
||||
|
||||
class Alchemy:
|
||||
def __init__(self, database_uri: str, tables: typing.Optional[typing.Set] = None):
|
||||
"""A wrapper around SQLAlchemy declarative that allows to use multiple databases at once while maintaining a single table-class for both of them."""
|
||||
|
||||
def __init__(self, database_uri: str, tables: typing.Set):
|
||||
"""Create a new Alchemy object.
|
||||
|
||||
Args:
|
||||
database_uri: The uri of the database, as described at https://docs.sqlalchemy.org/en/13/core/engines.html .
|
||||
tables: The set of tables to be created and used in the selected database. Check the tables submodule for more details.
|
||||
"""
|
||||
if database_uri.startswith("sqlite"):
|
||||
raise NotImplementedError("Support for sqlite databases is currently missing")
|
||||
self.engine = create_engine(database_uri)
|
||||
|
@ -20,7 +28,7 @@ class Alchemy:
|
|||
self.Session = sessionmaker(bind=self.engine)
|
||||
self._create_tables(tables)
|
||||
|
||||
def _create_tables(self, tables: typing.Optional[typing.List]):
|
||||
def _create_tables(self, tables: typing.Set):
|
||||
for table in tables:
|
||||
name = table.__name__
|
||||
try:
|
||||
|
@ -34,7 +42,8 @@ class Alchemy:
|
|||
self.Base.metadata.create_all()
|
||||
|
||||
@contextmanager
|
||||
async def session_cm(self):
|
||||
def session_cm(self):
|
||||
"""Use Alchemy as a context manager (to be used in with statements)."""
|
||||
session = self.Session()
|
||||
try:
|
||||
yield session
|
||||
|
@ -46,6 +55,7 @@ class Alchemy:
|
|||
|
||||
@asynccontextmanager
|
||||
async def session_acm(self):
|
||||
"""Use Alchemy as a asyncronous context manager (to be used in async with statements)."""
|
||||
session = await asyncify(self.Session)
|
||||
try:
|
||||
yield session
|
||||
|
|
|
@ -2,6 +2,8 @@ import typing
|
|||
|
||||
|
||||
class DatabaseConfig:
|
||||
"""The configuration to be used for the :ref:`royalnet.database.Alchemy` component of :ref:`royalnet.bots.GenericBot`."""
|
||||
|
||||
def __init__(self,
|
||||
database_uri: str,
|
||||
master_table: typing.Type,
|
||||
|
|
Loading…
Reference in a new issue