# Delete Anime Feature ## Overview The Delete Anime feature allows authenticated users to remove an anime series from the Aniworld library. It supports three deletion modes: **database only**, **folder only**, or **both**. A mandatory typed-confirmation (`delete`) prevents accidental deletions. --- ## Usage ### How to Delete an Anime 1. **Right-click** on any anime series card in the library grid. 2. Select **"Delete Anime"** from the context menu. 3. A confirmation modal appears with two options: - ☑️ **Remove from database** (recommended) — removes series and episodes from SQLite - ☐ **Delete folder from filesystem** — deletes the folder and all files inside 4. **Type `delete`** in the confirmation text field to enable the Delete button. 5. Click **Delete** to proceed. ### What Gets Deleted | Option | Effect | |--------|--------| | Database only | Series, episodes, and queue entries removed from SQLite. Folder on disk is preserved. Downloaded episode files remain. | | Folder only | Entire folder and all files inside deleted from filesystem. Database record preserved with `is_downloaded=True`. | | Both | Full removal: database record deleted AND folder/files deleted from disk. | --- ## Architecture ### Backend Components | File | Role | |------|------| | `src/server/api/anime.py` | `DELETE /api/anime/{key}` endpoint | | `src/server/services/anime_service.py` | `AnimeService.delete_series()` orchestrator | | `src/server/services/websocket_service.py` | `broadcast_series_deleted()` for real-time UI updates | | `src/server/database/service.py` | `AnimeSeriesService.get_folder_path()` + existing `delete()` | | `src/server/models/anime.py` | `DeleteSeriesRequest` / `DeleteSeriesResult` Pydantic models | ### Frontend Components | File | Role | |------|------| | `src/server/web/static/js/index/delete-modal.js` | Modal UI, confirm text validation, API calls | | `src/server/web/static/js/index/context-menu.js` | Right-click "Delete Anime" menu item | | `src/server/web/static/js/index/socket-handler.js` | `SERIES_DELETED` WebSocket event handler | | `src/server/web/static/js/index/series-manager.js` | `removeSeries(key)` — removes card from grid | | `src/server/web/static/js/index/app-init.js` | Initializes `DeleteModal` | | `src/server/web/static/css/components/modals.css` | Modal and context menu styles | | `src/server/web/templates/index.html` | Loads `delete-modal.js` before `app-init.js` | ### API Endpoint ``` DELETE /api/anime/{key} ``` **Request body:** ```json { "delete_database": true, "delete_folder": false, "confirm_text": "delete" } ``` **Success response (200):** ```json { "success": true, "key": "attack-on-titan", "name": "Attack on Titan", "deleted_from_database": true, "deleted_folder": false, "folder_path": null, "database_error": null, "folder_error": null, "message": "Removed from database." } ``` **Error responses:** | Status | Condition | |--------|-----------| | 400 | `confirm_text != "delete"` or neither flag is `true` | | 401 | Not authenticated | | 404 | Series `key` not found in database | | 500 | Unexpected server error | ### WebSocket Event After a successful delete, the server broadcasts a `series_deleted` event: ```json { "type": "series_deleted", "data": { "key": "attack-on-titan", "name": "Attack on Titan" } } ``` All connected clients remove the card from their grid in real-time. --- ## Safety Mechanisms ### 1. Typed Confirmation Users must type exactly `delete` (case-sensitive) to unlock the Delete button. This prevents accidental clicks from triggering deletion. ### 2. Path Traversal Protection Before deleting a folder, `is_safe_path()` validates the path stays within the configured anime base directory. Paths outside this boundary are rejected with a `folder_error`. ### 3. Granular Options The two independent checkboxes ensure users consciously choose what to delete. Default is **database only** (recommended). ### 4. WebSocket Broadcast All clients are notified immediately when a series is deleted, keeping multiple browser sessions in sync. ### 5. No Shell Injection Series keys are never passed to shell commands. All file operations use `pathlib.Path`. --- ## Logging ### Backend Logs (Python/`logging`) | Event | Level | Message | |-------|-------|---------| | Delete initiated | INFO | `Delete anime initiated: key={key} delete_db={x} delete_folder={x}` | | Series not found | WARNING | `Delete anime failed — series not found: key={key}` | | Path traversal attempt | WARNING | `Delete anime blocked — path traversal attempt: key={key} path={path}` | | DB error | ERROR | `Delete anime DB error: key={key} error={message}` | | Folder delete error | ERROR | `Delete anime folder error: key={key} error={message}` | | Delete succeeded | INFO | `Delete anime succeeded: key={key} deleted_db={x} deleted_folder={x}` | ### Frontend Logs (JS/`console`) | Event | Method | |-------|--------| | Modal opened | `console.info('[DeleteModal] Opening for key:', key)` | | Delete confirmed | `console.info('[DeleteModal] Initiating delete:', {...})` | | Delete succeeded | `console.info('[DeleteModal] Delete succeeded:', result)` | | API/network error | `console.error('[DeleteModal] Delete request failed:', err)` | | Series removed from grid | `console.info('[SeriesManager] Removed series from local state:', key)` | | WS event received | `console.info('[SocketHandler] Series deleted:', data)` | --- ## Configuration No new configuration options are required. The feature uses existing paths: - **Anime base directory**: `settings.anime_directory` (for path traversal validation) - **Database path**: `series_app.database_path` (for DB deletion) - **Queue cleanup**: `AnimeSeriesService.delete(series_key)` cascades to queue items --- ## Testing See: - `tests/unit/test_delete_anime_service.py` — unit tests for `AnimeService.delete_series()` - `tests/api/test_delete_anime_endpoint.py` — API endpoint tests including auth, validation, error cases - `tests/frontend/test_delete_modal.py` — frontend modal logic and DOM validation tests - `tests/security/test_delete_anime_security.py` — security tests for path traversal, XSS, auth bypass --- ## Changelog | Date | Change | |------|--------| | 2026-08-16 | Feature added. `DELETE /api/anime/{key}`, right-click context menu, typed-confirmation modal, WebSocket sync. |