file structure
This commit is contained in:
parent
11c95e93f2
commit
71ff713417
29
__main__.py
29
__main__.py
@ -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)
|
||||||
|
@ -1 +1,2 @@
|
|||||||
from .misc import Greetings
|
from .misc import Greetings
|
||||||
|
from .music import Music
|
35
cog/music.py
Normal file
35
cog/music.py
Normal 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))
|
15
config.py
15
config.py
@ -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"])
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
5
entity/file.py
Normal 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
4
entity/playlist.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
class Playlist:
|
||||||
|
def __init__(self, title: str, url: str) -> None:
|
||||||
|
self.title = title
|
||||||
|
self.url = url
|
@ -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)
|
||||||
|
@ -1 +1,2 @@
|
|||||||
from .redis import Redis
|
from .redis import Redis
|
||||||
|
from .youtube import Youtube
|
||||||
|
0
framework/mysql.py
Normal file
0
framework/mysql.py
Normal 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}")
|
||||||
|
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 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
0
framework/spotify.py
Normal file
25
framework/youtube.py
Normal file
25
framework/youtube.py
Normal 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)
|
@ -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
1
service/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .queueManager import QueueManager
|
35
service/queueManager.py
Normal file
35
service/queueManager.py
Normal 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])
|
0
service/youtubeDownloader.py
Normal file
0
service/youtubeDownloader.py
Normal file
1
usecase/__init__.py
Normal file
1
usecase/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .sources import Sources
|
11
usecase/sources.py
Normal file
11
usecase/sources.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user