25 Commits

Author SHA1 Message Date
6521e7e26c Merge pull request #25 from Raventhicc/patch-1
Added Lyrics in docker-compose.yml and slightly changed instructions
2021-11-28 14:54:32 -07:00
add1a1af0d Added Lyrics in docker-compose.yml and slightly changed instructions
This was just done to make it easier for people to understand since some people stumbled on this part.
2021-11-28 20:59:45 +05:30
8f05f5e27f Merge pull request #19 from profesaurus/feature_playlists
Feature playlists
2021-05-30 01:33:09 -06:00
eeb430c016 Fixed even more Codacy Static Code Analysis issues. 2021-05-27 15:39:50 -07:00
1ffa5a7229 Attempt to fix additional Codacy Static Code Analysis issues. 2021-05-27 15:35:34 -07:00
f4cd675502 Attempting to fix the Codacy Static Code Analysis issues. 2021-05-27 14:33:24 -07:00
921dfc02b8 Updated README.md to include the playlist command. Added the playlist command to bot.py comments. 2021-05-27 08:11:02 -07:00
9fb091e9e1 Added the ability to play a playlist with a new "playlist" command.
Ex. ?playlist MyPlaylistName
2021-05-26 09:28:57 -07:00
efdd604d65 Merge pull request #14 from jarulsamy/hotfix
Hotfix
2020-09-23 22:46:04 -06:00
7d6060ed15 🔖 Bump version 2020-09-23 22:40:20 -06:00
d6f5174d20 🐛 Bump discord version
Remedies a seemingly random socketio error:

```
    future: <Task finished coro=<VoiceClient._create_socket() done, defined at /usr/local/lib/python3.7/site-packages/discord/voice_client.py:172>
    exception=gaierror(-2, 'Name or service not known')>

    Traceback (most recent call last):
    File "/usr/local/lib/python3.7/site-packages/discord/voice_client.py", line 191, in _create_socket
    self.endpoint_ip = socket.gethostbyname(self.endpoint) socket.gaierror: [Errno -2] Name or service not known
```
2020-09-23 22:36:47 -06:00
151f650bb2 Merge pull request #13 from jarulsamy/dev
v1.0.2
2020-09-06 15:39:10 -06:00
5eceab7a22 📝 Add lyrics updates 2020-09-06 15:33:25 -06:00
545654f9a7 Add lyrics command
Optionally grab lyrics of a song using the
lyrics command. Required LyricsGenius API token.
2020-09-06 15:32:40 -06:00
57490cdf17 Merge pull request #12 from jarulsamy/dev
v1.0.1
2020-08-29 20:09:14 -06:00
a6be6eac37 Merge branch 'master' into dev 2020-08-29 20:08:38 -06:00
5ba7d751d0 👥 Add label of maintainer 2020-08-14 20:57:03 -06:00
d0b6c25359 Pinned dependencies 2020-08-13 03:21:00 -06:00
db188d968b 🎨 Static methods 2020-08-13 03:20:41 -06:00
b05dd0598b Merge pull request #11 from jarulsamy/dev
v1.0.1
2020-08-13 02:53:46 -06:00
cf2bc24f8a 🐛 Fix always skip push 2020-08-10 03:03:54 -06:00
1839cc5d03 Merge pull request #10 from codacy-badger/codacy-badge
Add a Codacy badge to README.md
2020-08-10 02:42:52 -06:00
e6c4b84538 Add Codacy badge 2020-08-10 08:42:13 +00:00
c1cba637b8 Merge pull request #9 from jarulsamy/dev
v1.0.0
2020-08-10 02:37:42 -06:00
3e90c9e9ef Merge pull request #8 from jarulsamy/dev
Docs Overhaul
2020-08-09 15:26:54 -06:00
9 changed files with 270 additions and 49 deletions

View File

@ -1,8 +1,10 @@
FROM python:3.7-slim FROM python:3.7-slim
LABEL maintainer="Joshua Arulsamy <joshua.gf.arul@gmail.com>"
# Install ffmpeg # Install ffmpeg
RUN apt-get -y update && \ RUN apt-get -y update && \
apt-get install -y --no-install-recommends ffmpeg && \ apt-get install -y --no-install-recommends ffmpeg=7:4.1.6-1~deb10u1 && \
apt-get autoremove -y && \ apt-get autoremove -y && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*

11
Jenkinsfile vendored
View File

@ -25,6 +25,7 @@ pipeline {
sh ''' conda create --yes -n ${BUILD_TAG} python sh ''' conda create --yes -n ${BUILD_TAG} python
source /var/lib/jenkins/miniconda3/etc/profile.d/conda.sh source /var/lib/jenkins/miniconda3/etc/profile.d/conda.sh
conda activate ${BUILD_TAG} conda activate ${BUILD_TAG}
pip install -r requirements.txt
pip install pylint pip install pylint
''' '''
} }
@ -47,18 +48,10 @@ pipeline {
} }
} }
steps { steps {
sh ''' source /var/lib/jenkins/miniconda3/etc/profile.d/conda.sh sh './deploy/build.sh'
conda activate ${BUILD_TAG}
./deploy/build.sh
'''
} }
} }
stage('Push Image') { stage('Push Image') {
when {
expression {
currentBuild.result == 'SUCCESS'
}
}
steps { steps {
sh './deploy/push.sh' sh './deploy/push.sh'
} }

View File

@ -56,4 +56,7 @@ def load_config(filename: str) -> Dict[str, str]:
config["plex"]["log_level"] = levels[config["plex"]["log_level"].upper()] config["plex"]["log_level"] = levels[config["plex"]["log_level"].upper()]
config["discord"]["log_level"] = levels[config["discord"]["log_level"].upper()] config["discord"]["log_level"] = levels[config["discord"]["log_level"].upper()]
if config["lyrics"]["token"].lower() == "none":
config["lyrics"]["token"] = None
return config return config

View File

@ -20,6 +20,8 @@ BASE_URL = config["plex"]["base_url"]
PLEX_TOKEN = config["plex"]["token"] PLEX_TOKEN = config["plex"]["token"]
LIBRARY_NAME = config["plex"]["library_name"] LIBRARY_NAME = config["plex"]["library_name"]
LYRICS_TOKEN = config["lyrics"]["token"]
# Set appropiate log level # Set appropiate log level
root_log = logging.getLogger() root_log = logging.getLogger()
plex_log = logging.getLogger("Plex") plex_log = logging.getLogger("Plex")
@ -28,9 +30,16 @@ bot_log = logging.getLogger("Bot")
plex_log.setLevel(config["plex"]["log_level"]) plex_log.setLevel(config["plex"]["log_level"])
bot_log.setLevel(config["discord"]["log_level"]) bot_log.setLevel(config["discord"]["log_level"])
plex_args = {
"base_url": BASE_URL,
"plex_token": PLEX_TOKEN,
"lib_name": LIBRARY_NAME,
"lyrics_token": LYRICS_TOKEN,
}
bot = Bot(command_prefix=BOT_PREFIX) bot = Bot(command_prefix=BOT_PREFIX)
# Remove help command, we have our own custom one. # Remove help command, we have our own custom one.
bot.remove_command("help") bot.remove_command("help")
bot.add_cog(General(bot)) bot.add_cog(General(bot))
bot.add_cog(Plex(bot, BASE_URL, PLEX_TOKEN, LIBRARY_NAME, BOT_PREFIX)) bot.add_cog(Plex(bot, **plex_args))
bot.run(TOKEN) bot.run(TOKEN)

View File

@ -1,5 +1,5 @@
"""Track version number of package.""" """Track version number of package."""
VERSION = "1.0.1" VERSION = "1.0.3"
if __name__ == "__main__": if __name__ == "__main__":
print(VERSION) print(VERSION)

View File

@ -3,13 +3,16 @@ import asyncio
import io import io
import logging import logging
from urllib.request import urlopen from urllib.request import urlopen
import requests
import discord import discord
import lyricsgenius
from async_timeout import timeout from async_timeout import timeout
from discord import FFmpegPCMAudio from discord import FFmpegPCMAudio
from discord.ext import commands from discord.ext import commands
from discord.ext.commands import command from discord.ext.commands import command
from plexapi.exceptions import Unauthorized from plexapi.exceptions import Unauthorized
from plexapi.exceptions import NotFound
from plexapi.server import PlexServer from plexapi.server import PlexServer
from .exceptions import MediaNotFoundError from .exceptions import MediaNotFoundError
@ -28,6 +31,8 @@ General:
Plex: Plex:
play <SONG_NAME> - Play a song from the plex server. play <SONG_NAME> - Play a song from the plex server.
album <ALBUM_NAME> - Queue an entire album to play. album <ALBUM_NAME> - Queue an entire album to play.
playlist <PLAYLIST_NAME> - Queue an entire playlist to play.
lyrics - Print the lyrics of the song (Requires Genius API)
np - Print the current playing song. np - Print the current playing song.
stop - Halt playback and leave vc. stop - Halt playback and leave vc.
pause - Pause playback. pause - Pause playback.
@ -39,13 +44,15 @@ Plex:
class General(commands.Cog): class General(commands.Cog):
"""General commands """
General commands
Manage general bot behavior Manage general bot behavior
""" """
def __init__(self, bot): def __init__(self, bot):
"""Initialize commands """
Initialize commands
Args: Args:
bot: discord.ext.command.Bot, bind for cogs bot: discord.ext.command.Bot, bind for cogs
@ -60,7 +67,8 @@ class General(commands.Cog):
@command() @command()
async def kill(self, ctx, *args): async def kill(self, ctx, *args):
"""Kill the bot """
Kill the bot
Args: Args:
ctx: discord.ext.commands.Context message context from command ctx: discord.ext.commands.Context message context from command
@ -80,7 +88,8 @@ class General(commands.Cog):
@command(name="help") @command(name="help")
async def help(self, ctx): async def help(self, ctx):
"""Prints command help """
Prints command help
Args: Args:
ctx: discord.ext.commands.Context message context from command ctx: discord.ext.commands.Context message context from command
@ -96,7 +105,8 @@ class General(commands.Cog):
@command() @command()
async def cleanup(self, ctx, limit=250): async def cleanup(self, ctx, limit=250):
"""Delete old messages from bot """
Delete old messages from bot
Args: Args:
ctx: discord.ext.commands.Context message context from command ctx: discord.ext.commands.Context message context from command
@ -119,6 +129,15 @@ class General(commands.Cog):
except (discord.Forbidden, discord.NotFound, discord.HTTPException): except (discord.Forbidden, discord.NotFound, discord.HTTPException):
pass pass
async for i in channel.history(limit=limit):
if i.author == ctx.message.author and i.content.startswith(
self.bot.command_prefix
):
try:
await i.delete()
except (discord.Forbidden, discord.NotFound, discord.HTTPException):
pass
except discord.Forbidden: except discord.Forbidden:
bot_log.info("Unable to delete messages, insufficient permissions.") bot_log.info("Unable to delete messages, insufficient permissions.")
await ctx.send("I don't have the necessary permissions to delete messages.") await ctx.send("I don't have the necessary permissions to delete messages.")
@ -136,10 +155,9 @@ class Plex(commands.Cog):
# All are necessary to detect global interactions # All are necessary to detect global interactions
# within the bot. # within the bot.
def __init__( def __init__(self, bot, **kwargs):
self, bot, base_url: str, plex_token: str, lib_name: str, bot_prefix: str """
): Initializes Plex resources
"""Initializes Plex resources
Connects to Plex library and sets up Connects to Plex library and sets up
all asyncronous communications. all asyncronous communications.
@ -149,7 +167,6 @@ class Plex(commands.Cog):
base_url: str url to Plex server base_url: str url to Plex server
plex_token: str X-Token of Plex server plex_token: str X-Token of Plex server
lib_name: str name of Plex library to search through lib_name: str name of Plex library to search through
bot_prefix: str prefix used to interact with bots
Raises: Raises:
plexapi.exceptions.Unauthorized: Invalid Plex token plexapi.exceptions.Unauthorized: Invalid Plex token
@ -159,10 +176,16 @@ class Plex(commands.Cog):
""" """
self.bot = bot self.bot = bot
self.base_url = base_url self.base_url = kwargs["base_url"]
self.plex_token = plex_token self.plex_token = kwargs["plex_token"]
self.library_name = lib_name self.library_name = kwargs["lib_name"]
self.bot_prefix = bot_prefix self.bot_prefix = bot.command_prefix
if kwargs["lyrics_token"]:
self.genius = lyricsgenius.Genius(kwargs["lyrics_token"])
else:
plex_log.warning("No lyrics token specified, lyrics disabled")
self.genius = None
# Log fatal invalid plex token # Log fatal invalid plex token
try: try:
@ -188,7 +211,8 @@ class Plex(commands.Cog):
self.bot.loop.create_task(self._audio_player_task()) self.bot.loop.create_task(self._audio_player_task())
def _search_tracks(self, title: str): def _search_tracks(self, title: str):
"""Search the Plex music db for track """
Search the Plex music db for track
Args: Args:
title: str title of song to search for title: str title of song to search for
@ -206,7 +230,8 @@ class Plex(commands.Cog):
raise MediaNotFoundError("Track cannot be found") raise MediaNotFoundError("Track cannot be found")
def _search_albums(self, title: str): def _search_albums(self, title: str):
"""Search the Plex music db for album """
Search the Plex music db for album
Args: Args:
title: str title of album to search for title: str title of album to search for
@ -223,8 +248,27 @@ class Plex(commands.Cog):
except IndexError: except IndexError:
raise MediaNotFoundError("Album cannot be found") raise MediaNotFoundError("Album cannot be found")
def _search_playlists(self, title: str):
"""
Search the Plex music db for playlist
Args:
title: str title of playlist to search for
Returns:
plexapi.playlist pointing to best matching title
Raises:
MediaNotFoundError: Title of playlist can't be found in plex db
"""
try:
return self.pms.playlist(title)
except NotFound:
raise MediaNotFoundError("Playlist cannot be found")
async def _play(self): async def _play(self):
"""Heavy lifting of playing songs """
Heavy lifting of playing songs
Grabs the appropiate streaming URL, sends the `now playing` Grabs the appropiate streaming URL, sends the `now playing`
message, and initiates playback in the vc. message, and initiates playback in the vc.
@ -252,7 +296,8 @@ class Plex(commands.Cog):
self.np_message_id = await self.ctx.send(embed=embed, file=img) self.np_message_id = await self.ctx.send(embed=embed, file=img)
async def _audio_player_task(self): async def _audio_player_task(self):
"""Coroutine to handle playback and queuing """
Coroutine to handle playback and queuing
Always-running function awaiting new songs to be added. Always-running function awaiting new songs to be added.
Auto disconnects from VC if idle for > 15 seconds. Auto disconnects from VC if idle for > 15 seconds.
@ -286,7 +331,8 @@ class Plex(commands.Cog):
await self.np_message_id.delete() await self.np_message_id.delete()
def _toggle_next(self, error=None): def _toggle_next(self, error=None):
"""Callback for vc playback """
Callback for vc playback
Clears current track, then activates _audio_player_task Clears current track, then activates _audio_player_task
to play next in queue or disconnect. to play next in queue or disconnect.
@ -303,8 +349,10 @@ class Plex(commands.Cog):
self.current_track = None self.current_track = None
self.bot.loop.call_soon_threadsafe(self.play_next_event.set) self.bot.loop.call_soon_threadsafe(self.play_next_event.set)
def _build_embed_track(self, track, type_="play"): @staticmethod
"""Creates a pretty embed card for tracks def _build_embed_track(track, type_="play"):
"""
Creates a pretty embed card for tracks
Builds a helpful status embed with the following info: Builds a helpful status embed with the following info:
Status, song title, album, artist and album art. All Status, song title, album, artist and album art. All
@ -322,7 +370,7 @@ class Plex(commands.Cog):
ValueError: Unsupported type of embed {type_} ValueError: Unsupported type of embed {type_}
""" """
# Grab the relevant thumbnail # Grab the relevant thumbnail
img_stream = urlopen(track.thumbUrl) img_stream = requests.get(track.thumbUrl, stream=True).raw
img = io.BytesIO(img_stream.read()) img = io.BytesIO(img_stream.read())
# Attach to discord embed # Attach to discord embed
@ -350,8 +398,10 @@ class Plex(commands.Cog):
return embed, art_file return embed, art_file
def _build_embed_album(self, album): @staticmethod
"""Creates a pretty embed card for albums def _build_embed_album(album):
"""
Creates a pretty embed card for albums
Builds a helpful status embed with the following info: Builds a helpful status embed with the following info:
album, artist, and album art. All pertitent information album, artist, and album art. All pertitent information
@ -368,7 +418,7 @@ class Plex(commands.Cog):
None None
""" """
# Grab the relevant thumbnail # Grab the relevant thumbnail
img_stream = urlopen(album.thumbUrl) img_stream = requests.get(album.thumbUrl, stream=True).raw
img = io.BytesIO(img_stream.read()) img = io.BytesIO(img_stream.read())
# Attach to discord embed # Attach to discord embed
@ -385,8 +435,46 @@ class Plex(commands.Cog):
return embed, art_file return embed, art_file
@staticmethod
def _build_embed_playlist(self, playlist):
"""
Creates a pretty embed card for playlists
Builds a helpful status embed with the following info:
playlist art. All pertitent information
is grabbed dynamically from the Plex db.
Args:
playlist: plexapi.playlist object of playlist
Returns:
embed: discord.embed fully constructed payload.
thumb_art: io.BytesIO of playlist thumbnail img.
Raises:
None
"""
# Grab the relevant thumbnail
img_stream = requests.get(self.pms.url(playlist.composite, True), stream=True).raw
img = io.BytesIO(img_stream.read())
# Attach to discord embed
art_file = discord.File(img, filename="image0.png")
title = "Added playlist to queue"
descrip = f"{playlist.title}"
embed = discord.Embed(
title=title, description=descrip, colour=discord.Color.red()
)
embed.set_author(name="Plex")
embed.set_thumbnail(url="attachment://image0.png")
bot_log.debug("Built embed for playlist - %s", playlist.title)
return embed, art_file
async def _validate(self, ctx): async def _validate(self, ctx):
"""Ensures user is in a vc """
Ensures user is in a vc
Args: Args:
ctx: discord.ext.commands.Context message context from command ctx: discord.ext.commands.Context message context from command
@ -410,7 +498,8 @@ class Plex(commands.Cog):
@command() @command()
async def play(self, ctx, *args): async def play(self, ctx, *args):
"""User command to play song """
User command to play song
Searchs plex db and either, initiates playback, or Searchs plex db and either, initiates playback, or
adds to queue. Handles invalid usage from the user. adds to queue. Handles invalid usage from the user.
@ -452,7 +541,8 @@ class Plex(commands.Cog):
@command() @command()
async def album(self, ctx, *args): async def album(self, ctx, *args):
"""User command to play song """
User command to play song
Searchs plex db and either, initiates playback, or Searchs plex db and either, initiates playback, or
adds to queue. Handles invalid usage from the user. adds to queue. Handles invalid usage from the user.
@ -490,9 +580,52 @@ class Plex(commands.Cog):
for track in album.tracks(): for track in album.tracks():
await self.play_queue.put(track) await self.play_queue.put(track)
@command()
async def playlist(self, ctx, *args):
"""
User command to play playlist
Searchs plex db and either, initiates playback, or
adds to queue. Handles invalid usage from the user.
Args:
ctx: discord.ext.commands.Context message context from command
*args: Title of playlist to play
Returns:
None
Raises:
None
"""
# Save the context to use with async callbacks
self.ctx = ctx
title = " ".join(args)
try:
playlist = self._search_playlists(title)
except MediaNotFoundError:
await ctx.send(f"Can't find playlist: {title}")
bot_log.debug("Failed to queue playlist, can't find - %s", title)
return
try:
await self._validate(ctx)
except VoiceChannelError:
pass
bot_log.debug("Added to queue - %s", title)
embed, img = self._build_embed_playlist(self, playlist)
await ctx.send(embed=embed, file=img)
for item in playlist.items():
if (item.TYPE == "track"):
await self.play_queue.put(item)
@command() @command()
async def stop(self, ctx): async def stop(self, ctx):
"""User command to stop playback """
User command to stop playback
Stops playback and disconnects from vc. Stops playback and disconnects from vc.
@ -515,7 +648,8 @@ class Plex(commands.Cog):
@command() @command()
async def pause(self, ctx): async def pause(self, ctx):
"""User command to pause playback """
User command to pause playback
Pauses playback, but doesn't reset anything Pauses playback, but doesn't reset anything
to allow playback resuming. to allow playback resuming.
@ -536,7 +670,8 @@ class Plex(commands.Cog):
@command() @command()
async def resume(self, ctx): async def resume(self, ctx):
"""User command to resume playback """
User command to resume playback
Args: Args:
ctx: discord.ext.commands.Context message context from command ctx: discord.ext.commands.Context message context from command
@ -554,7 +689,8 @@ class Plex(commands.Cog):
@command() @command()
async def skip(self, ctx): async def skip(self, ctx):
"""User command to skip song in queue """
User command to skip song in queue
Skips currently playing song. If no other songs in Skips currently playing song. If no other songs in
queue, stops playback, otherwise moves to next song. queue, stops playback, otherwise moves to next song.
@ -576,7 +712,8 @@ class Plex(commands.Cog):
@command(name="np") @command(name="np")
async def now_playing(self, ctx): async def now_playing(self, ctx):
"""User command to get currently playing song. """
User command to get currently playing song.
Deletes old `now playing` status message, Deletes old `now playing` status message,
Creates a new one with up to date information. Creates a new one with up to date information.
@ -602,7 +739,8 @@ class Plex(commands.Cog):
@command() @command()
async def clear(self, ctx): async def clear(self, ctx):
"""User command to clear play queue. """
User command to clear play queue.
Args: Args:
ctx: discord.ext.commands.Context message context from command ctx: discord.ext.commands.Context message context from command
@ -616,3 +754,55 @@ class Plex(commands.Cog):
self.play_queue = asyncio.Queue() self.play_queue = asyncio.Queue()
bot_log.debug("Cleared queue") bot_log.debug("Cleared queue")
await ctx.send(":boom: Queue cleared.") await ctx.send(":boom: Queue cleared.")
@command()
async def lyrics(self, ctx):
"""
User command to get lyrics of a song.
Args:
ctx: discord.ext.commands.Context message context from command
Returns:
None
Raises:
None
"""
if not self.current_track:
plex_log.info("No song currently playing")
return
if self.genius:
plex_log.info(
"Searching for %s, %s",
self.current_track.title,
self.current_track.artist().title,
)
try:
song = self.genius.search_song(
self.current_track.title, self.current_track.artist().title
)
except TypeError:
self.genius = None
plex_log.error("Invalid genius token, disabling lyrics")
return
try:
lyrics = song.lyrics
# Split into 1950 char chunks
# Discord max message length is 2000
lines = [(lyrics[i : i + 1950]) for i in range(0, len(lyrics), 1950)]
for i in lines:
if i == "":
continue
# Apply code block format
i = f"```{i}```"
await ctx.send(i)
except (IndexError, TypeError):
plex_log.info("Could not find lyrics")
await ctx.send("Can't find lyrics for this song.")
else:
plex_log.warning("Attempted lyrics without valid token")

View File

@ -1,5 +1,6 @@
# Plex-Bot # Plex-Bot
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c93b8ff976ce4205a95046487917476b)](https://app.codacy.com/manual/jarulsamy/Plex-Bot?utm_source=github.com&utm_medium=referral&utm_content=jarulsamy/Plex-Bot&utm_campaign=Badge_Grade_Dashboard)
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html) [![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html)
![docker pulls](https://img.shields.io/docker/pulls/jarulsamy/plex-bot) ![docker pulls](https://img.shields.io/docker/pulls/jarulsamy/plex-bot)
![docker img size](https://img.shields.io/docker/image-size/jarulsamy/plex-bot) ![docker img size](https://img.shields.io/docker/image-size/jarulsamy/plex-bot)
@ -62,6 +63,9 @@ Plex-Bot runs entirely in a Docker container. Ensure you have Docker and docker-
token: "<PLEX_TOKEN>" token: "<PLEX_TOKEN>"
library_name: "<LIBRARY_NAME>" library_name: "<LIBRARY_NAME>"
log_level: "debug" log_level: "debug"
lyrics:
token: "none" # Add your token here if you enable lyrics
``` ```
4. Create a Discord bot application: 4. Create a Discord bot application:
@ -88,11 +92,25 @@ Plex-Bot runs entirely in a Docker container. Ensure you have Docker and docker-
* Add it to `config/config.yaml` in the appropiate spot. * Add it to `config/config.yaml` in the appropiate spot.
6. Customize remaining settings 6. Get your Lyrics Genius token (Optional):
If you would like to enable the lyrics feature of the bot, you need to signup for a free GeniusLyrics account, [here](https://genius.com/api-clients).
After you make an account:
1. Click New API Client
2. Set the app website url to: `https://github.com/jarulsamy/Plex-Bot`
3. Set the redirect url to: `http://localhost`
4. Copy the **Client Access Token** and replace `None` with your token in `config/config.yaml`
7. Customize remaining settings
Set any remaining settings in the config file that you would like. Such as music library, and base url of the Plex server. Set any remaining settings in the config file that you would like. Such as music library, and base url of the Plex server.
7. Start the service: 8. Start the service:
```bash ```bash
docker-compose up -d docker-compose up -d
@ -120,6 +138,8 @@ General:
Plex: Plex:
play <SONG_NAME> - Play a song from the plex server. play <SONG_NAME> - Play a song from the plex server.
album <ALBUM_NAME> - Queue an entire album to play. album <ALBUM_NAME> - Queue an entire album to play.
playlist <PLAYLIST_NAME> - Queue an entire playlist to play.
lyrics - Print the lyrics of the song (Requires Genius API)
np - Print the current playing song. np - Print the current playing song.
stop - Halt playback and leave vc. stop - Halt playback and leave vc.
pause - Pause playback. pause - Pause playback.

View File

@ -1,6 +1,7 @@
discord.py==1.3.4 discord.py==1.4.1
PlexAPI==4.0.0 PlexAPI==4.0.0
fuzzywuzzy==0.18.0 fuzzywuzzy==0.18.0
pynacl==1.4.0 pynacl==1.4.0
ffmpeg==1.4 ffmpeg==1.4
PyYAML==5.3.1 PyYAML==5.3.1
lyricsgenius==2.0.0

View File

@ -11,3 +11,6 @@ plex:
token: "<PLEX_TOKEN>" token: "<PLEX_TOKEN>"
library_name: "<LIBRARY_NAME>" library_name: "<LIBRARY_NAME>"
log_level: "debug" log_level: "debug"
lyrics:
token: <CLIENT_ACCESS_TOKEN>