{"id":47156642,"url":"https://github.com/moderras/screenpilot","last_synced_at":"2026-05-16T04:14:14.342Z","repository":{"id":343370027,"uuid":"1177420143","full_name":"ModerRAS/ScreenPilot","owner":"ModerRAS","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-14T00:24:34.000Z","size":300,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-14T02:29:23.704Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ModerRAS.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-10T02:22:58.000Z","updated_at":"2026-03-17T06:52:59.000Z","dependencies_parsed_at":"2026-03-15T04:05:44.539Z","dependency_job_id":null,"html_url":"https://github.com/ModerRAS/ScreenPilot","commit_stats":null,"previous_names":["moderras/screenpilot"],"tags_count":53,"template":false,"template_full_name":null,"purl":"pkg:github/ModerRAS/ScreenPilot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ModerRAS%2FScreenPilot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ModerRAS%2FScreenPilot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ModerRAS%2FScreenPilot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ModerRAS%2FScreenPilot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ModerRAS","download_url":"https://codeload.github.com/ModerRAS/ScreenPilot/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ModerRAS%2FScreenPilot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32162614,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-22T17:06:48.269Z","status":"online","status_checked_at":"2026-04-23T02:00:06.710Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-13T02:03:02.956Z","updated_at":"2026-04-23T02:04:40.654Z","avatar_url":"https://github.com/ModerRAS.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ScreenPilot\n\n[中文说明](./readme_cn.md)\n\nA LAN digital signage controller built with **Rust + Axum** (backend) and **Vue 3 + ElementUI** (frontend), using **DLNA / UPnP AV** to control multiple screens.\n\n## Features\n\n- **SSDP Discovery** — automatically finds DLNA MediaRenderer devices on your LAN (manual trigger)\n- **DLNA Control** — sends UPnP AVTransport SOAP commands (SetAVTransportURI, Play, Pause, Stop) to each renderer\n- **Media HTTP Server** — built-in Axum HTTP server that serves media files from a local `media/` directory (port 8090)\n- **Per-Screen Control** — each device is independently controllable from the UI\n- **Scenes** — group device→media assignments and apply them with one click\n\n## Architecture\n\n```\nScreenPilot/\n├── Cargo.toml                  # Workspace root\n├── package.json                # Root package (for pnpm workspace)\n├── backend/                   # Rust + Axum API server\n│   ├── Cargo.toml\n│   └── src/\n│       ├── main.rs             # Axum routes + app entry\n│       ├── discovery.rs       # SSDP discovery\n│       ├── dlna.rs            # UPnP AVTransport SOAP commands\n│       ├── media_server.rs    # Axum media file server\n│       └── state.rs           # RendererDevice, Scene, AppState\n├── frontend/                  # Vue 3 + ElementUI + Vite + pnpm\n│   ├── package.json\n│   ├── vite.config.ts\n│   └── src/\n│       ├── main.ts            # Vue entry point\n│       ├── App.vue            # Root component\n│       ├── api/               # API client (axios)\n│       ├── views/             # DevicesView, ScenesView\n│       ├── stores/            # Pinia state\n│       └── types/             # TypeScript types\n└── media/                    # Drop your .mp4 / .webm files here\n```\n\n## Prerequisites\n\n- **Rust** — Install via [rustup](https://rustup.rs/)\n- **Node.js** — v20+\n- **pnpm** — Install via `npm install -g pnpm`\n\n## Development\n\n```bash\n# Terminal 1: Build frontend first\ncd frontend \u0026\u0026 pnpm build\n\n# Terminal 2: Start backend (serves both API and frontend on port 8080)\ncd backend \u0026\u0026 cargo run\n```\n\nThen open http://localhost:8080/web in your browser.\n\n## Build\n\n```bash\n# Build frontend\ncd frontend \u0026\u0026 pnpm build\n\n# Build backend\ncd backend \u0026\u0026 cargo build --release\n\n# Or use pnpm workspace to build both\npnpm --filter frontend build\npnpm --filter backend build\n```\n\n## Production\n\n```bash\n# Run backend\n./backend/target/release/screen-pilot-backend\n\n# Serve frontend dist (e.g., with nginx, or use Vite preview)\ncd frontend \u0026\u0026 pnpm preview\n```\n\n## Usage\n\n1. Drop media files (`.mp4`, `.webm`, etc.) into the `media/` directory.\n2. Start the backend: `cargo run --manifest-path backend/Cargo.toml`\n3. Start the frontend: `pnpm --dir frontend dev`\n4. Open http://localhost:5173\n5. Click **Discover Devices** to scan the LAN.\n6. Select a media file and click **▶ Play** for any discovered renderer.\n7. Use **Scenes** to define and apply multi-screen layouts at once.\n\n## API Endpoints\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| GET | `/api/devices` | List all discovered devices |\n| POST | `/api/devices/discover` | Trigger SSDP discovery |\n| POST | `/api/devices/:uuid/play` | Play media on device |\n| POST | `/api/devices/:uuid/pause` | Pause playback |\n| POST | `/api/devices/:uuid/stop` | Stop playback |\n| GET | `/api/media` | List media files |\n| GET | `/api/scenes` | List saved scenes |\n| POST | `/api/scenes` | Save a scene |\n| DELETE | `/api/scenes/:name` | Delete a scene |\n| POST | `/api/scenes/:name/apply` | Apply scene to devices |\n| GET | `/api/config/media-server-url` | Get media server URL |\n| GET | `/api/config/encoder` | Get encoder preference |\n| PUT | `/api/config/encoder` | Set encoder preference |\n\n## Hardware Encoding\n\nScreenPilot supports hardware-accelerated video encoding for streaming. The system automatically detects available hardware encoders or allows manual selection.\n\n### Supported Encoders\n\n| Encoder | Description | Platform |\n|---------|-------------|----------|\n| `auto` | Auto-detect best available hardware encoder | All |\n| `nvidia` | NVIDIA NVENC | Windows/Linux |\n| `amd` | AMD VCE (AMF) | Windows |\n| `intel` | Intel Quick Sync Video (QSV) | Windows/Linux |\n| `apple` | Apple VideoToolbox | macOS |\n| `software` | Software encoding (libx264) | All |\n\n### Encoder API\n\n```bash\n# Get current encoder preference\nGET /api/config/encoder\n# Response: \"auto\" | \"nvidia\" | \"amd\" | \"intel\" | \"apple\" | \"software\"\n\n# Set encoder preference\nPUT /api/config/encoder\n# Body: { \"encoder\": \"amd\" }\n```\n\n### How It Works\n\n1. **Detection Order**: When set to `auto`, the system checks for encoders in this order: AMD → NVIDIA → Intel → Apple → VAAPI → Software\n2. **Fallback**: If the selected hardware encoder fails to initialize, the system automatically falls back to software encoding (libx264)\n3. **Streaming**: Video is encoded with CQP 18 (high quality) and audio with AAC 256k, output as MPEG-TS for DLNA compatibility\n4. **Infinite Loop**: Media files loop infinitely using ffmpeg's `-stream_loop -1` option\n\n### Troubleshooting\n\nIf hardware encoding doesn't work:\n- Select `software` to use CPU encoding instead\n- Check ffmpeg logs for error messages (e.g., \"Cannot load nvcuda.dll\" for NVIDIA)\n- Ensure appropriate GPU drivers are installed\n\n## Rust Modules (Backend)\n\n| Module | Responsibility |\n|--------|----------------|\n| `discovery` | SSDP M-SEARCH, device description XML parsing |\n| `dlna` | SOAP envelope building, AVTransport HTTP calls |\n| `media_server` | Axum static file server, `local_ip()` helper |\n| `state` | `RendererDevice`, `Scene`, `AppState`, `PlaybackStatus` |\n\n## Example SOAP — Play\n\n```xml\nPOST /upnp/control/AVTransport HTTP/1.1\nContent-Type: text/xml; charset=\"utf-8\"\nSOAPAction: \"urn:schemas-upnp-org:service:AVTransport:1#Play\"\n\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cs:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n            s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\u003e\n  \u003cs:Body\u003e\n    \u003cu:Play xmlns:u=\"urn:schemas-upnp-org:service:AVTransport:1\"\u003e\n      \u003cInstanceID\u003e0\u003c/InstanceID\u003e\n      \u003cSpeed\u003e1\u003c/Speed\u003e\n    \u003c/u:Play\u003e\n  \u003c/s:Body\u003e\n\u003c/s:Envelope\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoderras%2Fscreenpilot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoderras%2Fscreenpilot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoderras%2Fscreenpilot/lists"}