mirror of
https://github.com/RafaelSolVargas/Vulkan.git
synced 2025-10-29 16:57:23 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50363cd630 | ||
|
|
d605e6a5a8 | ||
|
|
b3a9920037 | ||
|
|
6be5c0d27b | ||
|
|
e3e7f2048a | ||
|
|
634e6560c6 | ||
|
|
582e35bce1 | ||
|
|
a82bf7950b | ||
|
|
aa5649bcdc | ||
|
|
6cfd188c3b | ||
|
|
83a864725d | ||
|
|
f336df43ee | ||
|
|
d86f014a3a | ||
|
|
219fef7daa | ||
|
|
45d27b4c47 | ||
|
|
2114f15840 |
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
BOT_TOKEN=Your_Own_Bot_Token
|
||||
# If you do not want to use spotify, remove the 2 following lines
|
||||
SPOTIFY_ID=Your_Own_Spotify_ID
|
||||
SPOTIFY_SECRET=Your_Own_Spotify_Secret
|
||||
BOT_PREFIX=Your_Wanted_Prefix_For_Vulkan
|
||||
SHOULD_AUTO_DISCONNECT_WHEN_ALONE=True #all settings can be set like this
|
||||
#etc... All settings can be set this way
|
||||
33
.github/Docs/DOCKER.md
vendored
Normal file
33
.github/Docs/DOCKER.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<h1 align="center">Docker</h1>
|
||||
|
||||
This bot can be easily deployed using Docker.
|
||||
|
||||
## **Requirements**
|
||||
To run this project in a Docker container you must have Docker and Docker Compose installed in your machine. Find how to install Docker in your machine [here](https://docs.docker.com/get-docker/) and find how to install Docker Compose in your machine [here](https://docs.docker.com/compose/install/).
|
||||
|
||||
Once you have Docker and Docker Compose installed in your machine, clone or download this repository and follow the instructions below.
|
||||
|
||||
## **Running the Bot**
|
||||
To run the bot in a Docker container, you must first create a `.env` file in the root of the project if there isn't one already. You will need to change the parameters in the `.env` file to your own parameters. You can find an example of a `.env` file [here](.env.example). You will also be able to change the settings in that environment file as explained in the [Settings page](.github/Docs/SETTINGS.md).
|
||||
|
||||
**If any of the following commands fail, try without a dash between docker and compose.**
|
||||
|
||||
To run the bot, simply execute the following command in the root of the project:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
This will build the Docker image and run the bot in a Docker container. The `-d` flag is used to run the container in detached mode, which means that the container will run in the background. If you want to see the logs of the container, you can run the following command:
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
To stop the container, run the following command:
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
## **Updating the Bot**
|
||||
To update the bot, you must first stop the container as explained in the previous section. Then, you must pull the latest changes from the repository, in any way you want. Finally, you must build the Docker image again and run the container again. To do this, run the following commands:
|
||||
```bash
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
23
.github/Docs/SETTINGS.md
vendored
23
.github/Docs/SETTINGS.md
vendored
@@ -8,31 +8,40 @@
|
||||
- Maximum songs downloading at a time
|
||||
- Maximum songs in a Queue Page
|
||||
|
||||
All parameters can be modified in the Configs.py file that are located in the Config folder
|
||||
> The path to the file is ./Config/Configs.py
|
||||
All parameters can be modified in the .env file or in an environment variable.
|
||||
Here is a sample of the .env file:
|
||||
|
||||
```env
|
||||
BOT_TOKEN=Your_Own_Bot_Token
|
||||
SPOTIFY_ID=Your_Own_Spotify_ID
|
||||
SPOTIFY_SECRET=Your_Own_Spotify_Secret
|
||||
BOT_PREFIX=Your_Wanted_Prefix_For_Vulkan
|
||||
SHOULD_AUTO_DISCONNECT_WHEN_ALONE=True #all settings can be set like this
|
||||
#etc... All settings can be set this way
|
||||
```
|
||||
|
||||
|
||||
### **Bot Prefix**
|
||||
The Bot Prefix is just a string that must be passed as prefix when calling any Bot command from the Discord.
|
||||
To change that you must: <br>
|
||||
- Change the property BOT_PREFIX of the VConfigs class to what prefix you want to.
|
||||
- Change the property BOT_PREFIX in the .env file or in an environment variable to what you want to.
|
||||
|
||||
### **Auto Disconnect**
|
||||
As a result of the [Issue 33](https://github.com/RafaelSolVargas/Vulkan/issues/33) you can configure if the Bot will auto disconnect when being alone in the voice channel. The default configuration is to disconnect within 300 seconds if it finds out no one is currently listing to it.
|
||||
To change that you must: <br>
|
||||
- Change the property SHOULD_AUTO_DISCONNECT_WHEN_ALONE of the VConfigs class to False
|
||||
- Change the property SHOULD_AUTO_DISCONNECT_WHEN_ALONE in the .env file or in an environment variable to False
|
||||
|
||||
### **Multiprocessing or Threading**
|
||||
As a result of the [Issue 35](https://github.com/RafaelSolVargas/Vulkan/issues/35) you can configure if the Bot will create a specific Python Process for each Player (Guild) that he is playing songs or all will happen in the Main Process. The Default behavior is to create a new process.
|
||||
To change that you must: <br>
|
||||
- Change the property SONG_PLAYBACK_IN_SEPARATE_PROCESS of the VConfigs class to False
|
||||
- Change the property SONG_PLAYBACK_IN_SEPARATE_PROCESS in the .env file or in an environment variable to False
|
||||
|
||||
### **Maximum Downloading Quant**
|
||||
The download of songs can be very fast or very slow, the faster it is the slower the response time for any command (during the download) is higher, (including the playback quality), because there will be a Task for each song. But it's possible to set up this variable to slow the download and keep the response time better.
|
||||
To change that you must: <br>
|
||||
- Change the property MAX_DOWNLOAD_SONGS_AT_A_TIME of the VConfigs class to what you want to.
|
||||
- Change the property MAX_DOWNLOAD_SONGS_AT_A_TIME in the .env file or in an environment variable to what you want to.
|
||||
|
||||
### **Maximum Songs In Queue Page**
|
||||
When the ```Queue``` command is called, the current song playlist is presented in the Discord, you can configure how many songs you will want to show in each page.
|
||||
To change that you must: <br>
|
||||
- Change the property MAX_SONGS_IN_PAGE of the VConfigs class to what you want to.
|
||||
- Change the property MAX_SONGS_IN_PAGE in the .env file or in an environment variable to what you want to.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 83 KiB |
@@ -1,54 +1,71 @@
|
||||
from decouple import config
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from Config.Singleton import Singleton
|
||||
from Config.Folder import Folder
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class VConfigs(Singleton):
|
||||
def __init__(self) -> None:
|
||||
if not super().created:
|
||||
# You can change this boolean to False if you want to prevent the Bot from auto disconnecting
|
||||
# Resolution for the issue: https://github.com/RafaelSolVargas/Vulkan/issues/33
|
||||
self.SHOULD_AUTO_DISCONNECT_WHEN_ALONE = False
|
||||
self.SHOULD_AUTO_DISCONNECT_WHEN_ALONE = os.getenv('SHOULD_AUTO_DISCONNECT_WHEN_ALONE') == 'True'
|
||||
|
||||
# Recommended to be True, except in cases when your Bot is present in thousands servers, in that case
|
||||
# the delay to start a new Python process for the playback is too much, and to avoid that you set as False
|
||||
# This feature is for now in testing period, for a more stable version, keep this boolean = True
|
||||
self.SONG_PLAYBACK_IN_SEPARATE_PROCESS = True
|
||||
# This feature is for now in testing period, for a more stable version, keep this boolean = Trued
|
||||
self.SONG_PLAYBACK_IN_SEPARATE_PROCESS = os.getenv('SONG_PLAYBACK_IN_SEPARATE_PROCESS', 'True') == 'True'
|
||||
|
||||
# Maximum of songs that will be downloaded at once, the higher this number is, the faster the songs will be all available
|
||||
# but the slower will be the others commands of the Bot during the downloading time, for example, the playback quality
|
||||
self.MAX_DOWNLOAD_SONGS_AT_A_TIME = 5
|
||||
self.MAX_DOWNLOAD_SONGS_AT_A_TIME = int(os.getenv('MAX_DOWNLOAD_SONGS_AT_A_TIME', 5))
|
||||
|
||||
self.BOT_PREFIX = '!'
|
||||
try:
|
||||
self.BOT_TOKEN = config('BOT_TOKEN')
|
||||
self.SPOTIFY_ID = config('SPOTIFY_ID')
|
||||
self.SPOTIFY_SECRET = config('SPOTIFY_SECRET')
|
||||
self.BOT_PREFIX = config('BOT_PREFIX')
|
||||
except:
|
||||
print(
|
||||
'[ERROR] -> You must create and .env file with all required fields, see documentation for help')
|
||||
self.BOT_PREFIX = os.getenv('BOT_PREFIX', '!')
|
||||
|
||||
self.CLEANER_MESSAGES_QUANT = 5
|
||||
self.ACQUIRE_LOCK_TIMEOUT = 10
|
||||
self.QUEUE_VIEW_TIMEOUT = 120
|
||||
self.COMMANDS_FOLDER_NAME = 'DiscordCogs'
|
||||
if self.BOT_PREFIX == 'Your_Wanted_Prefix_For_Vulkan':
|
||||
self.BOT_PREFIX = '!'
|
||||
|
||||
self.BOT_TOKEN = os.getenv('BOT_TOKEN')
|
||||
if self.BOT_TOKEN is None:
|
||||
raise ValueError('No token was given')
|
||||
|
||||
self.SPOTIFY_ID = os.getenv('SPOTIFY_ID')
|
||||
self.SPOTIFY_SECRET = os.getenv('SPOTIFY_SECRET')
|
||||
|
||||
if self.SPOTIFY_ID == "Your_Own_Spotify_ID":
|
||||
self.SPOTIFY_ID = None
|
||||
|
||||
if self.SPOTIFY_SECRET == "Your_Own_Spotify_Secret":
|
||||
self.SPOTIFY_SECRET = None
|
||||
|
||||
if self.SPOTIFY_ID is None or self.SPOTIFY_SECRET is None:
|
||||
print('Spotify will not work')
|
||||
|
||||
self.CLEANER_MESSAGES_QUANT = int(os.getenv('CLEANER_MESSAGES_QUANT', 5))
|
||||
self.ACQUIRE_LOCK_TIMEOUT = int(os.getenv('ACQUIRE_LOCK_TIMEOUT', 10))
|
||||
self.QUEUE_VIEW_TIMEOUT = int(os.getenv('QUEUE_VIEW_TIMEOUT', 120))
|
||||
|
||||
self.COMMANDS_FOLDER_NAME = os.getenv('COMMANDS_FOLDER_NAME', 'DiscordCogs')
|
||||
self.COMMANDS_PATH = f'{Folder().rootFolder}{self.COMMANDS_FOLDER_NAME}'
|
||||
self.VC_TIMEOUT = 300
|
||||
self.VC_TIMEOUT = int(os.getenv('VC_TIMEOUT', 300))
|
||||
|
||||
self.CHANCE_SHOW_PROJECT = 15
|
||||
self.PROJECT_URL = 'https://github.com/RafaelSolVargas/Vulkan'
|
||||
self.SUPPORTING_ICON = 'https://i.pinimg.com/originals/d6/05/b4/d605b4f8c5d1c6ae20dc353ef9f091bd.png'
|
||||
self.CHANCE_SHOW_PROJECT = int(os.getenv('CHANCE_SHOW_PROJECT', 15))
|
||||
self.PROJECT_URL = os.getenv('PROJECT_URL', 'https://github.com/RafaelSolVargas/Vulkan')
|
||||
self.SUPPORTING_ICON = os.getenv('SUPPORTING_ICON', 'https://i.pinimg.com/originals/d6/05/b4/d605b4f8c5d1c6ae20dc353ef9f091bd.png')
|
||||
|
||||
self.MAX_PLAYLIST_LENGTH = 50
|
||||
self.MAX_PLAYLIST_FORCED_LENGTH = 5
|
||||
self.MAX_SONGS_IN_PAGE = 10
|
||||
self.MAX_PRELOAD_SONGS = 15
|
||||
self.MAX_SONGS_HISTORY = 15
|
||||
self.MAX_PLAYLIST_LENGTH = int(os.getenv('MAX_PLAYLIST_LENGTH', 50))
|
||||
self.MAX_PLAYLIST_FORCED_LENGTH = int(os.getenv('MAX_PLAYLIST_FORCED_LENGTH', 5))
|
||||
self.MAX_SONGS_IN_PAGE = int(os.getenv('MAX_SONGS_IN_PAGE', 10))
|
||||
self.MAX_PRELOAD_SONGS = int(os.getenv('MAX_PRELOAD_SONGS', 15))
|
||||
self.MAX_SONGS_HISTORY = int(os.getenv('MAX_SONGS_HISTORY', 15))
|
||||
|
||||
self.INVITE_MESSAGE = """To invite Vulkan to your own server, click [here]({}).
|
||||
Or use this direct URL: {}"""
|
||||
self.INVITE_MESSAGE = os.getenv('INVITE_MESSAGE', """To invite Vulkan to your own server, click [here]({}).
|
||||
Or use this direct URL: {}""")
|
||||
|
||||
self.MY_ERROR_BAD_COMMAND = 'This string serves to verify if some error was raised by myself on purpose'
|
||||
self.INVITE_URL = 'https://discordapp.com/oauth2/authorize?client_id={}&scope=bot'
|
||||
self.MY_ERROR_BAD_COMMAND = os.getenv('MY_ERROR_BAD_COMMAND', 'This string serves to verify if some error was raised by myself on purpose')
|
||||
self.INVITE_URL = os.getenv('INVITE_URL', 'https://discordapp.com/oauth2/authorize?client_id={}&permissions=8&scope=bot')
|
||||
|
||||
def getPlayersManager(self):
|
||||
return self.__manager
|
||||
|
||||
@@ -343,6 +343,13 @@ class VEmbeds:
|
||||
description=self.__messages.PLAYER_NOT_PLAYING,
|
||||
colour=self.__colors.BLUE)
|
||||
return embed
|
||||
|
||||
def VOLUME_CHANGED(self, volume: float) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.VOLUME_CHANGED.format(volume),
|
||||
colour=self.__colors.BLUE)
|
||||
return embed
|
||||
|
||||
def QUEUE(self, title: str, description: str) -> Embed:
|
||||
embed = Embed(
|
||||
|
||||
@@ -29,7 +29,9 @@ class Helper(Singleton):
|
||||
self.HELP_PREV_LONG = 'Play the previous song. If playing, the current song will return to queue.\n\nRequire: Loop to be disable.\nArguments: None.'
|
||||
self.HELP_SHUFFLE = 'Shuffle the songs playing.'
|
||||
self.HELP_SHUFFLE_LONG = 'Randomly shuffle the songs in the queue.\n\nArguments: None.'
|
||||
self.HELP_PLAY = 'Plays a song from URL'
|
||||
self.HELP_PLAY = 'Plays a song from URL.'
|
||||
self.CHANGE_VOLUME = 'Set the volume of the song.'
|
||||
self.CHANGE_VOLUME_LONG = 'Change the volume of the song, expect a number from 0 to 100.'
|
||||
self.HELP_PLAY_LONG = 'Play a song in discord. \n\nRequire: You to be connected to a voice channel.\nArguments: Youtube, Spotify or Deezer song/playlist link or the title of the song to be searched in Youtube.'
|
||||
self.HELP_HISTORY = f'Show the history of played songs.'
|
||||
self.HELP_HISTORY_LONG = f'Show the last {config.MAX_SONGS_HISTORY} played songs'
|
||||
|
||||
@@ -16,6 +16,7 @@ class Messages(Singleton):
|
||||
self.SONGINFO_REQUESTER = 'Requester: '
|
||||
self.SONGINFO_POSITION = 'Position: '
|
||||
|
||||
self.VOLUME_CHANGED = 'Song volume changed to `{}`%'
|
||||
self.SONGS_ADDED = 'Downloading `{}` songs to add to the queue'
|
||||
self.SONG_ADDED = 'Downloading the song `{}` to add to the queue'
|
||||
self.SONG_ADDED_TWO = f'{self.__emojis.MUSIC} Song added to the queue'
|
||||
@@ -56,6 +57,7 @@ class Messages(Singleton):
|
||||
self.ERROR_MOVING = f'{self.__emojis.ERROR} Error while moving the songs'
|
||||
self.LENGTH_ERROR = f'{self.__emojis.ERROR} Numbers must be between 1 and queue length, use -1 for the last song'
|
||||
self.ERROR_NUMBER = f'{self.__emojis.ERROR} This command require a number'
|
||||
self.ERROR_VOLUME_NUMBER = f'{self.__emojis.ERROR} This command require a number between 0 and 100'
|
||||
self.ERROR_PLAYING = f'{self.__emojis.ERROR} Error while playing songs'
|
||||
self.COMMAND_NOT_FOUND = f'{self.__emojis.ERROR} Command not found, type {configs.BOT_PREFIX}help to see all commands'
|
||||
self.UNKNOWN_ERROR = f'{self.__emojis.ERROR} Unknown Error, if needed, use {configs.BOT_PREFIX}reset to reset the player of your server'
|
||||
|
||||
@@ -21,7 +21,7 @@ class ControlCog(Cog):
|
||||
'MUSIC': ['resume', 'pause', 'loop', 'stop',
|
||||
'skip', 'play', 'queue', 'clear',
|
||||
'np', 'shuffle', 'move', 'remove',
|
||||
'reset', 'prev', 'history'],
|
||||
'reset', 'prev', 'history', 'volume'],
|
||||
'RANDOM': ['choose', 'cara', 'random']
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ from Handlers.ResumeHandler import ResumeHandler
|
||||
from Handlers.HistoryHandler import HistoryHandler
|
||||
from Handlers.QueueHandler import QueueHandler
|
||||
from Handlers.LoopHandler import LoopHandler
|
||||
from Handlers.VolumeHandler import VolumeHandler
|
||||
from Messages.MessagesCategory import MessagesCategory
|
||||
from Messages.Responses.EmoteCogResponse import EmoteCommandResponse
|
||||
from Messages.Responses.EmbedCogResponse import EmbedCommandResponse
|
||||
@@ -64,6 +65,25 @@ class MusicCog(Cog):
|
||||
except Exception as e:
|
||||
print(f'[ERROR IN COG] -> {e}')
|
||||
|
||||
@command(name="volume", help=helper.CHANGE_VOLUME, description=helper.CHANGE_VOLUME_LONG, aliases=['v'])
|
||||
async def volume(self, ctx: Context, *args) -> None:
|
||||
try:
|
||||
controller = VolumeHandler(ctx, self.__bot)
|
||||
|
||||
if len(args) > 1:
|
||||
track = " ".join(args)
|
||||
else:
|
||||
track = args[0]
|
||||
|
||||
response = await controller.run(track)
|
||||
if response is not None:
|
||||
cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER)
|
||||
cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER)
|
||||
await cogResponser1.run()
|
||||
await cogResponser2.run()
|
||||
except Exception as e:
|
||||
print(f'[ERROR IN COG] -> {e}')
|
||||
|
||||
@command(name="queue", help=helper.HELP_QUEUE, description=helper.HELP_QUEUE_LONG, aliases=['q', 'fila', 'musicas'])
|
||||
async def queue(self, ctx: Context, *args) -> None:
|
||||
try:
|
||||
|
||||
@@ -15,6 +15,7 @@ from Handlers.ResumeHandler import ResumeHandler
|
||||
from Handlers.HistoryHandler import HistoryHandler
|
||||
from Handlers.QueueHandler import QueueHandler
|
||||
from Handlers.LoopHandler import LoopHandler
|
||||
from Handlers.VolumeHandler import VolumeHandler
|
||||
from Messages.MessagesCategory import MessagesCategory
|
||||
from Messages.Responses.SlashEmbedResponse import SlashEmbedResponse
|
||||
from Music.VulkanBot import VulkanBot
|
||||
@@ -237,6 +238,22 @@ class SlashCommands(Cog):
|
||||
except Exception:
|
||||
print(f'[ERROR IN SLASH COMMAND] -> {traceback.format_exc()}')
|
||||
|
||||
@slash_command(name='volume', description=helper.CHANGE_VOLUME_LONG)
|
||||
async def move(self, ctx: ApplicationContext,
|
||||
volume: Option(float, "The new volume of the song", min_value=1, default= 100)) -> None:
|
||||
if not self.__bot.listingSlash:
|
||||
return
|
||||
try:
|
||||
await ctx.defer()
|
||||
|
||||
controller = VolumeHandler(ctx, self.__bot)
|
||||
|
||||
response = await controller.run(f'{volume}')
|
||||
cogResponser = SlashEmbedResponse(response, ctx, MessagesCategory.PLAYER)
|
||||
await cogResponser.run()
|
||||
except Exception:
|
||||
print(f'[ERROR IN SLASH COMMAND] -> {traceback.format_exc()}')
|
||||
|
||||
@slash_command(name='remove', description=helper.HELP_REMOVE)
|
||||
async def remove(self, ctx: ApplicationContext,
|
||||
position: Option(int, "The song position to remove", min_value=1)) -> None:
|
||||
|
||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM python:3.10.0
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
WORKDIR /app
|
||||
RUN git clone https://github.com/RafaelSolVargas/Vulkan.git /app
|
||||
RUN pip install -r requirements.txt
|
||||
RUN apt-get update && apt-get install -y software-properties-common && apt-get install -y ffmpeg
|
||||
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
|
||||
USER appuser
|
||||
CMD ["python", "main.py"]
|
||||
62
Handlers/VolumeHandler.py
Normal file
62
Handlers/VolumeHandler.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from Config.Exceptions import BadCommandUsage, NumberRequired, VulkanError
|
||||
from Parallelism.AbstractProcessManager import AbstractPlayersManager
|
||||
from Parallelism.Commands import VCommands, VCommandsType
|
||||
from Handlers.AbstractHandler import AbstractHandler
|
||||
from Handlers.HandlerResponse import HandlerResponse
|
||||
from discord.ext.commands import Context
|
||||
from Music.VulkanBot import VulkanBot
|
||||
from discord import Interaction
|
||||
from typing import Union
|
||||
|
||||
|
||||
class VolumeHandler(AbstractHandler):
|
||||
def __init__(self, ctx: Union[Context, Interaction], bot: VulkanBot) -> None:
|
||||
super().__init__(ctx, bot)
|
||||
|
||||
async def run(self, args: str) -> HandlerResponse:
|
||||
if args is None or args.strip() == '':
|
||||
error = BadCommandUsage()
|
||||
return HandlerResponse(self.ctx, embed, error)
|
||||
|
||||
error = self.__validateInput(args)
|
||||
if error:
|
||||
embed = self.embeds.ERROR_EMBED(error.message)
|
||||
return HandlerResponse(self.ctx, embed, error)
|
||||
|
||||
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
|
||||
if not playersManager.verifyIfPlayerExists(self.guild):
|
||||
embed = self.embeds.NOT_PLAYING()
|
||||
error = BadCommandUsage()
|
||||
return HandlerResponse(self.ctx, embed, error)
|
||||
|
||||
playerLock = playersManager.getPlayerLock(self.guild)
|
||||
acquired = playerLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
|
||||
volume = self.__convert_input_to_volume(args)
|
||||
if acquired:
|
||||
volumeCommand = VCommands(VCommandsType.VOLUME, volume)
|
||||
await playersManager.sendCommandToPlayer(volumeCommand, self.guild, self.ctx)
|
||||
|
||||
playerLock.release()
|
||||
|
||||
embed = self.embeds.VOLUME_CHANGED(volume)
|
||||
return HandlerResponse(self.ctx, embed)
|
||||
else:
|
||||
playersManager.resetPlayer(self.guild, self.ctx)
|
||||
|
||||
embed = self.embeds.PLAYER_RESTARTED()
|
||||
return HandlerResponse(self.ctx, embed)
|
||||
|
||||
def __convert_input_to_volume(self, input_volume: str) -> float:
|
||||
volume = float(input_volume)
|
||||
if volume < 0:
|
||||
volume = 0
|
||||
if volume > 100:
|
||||
volume = 100
|
||||
|
||||
return volume
|
||||
|
||||
def __validateInput(self, volume: str) -> Union[VulkanError, None]:
|
||||
try:
|
||||
_ = float(volume)
|
||||
except:
|
||||
return NumberRequired(self.messages.ERROR_VOLUME_NUMBER)
|
||||
@@ -1,14 +1,14 @@
|
||||
from Config.Exceptions import DeezerError, InvalidInput, SpotifyError, VulkanError, YoutubeError
|
||||
from Music.Downloader import Downloader
|
||||
from Music.Types import Provider
|
||||
from Music.SpotifySearcher import SpotifySearch
|
||||
from Music.DeezerSearcher import DeezerSearcher
|
||||
from Utils.Utils import Utils
|
||||
from Utils.UrlAnalyzer import URLAnalyzer
|
||||
from Config.Messages import SearchMessages
|
||||
from Music.Downloader import Downloader
|
||||
from Music.Types import Provider
|
||||
from Utils.Utils import Utils
|
||||
|
||||
|
||||
class Searcher():
|
||||
class Searcher:
|
||||
def __init__(self) -> None:
|
||||
self.__spotify = SpotifySearch()
|
||||
self.__deezer = DeezerSearcher()
|
||||
@@ -35,8 +35,7 @@ class Searcher():
|
||||
try:
|
||||
musics = self.__spotify.search(track)
|
||||
if musics == None or len(musics) == 0:
|
||||
raise SpotifyError(self.__messages.SPOTIFY_NOT_FOUND,
|
||||
self.__messages.GENERIC_TITLE)
|
||||
raise SpotifyError(self.__messages.SPOTIFY_NOT_FOUND, self.__messages.GENERIC_TITLE)
|
||||
|
||||
return musics
|
||||
except SpotifyError as error:
|
||||
@@ -79,7 +78,7 @@ class Searcher():
|
||||
if not Utils.is_url(track):
|
||||
return Provider.Name
|
||||
|
||||
if "https://www.youtu" in track or "https://youtu.be" in track or "https://music.youtube" in track:
|
||||
if "https://www.youtu" in track or "https://youtu.be" in track or "https://music.youtube" in track or "m.youtube" in track:
|
||||
return Provider.YouTube
|
||||
|
||||
if "https://open.spotify.com" in track:
|
||||
|
||||
@@ -13,6 +13,7 @@ class VCommandsType(Enum):
|
||||
RESET = 'Reset'
|
||||
NOW_PLAYING = 'Now Playing'
|
||||
TERMINATE = 'Terminate'
|
||||
VOLUME = 'Volume'
|
||||
SLEEPING = 'Sleeping'
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import asyncio
|
||||
from time import sleep, time
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
from Music.VulkanInitializer import VulkanInitializer
|
||||
from discord import VoiceClient
|
||||
from discord import PCMVolumeTransformer, VoiceClient
|
||||
from asyncio import AbstractEventLoop, Semaphore, Queue
|
||||
from multiprocessing import Process, RLock, Lock, Queue
|
||||
from threading import Thread
|
||||
@@ -54,6 +54,9 @@ class ProcessPlayer(Process):
|
||||
self.__voiceChannel: VoiceChannel = None
|
||||
self.__voiceClient: VoiceClient = None
|
||||
|
||||
self.__songVolumeUsing = 1
|
||||
self.__currentSongChangeVolume = False
|
||||
|
||||
self.__playing = False
|
||||
self.__forceStop = False
|
||||
self.__botCompletedLoad = False
|
||||
@@ -98,6 +101,32 @@ class ProcessPlayer(Process):
|
||||
# In this point the process should finalize
|
||||
self.__timer.cancel()
|
||||
|
||||
def __set_volume(self, volume: float) -> None:
|
||||
"""Set the volume of the player, must be values between 0 and 100"""
|
||||
try:
|
||||
if self.__voiceClient is None:
|
||||
return
|
||||
|
||||
if not isinstance(volume, float):
|
||||
print('[PROCESS ERROR] -> Volume instance must be float')
|
||||
return
|
||||
|
||||
if volume < 0:
|
||||
volume = 0
|
||||
if volume > 100:
|
||||
volume = 100
|
||||
|
||||
volume = volume / 100
|
||||
|
||||
if not self.__currentSongChangeVolume:
|
||||
print('[PROCESS ERROR] -> Cannot change the volume of this song')
|
||||
return
|
||||
|
||||
self.__songVolumeUsing = volume
|
||||
self.__voiceClient.source.volume = volume
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def __verifyIfIsPlaying(self) -> bool:
|
||||
if self.__voiceClient is None:
|
||||
return False
|
||||
@@ -151,6 +180,10 @@ class ProcessPlayer(Process):
|
||||
self.__songPlaying = song
|
||||
|
||||
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
|
||||
if not player.is_opus():
|
||||
player = PCMVolumeTransformer(player, self.__songVolumeUsing)
|
||||
self.__currentSongChangeVolume = True
|
||||
|
||||
self.__voiceClient.play(player, after=lambda e: self.__playNext(e))
|
||||
|
||||
self.__timer.cancel()
|
||||
@@ -169,6 +202,8 @@ class ProcessPlayer(Process):
|
||||
print(f'[PROCESS PLAYER -> ERROR PLAYING SONG] -> {error}')
|
||||
with self.__playlistLock:
|
||||
with self.__playerLock:
|
||||
self.__currentSongChangeVolume = False
|
||||
|
||||
if self.__forceStop: # If it's forced to stop player
|
||||
self.__forceStop = False
|
||||
return None
|
||||
@@ -271,6 +306,8 @@ class ProcessPlayer(Process):
|
||||
asyncio.run_coroutine_threadsafe(self.__reset(), self.__loop)
|
||||
elif type == VCommandsType.STOP:
|
||||
asyncio.run_coroutine_threadsafe(self.__stop(), self.__loop)
|
||||
elif type == VCommandsType.VOLUME:
|
||||
self.__set_volume(args)
|
||||
else:
|
||||
print(f'[PROCESS PLAYER ERROR] -> Unknown Command Received: {command}')
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import asyncio
|
||||
from time import time
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
from discord import VoiceClient
|
||||
from discord import PCMVolumeTransformer, VoiceClient
|
||||
from asyncio import AbstractEventLoop
|
||||
from threading import RLock, Thread
|
||||
from multiprocessing import Lock
|
||||
@@ -45,6 +45,9 @@ class ThreadPlayer(Thread):
|
||||
self.__voiceChannel: VoiceChannel = voiceChannel
|
||||
self.__voiceClient: VoiceClient = None
|
||||
|
||||
self.__currentSongChangeVolume = False
|
||||
self.__songVolumeUsing = 1
|
||||
|
||||
self.__downloader = Downloader()
|
||||
self.__callback = callbackToSendCommand
|
||||
self.__exitCB = exitCB
|
||||
@@ -56,6 +59,32 @@ class ThreadPlayer(Thread):
|
||||
self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
|
||||
'options': '-vn'}
|
||||
|
||||
def __set_volume(self, volume: float) -> None:
|
||||
"""Set the volume of the player, must be values between 0 and 100"""
|
||||
try:
|
||||
if self.__voiceClient is None:
|
||||
return
|
||||
|
||||
if not isinstance(volume, float):
|
||||
print('[THREAD ERROR] -> Volume instance must be float')
|
||||
return
|
||||
|
||||
if volume < 0:
|
||||
volume = 0
|
||||
if volume > 100:
|
||||
volume = 100
|
||||
|
||||
volume = volume / 100
|
||||
|
||||
if not self.__currentSongChangeVolume:
|
||||
print('[THREAD ERROR] -> Cannot change the volume of this song')
|
||||
return
|
||||
|
||||
self.__songVolumeUsing = volume
|
||||
self.__voiceClient.source.volume = volume
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def __verifyIfIsPlaying(self) -> bool:
|
||||
if self.__voiceClient is None:
|
||||
return False
|
||||
@@ -109,6 +138,9 @@ class ThreadPlayer(Thread):
|
||||
self.__songPlaying = song
|
||||
|
||||
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
|
||||
if not player.is_opus():
|
||||
player = PCMVolumeTransformer(player, self.__songVolumeUsing)
|
||||
self.__currentSongChangeVolume = True
|
||||
self.__voiceClient.play(player, after=lambda e: self.__playNext(e))
|
||||
|
||||
self.__timer.cancel()
|
||||
@@ -127,6 +159,7 @@ class ThreadPlayer(Thread):
|
||||
print(f'[THREAD PLAYER -> ERROR PLAYING SONG] -> {error}')
|
||||
with self.__playlistLock:
|
||||
with self.__playerLock:
|
||||
self.__currentSongChangeVolume = False
|
||||
if self.__forceStop: # If it's forced to stop player
|
||||
self.__forceStop = False
|
||||
return None
|
||||
@@ -217,6 +250,8 @@ class ThreadPlayer(Thread):
|
||||
await self.__reset()
|
||||
elif type == VCommandsType.STOP:
|
||||
await self.__stop()
|
||||
elif type == VCommandsType.VOLUME:
|
||||
self.__set_volume(args)
|
||||
else:
|
||||
print(f'[THREAD PLAYER ERROR] -> Unknown Command Received: {command}')
|
||||
except Exception as e:
|
||||
|
||||
11
README.md
11
README.md
@@ -18,6 +18,7 @@ Vulkan uses multiprocessing and asynchronous Python modules to maximize Music Pl
|
||||
- Support for the new Discord Slash commands.
|
||||
- Search for all musics in Queue using buttons.
|
||||
- Shortcut the playing of one song using dropdown menu.
|
||||
- Manage the volume of the songs.
|
||||
- Manage the loop of one or all playing musics.
|
||||
- Manage the order and remove musics from the queue.
|
||||
- Shuffle the musics queue order.
|
||||
@@ -62,16 +63,17 @@ You can download the executables in this link `https://www.ffmpeg.org/download.h
|
||||
|
||||
### **.Env File Example**
|
||||
This is an example of how your .env file (located in root) should look like.
|
||||
```
|
||||
```env
|
||||
BOT_TOKEN=Your_Own_Bot_Token
|
||||
SPOTIFY_ID=Your_Own_Spotify_ID
|
||||
SPOTIFY_SECRET=Your_Own_Spotify_Secret
|
||||
BOT_PREFIX=Your_Wanted_Prefix_For_Vulkan
|
||||
|
||||
SHOULD_AUTO_DISCONNECT_WHEN_ALONE=True #all settings can be set like this
|
||||
#etc... All settings can be set this way
|
||||
```
|
||||
|
||||
### **⚙️ Configs**
|
||||
The config file is located at ```./config/Configs.py```, it doesn't require any change, but if you can change the values to the way you want. <br>
|
||||
The bot's configuration is stored in the [.env](.env) file, you can change the prefix and the bot token there, as well as all the other configurations.
|
||||
Take a look in the [Settings page](.github/Docs/SETTINGS.md) to personalize the Bot for you.
|
||||
|
||||
|
||||
@@ -80,6 +82,9 @@ Take a look in the [Settings page](.github/Docs/SETTINGS.md) to personalize the
|
||||
- Run ```python main.py``` in console to start
|
||||
- Give this project a nice 🌟
|
||||
|
||||
### **🐳 Docker**
|
||||
You can also run this project in a Docker container. You can find the instructions to run this project in a Docker container in the [Docker Instructions](.github/Docs/DOCKER.md) page.
|
||||
|
||||
|
||||
<br>
|
||||
<hr>
|
||||
|
||||
8
docker-compose.yaml
Normal file
8
docker-compose.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
version: '3'
|
||||
services:
|
||||
vulkan:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
env_file:
|
||||
- .env
|
||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Reference in New Issue
Block a user