Skip to content

Database

TheMRX13 edited this page Jul 3, 2026 · 2 revisions

Database

🌐 English · Deutsch

SQLite database at ~/.mediaforge/mediaforge.db — WAL mode, 30 s busy timeout, connections are cached per Flask request. A PID lock (~/.mediaforge/mediaforge.pid) warns when two instances use the same DB.

Tables

Table Content
users Users: role (admin/user), password hash (Werkzeug), SSO identity (sso_issuer+sso_subject, unique), UI language
app_settings Key-value store of all settings (sensitive values encrypted, see below)
download_queue Download jobs: episode list (JSON), language, provider, status (queued/running/completed/partial/failed/cancelled), progress, errors (JSON), statistics, captcha URL, upscale flag
custom_paths Named additional download targets
autosync_jobs Sync jobs: language, provider, custom path, enabled, on_hold, path_unavailable_action, episode_filter (JSON: season/episode selection), movie_custom_path_id (separate path for movies/specials), filter_dirty (silent baseline recompute after a filter change), group_name (optional manual group), counters (episodes_found, local_episodes_found, last_new_count), error/retry state
favourites Series bookmarks per user
seerr_hidden Seerr requests hidden per user
library_cache Cached library scan per path (JSON) + scan status
user_notification_prefs Notification settings per user (chat IDs, event toggles)
push_subscriptions Web Push subscriptions (endpoint + keys, per user)
tmdb_cache TMDB lookups (24 h TTL, hourly cleanup)
browse_cache Browse lists (new/popular titles)
upscale_queue Anime4K jobs (multi-file entries via JSON column files)
mediascan_cache Plex/Jellyfin inventory (TMDB/IMDB/TVDB IDs)
download_history Persistent per-episode download log: title, season/episode, source, user, target path, size, average speed, duration, status (completed/failed/cancelled/skipped), error (error or skip reason), start/finish timestamps (UTC)
calendar_media Calendar watcher cache — one row per TMDB title: tmdb_id (unique), bilingual title (title/title_en), poster, last_updated
calendar_episodes Calendar watcher cache — dated episodes/releases per media: season/episode (NULL for movies), bilingual name (name/name_en), air_date, still
watch_progress Playback position per file for the browser player

Schema migrations run automatically at startup (ALTER TABLE retrofits inside the init_* functions) — downgrades are not supported.

Encrypted settings

Sensitive keys are stored Fernet-encrypted (prefix enc:); the key is derived from the Flask secret (~/.mediaforge/.flask_secret). Affected keys include:

external_api_key, seerr_api_key, oidc_client_secret, cineinfo_tmdb_api_key, mediaplayer_apikey, mediascan_jf_apikey, notif_telegram_bot_token, notif_pushover_app_token, notif_discord_webhook_url, notif_ntfy_auth_token, notif_ntfy_password, pushover_user_key.

Existing plaintext values are migrated automatically at startup.

Backup note: Always back up mediaforge.db and .flask_secret together — without the secret the encrypted settings cannot be recovered.

Access patterns

  • get_setting(key, default) / set_setting(key, value) / delete_setting(key) — central settings API (encrypts/decrypts transparently).
  • Queue claiming (claim_next_queued) uses a dedicated connection with an atomic UPDATE … WHERE status='queued' so two workers can never grab the same job.
  • get_encoding_ffmpeg_opts() translates the encoding settings into ready-to-use FFmpeg arguments (incl. the expert flag parser).

Clone this wiki locally