https://github.com/githubixx/vdradmin-go
https://github.com/githubixx/vdradmin-go
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/githubixx/vdradmin-go
- Owner: githubixx
- Created: 2025-10-07T19:03:24.000Z (6 months ago)
- Default Branch: master
- Last Pushed: 2025-10-07T20:02:51.000Z (6 months ago)
- Last Synced: 2025-10-07T22:08:55.585Z (6 months ago)
- Language: Go
- Size: 85 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# vdradmin-go
`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 ;-)
## Note
I'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!
This 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.
## Screenshots
### Channels

### Watch TV (Screenshot)

## Gallery
See [GALLERY.md](GALLERY.md) for theme screenshots.
## Goals
- **Modern Architecture**: Hexagonal (ports & adapters) architecture for maintainability
- **Clean Code**: Following Go best practices and SOLID principles
- **Modern UI**: [htmx](https://htmx.org/) for dynamic interactions, modern CSS, minimal JavaScript
- **Customizable**: Modular theme system with built-in light/dark themes and support for custom themes
- **Type Safety**: Strong typing with comprehensive error handling
- **Performance**: Concurrent operations, efficient caching
- **Security**: Secure authentication, input validation, HTTPS support
- **Observability**: Structured logging, metrics, tracing ready
## Architecture
vdradmin-go follows **Hexagonal Architecture** (Ports & 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.
- **Domain** (`internal/domain`): domain models + domain-specific rules/errors.
- **Application** (`internal/application`): use-cases/services that orchestrate domain logic and call ports.
- **Ports** (`internal/ports`): interfaces the application depends on (e.g. VDR client).
- **Adapters** (`internal/adapters`): concrete implementations of ports.
- **Primary adapters** (`internal/adapters/primary/http`): HTTP server, handlers, middleware.
- **Secondary adapters** (`internal/adapters/secondary/svdrp`): SVDRP client integration to talk to VDR.
- **Infrastructure** (`internal/infrastructure`): cross-cutting concerns like configuration.
- **Web UI assets** (`web/templates`, `web/static`): server-rendered templates + htmx + CSS/JS.
Typical request flow:
```plain
HTTP request → handler (primary adapter) → application service → port interface → secondary adapter (SVDRP) → VDR
```
For a detailed overview (including more diagrams and examples), see `docs/ARCHITECTURE.md`.
```plain
vdradmin-go/
├── cmd/
│ └── vdradmin/ # Application entry point (main)
├── internal/
│ ├── domain/ # Domain models + domain errors
│ ├── ports/ # Port interfaces (e.g. VDR client)
│ ├── application/ # Use cases / orchestration
│ │ ├── services/ # EPG, timers, recordings, autotimers
│ │ └── archive/ # Recording archive jobs
│ ├── adapters/ # Adapter implementations
│ │ ├── primary/http/ # HTTP server, handlers, middleware, HLS proxy
│ │ └── secondary/svdrp/ # SVDRP integration to talk to VDR
│ ├── infrastructure/
│ │ ├── config/ # Config loading + validation
│ │ └── theme/ # Theme discovery and management
│ └── integration/ # Container-based integration tests
├── web/
│ ├── templates/ # HTML templates
│ ├── themes/ # Theme CSS and metadata (light, dark, custom)
│ └── static/ # Frontend assets
│ ├── css/
│ └── js/
├── configs/ # Example configuration(s)
├── deployments/ # Docker + systemd
├── test/ # Integration test assets (e.g. svdrpstub)
├── scripts/ # Helper scripts
├── docs/ # Documentation (see docs/ARCHITECTURE.md)
├── screenshots/ # UI screenshots
├── build/ # Local build output (make build)
├── dist/ # GoReleaser output (local)
├── go.mod
├── go.sum
├── Makefile
└── README.md
```
## Technology Stack
- **Language**: Go 1.25+
- **Web**: Go 1.22+ internal router
- **Templates**: html/template
- **Config**: YAML with validation
- **Logging**: slog (stdlib)
- **Frontend**: htmx + modern CSS
- **Themes**: Modular CSS theme system with automatic discovery
## Requirements
### Core requirements (always)
- A running **VDR** instance you can reach from where `vdradmin-go` runs.
- **SVDRP access** to that VDR (host/port and credentials depending on your setup).
- Basic network connectivity between `vdradmin-go` and VDR.
- Some features require that `vdradmin-go` runs on the same host as VDR.
- `config.yaml` needs to be writeable by the user `vdradmin-go` process uses (e.g. `vdr`) if you make changes via the UI.
### Build from source ("make run" / "make build")
- **Go 1.25+** (see `go.mod`). If your distro packages an older Go version, install Go from [go.dev/dl](https://go.dev/dl/).
- `make` and `git`.
Arch Linux:
```bash
sudo pacman -Syu
sudo pacman -S --needed go make git
```
### Using the released binary
- No build toolchain needed.
### Docker / docker-compose
- Docker Engine.
- For `docker compose`, install the Docker Compose plugin.
- Ensure the container can reach your VDR (networking/hostnames/firewalls).
Arch Linux:
```bash
sudo pacman -Syu
sudo pacman -S --needed docker docker-compose
```
### systemd service
- A Linux system with `systemd`.
- A suitable place for the binary and config file, and correct permissions for the service user.
### Feature-specific requirements (optional)
- **Archive recordings** (`/recordings` → Archive): requires `ffmpeg` on the `vdradmin-go` host; optional `ffprobe` for percentage progress.
- **Watch TV snapshots** (`/watch`): requires a VDR setup where the SVDRP `GRAB` command works (often not available on headless/recording-only setups).
- **Watch TV streaming** (HLS proxy mode): requires a stream source (commonly `vdr-plugin-streamdev-server`) and `ffmpeg` on the `vdradmin-go` host.
- **EPGSearch-related pages/features**: typically require the VDR `epgsearch` plugin (package names vary by distro).
Arch Linux:
```bash
sudo pacman -S --needed ffmpeg
yay -S vdr-streamdev-server vdr-epgsearch
```
## Quick Start
```bash
# Build
make run
```
Open 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.
## Usage / Deployment Options
`vdradmin-go` can be run in multiple ways depending on your setup.
Releases are built via GitHub Actions (GoReleaser) when a semantic version tag is pushed (e.g. `0.1.0` or `0.1.0-rc.1`).
### 1) Use the released binary
- Download the `linux_amd64` archive from the latest [GitHub Release](https://github.com/githubixx/vdradmin-go/releases). E.g.:
```bash
export VDRADMIN_GO_VERSION="0.3.0"
wget https://github.com/githubixx/vdradmin-go/releases/download/${VDRADMIN_GO_VERSION}/vdradmin-go_${VDRADMIN_GO_VERSION}_linux_amd64.tar.gz
```
- Extract it
```bash
tar xvfz vdradmin-go_${VDRADMIN_GO_VERSION}_linux_amd64.tar.gz
```
- Run it with default settings:
```bash
./vdradmin
```
- Or with a configuration file
```bash
./vdradmin --config /path/to/config.yaml
```
### 2) Use the Docker container (GHCR)
- Check for latest version in [Github Container Registry (GHCR)](https://github.com/githubixx/vdradmin-go/releases)
- Pull the image from GitHub Container Registry:
```bash
export VDRADMIN_GO_VERSION="0.3.0"
docker pull ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}
```
- Run the container:
```bash
docker run \
--rm -p 8080:8080 \
-v "${PWD}/config.yaml:/app/config.yaml" \
ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}
```
- Run the container (also mount your VDR recordings directory):
Make sure `vdr.video_dir` in your `config.yaml` matches the container path (example below uses `/var/lib/video.00`).
```bash
docker run \
--rm -p 8080:8080 \
-v "${PWD}/config.yaml:/app/config.yaml" \
-v "/var/lib/video.00:/var/lib/video.00" \
ghcr.io/githubixx/vdradmin-go:${VDRADMIN_GO_VERSION}
```
### 3) Run as a systemd service
If you want vdradmin-go to start automatically on boot:
1. Copy the example unit file from `deployments/systemd/vdradmin.service` to your systemd directory.
2. Install the `vdradmin` binary somewhere like `/usr/local/bin/vdradmin`.
3. Ensure the unit points to your config path.
4. Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now vdradmin
```
### 4) Use docker-compose
There is an example compose file at `deployments/docker-compose.yml`.
Typically you will:
- 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))
- mount your `config.yaml` into the container
Then run:
```bash
docker compose -f deployments/docker-compose.yml up -d
```
## Configuration
See `configs/config.example.yaml` for full configuration options.
## Themes
vdradmin-go includes a modular theme system for easy customization:
### Built-in themes
- **System** (default): Automatically follows OS/browser dark/light mode preference
- **Light**: Always use light theme
- **Dark**: Always use dark theme
- **Spaceship (orange)**: Futuristic spaceship HUD interface with glowing orange accents and angular design
- **Spaceship variants**: `spaceship-cyan-(light|dark)`, `spaceship-grey-(light|dark)`, `spaceship-blue-(light|dark)`, `spaceship-green-(light|dark)`, `spaceship-yellow-(light|dark)`, `spaceship-red-(light|dark)`, `spaceship-purple-(light|dark)`, `spaceship-magenta-(light|dark)`, `spaceship-mono-(light|dark)`
### Custom themes
Create custom themes by copying an existing theme directory:
```bash
# Copy built-in theme as starting point
cp -r web/themes/light web/themes/mytheme
# Edit colors in theme.css (40 CSS variables)
nano web/themes/mytheme/theme.css
# Update metadata
nano web/themes/mytheme/theme.yaml
```
Custom themes appear automatically in **Configurations** → **Theme** dropdown after restart.
For complete documentation on creating custom themes, including:
- CSS variable reference
- Color customization examples
- Troubleshooting guide
See `docs/THEMES.md`.
## Archive recordings
The **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`.
Requirements:
- `ffmpeg` installed on the `vdradmin-go` host
- Optional (for percentage progress): `ffprobe`
Configuration:
- `archive.base_dir`: absolute output directory root
- `archive.profiles`: optional list of destination profiles (movie/series) so you can add more archive directories or customize defaults
- `archive.ffmpeg_args`: additional ffmpeg output args (defaults are hardware-accel friendly for AMD GPUs but can be changed)
Safety defaults:
- keeps originals
- runs in background
- refuses to overwrite an existing output file
## Watch TV
The **Watch TV** page (`/watch`) provides:
- a periodically refreshed **snapshot** (configurable interval + size)
- a **remote control** (SVDRP `HITK`)
- a **channel list** restricted to channels configured in **Configurations** (wanted channels)
### Snapshot requirements
The snapshot feature uses the SVDRP `GRAB` command.
- 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.
For troubleshooting and background, see `docs/WATCHTV.md`.
### Optional: stream URL mode (headless setups)
If you run VDR headless (recording-only) and `GRAB` cannot work, you can enable streaming for `/watch`.
#### Recommended: HLS proxy (built-in transcoding)
Configure `vdr.streamdev_backend_url` in Configurations:
- Example: `http://127.0.0.1:3000/{channel}`
- Requires: `ffmpeg` installed on the `vdradmin-go` host
- Requires: Suitable plugin like `vdr-plugin-streamdev-server`
- `vdradmin-go` will transcode streamdev MPEG-TS to browser-playable HLS automatically
- `/watch` uses internal proxy endpoint `/watch/stream/{channel}/index.m3u8`
#### Alternative: Direct external stream URL
- Configure `vdr.stream_url_template` if you have a pre-existing HLS/MJPEG/WebM endpoint
- The template may contain `{channel}`, which will be replaced with the selected VDR channel **number** (e.g. `1`, `2`, `3`).
- `/watch` embeds the URL into an HTML5 `` element. The URL must point to a format your browser can play (for example HLS `.m3u8` or a browser-supported MP4/WebM stream).
If 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`.
Note: 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.
## Testing
vdradmin-go has a comprehensive test suite covering multiple testing strategies:
### Running Tests
```bash
# Run all tests (unit + race detection)
make test
# Run tests with coverage report
make test-coverage
# Run integration tests (requires Docker)
go test -tags=integration ./internal/integration -v
# Run fuzz tests (optional, time-intensive)
go test -fuzz=FuzzParseSVDRPResponse -fuzztime=5m ./internal/adapters/secondary/svdrp/
```
### Test Coverage
- **250+ tests** across 7 packages
- **Unit tests**: Domain models, services, handlers, SVDRP protocol
- **Integration tests**: Real SVDRP protocol communication with testcontainers
- **Property-based tests**: Domain invariants using `testing/quick`
- **Fuzz tests**: SVDRP protocol parsing with Go 1.18+ fuzzing
- **Race detection**: Concurrent access validation (automatically enabled by `make test`)
- **Port contract tests**: VDRClient interface compliance validation
### Test Architecture
The test suite follows the same hexagonal architecture as the application:
- **Domain tests** (`internal/domain/*_test.go`): Model validation, business rules, property-based testing
- **Port contract tests** (`internal/ports/vdr_contract_test.go`): Reusable test suite ensuring all VDRClient implementations behave consistently
- **Service tests** (`internal/application/services/*_test.go`): Use case orchestration with canonical mocks
- **Adapter tests** (`internal/adapters/**/*_test.go`): HTTP handlers, SVDRP protocol parsing, fuzz testing
- **Integration tests** (`internal/integration/svdrp_integration_test.go`): Full protocol validation with Docker containers
- **Race tests** (`internal/application/services/race_test.go`): Concurrency safety validation
### Integration Tests (Docker)
Integration tests use testcontainers-go to spin up a real SVDRP stub server and verify:
- Channel list retrieval and parsing
- Timer CRUD operations (Create, Read, Update, Delete)
- Connection resilience and automatic reconnection
- Timeout handling and context cancellation
- Concurrent operations safety
- Timer collision detection
- Channel ID format parsing
```bash
# Run all integration tests (requires Docker)
go test -tags=integration ./internal/integration -v
# Run specific integration test
go test -tags=integration ./internal/integration -run TestSVDRP_RealProtocol_ChannelListRetrieval -v
```
### Coverage Metrics
Recent coverage (via `make test-coverage`):
- HTTP handlers: ~36%
- SVDRP adapter: ~57%
- Archive service: ~31%
- Application services: ~47%
- Port interfaces: ~79%
- Config infrastructure: ~32%
Note: Coverage percentages reflect tested code paths. Low percentages often indicate error handling paths that require specific VDR states to trigger.
## License
LGPL v2.1 (same as original [vdradmin-am](http://andreas.vdr-developer.org/vdradmin-am/index.html))