diff --git a/data/config.json b/data/config.json index c322291..8ad7837 100644 --- a/data/config.json +++ b/data/config.json @@ -17,7 +17,7 @@ "keep_days": 30 }, "other": { - "master_password_hash": "$pbkdf2-sha256$29000$4Ny7tzZGaG2ttVaKsRZiLA$29mSesYMcIC0u0JfpP3SM7c.fEiE82.VYh9q2vZEBRw" + "master_password_hash": "$pbkdf2-sha256$29000$R0hpDWEspRSidA4BoPTemw$NL4pP6ch.3sRxe6gjQ1tM3VPntwZMoZUFAI9sTQuHPE" }, "version": "1.0.0" } \ No newline at end of file diff --git a/data/config_backups/config_backup_20251202_155127.json b/data/config_backups/config_backup_20251202_155127.json deleted file mode 100644 index a3009f6..0000000 --- a/data/config_backups/config_backup_20251202_155127.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Aniworld", - "data_dir": "data", - "scheduler": { - "enabled": true, - "interval_minutes": 60 - }, - "logging": { - "level": "INFO", - "file": null, - "max_bytes": null, - "backup_count": 3 - }, - "backup": { - "enabled": false, - "path": "data/backups", - "keep_days": 30 - }, - "other": { - "master_password_hash": "$pbkdf2-sha256$29000$EUKI8d67d86ZE.K8VypF6A$4mqRLeh3WL2AsHFXNET.1D9T.weMNIE5Ffw6cIgA4ho" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/data/config_backups/config_backup_20251202_155310.json b/data/config_backups/config_backup_20251202_155310.json deleted file mode 100644 index a95bd88..0000000 --- a/data/config_backups/config_backup_20251202_155310.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Aniworld", - "data_dir": "data", - "scheduler": { - "enabled": true, - "interval_minutes": 60 - }, - "logging": { - "level": "INFO", - "file": null, - "max_bytes": null, - "backup_count": 3 - }, - "backup": { - "enabled": false, - "path": "data/backups", - "keep_days": 30 - }, - "other": { - "master_password_hash": "$pbkdf2-sha256$29000$VooRQui9t/beGwMAgNAaQw$idnI9fpdgl0hAd7susBuX6rpux/L/k4PJ1QMQfjwpvo" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/data/config_backups/config_backup_20251202_155359.json b/data/config_backups/config_backup_20251202_155359.json deleted file mode 100644 index 61a5e9d..0000000 --- a/data/config_backups/config_backup_20251202_155359.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Aniworld", - "data_dir": "data", - "scheduler": { - "enabled": true, - "interval_minutes": 60 - }, - "logging": { - "level": "INFO", - "file": null, - "max_bytes": null, - "backup_count": 3 - }, - "backup": { - "enabled": false, - "path": "data/backups", - "keep_days": 30 - }, - "other": { - "master_password_hash": "$pbkdf2-sha256$29000$/x8jxFgLofQegzAm5DzHeA$kO44/L.4b3sEDOCuzJkunefAZ9ap5jsFZP/JDaRIUt0" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/data/config_backups/config_backup_20251202_155607.json b/data/config_backups/config_backup_20251202_155607.json deleted file mode 100644 index b3e64fa..0000000 --- a/data/config_backups/config_backup_20251202_155607.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Aniworld", - "data_dir": "data", - "scheduler": { - "enabled": true, - "interval_minutes": 60 - }, - "logging": { - "level": "INFO", - "file": null, - "max_bytes": null, - "backup_count": 3 - }, - "backup": { - "enabled": false, - "path": "data/backups", - "keep_days": 30 - }, - "other": { - "master_password_hash": "$pbkdf2-sha256$29000$htA6x1jrHYPwvre2FkJoTQ$37rrE4hOMgdowfzS9XaaH/EjPDZZFSlc0RL1blcXEVU" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/data/config_backups/config_backup_20251202_155748.json b/data/config_backups/config_backup_20251202_155748.json deleted file mode 100644 index 456f01d..0000000 --- a/data/config_backups/config_backup_20251202_155748.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Aniworld", - "data_dir": "data", - "scheduler": { - "enabled": true, - "interval_minutes": 60 - }, - "logging": { - "level": "INFO", - "file": null, - "max_bytes": null, - "backup_count": 3 - }, - "backup": { - "enabled": false, - "path": "data/backups", - "keep_days": 30 - }, - "other": { - "master_password_hash": "$pbkdf2-sha256$29000$.t.bk1IKQah1bg0BoNS6tw$TbbOVxdX4U7xhiRPPyJM6cXl5EnVzlM/3YMZF714Aoc" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/data/config_backups/config_backup_20251202_155022.json b/data/config_backups/config_backup_20251202_173540.json similarity index 74% rename from data/config_backups/config_backup_20251202_155022.json rename to data/config_backups/config_backup_20251202_173540.json index d7d349b..15e8092 100644 --- a/data/config_backups/config_backup_20251202_155022.json +++ b/data/config_backups/config_backup_20251202_173540.json @@ -17,7 +17,7 @@ "keep_days": 30 }, "other": { - "master_password_hash": "$pbkdf2-sha256$29000$F0JIKQWAEEJoba3VGuOckw$ae64QkQc0QkMiSiO3H3Bg8mZE5nOQ8hrN5gl9LQLjnw" + "master_password_hash": "$pbkdf2-sha256$29000$JWTsXWstZYyxNiYEQAihFA$K9QPNr2J9biZEX/7SFKU94dnynvyCICrGjKtZcEu6t8" }, "version": "1.0.0" } \ No newline at end of file diff --git a/docs/infrastructure.md b/docs/infrastructure.md index f93e07e..a9a096e 100644 --- a/docs/infrastructure.md +++ b/docs/infrastructure.md @@ -166,22 +166,22 @@ All series-related WebSocket events include `key` as the primary identifier in t ### DownloadQueueItem Fields -| Field | Type | Purpose | -| -------------- | ----------- | --------------------------------------------- | -| `id` | String (PK) | UUID for the queue item | -| `serie_id` | String | Series key for identification | -| `serie_folder` | String | Filesystem folder path | -| `serie_name` | String | Display name for the series | -| `season` | Integer | Season number | -| `episode` | Integer | Episode number | -| `status` | Enum | pending, downloading, completed, failed | -| `priority` | Enum | low, normal, high | -| `progress` | Float | Download progress percentage (0.0-100.0) | -| `error` | String | Error message if failed | -| `retry_count` | Integer | Number of retry attempts | -| `added_at` | DateTime | When item was added to queue | -| `started_at` | DateTime | When download started (nullable) | -| `completed_at` | DateTime | When download completed/failed (nullable) | +| Field | Type | Purpose | +| -------------- | ----------- | ----------------------------------------- | +| `id` | String (PK) | UUID for the queue item | +| `serie_id` | String | Series key for identification | +| `serie_folder` | String | Filesystem folder path | +| `serie_name` | String | Display name for the series | +| `season` | Integer | Season number | +| `episode` | Integer | Episode number | +| `status` | Enum | pending, downloading, completed, failed | +| `priority` | Enum | low, normal, high | +| `progress` | Float | Download progress percentage (0.0-100.0) | +| `error` | String | Error message if failed | +| `retry_count` | Integer | Number of retry attempts | +| `added_at` | DateTime | When item was added to queue | +| `started_at` | DateTime | When download started (nullable) | +| `completed_at` | DateTime | When download completed/failed (nullable) | ## Data Storage @@ -189,13 +189,13 @@ All series-related WebSocket events include `key` as the primary identifier in t The application uses **SQLite database** as the primary storage for all application data. -| Data Type | Storage Location | Service | -| --------------- | ------------------ | ---------------------------- | -| Anime Series | `data/aniworld.db` | `AnimeSeriesService` | -| Episodes | `data/aniworld.db` | `AnimeSeriesService` | -| Download Queue | `data/aniworld.db` | `DownloadService` via `QueueRepository` | -| User Sessions | `data/aniworld.db` | `AuthService` | -| Configuration | `data/config.json` | `ConfigService` | +| Data Type | Storage Location | Service | +| -------------- | ------------------ | --------------------------------------- | +| Anime Series | `data/aniworld.db` | `AnimeSeriesService` | +| Episodes | `data/aniworld.db` | `AnimeSeriesService` | +| Download Queue | `data/aniworld.db` | `DownloadService` via `QueueRepository` | +| User Sessions | `data/aniworld.db` | `AuthService` | +| Configuration | `data/config.json` | `ConfigService` | ### Download Queue Storage @@ -219,11 +219,12 @@ await repository.update_progress(item_id, progress=45.5, downloaded=450, total=1 ``` **Queue Persistence Features:** -- Queue state survives server restarts -- Items in `downloading` status are reset to `pending` on startup -- Failed items within retry limit are automatically re-queued -- Completed and failed history is preserved (with limits) -- Real-time progress updates are persisted to database + +- Queue state survives server restarts +- Items in `downloading` status are reset to `pending` on startup +- Failed items within retry limit are automatically re-queued +- Completed and failed history is preserved (with limits) +- Real-time progress updates are persisted to database ### Anime Series Database Storage diff --git a/instructions.md b/instructions.md index ebf177d..a9f3d8c 100644 --- a/instructions.md +++ b/instructions.md @@ -125,20 +125,21 @@ For each task completed: The download queue has been successfully migrated from JSON file to SQLite database: -| Component | Status | Description | -| ---------------------- | --------- | ------------------------------------------------ | -| QueueRepository | ✅ Done | `src/server/services/queue_repository.py` | -| DownloadService | ✅ Done | Refactored to use repository pattern | -| Application Startup | ✅ Done | Queue restored from database on startup | -| API Endpoints | ✅ Done | All endpoints work with database-backed queue | -| Tests Updated | ✅ Done | All 1104 tests passing with MockQueueRepository | -| Documentation Updated | ✅ Done | `infrastructure.md` updated with new architecture| +| Component | Status | Description | +| --------------------- | ------- | ------------------------------------------------- | +| QueueRepository | ✅ Done | `src/server/services/queue_repository.py` | +| DownloadService | ✅ Done | Refactored to use repository pattern | +| Application Startup | ✅ Done | Queue restored from database on startup | +| API Endpoints | ✅ Done | All endpoints work with database-backed queue | +| Tests Updated | ✅ Done | All 1104 tests passing with MockQueueRepository | +| Documentation Updated | ✅ Done | `infrastructure.md` updated with new architecture | **Key Changes:** -- `DownloadService` no longer uses `persistence_path` parameter -- Queue state is persisted to SQLite via `QueueRepository` -- In-memory cache maintained for performance -- All tests use `MockQueueRepository` fixture + +- `DownloadService` no longer uses `persistence_path` parameter +- Queue state is persisted to SQLite via `QueueRepository` +- In-memory cache maintained for performance +- All tests use `MockQueueRepository` fixture --- diff --git a/src/server/fastapi_app.py b/src/server/fastapi_app.py index 3333aa3..0004ca8 100644 --- a/src/server/fastapi_app.py +++ b/src/server/fastapi_app.py @@ -113,11 +113,21 @@ async def lifespan(app: FastAPI): progress_service.subscribe("progress_updated", progress_event_handler) # Initialize download service and restore queue from database + # Only if anime directory is configured try: + from src.server.config.settings import settings + from src.server.utils.dependencies import get_download_service - download_service = get_download_service() - await download_service.initialize() - logger.info("Download service initialized and queue restored") + + if settings.anime_directory: + download_service = get_download_service() + await download_service.initialize() + logger.info("Download service initialized and queue restored") + else: + logger.info( + "Download service initialization skipped - " + "anime directory not configured" + ) except Exception as e: logger.warning("Failed to initialize download service: %s", e) # Continue startup - download service can be initialized later