Skip to content

joedborg/tapedeck

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“Ό Tapedeck

A self-hosted BBC iPlayer & BBC Sounds download manager. Rust/Axum backend Β· Ember.js web UI Β· SQLite persistence Β· Docker deployment.

Warning

A UK TV licence is required to access BBC iPlayer TV content legally.


Features

  • Search BBC iPlayer and BBC Sounds programmes (TV and radio) via get_iplayer
  • Series drill-down β€” expand any show on the search page to browse all series and episodes; queue an entire series or individual episodes
  • Queue management β€” add, remove, cancel, retry, reorder downloads
  • Background worker pool β€” configurable concurrent downloads
  • Exponential-backoff retries β€” automatically retry failed downloads up to a configurable limit (2 s β†’ 4 s β†’ 8 s …)
  • Scheduled downloads β€” specify a future date/time per item
  • Live progress β€” WebSocket push updates (progress bar, speed, ETA)
  • Basic auth β€” token-based login, multi-user support
  • Settings UI β€” configure output directory, quality, tools, proxy, concurrency and retry limits

Quick start (Docker)

# 1. Clone
git clone https://github.com/you/tapedeck && cd tapedeck

# 2. Configure
cp .env.example .env
#   β†’ Edit .env: set SECRET, ADMIN_PASSWORD, DOWNLOAD_DIR

# 3. Run
docker compose up -d

# 4. Open
open http://localhost:3000

Log in with the ADMIN_USERNAME / ADMIN_PASSWORD you set in .env (defaults: admin / changeme).


Optional: WireGuard deployment

WG.Dockerfile is a variant that bakes a WireGuard VPN tunnel into the container, starting it before the tapedeck app. This is useful if you want all get_iplayer traffic to egress through a VPN without configuring the host network.

Prerequisites

  • A WireGuard configuration file named wg0.conf somewhere on the host β€” it is mounted into the container at runtime, so private keys never appear in an image layer.
  • The container must be granted the NET_ADMIN capability (and optionally SYS_MODULE if the wireguard kernel module is not already loaded on the host).

Compose setup

These are the changes needed to make the docker-compose setup with Wireguard.

services:
  tapedeck:
    build:
      dockerfile: WG.Dockerfile
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./wg0.conf:/etc/wireguard/wg0.conf:ro

How it works

On startup the container (running as root) mounts and reads /etc/wireguard/wg0.conf, calls wg-quick up wg0 to bring up the WireGuard interface, then drops to the unprivileged tapedeck user to run the application. Because the config is mounted at runtime rather than copied into the image, private keys never appear in any image layer and it is safe to push the image to a registry.


Configuration

All settings come from environment variables (or a .env file β€” copy .env.example to get started):

Variable Default Description
SECRET (required in prod) HMAC signing secret β€” generate with openssl rand -hex 32
ADMIN_USERNAME admin Initial admin username, seeded on first boot
ADMIN_PASSWORD changeme Initial admin password, seeded on first boot
DOWNLOAD_DIR ./downloads Host directory where downloaded programmes are stored (mounted as /downloads in the container)
MAX_CONCURRENT 5 Maximum simultaneous downloads
MAX_DOWNLOAD_RETRIES 5 Times to retry a failed download (0 = no retries); backs off exponentially (2 s β†’ 4 s β†’ 8 s …)
PROXY (empty) Optional HTTP proxy URL passed to get_iplayer
BIND 0.0.0.0:3000 HTTP listen address
DATABASE_URL /data/tapedeck.db SQLite path inside the container
OUTPUT_DIR /downloads Download destination inside the container
GET_IPLAYER_PATH /usr/local/bin/get_iplayer Path to the get_iplayer binary
FFMPEG_PATH /usr/bin/ffmpeg Path to ffmpeg

Runtime settings (output dir, quality, retry limit, concurrency) can also be updated via the Settings page in the UI and are stored in the database; they take effect on the next download attempt.

Quality values

The Default Quality setting accepts the following values:

Value TV (--tv-quality) Radio (--radio-quality)
best fhd high
good hd standard
worst mobile low

Raw get_iplayer values (1080p, 720p, sd, etc.) can also be entered directly.


Development

Backend (Rust)

cd backend
cargo run
# binds to http://localhost:3000
# API at http://localhost:3000/api
# WS  at ws://localhost:3000/ws?token=<token>

Requires get_iplayer (v3.36+) and ffmpeg to be in PATH.

Frontend (Ember)

cd frontend
npm install
npm start   # dev server on http://localhost:4200 (proxies /api β†’ localhost:3000)

Production build output goes to frontend/dist/, which is served as static files by the Rust server.


REST API

Method Path Description
POST /api/auth/login Login β†’ { token, user_id, username }
GET /api/queue List queue; ?status=&page=&per_page=
POST /api/queue Add item
GET /api/queue/:id Get item
DELETE /api/queue/:id Cancel / remove
POST /api/queue/:id/retry Retry failed/cancelled
POST /api/queue/reorder Bulk reprioritise
GET /api/search?q=&type=tv|radio Search programmes
GET /api/search/episodes?pid=&type=tv|radio List all episodes for a brand/series PID
POST /api/search/refresh Refresh programme cache
GET /api/settings List all settings
PUT /api/settings/:key Update one setting
PATCH /api/settings Bulk update settings
GET /api/users List users
GET /api/users/me Current user
POST /api/users Create user
DELETE /api/users/:id Delete user
PUT /api/users/:id/password Change password

All endpoints except /api/auth/login require Authorization: Bearer <token>.

WebSocket

Connect to ws://<host>/ws?token=<token> for real-time events:

// Progress update (HLS streams β€” live percent + speed + ETA)
{ "type": "progress", "id": "...", "progress": 42.5, "speed": "97.8 Mb/s", "eta": "00:01:23" }
// Progress update (DASH streams β€” no percent available; shows speed/elapsed)
{ "type": "progress", "id": "...", "progress": 0, "speed": "2.3x", "eta": "00:05:32" }
// Status change
{ "type": "status_change", "id": "...", "status": "done" }
// Item added / removed
{ "type": "item_added",   "item": { ... } }
{ "type": "item_removed", "id": "..." }
// Error
{ "type": "error", "id": "...", "message": "..." }

For DASH streams (progress: 0), the UI shows an indeterminate animated bar until the download completes. A heartbeat event is also emitted every 30 seconds with the elapsed time so the UI stays live.


Project layout

tapedeck/
β”œβ”€β”€ backend/               Rust/Axum service
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ main.rs        Entry point
β”‚   β”‚   β”œβ”€β”€ config.rs      Environment config (incl. MAX_DOWNLOAD_RETRIES)
β”‚   β”‚   β”œβ”€β”€ auth.rs        Password hashing + token auth
β”‚   β”‚   β”œβ”€β”€ db.rs          SQLite pool + migrations
β”‚   β”‚   β”œβ”€β”€ models.rs      Shared types + DTOs
β”‚   β”‚   β”œβ”€β”€ queue.rs       Background worker pool + retry logic
β”‚   β”‚   β”œβ”€β”€ iplayer.rs     get_iplayer subprocess wrapper + episode parser
β”‚   β”‚   β”œβ”€β”€ state.rs       Shared Axum state
β”‚   β”‚   └── routes/
β”‚   β”‚       β”œβ”€β”€ mod.rs     Router assembly
β”‚   β”‚       β”œβ”€β”€ queue.rs   Queue endpoints
β”‚   β”‚       β”œβ”€β”€ search.rs  Search + episode-listing endpoints
β”‚   β”‚       β”œβ”€β”€ settings.rs Settings CRUD
β”‚   β”‚       β”œβ”€β”€ users.rs   User management
β”‚   β”‚       └── ws.rs      WebSocket handler
β”‚   └── migrations/
β”‚       β”œβ”€β”€ 001_initial.sql
β”‚       └── 002_max_retries_setting.sql
β”œβ”€β”€ frontend/              Ember.js SPA
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ app.js
β”‚   β”‚   β”œβ”€β”€ router.js
β”‚   β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”‚   β”œβ”€β”€ api.js     HTTP client (incl. fetchEpisodes)
β”‚   β”‚   β”‚   └── socket.js  WebSocket client
β”‚   β”‚   β”œβ”€β”€ routes/        Route classes
β”‚   β”‚   β”œβ”€β”€ controllers/   Controller classes (search: series drill-down)
β”‚   β”‚   β”œβ”€β”€ helpers/
β”‚   β”‚   β”‚   └── includes.js
β”‚   β”‚   β”œβ”€β”€ templates/     Handlebars templates
β”‚   β”‚   └── styles/
β”‚   β”‚       └── app.css
β”‚   └── config/
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ docker-compose.yml
└── .env.example

Licence

GNU General Public License v3.0 or later (GPL-3.0-or-later)

About

Platform for downloading BBC iPlayer programmes

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors