服务卡片 · 媒体
Audiobookshelf:让进度在车里和厨房之间跟着你
有声书服务器真正提供的东西不是文件,是位置。用文件管理器在手机上放 MP3,这个位置每次都会丢;Audiobookshelf 把位置、倍速和书签都存在服务端,于是同一本书能在车载音响、厨房音箱和笔记本上接着听,而不需要任何人去翻文件夹。
| 容器镜像 | ghcr.io/advplyr/audiobookshelf:2.19.4 |
|---|---|
| 宿主端口 | :13378/tcp |
| 数据目录 | /srv/homelab/audiobookshelf/config |
| 内存预留 | 180 MB |
| CPU 上限 | 0.40 vCPU (cpus: "0.40") — transcoding for mobile clients is the peak |
| 更新节奏 | quarterly. The mobile app updates more often than the server, and the server usually needs to be within a few versions of it |
| ARM 兼容 | arm64 native including the ffmpeg build used for streaming and chapter embedding |
| 宿主 | 容器 | proto | 暴露 | 用途 |
|---|---|---|---|---|
| :13378 | 80 | tcp | 仅局域网 | web player and the mobile app API |
部署步骤
建好配置和元数据目录,并把属主交给容器
两个目录都必须对容器的 uid 可写。其中任何一个只读挂载,都会得到一个能提供媒体库、但保存进度时静默失败的服务端。
run sudo mkdir -p /srv/homelab/audiobookshelf/{config,metadata} sudo chown -R 1000:1000 /srv/homelab/audiobookshelf媒体库只读挂载,并让扫描器指向它
只读是刻意的:扫描器把标签读进自己的数据库,从不写回文件。要改一个标签,就在 NAS 上改文件然后重新扫描。
run cd /srv/homelab/audiobookshelf && docker compose up -d # 界面里:设置 -> 添加媒体库 -> 有声书 -> /audiobooks docker logs --tail 30 audiobookshelf | grep -i scan加一个播客源,并限制保留多少集
没有保留设置的话,播客目录会在固态盘上无限增长。在添加源的时候就设好每档节目的集数上限,而不是等盘满了再补。
run # 媒体库 -> 添加播客 -> 粘贴 RSS 地址 du -sh /srv/homelab/audiobookshelf/metadata/feeds # 然后在节目设置里限定保留集数
文件从哪来,以及唯一必须做对的一件事
有声书是唯一一种「目录结构比标签更重要」的媒体类型,因为一本书就是一个装着章节文件的文件夹,扫描器以文件夹为单位。一本书一个文件夹,上面是作者文件夹,配一张 cover.jpg 或在第一条轨道里嵌入封面。
在首次扫描之前把这件事做对,值十分钟;做完之后再改,意味着媒体库里会把一百本书显示成一本有一百章的书,或者更糟——一百本各自独立的书。
# /mnt/nas/audiobooks/
# 作者名/
# 书名/
# 01 - Chapter.mp3
# 02 - Chapter.mp3
# cover.jpg
# metadata.json(可选)直连与转码
在局域网内,App 播原始文件,派完全不干活。走移动网络时,App 可以请求一个低码率的转码流,这时派的 CPU 才有意义:Pi 4 上一次转码很轻松,两次就不行了。
要限制的是转码并发数。有声书很长——一次要四分钟才能追上听众的转码是糟糕的体验,所以不设上限时,第二个听众会让两个人一起缓冲。
| 场景 | 发生什么 | 派的开销 |
|---|---|---|
| 局域网,原始文件 | 直接播放 | 无 |
| 移动网络,64k opus | 实时转码 | 约单核的 12% |
| 两个移动端听众 | 第二个排队 | 不设上限时一个核、双方都在缓冲 |
播客是被低估的那一半
播客功能会按计划从 RSS 源下载剧集、保留可配置数量的最新剧集,并用和有声书一样的进度跟踪来提供服务。对一个听四五个节目的家庭来说,这替代掉了播客 App 的云账号。
下载内容进的是元数据目录而不是 NAS,这是对的位置——它们本来就是一次性的,放在 NAS 上以外的地方能让备份集保持干净。
# 添加源:媒体库 -> 添加播客 -> 粘贴 RSS 地址
# 设置每个节目保留的剧集数与检查间隔
# 存在 /srv/homelab/audiobookshelf/metadata/feeds 下compose 文件
整份文件放在 /srv/homelab/audiobookshelf/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:2.19.4
container_name: audiobookshelf
restart: unless-stopped
ports:
- "13378:80/tcp"
environment:
TZ: Asia/Shanghai
AUDIOBOOKSHELF_UID: "1000"
AUDIOBOOKSHELF_GID: "1000"
volumes:
- /srv/homelab/audiobookshelf/config:/config
- /srv/homelab/audiobookshelf/metadata:/metadata
- /mnt/nas/audiobooks:/audiobooks:ro
- /mnt/nas/podcasts:/podcasts:ro
networks: [rack]
networks:
rack:
external: true加固清单
- 账号按人创建,首次运行的管理员账号从默认名改掉
- 媒体目录以只读挂载,扫描器无法就地修改标签
- 反向代理路由经认证门户要求家庭成员组
- 手机 App 指向 VPN 地址而不是转发端口,收听进度因此保持私密
备份方案
音频文件本身在 NAS 上,这里不复制;配置目录里有媒体库索引、收听进度和用户账号,要备份的是它。进度是人们真正在意的部分——丢了它意味着每本书都要从头开始听。
上线后验证
- 在手机 App 里开始的一本书,在浏览器播放器里从同一位置继续
- 媒体库扫描显示每本书只有一条记录、包含全部章节,而不是拆成多个条目
- 走移动网络的转码流播放没有断续,同时派上显示一个核被部分占用
- 在某一台设备上听完的播客剧集,在另一台上显示为已播放
踩过的坑
- 把书文件直接放在作者目录下的文件夹,会产出一本巨大的书。目录结构就是元数据模型,不是建议。
- 元数据目录必须从任何同时监视 NAS 的同步工具里排除,否则 .metadata 里的 JSON 文件会和正在写它们的服务互相打架。
- 配置和元数据目录必须对容器的 uid 可写。其中任何一个只读挂载,都会造成「服务能启动、能提供媒体库、但保存进度时静默失败」——对这个服务来说这是最糟糕的失败方式。
- 手机 App 缓存很激进,常常需要手动刷新才能看到新书。这是 App 的行为而不是服务端的问题,重装服务端也修不了。
硬件与选型问答
- 有了 Navidrome 还需要这个吗?
- 它们的重叠比人们以为的少。Navidrome 是给音乐的——专辑、歌单、随机播放、大量短曲目。Audiobookshelf 是给长音频的,那里位置和章节结构就是全部意义。既有音乐收藏又有有声书的家庭两个都跑,加起来占 320MB。
- 手机 App 够不够好用,能天天用吗?
- 它是开源选项里最好的一个,车载使用也说得过去,包括 Android Auto。弱点和所有自建 App 一样:缓存行为偶尔需要手动同步,以及要求服务端可达——而 VPN 就是为此存在的。
- 有声书库要占多少空间?
- 按常见码率,每本书大约 200 到 400MB,所以一百本大约 30GB,再加上由保留设置管理的播客下载。这个体量小到可以放在 NAS 上而不用多想。