16 Commits

Author SHA1 Message Date
Rafael Vargas
50363cd630 Merge pull request #66 from rivai45/patch-1
Configuring the Bot to be invited as an Administrator to allow further actions
2024-04-02 08:31:28 -03:00
Rivai
d605e6a5a8 Update Configs.py
Add administrator permission for the bot, so the bot can play in hidden or private channel
2024-04-01 12:27:17 +07:00
Rafael Vargas
b3a9920037 Merge pull request #53 from RafaelSolVargas/pr1044-from-someone
Upgrading documentation
2023-07-16 16:17:59 -03:00
Rafael Vargas
6be5c0d27b Upgrading documentation 2023-07-16 16:12:47 -03:00
Rafael Vargas
e3e7f2048a Merge pull request #52 from Paillat-dev/master
Adding docker support
2023-07-16 16:03:31 -03:00
Paillat
634e6560c6 docs(DOCKER.md): update instructions for running the bot in a Docker container
- Add clarification about creating a `.env` file in the root of the project
- Mention the need to change parameters in the `.env` file
- Provide a link to an example `.env` file
- Refer to the [Settings page](.github/Docs/SETTINGS.md) for changing environment file settings
- Add a note about trying the commands without a dash between docker and compose if they fail
2023-07-14 16:55:59 +02:00
Paillat
582e35bce1 chore(.env.example): add example .env file with configurable settings
docs(DOCKER.md): add instructions for running the bot in a Docker container
docs(SETTINGS.md): update instructions for changing bot settings in .env file
docs(README.md): update instructions for running the bot and add Docker instructions
2023-07-14 16:53:55 +02:00
Paillat
a82bf7950b fix(Configs.py): change default BOT_PREFIX to '!' if it is set to 'Your_Wanted_Prefix_For_Vulkan' 2023-07-14 16:53:23 +02:00
Paillat
aa5649bcdc fix(Configs.py): set SPOTIFY_ID and SPOTIFY_SECRET to None if they are set to default values 2023-07-14 16:50:14 +02:00
Paillat
6cfd188c3b feat: add Dockerfile and docker-compose.yaml for containerization
The Dockerfile is added to define the container image for the application. It uses the Python 3.10.0 base image, sets the PYTHONUNBUFFERED environment variable, clones the Vulkan repository, installs the required dependencies, installs ffmpeg, creates a non-root user, and sets the command to run the main.py file.

The docker-compose.yaml file is added to define the Docker Compose configuration for the application. It specifies a service named "vulkan" that builds the image using the Dockerfile and uses the .env file for environment variables.
2023-07-14 16:22:53 +02:00
Paillat
83a864725d fix(Configs.py): update PROJECT_URL to point to the correct GitHub repository 2023-07-14 16:12:38 +02:00
Paillat
f336df43ee chore(requirements.txt): update requirements.txt file 2023-07-13 14:47:59 +02:00
Paillat
d86f014a3a chore(requirements.txt): update requirements.txt file 2023-07-13 14:26:37 +02:00
Paillat
219fef7daa chore(Configs.py): refactor Configs.py to use environment variables instead of decouple config
feat(Configs.py): add support for loading environment variables from .env file using dotenv
feat(Configs.py): add support for SHOULD_AUTO_DISCONNECT_WHEN_ALONE environment variable
feat(Configs.py): add support for SONG_PLAYBACK_IN_SEPARATE_PROCESS environment variable
feat(Configs.py): add support for MAX_DOWNLOAD_SONGS_AT_A_TIME environment variable
feat(Configs.py): add support for BOT_PREFIX environment variable
feat(Configs.py): add support for BOT_TOKEN environment variable
feat(Configs.py): add support for SPOTIFY_ID and SPOTIFY_SECRET environment variables
feat(Configs.py): add support for CLEANER_MESSAGES_QUANT environment variable
feat(Configs.py): add support for ACQUIRE_LOCK_TIMEOUT environment variable
feat(Configs.py): add support for QUEUE_VIEW_TIMEOUT environment variable
feat(Configs.py): add support for COMMANDS
2023-07-13 14:25:38 +02:00
Rafael Vargas
45d27b4c47 Fixing mobile youtube links and volume returning to 100% when changing songs 2023-07-01 16:12:21 -03:00
Rafael Vargas
2114f15840 Adding volume change command, pre-release 2023-06-24 22:05:23 -03:00
20 changed files with 324 additions and 52 deletions

7
.env.example Normal file
View 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
View 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
```

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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'

View File

@@ -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'

View File

@@ -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']
}

View File

@@ -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:

View File

@@ -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
View 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
View 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)

View File

@@ -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:

View File

@@ -13,6 +13,7 @@ class VCommandsType(Enum):
RESET = 'Reset'
NOW_PLAYING = 'Now Playing'
TERMINATE = 'Terminate'
VOLUME = 'Volume'
SLEEPING = 'Sleeping'

View File

@@ -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:

View File

@@ -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:

View File

@@ -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
View File

@@ -0,0 +1,8 @@
version: '3'
services:
vulkan:
build:
context: .
dockerfile: Dockerfile
env_file:
- .env

Binary file not shown.