diff --git a/PlexBot/__init__.py b/PlexBot/__init__.py index 6768d27..0b1228c 100644 --- a/PlexBot/__init__.py +++ b/PlexBot/__init__.py @@ -1,8 +1,6 @@ """ Plex music bot for discord. -Do not import this module, it is intended to be -used exclusively within a docker environment. """ import logging import sys @@ -19,7 +17,7 @@ plex_log = logging.getLogger("Plex") bot_log = logging.getLogger("Bot") -def load_config(filename: str) -> Dict[str, str]: +def load_config(basedir: str,filename: str) -> Dict[str, str]: """Loads config from yaml file Grabs key/value config pairs from a file. @@ -35,12 +33,12 @@ def load_config(filename: str) -> Dict[str, str]: """ # All config files should be in /config # for docker deployment. - filename = Path("/config", filename) + filename = Path(basedir, filename) try: with open(filename, "r") as config_file: config = yaml.safe_load(config_file) except FileNotFoundError: - root_log.fatal("Configuration file not found.") + root_log.fatal("Configuration file not found at '"+str(filename)+"'.") sys.exit(-1) # Convert str level type to logging constant @@ -56,7 +54,7 @@ def load_config(filename: str) -> Dict[str, str]: config["plex"]["log_level"] = levels[config["plex"]["log_level"].upper()] config["discord"]["log_level"] = levels[config["discord"]["log_level"].upper()] - if config["lyrics"]["token"].lower() == "none": - config["lyrics"]["token"] = None + if config["lyrics"] and config["lyrics"]["token"].lower() == "none": + config["lyrics"] = None return config diff --git a/PlexBot/__main__.py b/PlexBot/__main__.py index fc8fb9c..57d1982 100644 --- a/PlexBot/__main__.py +++ b/PlexBot/__main__.py @@ -11,7 +11,11 @@ from .bot import General from .bot import Plex # Load config from file -config = load_config("config.yaml") +configdir = "config" +from os import geteuid +if geteuid() == 0: + configdir = "/config" +config = load_config(configdir,"config.yaml") BOT_PREFIX = config["discord"]["prefix"] TOKEN = config["discord"]["token"] @@ -20,8 +24,11 @@ BASE_URL = config["plex"]["base_url"] PLEX_TOKEN = config["plex"]["token"] LIBRARY_NAME = config["plex"]["library_name"] -LYRICS_TOKEN = config["lyrics"]["token"] - +if config["lyrics"]: + LYRICS_TOKEN = config["lyrics"]["token"] +else: + LYRICS_TOKEN = None + # Set appropiate log level root_log = logging.getLogger() plex_log = logging.getLogger("Plex") diff --git a/PlexBot/bot.py b/PlexBot/bot.py index 9dbb53c..d077981 100644 --- a/PlexBot/bot.py +++ b/PlexBot/bot.py @@ -6,7 +6,6 @@ from urllib.request import urlopen import requests import discord -import lyricsgenius from async_timeout import timeout from discord import FFmpegPCMAudio from discord.ext import commands @@ -32,11 +31,13 @@ Plex: play - Play a song from the plex server. album - Queue an entire album to play. playlist - Queue an entire playlist to play. + show_playlists - Query for playlists with a name matching any of the arguments. lyrics - Print the lyrics of the song (Requires Genius API) np - Print the current playing song. stop - Halt playback and leave vc. pause - Pause playback. resume - Resume playback. + skip - Skip the current song. clear - Clear play queue. [] - Optional args. @@ -182,6 +183,7 @@ class Plex(commands.Cog): self.bot_prefix = bot.command_prefix if kwargs["lyrics_token"]: + import lyricsgenius self.genius = lyricsgenius.Genius(kwargs["lyrics_token"]) else: plex_log.warning("No lyrics token specified, lyrics disabled") @@ -266,6 +268,15 @@ class Plex(commands.Cog): except NotFound: raise MediaNotFoundError("Playlist cannot be found") + def _get_playlists(self): + """ + Search the Plex music db for playlist + + Returns: + List of plexapi.playlist + """ + return self.pms.playlists() + async def _play(self): """ Heavy lifting of playing songs @@ -436,7 +447,7 @@ class Plex(commands.Cog): return embed, art_file @staticmethod - def _build_embed_playlist(self, playlist): + def _build_embed_playlist(self, playlist, title, descrip): """ Creates a pretty embed card for playlists @@ -455,13 +466,14 @@ class Plex(commands.Cog): None """ # Grab the relevant thumbnail - img_stream = requests.get(self.pms.url(playlist.composite, True), stream=True).raw - img = io.BytesIO(img_stream.read()) + try: + img_stream = requests.get(self.pms.url(playlist.composite, True), stream=True).raw + img = io.BytesIO(img_stream.read()) + except: + raise MediaNotFoundError("no image available") # 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() @@ -614,13 +626,52 @@ class Plex(commands.Cog): 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) + try: + embed, img = self._build_embed_playlist(self, playlist, "Added playlist to queue", playlist.title) + await ctx.send(embed=embed, file=img) - for item in playlist.items(): - if (item.TYPE == "track"): - await self.play_queue.put(item) + for item in playlist.items(): + if (item.TYPE == "track"): + await self.play_queue.put(item) + bot_log.debug("Added to queue - %s", title) + except MediaNotFoundError: + await ctx.send(message="Playlist "+title+" seems to be empty!") + bot_log.debug("Playlist empty - %s", title) + @command() + async def show_playlists(self, ctx, *args): + """ + User command to show playlists + + Searchs plex db and shows playlists matching. + + Args: + ctx: discord.ext.commands.Context message context from command + *args: String filter for playlist names + + Returns: + None + + Raises: + None + """ + # Save the context to use with async callbacks + self.ctx = ctx + + playlists = self._get_playlists() + + try: + await self._validate(ctx) + except VoiceChannelError: + pass + + for playlist in playlists: + if args and not any(arg in playlist.title for arg in args): + continue + from datetime import timedelta + if playlist.duration: + seconds = playlist.duration / 1000 + embed, img = self._build_embed_playlist(self, playlist, playlist.title, "{:0>8}".format(str(timedelta(seconds=seconds)))) + await ctx.send(embed=embed, file=img) @command() async def stop(self, ctx):