file structure

This commit is contained in:
Yanis Rigaudeau 2023-04-28 01:19:18 +02:00
parent 11c95e93f2
commit 71ff713417
Signed by: yanis
GPG Key ID: 4DD2841DF1C94D83
24 changed files with 241 additions and 37 deletions

View File

@ -3,15 +3,15 @@ from discord import Bot, Intents
from config import Config from config import Config
from logger import Logger from logger import Logger
from framework import Redis from framework import Redis, Youtube
from cogs import Greetings from cog import Greetings, Music
from service import QueueManager
from usecase import Sources
if __name__ == "__main__": if __name__ == "__main__":
# Read Config # Read Config
try: try:
config = Config("config.toml") config = Config("config.toml")
print(config)
except TomlDecodeError as error: except TomlDecodeError as error:
print("Config/DecodeError : %s" % error) print("Config/DecodeError : %s" % error)
exit(1) exit(1)
@ -23,10 +23,23 @@ if __name__ == "__main__":
logger = Logger(config.logging)() logger = Logger(config.logging)()
# Redis Client # Redis Client
redis = Redis(config.redis) redis = Redis(logger, config.redis)
# Bot # Queue Manager
intents = Intents.default() queueManager = QueueManager(redis)
bot = Bot(intents=intents)
# Bot Init
bot = Bot(intents=Intents.default())
# Youtube Client
youtube = Youtube(bot.loop, config.youtube)
# Sources
sources = Sources(youtube)
# Add Cogs
bot.add_cog(Greetings(bot, logger, redis)) bot.add_cog(Greetings(bot, logger, redis))
bot.add_cog(Music(bot, logger, queueManager, sources))
# Run
bot.run(config.discord.token) bot.run(config.discord.token)

View File

@ -1 +1,2 @@
from .misc import Greetings from .misc import Greetings
from .music import Music

35
cog/music.py Normal file
View File

@ -0,0 +1,35 @@
from discord import Bot, Cog, ApplicationContext
from discord.commands import slash_command
from entity import Entry
from logging import Logger
from service import QueueManager
from usecase import Sources
class Music(Cog):
def __init__(
self, bot: Bot, logger: Logger, queueManager: QueueManager, sources: Sources
):
self.bot = bot
self.logger = logger
self.queueManager = queueManager
self.sources = sources
@slash_command(name="play")
async def play(self, context: ApplicationContext, query: str):
async with self.queueManager(context.guild_id) as queue:
test = await self.sources.processQuery(query)
queue.add(
Entry(
title="title",
artist="artist",
album="album",
thumbnail="thumb",
link="fdsdfsd",
requesterId=context.author.id,
)
)
await context.respond(str(test))

View File

@ -2,17 +2,24 @@ import toml
class DiscordConfig: class DiscordConfig:
def __init__(self, discord_config: any) -> None: def __init__(self, discord_config) -> None:
self.token: str = discord_config["token"] self.token: str = discord_config["token"]
self.timeout: int = discord_config["timeout"]
class YoutubeConfig:
def __init__(self, youtube_config) -> None:
self.language: str = youtube_config["language"]
self.region: str = youtube_config["region"]
class LoggingConfig: class LoggingConfig:
def __init__(self, logging_config: any) -> None: def __init__(self, logging_config) -> None:
self.level: str = logging_config["level"] self.level: str = logging_config["level"]
class RedisConfig: class RedisConfig:
def __init__(self, redis_config: any) -> None: def __init__(self, redis_config) -> None:
self.host: str = redis_config["host"] self.host: str = redis_config["host"]
self.port: int = redis_config["port"] self.port: int = redis_config["port"]
self.password: str = redis_config["password"] self.password: str = redis_config["password"]
@ -21,7 +28,9 @@ class RedisConfig:
class Config: class Config:
def __init__(self, config_path: str) -> None: def __init__(self, config_path: str) -> None:
self._config = toml.load(config_path) self._config = toml.load(config_path)
self.appName: str = self._config["appName"]
self.discord = DiscordConfig(self._config["discord"]) self.discord = DiscordConfig(self._config["discord"])
self.youtube = YoutubeConfig(self._config["youtube"])
self.logging = LoggingConfig(self._config["logging"]) self.logging = LoggingConfig(self._config["logging"])
self.redis = RedisConfig(self._config["redis"]) self.redis = RedisConfig(self._config["redis"])

View File

@ -5,4 +5,4 @@ services:
image: redis:alpine image: redis:alpine
command: /bin/sh -c "redis-server --requirepass dev" command: /bin/sh -c "redis-server --requirepass dev"
ports: ports:
- 127.0.0.1:6379:6379 - 6379:6379

View File

@ -1,2 +1,4 @@
from .queue import Queue from .queue import Queue
from .entry import Entry from .entry import Entry
from .file import File
from .playlist import Playlist

View File

@ -1,6 +1,24 @@
from .playlist import Playlist
from .file import File
class Entry: class Entry:
def __init__(self, entry_info) -> None: def __init__(
self.title: str = entry_info["title"] self,
self.artist: str = entry_info["artist"] title: str,
self.thumbnail: str = entry_info["thumbnail"] artist: str,
self.requester: str = entry_info["requester"] album: str,
thumbnail: str,
link: str,
requesterId: int,
playlist: Playlist | None = None,
file: File | None = None,
) -> None:
self.title = title
self.artist = artist
self.album = album
self.thumbnail = thumbnail
self.link = link
self.requester = requesterId
self.playlist = playlist
self.file = file

5
entity/file.py Normal file
View File

@ -0,0 +1,5 @@
class File:
def __init__(self, name: str, path: str, size: int) -> None:
self.name = name
self.path = path
self.size = size

4
entity/playlist.py Normal file
View File

@ -0,0 +1,4 @@
class Playlist:
def __init__(self, title: str, url: str) -> None:
self.title = title
self.url = url

View File

@ -1,23 +1,43 @@
from entry import Entry from .entry import Entry
from .playlist import Playlist
class Queue: class Queue:
def __init__(self) -> None: def __init__(self) -> None:
self._queue: list[Entry] = [] self._entries: list[Entry] = []
self.cursor = 0 self.cursor = 0
def append(self, entry: Entry) -> None: def add(self, entry: Entry) -> None:
self._queue.append(entry) self._entries.append(entry)
def remove(self, index: int) -> Entry | None: def addPlalist(self, playlist: Playlist) -> None:
if 0 < index < len(self._queue): for entry in playlist:
return self._queue.pop() self._entries.append(entry)
def remove(self, index: int, recursive: bool) -> None:
if not 0 < index < len(self._entries):
return
# if recursive and self[index].playlist is not None:
# first_entry = ""
# else:
self._entries.pop()
def move(self, frm: int, to: int) -> None: def move(self, frm: int, to: int) -> None:
if 0 < frm < len(self._queue) and 0 < to < len(self._queue) and frm != to: if (
self._queue.insert(to, self._queue.pop(frm)) not 0 < frm < len(self._entries)
or not 0 < to < len(self._entries)
or frm == to
):
return
self._entries.insert(to, self._entries.pop(frm))
def __getitem__(self, index: int) -> Entry | None: def __getitem__(self, index: int) -> Entry | None:
if index < 0 or index > len(self._queue): if not 0 < index < len(self._entries):
return None return
return self._queue[index]
return self._entries[index]
def __len__(self) -> int:
return len(self._entries)

View File

@ -1 +1,2 @@
from .redis import Redis from .redis import Redis
from .youtube import Youtube

0
framework/mysql.py Normal file
View File

View File

@ -1,3 +1,5 @@
from logging import Logger
from redis.asyncio.lock import Lock
import redis.asyncio as redis import redis.asyncio as redis
import pickle import pickle
@ -5,20 +7,39 @@ from config import RedisConfig
class Redis: class Redis:
def __init__(self, config: RedisConfig) -> None: def __init__(self, logger: Logger, config: RedisConfig) -> None:
self.client = redis.Redis( self._client = redis.Redis(
host=config.host, host=config.host,
port=config.port, port=config.port,
password=config.password, password=config.password,
auto_close_connection_pool=False, auto_close_connection_pool=False,
) )
self._locks: dict[str, Lock] = {}
self.logger = logger
async def set(self, key: str, value: any) -> None: async def _get_lock(self, key) -> Lock:
await self.client.set(key, pickle.dumps(value)) if key not in self._locks:
self._locks[key] = self._client.lock(key)
return self._locks[key]
async def get(self, key: str) -> any: async def acquire(self, key: str) -> None:
value = await self.client.get(key) lock = await self._get_lock(f"djembe:queue:{key}")
return pickle.loads(value) await lock.acquire()
async def release(self, key: str) -> None:
lock = await self._get_lock(f"djembe:queue:{key}")
await lock.release()
async def get(self, key: str):
self.logger.info(f"get value {key} from redis")
value = await self._client.get(f"djembe:queue:{key}")
if value:
return pickle.loads(value)
return None
async def set(self, key: str, value) -> None:
self.logger.info(f"set value {key} to redis")
await self._client.set(f"djembe:queue:{key}", pickle.dumps(value))
async def close(self) -> None: async def close(self) -> None:
await self.client.close() await self._client.close()

0
framework/spotify.py Normal file
View File

25
framework/youtube.py Normal file
View File

@ -0,0 +1,25 @@
from yt_dlp import YoutubeDL
from youtubesearchpython.__future__ import VideosSearch
from asyncio import AbstractEventLoop
from config import YoutubeConfig
class Youtube:
def __init__(self, loop: AbstractEventLoop, config: YoutubeConfig) -> None:
self.params = {}
self.client = YoutubeDL(self.params)
self.loop = loop
self.config = config
async def searchVideo(self, query: str):
videosSearch = VideosSearch(
query, limit=1, language=self.config.language, region=self.config.region
)
return await videosSearch.next()
async def get_data(self, url: str) -> str:
data = await self.loop.run_in_executor(
None, lambda: self.client.extract_info(url, download=False)
)
print(data)

View File

@ -2,3 +2,5 @@ py-cord==2.4.1
PyNaCl==1.5.0 PyNaCl==1.5.0
toml==0.10.2 toml==0.10.2
redis==4.5.4 redis==4.5.4
youtube-search-python==1.6.6
yt-dlp==2023.3.4

1
service/__init__.py Normal file
View File

@ -0,0 +1 @@
from .queueManager import QueueManager

35
service/queueManager.py Normal file
View File

@ -0,0 +1,35 @@
from contextlib import asynccontextmanager
from entity import Queue
from framework import Redis
class QueueManager:
def __init__(self, redis: Redis) -> None:
self.redis = redis
self.queues: dict[int, Queue] = {}
@asynccontextmanager
async def __call__(self, guildId: int) -> Queue:
#await self.acquire(guildId)
queue = await self.get(guildId)
yield queue
await self.save(guildId)
#await self.release(guildId)
async def acquire(self, guildId: int) -> None:
await self.redis.acquire(guildId)
async def release(self, guildId: int) -> None:
await self.redis.release(guildId)
async def get(self, guildId: int) -> Queue:
queue: Queue | None = await self.redis.get(guildId)
if queue is None:
queue = Queue()
self.queues[guildId] = queue
return self.queues[guildId]
async def save(self, guildId: int) -> None:
await self.redis.set(guildId, self.queues[guildId])

View File

0
test/TODO Normal file
View File

1
usecase/__init__.py Normal file
View File

@ -0,0 +1 @@
from .sources import Sources

11
usecase/sources.py Normal file
View File

@ -0,0 +1,11 @@
import re
from framework import Youtube
class Sources:
def __init__(self, youtube: Youtube) -> None:
self.youtube = youtube
async def processQuery(self, query: str):
return await self.youtube.searchVideo(query)