Merge pull request #1 from jarulsamy/docker

Docker
This commit is contained in:
Joshua Arulsamy 2020-08-04 04:00:55 -06:00 committed by GitHub
commit da1f59de11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 14 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# Project specific # Project specific
config.yaml config.yaml
config/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

24
Dockerfile Normal file
View File

@ -0,0 +1,24 @@
# Python 3.7
FROM python:3.7
# Update system
RUN apt-get -y update
RUN apt-get -y upgrade
# Install ffmpeg
RUN apt-get install -y ffmpeg
# All source code
WORKDIR /src
# Copy of dependency manifest
COPY requirements.txt .
# Install all dependencies.
RUN pip install -r requirements.txt
# Copy PlexBot over to src.
COPY PlexBot/ PlexBot
# Run the bot
# CMD ["python", "-OO", "-m", "PlexBot"]
CMD ["python", "-m", "PlexBot"]

View File

@ -1,8 +1,37 @@
import logging
import sys
from pathlib import Path
from typing import Dict
import yaml import yaml
FORMAT = "%(asctime)s %(levelname)s: [%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
def load_config(filename: str) -> None: logging.basicConfig(format=FORMAT)
logger = logging.getLogger("PlexBot")
def load_config(filename: str) -> Dict[str, str]:
# All config files should be in /config
# for docker deployment.
filename = Path("/config", filename)
try:
with open(filename, "r") as f: with open(filename, "r") as f:
config = yaml.safe_load(f) config = yaml.safe_load(f)
except FileNotFoundError:
logging.fatal("Configuration file not found.")
sys.exit(-1)
# Convert str level type to logging constant
levels = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
}
level = config["general"]["log_level"]
config["general"]["log_level"] = levels[level.upper()]
return config return config

View File

@ -3,7 +3,11 @@ from discord.ext.commands import Bot
from .bot import General from .bot import General
from .bot import Plex from .bot import Plex
from PlexBot import load_config from PlexBot import load_config
from . import FORMAT
import logging
# Load config from file
config = load_config("config.yaml") config = load_config("config.yaml")
BOT_PREFIX = config["discord"]["prefix"] BOT_PREFIX = config["discord"]["prefix"]
@ -12,6 +16,12 @@ TOKEN = config["discord"]["token"]
BASE_URL = config["plex"]["base_url"] 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"]
LOG_LEVEL = config["general"]["log_level"]
# Set appropiate log level
logger = logging.getLogger("PlexBot")
logging.basicConfig(format=FORMAT)
logger.setLevel(LOG_LEVEL)
bot = Bot(command_prefix=BOT_PREFIX) bot = Bot(command_prefix=BOT_PREFIX)
bot.add_cog(General(bot)) bot.add_cog(General(bot))

View File

@ -1,3 +1,4 @@
import logging
from queue import Queue from queue import Queue
import discord import discord
@ -5,8 +6,11 @@ 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 fuzzywuzzy import fuzz from fuzzywuzzy import fuzz
from plexapi.exceptions import Unauthorized
from plexapi.server import PlexServer from plexapi.server import PlexServer
logger = logging.getLogger("PlexBot")
class General(commands.Cog): class General(commands.Cog):
def __init__(self, bot): def __init__(self, bot):
@ -16,6 +20,7 @@ class General(commands.Cog):
async def kill(self, ctx): async def kill(self, ctx):
await ctx.send(f"Stopping upon the request of {ctx.author.mention}") await ctx.send(f"Stopping upon the request of {ctx.author.mention}")
await self.bot.close() await self.bot.close()
logger.info(f"Stopping upon the request of {ctx.author.mention}")
class Plex(commands.Cog): class Plex(commands.Cog):
@ -25,12 +30,19 @@ class Plex(commands.Cog):
self.plex_token = plex_token self.plex_token = plex_token
self.library_name = lib_name self.library_name = lib_name
try:
self.pms = PlexServer(self.base_url, self.plex_token) self.pms = PlexServer(self.base_url, self.plex_token)
except Unauthorized:
logger.fatal("Invalid Plex token, stopping...")
raise Unauthorized("Invalid Plex token")
self.music = self.pms.library.section(self.library_name) self.music = self.pms.library.section(self.library_name)
self.vc = None self.vc = None
self.current_track = None
self.play_queue = Queue() self.play_queue = Queue()
# self.callback_ctx = None
logger.info("Started bot successfully")
def _search_tracks(self, title): def _search_tracks(self, title):
tracks = self.music.searchTracks() tracks = self.music.searchTracks()
@ -51,13 +63,24 @@ class Plex(commands.Cog):
await ctx.send(f"Hello {member}") await ctx.send(f"Hello {member}")
async def _after_callback(self, error=None): async def _after_callback(self, error=None):
logger.debug("After callbacked")
if self.play_queue.empty():
self.current_track = None
logger.debug("No tracks left in queue, returning")
else:
track = self.play_queue.get() track = self.play_queue.get()
audio_stream = FFmpegPCMAudio(track.getStreamURL()) audio_stream = FFmpegPCMAudio(track.getStreamURL())
self.current_track = track
logger.debug(f"Started playing next song in queue: {track.title}")
self.vc.play(audio_stream) self.vc.play(audio_stream)
await self.callback_ctx.send(f"Playing {track.title}") await self.callback_ctx.send(f"Playing {track.title}")
@command() @command()
async def play(self, ctx, *args): async def play(self, ctx, *args):
if not len(args):
await ctx.send(f"Usage: {BOT_PREFIX}play TITLE_OF_SONG")
return
title = " ".join(args) title = " ".join(args)
track = self._search_tracks(title) track = self._search_tracks(title)
if track: if track:
@ -67,17 +90,22 @@ class Plex(commands.Cog):
return return
if not self.vc: if not self.vc:
self.vc = await ctx.author.voice.channel.connect() self.vc = await ctx.author.voice.channel.connect()
logger.debug("Connected to vc.")
if self.vc.is_playing(): if self.vc.is_playing():
self.play_queue.put(track) self.play_queue.put(track)
self.callback_ctx = ctx self.callback_ctx = ctx
await ctx.send(f"Added {track.title} to queue.") await ctx.send(f"Added {track.title} to queue.")
logger.debug(f"Added {track.title} to queue.")
else: else:
audio_stream = FFmpegPCMAudio(track_url) audio_stream = FFmpegPCMAudio(track_url)
self.vc.play(audio_stream, after=self._after_callback) self.vc.play(audio_stream, after=self._after_callback)
self.current_track = track
logger.debug(f"Playing {track.title}")
await ctx.send(f"Playing {track.title}") await ctx.send(f"Playing {track.title}")
else: else:
await ctx.send("Song not found!") logger.debug(f"{title} was not found.")
await ctx.send(f"{title} was not found.")
@command() @command()
async def stop(self, ctx): async def stop(self, ctx):
@ -85,7 +113,6 @@ class Plex(commands.Cog):
self.vc.stop() self.vc.stop()
await self.vc.disconnect() await self.vc.disconnect()
self.vc = None self.vc = None
await ctx.send("Stopped") await ctx.send("Stopped")
@command() @command()
@ -102,7 +129,11 @@ class Plex(commands.Cog):
@command() @command()
async def skip(self, ctx): async def skip(self, ctx):
logger.debug("Skip")
if self.vc: if self.vc:
await self.vc.stop() self.vc.stop()
if not self.play_queue.empty():
await self._after_callback() await self._after_callback()
@command()
async def np(self, ctx):
await ctx.send(f"Currently playing: {self.current_track.title}")

13
docker-compose.yml Normal file
View File

@ -0,0 +1,13 @@
version: "3"
services:
plex-bot:
container_name: "PlexBot"
build: .
environment:
- PUID=1000
- PGID=1000
- TZ=America/Denver
# Required dir for configuration files
volumes:
- "./config:/config:ro"
restart: unless-stopped

View File

@ -4,3 +4,4 @@ fuzzywuzzy==0.18.0
python-Levenshtein==0.12.0 python-Levenshtein==0.12.0
pynacl==1.4.0 pynacl==1.4.0
ffmpeg==1.4 ffmpeg==1.4
PyYAML==5.3.1

View File

@ -1,3 +1,7 @@
general:
# Options: debug, info, warning, error, critical
log_level: "info"
discord: discord:
prefix: "?" prefix: "?"
token: "<BOT_TOKEN>" token: "<BOT_TOKEN>"