mirror of
https://github.com/phin05/discord-rich-presence-plex
synced 2024-11-25 02:50:17 +00:00
Minor refactoring and tweaks
This commit is contained in:
parent
1c72a08041
commit
cf19147b6c
5 changed files with 36 additions and 38 deletions
|
@ -165,7 +165,7 @@ The "Display current activity as a status message" setting must be enabled in Di
|
||||||
|
|
||||||
## Configuration - Environment Variables
|
## 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
|
## 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
|
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
|
### Containerised Discord
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ plexClientID = "discord-rich-presence-plex"
|
||||||
discordClientID = "413407336082833418"
|
discordClientID = "413407336082833418"
|
||||||
|
|
||||||
dataDirectoryPath = "data"
|
dataDirectoryPath = "data"
|
||||||
configFilePathRoot = os.path.join(dataDirectoryPath, "config")
|
configFilePathBase = os.path.join(dataDirectoryPath, "config")
|
||||||
cacheFilePath = os.path.join(dataDirectoryPath, "cache.json")
|
cacheFilePath = os.path.join(dataDirectoryPath, "cache.json")
|
||||||
logFilePath = os.path.join(dataDirectoryPath, "console.log")
|
logFilePath = os.path.join(dataDirectoryPath, "console.log")
|
||||||
|
|
||||||
|
@ -16,4 +16,5 @@ isUnix = sys.platform in ["linux", "darwin"]
|
||||||
processID = os.getpid()
|
processID = os.getpid()
|
||||||
isInteractive = sys.stdin and sys.stdin.isatty()
|
isInteractive = sys.stdin and sys.stdin.isatty()
|
||||||
isInContainer = os.environ.get("DRPP_IS_IN_CONTAINER", "") == "true"
|
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"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from config.constants import configFilePathRoot
|
from config.constants import configFilePathBase
|
||||||
from utils.dict import copyDict
|
from utils.dict import copyDict
|
||||||
from utils.logging import logger
|
from utils.logging import logger
|
||||||
import json
|
import json
|
||||||
|
@ -36,12 +36,12 @@ def loadConfig() -> None:
|
||||||
global configFileExtension, configFileType, configFilePath
|
global configFileExtension, configFileType, configFilePath
|
||||||
doesFileExist = False
|
doesFileExist = False
|
||||||
for i, (fileExtension, fileType) in enumerate(supportedConfigFileExtensions.items()):
|
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
|
isFirstItem = i == 0
|
||||||
if doesFileExist or isFirstItem:
|
if doesFileExist or isFirstItem:
|
||||||
configFileExtension = fileExtension
|
configFileExtension = fileExtension
|
||||||
configFileType = fileType
|
configFileType = fileType
|
||||||
configFilePath = f"{configFilePathRoot}.{configFileExtension}"
|
configFilePath = f"{configFilePathBase}.{configFileExtension}"
|
||||||
if doesFileExist:
|
if doesFileExist:
|
||||||
break
|
break
|
||||||
if doesFileExist:
|
if doesFileExist:
|
||||||
|
@ -52,7 +52,7 @@ def loadConfig() -> None:
|
||||||
else:
|
else:
|
||||||
loadedConfig = json.load(configFile) or {}
|
loadedConfig = json.load(configFile) or {}
|
||||||
except:
|
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.")
|
logger.exception("Failed to parse the config file. A new one will be created.")
|
||||||
else:
|
else:
|
||||||
copyDict(loadedConfig, config)
|
copyDict(loadedConfig, config)
|
||||||
|
|
|
@ -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 typing import Any, Optional
|
||||||
from utils.logging import logger
|
from utils.logging import logger
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -10,17 +10,15 @@ import time
|
||||||
|
|
||||||
class DiscordIpcService:
|
class DiscordIpcService:
|
||||||
|
|
||||||
def __init__(self, ipcPipeNumber: Optional[int]):
|
def __init__(self, pipeNumber: Optional[int]):
|
||||||
ipcPipeNumber = ipcPipeNumber or -1
|
pipeNumber = pipeNumber or -1
|
||||||
ipcPipeNumbers = range(10) if ipcPipeNumber == -1 else [ipcPipeNumber]
|
pipeNumbers = range(10) if pipeNumber == -1 else [pipeNumber]
|
||||||
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.pipes: list[str] = []
|
||||||
self.ipcPipes: list[str] = []
|
for pipeNumber in pipeNumbers:
|
||||||
for ipcPipeNumber in ipcPipeNumbers:
|
pipeFilename = f"discord-ipc-{pipeNumber}"
|
||||||
pipeFilename = f"discord-ipc-{ipcPipeNumber}"
|
self.pipes.append(os.path.join(ipcPipeBase, pipeFilename))
|
||||||
self.ipcPipes.append(os.path.join(ipcPipeBase, pipeFilename))
|
self.pipes.append(os.path.join(ipcPipeBase, "app", "com.discordapp.Discord", pipeFilename))
|
||||||
if ipcPipeBase == os.environ.get("XDG_RUNTIME_DIR"):
|
self.pipes.append(os.path.join(ipcPipeBase, ".flatpak", "com.discordapp.Discord", "xdg-run", pipeFilename))
|
||||||
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))
|
|
||||||
self.loop: Optional[asyncio.AbstractEventLoop] = None
|
self.loop: Optional[asyncio.AbstractEventLoop] = None
|
||||||
self.pipeReader: Optional[asyncio.StreamReader] = None
|
self.pipeReader: Optional[asyncio.StreamReader] = None
|
||||||
self.pipeWriter: Optional[asyncio.StreamWriter] = None
|
self.pipeWriter: Optional[asyncio.StreamWriter] = None
|
||||||
|
@ -29,24 +27,24 @@ class DiscordIpcService:
|
||||||
async def handshake(self) -> None:
|
async def handshake(self) -> None:
|
||||||
if not self.loop:
|
if not self.loop:
|
||||||
return
|
return
|
||||||
for ipcPipe in self.ipcPipes:
|
for pipe in self.pipes:
|
||||||
try:
|
try:
|
||||||
if isUnix:
|
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:
|
else:
|
||||||
self.pipeReader = asyncio.StreamReader()
|
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 })
|
self.write(0, { "v": 1, "client_id": discordClientID })
|
||||||
if await self.read():
|
if await self.read():
|
||||||
self.connected = True
|
self.connected = True
|
||||||
logger.info(f"Connected to Discord IPC pipe {ipcPipe}")
|
logger.info(f"Connected to Discord IPC pipe {pipe}")
|
||||||
break
|
break
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
except:
|
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:
|
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]:
|
async def read(self) -> Optional[Any]:
|
||||||
if not self.pipeReader:
|
if not self.pipeReader:
|
||||||
|
|
23
main.py
23
main.py
|
@ -1,10 +1,11 @@
|
||||||
from config.constants import isInContainer, runtimeDirectory
|
from config.constants import isInContainer, runtimeDirectory
|
||||||
|
from utils.logging import logger
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if isInContainer:
|
if isInContainer:
|
||||||
if not os.path.isdir(runtimeDirectory):
|
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)
|
exit(1)
|
||||||
statResult = os.stat(runtimeDirectory)
|
statResult = os.stat(runtimeDirectory)
|
||||||
os.system(f"chown -R {statResult.st_uid}:{statResult.st_gid} {os.path.dirname(os.path.realpath(__file__))}")
|
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():
|
for packageName, packageVersion in requiredPackages.items():
|
||||||
if packageName not in installedPackages:
|
if packageName not in installedPackages:
|
||||||
package = f"{packageName}{f'=={packageVersion}' if packageVersion else ''}"
|
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)
|
subprocess.run([sys.executable, "-m", "pip", "install", "-U", package], check = True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
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")
|
||||||
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")
|
|
||||||
|
|
||||||
from config.constants import dataDirectoryPath, logFilePath, name, version, isInteractive
|
from config.constants import dataDirectoryPath, logFilePath, name, version, isInteractive
|
||||||
from core.config import config, loadConfig, saveConfig
|
from core.config import config, loadConfig, saveConfig
|
||||||
|
@ -35,15 +34,15 @@ from core.discord import DiscordIpcService
|
||||||
from core.plex import PlexAlertListener, initiateAuth, getAuthToken
|
from core.plex import PlexAlertListener, initiateAuth, getAuthToken
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from utils.cache import loadCache
|
from utils.cache import loadCache
|
||||||
from utils.logging import logger, formatter
|
from utils.logging import formatter
|
||||||
from utils.text import formatSeconds
|
from utils.text import formatSeconds
|
||||||
import logging
|
import logging
|
||||||
import models.config
|
import models.config
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def init() -> None:
|
def init() -> None:
|
||||||
if not os.path.exists(dataDirectoryPath):
|
if not os.path.isdir(dataDirectoryPath):
|
||||||
os.mkdir(dataDirectoryPath)
|
os.makedirs(dataDirectoryPath)
|
||||||
for oldFilePath in ["config.json", "cache.json", "console.log"]:
|
for oldFilePath in ["config.json", "cache.json", "console.log"]:
|
||||||
if os.path.isfile(oldFilePath):
|
if os.path.isfile(oldFilePath):
|
||||||
os.rename(oldFilePath, os.path.join(dataDirectoryPath, oldFilePath))
|
os.rename(oldFilePath, os.path.join(dataDirectoryPath, oldFilePath))
|
||||||
|
@ -90,7 +89,7 @@ def authNewUser() -> Optional[models.config.User]:
|
||||||
authToken = getAuthToken(id, code)
|
authToken = getAuthToken(id, code)
|
||||||
if authToken:
|
if authToken:
|
||||||
logger.info("Authentication successful")
|
logger.info("Authentication successful")
|
||||||
serverName = os.environ.get("PLEX_SERVER_NAME")
|
serverName = os.environ.get("DRPP_PLEX_SERVER_NAME_INPUT")
|
||||||
if not serverName:
|
if not serverName:
|
||||||
serverName = input("Enter the name of the Plex Media Server you wish to connect to: ") if isInteractive else "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 }] }
|
return { "token": authToken, "servers": [{ "name": serverName }] }
|
||||||
|
@ -98,10 +97,10 @@ def authNewUser() -> Optional[models.config.User]:
|
||||||
else:
|
else:
|
||||||
logger.info(f"Authentication timed out ({formatSeconds(180)})")
|
logger.info(f"Authentication timed out ({formatSeconds(180)})")
|
||||||
|
|
||||||
def testIpc(ipcPipeNumber: int) -> None:
|
def testIpc(pipeNumber: int) -> None:
|
||||||
init()
|
init()
|
||||||
logger.info("Testing Discord IPC connection")
|
logger.info("Testing Discord IPC connection")
|
||||||
discordIpcService = DiscordIpcService(ipcPipeNumber)
|
discordIpcService = DiscordIpcService(pipeNumber)
|
||||||
discordIpcService.connect()
|
discordIpcService.connect()
|
||||||
discordIpcService.setActivity({
|
discordIpcService.setActivity({
|
||||||
"details": "details",
|
"details": "details",
|
||||||
|
@ -124,6 +123,6 @@ if __name__ == "__main__":
|
||||||
elif mode == "test-ipc":
|
elif mode == "test-ipc":
|
||||||
testIpc(int(sys.argv[2]) if len(sys.argv) > 2 else -1)
|
testIpc(int(sys.argv[2]) if len(sys.argv) > 2 else -1)
|
||||||
else:
|
else:
|
||||||
print(f"Invalid mode: {mode}")
|
logger.error(f"Invalid mode: {mode}")
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in a new issue