Files
Aniworld/docs/instructions.md

25 KiB
Raw Blame History

Aniworld Web Application Development Instructions

This document provides detailed tasks for AI agents to implement a modern web application for the Aniworld anime download manager. All tasks should follow the coding guidelines specified in the project's copilot instructions.

Project Overview

The goal is to create a FastAPI-based web application that provides a modern interface for the existing Aniworld anime download functionality. The core anime logic should remain in SeriesApp.py while the web layer provides REST API endpoints and a responsive UI.

Architecture Principles

  • Single Responsibility: Each file/class has one clear purpose
  • Dependency Injection: Use FastAPI's dependency system
  • Clean Separation: Web layer calls core logic, never the reverse
  • File Size Limit: Maximum 500 lines per file
  • Type Hints: Use comprehensive type annotations
  • Error Handling: Proper exception handling and logging

Additional Implementation Guidelines

Code Style and Standards

  • Type Hints: Use comprehensive type annotations throughout all modules
  • Docstrings: Follow PEP 257 for function and class documentation
  • Error Handling: Implement custom exception classes with meaningful messages
  • Logging: Use structured logging with appropriate log levels
  • Security: Validate all inputs and sanitize outputs
  • Performance: Use async/await patterns for I/O operations

📞 Escalation

If you encounter:

  • Architecture issues requiring design decisions
  • Tests that conflict with documented requirements
  • Breaking changes needed
  • Unclear requirements or expectations

Document the issue and escalate rather than guessing.


<EFBFBD> Credentials

Admin Login:

  • Username: admin
  • Password: Hallo123!

<EFBFBD>📚 Helpful Commands

# Run all tests
conda run -n AniWorld python -m pytest tests/ -v --tb=short

# Run specific test file
conda run -n AniWorld python -m pytest tests/unit/test_websocket_service.py -v

# Run specific test class
conda run -n AniWorld python -m pytest tests/unit/test_websocket_service.py::TestWebSocketService -v

# Run specific test
conda run -n AniWorld python -m pytest tests/unit/test_websocket_service.py::TestWebSocketService::test_broadcast_download_progress -v

# Run with extra verbosity
conda run -n AniWorld python -m pytest tests/ -vv

# Run with full traceback
conda run -n AniWorld python -m pytest tests/ -v --tb=long

# Run and stop at first failure
conda run -n AniWorld python -m pytest tests/ -v -x

# Run tests matching pattern
conda run -n AniWorld python -m pytest tests/ -v -k "auth"

# Show all print statements
conda run -n AniWorld python -m pytest tests/ -v -s

#Run app
conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload

Implementation Notes

  1. Incremental Development: Implement features incrementally, testing each component thoroughly before moving to the next
  2. Code Review: Review all generated code for adherence to project standards
  3. Documentation: Document all public APIs and complex logic
  4. Testing: Maintain test coverage above 80% for all new code
  5. Performance: Profile and optimize critical paths, especially download and streaming operations
  6. Security: Regular security audits and dependency updates
  7. Monitoring: Implement comprehensive monitoring and alerting
  8. Maintenance: Plan for regular maintenance and updates

Task Completion Checklist

For each task completed:

  • Implementation follows coding standards
  • Unit tests written and passing
  • Integration tests passing
  • Documentation updated
  • Error handling implemented
  • Logging added
  • Security considerations addressed
  • Performance validated
  • Code reviewed
  • Task marked as complete in instructions.md
  • Infrastructure.md updated and other docs
  • Changes committed to git; keep your messages in git short and clear
  • Take the next task

TODO List:

Task 1: Add APScheduler Dependency

  • 1.1 Add APScheduler>=3.10.4 to requirements.txt
  • 1.2 Verify installation: conda run -n AniWorld pip install APScheduler>=3.10.4
  • 1.3 Verify import works: conda run -n AniWorld python -c "from apscheduler.schedulers.asyncio import AsyncIOScheduler; from apscheduler.triggers.cron import CronTrigger; print('OK')"

Task 2: Extend SchedulerConfig Model

File: src/server/models/config.py

The existing SchedulerConfig has enabled: bool and interval_minutes: int. Extend it with cron-based fields.

  • 2.1 Add field schedule_time: str = "03:00" — 24h HH:MM format for the daily run time
  • 2.2 Add field schedule_days: List[str] with default ["mon","tue","wed","thu","fri","sat","sun"] — lowercase 3-letter day abbreviations
  • 2.3 Add field auto_download_after_rescan: bool = False — whether to auto-queue and start downloads of all missing episodes after a scheduled rescan completes
  • 2.4 Add a field_validator for schedule_time that validates HH:MM format (00:0023:59). Raise ValueError with a clear message on invalid input
  • 2.5 Add a field_validator for schedule_days that validates each entry is one of ["mon","tue","wed","thu","fri","sat","sun"]. Raise ValueError on invalid entries. Empty list is allowed (means scheduler won't run)
  • 2.6 Keep interval_minutes field for backward compatibility but it will no longer be used when schedule_time is set. Add a docstring noting this deprecation
  • 2.7 Update data/config.json default to include the new fields with their defaults

Verification:

  • 2.8 Run: conda run -n AniWorld python -c "from src.server.models.config import SchedulerConfig; c = SchedulerConfig(); print(c.schedule_time, c.schedule_days, c.auto_download_after_rescan)"
  • 2.9 Verify invalid time raises error: SchedulerConfig(schedule_time='25:00') should raise ValidationError
  • 2.10 Verify invalid day raises error: SchedulerConfig(schedule_days=['monday']) should raise ValidationError

Task 3: Write Unit Tests for SchedulerConfig Model

File: tests/unit/test_scheduler_config_model.py (new file)

  • 3.1 Test default values: SchedulerConfig() creates instance with schedule_time="03:00", schedule_days = all 7 days, auto_download_after_rescan=False
  • 3.2 Test valid schedule_time values: "00:00", "03:00", "12:30", "23:59"
  • 3.3 Test invalid schedule_time values: "25:00", "3pm", "", "3:00pm", "24:00", "-1:00" — all must raise ValidationError
  • 3.4 Test valid schedule_days values: ["mon"], ["mon","fri"], all 7 days, empty list []
  • 3.5 Test invalid schedule_days values: ["monday"], ["xyz"], ["Mon"] (case-sensitive), [""] — all must raise ValidationError
  • 3.6 Test auto_download_after_rescan accepts True and False
  • 3.7 Test backward compatibility: creating SchedulerConfig(enabled=True, interval_minutes=30) without new fields uses defaults for schedule_time, schedule_days, auto_download_after_rescan
  • 3.8 Test serialization roundtrip: config.model_dump() then SchedulerConfig(**dumped) produces identical config

Verification:

  • 3.9 Run: conda run -n AniWorld python -m pytest tests/unit/test_scheduler_config_model.py -v — all tests pass

Task 4: Rewrite SchedulerService to Use APScheduler with CronTrigger

File: src/server/services/scheduler_service.py

The existing service used a custom asyncio.sleep() loop in _scheduler_loop(). Replaced with APScheduler's AsyncIOScheduler and CronTrigger.

  • 4.1 Add imports: from apscheduler.schedulers.asyncio import AsyncIOScheduler and from apscheduler.triggers.cron import CronTrigger
  • 4.2 Add a private method _build_cron_trigger(self) -> CronTrigger that converts self._config.schedule_time (HH:MM) and self._config.schedule_days (list of day abbreviations) into a CronTrigger(hour=H, minute=M, day_of_week='mon,wed,fri'). If schedule_days is empty, return None (no trigger = don't schedule)
  • 4.3 Refactor start(): instead of creating an asyncio.Task with _scheduler_loop, create an AsyncIOScheduler instance, add a job using the CronTrigger from _build_cron_trigger(), and call scheduler.start(). Store the scheduler as self._scheduler. If _build_cron_trigger() returns None, log a warning and don't add a job. Set self._is_running = True
  • 4.4 Refactor stop(): call self._scheduler.shutdown(wait=False) instead of cancelling an asyncio task. Set self._is_running = False
  • 4.5 Add method reload_config(self, config: SchedulerConfig) -> None: update self._config, then if scheduler is running, reschedule the job using self._scheduler.reschedule_job() with the new cron trigger. If trigger is None, remove the job. If scheduler is not running and config is enabled, call start()
  • 4.6 Remove or deprecate _scheduler_loop() method entirely
  • 4.7 Update get_status() to return next_run_time obtained from self._scheduler.get_job('scheduled_rescan').next_run_time (handle case where job doesn't exist). Also include schedule_time, schedule_days, and auto_download_after_rescan in status dict
  • 4.8 The job function should be _perform_rescan() (existing method) — keep it as the scheduled task. It is already async
  • 4.9 Add structured logging: log when scheduler starts, stops, reschedules, and when cron trigger is built (include the cron expression in the log)
  • 4.10 Ensure the file stays under 500 lines. If it exceeds, extract helper functions

Verification:

  • 4.11 Start the app: conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000. Check logs for scheduler startup messages
  • 4.12 Verify no import errors or startup crashes

Task 5: Implement Auto-Download After Rescan Logic

File: src/server/services/scheduler_service.py

  • 5.1 At the end of _perform_rescan(), after the scan completes successfully, check self._config.auto_download_after_rescan
  • 5.2 If enabled: call self._anime_service.get_all_anime() to get all series. For each series that has missing episodes (non-empty missing_episodes list), call self._download_service.add_to_queue() for each missing episode.
  • 5.3 After all episodes are queued, call self._download_service.start_download() (or equivalent method from the existing download service) to begin processing the queue
  • 5.4 Broadcast a WebSocket event auto_download_started with {"queued_count": N} where N is the number of episodes queued. Use the existing _broadcast() helper method
  • 5.5 Wrap the auto-download logic in try/except. On failure, broadcast auto_download_error with {"error": str(e)} and log the error. Do NOT let auto-download failures crash the scheduler
  • 5.6 If auto_download_after_rescan is False, skip the auto-download logic entirely (just log that auto-download is disabled)
  • 5.7 Add structured logging for: auto-download started, number of episodes queued, auto-download completed, auto-download failed

Verification:

  • 5.8 Read src/server/services/download_service.py to confirm the exact method names and signatures used for queueing and starting downloads. Use those exact method names

Task 6: Update Scheduler API Endpoints

File: src/server/api/scheduler.py

  • 6.1 Update GET /api/scheduler/config: Return response that includes the new config fields (schedule_time, schedule_days, auto_download_after_rescan) plus runtime status (is_running, next_run, last_run). The response shape should be {"success": true, "config": {...all config fields...}, "status": {...runtime fields...}}
  • 6.2 Update POST /api/scheduler/config: Accept the new fields in the request body. Validate using the updated SchedulerConfig model. After saving config via config_service, call scheduler_service.reload_config(new_config) to apply changes immediately without restart. Return the same enriched response format as GET
  • 6.3 Ensure backward compatibility: if a POST request only sends {"enabled": true} without new fields, use existing/default values for the omitted fields
  • 6.4 Add proper error responses: 422 for validation errors (invalid time/days), 500 for scheduler failures
  • 6.5 The existing POST /api/scheduler/trigger-rescan endpoint should remain unchanged — it triggers an immediate rescan (which will also auto-download if configured)

Verification:

  • 6.6 Start the app and test with curl:
    • curl -H "Authorization: Bearer <token>" http://127.0.0.1:8000/api/scheduler/config — should return new fields
    • curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{"enabled":true,"schedule_time":"02:00","schedule_days":["mon","wed","fri"],"auto_download_after_rescan":true}' http://127.0.0.1:8000/api/scheduler/config — should save and return updated config

Task 7: Update Frontend HTML — Scheduler Settings Section

File: src/server/web/templates/index.html

  • 7.1 Keep the existing "Enable Scheduler" checkbox (scheduler-enabled)
  • 7.2 Replace or supplement the scheduler-interval (minutes) input with a time picker input (type="time", id scheduler-time). Set default value to 03:00. Add a label: "Run at" or equivalent with data-text attribute for i18n
  • 7.3 Add 7 day-of-week toggle checkboxes (MonSun) with ids scheduler-day-mon through scheduler-day-sun. All checked by default. Display them in a horizontal row. Each checkbox label should have a data-text attribute for i18n. Use short labels: "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
  • 7.4 Keep the existing "Auto-download after rescan" checkbox (it already exists in the HTML as auto-download-checkbox around line 292). Ensure it maps to auto_download_after_rescan
  • 7.5 Hide or remove the old scheduler-interval minutes input. If keeping for backward compat, move it to a collapsible "Advanced" section
  • 7.6 Add a help text below the day checkboxes: "Scheduler runs at the selected time on checked days. Uncheck all to disable scheduling." — with data-text for i18n
  • 7.7 Add a display area showing "Next scheduled run: " that gets populated by JS (id scheduler-next-run)
  • 7.8 Ensure all new elements work in both dark and light themes (use existing CSS classes)

Verification:

  • 7.9 Start the app, open the settings modal, verify all new UI elements render correctly in both dark and light mode
  • 7.10 Verify the day-of-week checkboxes are displayed horizontally and are all checked by default

Task 8: Update Frontend CSS — Day-of-Week Toggle Styling

File: src/server/web/static/css/styles.css

⚠️ NOT YET DONE — The HTML elements exist but CSS classes .scheduler-days-container, .scheduler-day-toggle, .scheduler-day-label are not defined in styles.css.

  • 8.1 Add styles for .scheduler-days-container: horizontal flexbox layout, gap between items, wrapping on small screens
  • 8.2 Add styles for .scheduler-day-toggle checkboxes: styled as pill/button toggles. Unchecked = outline/muted, Checked = accent/filled. Use existing theme CSS variables for colors
  • 8.3 Add styles for .scheduler-day-label: centered text, pointer cursor, appropriate padding
  • 8.4 Ensure dark mode support using existing dark mode CSS variables/selectors (check existing dark mode patterns in the file)
  • 8.5 Ensure responsive layout: on mobile, days should wrap to 2 rows if needed
  • 8.6 Style the #scheduler-next-run display: muted text, smaller font size, italic

Verification:

  • 8.7 Test in browser: verify day toggles look like toggle buttons, dark/light mode both work, responsive on narrow viewport

Task 9: Update Frontend JavaScript — Scheduler Config Load/Save

File: src/server/web/static/js/app.js

  • 9.1 Update loadSchedulerConfig() — reads config.schedule_time, config.schedule_days, config.auto_download_after_rescan, and status.next_run
  • 9.2 Update saveSchedulerConfig() — sends schedule_time, schedule_days, auto_download_after_rescan, and enabled to POST /api/scheduler/config
  • 9.3 Update toggleSchedulerTimeInput() — toggles visibility of day checkboxes and next-run display when scheduler is enabled/disabled
  • 9.4 Handle WebSocket events auto_download_started and auto_download_error with toast notifications

Verification:

  • 9.5 Start the app, open settings, change time to 02:00, uncheck "Sat" and "Sun", enable auto-download, save. Reload the page and verify values persist
  • 9.6 Check browser console for any JS errors during load/save
  • 9.7 Verify the "Next scheduled run" display updates after saving

Task 10: Update Config Service for Backward Compatibility

File: src/server/services/config_service.py

  • 10.1 Ensure load_config() handles old config.json files that don't have schedule_time, schedule_days, or auto_download_after_rescan — Pydantic defaults handle this automatically
  • 10.2 Ensure save_config() writes the new fields to disk
  • 10.3 Pydantic model defaults handle migration automatically; no explicit migration step required

Verification:

  • 10.4 Delete schedule_time, schedule_days, and auto_download_after_rescan from data/config.json, restart the app, verify defaults are used and the app doesn't crash
  • 10.5 Verify the config is saved with the new fields after any settings change

Task 11: Update FastAPI Lifespan for APScheduler

File: src/server/fastapi_app.py

  • 11.1 Verify that the lifespan function calls scheduler_service.start() on startup — initializes the APScheduler. Confirmed working
  • 11.2 Verify that scheduler_service.stop() is called on shutdown — calls scheduler.shutdown(). Confirmed working
  • 11.3 Ensure no duplicate scheduler instances are created (singleton pattern in get_scheduler_service() prevents this)

Verification:

  • 11.4 Start and stop the app, verify no errors in logs about scheduler startup/shutdown
  • 11.5 Verify logs show "Scheduler started with cron trigger: ..." or similar

Task 12: Write Unit Tests for SchedulerService (APScheduler + Auto-Download)

File: tests/unit/test_scheduler_service.py

  • 12.1 Test _build_cron_trigger() with various input combinations
  • 12.2 Test start() — mocks AsyncIOScheduler, verifies add_job() and scheduler.start() called
  • 12.3 Test start() with empty schedule_days — no job added, warning logged
  • 12.4 Test stop() — verifies scheduler.shutdown(wait=False) called
  • 12.5 Test reload_config() — verifies reschedule_job() called with new CronTrigger
  • 12.6 Test reload_config() with empty schedule_days — verifies job removed
  • 12.7 Test _perform_rescan() with auto_download_after_rescan=True
  • 12.8 Test _perform_rescan() with auto_download_after_rescan=False
  • 12.9 Test _perform_rescan() auto-download error handling
  • 12.10 Test get_status() returns correct fields
  • 12.11 Removed tests for old _scheduler_loop() method
  • 12.12 File stays under 500 lines

Verification:

  • 12.13 Run: conda run -n AniWorld python -m pytest tests/unit/test_scheduler_service.py -v --tb=short — all tests pass
  • 12.14 Run: conda run -n AniWorld python -m pytest tests/unit/test_scheduler_config_model.py -v --tb=short — all tests pass
  • 12.15 Run coverage: conda run -n AniWorld python -m pytest tests/unit/test_scheduler_service.py tests/unit/test_scheduler_config_model.py --cov=src/server/services/scheduler_service --cov=src/server/models/config --cov-report=term-missing — target ≥80% on both modules

Task 13: Write/Update API Endpoint Tests

File: tests/api/test_scheduler_endpoints.py

  • 13.1 Test GET /api/scheduler/config returns response with new fields
  • 13.2 Test POST /api/scheduler/config with valid new fields saves correctly
  • 13.3 Test POST with invalid schedule_time ("25:00") returns 422 validation error
  • 13.4 Test POST with invalid schedule_days (["monday"]) returns 422 validation error
  • 13.5 Test POST with only {"enabled": true} (backward compat) — defaults used
  • 13.6 Test POST with schedule_days: [] — valid, scheduler has no scheduled jobs
  • 13.7 Test trigger-rescan endpoint still works correctly

Verification:

  • 13.8 Run: conda run -n AniWorld python -m pytest tests/api/test_scheduler_endpoints.py -v --tb=short — all tests pass

Task 14: Write/Update Integration Tests

File: tests/integration/test_scheduler_workflow.py

  • 14.1 Test full workflow: Enable scheduler with schedule_time="02:00" and schedule_days=["mon","fri"]
  • 14.2 Test config change workflow: Change schedule_time via API → job rescheduled
  • 14.3 Test disable/re-enable workflow
  • 14.4 Test auto-download integration with auto_download_after_rescan=True
  • 14.5 Test auto-download disabled scenario

Verification:

  • 14.6 Run: conda run -n AniWorld python -m pytest tests/integration/test_scheduler_workflow.py -v --tb=short — all tests pass

Task 15: Run Full Test Suite and Verify Coverage

  • 15.1 Run all tests: conda run -n AniWorld python -m pytest tests/ -v --tb=short — all tests pass, no regressions
  • 15.2 Run coverage for scheduler-related code: conda run -n AniWorld python -m pytest tests/ --cov=src/server/services/scheduler_service --cov=src/server/models/config --cov=src/server/api/scheduler --cov-report=term-missing — all three modules ≥80%
  • 15.3 Fix any failing tests from other test files that may have been affected by the config model changes
  • 15.4 Run: conda run -n AniWorld python -m pytest tests/ -v -x — stop at first failure, fix, repeat until all green

Task 16: Update Documentation

  • 16.1 Updated docs/features.md: Cron-based scheduling, auto-download after rescan, and time + day-of-week configuration documented
  • 16.2 Updated docs/CONFIGURATION.md: scheduler.schedule_time, scheduler.schedule_days, scheduler.auto_download_after_rescan config fields documented with table and examples
  • 16.3 Updated docs/API.md: GET/POST /api/scheduler/config request/response schemas documented with new fields
  • 16.4 Updated docs/instructions.md: Tasks 116 marked as completed
  • 16.5 Updated docs/CHANGELOG.md: Cron scheduler feature entry added under [Unreleased]

Task 17: Final Manual Verification

  • 17.1 Start the app: conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload
  • 17.2 Log in with admin credentials (admin / Hallo123!)
  • 17.3 Open settings modal → Scheduler section. Verify: time picker shows 03:00, all 7 days are checked, auto-download is unchecked
  • 17.4 Change time to 02:00, uncheck Sat and Sun, enable auto-download, save. Verify toast notification confirms save
  • 17.5 Reload the page, reopen settings. Verify saved values persist
  • 17.6 Check terminal logs for: "Scheduler rescheduled with cron trigger: ..." message
  • 17.7 Trigger manual rescan. If auto-download is enabled and there are missing episodes, verify episodes appear in download queue
  • 17.8 Disable scheduler entirely, save. Verify logs show scheduler stopped
  • 17.9 Re-enable scheduler, save. Verify logs show scheduler started with correct cron expression
  • 17.10 Test dark mode and light mode — all scheduler UI elements render correctly
  • 17.11 Test on narrow viewport (mobile) — day checkboxes wrap properly (note: CSS Task 8 must be done first)

Task 18: Commit and Clean Up

  • 18.1 Review all changed files for adherence to architecture principles (single responsibility, <500 lines, type hints, docstrings)
  • 18.2 Run linter: conda run -n AniWorld python -m flake8 src/server/services/scheduler_service.py src/server/models/config.py src/server/api/scheduler.py — no critical issues
  • 18.3 Commit with message: feat: cron-based scheduler with auto-download after rescan
  • 18.4 Mark this feature as complete in docs/instructions.md TODO list