Add new features, fix .env setup, fix permissions and UID conflicts (#50)

* add support for Flaresolverr,
automatic timezone detection

* add scripts for cleaning up old users,
and updating the containers

* fix audiobookshelf, re-add jacket support

* add optional folder and permission generation
also use a previously unused function to clean up code

* fix python UID for tautulli and jellyfin

* remove empty file

* docker-compose sample that uses env variables

new .env sample, fixed setup

* change default install directory to current dir

* fix update_containers

* remove obsolete version line

* fix directory ownership

* update README

* address nitpicks

* Update check_running.yml

Updated auto-check workflow to also copy docker-compose sample file, updated syntax to docker compose without "-"
This commit is contained in:
Pavlogal
2024-07-03 13:27:02 +02:00
committed by GitHub
parent fe24e8e0fd
commit 1e8220bf30
12 changed files with 519 additions and 274 deletions

View File

@@ -1,8 +1,34 @@
# If you're not sure what timezone you should fill in, you can look at the following list:
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# Use the column that says "TZ database name".
# To find your system's preconfigured timezone run: cat /etc/timezone
TIMEZONE=Europe/Amsterdam
# The PleX claim token is used to connect your server to your account. It can be obtained at
# https://www.plex.tv/claim/ and is valid for 4 minutes after generating the token.
PLEX_CLAIM=
ROOT_DIR=/
# Choose your root directory for keeping all your container configs and data.
ROOT_DIR=
# USER IDs for each container that requires their own user. The default values will most likely suffice, but in case one of these IDs is already in use on your system it will have to be changed. Any value should work as long as there aren't conflicts with other users.
# If you've already set up the containers and want to change user IDs after the fact, you have to remove the old users first and re-set permissions. First run remove_old_users.sh and then the steps for manual installation.
# If you're doing a manual installation make sure to first change the IDs in this file, and then run setup.sh to set up the directory structure and permissions before you start the docker containers.
MEDIACENTER_GID=13000
SONARR_UID=13001
RADARR_UID=13002
LIDARR_UID=13003
READARR_UID=13004
MYLAR_UID=13005
AUDIOBOOKSHELF_UID=13014
BAZARR_UID=13013
PROWLARR_UID=13006
QBITTORRENT_UID=13007
JACKETT_UID=13008
PLEX_UID=13010
OVERSEERR_UID=13009
SABNZBD_UID=13011
JELLYSEERR_UID=13012
# The user used to run docker-compose. To find this run: id -u
UID=1000

View File

@@ -18,9 +18,12 @@ jobs:
- name: Copy environment file
run: cp .env.sample .env
- name: Copy docker-compose file
run: cp docker-compose.yml.sample docker-compose.yml
- name: Run setup.sh
run: ./setup.sh
- name: Run docker-compose
run: docker-compose up -d
- name: Run docker compose
run: docker compose up -d

View File

@@ -3,7 +3,8 @@
Ezarr is a project built to make it EZ to deploy a Servarr mediacenter on an Ubuntu server. The
badge above means that the shell script and docker-compose file in this repository at least *don't
crash*. It doesn't necessarily mean it will run well on your system ;) It features:
crash*. It doesn't necessarily mean it will run well on your system ;)
It's set up to follow the [TRaSH guidelines](https://trash-guides.info/Hardlinks/How-to-setup-for/Docker/) so it should at least perform optimally. It features:
- [Sonarr](https://sonarr.tv/) is an application to manage TV shows. It is capable of keeping track
of what you'd like to watch, at what quality, in which language and more, and can find a place to
download this if connected to Prowlarr and qBittorrent. It can also reorganize the media you
@@ -24,6 +25,7 @@ crash*. It doesn't necessarily mean it will run well on your system ;) It featur
less than 1. Less than 1 means that you upload less than you download. Not only is this
unfriendly towards your fellow users, but it can also get you banned from certain indexers.
- [Jackett](https://github.com/Jackett/Jackett) is an alternative to Prowlarr.
- [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) is a proxy server to bypass Cloudflare and DDoS-GUARD protection.
- [qBittorrent](https://www.qbittorrent.org/) can download torrents and provides a bunch more
features for management.
- [SABnzbd](https://sabnzbd.org/) can download nzb's
@@ -39,35 +41,63 @@ crash*. It doesn't necessarily mean it will run well on your system ;) It featur
tool.
- [Jellyseerr](https://github.com/Fallenbagel/jellyseerr) is like Overseerr, but for Jellyfin.
## Requirements
Currently this script only works on Linux. There is a chance that the sample docker compose file will work on Windows, although untested.
The only requirements other than that are **Python 3** and **docker** with **docker-compose-v2**.
While this script _may_ work on docker-compose-v1 it's made to be and highly recommended to be run using v2.
The easiest way to install these dependencies on Ubuntu and other Debian-based distors is by running:
```
sudo apt-get install python3 docker.io docker-compose-v2
```
For other Linux distros you may have to use a different package manager or download directly from docker's website.
## Using
### Using the CLI
To make things easier, a CLI has been developed. First, clone the repository in a directory of your
choosing. You can run it by entering `python main.py` and the CLI will guide you through the
process. Please take a look at [important notes](#important-notes) before you continue.
choosing. You can run it by entering `python3 main.py` and the CLI will guide you through the
process. This is the recommended method if you're setting this up for the first time on a new system.
Please take a look at [important notes](#important-notes) before you continue.
**NOTE: This script will create users for each container with IDs ranging from 13001 to 13014.
If you want to choose your own IDs (or some of them are occupied) you have to go through the manual install.**
### Manually
1. To get started, clone the repository in a directory of your choosing. **Note: this will be where
your installation and media will be as well, so think about this a bit.**
If you're installing this for the first time simply follow these steps.
If you're coming from an older version or reinstalling with different IDs, run `remove_old_users.sh` to clean up old users and then follow these steps.
1. To get started, clone the repository in a directory of your choosing. `git clone https://github.com/Luctia/ezarr.git`
2. Copy `.env.sample` to a real `.env` by running `$ cp .env.sample .env`.
3. Set the environment variables to your liking. Note that `ROOT_DIR` should be the directory you
have cloned this in.
4. Run `setup.sh` as superuser. This will set up your users, a system of directories, ensure
permissions are set correctly and sets some more environment variables for docker compose.
5. Take a look at the `docker-compose.yml` file. If there are services you would like to ignore
3. Set the environment variables to your liking. Pay special attention `ROOT_DIR` as this is where everything is going to be stored in.
The path in this value needs to be **absolute**. If you leave it empty it's going to install in the directory the .env file is currently in.
`UID` should be set to the ID of the user that you want to run docker with. You can find this by running `id -u` from that user's shell.
4. Run `setup.sh` as superuser. This will set up your users, a system of directories and ensure permissions are set correctly.
5. Copy `docker-compose.yml.sample` to a real `docker-compose.yml` by running `$ cp docker-compose.yml.sample docker-compose.yml`.
6. Take a look at the `docker-compose.yml` file. If there are services you would like to ignore
(for example, running PleX and Jellyfin at the same time is a bit unusual), you can comment them
out by placing `#` in front of the lines. This ensures they are ignored by Docker compose.
6. Run `docker compose up`.
out by placing `#` in front of the lines. This ensures they are ignored by Docker compose.
Double check that your .env file is set up properly.
7. Run `docker compose up -d` to start the containers. If it complains about permissions run the following commands to add your current user to the docker group and apply changes:
```
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
```
If it still doesn't work reboot your system.
That's it! Your containers are now up and you can continue to set up the settings in them. Please
take a look at [important notes](#important-notes) before you continue.
## Important notes
- You probably shouldn't run the python script as root. Ideally you should create a brand new user that's just for these services, but any regular user will do.
It will need your password for `sudo` to set up the permissions and folder structures, but you shouldn't run it *as* root.
- If you already used this script previously and want to clean up old users, run `remove_old_users.sh`.
This is also recommended if you are updating from an earlier version of this script, since there were previously some conflicts in user IDs.
- It is recommended to restart your system after script completion, so that newly created users and groups can be loaded properly.
- When linking one service to another, remember to use the container name instead of `localhost`.
- Please set the settings of the -arr containers as soon as possible to the following (use
advanced):
- Media management:
- Use hardlinks instead of Copy: `true`
- Root folder: `/data/media/` and then tv, movies or music depending on service
- qBittorrent ships with a default username `admin` and a one-time password that can be viewed by running `docker logs qbittorrent`.
- Make sure to set a username and password for all servarr services and qBittorrent!
- In qBittorrent, after connecting it to the -arr services, you can indicate it should move
torrents in certain categories to certain directories, like torrents in the `radarr` category
@@ -76,6 +106,18 @@ take a look at [important notes](#important-notes) before you continue.
field: `chmod -R 775 "%F/"`.
- You'll have to add indexers in Prowlarr by hand. Use Prowlarrs settings to connect it to the
other -arr apps.
### IMPORTANT IF USING NFS SHARES
- NFS shares' permissions are mapped by user IDs. If you want to access a file as a client, your user ID needs to match the user ID of the owner (or group) of that file on the NFS server.
Note that if you are a group member (and not the owner), having matching group IDs won't be enough, there also needs to be a corresponding user on the NFS server. The easiest way to make sure
the users and groups are set up on both sides correctly is to run `setup.sh` on both your NFS server and your client.
On your server:
- Copy `.env` and `setup.sh` to your NFS server.
- You may have to adjust `.env` so that `ROOT_DIR` reflects where it will be stored on your server, which is most likely different from the mapped location on the client.
- Make sure that the `.env` file is not a .sample. Run `setup.sh`.
- Now follow all the same steps but on your client machine. Always double-check that `.env` is set correctly, especially `ROOT_DIR`.
You don't have to do this on your server first but it's recommended. If you are running this script on the client **make sure that you temporarily enable -no-root-squash on your NFS server**,
as the script needs superuser privileges to run and by default on NFS the root user is mapped to nowhere to prevent abuse.
### SABnzbd External internet access denied message
When you're trying to access SABnzbd the first time you'll come across the message `External
@@ -92,7 +134,10 @@ official SABnzbd website.
## FAQ
### How to update containers
If you'd like to update containers, you can move to the directory of your `docker-compose.yml` file
There is an `update_containers.sh` script that takes care of this. Simply run it and it updates
all containers and removes old images. If you want to keep them, simply comment out the last line of the script.
It's essentially the following steps but automated:
If you'd like to it manually, go to the directory of your `docker-compose.yml` file
and run `(sudo) docker compose pull`. This pulls the newest versions of all images (blueprints for
containers) listed in the `docker-compose.yml` file. Then, you can run `(sudo) docker compose up
-d`. This will deploy the new versions without losing uptime. Afterwards, you can run `(sudo)

View File

@@ -1,3 +1,5 @@
import os
class ContainerConfig:
def __init__(self,
root_dir,
@@ -15,6 +17,7 @@ class ContainerConfig:
self.comic_dir = root_dir + '/media/comics'
self.torrent_dir = root_dir + '/data/torrents'
self.usenet_dir = root_dir + '/data/usenet'
self.UID = os.popen('id -u').read().rstrip('\n')
def plex(self):
return (
@@ -41,7 +44,7 @@ class ContainerConfig:
' depends_on:\n'
' - plex\n'
' environment:\n'
' - PUID=${UID}\n'
' - PUID='+ self.UID + '\n'
' - PGID=13000\n'
' - TZ=' + self.timezone + '\n'
' volumes:\n'
@@ -57,7 +60,7 @@ class ContainerConfig:
' image: lscr.io/linuxserver/jellyfin:latest\n'
' container_name: jellyfin\n'
' environment:\n'
' - PUID=${UID}\n'
' - PUID='+ self.UID + '\n'
' - PGID=13000\n'
' - UMASK=002\n'
' - TZ=' + self.timezone + '\n'
@@ -181,13 +184,13 @@ class ContainerConfig:
' image: ghcr.io/advplyr/audiobookshelf:latest\n'
' container_name: audiobookshelf\n'
' environment:\n'
' - AUDIOBOOKSHELF_UID=13009\n'
' - AUDIOBOOKSHELF_GID=13000\n'
' - user=13014:13000\n'
' - TZ=' + self.timezone + '\n'
' volumes:\n'
' - ' + self.config_dir + '/audiobookshelf:/config\n'
' - ' + self.root_dir + '/data/audiobooks:/audiobooks\n'
' - ' + self.root_dir + '/data/podcasts:/podcasts\n'
' - ' + self.root_dir + '/data/metadata:/metadata\n'
' - ' + self.root_dir + '/data/media/audiobooks:/audiobooks\n'
' - ' + self.root_dir + '/data/media/podcasts:/podcasts\n'
' - ' + self.root_dir + '/data/media/audiobookshelf-metadata:/metadata\n'
' ports:\n'
' - "13378:80"\n'
' restart: unless-stopped\n\n'
@@ -282,3 +285,36 @@ class ContainerConfig:
' - "8081:8080"\n'
' restart: unless-stopped\n\n'
)
def jackett(self):
return (
' jackett:\n'
' image: lscr.io/linuxserver/jackett:latest\n'
' container_name: jackett\n'
' environment:\n'
' - PUID=13008\n'
' - PGID=13000\n'
' - UMASK=002\n'
' - TZ=' + self.timezone + '\n'
' volumes:\n'
' - ' + self.config_dir + '/jackett-config:/config\n'
' ports:\n'
' - 9117:9117\n'
' restart: unless-stopped\n\n'
)
def flaresolverr(self):
return (
' flaresolverr:\n'
' image: ghcr.io/flaresolverr/flaresolverr:latest\n'
' container_name: flaresolverr\n'
' environment:\n'
' - LOG_LEVEL=${LOG_LEVEL:-info}\n'
' - LOG_HTML=${LOG_HTML:-false}\n'
' - CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}\n'
' - TZ=' + self.timezone + '\n'
' ports:\n'
' - "8191:8191"\n'
' restart: unless-stopped\n\n'
)

View File

@@ -1,196 +0,0 @@
---
version: "3.1"
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=13001
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/sonarr-config:/config
- /home/user/ezarr/data:/data
ports:
- "8989:8989"
restart: unless-stopped
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=13002
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/radarr-config:/config
- /home/user/ezarr/data:/data
ports:
- "7878:7878"
restart: unless-stopped
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
environment:
- PUID=13012
- PGID=13000
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/bazarr-config:/config
- /home/user/ezarr/data/media:/media
ports:
- "6767:6767"
restart: unless-stopped
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
environment:
- PUID=13003
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/lidarr-config:/config
- /home/user/ezarr/data:/data
ports:
- "8686:8686"
restart: unless-stopped
readarr:
image: lscr.io/linuxserver/readarr:develop
container_name: readarr
environment:
- PUID=13004
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/readarr-config:/config
- /home/user/ezarr/data:/data
ports:
- "8787:8787"
restart: unless-stopped
mylar3:
image: lscr.io/linuxserver/mylar3:latest
container_name: mylar3
environment:
- PUID=13005
- PGID=13000
- UMASK=002
volumes:
- /home/user/ezarr/config/mylar-config:/config
- /home/user/ezarr/data:/data
ports:
- "8090:8090"
restart: unless-stopped
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
environment:
- AUDIOBOOKSHELF_UID=13009
- AUDIOBOOKSHELF_GID=13000
volumes:
- /home/user/ezarr/config/audiobookshelf:/config
- /home/user/ezarr/data/audiobooks:/audiobooks
- /home/user/ezarr/data/podcasts:/podcasts
- /home/user/ezarr/data/metadata:/metadata
ports:
- "13378:80"
restart: unless-stopped
prowlarr:
image: lscr.io/linuxserver/prowlarr:develop
container_name: prowlarr
environment:
- PUID=13006
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/prowlarr-config:/config
ports:
- "9696:9696"
restart: unless-stopped
plex:
image: lscr.io/linuxserver/plex:latest
container_name: plex
network_mode: host
environment:
- PUID=13010
- PGID=13000
- VERSION=docker
- PLEX_CLAIM=
volumes:
- /home/user/ezarr/config/plex-config:/config
- /home/user/ezarr/data/media:/media
restart: unless-stopped
tautulli:
image: lscr.io/linuxserver/tautulli:latest
container_name: tautulli
depends_on:
- plex
environment:
- PUID=${UID}
- PGID=13000
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/tautulli-config:/config
ports:
- "8181:8181"
restart: unless-stopped
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
container_name: jellyfin
environment:
- PUID=${UID}
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/jellyfin-config:/config
- /home/user/ezarr/data/media:/data
ports:
- "8096:8096"
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
environment:
- PUID=13007
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
- WEBUI_PORT=8080
volumes:
- /home/user/ezarr/config/qbittorrent-config:/config
- /home/user/ezarr/data/torrents:/data/torrents
ports:
- "8080:8080"
- "6881:6881"
- "6881:6881/udp"
restart: unless-stopped
sabnzbd:
image: lscr.io/linuxserver/sabnzbd:latest
container_name: sabnzbd
environment:
- PUID=13011
- PGID=13000
- UMASK=002
- TZ=Europe/Amsterdam
volumes:
- /home/user/ezarr/config/sabnzbd-config:/config
- /home/user/ezarr/data/usenet:/data/usenet
ports:
- "8081:8080"
restart: unless-stopped

252
docker-compose.yml.sample Normal file
View File

@@ -0,0 +1,252 @@
---
name: ezarr
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${SONARR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/sonarr-config:/config
- ${ROOT_DIR}/data:/data
ports:
- "8989:8989"
restart: unless-stopped
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=${RADARR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/radarr-config:/config
- ${ROOT_DIR}/data:/data
ports:
- "7878:7878"
restart: unless-stopped
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
environment:
- PUID=${LIDARR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/lidarr-config:/config
- ${ROOT_DIR}/data:/data
ports:
- "8686:8686"
restart: unless-stopped
readarr:
image: lscr.io/linuxserver/readarr:develop
container_name: readarr
environment:
- PUID=${READARR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/readarr-config:/config
- ${ROOT_DIR}/data:/data
ports:
- "8787:8787"
restart: unless-stopped
mylar3:
image: lscr.io/linuxserver/mylar3:latest
container_name: mylar3
environment:
- PUID=${MYLAR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
volumes:
- ${ROOT_DIR}/config/mylar-config:/config
- ${ROOT_DIR}/data:/data
ports:
- "8090:8090"
restart: unless-stopped
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
environment:
- user=${AUDIOBOOKSHELF_UID}:${MEDIACENTER_GID}
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/audiobookshelf:/config
- ${ROOT_DIR}/data/media/audiobooks:/audiobooks
- ${ROOT_DIR}/data/media/podcasts:/podcasts
- ${ROOT_DIR}/data/media/audiobookshelf-metadata:/metadata
ports:
- "13378:80"
restart: unless-stopped
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
environment:
- PUID=${BAZARR_UID}
- PGID=${MEDIACENTER_GID}
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/bazarr-config:/config
- ${ROOT_DIR}/data/media:/media
ports:
- "6767:6767"
restart: unless-stopped
overseerr:
image: sctx/overseerr:latest
container_name: overseerr
environment:
- PUID=${OVERSEERR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/overseerr-config:/app/config
ports:
- "5055:5055"
restart: unless-stopped
jellyseerr:
image: fallenbagel/jellyseerr:latest
container_name: jellyseerr
environment:
- PUID=${JELLYSEERR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/jellyseerr-config:/app/config
ports:
- "5056:5055"
restart: unless-stopped
prowlarr:
image: lscr.io/linuxserver/prowlarr:develop
container_name: prowlarr
environment:
- PUID=${PROWLARR_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/prowlarr-config:/config
ports:
- "9696:9696"
restart: unless-stopped
jackett:
image: lscr.io/linuxserver/jackett:latest
container_name: jackett
environment:
- PUID=${JACKETT_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/jackett-config:/config
ports:
- 9117:9117
restart: unless-stopped
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- LOG_HTML=${LOG_HTML:-false}
- CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}
- TZ=${TIMEZONE}
ports:
- "8191:8191"
restart: unless-stopped
plex:
image: lscr.io/linuxserver/plex:latest
container_name: plex
network_mode: host
environment:
- PUID=${PLEX_UID}
- PGID=${MEDIACENTER_GID}
- VERSION=docker
- PLEX_CLAIM=${PLEX_CLAIM}
volumes:
- ${ROOT_DIR}/config/plex-config:/config
- ${ROOT_DIR}/data/media:/media
restart: unless-stopped
tautulli:
image: lscr.io/linuxserver/tautulli:latest
container_name: tautulli
# commented out because plex can also run outside of docker
#depends_on:
# - plex
environment:
- PUID=${UID}
- PGID=${MEDIACENTER_GID}
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/tautulli-config:/config
ports:
- "8181:8181"
restart: unless-stopped
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
container_name: jellyfin
environment:
- PUID=${UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/jellyfin-config:/config
- ${ROOT_DIR}/data/media:/data
ports:
- "8096:8096"
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
environment:
- PUID=${QBITTORRENT_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
- WEBUI_PORT=8080
volumes:
- ${ROOT_DIR}/config/qbittorrent-config:/config
- ${ROOT_DIR}/data/torrents:/data/torrents
ports:
- "8080:8080"
- "6881:6881"
- "6881:6881/udp"
restart: unless-stopped
sabnzbd:
image: lscr.io/linuxserver/sabnzbd:latest
container_name: sabnzbd
environment:
- PUID=${SABNZBD_UID}
- PGID=${MEDIACENTER_GID}
- UMASK=002
- TZ=${TIMEZONE}
volumes:
- ${ROOT_DIR}/config/sabnzbd-config:/config
- ${ROOT_DIR}/data/usenet:/downloads
ports:
- "8081:8080"
restart: unless-stopped

50
main.py
View File

@@ -4,14 +4,6 @@ from users_groups_setup import UserGroupSetup
services_classed = dict()
def take_input(service_name, service_type):
choice = input()
if choice == 'Y' or choice == 'y' or choice == '':
services_classed[service_type].append(service_name)
elif choice != 'n' and choice != 'N':
print('Invalid input, please enter Y or N. Not adding ' + service_name + ".")
def take_boolean_input(default=True):
while True:
ans = input()
@@ -23,6 +15,12 @@ def take_boolean_input(default=True):
return False
print('Please answer with y or n.', end=' ')
def take_input(service_name, service_type):
choice = take_boolean_input()
if choice:
services_classed[service_type].append(service_name)
else:
print('Not adding ' + service_name + ".")
def take_directory_input():
while True:
@@ -62,9 +60,16 @@ print('\n===INDEXERS===')
services_classed['indexer'] = []
print('Use Prowlarr? [Y/n]', end=" ")
take_input('prowlarr', 'indexer')
print('Use Jackett? [Y/n]', end=" ")
take_input('jackett', 'indexer')
if len(services_classed['indexer']) == 0:
print('Warning: no indexing service selected.')
print('\n===CLOUDFLARE BYPASS===')
services_classed['bypass'] = []
print('Use Flaresolverr? [Y/n]', end=" ")
take_input('flaresolverr', 'bypass')
print('\n===MEDIA SERVERS===')
services_classed['ms'] = []
print('Use PleX? [Y/n]', end=" ")
@@ -105,10 +110,15 @@ if len(services) == 0:
exit(1)
print('\n===CONFIGURATION===')
print('Please enter your timezone (like "Europe/Amsterdam")', end=' ')
print('Please enter your timezone (like "Europe/Amsterdam") or press enter to use your system\'s configured timezone:', end=' ')
timezone = input()
if len(timezone) == 0:
if (timezone == ''):
timezone = open("/etc/timezone", "r").readline()
if len(timezone) == 0: # if user pressed enter and reading timezone from /etc/timezone failed then default to Amsterdam
timezone = 'Europe/Amsterdam'
plex_claim = ''
if services.__contains__('plex'):
print('If you have a PleX claim token, enter it now. Otherwise, just press enter.', end=' ')
@@ -120,20 +130,28 @@ root_dir = take_directory_input()
compose = open('docker-compose.yml', 'w')
compose.write(
'---\n'
'version: "3.1"\n'
'services:\n'
)
container_config = ContainerConfig(root_dir, timezone, plex_claim=plex_claim)
permission_setup = UserGroupSetup(root_dir=root_dir)
for service in services:
try:
getattr(permission_setup, service)()
except AttributeError:
pass
compose.write(getattr(container_config, service)())
compose.close()
print("Docker compose file generated successfully.")
print("Do you want to also generate the required folder structure and permissions? (this is required for first time setup) [Y/n]: ")
generate_permissions = take_boolean_input()
if generate_permissions:
permission_setup = UserGroupSetup(root_dir=root_dir)
for service in services:
try:
getattr(permission_setup, service)()
except AttributeError:
pass
else:
print("Permission and folder structure generation skipped by user.")
print('Process complete. You can now run "docker compose up -d" to start your containers.')
print('Thank you for using EZarr. If you experience any issues or have feature requests, add them to our issues.')

23
remove_old_users.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# shut down all ezarr containers referenced by docker-compose.yml
# If you previously had ezarr installed somewhere else you have to do this manually in the directory where the docker-compose file is.
sudo docker compose down
# Remove old users and group
sudo userdel sonarr
sudo userdel radarr
sudo userdel lidarr
sudo userdel readarr
sudo userdel mylar
sudo userdel audiobookshelf
sudo userdel bazarr
sudo userdel prowlarr
sudo userdel jackett
sudo userdel plex
sudo userdel overseerr
sudo userdel jellyseerr
sudo userdel qbittorrent
sudo userdel sabnzbd
sudo groupdel mediacenter

View File

@@ -1,20 +1,38 @@
#!/bin/bash
# Set up your .env file BEFORE running this script!!!
# export all variables from .env
set -a
source .env
set +a
# This is always going to complain about UID being a read-only variable.
# However that is not a problem and it's necessary for UID to be defined in the .env so that docker-compose.yml can take it.
# Reminder to set the UID variable in .env if you haven't already. It needs to be the user you want to run docker as. Find what it should be by running "id -u" from that user's shell.
# Make users and group
sudo useradd sonarr -u 13001
sudo useradd radarr -u 13002
sudo useradd lidarr -u 13003
sudo useradd readarr -u 13004
sudo useradd mylar -u 13005
sudo useradd prowlarr -u 13006
sudo useradd qbittorrent -u 13007
sudo useradd jackett -u 13008
sudo useradd overseerr -u 13009
sudo useradd plex -u 13010
sudo useradd sabnzbd -u 13011
sudo useradd jellyseerr -u 13012
sudo useradd bazarr -u 13013
sudo groupadd mediacenter -g 13000
sudo useradd sonarr -u $SONARR_UID
sudo useradd radarr -u $RADARR_UID
sudo useradd lidarr -u $LIDARR_UID
sudo useradd readarr -u $READARR_UID
sudo useradd mylar -u $MYLAR_UID
sudo useradd prowlarr -u $PROWLARR_UID
sudo useradd qbittorrent -u $QBITTORRENT_UID
sudo useradd jackett -u $JACKETT_UID
sudo useradd overseerr -u $OVERSEERR_UID
sudo useradd plex -u $PLEX_UID
sudo useradd sabnzbd -u $SABNZBD_UID
sudo useradd jellyseerr -u $SABNZBD_UID
sudo useradd bazarr -u $BAZARR_UID
sudo useradd audiobookshelf -u $AUDIOBOOKSHELF_UID
sudo groupadd mediacenter -g $MEDIACENTER_GID
# Adds current user to the mediacenter group. This is recommended so that you can still have access to files inside the ezarr folder structure for manual control.
# This is way better than just doing everything as root, especially on NFS shares. Also some services run as the default user anyway (Jellyfin, Tautulli).
sudo usermod -a -G mediacenter $USER
# When you add the user to the group the changes don't take effect immediately.
# You can force them by running "sudo newgrp mediacenter" but that won't always work and it's better to just reboot after the script finishes running.
# adds all the service users to the group
sudo usermod -a -G mediacenter sonarr
sudo usermod -a -G mediacenter radarr
sudo usermod -a -G mediacenter lidarr
@@ -28,14 +46,18 @@ sudo usermod -a -G mediacenter plex
sudo usermod -a -G mediacenter sabnzbd
sudo usermod -a -G mediacenter jellyseerr
sudo usermod -a -G mediacenter bazarr
sudo usermod -a -G mediacenter audiobookshelf
# Make directories
sudo mkdir -pv config/{sonarr,radarr,lidarr,readarr,mylar,prowlarr,qbittorrent,jackett,audiobookshelf,overseerr,plex,tautulli,sabnzbd,jellyseerr,bazarr}-config
sudo mkdir -pv data/{torrents,usenet,media}/{tv,movies,music,books,comics,audiobooks,podcasts,audiobookshelf-metadata}
# ${ROOT_DIR:-.}/ means take the value from ROOT_DIR value, if failed or empty place it in the current folder
sudo mkdir -pv ${ROOT_DIR:-.}/config/{sonarr,radarr,lidarr,readarr,mylar,prowlarr,qbittorrent,jackett,audiobookshelf,overseerr,plex,jellyfin,tautulli,sabnzbd,jellyseerr,bazarr}-config
sudo mkdir -pv ${ROOT_DIR:-.}/data/{torrents,usenet,media}/{tv,movies,music,books,comics,audiobooks,podcasts,audiobookshelf-metadata}
# Set permissions
sudo chmod -R 775 data/
sudo chown -R $(id -u):mediacenter data/
sudo chmod -R 775 config/
sudo chown -R $UID:mediacenter data/
sudo chown -R $UID:mediacenter config/
sudo chown -R sonarr:mediacenter config/sonarr-config
sudo chown -R radarr:mediacenter config/radarr-config
sudo chown -R lidarr:mediacenter config/lidarr-config
@@ -46,8 +68,11 @@ sudo chown -R qbittorrent:mediacenter config/qbittorrent-config
sudo chown -R jackett:mediacenter config/jackett-config
sudo chown -R overseerr:mediacenter config/overseerr-config
sudo chown -R plex:mediacenter config/plex-config
sudo chown -R $UID:mediacenter config/jellyfin-config
sudo chown -R $UID:mediacenter config/tautulli-config
sudo chown -R sabnzbd:mediacenter config/sabnzbd-config
sudo chown -R jellyseerr:mediacenter config/jellyseerr-config
sudo chown -R bazarr:mediacenter config/bazarr-config
sudo chown -R audiobookshelf:mediacenter config/audiobookshelf-config
echo "UID=$(id -u)" >> .env
echo "Done! It is recommended to reboot now."

View File

14
update_containers.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Shutds down all the containers, updates them and removes the old images.
# If you want to keep the old images remove or comment the last line.
# If the following commands throw permission errors, uncomment the following lines
#sudo groupadd docker
#sudo usermod -aG docker $USER
#newgrp docker
# if that isnt enough then reboot
docker compose pull
docker compose up -d
docker image prune -f

View File

@@ -5,10 +5,16 @@ class UserGroupSetup:
def __init__(self, root_dir='/'):
self.root_dir = root_dir
os.system('sudo groupadd mediacenter -g 13000')
os.system('sudo usermod -a -G mediacenter $USER')
os.system(
'/bin/bash -c "sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents} -m 775'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
)
def create_config_dir(self, service_name):
os.system(
f'sudo mkdir -p {self.root_dir}/config/{service_name}-config'
f'sudo mkdir -p {self.root_dir}/config/{service_name}-config -m 775' # -m 775 gives read/write access to the whole mediacenter group, change to 755 for only read
f' ; sudo chown -R {service_name}:mediacenter {self.root_dir}/config/{service_name}-config'
f' ; sudo chown $(id -u):mediacenter {self.root_dir}/config'
)
@@ -17,9 +23,7 @@ class UserGroupSetup:
os.system(
'/bin/bash -c "sudo useradd sonarr -u 13001'
' ; sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents}/tv -m 775'
' ; sudo chown -R sonarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/tv'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
' ; sudo chown -R sonarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/tv"'
)
self.create_config_dir('sonarr')
os.system('sudo usermod -a -G mediacenter sonarr')
@@ -28,9 +32,7 @@ class UserGroupSetup:
os.system(
'/bin/bash -c "sudo useradd radarr -u 13002'
' ; sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents}/movies -m 775'
' ; sudo chown -R radarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/movies'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
' ; sudo chown -R radarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/movies"'
)
self.create_config_dir('radarr')
os.system('sudo usermod -a -G mediacenter radarr')
@@ -44,9 +46,7 @@ class UserGroupSetup:
os.system(
'/bin/bash -c "sudo useradd lidarr -u 13003'
' ; sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents}/music -m 775'
' ; sudo chown -R lidarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/music'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
' ; sudo chown -R lidarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/music"'
)
self.create_config_dir('lidarr')
os.system('sudo usermod -a -G mediacenter lidarr')
@@ -55,9 +55,7 @@ class UserGroupSetup:
os.system(
'/bin/bash -c "sudo useradd readarr -u 13004'
' ; sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents}/books -m 775'
' ; sudo chown -R readarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/books'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
' ; sudo chown -R readarr:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/books"'
)
self.create_config_dir('readarr')
os.system('sudo usermod -a -G mediacenter readarr')
@@ -66,20 +64,16 @@ class UserGroupSetup:
os.system(
'/bin/bash -c "sudo useradd mylar -u 13005'
' ; sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents}/comics -m 775'
' ; sudo chown -R mylar:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/comics'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
' ; sudo chown -R mylar:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/comics"'
)
self.create_config_dir('mylar')
os.system('sudo usermod -a -G mediacenter mylar')
def audiobookshelf(self):
os.system(
'/bin/bash -c "sudo useradd audiobookshelf -u 13009'
' ; sudo mkdir -pv ' + self.root_dir + '/data/{media,usenet,torrents}/audiobooks -m 775'
' ; sudo chown -R audiobookshelf:mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}/audiobooks'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data'
' ; sudo chown $(id -u):mediacenter ' + self.root_dir + '/data/{media,usenet,torrents}"'
'/bin/bash -c "sudo useradd audiobookshelf -u 13014'
' ; sudo mkdir -pv ' + self.root_dir + '/data/media/{audiobooks,podcasts,audiobookshelf-metadata} -m 775'
' ; sudo chown -R audiobookshelf:mediacenter ' + self.root_dir + '/data/media/{audiobooks,podcasts,audiobookshelf-metadata}"'
)
self.create_config_dir('audiobookshelf')
os.system('sudo usermod -a -G mediacenter audiobookshelf')
@@ -112,3 +106,8 @@ class UserGroupSetup:
os.system('sudo useradd jellyseerr -u 13012')
self.create_config_dir('jellyseerr')
os.system('sudo usermod -a -G mediacenter jellyseerr')
def jackett(self):
os.system('sudo useradd jackett -u 13008')
self.create_config_dir('jackett')
os.system('sudo usermod -a -G mediacenter jackett')