# 05 · Blueprints & REST API DigiServer v2 registers **7 active blueprints**. --- ## 1. Blueprint Overview | Blueprint | URL prefix | Module | Focus | |---|---|---|---| | `main_bp` | `/` | `blueprints/main.py` | Dashboard, health | | `auth_bp` | `/` | `blueprints/auth.py` | Login/logout/register/password | | `admin_bp` | `/admin` | `blueprints/admin.py` | Admin panel, HTTPS, build, users, logos | | `players_bp` | `/players` | `blueprints/players.py` | Player management | | `content_bp` | `/content` | `blueprints/content.py` | Media library + modern playlist management | | `playlist_bp` | `/playlist` | `blueprints/playlist.py` | Legacy per-player playlist (redirects) | | `api_bp` | `/api` | `blueprints/api.py` | Player-facing REST + deployment API | --- ## 2. `main` — Dashboard & Health (`/`) | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/` | GET | `dashboard` | Counts (players/playlists/content), storage MB, 20 recent logs (cached 60s except `viewer` role) | | `/health` | GET | `health` | Health JSON: `SELECT 1` DB ping + free disk space | --- ## 3. `auth` — Authentication (`/`) | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/login` | GET/POST | `login` | bcrypt login, `remember`, `next` redirect, failed-attempt logging | | `/logout` | GET | `logout` | Logs out + logs action | | `/register` | GET/POST | `register` | Self-registration → default `viewer` role | | `/change-password` | GET/POST | `change_password` | Change own password (verifies current) | --- ## 4. `admin` — Admin Panel (`/admin`) **Security:** `@login_required` on all routes; `@admin_required` (custom decorator, `role == 'admin'`) on user-management. ### Users | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/` | GET | `admin_panel` | Stats, recent logs, users, storage | | `/user/create` | POST | `create_user` | Create user (username ≥3, password ≥6) | | `/user//role` | POST | `change_user_role` | Change role (not own) | | `/user//delete` | POST | `delete_user` | Delete user (not own) | | `/user//password` | POST | `reset_user_password` | Admin reset password | | `/users` | GET | `user_management` | User list page | | `/theme` | POST | `change_theme` | light/dark theme | ### Media housekeeping | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/leftover-media` | GET | `leftover_media` | Content not assigned to any playlist, by type + size | | `/delete-leftover-images` | POST | — | Bulk-delete leftover images (+ archive + PlayerEdit) | | `/delete-leftover-videos` | POST | — | Bulk-delete leftover videos | | `/delete-single-leftover/` | POST | — | Delete one leftover item | ### System / dependencies | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/system/info` | GET | `system_info` | JSON: platform + psutil (CPU/mem/disk) | | `/dependencies` | GET | `dependencies` | Checks LibreOffice, Poppler, FFmpeg, emoji fonts | | `/install-libreoffice` | POST | — | `sudo -n install_libreoffice.sh` | | `/install-emoji-fonts` | POST | — | `sudo -n install_emoji_fonts.sh` | | `/logo/upload` | POST | `upload_logo` | Upload logo.png | | `/logs/clear` | POST | `clear_logs` | Delete all `ServerLog` rows | | `/customize-logos` | GET | `customize_logos` | Logo customization page | | `/upload-header-logo` | POST | — | Save header_logo.png | | `/upload-login-logo` | POST | — | Save login_logo.png | ### Editing users | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/editing-users` | GET | `manage_editing_users` | List `PlayerUser` + per-user edit counts | | `/editing-users//update` | POST | — | Rename editing user | | `/editing-users//delete` | POST | — | Delete editing user | ### HTTPS (Caddy) | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/https-config` | GET | `https_config` | HTTPS config page (auto-corrects DB flag if request is HTTPS) | | `/https-config/update` | POST | `update_https_config` | Validate + save config → regenerate Caddyfile → reload Caddy | | `/https-config/status` | GET | `https_config_status` | Config as JSON | ### Player build & deployment | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/build-player` | GET | `build_player` | Build page + staged-code status | | `/build-player` | POST | `build_player_action` | `build_files` / `save_config` / `build_and_config` → clone/refresh code, write `app_config.json`, persist `instance/player_build.json` | --- ## 5. `players` — Player Management (`/players`) | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/` and `/list` | GET | `list` | Player list + live deployment-status polling data | | `/add` | GET/POST | `add_player` | Create player; optional **background SSH deployment** | | `/bulk/delete` | POST | — | JSON bulk delete | | `/bulk/assign-playlist` | POST | — | JSON bulk assign to playlist | | `/deployment-status` | GET | — | JSON deployment status for all players (polling) | | `/` | GET | — | Redirect to `manage_player` | | `//edit` | GET/POST | — | Edit name/location (legacy) | | `//delete` | POST | — | Delete player + feedback | | `//regenerate-auth` | POST | — | New auth code | | `//manage` | GET/POST | `manage_player` | Manage page: `update_credentials` / `assign_playlist`; feedback, edits, status | | `//edited-media` | GET | `edited_media` | All edited media + user mapping | | `//edited-media-report` | GET | `edited_media_report` | Tabular report | | `//fullscreen` | GET | — | Player fullscreen (NO auth; optional `?auth=` check), cached playlist | | `//reorder` | POST | — | Legacy stub → 400 "use Playlists page" | | `//playlist/reorder` | POST | — | Legacy per-player reorder (JSON up/down) | | `//playlist/remove` | POST | — | Legacy removal (bumps `playlist_version`) | Helper: `get_player_playlist(player_id)` — `@cache.memoize(300)` builds playlist dicts (`url`, `type`, `duration`, `position`, `muted`, `audio`). --- ## 6. `content` — Media Library & Modern Playlist Management (`/content`) > This is the **primary workflow** in v2 (1,519 lines). ### Library & uploads | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/` | GET | `content_list` | Main page `content_list_new.html`: playlists, last-3 media, counts, players | | `/media-library` | GET | `media_library` | All media by type + edit counts | | `/media//delete` | POST | `delete_media` | Delete file + archive + edits; removes from playlists + bumps versions | | `/upload-media-page` | GET | `upload_media_page` | Upload page (playlist selector) | | `/upload-media` | POST | `upload_media` | Core upload: images optimized, videos validated, **PDF/PPTX → Full-HD PNGs**, large files → background threads | ### Web links | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/add-weblink` | POST | `add_weblink` | Create weblink Content (http/https validated), optional playlist add | | `/playlist//add-weblink` | POST | — | Create weblink + append to playlist | ### Playlist management | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/playlist/create` | POST | — | Create playlist (name, description, orientation) | | `/playlist//delete` | POST | — | Delete + unassign players | | `/playlist//manage` | GET | `manage_playlist_content` | Ordered content + available library (no weblinks) | | `/playlist//add-content` | POST | — | Add library content at next position | | `/playlist//remove-content/` | POST | — | Remove content (deletes orphan weblinks) | | `/playlist//bulk-remove` | POST | — | JSON bulk remove | | `/playlist//reorder` | POST | — | JSON reorder (position = index) | | `/playlist//update-muted/` | POST | — | Toggle muted | | `/playlist//update-edit-enabled/` | POST | — | Toggle `edit_on_player_enabled` | | `/playlist//update-duration/` | POST | — | Per-playlist duration (≥1s) | | `/player//assign-playlist` | POST | — | Assign/unassign player to playlist | **All playlist mutations** call `Playlist.increment_version()` and `cache.clear()`. ### Processing helpers (module-level) `process_image_file`, `process_video_file_extended` (ffprobe), `process_pdf_file` (pdf2image @300 DPI → `_pageNNN.png`), `process_presentation_file` (LibreOffice → PDF → pdftoppm → Full-HD PNG), `create_fullhd_image`, `resize_image_to_fullhd`, `optimize_image_to_fullhd`, `process_file_in_background` (thread wrapper). --- ## 7. `playlist` — Legacy Per-Player Routes (`/playlist`) Redirects/legacy — kept for compatibility: | Route | Methods | Purpose | |---|---|---| | `/` | GET | Redirect to modern manage page | | `//add` | POST | Add content to player's playlist | | `//remove/` | POST | Remove + renumber | | `//reorder` | POST | JSON reorder from `content_ids` | | `//update-duration/` | POST | Update duration | | `//update-muted/` | POST | Update muted | | `//clear` | POST | Clear all items | --- ## 8. `api` — Player-Facing REST + Deployment API (`/api`) **Custom decorators:** `rate_limit(max_requests, window)` (in-memory, IP/Bearer keyed) and `verify_player_auth` (validates Bearer `auth_code` → sets `request.player`). ### Health & auth | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/health` | GET | `health_check` | API health (`version: 2.0.0`) | | `/certificate` | GET | — | TLS certificate test | | `/auth/player` | POST | `authenticate_player` | hostname + password/quickconnect → auth_code, playlist_id, orientation; sets online | | `/auth/verify` | POST | `verify_auth_code` | Verify auth code → player info | ### Playlist delivery | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/playlists` | GET | `get_playlist_by_quickconnect` | Kivy-compatible: `?hostname=&quickconnect_code=` → playlist + version + hashed quickconnect | | `/playlists/` | GET | `get_player_playlist` | Bearer-authed fetch (cached) | | `/playlist-version/` | GET | `get_playlist_version` | Lightweight refresh-poll version check | **Payload keys the players expect** (from `get_cached_playlist`): `file_name`, `type`, `duration`, `position`, `url`, `description`, `edit_on_player_enabled`, `muted`, `audio` (`"on"`/`"off"`). ### Feedback & monitoring | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/player-feedback` | POST | `receive_player_feedback` | Status (playing/paused/error/restarting); infers player; **auto-marks deployment `deployed`** | | `/player-status/` | GET | `get_player_status` | Online (5-min), latest feedback | | `/system-info` | GET | — | Counts: players online/total, content, 24h logs | | `/content` | GET | — | List content with counts | | `/logs` | GET | `get_logs` | Query logs (`limit`/`level`/`since`) | ### Edited media | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/player-edit-media` | POST | `receive_edited_media` | Bearer-authed multipart upload of edited image + metadata; versionizes to `edited_media//`; records `PlayerEdit`; auto-creates `PlayerUser`; repoints `Content.filename`; bumps playlist version; clears cache | ### SSH deployment | Route | Methods | Endpoint | Purpose | |---|---|---|---| | `/deploy/test-ssh` | POST | `test_ssh_connection` | Test SSH (sshpass/ssh) | | `/deploy/player` | POST | `deploy_player` | Full remote deploy (`deploy_player_to_host`); API key = `sha256(name:hostname)[:32]` | **Error handlers:** JSON 404 / 405 / 500. --- ## 9. API Call Flows (Mermaid) ### Player bootstrap (auth → playlist) ```mermaid sequenceDiagram participant P as Player participant A as /api P->>A: POST /api/auth/player {hostname, password|quickconnect} A->>A: Player.authenticate() A-->>P: {auth_code, playlist_id, orientation} P->>A: GET /api/playlists/ (Bearer auth_code) A-->>P: playlist items + version loop refresh poll P->>A: GET /api/playlist-version/ A-->>P: {version} end ``` ### Edited media upload ```mermaid sequenceDiagram participant P as Player participant A as /api participant DB as SQLite P->>A: POST /api/player-edit-media (Bearer, multipart image + metadata) A->>DB: locate Content (filename / edited_media// regex / last PlayerEdit) A->>DB: move original → edited_media//original_* A->>DB: save new version + side-car JSON A->>DB: get_or_create PlayerUser (user_code) A->>DB: create PlayerEdit A->>DB: repoint Content.filename → latest edit (preserve original_filename) A->>DB: Playlist.increment_version() A-->>P: 200 OK ``` ### Deployment ```mermaid sequenceDiagram participant Admin as Admin UI participant S as Server (app) participant T as Background task participant H as Player host Admin->>S: add player / deploy S->>T: background_player_deployment() T->>H: sshpass test_ssh_connection() T->>H: rsync staged code (or git clone/pull) T->>H: write config/app_config.json T->>H: temp passwordless sudo → install.sh → start.sh T->>H: cleanup sudoers T-->>S: update player.deployment_status = deployed|failed Admin->>S: GET /players/deployment-status (poll) ``` --- > Next: [06 · Utils & Services](06-utils-services.md)