Skip to content

Configuration

unit3dprep reads configuration from three layers, in priority order:

  1. Environment variables — always win.
  2. Shared .env file — single file read by both unit3dprep and the Unit3DWebUp 0.0.20+ bot. Editable from the Web UI through Settings.
  3. Built-in defaults — used when neither env nor file sets a value.

U3DP_* and W_* keys are re-read on every access via config.runtime_setting(): changing them from the Web UI takes effect immediately, no restart. Exceptions are U3DP_HOST, U3DP_PORT, U3DP_ROOT_PATH, and U3DP_HTTPS_ONLY, which are read once at uvicorn startup.


Environment variables

Required by the Web UI

Variable Description
U3DP_PASSWORD_HASH Bcrypt hash of the web password. Generated by generate_hash.py. Always single-quoted in .env/bash files (see Installation).
U3DP_SECRET Hex secret signing session cookies. Generated by generate_hash.py.

TMDB

Variable Default Description
TMDB_API_KEY TMDB v3 API key. Used by unit3dprep's TMDB lookup and metadata fetch. The CLI prompts the user if missing. The same value must also exist as TMDB_APIKEY in the shared .env (Unit3DWebUp requires it).

Runtime (re-read on every access)

Variable Default Description
U3DP_HOST 127.0.0.1 uvicorn bind address. (read-once at startup)
U3DP_PORT 8765 Listening port. On Ultra.cc pick a reserved port (app-ports free). (read-once)
U3DP_ROOT_PATH "" Nginx prefix (e.g. /unit3dprep). (read-once) See ROOT_PATH notes.
U3DP_HTTPS_ONLY 0 1 sets the https_only flag on the session cookie (required behind HTTPS). (read-once)
U3DP_MEDIA_ROOT ~/media Base media folder. Subfolders become categories.
U3DP_SEEDINGS_DIR ~/seedings Hardlink destination. Must live on the same FS as U3DP_MEDIA_ROOT.
U3DP_TMDB_LANG it-IT TMDB response language (internal lookup). Examples: en-US, es-ES.
U3DP_LANG it UI language (it/en). Persisted in localStorage + .env.
U3DP_DB_PATH ~/.unit3dprep_db.json Upload history (JSON).
U3DP_TMDB_CACHE_PATH ~/.unit3dprep_tmdb_cache.json TMDB query cache.
U3DP_LANG_CACHE_PATH ~/.unit3dprep_lang_cache.json Audio language detection cache.
U3DP_SYSTEMD_UNIT unit3dprep-web.service App systemd user unit name, used by the "Update app" button for systemctl --user cat/restart. Override only if your unit has a different name.
U3DP_DRY_RUN_TRACKER 0 When 1 (or true/yes) the wizard skips the /upload call to Unit3DWebUp: useful in dev/WSL to exercise the pipeline (setenv→scan→maketorrent→seed) without polluting the live tracker.
U3DP_GITHUB_REPO davidesidoti/unit3dprep owner/repo slug used to poll the app's GitHub releases (env-only, read at import).

Unit3DWebUp bridge

Variable Default Description
WEBUP_URL http://127.0.0.1:8000 Base address of the Unit3DWebUp bot.
WEBUP_REPO_PATH ~/dev/Unit3DWebUp Path to the Unit3DWebUp clone (legacy). Only used by the "git" mode of the webup auto-update.
WEBUP_VENV_BIN auto bin folder of the venv that holds Unit3DwebUp. Auto-detect: if unset, try <WEBUP_REPO_PATH>/.venv/bin/python, fall back to sys.executable (canonical PyPI install in the same venv as unit3dprep). Set it explicitly when webup lives in a separate venv.
WEBUP_SYSTEMD_UNIT unit3dwebup.service Unit3DWebUp systemd user unit, restarted after SSE auto-update.

Shared .env storage

Variable Default Description
U3DP_ENV_PATH Full file path of the shared .env. Explicit unit3dprep override.
ENVPATH Directory containing the .env (file = <dir>/.env). Same convention used by Unit3DWebUp: pass the same value to the bot's uvicorn.
UNIT3DUP_CONFIG ~/Unit3Dup_config/Unit3Dbot.json Path of the legacy Unit3Dbot.json to read during the one-shot migration at first startup. Not used afterwards.

Precedence for the on-disk .env file: U3DP_ENV_PATHENVPATH/.env~/.config/unit3dprep/.env (XDG default).

Web UI wizard (W_*)

Control default state of upload wizard toggles. Stored in the shared .env, edit from Settings.

Key Default Description
W_AUDIO_CHECK true Enable Italian audio check inside the wizard.
W_AUTO_TMDB true Auto-fetch TMDB metadata if an ID is already present.
W_HIDE_UPLOADED true Hide already-uploaded items from the Library.
W_HIDE_NO_ITALIAN false Hide already-scanned items without Italian audio from the Library.
W_HARDLINK_ONLY false End the wizard after the hardlink, without invoking the Unit3DWebUp upload.
W_CONFIRM_NAMES true Require confirmation of the final name before hardlinking.
W_DUPLICATE_CHECK true Before hardlinking, query the ITT API (/api/torrents/filter?tmdbId=<id>) and prompt for confirmation if a torrent with the exact same byte size already exists. See Usage › Wizard › Duplicate check.

Shared .env file

Single storage for app + bot, conforming to Unit3DWebUp 0.0.20+. Default path: ~/.config/unit3dprep/.env (override via U3DP_ENV_PATH or ENVPATH).

Keys understood by webup are written on disk with the canonical naming the bot expects (prefixes TRACKER__, TORRENT__, PREFS__); in memory and in the /api/settings API the short historical names (ITT_APIKEY, QBIT_HOST, …) are kept. Translation is confined to unit3dprep/web/config.py (WEBUP_KEY_MAP).

To run the bot alongside, just point it at the same ENVPATH:

ENVPATH=~/.config/unit3dprep uvicorn unit3dwup.start:app

Short ↔ canonical mapping (extract)

Short name (API/UI) Canonical name (on disk / .env)
ITT_APIKEY TRACKER__ITT_APIKEY
ITT_URL TRACKER__ITT_URL
ITT_PID TRACKER__ITT_PID
MULTI_TRACKER TRACKER__MULTI_TRACKER=["itt", ...] (JSON array)
TMDB_APIKEY TRACKER__TMDB_APIKEY
TVDB_APIKEY TRACKER__TVDB_APIKEY
QBIT_HOST / QBIT_PORT / QBIT_USER / QBIT_PASS TORRENT__QBIT_HOST / …_PORT / …_USER / …_PASS
IMGFI_KEY TRACKER__IMGFI_KEY
IMAGE_HOST_ORDER projected to PREFS__<HOST>_PRIORITY (1, 2, …, 99 for hosts not in the list)
TAG_ORDER_MOVIE / TAG_ORDER_SERIE PREFS__TAG_POSITION_MOVIE / …_SERIE
NUMBER_OF_SCREENSHOTS, ANON, PERSONAL_RELEASE, … PREFS__<KEY>

The full list lives in unit3dprep/web/config.py (WEBUP_KEY_MAP).

PREFERRED_LANG — ISO 639-1 format

PREFS__PREFERRED_LANG (short name PREFERRED_LANG) must be a 2-letter ISO 639-1 code: "it", "en", "fr", "es", etc. Webup 0.0.25 compares this value against each audio track's language field in the mediainfo, which mediainfo emits as the 2-letter code. Using ISO 639-2 ("ita", "eng") the match silently fails, media.can_upload becomes False, and UploadUseCase.execute() skips the tracker submission without emitting any WebSocket log — qBit seeds locally but the tracker responds "InfoHash not found" on announce.

Default in DEFAULT_CONFIG: "it" (since v0.6.4+). Editable from Settings → Behavior → Preferred language. See Troubleshooting › silent /upload.

Skip rules

_to_webup_env_payload skips empty values ("", None) and the placeholders no_key / no_pass / no_path / no_comment from the push to Unit3DWebUp: the bot keeps its own defaults and its pydantic validator (empty_to_none) does not hit None-to-str conversion errors that would SystemExit(1) on the next setenv.

Three keys are exceptions: PREFS__TORRENT_ARCHIVE_PATH, PREFS__WATCHER_PATH, PREFS__WATCHER_DESTINATION_PATH, PREFS__SCAN_PATH are materialized with a . fallback when our config has them empty, because webup requires a "Path-able" value that exists (otherwise get_settings() calls SystemExit).

Automatic migration from legacy Unit3Dbot.json

On the first startup, if ~/Unit3Dup_config/Unit3Dbot.json (or whatever $UNIT3DUP_CONFIG points to) exists:

  1. the file is read;
  2. rewritten as .env with the canonical naming;
  3. renamed to Unit3Dbot.json.migrated-bak.

The migration is idempotent. The backup is never deleted by the app — the user can remove it manually after confirming everything works.

Secret masking

Secrets (API keys, passwords, PIDs) are masked as "__SET__" in GET /api/settings responses. On PUT, if the client sends back "__SET__" the server preserves the original value. This avoids leaks in browser logs while letting you edit other keys without re-entering every secret.

Masked keys are defined in unit3dprep/web/config.py (MASKED_KEYS): ITT_APIKEY, ITT_PID, PTT_APIKEY, PTT_PID, SIS_APIKEY, SIS_PID, TMDB_APIKEY, TVDB_APIKEY, YOUTUBE_KEY, IGDB_CLIENT_ID, IGDB_ID_SECRET, QBIT_PASS, TRASM_PASS, RTORR_PASS, FTPX_PASS, PTSCREENS_KEY, PASSIMA_KEY, IMGBB_KEY, IMGFI_KEY, FREE_IMAGE_KEY, LENSDUMP_KEY, IMARIDE_KEY.

Atomic writes + live sync

Writes to the .env go through tempfile.mkstemp + os.replace → neither unit3dprep nor Unit3DWebUp ever sees a half-written file.

Every PUT /api/settings cascades into webup_client.setenv(key, value) (POST /setenv to the bot) for each canonical key changed: the bot rebuilds Settings() internally without restart. A Save in the UI does not require systemctl restart unit3dwebup.service.

Exceptions that need a bot restart: REDIS_* (anyway hardcoded to localhost:6379), changes to process env-vars not in .env (e.g. DOCKER, PYTHONUNBUFFERED), and obviously upgrading the webup package (handled automatically by the SSE auto-update).


U3DP_ROOT_PATH and nginx

When you serve the Web UI behind a reverse proxy at a subpath (e.g. /unit3dprep), set:

export U3DP_ROOT_PATH="/unit3dprep"

Important — on Ultra.cc the nginx user-proxy does not strip the prefix: requests arrive at uvicorn with /unit3dprep still in place. That is why the app mounts routes under the prefix with app.include_router(r, prefix=ROOT_PATH) instead of FastAPI(root_path=...) (the latter is only for proxies that do strip).

The SPA reads window.__ROOT_PATH__ injected at serve-time into index.html, so assets and API calls automatically hit the right prefix with no rebuild.

On a generic VPS where nginx strips the prefix (proxy_pass http://127.0.0.1:8765/; with trailing slash), set U3DP_ROOT_PATH="" instead and handle the path from the server block.


In-app auto-update

The Web UI ships an integrated update system for two distinct artifacts:

  • App (unit3dprep) — installed version (importlib.metadata.version("unit3dprep") or pyproject.toml in git mode) compared against GitHub Releases.
  • Unit3DWebUp — version installed in the bot's venv (detected via subprocess python -c "import importlib.metadata; print(importlib.metadata.version('Unit3DwebUp'))") compared against PyPI.

Cards rendered in Settings › Version. Click "Install update":

  1. Modal with pip install --upgrade Unit3DwebUp or git pull + pip install -e . live-streamed via SSE (/api/version/update/{webup,app}/stream).
  2. Once done, /api/version/info _cache is cleared and systemd <unit> is restarted in a transient scope (systemd-run --user --on-active=3s) to survive the parent process SIGTERM.
  3. 5s countdown + browser reload; release-body popup post-reload.

App prerequisites

The "Update app" button stays disabled (can_update_app: false) when:

  • systemctl is not in PATH, or
  • the configured unit does not exist (systemctl --user cat <unit> fails).

The default for U3DP_SYSTEMD_UNIT is already unit3dprep-web.service (the name used on both Ultra.cc and the VPS deploy). If your unit has a different name, set it:

# ~/.config/systemd/user/unit3dprep-web.service
[Service]
Environment=U3DP_SYSTEMD_UNIT=unit3dprep-web.service

or save the key through Settings › App Auto-Update. The key is read at runtime, so a Save takes effect immediately. daemon-reload + restart are only needed when you add a fresh Environment= entry to the unit file.

Webup prerequisites

Likewise, the "Update" button on the webup card requires:

  • a python interpreter with Unit3DwebUp installed accessible. Auto-detect: WEBUP_VENV_BIN/python if set, then <WEBUP_REPO_PATH>/.venv/bin/python (legacy), finally sys.executable (canonical PyPI install in the same venv as unit3dprep). Set WEBUP_VENV_BIN explicitly only when webup lives in a separate venv;
  • the unit3dwebup.service systemd user unit existing (override with WEBUP_SYSTEMD_UNIT).

Install mode (app)

_install_mode() picks the flow based on the install layout:

  • git — Python source has reachable .git (typical for pip install -e . from a checkout). Runs git pull --ff-only origin main + pip install -e ..
  • pip — installed via pip install git+https://...@vX (no .git). Runs pip install --upgrade --force-reinstall git+URL@vX.

Editable install + service WorkingDirectory

If the systemd [Service] has WorkingDirectory=<repo> and that folder contains .git, Python imports unit3dprep from the local source (editable) even when you installed via pip install git+.... The flow becomes "git" as a consequence. To force the pip flow, use WorkingDirectory=%h and remove/rename the checkout.


Complete examples

Local deploy (just you, on a trusted machine)

export U3DP_PASSWORD_HASH='$2b$12$...'
export U3DP_SECRET="..."
export TMDB_API_KEY="..."
export U3DP_HOST="127.0.0.1"
export U3DP_PORT="8765"
export ENVPATH="$HOME/.config/unit3dprep"

# bot in background
ENVPATH=$HOME/.config/unit3dprep uvicorn unit3dwup.start:app --host 127.0.0.1 --port 8000 &
unit3dprep-web

Behind nginx with HTTPS and a subpath

export U3DP_PASSWORD_HASH='$2b$12$...'
export U3DP_SECRET="..."
export TMDB_API_KEY="..."
export U3DP_HOST="127.0.0.1"
export U3DP_PORT="45678"                  # backend port, not exposed
export U3DP_ROOT_PATH="/unit3dprep"      # public subpath
export U3DP_HTTPS_ONLY="1"
export ENVPATH="$HOME/.config/unit3dprep"
unit3dprep-web

Media on a separate disk

export U3DP_MEDIA_ROOT="/mnt/storage/media"
export U3DP_SEEDINGS_DIR="/mnt/storage/seedings"

Same filesystem

U3DP_SEEDINGS_DIR must live on the same device as U3DP_MEDIA_ROOT, otherwise hardlink creation fails with OSError: [Errno 18] Invalid cross-device link. Check with df <media> <seedings>.

WSL dev — dry-run without polluting the tracker

export U3DP_DRY_RUN_TRACKER=1
unit3dprep-web

The wizard runs setenv → scan → maketorrent → seed end-to-end but skips /upload. Useful for testing pre-flight + bridge changes without creating real uploads.