From cf19147b6ccb5f236812b6b0ab4e9bfc3cc8afff Mon Sep 17 00:00:00 2001 From: Phin <59180111+phin05@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:40:41 +0530 Subject: [PATCH] Minor refactoring and tweaks --- README.md | 4 ++-- config/constants.py | 5 +++-- core/config.py | 8 ++++---- core/discord.py | 34 ++++++++++++++++------------------ main.py | 23 +++++++++++------------ 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 47dbefa..378db00 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ The "Display current activity as a status message" setting must be enabled in Di ## Configuration - Environment Variables -* `PLEX_SERVER_NAME` - Name of the Plex Media Server you wish to connect to. Used only during the initial setup (when there are no users in the config) for adding a server to the config after authentication. If this isn't set, in interactive environments, the user is prompted for an input, and in non-interactive environments, "ServerName" is used as a placeholder, which can later be changed by editing the config file and restarting the script. +* `DRPP_PLEX_SERVER_NAME_INPUT` - This is used only during the initial setup (when there are no users in the config) as the name of the Plex server to be added to the config file after user authentication. If this isn't set, in interactive environments, the user is prompted for an input, and in non-interactive environments, "ServerName" is used as a placeholder, which can later be changed by editing the config file and restarting the script. ## Run with Docker @@ -201,7 +201,7 @@ For example, if the environment variable `XDG_RUNTIME_DIR` is set to `/run/user/ docker run -v ./drpp:/app/data -v /run/user/1000:/run/app:ro -d --restart unless-stopped --name drpp ghcr.io/phin05/discord-rich-presence-plex:latest ``` -If you're running the container for the first time (when there are no users in the config), make sure that the `PLEX_SERVER_NAME` environment variable is set (see the [environment variables](#configuration---environment-variables) section above), and check the container logs for the authentication link. +If you're running the container for the first time (when there are no users in the config), make sure that the `DRPP_PLEX_SERVER_NAME_INPUT` environment variable is set (see the [environment variables](#configuration---environment-variables) section above), and check the container logs for the authentication link. ### Containerised Discord diff --git a/config/constants.py b/config/constants.py index d8bd82d..fa3305c 100644 --- a/config/constants.py +++ b/config/constants.py @@ -8,7 +8,7 @@ plexClientID = "discord-rich-presence-plex" discordClientID = "413407336082833418" dataDirectoryPath = "data" -configFilePathRoot = os.path.join(dataDirectoryPath, "config") +configFilePathBase = os.path.join(dataDirectoryPath, "config") cacheFilePath = os.path.join(dataDirectoryPath, "cache.json") logFilePath = os.path.join(dataDirectoryPath, "console.log") @@ -16,4 +16,5 @@ isUnix = sys.platform in ["linux", "darwin"] processID = os.getpid() isInteractive = sys.stdin and sys.stdin.isatty() isInContainer = os.environ.get("DRPP_IS_IN_CONTAINER", "") == "true" -runtimeDirectory = "/run/app" +runtimeDirectory = "/run/app" if isInContainer else os.environ.get("XDG_RUNTIME_DIR", os.environ.get("TMPDIR", os.environ.get("TMP", os.environ.get("TEMP", "/tmp")))) +ipcPipeBase = runtimeDirectory if isUnix else r"\\?\pipe" diff --git a/core/config.py b/core/config.py index e9f6ad3..edc13dc 100644 --- a/core/config.py +++ b/core/config.py @@ -1,4 +1,4 @@ -from config.constants import configFilePathRoot +from config.constants import configFilePathBase from utils.dict import copyDict from utils.logging import logger import json @@ -36,12 +36,12 @@ def loadConfig() -> None: global configFileExtension, configFileType, configFilePath doesFileExist = False for i, (fileExtension, fileType) in enumerate(supportedConfigFileExtensions.items()): - doesFileExist = os.path.isfile(f"{configFilePathRoot}.{fileExtension}") + doesFileExist = os.path.isfile(f"{configFilePathBase}.{fileExtension}") isFirstItem = i == 0 if doesFileExist or isFirstItem: configFileExtension = fileExtension configFileType = fileType - configFilePath = f"{configFilePathRoot}.{configFileExtension}" + configFilePath = f"{configFilePathBase}.{configFileExtension}" if doesFileExist: break if doesFileExist: @@ -52,7 +52,7 @@ def loadConfig() -> None: else: loadedConfig = json.load(configFile) or {} except: - os.rename(configFilePath, f"{configFilePathRoot}-{time.time():.0f}.{configFileExtension}") + os.rename(configFilePath, f"{configFilePathBase}-{time.time():.0f}.{configFileExtension}") logger.exception("Failed to parse the config file. A new one will be created.") else: copyDict(loadedConfig, config) diff --git a/core/discord.py b/core/discord.py index c38b1ed..8f4a1f8 100644 --- a/core/discord.py +++ b/core/discord.py @@ -1,4 +1,4 @@ -from config.constants import discordClientID, isUnix, processID, runtimeDirectory +from config.constants import discordClientID, isUnix, processID, ipcPipeBase from typing import Any, Optional from utils.logging import logger import asyncio @@ -10,17 +10,15 @@ import time class DiscordIpcService: - def __init__(self, ipcPipeNumber: Optional[int]): - ipcPipeNumber = ipcPipeNumber or -1 - ipcPipeNumbers = range(10) if ipcPipeNumber == -1 else [ipcPipeNumber] - ipcPipeBase = (runtimeDirectory if os.path.isdir(runtimeDirectory) else os.environ.get("XDG_RUNTIME_DIR", os.environ.get("TMPDIR", os.environ.get("TMP", os.environ.get("TEMP", "/tmp"))))) if isUnix else r"\\?\pipe" - self.ipcPipes: list[str] = [] - for ipcPipeNumber in ipcPipeNumbers: - pipeFilename = f"discord-ipc-{ipcPipeNumber}" - self.ipcPipes.append(os.path.join(ipcPipeBase, pipeFilename)) - if ipcPipeBase == os.environ.get("XDG_RUNTIME_DIR"): - self.ipcPipes.append(os.path.join(ipcPipeBase, "app", "com.discordapp.Discord", pipeFilename)) - self.ipcPipes.append(os.path.join(ipcPipeBase, ".flatpak", "com.discordapp.Discord", "xdg-run", pipeFilename)) + def __init__(self, pipeNumber: Optional[int]): + pipeNumber = pipeNumber or -1 + pipeNumbers = range(10) if pipeNumber == -1 else [pipeNumber] + self.pipes: list[str] = [] + for pipeNumber in pipeNumbers: + pipeFilename = f"discord-ipc-{pipeNumber}" + self.pipes.append(os.path.join(ipcPipeBase, pipeFilename)) + self.pipes.append(os.path.join(ipcPipeBase, "app", "com.discordapp.Discord", pipeFilename)) + self.pipes.append(os.path.join(ipcPipeBase, ".flatpak", "com.discordapp.Discord", "xdg-run", pipeFilename)) self.loop: Optional[asyncio.AbstractEventLoop] = None self.pipeReader: Optional[asyncio.StreamReader] = None self.pipeWriter: Optional[asyncio.StreamWriter] = None @@ -29,24 +27,24 @@ class DiscordIpcService: async def handshake(self) -> None: if not self.loop: return - for ipcPipe in self.ipcPipes: + for pipe in self.pipes: try: if isUnix: - self.pipeReader, self.pipeWriter = await asyncio.open_unix_connection(ipcPipe) # pyright: ignore[reportGeneralTypeIssues,reportUnknownMemberType] + self.pipeReader, self.pipeWriter = await asyncio.open_unix_connection(pipe) # pyright: ignore[reportGeneralTypeIssues,reportUnknownMemberType] else: self.pipeReader = asyncio.StreamReader() - self.pipeWriter = (await self.loop.create_pipe_connection(lambda: asyncio.StreamReaderProtocol(self.pipeReader), ipcPipe))[0] # pyright: ignore[reportGeneralTypeIssues,reportUnknownMemberType] + self.pipeWriter = (await self.loop.create_pipe_connection(lambda: asyncio.StreamReaderProtocol(self.pipeReader), pipe))[0] # pyright: ignore[reportGeneralTypeIssues,reportUnknownMemberType] self.write(0, { "v": 1, "client_id": discordClientID }) if await self.read(): self.connected = True - logger.info(f"Connected to Discord IPC pipe {ipcPipe}") + logger.info(f"Connected to Discord IPC pipe {pipe}") break except FileNotFoundError: pass except: - logger.exception(f"An unexpected error occured while connecting to Discord IPC pipe {ipcPipe}") + logger.exception(f"An unexpected error occured while connecting to Discord IPC pipe {pipe}") if not self.connected: - logger.error(f"Discord IPC pipe not found (attempted pipes: {', '.join(self.ipcPipes)})") + logger.error(f"Discord IPC pipe not found (attempted pipes: {', '.join(self.pipes)})") async def read(self) -> Optional[Any]: if not self.pipeReader: diff --git a/main.py b/main.py index 2511309..9b1fe76 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,11 @@ from config.constants import isInContainer, runtimeDirectory +from utils.logging import logger import os import sys if isInContainer: if not os.path.isdir(runtimeDirectory): - print(f"Runtime directory does not exist. Make sure that it is mounted into the container at {runtimeDirectory}") + logger.error(f"Runtime directory does not exist. Ensure that it is mounted into the container at {runtimeDirectory}") exit(1) statResult = os.stat(runtimeDirectory) os.system(f"chown -R {statResult.st_uid}:{statResult.st_gid} {os.path.dirname(os.path.realpath(__file__))}") @@ -22,12 +23,10 @@ else: for packageName, packageVersion in requiredPackages.items(): if packageName not in installedPackages: package = f"{packageName}{f'=={packageVersion}' if packageVersion else ''}" - print(f"Installing missing dependency: {package}") + logger.info(f"Installing missing dependency: {package}") subprocess.run([sys.executable, "-m", "pip", "install", "-U", package], check = True) except Exception as e: - import traceback - traceback.print_exception(e) - print("An unexpected error occured during automatic installation of dependencies. Install them manually by running the following command: python -m pip install -U -r requirements.txt") + logger.exception("An unexpected error occured during automatic installation of dependencies. Install them manually by running the following command: python -m pip install -U -r requirements.txt") from config.constants import dataDirectoryPath, logFilePath, name, version, isInteractive from core.config import config, loadConfig, saveConfig @@ -35,15 +34,15 @@ from core.discord import DiscordIpcService from core.plex import PlexAlertListener, initiateAuth, getAuthToken from typing import Optional from utils.cache import loadCache -from utils.logging import logger, formatter +from utils.logging import formatter from utils.text import formatSeconds import logging import models.config import time def init() -> None: - if not os.path.exists(dataDirectoryPath): - os.mkdir(dataDirectoryPath) + if not os.path.isdir(dataDirectoryPath): + os.makedirs(dataDirectoryPath) for oldFilePath in ["config.json", "cache.json", "console.log"]: if os.path.isfile(oldFilePath): os.rename(oldFilePath, os.path.join(dataDirectoryPath, oldFilePath)) @@ -90,7 +89,7 @@ def authNewUser() -> Optional[models.config.User]: authToken = getAuthToken(id, code) if authToken: logger.info("Authentication successful") - serverName = os.environ.get("PLEX_SERVER_NAME") + serverName = os.environ.get("DRPP_PLEX_SERVER_NAME_INPUT") if not serverName: serverName = input("Enter the name of the Plex Media Server you wish to connect to: ") if isInteractive else "ServerName" return { "token": authToken, "servers": [{ "name": serverName }] } @@ -98,10 +97,10 @@ def authNewUser() -> Optional[models.config.User]: else: logger.info(f"Authentication timed out ({formatSeconds(180)})") -def testIpc(ipcPipeNumber: int) -> None: +def testIpc(pipeNumber: int) -> None: init() logger.info("Testing Discord IPC connection") - discordIpcService = DiscordIpcService(ipcPipeNumber) + discordIpcService = DiscordIpcService(pipeNumber) discordIpcService.connect() discordIpcService.setActivity({ "details": "details", @@ -124,6 +123,6 @@ if __name__ == "__main__": elif mode == "test-ipc": testIpc(int(sys.argv[2]) if len(sys.argv) > 2 else -1) else: - print(f"Invalid mode: {mode}") + logger.error(f"Invalid mode: {mode}") except KeyboardInterrupt: pass