diff --git a/.dockerignore b/.dockerignore index 42802ab..8241b7b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,7 +3,6 @@ # Allow files and directories !/main.py -!/start.sh !/Pipfile !/Pipfile.lock !/modules @@ -11,7 +10,7 @@ !/scripts !/config !/VERSION - +!/root # Ignore unnecessary files inside allowed directories # This should go after the allowed directories diff --git a/Dockerfile b/Dockerfile index 3dc7155..ff9f530 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,41 @@ -# Stage 1: Create an intermediate image for installing pipenv and converting Pipfile to requirements.txt -FROM python:3.11-slim as pipenv +FROM hotio/base:alpinevpn -# Copy Pipfile and Pipfile.lock to the intermediate image -COPY Pipfile Pipfile.lock ./ +# Set working directory +WORKDIR /app + +# Copy to the working directory +COPY . . + +# Install Python +RUN apk add --no-cache python3 + +# Install pip3 +RUN apk add --no-cache py3-pip # Install pipenv and use it to generate requirements.txt RUN pip3 install --no-cache-dir --upgrade pipenv; \ pipenv requirements > requirements.txt -# Debugging: Display the contents of requirements.txt -RUN cat requirements.txt +# Install gcc for building Python dependencies; install app dependencies +RUN apk update; \ + apk install -y gcc; \ + pip3 install --no-cache-dir -r requirements.txt -# Stage 2: Create an intermediate image for installing Python dependencies from requirements.txt -FROM python:3.11-slim as python-reqs +# Install wget, curl, unzip, p7zip-full, tzdata, vim +RUN apk add wget curl unzip tzdata && \ + apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ --allow-untrusted p7zip -# Copy requirements.txt from the pipenv stage to the intermediate image -COPY --from=pipenv /requirements.txt requirements.txt +# # Install rclone +# RUN curl https://rclone.org/install.sh | bash -# Install gcc for building Python dependencies; install app dependencies -RUN apt-get update; \ - apt-get install -y gcc; \ - pip3 install --no-cache-dir -r requirements.txt +# # Install rclone dependencies +# RUN apk add --no-cache ca-certificates fuse -# Stage 3: Create the final image with the application and rclone setup -FROM python:3.11-slim +# # Test rclone installation +# RUN rclone --version + +# # Install docker-cli +# RUN apk add --no-cache docker-cli # Metadata and labels LABEL maintainer="Drazzilb" \ @@ -32,51 +44,13 @@ LABEL maintainer="Drazzilb" \ org.opencontainers.image.authors="Drazzilb" \ org.opencontainers.image.title="userScripts" -# Set working directory and copy Python packages from the python-reqs stage -WORKDIR /app - -COPY --from=python-reqs /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages - # Set script environment variables -ENV XDG_CONFIG_HOME=/config -ENV US_CONFIG=/config/config.yml -ENV US_LOGS=/config/logs +ENV CONFIG_DIR=/config +ENV DATA_DIR=/data +ENV LOG_DIR=/config/logs ENV TZ=America/Los_Angeles -# Delete unnecessary setup files -RUN set -eux; \ - rm -f Pipfile Pipfile.lock; \ - groupadd -g 99 dockeruser; \ - useradd -u 100 -g 99 dockeruser; \ - apt-get update; \ - apt-get install -y --no-install-recommends wget curl unzip p7zip-full tzdata vim; - -# Install rclone dependencies and rclone -RUN curl https://rclone.org/install.sh | bash && \ - apt-get remove -y curl && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -# Test rclone installation -RUN rclone --version - -# Install Docker CLI inside the container -RUN apt-get update; \ - apt-get install -y --no-install-recommends docker-cli; \ - apt-get clean; \ - rm -rf /var/lib/apt/lists/* - -# Share the Docker socket with the container -VOLUME /var/run/docker.sock - -# Test rclone installation -RUN rclone --version - -# Copy the application source code into the container -COPY . . - -# Give permissions to all files under /app/scripts -RUN chmod -R 777 /app/scripts +VOLUME [ "/config" ] +VOLUME [ "/data" ] -CMD ["python", "main.py"] -ENTRYPOINT ["bash", "./start.sh"] \ No newline at end of file +COPY root/ / diff --git a/config/config.sample.yml b/config/config.sample.yml index 3da1d94..b2de1e3 100755 --- a/config/config.sample.yml +++ b/config/config.sample.yml @@ -17,7 +17,8 @@ schedule: border_replacerr: health_checkarr: labelarr: - nohl: + nohl: + sync_gdrive: poster_cleanarr: poster_renamerr: queinatorr: diff --git a/main.py b/main.py index 1a0b14c..56ad0be 100755 --- a/main.py +++ b/main.py @@ -43,7 +43,7 @@ "upgradinatorr", "unmatched_assets", ] - + def run_module(module_name, logger): diff --git a/modules/poster_cleanarr.py b/modules/poster_cleanarr.py index 857c699..e7dbe3a 100755 --- a/modules/poster_cleanarr.py +++ b/modules/poster_cleanarr.py @@ -369,7 +369,7 @@ def main(): # Check if assets exist, log and exit if not found if not all(assets_dict.values()): - logger.error("No assets found, Check asset_folders setting in your config. Exiting.") + logger.error("No assets found, Please double check your settings. Exiting...") sys.exit() # Check if media exists, log and exit if not found diff --git a/modules/unmatched_assets.py b/modules/unmatched_assets.py index 9fee5e9..982243c 100755 --- a/modules/unmatched_assets.py +++ b/modules/unmatched_assets.py @@ -66,8 +66,8 @@ def match_assets(assets_dict, media_dict, ignore_root_folders): if media_type in media_dict and media_type in assets_dict: # Iterate through each media data in the media dictionary of the current type for media_data in tqdm(media_dict[media_type], desc=f"Matching {media_type}", unit="media", total=len(media_dict[media_type]), leave=True, disable=None): - # Check if the media is released, ended, or continuing - if media_type in ['series', 'movies'] and not media_data['status'] in ['released', 'ended', 'continuing']: + # Check if the media is released, ended, or continuing or not monitored + if media_type in ['series', 'movies'] and not media_data['status'] in ['released', 'ended', 'continuing'] or media_type in ['series', 'movies'] and not media_data['monitored']: continue if media_type == "collections": location = media_data['location'] diff --git a/root/etc/s6-overlay/s6-rc.d/init-setup-app/dependencies.d/init-setup b/root/etc/s6-overlay/s6-rc.d/init-setup-app/dependencies.d/init-setup new file mode 100644 index 0000000..e69de29 diff --git a/root/etc/s6-overlay/s6-rc.d/init-setup-app/run b/root/etc/s6-overlay/s6-rc.d/init-setup-app/run new file mode 100644 index 0000000..6a0a476 --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/init-setup-app/run @@ -0,0 +1,34 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +umask "${UMASK}" + +echo " +------------------------------------------------ + Starting userScripts + PUID: ${PUID} + PGID: ${PGID} + UMASK: ${UMASK} + CONFIG_DIR: ${CONFIG_DIR} + DATA_DIR: ${DATA_DIR} + APP_DIR: ${APP_DIR} +------------------------------------------------ +" + +if [[ ! -f "${CONFIG_DIR}/config.yml" || ! -f "${CONFIG_DIR}/config.sample.yml" ]]; then + echo "Config file not found. Copying config.sample.yml to ${CONFIG_DIR}" + cp /app/config/config.sample.yml "${CONFIG_DIR}" +fi +if [[ ! -f "${CONFIG_DIR}/backup-appdata.conf" || ! -f "${CONFIG_DIR}/backup-appdata-example.conf" ]]; then + echo "Backup appdata config file not found. Copying backup-appdata-example.conf to ${CONFIG_DIR}" + cp /app/config/backup-appdata-example.conf "${CONFIG_DIR}" +fi +if [[ ! -f "${CONFIG_DIR}/backup-plex.conf" || ! -f "${CONFIG_DIR}/backup-plex-example.conf" ]]; then + echo "Backup plex config file not found. Copying backup-plex-example.conf to ${CONFIG_DIR}" + cp /app/config/backup-plex-example.conf "${CONFIG_DIR}" +fi + +echo "Setting permissions for ${CONFIG_DIR} and ${DATA_DIR} to ${PUID}:${PGID}" +chown hotio:hotio -R "${CONFIG_DIR}" +chown hotio:hotio -R "${DATA_DIR}" +chown hotio:hotio -R "${APP_DIR}" diff --git a/root/etc/s6-overlay/s6-rc.d/init-setup-app/type b/root/etc/s6-overlay/s6-rc.d/init-setup-app/type new file mode 100644 index 0000000..bdd22a1 --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/init-setup-app/type @@ -0,0 +1 @@ +oneshot diff --git a/root/etc/s6-overlay/s6-rc.d/init-setup-app/up b/root/etc/s6-overlay/s6-rc.d/init-setup-app/up new file mode 100644 index 0000000..2338a3f --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/init-setup-app/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-setup-app/run diff --git a/root/etc/s6-overlay/s6-rc.d/service-userscripts/dependencies.d/init-wireguard b/root/etc/s6-overlay/s6-rc.d/service-userscripts/dependencies.d/init-wireguard new file mode 100644 index 0000000..e69de29 diff --git a/root/etc/s6-overlay/s6-rc.d/service-userscripts/finish b/root/etc/s6-overlay/s6-rc.d/service-userscripts/finish new file mode 100644 index 0000000..fd13af5 --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/service-userscripts/finish @@ -0,0 +1,11 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +if test "$1" -eq 256 ; then + e=$((128 + $2)) +else + e="$1" +fi + +echo "$e" > /run/s6-linux-init-container-results/exitcode +/run/s6/basedir/bin/halt diff --git a/root/etc/s6-overlay/s6-rc.d/service-userscripts/run b/root/etc/s6-overlay/s6-rc.d/service-userscripts/run new file mode 100644 index 0000000..dde2edd --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/service-userscripts/run @@ -0,0 +1,7 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +umask "${UMASK}" + +# shellcheck disable=SC2086 +exec s6-setuidgid hotio python3 "${APP_DIR}/main.py" diff --git a/root/etc/s6-overlay/s6-rc.d/service-userscripts/type b/root/etc/s6-overlay/s6-rc.d/service-userscripts/type new file mode 100644 index 0000000..5883cff --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/service-userscripts/type @@ -0,0 +1 @@ +longrun diff --git a/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup-app b/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup-app new file mode 100644 index 0000000..e69de29 diff --git a/root/etc/s6-overlay/s6-rc.d/user/contents.d/service-userscripts b/root/etc/s6-overlay/s6-rc.d/user/contents.d/service-userscripts new file mode 100644 index 0000000..e69de29 diff --git a/start.sh b/start.sh deleted file mode 100644 index 6acbf3f..0000000 --- a/start.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -PUID=${PUID:-100} -PGID=${PGID:-99} -UMASK=${UMASK:-002} - -# Create a new group called dockeruser with the specified group ID -umask $UMASK - -# Create a new user called dockeruser with the specified user ID -groupmod -o -g "$PGID" dockeruser - -# Modify the group ID of the dockeruser -usermod -o -u "$PUID" dockeruser - -# Copy contents of /app/config to /config -cp -Rn /app/config/* /config - -# Change permissions of the /config directory to 777 -chmod -R 777 /config - -# Loop through all mounted volumes -for volume in $(find / -mount | cut -d' ' -f3); do - # Skip over read-only volumes - if mount | grep -F "$volume" | grep -q "(ro,"; then - continue - fi - if [[ "$volume" =~ ^/(root|home|etc|var|boot|usr|mnt|lib|bin|sbin|lib64|proc|sys|dev|run|tmp|media|srv|opt|snap) ]]; then - continue - fi - # Change ownership recursively - chown -R $PUID:$PGID "$volume" -done - -# Run the command as the dockeruser -exec runuser -u dockeruser -g dockeruser -- "$@" \ No newline at end of file diff --git a/util/config.py b/util/config.py index a4ade39..c603efc 100755 --- a/util/config.py +++ b/util/config.py @@ -3,24 +3,28 @@ import os from pathlib import Path from util.logger import setup_logger +from util.utility import * import time -logger = setup_logger("info", "main") - -def is_docker(): - cgroup = Path('/proc/self/cgroup') - return Path('/.dockerenv').is_file() or (cgroup.is_file() and 'docker' in cgroup.read_text()) +logger = setup_logger("info", "main") # Set the config file path if is_docker(): - config_file_path = os.getenv('US_CONFIG', '/config/config.yml') + # Set the config path + config_path = os.getenv('CONFIG_DIR', '/config') + # Set the config file path + config_file_path = os.path.join(config_path, "config.yml") else: + # Set the config file path config_file_path = os.path.join(pathlib.Path(__file__).parents[1], "config/config.yml") + +# Wait for the config file to be created while not os.path.isfile(config_file_path): logger.info(f"Config file not found. Retrying in 60 seconds...") time.sleep(60) + class Config: """ A class to represent the config file diff --git a/util/logger.py b/util/logger.py index f5d310a..13c920f 100755 --- a/util/logger.py +++ b/util/logger.py @@ -3,7 +3,7 @@ import logging import logging.handlers import pathlib - +from util.utility import is_docker import logging import logging.handlers import os @@ -24,8 +24,8 @@ def setup_logger(log_level, script_name): A logger object for logging messages. """ - if 'HOSTNAME' in os.environ: # Heuristic to check for a container environment - log_dir = os.getenv(f'US_LOGS/{script_name}', f'/config/logs/{script_name}') + if is_docker(): + log_dir = os.getenv(f'LOG_DIR/{script_name}', f'/config/logs/{script_name}') else: log_dir = f"{os.path.join(pathlib.Path(__file__).parents[1], 'logs', script_name)}" diff --git a/util/utility.py b/util/utility.py index c7471cf..555e682 100755 --- a/util/utility.py +++ b/util/utility.py @@ -1,6 +1,8 @@ import re import os import json +from pathlib import Path +import shutil try: import html @@ -667,3 +669,8 @@ def validate(config, script_config, logger): else: return True +def is_docker(): + cgroup = Path('/proc/self/cgroup') + return Path('/.dockerenv').is_file() or (cgroup.is_file() and 'docker' in cgroup.read_text()) + +