A Go-based web application that bridges physical and digital media using NFC cards. Tap NFC cards on a reader to instantly play movies, TV shows, or music on your TV through Plex and Apple TV, managed via Home Assistant.
TapeDeck is feature-complete for personal use. Includes Plex authentication, multi-server media browsing, card mapping with real-time NFC pairing, Home Assistant integration with WebSocket, web-based setup wizard, Docker deployment, and hardware documentation.
TapeDeck recreates the nostalgic experience of physical media libraries for the streaming age. Kids (and adults!) can tap a physical card to play their favorite content without navigating complex streaming interfaces.
- Visual Media Browser: Browse Plex libraries with poster art and metadata
- Real-time NFC Pairing: Assign media to cards by tapping them during setup
- Zero YAML Editing: All mappings managed through web UI
- Smart TV Show Handling: Automatically plays next unwatched episode
- Home Assistant Integration: Seamless playback on Apple TV
- Multi-Server Support: Connect to multiple Plex servers simultaneously
- Multi-Device Support: Configure multiple Apple TVs for playback
- Web-Based Setup: First-time configuration wizard with auto-discovery
- Language: Go 1.25+
- Templating: Templ (type-safe Go templates)
- Frontend: Vanilla JavaScript with WebSocket for real-time NFC pairing
- Database: SQLite (pure Go driver, no CGO)
- WebSocket: Home Assistant integration for NFC events
- Deployment: Docker / Docker Compose (see Docker Deployment)
- Go 1.25 or higher
- Air (hot reload during development)
- Templ CLI (template generation)
- Plex Media Server
- Home Assistant with Apple TV integration
- ESPHome-compatible device (ESP32 or ESP8266)
- NFC reader (RC522 or PN532)
- NFC cards/tags (NTAG213/215/216 or MIFARE Classic)
- Apple TV with Plex app
See the Hardware Setup Guide for detailed instructions on wiring and configuring your NFC reader.
# Install Go tools
go install github.com/a-h/templ/cmd/templ@latest
go install github.com/air-verse/air@latestcd TapeDeck
go mod download
# Optional: Create .env file to override defaults
# cp .env.example .env
# Edit .env to customize PORT, DATA_DIR, LOG_LEVEL, or DEV_MODE# Start with hot reload
air
# Server will start on http://localhost:3001On first run, the application will redirect you to the setup wizard at http://localhost:3001/setup:
- Welcome: Introduction to the setup process
- Plex Authentication: Log in with your Plex account to discover servers
- Server Selection: Choose which Plex servers to connect to (supports multiple)
- Home Assistant: Enter your HA URL and long-lived access token
- Apple TV Selection: Choose which Apple TVs to use for playback (supports multiple)
- Completion: Review configuration and finish setup
The wizard creates config.yml with your settings. You can re-run setup anytime by deleting this file.
# Test health check endpoint
curl http://localhost:3001/health
# Expected response:
# {"status":"ok"}
# Run tests
go test -v ./...Air automatically watches for changes and rebuilds:
# Start development server
air
# Make changes to .go or .templ files
# Air will detect changes, rebuild, and restart automatically
# With proxy mode enabled (.air.toml), access via:
# http://localhost:3002 - Proxy with auto-refresh
# http://localhost:3001 - Direct app server# Run all tests
go test ./...
# Run with verbose output
go test -v ./...
# Run specific package
go test ./internal/config
# Run with coverage
go test -cover ./...# Format Go code
go fmt ./...
# Generate Templ files manually (Air does this automatically)
templ generatetapedeck/
├── main.go # Entry point, HTTP server
├── main_test.go # Tests for main package
├── go.mod # Go dependencies
├── .env.example # Environment variable template
├── .air.toml # Hot reload configuration
├── Dockerfile # Production build
├── docker-compose.yml # Local Docker setup
│
├── internal/ # Private application code
│ ├── config/ # Configuration loading
│ ├── db/ # Database operations
│ ├── models/ # Data models
│ ├── auth/ # Plex OAuth, sessions
│ ├── plex/ # Plex API client
│ ├── ha/ # Home Assistant WebSocket
│ ├── handlers/ # HTTP handlers
│ ├── middleware/ # Auth, logging middleware
│ └── pairing/ # NFC pairing logic
│
├── templates/ # Templ components
│ ├── layouts/ # Base layouts
│ ├── pages/ # Full pages
│ └── components/ # Reusable components
│
├── static/ # Static assets
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript
│ └── icons/ # Icons and images
│
├── migrations/ # SQL migration files
└── data/ # SQLite database (gitignored)
Application settings can be configured via environment variables (.env file). All settings have sensible defaults and are optional:
| Variable | Description | Default | Example |
|---|---|---|---|
PORT |
HTTP server port | 3001 |
3001 |
DATA_DIR |
Directory for generated files | . |
/path/to/data |
LOG_LEVEL |
Logging level | info |
info, debug, warn, error |
DEV_MODE |
Skip TLS verification (dev only) | false |
true |
REQUIRE_TLS |
Require HTTPS for session cookies | false |
true |
Data Storage: All generated files are stored in DATA_DIR (defaults to current directory):
- Session Security:
.session_secret- Session encryption key auto-generated on first run (gitignored, 0600 permissions) - CSRF Protection:
.csrf_key- CSRF token key auto-generated on first run (gitignored, 0600 permissions) - Token Encryption:
.encryption_key- AES-256-GCM key auto-generated on first run (gitignored, 0600 permissions) - Database:
tapedeck.db- SQLite database with encrypted tokens
Plex servers, Home Assistant URL, and Apple TVs are configured through the setup wizard, which creates a config.yml file:
version: 1
plex_servers:
- id: "server-id-123"
name: "Home Server"
owner: "username"
connections:
- uri: "http://192.168.1.100:32400"
local: true
home_assistant:
url: "http://192.168.1.101:8123"
# Note: Token is stored encrypted in database, not in this file
apple_tvs:
- entity: "media_player.living_room_apple_tv"
name: "Living Room"
default: true
- entity: "media_player.bedroom_apple_tv"
name: "Bedroom"
default: falseToken Storage: The Home Assistant long-lived access token is stored encrypted in the database (using AES-256-GCM), not in config.yml. This keeps the configuration file human-editable and prevents accidental exposure of credentials.
Multiple Server Support: You can connect to multiple Plex servers. When pairing cards, you'll select which server the media comes from.
Multiple Apple TV Support: Configure multiple Apple TVs and choose which one to use when pairing each card.
# Build for production
docker build -t tapedeck:latest .
# Build with docker-compose
docker-compose build# Using docker-compose (recommended)
docker-compose up -d
# Or directly with docker
docker run -d \
--name tapedeck \
-p 3001:3001 \
-v $(pwd)/data:/data \
-e DATA_DIR=/data \
-e PUID=1000 \
-e PGID=1000 \
--env-file .env \
tapedeck:latestThe container supports PUID and PGID environment variables to match your host user/group (similar to LinuxServer.io images):
# Find your user ID and group ID
id $(whoami)
# Set PUID/PGID in docker-compose.yml or pass as environment variables
docker run -d \
-e PUID=1027 \
-e PGID=100 \
...Default: PUID=1000, PGID=1000
For Synology NAS: Use your dockerlimited user's UID/GID (typically PUID=1027, PGID=100).
docker-compose logs -f tapedeck- Unit tests:
*_test.gonext to implementation files - Mock HTTP responses for external APIs (Plex, HA)
Core Functionality:
- Plex authentication and multi-server support
- Media browsing with library and search interfaces
- Card mapping with inline search autocomplete
- Real-time NFC pairing with live tag detection
- Home Assistant WebSocket integration
- Playback via Apple TV with multi-device support
Production Features:
- Web-based setup wizard with auto-discovery
- SQLite database with migrations
- Session management and authentication
- Prometheus metrics endpoint
- Docker deployment with multi-platform support
- Request logging and health checks
Documentation:
- Complete hardware setup guide (docs/hardware-setup.md)
- Home Assistant integration guide (docs/home-assistant-setup.md)
- ESPHome configuration examples for RC522 and PN532 readers
The following improvements are documented for future work and may be good candidates for community contributions:
- End-to-End Testing: Automated integration tests covering full user workflows
- User Documentation: Comprehensive user guide with screenshots and video tutorials
- Plex SDK Integration: Replace custom Plex API client with established SDK like plexgo to avoid XML parsing issues and stay current with API changes
- Media Player Filtering: Add ability to filter/distinguish between different types of media players (Apple TV, Chromecast, smart speakers) during setup wizard, possibly using entity ID patterns or Home Assistant device attributes
- Responsive Design: Mobile and tablet optimization - see
docs/responsive-design.md - Accessibility: WCAG compliance, keyboard navigation, screen reader support - see
docs/accessibility.md
Contributions are welcome. See the Future Enhancements section for potential areas to work on.
When contributing:
- Follow existing code style and patterns
- Add tests for new functionality
- Update documentation as needed
- Open an issue first for major changes
MIT
Inspired by the blog post "How I Built an NFC Movie Library for My Kids" and similar projects in the home automation community.