Skip to content
DerKezormPublic

About

Movies, shows and music in one self-hosted app, with several versions per title and a page to discover what you are missing. One app instead of Radarr, Sonarr and Lidarr, and Bazarr still finds the subtitles.

Topics

Resources

Stars

14 stars

Watchers

0 watching

Forks

Repository files navigation

nexcrate

Movies, shows and music in one self-hosted app. A movie or a show can have several versions, such as Full HD and 4K; music has one version for now. One app instead of Radarr, Sonarr and Lidarr side by side.

The library: movies with their versions and states

The screenshots show a throwaway demo instance with a few movies, shows and artists and made-up releases. Posters and covers in them are placeholder artwork.

What it does

  • Versions: a movie or a show can have several versions, each with its own profile and folder. No second instance for 4K. Music has one version for now.
  • Profiles from everyday questions, with the rules of the TRaSH Guides inside: qualities, custom formats, sizes and scores as Radarr and Sonarr use them. An expert mode edits every part by hand, and a profile can be read from an existing Radarr or Sonarr.
  • Movies, shows and anime from TMDB, scene numbering from TheXEM, episodes counted through for anime, double episodes, specials.
  • Music from MusicBrainz: artists, albums and the release that fits best, files matched to tracks by tags, names and AcoustID fingerprints, tags written on filing.
  • Search over Newznab and Torznab indexers, entered by hand, read from Radarr, Sonarr or Lidarr, or kept up to date from Prowlarr, with the decision per version and the reason for every release.
  • Downloads through SABnzbd, NZBGet, qBittorrent, Transmission or Deluge. Torrents are hardlinked and seed until their indexer's goal (ratio, seed time), then leave the download client while the library file stays; Usenet downloads are moved, failed ones are replaced.
  • Automatic search, RSS and delay rules per version, with a daily limit and a pause for upgrades.
  • Switching over from Radarr, Sonarr and Lidarr: read their library, indexers, download clients and profiles, then take the files over; or start from the folders on disk.
  • Rename whole libraries with a preview and an undo, library rules that pick the target folder by genre, certification, tag or kind of series.
  • Discover: twenty movies, shows or albums per list that the library lacks yet: new to get, popular, acclaimed, classics. Movies only once they are out on digital or disc, filters for country, genre and language, and "not interested" hides a title for good.
  • Calendar with an iCal feed, notifications (ntfy, Gotify, Telegram, Discord, webhook, Apprise, e-mail), Plex, Jellyfin, Emby and Kodi told about new files, backups with an encrypted download.
  • An API for other programs (/api/v1) with keys, an event feed and webhooks.
  • Bazarr and Lingarr (beta) read nexcrate as Radarr and Sonarr, and the subtitles they write move with their video.
  • Sign-in through authentik, Microsoft Entra ID or any other OpenID Connect provider, several side by side, authentik set up with one button, and a second factor (authenticator app plus recovery codes) for the sign-in with a password.
A movie with a Full HD version on disk and a 4K version wanted A search: the decision for the 4K version and the releases that fit
Shows A show with its seasons
Music: artists An artist with its albums
Discover: classics the library does not have yet The calendar
Download clients with the retention read from the news servers

Running with Docker

docker compose up -d

The bundled docker-compose.yml pulls ghcr.io/derkezorm/nexcrate:latest. Open http://<your-host>:8390 and create the account.

⚠️ Whoever reaches a fresh installation first can claim it. Until the account exists, anybody who can open the page can create it, and it is the only account there is. Create it right after the first start, before the port is reachable from other networks. Once the account exists, the setup route is closed for good.

Forgot the password?

There is one account and no other way back in. Whoever controls the server sets a new one:

docker exec -it nexcrate python -m app.cli reset-password

It asks for the new password twice without showing it, and ends every session.

Sign-in through authentik and other providers

Settings › System › Sign-in has three cards. Sign-in: the switch for the sign-in with a password and the public address. Sign-in providers: every provider that speaks OpenID Connect (authentik, Microsoft Entra ID with common or organizations, Keycloak, Authelia, Pocket ID), each with a button on the sign-in page and its own redirect address (/api/oidc/<short name>/callback). authentik in one step: enter the address of authentik and an API token, and nexcrate creates the signing key, the provider and the application, fills in the entry and binds the application to the token's own user, so authentik lets nobody else through. The token is used for this setup only and not stored. Rather without a token: download the blueprint there, apply it in authentik and enter client ID and secret in the form.

nexcrate never registers an address it guessed: the redirect address at authentik is built from the public address. While there is none, the card suggests the address your browser uses right now ("Use it and set up", or "Use it only" to just keep it), and it warns when the server sees another scheme or host than the browser, which is what a reverse proxy that ends TLS does when nexcrate does not believe its X-Forwarded-Proto. Changing the public address later asks first when authentik or a provider you entered by hand still knows the old redirect address, and the card says so until the button has run again.

Behind a reverse proxy that ends TLS nexcrate takes the scheme (X-Forwarded-Proto) only from the addresses named in FORWARDED_ALLOW_IPS (a comma separated list of addresses or networks; * trusts every sender, so only behind a proxy nobody can bypass). Out of the box the list is empty and nobody is believed. Name the address your proxy connects from, for example the gateway of the Docker network, or 127.0.0.1 for a proxy on the same host with host networking. nexcrate takes the rightmost value, the one your proxy wrote, whatever its case, and only http or https.

Then link your account under Settings › System › Account › Sign-in providers: nexcrate asks for its password and sends you to the provider once. One identity per provider opens nexcrate from then on; every other identity is turned away, however the provider is set up. A provider can be trusted with the second factor (the default) or not; then nexcrate asks for its own code after the provider too. Once linked, the sign-in with a password can be switched off. If the provider is ever gone, start nexcrate with NEXCRATE_PASSWORD_LOGIN=1 to open it again.

Coming from nexcrate 0.5 to 0.8: the provider of the settings becomes the entry "oidc" with its link at the first start, and its redirect address /api/oidc/callback keeps working.

Second factor

Settings › System › Account › "Second factor": after the password, nexcrate asks for a code from an authenticator app. Eight recovery codes are shown once. A sign-in through a provider asks for no code while the provider is trusted with the second factor (the default). Lost the phone and the codes?

docker exec -it nexcrate python -m app.cli reset-second-factor

The data directory

Everything lives in /data (./data next to the compose file): the database, the secret key, the logs and the backups (data/backups/).

⚠️ /data belongs on a local disk, never on an SMB or NFS share. SQLite's locking does not work reliably over network file systems, and that is how it loses data. On a NAS use a path on an internal volume, not a mounted share.

Back up secret.key together with the database. Stored credentials, such as API keys of other programs, are encrypted with it; without it they have to be entered again.

Media and downloads

nexcrate files into folders it can see inside its container, and you choose them from a list; there is no path to type. Mount the folder that holds your downloads and your media, for example:

    volumes:
      - ./data:/data
      - /srv/data:/media

/data is nexcrate's own and is never offered as a media folder.

⚠️ Hardlinks work only inside one mount. Keep the downloads of your download clients and your media folders below one host folder and mount that folder once. With two mounts, even of the same disk, every torrent is copied and takes its space twice.

  • Categories: nexcrate keeps its downloads apart under a category of its own, nexcrate: a category in SABnzbd and qBittorrent, which nexcrate creates; any category in NZBGet, which takes one without setup; a subfolder and, from version 4, a label in Transmission; a label in Deluge, whose Label plugin nexcrate switches on.
  • Usenet retention is read from the news servers set up in SABnzbd and NZBGet; a release older than the longest retention is left out. There is nothing to enter.
  • Indexer keys stay inside nexcrate. nexcrate fetches the NZB or torrent file itself and hands the file over; no download client ever sees an indexer key.
  • Prowlarr: Settings, Indexers, "Prowlarr". Enter its address and API key once; nexcrate reads Prowlarr's indexers on saving, on "Sync now" and every 15 minutes, keeps one indexer per Prowlarr indexer and searches through Prowlarr. Nothing to set up in Prowlarr, no app entry. Prowlarr 1.8.6 or newer. An indexer from Prowlarr is changed in Prowlarr; nexcrate keeps its own fields such as the automatic search.
  • Seeding goals: a torrent indexer can carry a ratio, a seed time and a seed time for season packs, taken from Prowlarr, from Radarr, Sonarr or Lidarr, or entered by hand. qBittorrent gets them with the torrent; Transmission and Deluge get the ratio, and nexcrate stops them itself once the seed time is reached. Without a goal a torrent keeps seeding.
  • Removing finished torrents: once a torrent is imported and has reached its goal, nexcrate removes it from the download client together with its files in the download folder. The hardlinked file in the library stays, and nothing inside a media folder is ever removed. A switch per download client, on for new clients. Should qBittorrent remove torrents by its own share limit rule, the client card says so.
  • Different paths: when a download client sees the folder under another path, for example /data instead of /media, nexcrate finds the finished download and asks once to confirm the mapping.
  • Recycle bin: a replaced or deleted file waits in .nexcrate-recycle inside its root folder, seven days by default, and can be taken back from Settings, Files.

The library on disk

Every title nexcrate owns carries a small file release.nex in its folder (for shows in every season folder, for albums in the album folder): the TMDB, IMDb or MusicBrainz number and per version the file, its quality and release. Should the database ever be lost, nexcrate reads the library back from these files without guessing. Plex, Jellyfin and Emby ignore the file.

Backups

Settings, System, "Backups". nexcrate copies its database weekly at night (or daily, monthly or never), before every schema change, and when you ask. Download turns a copy into an AES-encrypted ZIP with the database and secret.key, protected by a password you choose. Restore takes such an archive and restarts nexcrate, which swaps it in before anything opens the database; this needs a restart policy such as restart: unless-stopped, which the bundled docker-compose.yml sets.

Settings

All optional, see .env.example and docker-compose.yml.

Variable Default Meaning
NEXCRATE_DATA_DIR ./data Database, key, logs, backups
NEXCRATE_SECRET_KEY generated in data/secret.key Key for stored credentials
NEXCRATE_COOKIE_SECURE auto auto follows the scheme of each request, on for a reverse proxy that ends TLS, off never
NEXCRATE_LOG_LEVEL empty Fixes the log mode: quiet, normal, detailed, trace
NEXCRATE_PORT 8390 Port inside the container
NEXCRATE_URL_BASE empty Serve nexcrate under a sub path such as /nexcrate; /api/health also answers at the root
NEXCRATE_PASSWORD_LOGIN empty 1 opens the sign-in with a password even when it was switched off for the providers
NEXCRATE_PUBLIC_URL empty The address people reach nexcrate at, sub path included, when Settings › System › Sign-in names none; providers send the browser back there
FORWARDED_ALLOW_IPS empty (nobody) The proxies nexcrate believes when they say X-Forwarded-Proto: https (comma separated addresses or networks, * for all); needed behind a reverse proxy that ends TLS, see Sign-in through authentik

Logs

The log is in data/logs/ and in the interface, with download. There are four modes: quiet, normal, detailed and trace; the two deep ones switch back to normal after 30 minutes, 2 hours or 8 hours. Every answer carries an X-Request-Id header that finds the lines of that request. Secrets are masked before a line is written.

API

The interface uses the same API anybody can use. Documentation: /api/docs, the OpenAPI document: /api/openapi.json. Requests that change something need the header X-Requested-With: nexcrate.

Other programs use /api/v1, a contract that stays stable while the routes of the interface follow its pages. It opens with a key from Settings, System, API keys, sent as Authorization: Bearer <key>. A key is shown once and nexcrate keeps only its hash. Three scopes: read (versions, titles and their state, seasons and episodes, the queue, problems, history, the calendar, ratings, an event feed also as Server-Sent Events), request (request titles, take requests back, freeze, move files into the recycle bin, ask for a search) and operate (retry, remove, clear and assign the files of a stuck download). A program can also ask to be connected (POST /api/v1/pairing) and collect its key once the owner confirms. Webhooks send the same events signed with HMAC-SHA256.

Bazarr (beta)

Bazarr only knows Radarr and Sonarr, so nexcrate answers it as both. Switch it on under Settings, System, Bazarr and Lingarr, make a key with nothing but read under API keys, and enter nexcrate in Bazarr twice, under Radarr and under Sonarr, with the same address, port and key:

In Bazarr Base URL
Radarr /bazarr/radarr
Sonarr /bazarr/sonarr

With NEXCRATE_URL_BASE the sub path comes first, for example /nexcrate/bazarr/radarr. Bazarr keeps a live connection and hears of a new or replaced file within seconds.

  • Every version is an entry of its own in Bazarr: a movie in 1080p and in 4K shows up twice. The version's name comes along as a tag, and Bazarr's tag mapping can give each version a languages profile of its own.
  • Subtitles Bazarr writes are recorded and go where the video goes: renamed with it, into the recycle folder with it on an upgrade or a removal. Switching on looks once for subtitles that are already next to the files. Versions another Radarr or Sonarr still feeds are left out.
  • Paths: Bazarr gets the paths as nexcrate sees them. If Bazarr sees the media elsewhere, set path mappings in Bazarr.
  • Beta: nexcrate imitates the part of Radarr's and Sonarr's API that Bazarr reads, tested against Bazarr 1.6.2. A Bazarr update may break it until nexcrate catches up.
  • Bazarr sends the key in the address, as it does to Radarr; nexcrate's log masks it.

Lingarr (beta)

Lingarr translates subtitles and, like Bazarr, only knows Radarr and Sonarr. It answers from its own addresses, with the same switch and the same kind of key: in Lingarr under Settings, Integrations, enter nexcrate instead of Radarr and Sonarr, with a key that has nothing but read in both fields.

In Lingarr URL
Radarr http://<nexcrate>:8390/lingarr/radarr
Sonarr http://<nexcrate>:8390/lingarr/sonarr

With NEXCRATE_URL_BASE the sub path comes first, for example /nexcrate/lingarr/radarr. Lingarr picks up new movies and episodes on its next sync.

  • Every version is an entry of its own, as in Bazarr.
  • Subtitles Lingarr writes are found once a day and from then on go where the video goes.
  • Paths: if Lingarr sees the media elsewhere, set a path mapping in Lingarr.
  • Key length: Lingarr only takes keys of 32 characters. Keys made before nexcrate 1.2.1 are longer, so make a new one for Lingarr.
  • Beta: tested against Lingarr 1.4.0. A Lingarr update may break it until nexcrate catches up.

Run from source

Backend on port 8390:

cd backend
python -m venv .venv
.venv/bin/python -m pip install -r requirements-dev.txt
NEXCRATE_DATA_DIR=../data-dev .venv/bin/python -m uvicorn app.main:app --port 8390 --reload

Frontend on port 5390, talking to the backend under /api:

cd frontend
npm ci
npm run dev

Data sources

  • TMDB for movies and shows: This application uses TMDB and the TMDB APIs but is not endorsed, certified, or otherwise approved by TMDB. A free TMDB token is entered under Settings, Online services.
  • TheXEM for the scene numbering of shows and anime.
  • MusicBrainz and the Cover Art Archive for music, AcoustID for recognizing files without usable tags, ListenBrainz for the album lists of Discover (off until you switch it on).
  • The TRaSH Guides (MIT) for the rules inside the profiles.
  • IMDb ratings: Information courtesy of IMDb (https://www.imdb.com). Used with permission. Rotten Tomatoes and Metacritic with an OMDb key of your own.

TheXEM, MusicBrainz, AcoustID, ListenBrainz and the IMDb ratings can be switched off under Settings, Online services. Once a day nexcrate asks GitHub for its newest release; nothing but the request itself goes out, and it can be switched off on the About page.

Licence

GNU Affero General Public License v3.0.

About

Movies, shows and music in one self-hosted app, with several versions per title and a page to discover what you are missing. One app instead of Radarr, Sonarr and Lidarr, and Bazarr still finds the subtitles.

Topics

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages