Improved automatic installation of dependencies

This commit is contained in:
Phin 2024-02-10 20:52:27 +05:30
parent e819adb12f
commit 31e9628f26
4 changed files with 22 additions and 15 deletions

View file

@ -7,4 +7,5 @@ COPY requirements.txt .
RUN pip install -U -r requirements.txt --no-cache-dir RUN pip install -U -r requirements.txt --no-cache-dir
COPY . . COPY . .
ENV DRPP_IS_IN_CONTAINER=true ENV DRPP_IS_IN_CONTAINER=true
ENV DRPP_NO_PIP_INSTALL=true
ENTRYPOINT ["python", "main.py"] ENTRYPOINT ["python", "main.py"]

View file

@ -41,7 +41,7 @@ The config file is stored in a directory named `data`.
* `posters` * `posters`
* `enabled` (boolean, default: `false`) - Displays media posters if enabled. Requires `imgurClientID`. * `enabled` (boolean, default: `false`) - Displays media posters if enabled. Requires `imgurClientID`.
* `imgurClientID` (string, default: `""`) - [Obtention Instructions](#obtaining-an-imgur-client-id) * `imgurClientID` (string, default: `""`) - [Obtention Instructions](#obtaining-an-imgur-client-id)
* `maxSize` (int, default: `256`) * `maxSize` (int, default: `256`) - Maximum width and maximum height to use while downscaling posters before uploading them.
* `buttons` (list) - [Information](#buttons) * `buttons` (list) - [Information](#buttons)
* `label` (string) - The label to be displayed on the button. * `label` (string) - The label to be displayed on the button.
* `url` (string) - A web address or a [dynamic URL placeholder](#dynamic-button-urls). * `url` (string) - A web address or a [dynamic URL placeholder](#dynamic-button-urls).
@ -64,7 +64,7 @@ The config file is stored in a directory named `data`.
Discord can display up to 2 buttons in your Rich Presence. Discord can display up to 2 buttons in your Rich Presence.
Due to a strange Discord bug, these buttons are unresponsive or exhibit strange behaviour towards your own clicks, but other users are able to click on them to open their corresponding URLs. Due to a strange Discord bug, these buttons may be unresponsive or exhibit strange behaviour towards your own clicks, but other users are able to click on them to open their corresponding URLs.
#### Dynamic Button URLs #### Dynamic Button URLs
@ -90,8 +90,9 @@ display:
posters: posters:
enabled: true enabled: true
imgurClientID: 9e9sf637S8bRp4z imgurClientID: 9e9sf637S8bRp4z
maxSize: 256
buttons: buttons:
- label: IMDb Link - label: IMDb
url: dynamic:imdb url: dynamic:imdb
- label: My YouTube Channel - label: My YouTube Channel
url: https://www.youtube.com/channel/me url: https://www.youtube.com/channel/me
@ -123,11 +124,12 @@ users:
"useRemainingTime": false, "useRemainingTime": false,
"posters": { "posters": {
"enabled": true, "enabled": true,
"imgurClientID": "9e9sf637S8bRp4z" "imgurClientID": "9e9sf637S8bRp4z",
"maxSize": 256
}, },
"buttons": [ "buttons": [
{ {
"label": "IMDb Link", "label": "IMDb",
"url": "dynamic:imdb" "url": "dynamic:imdb"
}, },
{ {
@ -145,7 +147,6 @@ users:
}, },
{ {
"name": "A Friend's Server", "name": "A Friend's Server",
"listenForUser": "xyz",
"whitelistedLibraries": [ "whitelistedLibraries": [
"Movies" "Movies"
] ]
@ -167,6 +168,7 @@ The "Display current activity as a status message" setting must be enabled in Di
## Configuration - Environment Variables ## Configuration - Environment Variables
* `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. * `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.
* `DRPP_NO_PIP_INSTALL` - Automatic invocation of pip to install missing dependencies is skipped if this is set to `true`.
## Run with Docker ## Run with Docker
@ -247,7 +249,7 @@ services:
volumes: volumes:
- ./kasmcord:/run/user/1000 - ./kasmcord:/run/user/1000
user: "0" user: "0"
entrypoint: sh -c "chown -R kasm-user:kasm-user /run/user/1000 && su kasm-user -c '/dockerstartup/kasm_default_profile.sh /dockerstartup/vnc_startup.sh /dockerstartup/kasm_startup.sh'" entrypoint: sh -c "chmod 700 /run/user/1000 && chown -R kasm-user:kasm-user /run/user/1000 && su kasm-user -c '/dockerstartup/kasm_default_profile.sh /dockerstartup/vnc_startup.sh /dockerstartup/kasm_startup.sh'"
drpp: drpp:
container_name: drpp container_name: drpp
image: ghcr.io/phin05/discord-rich-presence-plex:latest image: ghcr.io/phin05/discord-rich-presence-plex:latest

View file

@ -15,6 +15,7 @@ logFilePath = os.path.join(dataDirectoryPath, "console.log")
isUnix = sys.platform in ["linux", "darwin"] 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()
noPipInstall = os.environ.get("DRPP_NO_PIP_INSTALL", "") == "true"
isInContainer = os.environ.get("DRPP_IS_IN_CONTAINER", "") == "true" isInContainer = os.environ.get("DRPP_IS_IN_CONTAINER", "") == "true"
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")))) 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" ipcPipeBase = runtimeDirectory if isUnix else r"\\?\pipe"

19
main.py
View file

@ -1,7 +1,6 @@
from config.constants import isInContainer, runtimeDirectory, uid, gid, containerCwd, noRuntimeDirChown from config.constants import isInContainer, runtimeDirectory, uid, gid, containerCwd, noRuntimeDirChown
from utils.logging import logger from utils.logging import logger
import os import os
import sys
if isInContainer: if isInContainer:
if not os.path.isdir(runtimeDirectory): if not os.path.isdir(runtimeDirectory):
@ -23,20 +22,24 @@ if isInContainer:
os.setuid(uid) # pyright: ignore[reportAttributeAccessIssue,reportUnknownMemberType] os.setuid(uid) # pyright: ignore[reportAttributeAccessIssue,reportUnknownMemberType]
else: else:
logger.warning(f"Not running as the superuser. Manually ensure appropriate ownership of mounted contents") logger.warning(f"Not running as the superuser. Manually ensure appropriate ownership of mounted contents")
else:
from config.constants import noPipInstall
import sys
if not noPipInstall:
try: try:
import subprocess import subprocess
def parsePipPackages(packagesStr: str) -> dict[str, str]: def parsePipPackages(packagesStr: str) -> dict[str, str]:
return { packageSplit[0]: packageSplit[1] if len(packageSplit) > 1 else "" for packageSplit in [package.split("==") for package in packagesStr.splitlines()] } return { packageSplit[0].lower(): packageSplit[1] if len(packageSplit) > 1 else "" for packageSplit in [package.split("==") for package in packagesStr.splitlines()] }
pipFreezeResult = subprocess.run([sys.executable, "-m", "pip", "freeze"], stdout = subprocess.PIPE, text = True, check = True) pipFreezeResult = subprocess.run([sys.executable, "-m", "pip", "freeze"], stdout = subprocess.PIPE, text = True, check = True)
installedPackages = parsePipPackages(pipFreezeResult.stdout) installedPackages = parsePipPackages(pipFreezeResult.stdout)
with open("requirements.txt", "r", encoding = "UTF-8") as requirementsFile: with open("requirements.txt", "r", encoding = "UTF-8") as requirementsFile:
requiredPackages = parsePipPackages(requirementsFile.read()) requiredPackages = parsePipPackages(requirementsFile.read())
for packageName, packageVersion in requiredPackages.items(): for packageName, requiredPackageVersion in requiredPackages.items():
if packageName not in installedPackages: installedPackageVersion = installedPackages.get(packageName, "none")
package = f"{packageName}{f'=={packageVersion}' if packageVersion else ''}" if installedPackageVersion != requiredPackageVersion:
logger.info(f"Installing missing dependency: {package}") logger.info(f"Installing dependency: {packageName} (required: {requiredPackageVersion}, installed: {installedPackageVersion})")
subprocess.run([sys.executable, "-m", "pip", "install", "-U", package], check = True) subprocess.run([sys.executable, "-m", "pip", "install", "-U", f"{packageName}=={requiredPackageVersion}"], check = True)
except Exception as e: except Exception as e:
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") 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")