中文 | English
这是 Roon Cover Art 的 16:9 显示版本(Docker),适合电视、宽屏显示器等 16:9 屏幕。
与方形画框版本不同,本版本重点是:
- 16:9 画面布局
- 播放时显示封面 + 曲目信息(标题 / 艺术家 / 专辑)
- 停止播放后自动切换 Art Wall(封面墙)
This is the 16:9 Docker edition of Roon Cover Art, designed for TVs and wide displays.
Compared with the square-frame version, this build focuses on:
- 16:9 layout
- Now playing cover art + track info (title / artist / album)
- Auto-switch to Art Wall mode when playback stops
- 实时显示当前播放专辑封面
- 显示曲目信息(标题 / 艺术家 / 专辑)
- 根据封面提取主色调作为背景氛围色
- 播放停止后约 15 秒自动切换到 Art Wall 模式
- Art Wall 每 60 秒刷新 3 张图片
- 支持键盘、媒体键和带视觉反馈的触摸手势控制(左滑下一曲、右滑上一曲、上滑停止、下滑播放)
- 支持 Docker 后端直接监听宿主机
/dev/input键盘事件来控制 Roon(无需浏览器) - 自动保存播放过的专辑封面到
images/ - Roon 配对信息持久化(通过
config.json,避免重启后重复授权)
epochaudio/coverart_docker:5.0.3epochaudio/coverart_docker:latest
OpenWrt / EpochBrain 系统推荐直接使用系统内置安装脚本:
install_coverart.sh脚本默认行为:
- 容器名:
roon-coverart - 镜像:
epochaudio/coverart_docker:latest - 数据目录:
/cache/roon-coverart_data - Web 端口:
3666 - 默认启用宿主机键盘监听:
KEYBOARD_ENABLED=true - 挂载
/dev/input,并在 Docker 支持时加入--device-cgroup-rule 'c 13:* rwm'
键盘不需要在安装脚本里做强判断。没有键盘或没有可用设备时,Cover Art 主服务仍会正常启动;后续插入键盘后,执行:
docker restart roon-coverart如果不需要宿主机键盘控制:
KEYBOARD_ENABLED=false install_coverart.sh如果需要固定某个键盘设备:
KEYBOARD_DEVICE=/dev/input/by-id/your-keyboard-event-kbd install_coverart.sh- 最简准备(默认参数即可运行)
mkdir -p images
printf '%s\n' '{}' > config.json说明:
images/用于保存封面缓存(建议持久化)config.json用于保存 Roon 配对信息(建议持久化,由 Roon 授权后写入)config/local.json是可选项,不创建也能启动(使用默认参数)
- 运行容器(配对信息持久化 + 默认启用宿主机键盘监听)
docker pull epochaudio/coverart_docker:latest
docker run -d \
--name roon-coverart \
--network host \
--restart unless-stopped \
--device /dev/input:/dev/input \
--device-cgroup-rule 'c 13:* rwm' \
-e KEYBOARD_ENABLED=true \
-e KEYBOARD_DEVICE= \
-e KEYBOARD_DEVICES= \
-e KEYBOARD_DEBOUNCE_MS=180 \
-v $(pwd)/images:/app/images \
-v $(pwd)/config.json:/app/config.json:rw \
-v /dev/input/by-id:/dev/input/by-id:ro \
-v /dev/input/by-path:/dev/input/by-path:ro \
epochaudio/coverart_docker:latest如果宿主机没有 /dev/input/by-id 或 /dev/input/by-path,删除对应 -v 行即可。如果 Docker 不支持 --device-cgroup-rule,删除该行即可;键盘已在容器启动前插好时,普通 /dev/input 挂载通常已经足够。
- 打开页面
- 默认地址:
http://localhost:3666 - 如果从局域网其他设备访问,请将
localhost替换为宿主机 IP。
services:
coverart:
build:
context: .
image: roon-coverart:5.0.3-local
container_name: roon-coverart
network_mode: "host"
restart: unless-stopped
environment:
- INPUT_GID=${INPUT_GID:-}
- KEYBOARD_ENABLED=${KEYBOARD_ENABLED:-true}
- KEYBOARD_DEVICE=${KEYBOARD_DEVICE:-}
- KEYBOARD_DEVICES=${KEYBOARD_DEVICES:-}
- KEYBOARD_DEBOUNCE_MS=${KEYBOARD_DEBOUNCE_MS:-180}
devices:
- /dev/input:/dev/input
device_cgroup_rules:
- "c 13:* rwm"
group_add:
- "${INPUT_GID:-0}"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
- ./images:/app/images:rw
- ./config.json:/app/config.json:rw
- /dev/input/by-id:/dev/input/by-id:ro
- /dev/input/by-path:/dev/input/by-path:ro
# 可选:先创建 config/local.json,再取消下一行注释
# - ./config/local.json:/app/config/local.json:ro启动:
docker compose up -d打开页面:http://localhost:3666
如果你需要固定端口、封面保存格式等参数,再创建 config/local.json 并挂载:
mkdir -p config
cat > config/local.json <<'EOF'
{
"server": {
"port": 3666
},
"artwork": {
"saveDir": "./images",
"autoSave": true,
"format": "jpg"
},
"access": {
"allowedOrigins": []
},
"keyboard": {
"enabled": true,
"device": "",
"devices": [],
"debounceMs": 180,
"keyMap": {}
},
"logging": {
"level": "info"
}
}
EOFDocker Run 增加挂载:
-v $(pwd)/config/local.json:/app/config/local.json:roDocker Compose 增加:
# - ./config/local.json:/app/config/local.json:roserver.port: Web 服务端口(默认3666)artwork.saveDir: 封面保存目录(默认./images)artwork.autoSave: 是否自动保存封面(默认true)artwork.format: 保存格式(jpg或png,默认jpg)access.allowedOrigins: 可选跨域来源白名单;环境变量中多个来源用逗号分隔keyboard.enabled: 是否启用宿主机键盘监听(安装脚本和 compose 默认通过环境变量启用)keyboard.device: 固定一个键盘设备路径,例如/dev/input/by-id/...-event-kbdkeyboard.devices: 固定多个键盘设备路径keyboard.debounceMs: 按键防抖时间,默认180keyboard.keyMap: 自定义按键到控制动作的映射logging.level: 日志级别,支持error/warn/info/debug,默认info
也支持环境变量(Docker):
SERVER_PORTARTWORK_SAVEDIRARTWORK_AUTOSAVEARTWORK_FORMATACCESS_ALLOWED_ORIGINSKEYBOARD_ENABLEDKEYBOARD_DEVICEKEYBOARD_DEVICESKEYBOARD_DEBOUNCE_MSLOG_LEVEL
说明:
- 固定参数建议放在
config/local.json - Roon 配对信息由 Roon 授权后写入根目录
config.json(请保留)
如果键盘插在运行 Docker 的宿主机上,可以让容器直接读取 /dev/input 事件并控制 Roon。推荐安装脚本和本仓库 docker-compose.yml 默认启用 KEYBOARD_ENABLED=true。如果启动时没有键盘,应用只会记录 warning,不影响网页和 Roon 扩展启动。
普通 Linux 主机建议查宿主机 input 组 GID:
getent group input例如输出 input:x:106:,则创建 .env:
INPUT_GID=106
KEYBOARD_ENABLED=true
KEYBOARD_DEVICE=
KEYBOARD_DEVICES=
KEYBOARD_DEBOUNCE_MS=180OpenWrt 常见情况是 /dev/input/event* 为 root:root 600,系统安装脚本会用容器运行参数处理读取权限;如果你手写 docker run,可以参考上面的 Docker Run 示例。
默认不指定 KEYBOARD_DEVICE / KEYBOARD_DEVICES,程序会自动扫描并监听所有可识别的键盘事件设备,包括 /dev/input/by-id/、/dev/input/by-path/ 和 /proc/bus/input/devices 中的键盘。也可以显式指定一个或多个稳定路径:
KEYBOARD_DEVICE=/dev/input/by-id/your-keyboard-event-kbd
KEYBOARD_DEVICES=/dev/input/by-id/kbd1-event-kbd,/dev/input/by-id/kbd2-event-kbd优先使用 /dev/input/by-id/...-event-kbd 或 /dev/input/by-path/...-event-kbd,不要优先使用 /dev/input/event3 这类编号,因为重启后编号可能变化。可用下面命令查看:
ls -l /dev/input/by-id/
ls -l /dev/input/by-path/默认按键映射:
KEY_RIGHT/KEY_NEXTSONG: 下一曲KEY_LEFT/KEY_PREVIOUSSONG: 上一曲KEY_SPACE/KEY_PLAYPAUSE: 播放/暂停KEY_UP/KEY_PLAY: 播放KEY_DOWN/KEY_STOP/KEY_STOPCD: 停止KEY_PAUSE: 暂停
如果没有发现键盘,或某个设备打开失败,只会输出 warning,不影响网页和 Roon 扩展启动。
键盘扫描发生在容器启动时,不会一直轮询新设备。典型场景:
- 启动前已插键盘:容器启动后直接监听
- 启动后才插键盘:执行
docker restart roon-coverart - 拔掉后重新插入:如果 event 编号变化,执行
docker restart roon-coverart - 多个输入设备:优先用
KEYBOARD_DEVICE或KEYBOARD_DEVICES指定/dev/input/by-id/...稳定路径
- 打开 Roon
- 进入
Settings->Extensions - 启用扩展(显示名:
CoverArt_docker) - 在扩展设置中选择播放区(Zone)
- 开始播放音乐,网页将显示封面与曲目信息
config.json必须持久化挂载,否则容器重建/重启后可能需要重新授权images/需要可写权限,用于保存专辑封面network_mode: "host"用于 Roon 发现,也会让3666端口暴露在宿主机网络上- 不要把
config.json、config/local.json、.env、images/提交到 GitHub
docker logs -f roon-coverart
docker restart roon-coverart
docker ps -a --filter name=roon-coverartdocker build -t roon-coverart:5.0.3-local .- Real-time now-playing album art display
- Track info display (title / artist / album)
- Dominant color extraction for ambient background
- Automatically switches to Art Wall mode about 15s after playback stops
- Art Wall refreshes 3 images every 60 seconds
- Keyboard, media-key, and visual touch gesture controls (swipe left for next, right for previous, up to stop, down to play)
- Optional Docker-side host keyboard control via
/dev/input(no browser required) - Auto-saves played album art to
images/ - Persistent Roon pairing state via
config.json(avoids re-authorization after restart)
epochaudio/coverart_docker:5.0.3epochaudio/coverart_docker:latest
On OpenWrt / EpochBrain systems, use the built-in installer:
install_coverart.shDefaults:
- Container:
roon-coverart - Image:
epochaudio/coverart_docker:latest - Data directory:
/cache/roon-coverart_data - Web port:
3666 - Host keyboard listening enabled with
KEYBOARD_ENABLED=true - Mounts
/dev/inputand adds--device-cgroup-rule 'c 13:* rwm'when Docker supports it
If no keyboard is attached, the web UI and Roon extension still start. Plug in a keyboard later, then run:
docker restart roon-coverartDisable host keyboard control:
KEYBOARD_ENABLED=false install_coverart.shPin one keyboard device:
KEYBOARD_DEVICE=/dev/input/by-id/your-keyboard-event-kbd install_coverart.sh- Minimal setup (defaults work out of the box)
mkdir -p images
printf '%s\n' '{}' > config.jsonNotes:
images/stores cached/saved artwork (recommended to persist)config.jsonstores Roon pairing state (recommended to persist, written after Roon authorization)config/local.jsonis optional (defaults are used if missing)
- Run the container (persistent pairing state + host keyboard enabled by default)
docker pull epochaudio/coverart_docker:latest
docker run -d \
--name roon-coverart \
--network host \
--restart unless-stopped \
--device /dev/input:/dev/input \
--device-cgroup-rule 'c 13:* rwm' \
-e KEYBOARD_ENABLED=true \
-e KEYBOARD_DEVICE= \
-e KEYBOARD_DEVICES= \
-e KEYBOARD_DEBOUNCE_MS=180 \
-v $(pwd)/images:/app/images \
-v $(pwd)/config.json:/app/config.json:rw \
-v /dev/input/by-id:/dev/input/by-id:ro \
-v /dev/input/by-path:/dev/input/by-path:ro \
epochaudio/coverart_docker:latestIf /dev/input/by-id or /dev/input/by-path does not exist on the host, remove the matching -v line. If Docker does not support --device-cgroup-rule, remove that line; mounting /dev/input is usually enough when the keyboard is attached before container startup.
- Open the UI
- Default URL:
http://localhost:3666 - For LAN access from another device, replace
localhostwith the host IP address.
services:
coverart:
build:
context: .
image: roon-coverart:5.0.3-local
container_name: roon-coverart
network_mode: "host"
restart: unless-stopped
environment:
- INPUT_GID=${INPUT_GID:-}
- KEYBOARD_ENABLED=${KEYBOARD_ENABLED:-true}
- KEYBOARD_DEVICE=${KEYBOARD_DEVICE:-}
- KEYBOARD_DEVICES=${KEYBOARD_DEVICES:-}
- KEYBOARD_DEBOUNCE_MS=${KEYBOARD_DEBOUNCE_MS:-180}
devices:
- /dev/input:/dev/input
device_cgroup_rules:
- "c 13:* rwm"
group_add:
- "${INPUT_GID:-0}"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
- ./images:/app/images:rw
- ./config.json:/app/config.json:rw
- /dev/input/by-id:/dev/input/by-id:ro
- /dev/input/by-path:/dev/input/by-path:ro
# Optional: create config/local.json first, then uncomment this mount.
# - ./config/local.json:/app/config/local.json:roStart:
docker compose up -dOpen the UI: http://localhost:3666
If you want to pin the port or artwork settings, create config/local.json and mount it:
mkdir -p config
cat > config/local.json <<'EOF'
{
"server": {
"port": 3666
},
"artwork": {
"saveDir": "./images",
"autoSave": true,
"format": "jpg"
},
"access": {
"allowedOrigins": []
},
"keyboard": {
"enabled": true,
"device": "",
"devices": [],
"debounceMs": 180,
"keyMap": {}
},
"logging": {
"level": "info"
}
}
EOFAdd this mount to Docker Run:
-v $(pwd)/config/local.json:/app/config/local.json:roAdd this line to Docker Compose:
# - ./config/local.json:/app/config/local.json:roserver.port: Web server port (default3666)artwork.saveDir: Artwork save directory (default./images)artwork.autoSave: Enable auto-save (defaulttrue)artwork.format: Save format (jpgorpng, defaultjpg)access.allowedOrigins: Optional CORS origin allowlist; comma-separated when set by env varkeyboard.enabled: Enable host keyboard listening; the installer and compose enable it through env vars by defaultkeyboard.device: Pin one keyboard device path, for example/dev/input/by-id/...-event-kbdkeyboard.devices: Pin multiple keyboard device pathskeyboard.debounceMs: Key debounce time, default180keyboard.keyMap: Custom key-to-action mappinglogging.level: Log level, one oferror/warn/info/debug, defaultinfo
Environment variables are also supported:
SERVER_PORTARTWORK_SAVEDIRARTWORK_AUTOSAVEARTWORK_FORMATACCESS_ALLOWED_ORIGINSKEYBOARD_ENABLEDKEYBOARD_DEVICEKEYBOARD_DEVICESKEYBOARD_DEBOUNCE_MSLOG_LEVEL
Notes:
- Put stable parameters in
config/local.json - Keep root
config.jsonfor Roon pairing state written after Roon authorization
If the keyboard is attached to the Docker host, the container can read /dev/input events and control Roon directly. The OpenWrt installer and this repository's docker-compose.yml enable it by default with KEYBOARD_ENABLED=true. If no keyboard is detected, the app logs a warning and the web UI/Roon extension keep running.
- Find the host
inputgroup GID:
getent group inputIf the output is input:x:106:, create .env like this:
INPUT_GID=106
KEYBOARD_ENABLED=true
KEYBOARD_DEVICE=
KEYBOARD_DEVICES=
KEYBOARD_DEBOUNCE_MS=180By default, leave KEYBOARD_DEVICE / KEYBOARD_DEVICES empty. The app scans and listens to all detected keyboard event devices from /dev/input/by-id/, /dev/input/by-path/, and /proc/bus/input/devices. You can also pin one or more stable paths:
KEYBOARD_DEVICE=/dev/input/by-id/your-keyboard-event-kbd
KEYBOARD_DEVICES=/dev/input/by-id/kbd1-event-kbd,/dev/input/by-id/kbd2-event-kbdPrefer /dev/input/by-id/...-event-kbd or /dev/input/by-path/...-event-kbd over /dev/input/event3, because event numbers can change after reboot:
ls -l /dev/input/by-id/
ls -l /dev/input/by-path/Default key mapping:
KEY_RIGHT/KEY_NEXTSONG: nextKEY_LEFT/KEY_PREVIOUSSONG: previousKEY_SPACE/KEY_PLAYPAUSE: play/pauseKEY_UP/KEY_PLAY: playKEY_DOWN/KEY_STOP/KEY_STOPCD: stopKEY_PAUSE: pause
If no keyboard is detected, or one device fails to open, the app only logs a warning and does not stop the web UI or Roon extension.
Keyboard devices are scanned at container startup; the app does not continuously poll for newly attached devices. Typical cases:
- Keyboard attached before startup: it is listened to immediately
- Keyboard attached after startup: run
docker restart roon-coverart - Keyboard unplugged and replugged: restart the container if the event number changes
- Multiple input devices: prefer
KEYBOARD_DEVICEorKEYBOARD_DEVICESwith stable/dev/input/by-id/...paths
- Open Roon
- Go to
Settings->Extensions - Enable the extension (
CoverArt_docker) - Select the playback zone in extension settings
- Start playing music and open the web page
- Persist
config.json, or you may need to re-authorize after container recreation/restart images/must be writable so artwork can be savednetwork_mode: "host"is used for Roon discovery and exposes port3666on the host network- Do not commit
config.json,config/local.json,.env, orimages/to GitHub
docker logs -f roon-coverart
docker restart roon-coverart
docker ps -a --filter name=roon-coverartdocker build -t roon-coverart:5.0.3-local .MIT (see package.json)