{"id":31823505,"url":"https://github.com/githubixx/vdradmin-go","last_synced_at":"2026-04-02T16:34:48.233Z","repository":{"id":318552605,"uuid":"1071751161","full_name":"githubixx/vdradmin-go","owner":"githubixx","description":null,"archived":false,"fork":false,"pushed_at":"2025-10-07T20:02:51.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-07T22:08:55.585Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/githubixx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":null,"dco":null,"cla":null}},"created_at":"2025-10-07T19:03:24.000Z","updated_at":"2025-10-07T20:02:52.000Z","dependencies_parsed_at":"2025-10-07T22:09:13.557Z","dependency_job_id":"9d9dd69b-c35d-475f-84b7-62634abdecfe","html_url":"https://github.com/githubixx/vdradmin-go","commit_stats":null,"previous_names":["githubixx/vdradmin-go"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/githubixx/vdradmin-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githubixx%2Fvdradmin-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githubixx%2Fvdradmin-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githubixx%2Fvdradmin-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githubixx%2Fvdradmin-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/githubixx","download_url":"https://codeload.github.com/githubixx/vdradmin-go/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githubixx%2Fvdradmin-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279007465,"owners_count":26084313,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"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":"2025-10-11T14:51:30.540Z","updated_at":"2026-04-02T16:34:48.215Z","avatar_url":"https://github.com/githubixx.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vdradmin-go\n\n`vdradmin-go` is a web frontend for [VDR](https://tvdr.de/) written in Go using hexagonal architecture, clean code practices, and modern web technologies. It's inspired by [vdradmin-am](http://andreas.vdr-developer.org/vdradmin-am/index.html) but most probably wont implement every feature of `vdradmin-am`. This is really a very opinionated implementation of a VDR web frontend that mainly satisfies my needs ;-)\n\n## Note\n\nI'm using `vdradmin-go` on a daily basis and it has a comprehensive test suite (250+ tests with race detection, fuzz testing, and integration tests) to ensure quality. Still, the code isn't that mature. Expect bugs. Some features have beta quality, some only alpha quality and some elements might not work at all. So be careful especially with features that change something on the file system like deleting recordings and things like that!\n\nThis code was mainly generated with Claude Code (initial conversion of `vdradmin-am` Perl code to Go) and GPT-5.2 (everything else). Originally, I just wanted to see how far I can get converting the Perl code base of `vdradmin-am` (~7500 lines of code) to Go and more recent technologies. But it turned out it worked very well and got a useable application at the end.\n\n## Screenshots\n\n### Channels\n\n![Alt text](screenshots/vdradmin-go_channels_01.png)\n\n### Watch TV (Screenshot)\n\n![Alt text](screenshots/vdradmin-go_watch_tv_01.png)\n\n## Gallery\n\nSee [GALLERY.md](GALLERY.md) for theme screenshots.\n\n## Goals\n\n- **Modern Architecture**: Hexagonal (ports \u0026 adapters) architecture for maintainability\n- **Clean Code**: Following Go best practices and SOLID principles\n- **Modern UI**: [htmx](https://htmx.org/) for dynamic interactions, modern CSS, minimal JavaScript\n- **Customizable**: Modular theme system with built-in light/dark themes and support for custom themes\n- **Type Safety**: Strong typing with comprehensive error handling\n- **Performance**: Concurrent operations, efficient caching\n- **Security**: Secure authentication, input validation, HTTPS support\n- **Observability**: Structured logging, metrics, tracing ready\n\n## Architecture\n\nvdradmin-go follows **Hexagonal Architecture** (Ports \u0026 Adapters): the domain and application logic stay independent of transport (HTTP) and integration details (SVDRP). This keeps use cases easy to test and adapters easy to swap.\n\n- **Domain** (`internal/domain`): domain models + domain-specific rules/errors.\n- **Application** (`internal/application`): use-cases/services that orchestrate domain logic and call ports.\n- **Ports** (`internal/ports`): interfaces the application depends on (e.g. VDR client).\n- **Adapters** (`internal/adapters`): concrete implementations of ports.\n  - **Primary adapters** (`internal/adapters/primary/http`): HTTP server, handlers, middleware.\n  - **Secondary adapters** (`internal/adapters/secondary/svdrp`): SVDRP client integration to talk to VDR.\n- **Infrastructure** (`internal/infrastructure`): cross-cutting concerns like configuration.\n- **Web UI assets** (`web/templates`, `web/static`): server-rendered templates + htmx + CSS/JS.\n\nTypical request flow:\n\n```plain\nHTTP request → handler (primary adapter) → application service → port interface → secondary adapter (SVDRP) → VDR\n```\n\nFor a detailed overview (including more diagrams and examples), see `docs/ARCHITECTURE.md`.\n\n```plain\nvdradmin-go/\n├── cmd/\n│   └── vdradmin/                # Application entry point (main)\n├── internal/\n│   ├── domain/                  # Domain models + domain errors\n│   ├── ports/                   # Port interfaces (e.g. VDR client)\n│   ├── application/             # Use cases / orchestration\n│   │   ├── services/            # EPG, timers, recordings, autotimers\n│   │   └── archive/             # Recording archive jobs\n│   ├── adapters/                # Adapter implementations\n│   │   ├── primary/http/        # HTTP server, handlers, middleware, HLS proxy\n│   │   └── secondary/svdrp/     # SVDRP integration to talk to VDR\n│   ├── infrastructure/\n│   │   ├── config/              # Config loading + validation\n│   │   └── theme/               # Theme discovery and management\n│   └── integration/             # Container-based integration tests\n├── web/\n│   ├── templates/               # HTML templates\n│   ├── themes/                  # Theme CSS and metadata (light, dark, custom)\n│   └── static/                  # Frontend assets\n│       ├── css/\n│       └── js/\n├── configs/                     # Example configuration(s)\n├── deployments/                 # Docker + systemd\n├── test/                        # Integration test assets (e.g. svdrpstub)\n├── scripts/                     # Helper scripts\n├── docs/                        # Documentation (see docs/ARCHITECTURE.md)\n├── screenshots/                 # UI screenshots\n├── build/                       # Local build output (make build)\n├── dist/                        # GoReleaser output (local)\n├── go.mod\n├── go.sum\n├── Makefile\n└── README.md\n```\n\n## Technology Stack\n\n- **Language**: Go 1.25+\n- **Web**: Go 1.22+ internal router\n- **Templates**: html/template\n- **Config**: YAML with validation\n- **Logging**: slog (stdlib)\n- **Frontend**: htmx + modern CSS\n- **Themes**: Modular CSS theme system with automatic discovery\n\n## Requirements\n\n### Core requirements (always)\n\n- A running **VDR** instance you can reach from where `vdradmin-go` runs.\n- **SVDRP access** to that VDR (host/port and credentials depending on your setup).\n- Basic network connectivity between `vdradmin-go` and VDR.\n- Some features require that `vdradmin-go` runs on the same host as VDR.\n- `config.yaml` needs to be writeable by the user `vdradmin-go` process uses (e.g. `vdr`) if you make changes via the UI.\n\n### Build from source (\"make run\" / \"make build\")\n\n- **Go 1.25+** (see `go.mod`). If your distro packages an older Go version, install Go from [go.dev/dl](https://go.dev/dl/).\n- `make` and `git`.\n\nArch Linux:\n\n```bash\nsudo pacman -Syu\nsudo pacman -S --needed go make git\n```\n\n### Using the released binary\n\n- No build toolchain needed.\n\n### Docker / docker-compose\n\n- Docker Engine.\n- For `docker compose`, install the Docker Compose plugin.\n- Ensure the container can reach your VDR (networking/hostnames/firewalls).\n\nArch Linux:\n\n```bash\nsudo pacman -Syu\nsudo pacman -S --needed docker docker-compose\n```\n\n### systemd service\n\n- A Linux system with `systemd`.\n- A suitable place for the binary and config file, and correct permissions for the service user.\n\n### Feature-specific requirements (optional)\n\n- **Archive recordings** (`/recordings` → Archive): requires `ffmpeg` on the `vdradmin-go` host; optional `ffprobe` for percentage progress.\n- **Watch TV snapshots** (`/watch`): requires a VDR setup where the SVDRP `GRAB` command works (often not available on headless/recording-only setups).\n- **Watch TV streaming** (HLS proxy mode): requires a stream source (commonly `vdr-plugin-streamdev-server`) and `ffmpeg` on the `vdradmin-go` host.\n- **EPGSearch-related pages/features**: typically require the VDR `epgsearch` plugin (package names vary by distro).\n\nArch Linux:\n\n```bash\nsudo pacman -S --needed ffmpeg\nyay -S vdr-streamdev-server vdr-epgsearch\n```\n\n## Quick Start\n\n```bash\n# Build\nmake run\n```\n\nOpen URL `http://127.0.0.1:8080` in your browser. Use default username `admin` and password `admin`. Some actions in the UI will cause creating a `config.yaml` in the current directory to store the state.\n\n## Usage / Deployment Options\n\n`vdradmin-go` can be run in multiple ways depending on your setup.\n\nReleases are built via GitHub Actions (GoReleaser) when a semantic version tag is pushed (e.g. `0.1.0` or `0.1.0-rc.1`).\n\n### 1) Use the released binary\n\n- Download the `linux_amd64` archive from the latest [GitHub Release](https://github.com/githubixx/vdradmin-go/releases). E.g.:\n\n```bash\nexport VDRADMIN_GO_VERSION=\"0.3.0\"\n\nwget https://github.com/githubixx/vdradmin-go/releases/download/${VDRADMIN_GO_VERSION}/vdradmin-go_${VDRADMIN_GO_VERSION}_linux_amd64.tar.gz\n```\n\n- Extract it\n\n```bash\ntar xvfz vdradmin-go_${VDRADMIN_GO_VERSION}_linux_amd64.tar.gz\n```\n\n- Run it with default settings:\n\n```bash\n./vdradmin\n```\n\n- Or with a configuration file\n\n```bash\n./vdradmin --config /path/to/config.yaml\n```\n\n### 2) Use the Docker container (GHCR)\n\n- Check for latest version in [Github Container Registry (GHCR)](https://github.com/githubixx/vdradmin-go/releases)\n\n- Pull the image from GitHub Container Registry:\n\n```bash\nexport VDRADMIN_GO_VERSION=\"0.3.0\"\n\ndocker pull ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}\n```\n\n- Run the container:\n\n```bash\ndocker run \\\n  --rm -p 8080:8080 \\\n  -v \"${PWD}/config.yaml:/app/config.yaml\" \\\n  ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}\n```\n\n- Run the container (also mount your VDR recordings directory):\n\nMake sure `vdr.video_dir` in your `config.yaml` matches the container path (example below uses `/var/lib/video.00`).\n\n```bash\ndocker run \\\n  --rm -p 8080:8080 \\\n  -v \"${PWD}/config.yaml:/app/config.yaml\" \\\n  -v \"/var/lib/video.00:/var/lib/video.00\" \\\n  ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}\n```\n\n### 3) Run as a systemd service\n\nIf you want vdradmin-go to start automatically on boot:\n\n1. Copy the example unit file from `deployments/systemd/vdradmin.service` to your systemd directory.\n2. Install the `vdradmin` binary somewhere like `/usr/local/bin/vdradmin`.\n3. Ensure the unit points to your config path.\n4. Enable and start:\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable --now vdradmin\n```\n\n### 4) Use docker-compose\n\nThere is an example compose file at `deployments/docker-compose.yml`.\nTypically you will:\n\n- set the image to `ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}` (check for latest version in [Github Container Registry (GHCR)](https://github.com/githubixx/vdradmin-go/releases))\n- mount your `config.yaml` into the container\n\nThen run:\n\n```bash\ndocker compose -f deployments/docker-compose.yml up -d\n```\n\n## Configuration\n\nSee `configs/config.example.yaml` for full configuration options.\n\n## Themes\n\nvdradmin-go includes a modular theme system for easy customization:\n\n### Built-in themes\n\n- **System** (default): Automatically follows OS/browser dark/light mode preference\n- **Light**: Always use light theme\n- **Dark**: Always use dark theme\n- **Curated bundled themes**: `cartoon-1`, `fitness`, `glas-1`, `gold-1`, `golden-moon`, `lighting-1`, `luxury-1`, `mantle-1`, `metal-1`, `retro-arcade`, `solar-system-1`, `space-night-1`, `spaceship-2`\n- **Spaceship variants**: `spaceship-blue-(dark|light)`, `spaceship-cyan-(dark|light)`, `spaceship-green-(dark|light)`, `spaceship-grey-(dark|light)`, `spaceship-magenta-(dark|light)`, `spaceship-mono-(dark|light)`, `spaceship-orange-(dark|light)`, `spaceship-purple-(dark|light)`, `spaceship-red-(dark|light)`, `spaceship-yellow-(dark|light)`\n\nThemes are discovered from subdirectories under `web/themes/` that contain a `theme.yaml` file. The Configurations page shows `System (auto)` plus all discovered themes, using the display name from `theme.yaml` when available.\n\n### Custom themes\n\nCreate custom themes by copying an existing theme directory:\n\n```bash\n# Copy built-in theme as starting point\ncp -r web/themes/light web/themes/mytheme\n\n# Edit colors in theme.css (40 CSS variables)\nnano web/themes/mytheme/theme.css\n\n# Update metadata\nnano web/themes/mytheme/theme.yaml\n```\n\nCustom themes are discovered automatically at startup and appear in **Configurations** → **Theme** after restart.\nIf you only edit an existing theme's `theme.css`, a browser reload is usually enough because theme CSS is served with `Cache-Control: no-cache`.\nIf you add a new theme directory, rename a theme, or change `theme.yaml`, restart vdradmin-go so the available theme list is rebuilt.\n\nFor complete documentation on creating custom themes, including:\n\n- CSS variable reference\n- Color customization examples\n- Troubleshooting guide\n\nSee `docs/THEMES.md`.\n\n## Archive recordings\n\nThe **Recordings** page (`/recordings`) includes an **Archive** action (admin-only) that remuxes a VDR recording directory (multiple `*.ts` segments) into a single `video.(mkv|mp4)` inside `archive.base_dir`.\n\nRequirements:\n\n- `ffmpeg` installed on the `vdradmin-go` host\n- Optional (for percentage progress): `ffprobe`\n\nConfiguration:\n\n- `archive.base_dir`: absolute output directory root\n- `archive.profiles`: optional list of destination profiles (movie/series) so you can add more archive directories or customize defaults\n- `archive.ffmpeg_args`: additional ffmpeg output args (defaults are hardware-accel friendly for AMD GPUs but can be changed)\n\nSafety defaults:\n\n- keeps originals\n- runs in background\n- refuses to overwrite an existing output file\n\n## Watch TV\n\nThe **Watch TV** page (`/watch`) provides:\n\n- a periodically refreshed **snapshot** (configurable interval + size)\n- a **remote control** (SVDRP `HITK`)\n- a **channel list** restricted to channels configured in **Configurations** (wanted channels)\n\n### Snapshot requirements\n\nThe snapshot feature uses the SVDRP `GRAB` command.\n\n- If your VDR does not support `GRAB` (or cannot grab a picture on the VDR host), the page will still load but snapshots will fail. This is common for headless/recording-only VDR instances that have tuners but no primary video output/decoder device.\n\nFor troubleshooting and background, see `docs/WATCHTV.md`.\n\n### Optional: stream URL mode (headless setups)\n\nIf you run VDR headless (recording-only) and `GRAB` cannot work, you can enable streaming for `/watch`.\n\n#### Recommended: HLS proxy (built-in transcoding)\n\nConfigure `vdr.streamdev_backend_url` in Configurations:\n\n- Example: `http://127.0.0.1:3000/{channel}`\n- Requires: `ffmpeg` installed on the `vdradmin-go` host\n- Requires: Suitable plugin like `vdr-plugin-streamdev-server`\n- `vdradmin-go` will transcode streamdev MPEG-TS to browser-playable HLS automatically\n- `/watch` uses internal proxy endpoint `/watch/stream/{channel}/index.m3u8`\n\n#### Alternative: Direct external stream URL\n\n- Configure `vdr.stream_url_template` if you have a pre-existing HLS/MJPEG/WebM endpoint\n- The template may contain `{channel}`, which will be replaced with the selected VDR channel **number** (e.g. `1`, `2`, `3`).\n- `/watch` embeds the URL into an HTML5 `\u003cvideo\u003e` element. The URL must point to a format your browser can play (for example HLS `.m3u8` or a browser-supported MP4/WebM stream).\n\nIf you use `vdr-plugin-streamdev-server`, its HTTP server commonly runs on port `3000` and can serve channels by number, e.g. `http://127.0.0.1:3000/1`.\n\nNote: streamdev’s default outputs are typically TS/PES/ES, which most browsers do not play directly; you may need an external remux/transcode step to get true in-browser playback.\n\n## Testing\n\nvdradmin-go has a comprehensive test suite covering multiple testing strategies:\n\n### Running Tests\n\n```bash\n# Run all tests (unit + race detection)\nmake test\n\n# Run tests with coverage report\nmake test-coverage\n\n# Run integration tests (requires Docker)\ngo test -tags=integration ./internal/integration -v\n\n# Run fuzz tests (optional, time-intensive)\ngo test -fuzz=FuzzParseSVDRPResponse -fuzztime=5m ./internal/adapters/secondary/svdrp/\n```\n\n### Test Coverage\n\n- **250+ tests** across 7 packages\n- **Unit tests**: Domain models, services, handlers, SVDRP protocol\n- **Integration tests**: Real SVDRP protocol communication with testcontainers\n- **Property-based tests**: Domain invariants using `testing/quick`\n- **Fuzz tests**: SVDRP protocol parsing with Go 1.18+ fuzzing\n- **Race detection**: Concurrent access validation (automatically enabled by `make test`)\n- **Port contract tests**: VDRClient interface compliance validation\n\n### Test Architecture\n\nThe test suite follows the same hexagonal architecture as the application:\n\n- **Domain tests** (`internal/domain/*_test.go`): Model validation, business rules, property-based testing\n- **Port contract tests** (`internal/ports/vdr_contract_test.go`): Reusable test suite ensuring all VDRClient implementations behave consistently\n- **Service tests** (`internal/application/services/*_test.go`): Use case orchestration with canonical mocks\n- **Adapter tests** (`internal/adapters/**/*_test.go`): HTTP handlers, SVDRP protocol parsing, fuzz testing\n- **Integration tests** (`internal/integration/svdrp_integration_test.go`): Full protocol validation with Docker containers\n- **Race tests** (`internal/application/services/race_test.go`): Concurrency safety validation\n\n### Integration Tests (Docker)\n\nIntegration tests use testcontainers-go to spin up a real SVDRP stub server and verify:\n\n- Channel list retrieval and parsing\n- Timer CRUD operations (Create, Read, Update, Delete)\n- Connection resilience and automatic reconnection\n- Timeout handling and context cancellation\n- Concurrent operations safety\n- Timer collision detection\n- Channel ID format parsing\n\n```bash\n# Run all integration tests (requires Docker)\ngo test -tags=integration ./internal/integration -v\n\n# Run specific integration test\ngo test -tags=integration ./internal/integration -run TestSVDRP_RealProtocol_ChannelListRetrieval -v\n```\n\n### Coverage Metrics\n\nRecent coverage (via `make test-coverage`):\n\n- HTTP handlers: ~36%\n- SVDRP adapter: ~57%\n- Archive service: ~31%\n- Application services: ~47%\n- Port interfaces: ~79%\n- Config infrastructure: ~32%\n\nNote: Coverage percentages reflect tested code paths. Low percentages often indicate error handling paths that require specific VDR states to trigger.\n\n## License\n\nLGPL v2.1 (same as original [vdradmin-am](http://andreas.vdr-developer.org/vdradmin-am/index.html))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithubixx%2Fvdradmin-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgithubixx%2Fvdradmin-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithubixx%2Fvdradmin-go/lists"}