25 KiB
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
- Incremental Development: Implement features incrementally, testing each component thoroughly before moving to the next
- Code Review: Review all generated code for adherence to project standards
- Documentation: Document all public APIs and complex logic
- Testing: Maintain test coverage above 80% for all new code
- Performance: Profile and optimize critical paths, especially download and streaming operations
- Security: Regular security audits and dependency updates
- Monitoring: Implement comprehensive monitoring and alerting
- 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.4torequirements.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_validatorforschedule_timethat validates HH:MM format (00:00–23:59). RaiseValueErrorwith a clear message on invalid input - 2.5 Add a
field_validatorforschedule_daysthat validates each entry is one of["mon","tue","wed","thu","fri","sat","sun"]. RaiseValueErroron invalid entries. Empty list is allowed (means scheduler won't run) - 2.6 Keep
interval_minutesfield for backward compatibility but it will no longer be used whenschedule_timeis set. Add a docstring noting this deprecation - 2.7 Update
data/config.jsondefault 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 raiseValidationError - 2.10 Verify invalid day raises error:
SchedulerConfig(schedule_days=['monday'])should raiseValidationError
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 withschedule_time="03:00",schedule_days= all 7 days,auto_download_after_rescan=False - 3.2 Test valid
schedule_timevalues:"00:00","03:00","12:30","23:59" - 3.3 Test invalid
schedule_timevalues:"25:00","3pm","","3:00pm","24:00","-1:00"— all must raiseValidationError - 3.4 Test valid
schedule_daysvalues:["mon"],["mon","fri"], all 7 days, empty list[] - 3.5 Test invalid
schedule_daysvalues:["monday"],["xyz"],["Mon"](case-sensitive),[""]— all must raiseValidationError - 3.6 Test
auto_download_after_rescanacceptsTrueandFalse - 3.7 Test backward compatibility: creating
SchedulerConfig(enabled=True, interval_minutes=30)without new fields uses defaults forschedule_time,schedule_days,auto_download_after_rescan - 3.8 Test serialization roundtrip:
config.model_dump()thenSchedulerConfig(**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 AsyncIOSchedulerandfrom apscheduler.triggers.cron import CronTrigger - 4.2 Add a private method
_build_cron_trigger(self) -> CronTriggerthat convertsself._config.schedule_time(HH:MM) andself._config.schedule_days(list of day abbreviations) into aCronTrigger(hour=H, minute=M, day_of_week='mon,wed,fri'). Ifschedule_daysis empty, returnNone(no trigger = don't schedule) - 4.3 Refactor
start(): instead of creating anasyncio.Taskwith_scheduler_loop, create anAsyncIOSchedulerinstance, add a job using theCronTriggerfrom_build_cron_trigger(), and callscheduler.start(). Store the scheduler asself._scheduler. If_build_cron_trigger()returnsNone, log a warning and don't add a job. Setself._is_running = True - 4.4 Refactor
stop(): callself._scheduler.shutdown(wait=False)instead of cancelling an asyncio task. Setself._is_running = False - 4.5 Add method
reload_config(self, config: SchedulerConfig) -> None: updateself._config, then if scheduler is running, reschedule the job usingself._scheduler.reschedule_job()with the new cron trigger. If trigger isNone, remove the job. If scheduler is not running and config is enabled, callstart() - 4.6 Remove or deprecate
_scheduler_loop()method entirely - 4.7 Update
get_status()to returnnext_run_timeobtained fromself._scheduler.get_job('scheduled_rescan').next_run_time(handle case where job doesn't exist). Also includeschedule_time,schedule_days, andauto_download_after_rescanin 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, checkself._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-emptymissing_episodeslist), callself._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_startedwith{"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_errorwith{"error": str(e)}and log the error. Do NOT let auto-download failures crash the scheduler - 5.6 If
auto_download_after_rescanisFalse, 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.pyto 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 updatedSchedulerConfigmodel. After saving config viaconfig_service, callscheduler_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-rescanendpoint 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 fieldscurl -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", idscheduler-time). Set default value to03:00. Add a label: "Run at" or equivalent withdata-textattribute for i18n - 7.3 Add 7 day-of-week toggle checkboxes (Mon–Sun) with ids
scheduler-day-monthroughscheduler-day-sun. All checked by default. Display them in a horizontal row. Each checkbox label should have adata-textattribute 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-checkboxaround line 292). Ensure it maps toauto_download_after_rescan - 7.5 Hide or remove the old
scheduler-intervalminutes 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-textfor 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-labelare not defined instyles.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-togglecheckboxes: 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-rundisplay: 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()— readsconfig.schedule_time,config.schedule_days,config.auto_download_after_rescan, andstatus.next_run - 9.2 Update
saveSchedulerConfig()— sendsschedule_time,schedule_days,auto_download_after_rescan, andenabledto 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_startedandauto_download_errorwith 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 oldconfig.jsonfiles that don't haveschedule_time,schedule_days, orauto_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, andauto_download_after_rescanfromdata/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
lifespanfunction callsscheduler_service.start()on startup — initializes the APScheduler. Confirmed working - 11.2 Verify that
scheduler_service.stop()is called on shutdown — callsscheduler.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()— mocksAsyncIOScheduler, verifiesadd_job()andscheduler.start()called - 12.3 Test
start()with emptyschedule_days— no job added, warning logged - 12.4 Test
stop()— verifiesscheduler.shutdown(wait=False)called - 12.5 Test
reload_config()— verifiesreschedule_job()called with new CronTrigger - 12.6 Test
reload_config()with emptyschedule_days— verifies job removed - 12.7 Test
_perform_rescan()withauto_download_after_rescan=True - 12.8 Test
_perform_rescan()withauto_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/configreturns response with new fields - 13.2 Test POST
/api/scheduler/configwith 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"andschedule_days=["mon","fri"] - 14.2 Test config change workflow: Change
schedule_timevia 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_rescanconfig fields documented with table and examples - 16.3 Updated
docs/API.md: GET/POST/api/scheduler/configrequest/response schemas documented with new fields - 16.4 Updated
docs/instructions.md: Tasks 1–16 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.mdTODO list