# Configuration Reference

## Document Purpose

This document provides a comprehensive reference for all configuration options in the Aniworld application.

---

## 1. Configuration Overview

### Configuration Sources

Aniworld uses a layered configuration system with **explicit precedence rules**:

1. **Environment Variables** (highest priority) - Takes precedence over all other sources
2. **`.env` file** in project root - Loaded as environment variables
3. **`data/config.json`** file - Persistent file-based configuration
4. **Default values** (lowest priority) - Built-in fallback values

### Precedence Rules

**Critical Principle**: `ENV VARS > config.json > defaults`

- **Environment variables always win**: If a value is set via environment variable, it will NOT be overridden by config.json
- **config.json as fallback**: If an ENV var is not set (or is empty/default), the value from config.json is used
- **Defaults as last resort**: Built-in default values are used only if neither ENV var nor config.json provide a value

### Loading Mechanism

Configuration is loaded at application startup in `src/server/fastapi_app.py`:

1. **Pydantic Settings** loads ENV vars and .env file with defaults
2. **config.json** is loaded via `ConfigService`
3. **Selective sync**: config.json values sync to settings **only if** ENV var not set
4. **Runtime access**: Code uses `settings` object (which has final merged values)

**Example**:

```bash
# If ENV var is set:
ANIME_DIRECTORY=/env/path  # This takes precedence

# config.json has:
{"other": {"anime_directory": "/config/path"}}  # This is ignored

# Result: settings.anime_directory = "/env/path"
```

**Source**: [src/config/settings.py](../src/config/settings.py#L1-L96), [src/server/fastapi_app.py](../src/server/fastapi_app.py#L139-L185)

---

## 2. Environment Variables

### Authentication Settings

| Variable                | Type   | Default          | Description                                                         |
| ----------------------- | ------ | ---------------- | ------------------------------------------------------------------- |
| `JWT_SECRET_KEY`        | string | (random)         | Secret key for JWT token signing. Auto-generated if not set.        |
| `PASSWORD_SALT`         | string | `"default-salt"` | Salt for password hashing.                                          |
| `MASTER_PASSWORD_HASH`  | string | (none)           | Pre-hashed master password. Loaded from config.json if not set.     |
| `MASTER_PASSWORD`       | string | (none)           | **DEVELOPMENT ONLY** - Plaintext password. Never use in production. |
| `SESSION_TIMEOUT_HOURS` | int    | `24`             | JWT token expiry time in hours.                                     |

Source: [src/config/settings.py](../src/config/settings.py#L13-L42)

### Server Settings

| Variable          | Type   | Default                          | Description                                                           |
| ----------------- | ------ | -------------------------------- | --------------------------------------------------------------------- |
| `ANIME_DIRECTORY` | string | `""`                             | Path to anime library directory.                                      |
| `LOG_LEVEL`       | string | `"INFO"`                         | Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL.                 |
| `DATABASE_URL`    | string | `"sqlite:///./data/aniworld.db"` | Database connection string.                                           |
| `CORS_ORIGINS`    | string | `"http://localhost:3000"`        | Comma-separated allowed CORS origins. Use `*` for localhost defaults. |
| `API_RATE_LIMIT`  | int    | `100`                            | Maximum API requests per minute.                                      |

Source: [src/config/settings.py](../src/config/settings.py#L43-L68)

### Provider Settings

| Variable           | Type   | Default         | Description                                   |
| ------------------ | ------ | --------------- | --------------------------------------------- |
| `DEFAULT_PROVIDER` | string | `"aniworld.to"` | Default anime provider.                       |
| `PROVIDER_TIMEOUT` | int    | `30`            | HTTP timeout for provider requests (seconds). |
| `RETRY_ATTEMPTS`   | int    | `3`             | Number of retry attempts for failed requests. |

Source: [src/config/settings.py](../src/config/settings.py#L69-L79)

### NFO Settings

| Variable              | Type   | Default  | Description                                        |
| --------------------- | ------ | -------- | -------------------------------------------------- |
| `TMDB_API_KEY`        | string | `""`     | The Movie Database (TMDB) API key for metadata.    |
| `NFO_AUTO_CREATE`     | bool   | `true`   | Automatically create NFO files during downloads.   |
| `NFO_UPDATE_ON_SCAN`  | bool   | `false`  | Update existing NFO files when scanning library.   |
| `NFO_DOWNLOAD_POSTER` | bool   | `true`   | Download poster images along with NFO files.       |
| `NFO_DOWNLOAD_LOGO`   | bool   | `false`  | Download logo images along with NFO files.         |
| `NFO_DOWNLOAD_FANART` | bool   | `false`  | Download fanart images along with NFO files.       |
| `NFO_IMAGE_SIZE`      | string | `"w500"` | Image size for TMDB images (w500, w780, original). |

Source: [src/server/models/config.py](../src/server/models/config.py#L109-L132)

---

## 3. Configuration File (config.json)

Location: `data/config.json`

### File Structure

```json
{
    "name": "Aniworld",
    "data_dir": "data",
    "scheduler": {
        "enabled": true,
        "interval_minutes": 60,
        "schedule_time": "03:00",
        "schedule_days": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"],
        "auto_download_after_rescan": false,
        "folder_scan_enabled": false
    },
    "logging": {
        "level": "INFO",
        "file": null,
        "max_bytes": null,
        "backup_count": 3
    },
    "backup": {
        "enabled": false,
        "path": "data/backups",
        "keep_days": 30
    },
    "nfo": {
        "tmdb_api_key": "",
        "auto_create": true,
        "update_on_scan": false,
        "download_poster": true,
        "download_logo": false,
        "download_fanart": false,
        "image_size": "w500"
    },
    "other": {
        "master_password_hash": "$pbkdf2-sha256$...",
        "anime_directory": "/path/to/anime"
    },
    "version": "1.0.1"
}
```

Source: [data/config.json](../data/config.json)

---

## 4. Configuration Sections

### 4.1 General Settings

| Field      | Type   | Default      | Description                    |
| ---------- | ------ | ------------ | ------------------------------ |
| `name`     | string | `"Aniworld"` | Application name.              |
| `data_dir` | string | `"data"`     | Base directory for data files. |

Source: [src/server/models/config.py](../src/server/models/config.py#L62-L66)

### 4.2 Scheduler Settings

Controls automatic cron-based library rescanning (powered by APScheduler).

| Field                                  | Type         | Default                                       | Description                                                          |
| -------------------------------------- | ------------ | --------------------------------------------- | -------------------------------------------------------------------- |
| `scheduler.enabled`                    | bool         | `true`                                        | Enable/disable automatic scans.                                      |
| `scheduler.interval_minutes`           | int          | `60`                                          | Legacy field kept for backward compatibility. Minimum: 1.            |
| `scheduler.schedule_time`              | string       | `"03:00"`                                     | Daily run time in 24-h `HH:MM` format.                               |
| `scheduler.schedule_days`              | list[string] | `["mon","tue","wed","thu","fri","sat","sun"]` | Days of the week to run the scan. Empty list disables the cron job.  |
| `scheduler.auto_download_after_rescan` | bool         | `false`                                       | Automatically queue missing episodes for download after each rescan. |
| `scheduler.folder_scan_enabled`        | bool         | `false`                                       | Run folder maintenance (NFO repair, folder renaming, poster checks) during scheduled runs. **When enabled, series folders are automatically renamed to match the `<title> (<year>)` convention derived from their `tvshow.nfo` files.** |

Valid day abbreviations: `mon`, `tue`, `wed`, `thu`, `fri`, `sat`, `sun`.

Source: [src/server/models/config.py](../src/server/models/config.py#L5-L12)

### 4.3 Logging Settings

| Field                  | Type   | Default  | Description                                       |
| ---------------------- | ------ | -------- | ------------------------------------------------- |
| `logging.level`        | string | `"INFO"` | Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
| `logging.file`         | string | `null`   | Optional log file path.                           |
| `logging.max_bytes`    | int    | `null`   | Maximum log file size for rotation.               |
| `logging.backup_count` | int    | `3`      | Number of rotated log files to keep.              |

Source: [src/server/models/config.py](../src/server/models/config.py#L27-L46)

### 4.4 Backup Settings

| Field              | Type   | Default          | Description                      |
| ------------------ | ------ | ---------------- | -------------------------------- |
| `backup.enabled`   | bool   | `false`          | Enable automatic config backups. |
| `backup.path`      | string | `"data/backups"` | Directory for backup files.      |
| `backup.keep_days` | int    | `30`             | Days to retain backups.          |

Source: [src/server/models/config.py](../src/server/models/config.py#L15-L24)

### 4.5 NFO Settings

| Field                 | Type   | Default  | Description                                                   |
| --------------------- | ------ | -------- | ------------------------------------------------------------- |
| `nfo.tmdb_api_key`    | string | `""`     | The Movie Database (TMDB) API key for fetching metadata.      |
| `nfo.auto_create`     | bool   | `true`   | Automatically create NFO files when downloading episodes.     |
| `nfo.update_on_scan`  | bool   | `false`  | Update existing NFO files during library scan operations.     |
| `nfo.download_poster` | bool   | `true`   | Download poster images (poster.jpg) along with NFO files.     |
| `nfo.download_logo`   | bool   | `false`  | Download logo images (logo.png) along with NFO files.         |
| `nfo.download_fanart` | bool   | `false`  | Download fanart images (fanart.jpg) along with NFO files.     |
| `nfo.image_size`      | string | `"w500"` | TMDB image size: `w500` (recommended), `w780`, or `original`. |

**Notes:**

- Obtain a TMDB API key from https://www.themoviedb.org/settings/api
- `auto_create` creates NFO files during the download process
- `update_on_scan` refreshes metadata when scanning existing anime
- `download_poster` also controls whether the scheduled folder scan checks for and re-downloads missing or corrupted `poster.jpg` files (see [NFO_GUIDE.md](NFO_GUIDE.md#6-poster-check))
- Image downloads require valid `tmdb_api_key`
- `TMDB_API_KEY` environment variable is optional when `nfo.tmdb_api_key` is configured in `data/config.json`
- Larger image sizes (`w780`, `original`) consume more storage space

Source: [src/server/models/config.py](../src/server/models/config.py#L109-L132)

### 4.6 Other Settings (Dynamic)

The `other` field stores arbitrary settings.

| Key                    | Type   | Description                             |
| ---------------------- | ------ | --------------------------------------- |
| `master_password_hash` | string | Hashed master password (pbkdf2-sha256). |
| `anime_directory`      | string | Path to anime library.                  |
| `advanced`             | object | Advanced configuration options.         |

---

## 5. Configuration Precedence

Settings are resolved in this order (first match wins):

1. Environment variable (e.g., `ANIME_DIRECTORY`)
2. `.env` file in project root
3. `data/config.json` (for dynamic settings)
4. Code defaults in `Settings` class

---

## 6. Validation Rules

### Password Requirements

Master password must meet all criteria:

- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least one special character

Source: [src/server/services/auth_service.py](../src/server/services/auth_service.py#L97-L125)

### Logging Level Validation

Must be one of: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`

Source: [src/server/models/config.py](../src/server/models/config.py#L43-L47)

### Backup Path Validation

If `backup.enabled` is `true`, `backup.path` must be set.

Source: [src/server/models/config.py](../src/server/models/config.py#L87-L91)

---

## 7. Example Configurations

### Minimal Development Setup

**.env file:**

```
LOG_LEVEL=DEBUG
ANIME_DIRECTORY=/home/user/anime
```

### Production Setup

**.env file:**

```
JWT_SECRET_KEY=your-secure-random-key-here
DATABASE_URL=postgresql+asyncpg://user:pass@localhost/aniworld
LOG_LEVEL=WARNING
CORS_ORIGINS=https://your-domain.com
API_RATE_LIMIT=60
```

### Docker Setup

```yaml
# docker-compose.yml
environment:
    - JWT_SECRET_KEY=${JWT_SECRET_KEY}
    - DATABASE_URL=sqlite:///./data/aniworld.db
    - ANIME_DIRECTORY=/media/anime
    - LOG_LEVEL=INFO
volumes:
    - ./data:/app/data
    - /media/anime:/media/anime:ro
```

---

## 8. Configuration Backup Management

### Automatic Backups

Backups are created automatically before config changes when `backup.enabled` is `true`.

Location: `data/config_backups/`

Naming: `config_backup_YYYYMMDD_HHMMSS.json`

### Manual Backup via API

```bash
# Create backup
curl -X POST http://localhost:8000/api/config/backups \
  -H "Authorization: Bearer $TOKEN"

# List backups
curl http://localhost:8000/api/config/backups \
  -H "Authorization: Bearer $TOKEN"

# Restore backup
curl -X POST http://localhost:8000/api/config/backups/config_backup_20251213.json/restore \
  -H "Authorization: Bearer $TOKEN"
```

Source: [src/server/api/config.py](../src/server/api/config.py#L67-L142)

---

## 9. Troubleshooting

### Configuration Not Loading

1. Check file permissions on `data/config.json`
2. Verify JSON syntax with a validator
3. Check logs for Pydantic validation errors

### Environment Variable Not Working

1. Ensure variable name matches exactly (case-sensitive)
2. Check `.env` file location (project root)
3. Restart application after changes

### Master Password Issues

1. Password hash is stored in `config.json` under `other.master_password_hash`
2. Delete this field to reset (requires re-setup)
3. Check hash format starts with `$pbkdf2-sha256$`

---

## 10. Related Documentation

- [API.md](API.md) - Configuration API endpoints
- [DEVELOPMENT.md](DEVELOPMENT.md) - Development environment setup
- [ARCHITECTURE.md](ARCHITECTURE.md) - Configuration service architecture
