# AGENTS.md ## What this project is - PYTV is a Django + Django-Ninja backend (`core/`, `api/`, `pytv/`) with a React/Vite frontend (`frontend/`) for a "cable TV" style experience. - Core domain model is in `core/models.py`: `Channel`, `ScheduleTemplate`/`ScheduleBlock`, `Airing`, `MediaSource`, `MediaItem`. - API entrypoint is `api/api.py`; all HTTP API routes are mounted under `/api/` in `pytv/urls.py`. ## Architecture and data flow to understand first - Schedule generation: `api/routers/schedule.py` -> `core/services/scheduler.py` (`ScheduleGenerator.generate_for_date`) -> creates `Airing` rows. - Playback signaling: `api/routers/channel.py` (`/channel/{id}/now`, `/airings`) computes `exact_playback_offset_seconds` for frontend sync. - Media caching pipeline for YouTube sources: - metadata sync only: `core/services/youtube.py::sync_source` - just-in-time download: `core/services/cache.py::run_cache` + `youtube.download_for_airing` - emergency replacement (<1h before air with no cache): `ScheduleGenerator.replace_undownloaded_airings`. - Local media serving in dev uses range-aware endpoint `api/views.py::serve_video_with_range` mounted at `/media/*` when `DEBUG=True`. ## Backend conventions (project-specific) - API style: Django Ninja `Schema`/Pydantic payloads in routers under `api/routers/`. - Auth pattern: session auth + CSRF cookie (`api/routers/auth.py`); frontend sends `withCredentials` and `X-CSRFToken` (`frontend/src/api.js`). - Session persistence is settings-driven; use `SESSION_COOKIE_AGE`/`SESSION_EXPIRE_AT_BROWSER_CLOSE` in `pytv/settings.py` (do not hardcode lifetimes in routers/frontend). - `Channel.requires_auth` is actively enforced in channel endpoints (`/now`, `/airings`, `/status`). - Source-rule behavior is business-critical: `allow/prefer/avoid/block` weighting in `core/services/scheduler.py` (not just CRUD metadata). - In `api/routers/sources.py`, literal routes (e.g. `/cache-upcoming`) must stay before parameterized routes (explicitly documented in file). ## Frontend/backend integration points - Frontend API client is centralized in `frontend/src/api.js` (do not scatter fetch logic). - Dev proxy is configured in `frontend/vite.config.js` for both `/api` and `/media` to `http://localhost:8000`. - Channel playback UX depends on `frontend/src/components/ChannelTuner.jsx` triple-buffer behavior and backend-provided offset. ## Developer workflows (discovered commands) ```bash # Local backend (from repo root) python manage.py migrate python manage.py runserver 0.0.0.0:8000 python manage.py seed python manage.py cache_upcoming --hours 24 python manage.py run_cache_worker --interval 600 --hours 24 python manage.py backup_now python manage.py run_backup_worker --interval 60 python manage.py state --channel --test-generate # Tests (pytest configured via pytest.ini) pytest # Frontend (from frontend/) npm install npm run dev npm run build npm run lint # Docker stack (repo root) docker compose up --build # Docker migrations (ALWAYS run inside the web container) docker compose exec web python manage.py migrate # Local DB reset (admin/dev only, destructive) docker compose exec web python manage.py flush --no-input docker compose exec web python manage.py migrate # Optional: reseed mock data after reset docker compose exec web python manage.py seed # Optional: run tests in the same containerized environment docker compose exec web pytest ``` ## Agent execution safety rails - In this repo, assume the user controls long-running processes. Do not start/stop `docker compose up`, Django dev server, or Vite dev server unless explicitly asked. - Prefer read-only investigation first (`read_file`, targeted tests) before broad edits. - Keep business logic in `core/services/`; routers should remain thin orchestration/API layers. - Preserve public API shapes used by frontend (`frontend/src/api.js`) unless the task explicitly includes coordinated frontend updates. - Do not reorder literal and parameterized source routes in `api/routers/sources.py` in a way that changes URL matching behavior. - Treat `cached_file_path` and `/media/...` URL behavior as compatibility-sensitive; verify signaling paths when changing cache/playback logic. - Backups are compatibility-sensitive and stored at fixed path `/Backups`; do not add user-configurable backup destination paths. ## Containerized dev/test expectations - The user runs Docker and dev servers; agents should not spin them up by default. - If a schema change is introduced, run migrations via container exec, not host Python: - `docker compose exec web python manage.py makemigrations` - `docker compose exec web python manage.py migrate` - Prefer running validation commands inside `web` for parity with runtime dependencies (yt-dlp/node/ffprobe path assumptions). - When sharing run steps in PR notes, provide container commands first, then local equivalents as secondary options. ## Testing map and where to extend - API router tests: `api/tests/test_*.py`. - End-to-end domain behavior tests: `tests/test_channel_actions.py`, `tests/test_source_rules.py`, `tests/test_playback_sync.py`, `tests/test_channel_auth.py`. - If changing scheduler/cache/youtube logic, update both API-level tests and service-behavior tests. ## Practical guardrails for AI agents - Prefer editing service-layer logic in `core/services/` over embedding business rules in routers. - Preserve idempotent schedule generation semantics (`generate_for_date` clears/rebuilds block window airings). - Keep `MediaItem.cached_file_path` semantics intact: frontend playback and cache status depend on it. - Validate cross-layer changes by exercising both API endpoints and frontend assumptions around `/media/...` URLs.